DVWA靶场通关实战

DVWA

  • Brute Force(暴力破解)
    • Low
    • Medium
    • High
    • Impossible
  • Command Injection(命令行注入)
    • Low
    • Medium
    • High
    • Impossible
  • CSRF(跨站请求伪造)
    • Low
    • Medium
    • High
    • Impossible
  • File Inclusion(文件包含)
    • Low
    • Medium
    • High
    • Impossible
  • File Upload(文件上传)
    • Low
    • Medium
    • High
    • Impossible
  • Insecure CAPTCHA (不安全的验证码)
    • Low
    • Medium
    • High
    • Impossible
  • SQL Injection(SQL注入)
    • Low
    • Medium
    • High
    • Impossible
  • SQL Injection(Blind)(SQL盲注)
    • Low
    • Medium
    • High
    • Impossible
  • Weak Session IDs (弱会话ID)
    • Low
    • Medium
    • High
    • Impossible
  • XSS (DOM) (DOM型跨站脚本)
    • Low
    • Medium
    • High
    • Impossible
  • XSS(Reflected)(反射型跨站脚本)
    • Low
    • Medium
    • High
    • Impossible
  • XSS(Stored)(存储型跨站脚本)
    • Low
    • Medium
    • High
    • Impossible
  • CSP Bypass (CSP绕过)
    • Low
    • Medium
    • High
    • Impossible
  • JavaScript
    • Low
    • Medium
    • High
    • Impossible
  • 完结撒花

截至2022.11.24,DVWA共有十四个模块,分别是:

Brute Force(暴力破解)
Command Injection(命令行注入)
CSRF(跨站请求伪造)
File Inclusion(文件包含)
File Upload(文件上传)
Insecure CAPTCHA (不安全的验证码)
SQL Injection(SQL注入)
SQL Injection(Blind)(SQL盲注)
Weak Session IDs (弱会话ID)
XSS (DOM) (DOM型跨站脚本)
XSS(Reflected)(反射型跨站脚本)
XSS(Stored)(存储型跨站脚本)
CSP Bypass (CSP绕过)
JavaScript

按我的思路就是,Low、Media、High是拿来攻击的,Impossible是来教你防御的
然后就是缩减了一下目录,本来攻击方式、源码审计、漏洞原理都加了标题,但是那样目录就太长太丑了,想想还是删了算了
直接开冲!

Brute Force(暴力破解)

暴力破解,又叫撞库、穷举,使用大量的字典逐个在认证接口尝试登录,理论上,只要字典足够强大,破解总是会成功的。
阻止暴力破解的最有效方式是设置复杂度高的密码(英文字母大小写、数字、符号混合)。

而如果你的字典是从某网站泄露出来的,你使用它试图登陆其他网站,就便是撞库。撞库攻击的成功率高于暴力破解,因为你在A网站的用户名、密码通常和B网站的用户名、密码一致。

Low

DVWA Security界面将难度设置为Low
DVWA靶场通关实战_第1张图片
先看登录界面,填写账号密码后抓包
DVWA靶场通关实战_第2张图片
bp抓包后转到intruder模块,给username和password加上tag§
type选择Cluster bomb
DVWA靶场通关实战_第3张图片
对应位置插入字典后,点击右边的attack开始攻击
DVWA靶场通关实战_第4张图片
也可以更改OptionsNumber of threads修改进程数,增加爆破效率
DVWA靶场通关实战_第5张图片

根据响应包的大小以及响应体的内容判断是否成功,可以看到成功爆出一个用户admin/password,也是平台登录的默认账号
DVWA靶场通关实战_第6张图片
马上停止爆破,点到为止,一千四百万条顶不住啊。

源码审计


if( isset( $_GET[ 'Login' ] ) ) {
	// Get username
	$user = $_GET[ 'username' ];
	// Get password
	$pass = $_GET[ 'password' ];
	$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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful $html .= "

Welcome to the password protected area {$user}

"
; $html .= "{$avatar}\" />"
; } else { // Login failed $html .= "

Username and/or password incorrect.
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

username未过滤password只有一个md5加密,但是对实际的爆破过程毫无影响
首先很直观的就是一个万能密码(
由于限制了结果只能有一条,要加上limit 1
admin' or 1=1 limit 1#
DVWA靶场通关实战_第7张图片

Medium

爆破过程同上,但是可以明显感觉到,爆破的速度慢了很多
为了方便演示,直接把admin/password放在前几条了,反正多跑无益,还浪费时间
DVWA靶场通关实战_第8张图片
源码审计



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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful $html .= "

Welcome to the password protected area {$user}

"
; $html .= "{$avatar}\" />"
; } else { // Login failed sleep( 2 ); $html .= "

Username and/or password incorrect.
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

用了mysqli_real_escape_string()函数转义输入的用户名密码,但是没有设置编码,在gbk编码的数据库上可以进行sql注入
登录失败后sleep(2)延迟两秒

High

抓包发现url栏多了一项user_token
bp抓包后发现,只有在token正确的情况下才能尝试登录,其他情况均为302重定向
DVWA靶场通关实战_第9张图片
bp也是有带token爆破的功能的,首先转到intruder模块,选择Pitchfork模式
DVWA靶场通关实战_第10张图片
payload1是密码位,正常插入密码字典
payload2是token位,选择Recursive grep
DVWA靶场通关实战_第11张图片

然后去option里设置Grep-Extract
DVWA靶场通关实战_第12张图片

设置Start和End,将数据留在中间,也可以直接用鼠标在下方拖动选取文字
PS:设置grep时建议刷新一次浏览器,填入最新的token,避免访问失效,下面框里没东西
DVWA靶场通关实战_第13张图片设置完成后回到payload界面选中
DVWA靶场通关实战_第14张图片
因为token不能复用,将option的线程设置为一,重定向选择always
DVWA靶场通关实战_第15张图片
DVWA靶场通关实战_第16张图片
开始爆破后即可根据响应包大小判断是否成功
DVWA靶场通关实战_第17张图片
源码审计



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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); if( $result && mysqli_num_rows( $result ) == 1 ) { // Get users details $row = mysqli_fetch_assoc( $result ); $avatar = $row["avatar"]; // Login successful $html .= "

Welcome to the password protected area {$user}

"
; $html .= "{$avatar}\" />"
; } else { // Login failed sleep( rand( 0, 3 ) ); $html .= "

Username and/or password incorrect.
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>

开始后先校验tokencheckToken(),若token不匹配则重定向到登陆界面,不再进行判断
结束前生成新的tokengenerateSessionToken()
账号密码多用了一个stripslashes()函数删除反斜杠

Impossible

失败三次,锁定15分钟,没辙了,摆烂了
DVWA靶场通关实战_第18张图片
源码审计



if( isset( $_POST[ 'Login' ] ) && isset ($_POST['username']) && isset ($_POST['password']) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

	// Sanitise username input
	$user = $_POST[ '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 = $_POST[ '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 );

	// Default values
	$total_failed_login = 3;
	$lockout_time       = 15;
	$account_locked     = false;

	// Check the database (Check user information)
	$data = $db->prepare( 'SELECT failed_login, last_login FROM users WHERE user = (:user) LIMIT 1;' );
	$data->bindParam( ':user', $user, PDO::PARAM_STR );
	$data->execute();
	$row = $data->fetch();

	// Check to see if the user has been locked out.
	if( ( $data->rowCount() == 1 ) && ( $row[ 'failed_login' ] >= $total_failed_login ) )  {
		// User locked out.  Note, using this method would allow for user enumeration!
		//$html .= "

This account has been locked due to too many incorrect logins.
";
// Calculate when the user would be allowed to login again $last_login = strtotime( $row[ 'last_login' ] ); $timeout = $last_login + ($lockout_time * 60); $timenow = time(); /* print "The last login was: " . date ("h:i:s", $last_login) . "
"; print "The timenow is: " . date ("h:i:s", $timenow) . "
"; print "The timeout is: " . date ("h:i:s", $timeout) . "
"; */
// Check to see if enough time has passed, if it hasn't locked the account if( $timenow < $timeout ) { $account_locked = true; // print "The account is locked
";
} } // Check the database (if username matches the password) $data = $db->prepare( 'SELECT * FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR); $data->bindParam( ':password', $pass, PDO::PARAM_STR ); $data->execute(); $row = $data->fetch(); // If its a valid login... if( ( $data->rowCount() == 1 ) && ( $account_locked == false ) ) { // Get users details $avatar = $row[ 'avatar' ]; $failed_login = $row[ 'failed_login' ]; $last_login = $row[ 'last_login' ]; // Login successful $html .= "

Welcome to the password protected area {$user}

"
; $html .= "{$avatar}\" />"
; // Had the account been locked out since last login? if( $failed_login >= $total_failed_login ) { $html .= "

Warning: Someone might of been brute forcing your account.

"
; $html .= "

Number of login attempts: {$failed_login}.
Last login attempt was at: ${last_login}.

"
; } // Reset bad login count $data = $db->prepare( 'UPDATE users SET failed_login = "0" WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); $data->execute(); } else { // Login failed sleep( rand( 2, 4 ) ); // Give the user some feedback $html .= "

Username and/or password incorrect.

Alternative, the account has been locked because of too many failed logins.
If this is the case, please try again in {$lockout_time} minutes.
"
; // Update bad login count $data = $db->prepare( 'UPDATE users SET failed_login = (failed_login + 1) WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); $data->execute(); } // Set the last login time $data = $db->prepare( 'UPDATE users SET last_login = now() WHERE user = (:user) LIMIT 1;' ); $data->bindParam( ':user', $user, PDO::PARAM_STR ); $data->execute(); } // Generate Anti-CSRF token generateSessionToken(); ?>

代码行数直接比High多了一倍多,虽然注释挺多的
提交方式由GET改为POST
失败次数是写入数据库的,sql语句也改为参数化查询
不好说了,立体机动防御了
DVWA靶场通关实战_第19张图片

Command Injection(命令行注入)

命令注入,是指在某些需要输入数据的位置,构造恶意代码破环原有的语句结构,而系统缺少有效的过滤,许多内容管理cms存在命令注入漏洞。

Low

简洁的界面
DVWA靶场通关实战_第20张图片
象征性的ping一下,编码问题就不管了,影响不大
先来一个经典的ping
DVWA靶场通关实战_第21张图片命令执行常用的方式为||&&管道符
更多的可以看看这个命令执行
还有一些过滤技巧CTF—命令执行总结
||当前项执行失败后执行后项
DVWA靶场通关实战_第22张图片&&前项成功后执行后项
DVWA靶场通关实战_第23张图片源码审计



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
	$html .= "
{$cmd}
"
; } ?>

没有过滤,简单判断一下操作系统就拼接执行了

Medium

再次尝试时发现&&被过滤了
DVWA靶场通关实战_第24张图片||可以正常执行
DVWA靶场通关实战_第25张图片
源码审计



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
	$html .= "
{$cmd}
"
; } ?>

还多过滤了一个分号,其他没啥了

High

过滤了&||,但是剩了个|还在
DVWA靶场通关实战_第26张图片源码审计



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
	$html .= "
{$cmd}
"
; } ?>

还真就剩了个|,不知道为什么过滤'| ' => '','||' => '',
l后面是空格说明仅仅过滤了l+空格 没过滤单独的l,他好温柔,我哭死

Impossible

DVWA靶场通关实战_第27张图片

打都不打,直接看源码
源码审计



if( isset( $_POST[ 'Submit' ]  ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

	// Get input
	$target = $_REQUEST[ 'ip' ];
	$target = stripslashes( $target );

	// Split the IP into 4 octects
	$octet = explode( ".", $target );

	// Check IF each octet is an integer
	if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
		// If all 4 octets are int's put the IP back together.
		$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

		// 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
		$html .= "
{$cmd}
"
; } else { // Ops. Let the user name theres a mistake $html .= '
ERROR: You have entered an invalid IP.
'
; } } // Generate Anti-CSRF token generateSessionToken(); ?>

还是先checkToken()
然后$octet = explode( ".", $target );is_numeric()
硬性规定只能输入ip了

CSRF(跨站请求伪造)

一种可以被攻击者用来通过用户浏览器冒充用户身份向服务器发送伪造请求并被目标服务器成功执行的漏洞被称之为CSRF漏洞。
特点:

用户浏览器:表示的受信任的用户
冒充身份:恶意程序冒充受信任用户(浏览器)身份
伪造请求:借助于受信任用户浏览器发起的访问

由于CSRF攻击的特殊性,还是以钓鱼和源码解析为主好了

Low

源码审计



if( isset( $_GET[ 'Change' ] ) ) {
	// 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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); // Feedback for the user $html .= "
Password Changed.
"
; } else { // Issue with passwords matching $html .= "
Passwords did not match.
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

没有安全防护措施,简单判断一下输入的新旧密码是否相同,然后修改当前用户的密码

渗透测试
先看一眼界面,下面是平平无奇的更改密码用的表格,上面是一个测试登陆用的弹窗,直接改密码!
DVWA靶场通关实战_第28张图片
可以看到是GET方法修改密码
DVWA靶场通关实战_第29张图片
点击Test,登录成功
DVWA靶场通关实战_第30张图片

此时就可以bp抓包生成CSRF Poc了
DVWA靶场通关实战_第31张图片
保存到本地文件然后将密码改为111111,点击发送
DVWA靶场通关实战_第32张图片
密码修改成功

Medium

源码审计



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( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); // Feedback for the user $html .= "
Password Changed.
"
; } else { // Issue with passwords matching $html .= "
Passwords did not match.
"
; } } else { // Didn't come from a trusted source $html .= "
That request didn't look correct.
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

增加了$_SERVER[ 'HTTP_REFERER' ]头验证

渗透测试
题目的过滤方式为stripos(),可以利用命名文件夹的方式绕过Referer头的检测
DVWA靶场通关实战_第33张图片

High

源码审计


$change = false;
$request_type = "html";
$return_message = "Request Failed";

if ($_SERVER['REQUEST_METHOD'] == "POST" && array_key_exists ("CONTENT_TYPE", $_SERVER) && $_SERVER['CONTENT_TYPE'] == "application/json") {
	$data = json_decode(file_get_contents('php://input'), true);
	$request_type = "json";
	if (array_key_exists("HTTP_USER_TOKEN", $_SERVER) &&
		array_key_exists("password_new", $data) &&
		array_key_exists("password_conf", $data) &&
		array_key_exists("Change", $data)) {
		$token = $_SERVER['HTTP_USER_TOKEN'];
		$pass_new = $data["password_new"];
		$pass_conf = $data["password_conf"];
		$change = true;
	}
} else {
	if (array_key_exists("user_token", $_REQUEST) &&
		array_key_exists("password_new", $_REQUEST) &&
		array_key_exists("password_conf", $_REQUEST) &&
		array_key_exists("Change", $_REQUEST)) {
		$token = $_REQUEST["user_token"];
		$pass_new = $_REQUEST["password_new"];
		$pass_conf = $_REQUEST["password_conf"];
		$change = true;
	}
}

if ($change) {
	// Check Anti-CSRF token
	checkToken( $token, $_SESSION[ 'session_token' ], 'index.php' );

	// Do the passwords match?
	if( $pass_new == $pass_conf ) {
		// They do!
		$pass_new = mysqli_real_escape_string ($GLOBALS["___mysqli_ston"], $pass_new);
		$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 );

		// Feedback for the user
		$return_message = "Password Changed.";
	}
	else {
		// Issue with passwords matching
		$return_message = "Passwords did not match.";
	}

	mysqli_close($GLOBALS["___mysqli_ston"]);

	if ($request_type == "json") {
		generateSessionToken();
		header ("Content-Type: application/json");
		print json_encode (array("Message" =>$return_message));
		exit;
	} else {
		$html .= "
" . $return_message . "
"
; } } // Generate Anti-CSRF token generateSessionToken(); ?>

先验证token是否存在,再验证token值是否正确。要绕过High级别的反CSRF机制,关键是要获取用户当前token。

渗透测试
还是先抓个包
DVWA靶场通关实战_第34张图片由于加了token,这里的token就要通过其他方法获取了,比如先弹一个xss什么的(

Impossible

标准答案来咯



if( isset( $_GET[ 'Change' ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

	// Get input
	$pass_curr = $_GET[ 'password_current' ];
	$pass_new  = $_GET[ 'password_new' ];
	$pass_conf = $_GET[ 'password_conf' ];

	// Sanitise current password input
	$pass_curr = stripslashes( $pass_curr );
	$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
	$pass_curr = md5( $pass_curr );

	// Check that the current password is correct
	$data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' );
	$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
	$data->bindParam( ':password', $pass_curr, PDO::PARAM_STR );
	$data->execute();

	// Do both new passwords match and does the current password match the user?
	if( ( $pass_new == $pass_conf ) && ( $data->rowCount() == 1 ) ) {
		// It does!
		$pass_new = stripslashes( $pass_new );
		$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 database with new password
		$data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' );
		$data->bindParam( ':password', $pass_new, PDO::PARAM_STR );
		$data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR );
		$data->execute();

		// Feedback for the user
		$html .= "
Password Changed.
"
; } else { // Issue with passwords matching $html .= "
Passwords did not match or current password incorrect.
"
; } } // Generate Anti-CSRF token generateSessionToken(); ?>

DVWA靶场通关实战_第35张图片
旧密码+新密码+token+数据库参数化查询,强无敌!

File Inclusion(文件包含)

程序开发人员通常会把可重复使用的函数写到单个文件中,在使用某个函数的时候,直接调用此文件,无需再次编写,这种调用文件的过程通常称为包含。
文件包含函数加载的参数没有经过过滤或严格定义,可以被用户控制,包含其他非预期文件,导致了文件信息泄露或执行非预期代码。

Low

点开界面是三个文件包含
在这里插入图片描述
点击后是三个文件包含,URL为?page=file3.php
DVWA靶场通关实战_第36张图片
甚至XSS
DVWA靶场通关实战_第37张图片

回到正题,有1、2、3那可以试试4
DVWA靶场通关实战_第38张图片
目录穿越,绝对路径,远程文件包含都是没问题的
DVWA靶场通关实战_第39张图片
DVWA靶场通关实战_第40张图片
DVWA靶场通关实战_第41张图片

源码审计
还真是,简洁明了啊



// The page we wish to display
$file = $_GET[ 'page' ];

?>

来者不拒,下一位!

Medium

先包含file4.php,没有问题
然后测试我们的phpinfo,可以发现目录穿越和远程包含失效了,准确的说../和http协议被替换为空了

DVWA靶场通关实战_第42张图片
在这里插入图片描述
DVWA靶场通关实战_第43张图片
源码审计



// The page we wish to display
$file = $_GET[ 'page' ];

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

?>

如图所示,润

High

file4依旧没问题
DVWA靶场通关实战_第44张图片
剩下的都是问题
DVWA靶场通关实战_第45张图片

不过可以用file协议绕过?page=file:///D:/phpstudy_pro/rua/DVWA-master/phpinfo.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;
}

?>

匹配$file必须为file开头的字符串或include.php
因为只匹配了开头,那么file协议刚刚好满足此条件
linux系统就可以尝试去包含一些日志啊、/etc/shadow、/etc/passwd之类的了

Impossible



// The page we wish to display
$file = $_GET[ 'page' ];

// Only allow include.php or file{1..3}.php
if( $file != "include.php" && $file != "file1.php" && $file != "file2.php" && $file != "file3.php" ) {
	// This isn't the page we want!
	echo "ERROR: File not found!";
	exit;
}

?>

写死了,摆烂了,没救了

File Upload(文件上传)

File Upload,即文件上传漏洞,指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限。

Low

没过滤,传个直接getshell
DVWA靶场通关实战_第46张图片
源码审计



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' ] );

	// Can we move the file to the upload folder?
	if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
		// No
		$html .= '
Your image was not uploaded.
'
; } else { // Yes! $html .= "
{$target_path} succesfully uploaded!
"
; } } ?>

无检测,无黑白名单过滤

Medium

接着上传php,提示只能上传jpeg或png
DVWA靶场通关实战_第47张图片
打开bp,抓包修改Content-Type,上传成功
DVWA靶场通关实战_第48张图片
源码审计



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
			$html .= '
Your image was not uploaded.
'
; } else { // Yes! $html .= "
{$target_path} succesfully uploaded!
"
; } } else { // Invalid file $html .= '
Your image was not uploaded. We can only accept JPEG or PNG images.
'
; } } ?>

判断了type和文件大小
无伤大雅

High

传来传去传不上去了,生成个图片去文件包含好了(
先来个图片
copy 1.png/b+1.php/a shell.png
DVWA靶场通关实战_第49张图片
上传的时候注意图片大小,或者直接用1像素图片()
DVWA靶场通关实战_第50张图片

DVWA靶场通关实战_第51张图片
本地测试成功
DVWA靶场通关实战_第52张图片
使用工具时记得带上cookie,到时候返回数据为空找谁都不好使
DVWA靶场通关实战_第53张图片

源码审计



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_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 ) ) {

		// Can we move the file to the upload folder?
		if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
			// No
			$html .= '
Your image was not uploaded.
'
; } else { // Yes! $html .= "
{$target_path} succesfully uploaded!
"
; } } else { // Invalid file $html .= '
Your image was not uploaded. We can only accept JPEG or PNG images.
'
; } } ?>

要求后缀名是图片,getimagesize()检测结果不能为false
只能传图片然后文件包含了

Impossible

源码来咯



if( isset( $_POST[ 'Upload' ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );


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

	// Where are we going to be writing to?
	$target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
	//$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
	$target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
	$temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
	$temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;

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

		// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
		if( $uploaded_type == 'image/jpeg' ) {
			$img = imagecreatefromjpeg( $uploaded_tmp );
			imagejpeg( $img, $temp_file, 100);
		}
		else {
			$img = imagecreatefrompng( $uploaded_tmp );
			imagepng( $img, $temp_file, 9);
		}
		imagedestroy( $img );

		// Can we move the file to the web root from the temp folder?
		if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
			// Yes!
			$html .= "
${target_file} succesfully uploaded!
"
; } else { // No $html .= '
Your image was not uploaded.
'
; } // Delete any temp files if( file_exists( $temp_file ) ) unlink( $temp_file ); } else { // Invalid file $html .= '
Your image was not uploaded. We can only accept JPEG or PNG images.
'
; } } // Generate Anti-CSRF token generateSessionToken(); ?>
checkToken()						判断token是否正确
md5( uniqid() . $uploaded_name )	对上传的文件进行重命名
strtolower( $uploaded_ext )			判断后缀名
$uploaded_type 						判断文件mimetype
getimagesize()						判断文件起始字符是否符合
imagecreatefromjpeg()				图片二次渲染(可以绕过)
rename()							判断文件是否能移动到web目录下

心血来潮想研究下getimagesize(),发现好像仅仅只对文件开始的几个字符进行了判断,例如PNG的


// Retrieve PNG width and height without downloading/reading entire image.
function getpngsize( $img_loc ) {
    $handle = fopen( $img_loc, "rb" ) or die( "Invalid file stream." );

    if ( ! feof( $handle ) ) {
        $new_block = fread( $handle, 24 );
        if ( $new_block[0] == "\x89" &&
            $new_block[1] == "\x50" &&
            $new_block[2] == "\x4E" &&
            $new_block[3] == "\x47" &&
            $new_block[4] == "\x0D" &&
            $new_block[5] == "\x0A" &&
            $new_block[6] == "\x1A" &&
            $new_block[7] == "\x0A" ) {
                if ( $new_block[12] . $new_block[13] . $new_block[14] . $new_block[15] === "\x49\x48\x44\x52" ) {
                    $width  = unpack( 'H*', $new_block[16] . $new_block[17] . $new_block[18] . $new_block[19] );
                    $width  = hexdec( $width[1] );
                    $height = unpack( 'H*', $new_block[20] . $new_block[21] . $new_block[22] . $new_block[23] );
                    $height  = hexdec( $height[1] );

                    return array( $width, $height );
                }
            }
        }

    return false;
}
?>

PHP手册里只有jpg和png的
DVWA靶场通关实战_第54张图片
至于gif的话,我感觉Gif89a应该是可以直接绕过判断的

if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' || strtolower( $uploaded_ext ) == 'gif' ) &&
	( $uploaded_size < 100000 ) &&
	( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' || $uploaded_type == 'image/gif' ) &&
	getimagesize( $uploaded_tmp ) ) {

然而在判断里加入gif后,经过实际测试发现,只需要GIF三个字符即可绕过getimagesize()
DVWA靶场通关实战_第55张图片
GI时就失败了,报错信息我没改,小写的Gif也是没有用的,小tips,可以记着
DVWA靶场通关实战_第56张图片

Insecure CAPTCHA (不安全的验证码)

Insecure CAPTCHA,不安全的验证码
验证码:6
感觉……不如Unsafe verification process(不安全的验证过程),主要是验证流程出现了逻辑漏洞。

没有密钥的可以根据提示链接,科学上网,创建一个key,类型没深究,选了一个v2隐形,然后将密钥复制到本地config.inc.php文件即可
网址乱填的,如有雷同,不胜荣幸,域名改为127.0.0.1即可,此时本地访问就不要用localhost了
DVWA靶场通关实战_第57张图片
DVWA靶场通关实战_第58张图片
我发现这个隐形嘎嘎简单,单击点一下就验证成功了,啥也不用干
DVWA靶场通关实战_第59张图片
开始正题

Low

本来挺快的
bp代理一开就卡死在google了,可能代理不走梯子?
DVWA靶场通关实战_第60张图片
本来想摆烂直接审源码的,后面一想,反正目前任务是绕过验证码,直接默认不通过就好了(
直接注释有关验证码的函数
在这里插入图片描述
直接改为0,默认不通过
DVWA靶场通关实战_第61张图片
搞定
DVWA靶场通关实战_第62张图片
好耶,码没了(
提交修改,bp抓包能看到step=1,并且响应包提示验证码错误
DVWA靶场通关实战_第63张图片
看到1就应该改2试试,更改成功,绕过了验证
在这里插入图片描述
源码审计



if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '1' ) ) {
	// Hide the CAPTCHA form
	$hide_form = true;

	// Get input
	$pass_new  = $_POST[ 'password_new' ];
	$pass_conf = $_POST[ 'password_conf' ];
	// Check CAPTCHA from 3rd party
	//	$resp = recaptcha_check_answer(
	//		$_DVWA[ 'recaptcha_private_key'],
	//		$_POST['g-recaptcha-response']
	//	);
	$resp=0;
	// Did the CAPTCHA fail?
	if( !$resp ) {
		// What happens when the CAPTCHA was entered incorrectly
		$html     .= "

The CAPTCHA was incorrect. Please try again.
"
; $hide_form = false; return; } else { // CAPTCHA was correct. Do both new passwords match? if( $pass_new == $pass_conf ) { // Show next stage for the user $html .= "

You passed the CAPTCHA! Click the button to confirm your changes.
{$pass_new}\" /> {$pass_conf}\" />
"
; } else { // Both new passwords do not match. $html .= "
Both passwords must match.
"
; $hide_form = false; } } } if( isset( $_POST[ 'Change' ] ) && ( $_POST[ 'step' ] == '2' ) ) { // Hide the CAPTCHA form $hide_form = true; // Get input $pass_new = $_POST[ 'password_new' ]; $pass_conf = $_POST[ 'password_conf' ]; // Check to see if both password 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 database $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';"; $result = mysqli_query($GLOBALS["___mysqli_ston"], $insert ) or die( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); // Feedback for the end user $html .= "
Password Changed.
"
; } else { // Issue with the passwords matching $html .= "
Passwords did not match.
"
; $hide_form = false; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } ?>

第一次看见Low这么长的,长归长,其实就两部分,分为step1和step2
step1的验证码通过两次密码相同后会出现一个全是type="hidden"的表单和一个按钮其中有一条,进入到step2
step2就更加简单了,比较密码是否相同后直接修改

Medium

step1不变,step2多了一条You have not passed the CAPTCHA.
DVWA靶场通关实战_第64张图片
偷偷将$resp改为1,发送抓包,看到响应包里多了一条passed_captcha=true
DVWA靶场通关实战_第65张图片
由于没有具体的数值,很明显,这也是可以伪造的,将$resp改回0,重新发包,修改step=2和添加passed_captcha=true。
结果的话还是和上图一样

源码审计

step1的表单多了一行
<input type="hidden" name="passed_captcha" value="true" />

step2多了一个对该行的判断
	// Check to see if they did stage 1
	if( !$_POST[ 'passed_captcha' ] ) {
		$html     .= "

You have not passed the CAPTCHA.
"
; $hide_form = false; return; }

High

不看源码都知道加了token
DVWA靶场通关实战_第66张图片
下一步只能根据源码了
修改UA头User-Agent: reCAPTCHA
添加POST参数g-recaptcha-response=hidd3n_valu3
token检测没开,无所谓了
DVWA靶场通关实战_第67张图片
源码审计



if( isset( $_POST[ 'Change' ] ) ) {
	// Hide the CAPTCHA form
	$hide_form = true;

	// Get input
	$pass_new  = $_POST[ 'password_new' ];
	$pass_conf = $_POST[ 'password_conf' ];

	// Check CAPTCHA from 3rd party
	//	$resp = recaptcha_check_answer(
	//		$_DVWA[ 'recaptcha_private_key'],
	//		$_POST['g-recaptcha-response']
	//	);
	$resp=0;

	if (
		$resp || 
		(
			$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
			&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
		)
	){
		// CAPTCHA was correct. Do both new passwords match?
		if ($pass_new == $pass_conf) {
			$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 database
			$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "' LIMIT 1;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); // Feedback for user $html .= "
Password Changed.
"
; } else { // Ops. Password mismatch $html .= "
Both passwords must match.
"
; $hide_form = false; } } else { // What happens when the CAPTCHA was entered incorrectly $html .= "

The CAPTCHA was incorrect. Please try again.
"
; $hide_form = false; return; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); } // Generate Anti-CSRF token generateSessionToken(); ?>

验证就看这一句

if (
		$resp || 
		(
			$_POST[ 'g-recaptcha-response' ] == 'hidd3n_valu3'
			&& $_SERVER[ 'HTTP_USER_AGENT' ] == 'reCAPTCHA'
		)
	)

token少加了一行验证

checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

验证码通过或者UA头和POST参数g-recaptcha-response符合要求
说白了还是没用上验证码的验证结果

Impossible

源码审计



if( isset( $_POST[ 'Change' ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

	// Hide the CAPTCHA form
	$hide_form = true;

	// Get input
	$pass_new  = $_POST[ 'password_new' ];
	$pass_new  = stripslashes( $pass_new );
	$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 );

	$pass_conf = $_POST[ 'password_conf' ];
	$pass_conf = stripslashes( $pass_conf );
	$pass_conf = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_conf ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
	$pass_conf = md5( $pass_conf );

	$pass_curr = $_POST[ 'password_current' ];
	$pass_curr = stripslashes( $pass_curr );
	$pass_curr = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_curr ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
	$pass_curr = md5( $pass_curr );

	// Check CAPTCHA from 3rd party
	$resp = recaptcha_check_answer(
		$_DVWA[ 'recaptcha_private_key' ],
		$_POST['g-recaptcha-response']
	);

	// Did the CAPTCHA fail?
	if( !$resp ) {
		// What happens when the CAPTCHA was entered incorrectly
		$html .= "

The CAPTCHA was incorrect. Please try again.
"
; $hide_form = false; } else { // Check that the current password is correct $data = $db->prepare( 'SELECT password FROM users WHERE user = (:user) AND password = (:password) LIMIT 1;' ); $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR ); $data->bindParam( ':password', $pass_curr, PDO::PARAM_STR ); $data->execute(); // Do both new password match and was the current password correct? if( ( $pass_new == $pass_conf) && ( $data->rowCount() == 1 ) ) { // Update the database $data = $db->prepare( 'UPDATE users SET password = (:password) WHERE user = (:user);' ); $data->bindParam( ':password', $pass_new, PDO::PARAM_STR ); $data->bindParam( ':user', dvwaCurrentUser(), PDO::PARAM_STR ); $data->execute(); // Feedback for the end user - success! $html .= "
Password Changed.
"
; } else { // Feedback for the end user - failed! $html .= "
Either your current password is incorrect or the new passwords did not match.
Please try again.
"
; $hide_form = false; } } } // Generate Anti-CSRF token generateSessionToken(); ?>

总结加固方式

验证token
过滤密码
确认验证结果if( !$resp ) (划重点!!!)
参数化处理

验证就要好好单步验证,别整那些花里胡哨的,又是step2又是passed_captcha又是||

SQL Injection(SQL注入)

SQL Injection(SQL注入),是指攻击者通过注入恶意的SQL命令,破坏SQL查询语句的结构,从而达到执行恶意SQL语句的目的。

Low

哟,老熟人
DVWA靶场通关实战_第68张图片
测试得单引号注入
order by 判断列数,2可以3报错得2列
DVWA靶场通关实战_第69张图片
点到为止,懒得注了
DVWA靶场通关实战_第70张图片

源码审计
删减版,另一个是SQLITE



if( isset( $_REQUEST[ 'Submit' ] ) ) {
	// Get input
	$id = $_REQUEST[ 'id' ];

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '
' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '
'
); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "
ID: {$id}
First name: {$first}
Surname: {$last}
"
; } mysqli_close($GLOBALS["___mysqli_ston"]); break; } } ?>

判断数据库,拼接执行,毫无过滤

Medium

F12瞅一眼流量,POST注入,复制请求体到hackbar
DVWA靶场通关实战_第71张图片
测试注入类型时发现单引号和双引号都被添加反斜杠过滤,我又是utf8的数据库,宽字节用不了了
结果反手就是一个数字型注入,瞬间失去了转义的意义
DVWA靶场通关实战_第72张图片
源码审计
接着删



if( isset( $_POST[ 'Submit' ] ) ) {
	// Get input
	$id = $_POST[ 'id' ];

	$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '
' . mysqli_error($GLOBALS["___mysqli_ston"]) . '
'
); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Display values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "
ID: {$id}
First name: {$first}
Surname: {$last}
"
; } break; } } ?>

mysqli_real_escape_string(),但是数字型

High

不让直接注了,改弹窗了
但是,没区别啊
DVWA靶场通关实战_第73张图片
源码审计
high.php



if( isset( $_SESSION [ 'id' ] ) ) {
	// Get input
	$id = $_SESSION[ 'id' ];

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// 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( '
Something went wrong.
'
); // Get results while( $row = mysqli_fetch_assoc( $result ) ) { // Get values $first = $row["first_name"]; $last = $row["last_name"]; // Feedback for end user $html .= "
ID: {$id}
First name: {$first}
Surname: {$last}
"
; } ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res); break; } } ?>

session-input.php



define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';

dvwaPageStartup( array( 'authenticated', 'phpids' ) );

$page = dvwaPageNewGrab();
$page[ 'title' ] = 'SQL Injection Session Input' . $page[ 'title_separator' ].$page[ 'title' ];

if( isset( $_POST[ 'id' ] ) ) {
	$_SESSION[ 'id' ] =  $_POST[ 'id' ];
	//$page[ 'body' ] .= "Session ID set!


";
$page[ 'body' ] .= "Session ID: {$_SESSION[ 'id' ]}


"
; $page[ 'body' ] .= ""; } $page[ 'body' ] .= "


"
; dvwaSourceHtmlEcho( $page ); ?>

弹窗设置$_SESSION[ ‘id’ ],直接利用$_SESSION[ ‘id’ ]注入,还是无过滤,还以为会有点黑白名单什么的

Impossible

源码审计



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 )) {
		$id = intval ($id);
		switch ($_DVWA['SQLI_DB']) {
			case MYSQL:
				// Check the database
				$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
				$data->bindParam( ':id', $id, PDO::PARAM_INT );
				$data->execute();
				$row = $data->fetch();

				// Make sure only 1 result is returned
				if( $data->rowCount() == 1 ) {
					// Get values
					$first = $row[ 'first_name' ];
					$last  = $row[ 'last_name' ];

					// Feedback for end user
					$html .= "
ID: {$id}
First name: {$first}
Surname: {$last}
"
; } break; case SQLITE: global $sqlite_db_connection; $stmt = $sqlite_db_connection->prepare('SELECT first_name, last_name FROM users WHERE user_id = :id LIMIT 1;' ); $stmt->bindValue(':id',$id,SQLITE3_INTEGER); $result = $stmt->execute(); $result->finalize(); if ($result !== false) { // There is no way to get the number of rows returned // This checks the number of columns (not rows) just // as a precaution, but it won't stop someone dumping // multiple rows and viewing them one at a time. $num_columns = $result->numColumns(); if ($num_columns == 2) { $row = $result->fetchArray(); // Get values $first = $row[ 'first_name' ]; $last = $row[ 'last_name' ]; // Feedback for end user $html .= "
ID: {$id}
First name: {$first}
Surname: {$last}
"
; } } break; } } } // Generate Anti-CSRF token generateSessionToken(); ?>

intval()过滤,绝杀!无解!
参数化查询锦上添花

SQL Injection(Blind)(SQL盲注)

Low

单引号盲注

?id=1' and 1=1#&Submit=Submit#
?id=1' and 0=1#&Submit=Submit#

DVWA靶场通关实战_第74张图片
过程,前几天写的掌控安全的靶场里有

源码审计
惯例删除SQLITE



if( isset( $_GET[ 'Submit' ] ) ) {
	// Get input
	$id = $_GET[ 'id' ];
	$exists = false;

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				try {
					$exists = (mysqli_num_rows( $result ) > 0);
				} catch(Exception $e) {
					$exists = false;
				}
			}
			((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
			break;
	}
	if ($exists) {
		// Feedback for end user
		$html .= '
User ID exists in the database.
'
; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '
User ID is MISSING from the database.
'
; } } ?>

判断查询结果有无数据
有就exists没有就MISSING

Medium

POST,数字型

id=1 and 0=1#&Submit=Submit
id=1 and 1=1#&Submit=Submit

DVWA靶场通关实战_第75张图片

源码审计



if( isset( $_POST[ 'Submit' ]  ) ) {
	// Get input
	$id = $_POST[ 'id' ];
	$exists = false;

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			$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
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				try {
					$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
				} catch(Exception $e) {
					$exists = false;
				}
			}
	}
?>

同上
mysqli_real_escape_string(),但是数字型

High

改成cookie注入了,大差不差

id=1' and 1=1#&Submit=Submit
id=1' and 0=1#&Submit=Submit

DVWA靶场通关实战_第76张图片
源码审计
high.php



if( isset( $_COOKIE[ 'id' ] ) ) {
	// Get input
	$id = $_COOKIE[ 'id' ];
	$exists = false;

	switch ($_DVWA['SQLI_DB']) {
		case MYSQL:
			// Check database
			$query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
			$result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ); // Removed 'or die' to suppress mysql errors

			$exists = false;
			if ($result !== false) {
				// Get results
				try {
					$exists = (mysqli_num_rows( $result ) > 0); // The '@' character suppresses errors
				} catch(Exception $e) {
					$exists = false;
				}
			}

			((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
			break;
	}

	if ($exists) {
		// Feedback for end user
		$html .= '
User ID exists in the database.
'
; } 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 $html .= '
User ID is MISSING from the database.
'
; } } ?>

cookie-input.php



define( 'DVWA_WEB_PAGE_TO_ROOT', '../../' );
require_once DVWA_WEB_PAGE_TO_ROOT . 'dvwa/includes/dvwaPage.inc.php';

dvwaPageStartup( array( 'authenticated', 'phpids' ) );

$page = dvwaPageNewGrab();
$page[ 'title' ] = 'Blind SQL Injection Cookie Input' . $page[ 'title_separator' ].$page[ 'title' ];

if( isset( $_POST[ 'id' ] ) ) {
	setcookie( 'id', $_POST[ 'id' ]);
	$page[ 'body' ] .= "Cookie ID set!


"
; $page[ 'body' ] .= ""; } $page[ 'body' ] .= "


"
; dvwaSourceHtmlEcho( $page ); ?>

没过滤,直接取cookie拼接查询,概率sleep()

Impossible

源码审计



if( isset( $_GET[ 'Submit' ] ) ) {
	// Check Anti-CSRF token
	checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
	$exists = false;

	// Get input
	$id = $_GET[ 'id' ];

	// Was a number entered?
	if(is_numeric( $id )) {
		$id = intval ($id);
		switch ($_DVWA['SQLI_DB']) {
			case MYSQL:
				// Check the database
				$data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
				$data->bindParam( ':id', $id, PDO::PARAM_INT );
				$data->execute();

				$exists = $data->rowCount();
				break;
			case SQLITE:
				global $sqlite_db_connection;

				$stmt = $sqlite_db_connection->prepare('SELECT COUNT(first_name) AS numrows FROM users WHERE user_id = :id LIMIT 1;' );
				$stmt->bindValue(':id',$id,SQLITE3_INTEGER);
				$result = $stmt->execute();
				$result->finalize();
				if ($result !== false) {
					// There is no way to get the number of rows returned
					// This checks the number of columns (not rows) just
					// as a precaution, but it won't stop someone dumping
					// multiple rows and viewing them one at a time.

					$num_columns = $result->numColumns();
					if ($num_columns == 1) {
						$row = $result->fetchArray();

						$numrows = $row[ 'numrows' ];
						$exists = ($numrows == 1);
					}
				}
				break;
		}

	}

	// Get results
	if ($exists) {
		// Feedback for end user
		$html .= '
User ID exists in the database.
'
; } else { // User wasn't found, so the page wasn't! header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' ); // Feedback for end user $html .= '
User ID is MISSING from the database.
'
; } } // Generate Anti-CSRF token generateSessionToken(); ?>

token,intval(),参数化
御三家

Weak Session IDs (弱会话ID)

当用户登录后,在服务器就会创建一个会话(session),叫做会话控制,接着访问页面的时候就不用登录,只需要携带对应的cookie去访问。

sessionID作为特定用户访问站点所需要的唯一内容。如果能够计算或轻易猜到该sessionID,则攻击者将可以轻易获取访问权限,无需录直接进入特定用户认证界面,进而进行其他操作。

之后只要cookies随着http请求发送服务器,服务器就知道你是谁了。SessionID一旦在生命周期内被窃取,就等同于账户失窃(也就是熟悉的越权)。

Low

先看
DVWA靶场通关实战_第77张图片
再点,可以发现每次点击dvwaSession都会加一
DVWA靶场通关实战_第78张图片
那么理论上就可以伪造成下一位或者上一位的id,来达到越权访问的效果
之前有次渗透就是,后台登录完cookie就是用户id,那时候我是4200,下意识把cookie改为1,成功访问到其他用户个人信息,嘎嘎越权。

dvwaSession咋改都没什么区别,还是建议加一个界面,比如

if ($_COOKIE['dvwaSession'] == 1){
	$html= '
welcome admin
'
; }

假设admin登录时$_SESSION[‘last_session_id’]为1,他的$_COOKIE[‘dvwaSession’]为1
此时我们将本地的$_COOKIE[‘dvwaSession’]改为1即可越权为admin登录
DVWA靶场通关实战_第79张图片

源码审计



$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);
}

if ($_COOKIE['dvwaSession'] == 1){
	$html= '
welcome admin
'
; } ?>

每次生成的id依次递增,遍历爆破一下即可越权。

Medium

先生成dvwaSession找找规律

1669944854
1669944903
1669944911
1669944955

干这行的多少还是有些敏感的,session就是时间戳,越权的话,还是从现在往前爆破就行
DVWA靶场通关实战_第80张图片
源码审计



$html = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
	$cookie_value = time();
	setcookie("dvwaSession", $cookie_value);
}
?>

time()

High

先找规律
很怪,我的setcookie不生效了
不过,看还是能看的
DVWA靶场通关实战_第81张图片

c51ce410c124a10e0db5e4b97fc2af39
aab3238922bcc25a6f606eb525ffdc56
9bf31c7ff062936a96d3c8bd1f8f2ff3
6f4922f45568161a8cdf4ad2299f6d23

不出意外是md5了
DVWA靶场通关实战_第82张图片
不出意外也是0开始递增了

源码审计



$html = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
	if (!isset ($_SESSION['last_session_id_high'])) {
		$_SESSION['last_session_id_high'] = 0;
	}
	$_SESSION['last_session_id_high']++;
	$cookie_value = md5($_SESSION['last_session_id_high']);
	setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], false, false);
}

?>

递增,然后md5

Impossible

源码审计



$html = "";

if ($_SERVER['REQUEST_METHOD'] == "POST") {
	$cookie_value = sha1(mt_rand() . time() . "Impossible");
	setcookie("dvwaSession", $cookie_value, time()+3600, "/vulnerabilities/weak_id/", $_SERVER['HTTP_HOST'], true, true);
}
?>

随机数,时间戳,Impossible
再sha1
这谁顶得住啊

XSS (DOM) (DOM型跨站脚本)

DOM型XSS漏洞通常出现在以下情况: JavaScript从攻击者可控的源(如URL)获取数据,并将其传递给支持动态代码执行的接收器(如eval()或innerHTML)。这使得攻击者能够执行恶意的JavaScript,这通常允许他们劫持其他用户的帐户。
要实现基于dom的XSS攻击,需要将数据放置到源中,以便将其传播到接收器并导致执行任意JavaScript。

Low

DVWA靶场通关实战_第83张图片一眼,无事发生,
在多一眼看一眼就会被爆炸
点开下拉列表可以发现这个English是独立于选项之外的,也就是url传参
DVWA靶场通关实战_第84张图片
经典永流传
DVWA靶场通关实战_第85张图片
源码审计
Low.php



# No protections, anything goes

?>

彻底开摆了
index.php

<script>
	if (document.location.href.indexOf("default=") >= 0) {
		var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
		document.write(" + $decodeURI(lang) + "");
		document.write("");
	}
	    
	document.write("");
	document.write("");
	document.write("");
	document.write("");
script>

无视风险,直接拼接

Medium

继续
但是直接重定向?default=English
直接尝试img标签发现直接拼接到value里了
DVWA靶场通关实战_第86张图片
闭合一下
English>/option>
DVWA靶场通关实战_第87张图片
DVWA靶场通关实战_第88张图片

源码审计



// 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, ") !== false) {
		header ("location: ?default=English");
		exit;
	}
}

?>

stripos()是不分大小写的,那么就用不了了,但是xss标签有一大堆,方法也有一大堆

High

初次尝试无果,怎么试都无果,后面切换页面的时候发现这个
DVWA靶场通关实战_第89张图片
URL第一个#后面出现的任何字符,都会被浏览器解读为位置标识符。这意味着,这些字符都不会被发送到服务器端。单单改变#后的部分,浏览器只会滚动到相应位置,不会重新加载网页。
再根据Low的html源码可得
php界面对$_GET['default']进行了过滤
html会读取default=后的全部字符串substring(document.location.href.indexOf("default=")+8)
构造Payload:

?default=English#

DVWA靶场通关实战_第90张图片

源码审计



// Is there any input?
if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
	# White list the allowable languages
	switch ($_GET['default']) {
		case "French":
		case "English":
		case "German":
		case "Spanish":
			# ok
			break;
		default:
			header ("location: ?default=English");
			exit;
	}
}

?>

php页面只允许表单内容提交,没有问题。
但是有内鬼,终止执行

Impossible

源码审计



# Don't need to do anything, protection handled on the client side

?>


转index.php

# For the impossible level, don't decode the querystring
$decodeURI = "decodeURI";
if ($vulnerabilityFile == 'impossible.php') {
	$decodeURI = "";
}

document.write(" + $decodeURI(lang) + "");

DVWA靶场通关实战_第91张图片
不解码就不执行了,妙啊

XSS(Reflected)(反射型跨站脚本)

反射型XXS是一种非持久性的攻击,它指的是恶意攻击者往Web页面里插入恶意代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意攻击用户的目的。

Low

alert(1)
DVWA靶场通关实战_第92张图片
过(

源码审计



header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
	// Feedback for end user
	$html .= '
Hello ' . $_GET[ 'name' ] . '
'
; } ?>

无过滤,直接拼

Medium

标签没了
DVWA靶场通关实战_第93张图片
上图片
DVWA靶场通关实战_第94张图片
反正图片噶了还有七七八八的标签和方法
源码审计



header ("X-XSS-Protection: 0");

// Is there any input?
if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
	// Get input
	$name = str_replace( '噶了
在这里插入图片描述


比起能弹窗,我更想知道我的img哪去了
在这里插入图片描述
POST请求

txtName=
mtxMessage=

div
在这里插入图片描述
我的img捏
源码审计



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( '
";
}
$page[ 'body' ] .= '

You can include scripts from external sources, examine the Content Security Policy and enter a URL to include here:

'
;

他好温柔,怕你创建不了特地准备了两个测试用例
但是我都执行不了,大抵是出不了网罢,算了,我不知道

Medium

DVWA靶场通关实战_第105张图片

在这里插入图片描述
还是先看CSP头

script-src 'self' 'unsafe-inline' 'nonce-TmV2ZXIgZ29pbmcgdG8gZ2l2ZSB5b3UgdXA=';

unsafe-inline:允许使用内联资源,如内联
DVWA靶场通关实战_第106张图片
源码审计



$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");

# 

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
	" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '

Whatever you enter here gets dropped directly into the page, see if you can get an alert box to pop up.

'
;

还是贴心的给出了教程

High

化繁为简了

Content-Security-Policy:script-src 'self';

粗看看不出什么,只能看到有两个solveSum是相同的
DVWA靶场通关实战_第107张图片
尝试bp抓包,将solveSum改为xss语句,执行成功
DVWA靶场通关实战_第108张图片
具体还得看网页引用的high.js

function clickButton() {
    var s = document.createElement("script");
    s.src = "source/jsonp.php?callback=solveSum";
    document.body.appendChild(s);
}
//单击按钮后生成一个js的外链(好像是这么叫,反正就是外部引入
function solveSum(obj) {
	if ("answer" in obj) {
		document.getElementById("answer").innerHTML = obj['answer'];
	}
}
//自定义函数solveSum(),修改answer的值
//solveSum({"answer":"15"})
var solve_button = document.getElementById ("solve");

if (solve_button) {
	solve_button.addEventListener("click", function() {
		clickButton();
	});
}
//给按钮上监听单机事件

所以引入XSS时便会执行alert(1)({"answer":"15"})弹窗

源码审计
high.php

<?php
$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
	" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
	<p>The page makes a call to ' . DVWA_WEB_PAGE_TO_ROOT . '/vulnerabilities/csp/source/jsonp.php to load some code. Modify that page to run your own code.</p>
	<p>1+2+3+4+5=<span id="answer"></span></p>
	<input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/high.js"></script>
';

jsonp.php


header("Content-Type: application/json; charset=UTF-8");

if (array_key_exists ("callback", $_GET)) {
	$callback = $_GET['callback'];
} else {
	return "";
}

$outp = array ("answer" => "15");

echo $callback . "(".json_encode($outp).")";
?>

可以发现除了按钮,还能通过post传参拼接xss语句
已知
DVWA靶场通关实战_第109张图片


DVWA靶场通关实战_第110张图片
再结解释下XSS的原因

$callback = $_GET['callback'];

echo $callback . "(".json_encode($outp).")";

jsonp.php?callback=solveSum
callback的值会原样输出回html页面造成XSS

Impossible

源码审计
impossible.php

<?php

$headerCSP = "Content-Security-Policy: script-src 'self';";

header($headerCSP);

?>
<?php
if (isset ($_POST['include'])) {
$page[ 'body' ] .= "
	" . $_POST['include'] . "
";
}
$page[ 'body' ] .= '
<form name="csp" method="POST">
	<p>Unlike the high level, this does a JSONP call but does not use a callback, instead it hardcodes the function to call.</p><p>The CSP settings only allow external JavaScript on the local server and no inline code.</p>
	<p>1+2+3+4+5=<span id="answer"></span></p>
	<input type="button" id="solve" value="Solve the sum" />
</form>

<script src="source/impossible.js"></script>
';

jsonp_impossible.php

<?php
header("Content-Type: application/json; charset=UTF-8");

$outp = array ("answer" => "15");

echo "solveSum (".json_encode($outp).")";
?>

CSP限制为自身,js回调函数写死,摆烂咯~

JavaScript

应该是js代码有问题,导致一些不允许的操作,比如说更改样式(color)、删除限制(maxlength=“10”)、关闭验证(删除function函数)、修改条件(控制台修改指定值属性false为true)等

Low

DVWA靶场通关实战_第111张图片
那就试success
DVWA靶场通关实战_第112张图片
token错误
f12查看源码可以发现token都为js生成,且固定(默认ChangeMemd5(rot13())
DVWA靶场通关实战_第113张图片
在这里插入图片描述
再根据token的生成原理以及错误信息可以大胆猜测提交的token要改为successmd5(rot13())
DVWA靶场通关实战_第114张图片
最简单的还是控制台,因为已经在js里看到数据的加密了
DVWA靶场通关实战_第115张图片
修改,提交,验证通过
DVWA靶场通关实战_第116张图片
源码审计
这回的分类在index.php里,为了防止剧透删除了switch的分支

if ($_SERVER['REQUEST_METHOD'] == "POST") {
	if (array_key_exists ("phrase", $_POST) && array_key_exists ("token", $_POST)) {

		$phrase = $_POST['phrase'];
		$token = $_POST['token'];

		if ($phrase == "success") {
			switch( $_COOKIE[ 'security' ] ) {
				case 'low':
					if ($token == md5(str_rot13("success"))) {
						$message = "

Well done!

"
; } else { $message = "

Invalid token.

"
; } break; } } else { $message = "

You got the phrase wrong.

"
; } } else { $message = "

Missing phrase or token.

"
; } }

if ($token == md5(str_rot13("success")))
猜测之中,简单,过

Medium

此时token变成了XXeMegnahCXX,逆向首尾加俩X
DVWA靶场通关实战_第117张图片
关于token,比较复杂的方法就是去逆向他的加密逻辑,来达到伪造数据,懒狗一点就是直接修改数据,调用函数
先更改id='phrase'的value值
DVWA靶场通关实战_第118张图片
控制台直接调用medium.js
DVWA靶场通关实战_第119张图片
再回看token,成功改变
DVWA靶场通关实战_第120张图片
提交成功
DVWA靶场通关实战_第121张图片
源码审计

case 'medium':
	if ($token == strrev("XXsuccessXX")) {
		$message = "

Well done!

"
; } else { $message = "

Invalid token.

"
; } break;

没啥好审的

High

这回token有点长
DVWA靶场通关实战_第122张图片
一看high.js,看不懂,找一下js混淆
可以得到是javascript-obfuscator混淆
在线反混淆一下
DVWA靶场通关实战_第123张图片只看独立的,不看被调用的

function token_part_3(t, y = "ZZ") {
    document.getElementById("token").value = sha256(document.getElementById("token").value + y)
}
function token_part_2(e = "YY") {
    document.getElementById("token").value = sha256(e + document.getElementById("token").value)
}
function token_part_1(a, b) {
    document.getElementById("token").value = do_something(document.getElementById("phrase").value)
}
document.getElementById("phrase").value = "";
setTimeout(function() {
    token_part_2("XX")
}, 300);
document.getElementById("send").addEventListener("click", token_part_3);
token_part_1("ABCD", 44);
token_part_1("ABCD", 44)直接执行
token_part_2("XX")延时300ms
token_part_3()绑定在按钮上

那么我们只要输入phrase的值为success再执行part1和part2,最后点击按钮就能成功验证token
token_part_1(“ABCD”, 44);
DVWA靶场通关实战_第124张图片
token_part_2(“XX”);
DVWA靶场通关实战_第125张图片
提交触发token_part_3
DVWA靶场通关实战_第126张图片
成功通过验证

源码审计

case 'high':
	if ($token == hash("sha256", hash("sha256", "XX" . strrev("success")) . "ZZ")) {
		$message = "

Well done!

"
; } else { $message = "

Invalid token.

"
; } break;

跟js的加密过程一样就是了,逆向,拼接,sha256

Impossible

DVWA靶场通关实战_第127张图片

DVWA靶场通关实战_第128张图片
挡不住啊,前端js再牛,终归也是开源的,谁都能看见,稍微有效点的方法可能也就是冷门的混淆了,不知道混淆套娃后的js还能不能用,总之,能用后台生成token就尽量用后台,前台看看数据就好哩。

完结撒花

前前后后水了一周,中间还查了好多怪东西。
总之还是很强的,漏洞的利用和修复都涵盖到了。

你可能感兴趣的:(打爆靶场,安全,数据库,网络)