二次注入——实战中较少,但代码审计使用比较多,分为黑盒和白盒
黑盒就是纯看页面,没有代码的审计;白盒就是纯看代码来审计;靶场这种既有页面又有代码的审计称为“白+黑”(调试)
二次注入在黑盒审计中很难做,只有看到源代码才可能能做二次注入
代码能力决定上限
拿到页面第一件事不是就开始注入,要先观察可能存在的注入:看到登录框首先就试一下万能密码
'or'1=1'# 密码同左
- 在靶场24里面,输入万能密码之后,显示BUG OFF,说明该页面存在过滤,也就是说逻辑上触发了检测,可能输入了敏感字符——过滤是普遍存在的
- 然后试一下别的框,如重置密码,然后注册一个账号,登录该账号,然后就会显示登录成功,还有一个修改密码的框组。(如果登录进去是空白或者只显示登录成功的话,查询该页面的源代码,搜索title,找到SQL Injections,在fofa里面搜索title="SQL Injections",然后在搜到的结果里面随便选一个)
- 功能键的了解到此结束。由于攻击是为了得到admin的账号权限,所以注册admin'#,密码随便设,然后登录,就会显示admin'#(此时只要使admin之后的单引号和输入框本来的前单引号形成闭合即可,所以使用admin'''#,在原理上也是可以的)账号登录成功,然后重置密码,如新密码为222,重置密码,重置成功之后就可以使用admin:222的账号登录成功
修改admin密码原理
注册时实际username='admin'# ',更改密码的时候,输入账号admin'# 在修改密码时传参为username='admin'#'——原本的单引号被注释,所以实际上修改的密码是admin的
(从攻击角度来说,一般会攻击管理员的账号,因为管理员的权限更大,一般都可以看到所有的账号和密码,并且也会一定的修改权限)
二次注入原理
- 用户输入不被正确过滤或转义,直接拼接到SQL查询中
- 攻击者在注入点处插入特定的恶意SQL代码
- 数据库执行查询时,将恶意SQL代码与原本的查询逻辑混合执行
- 恶意SQL代码执行后,可能会导致数据库返回敏感信息、修改数据或执行其他恶意操作
二次注入属于存储型注入,可以理解为构造恶意数据存储在数据库后,恶意数据被读取并进入到了SQL查询语句所导致的注入——精髓在于插入恶意语句之后再次引用,服务端将恶意语句存储,之后再次调用,就导致该恶意语句正常执行
注入的时候只是对插入的特殊字符进行了转义,但是该字符仍存在,此时恶意字符被插入到数据库里面,如果该字符导致SQL语句闭合,那么在注册admin的时候就会注释掉,再次利用的时候就会直接覆盖原数据内容
如:有一个登录功能,用户输入用户名和密码,后端代码如下:
username = getRequestParameter("username");
password = getRequestParameter("password");
sql = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'";
executeSQL(sql);
如果攻击者在用户名输入框中输入 `' OR '1'='1`,密码输入框输入任意密码,那么构造出来的SQL语句将变为:
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = '任意密码'
这个SQL语句中的 '1'='1' 始终为真——也就是说,查询的是所有username和password的布尔值为真的——数据库内存在的为真,不存在为假,所以登录查询将返回所有用户的数据,这样攻击者就绕过了认证,获取了未授权的访问权限
文件夹上有它的源代码(D:\phpstudy_pro\WWW\sqlilabs——白盒审计地址)
在login文件夹查看页面源代码,找到function sqllogin() 函数,里面有账号和密码变量的来源,抓包可以发现,这两个参数都是经过post传参,然后经过mysql_real_escape_string处理了
mysql_real_escape_string() 函数用于转移SQL语句中使用的单引号,双引号和反斜杠等特殊字符——也就是说,它是用来进行过滤的(转义字符使后面跟着的所有的字符都转化为普通字符,不再具有特殊意义),此时闭合都闭合不了,所以注入就不会成功
mysql_real_escape_string()存在删除线——是因为在高版本的php没有这个函数,删除线算是一个警告,但是仍旧能执行
防注入过滤
为了防止注入会在一些特殊字符的前面加反斜杠转义,比如:注册的时候可能会允许注册zhangsan' ,但是会加反斜杠转义,但是插入数据库的还是 zhangsan' ,所以下次登录的时候还是可以登进去的
代码是用来写网站的,注入是注入到网站的基建里,然后使网站认证为攻击者是合法用户
注入实际上是使攻击者拥有一定的权限,修改传参,加入注入的查询语句,查询网站连接的数据库,如果没有对传参进行验证,就会传到数据库的SQL查询框中,然后查到账号密码等去登录该网站——也就是说完成了从游客到管理员的提权。就是说,完成了在一个基础框里查询高级信息的过程。
为什么会发生SQL注入
SQL之所以能被注入,最主要的原因就是它的数据和代码(指令)是混合的
服务器对用户输入的内容没有严格过滤,攻击者通过精心构造输入,让输入的内容拼接到服务器原本的SQL语句中,改变了原有SQL语句的结构,而这个“新”的SQL语句代入到数据库中执行,产生了非预期的结果。
为什么预编译能够防止SQL注入
在预编译的机制下,用户在向原有SQL语句传入输入值之前,原有SQL语句的语法树就已经构建完成,因此无论用户输入什么样的内容,都无法再更改语法树的结构。至此,任何输入的内容都只会被当做值来看待,不会再出现非预期的查询——相当于python里的format函数
SQL之所以能被注入,最主要的原因就是它的数据和代码(指令)是混合的
其实我们想一下,C程序为什么从来没听说过注入这种说法,有的也是溢出
这是因为C是一种编译型语言,你没法在语义上欺骗它,语义解析这步提前做了,都生成二进制了
所以攻击C的方式大多是溢出,通过溢出让数据覆盖指令段
数据库也提供了这种分离数据和代码(指令)的方式,就是SQL预编译
而SQL语句在程序运行前已经进行了预编译,在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化
对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询,当运行时动态地把参数传给PreprareStatement时
即使参数里有敏感字符如 or ‘1=1’也数据库会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令