### 简要描述: 验证encrypt_key. 这个会上首页吗。。 好紧张。。 ### 详细说明: 首先说一下 官方的demo站竟然还没打补丁。 我进去的时候已经看见里面有几个马儿了。。 打下补丁 清下马儿把。 来看看0604出的补丁修改了哪里。 在plugins/phpdisk_client/client_sub.php ``` switch ($action){ case 'upload_file': //write_file(PHPDISK_ROOT.'system/2.txt',var_export($_POST,true)); //write_file(PHPDISK_ROOT.'system/3.txt',var_export($_FILES,true)); $sign_md5 = md5($uid.$settings[encrypt_key]); if(!$sign and $sign_md5<>$sign){ echo 'Sign Error!'; exit; } ``` 在这里上传的时候验证了 ``` $sign_md5 = md5($uid.$settings[encrypt_key]); if(!$sign and $sign_md5<>$sign){ echo 'Sign Error!'; exit; ``` 如果不相等则退出. $uid倒还容易搞 来看看$settings[encrypt_key] ``` 'encrypt_key' => 'Bw5xe2XIlwwj', ``` 我擦 这么复杂? 再来看看是如何生成这个的。 ``` function make_key(){ var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var tmp = ""; var code = ""; for(var i=0;i<12;i++){ code += chars.charAt(Math.ceil(Math.random()*100000000)%chars.length); } document.getElementById('encrypt_key').value = code; } ``` 这么多字符中随机取12个。 我擦...
### 简要描述: 验证encrypt_key. 这个会上首页吗。。 好紧张。。 ### 详细说明: 首先说一下 官方的demo站竟然还没打补丁。 我进去的时候已经看见里面有几个马儿了。。 打下补丁 清下马儿把。 来看看0604出的补丁修改了哪里。 在plugins/phpdisk_client/client_sub.php ``` switch ($action){ case 'upload_file': //write_file(PHPDISK_ROOT.'system/2.txt',var_export($_POST,true)); //write_file(PHPDISK_ROOT.'system/3.txt',var_export($_FILES,true)); $sign_md5 = md5($uid.$settings[encrypt_key]); if(!$sign and $sign_md5<>$sign){ echo 'Sign Error!'; exit; } ``` 在这里上传的时候验证了 ``` $sign_md5 = md5($uid.$settings[encrypt_key]); if(!$sign and $sign_md5<>$sign){ echo 'Sign Error!'; exit; ``` 如果不相等则退出. $uid倒还容易搞 来看看$settings[encrypt_key] ``` 'encrypt_key' => 'Bw5xe2XIlwwj', ``` 我擦 这么复杂? 再来看看是如何生成这个的。 ``` function make_key(){ var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; var tmp = ""; var code = ""; for(var i=0;i<12;i++){ code += chars.charAt(Math.ceil(Math.random()*100000000)%chars.length); } document.getElementById('encrypt_key').value = code; } ``` 这么多字符中随机取12个。 我擦 放弃逆这个了。 在plugins/phpdisk_client/client_sub.php中 ``` $agent = $_SERVER['HTTP_USER_AGENT']; if($agent!='phpdisk-client'){ exit('<a href="http://faq.phpdisk.com/search?w=p403&err=code" target="_blank">[PHPDisk Access Deny] Invalid Entry!</a>'); } $u_info = trim(gpc('u_info','P','')); parse_str(pd_encode(base64_decode($u_info),'DECODE')); ``` parse_str(pd_encode(base64_decode($u_info),'DECODE')); 这里调用了自定义的pd_encode 来解密 如果可以逆到key 就可以自己通过key来生成一个加密的 然后解密之后就可以变量覆盖。 ``` function pd_encode($string, $operation = 'ENCODE',$key = ''){ global $settings; $ckey_length = 4; $key = md5($key ? $key : ($settings['encrypt_key'] ? $settings['encrypt_key'] : 'PHPDisk=Rc9o')); $keya = md5(substr($key, 0, 16)); $keyb = md5(substr($key, 16, 16)); $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey = $keya.md5($keya.$keyc); $key_length = strlen($cryptkey); $string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d',0).substr(md5($string.$keyb), 0, 16).$string; $string_length = strlen($string); $result = ''; $arr = range(0, 255); $rndkey = array(); for($i = 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for($j = $i = 0; $i < 256; $i++) { $j = ($j + $arr[$i] + $rndkey[$i]) % 256; $tmp = $arr[$i]; $arr[$i] = $arr[$j]; $arr[$j] = $tmp; } for($a = $j = $i = 0; $i < $string_length; $i++) { $a = ($a + 1) % 256; $j = ($j + $arr[$a]) % 256; $tmp = $arr[$a]; $arr[$a] = $arr[$j]; $arr[$j] = $tmp; $result .= chr(ord($string[$i]) ^ ($arr[($arr[$a] + $arr[$j]) % 256])); } if($operation == 'DECODE') { if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16)) { return substr($result, 26); } else { return ''; } } else { return $keyc.str_replace('=', '', base64_encode($result)); } } ``` 调用了那12位的key。。 对算法不懂 放弃了。。 那就找找有没有哪里调用这函数来进行加密的。 如果要加密的可控的话也行 。 那就来找找在哪里调用了这函数来进行加密了的。 在plugins/phpdisk_client/client_main.php中 ``` if($action && $action<>'download'){ $agent = $_SERVER['HTTP_USER_AGENT']; if($agent!='phpdisk-client'){ exit('<a href="http://faq.phpdisk.com/search?w=p403&err=code" target="_blank">[PHPDisk Access Deny] Invalid Entry!</a>'); } } // checked username and pwd... $username = trim(gpc('username','GP','')); $password = trim(gpc('password','GP','')); $username = is_utf8() ? convert_str('gbk','utf-8',$username) : $username; $password = is_utf8() ? convert_str('gbk','utf-8',$password) : $password; $rs = $db->fetch_one_array("select * from {$tpf}users where username='$username' and password='$password'"); if(!$rs){ $str = '网盘登录出错:用户名或密码不正确,请重新输入'; if(is_utf8()){ echo convert_str('utf-8','gbk',$str); }else{ echo $str; } exit; }else{ if($rs[is_locked]){ $str = '网盘登录出错:用户名被锁定'; if(is_utf8()){ echo convert_str('utf-8','gbk',$str); }else{ echo $str; } exit; ``` 验证了user agent 可以修改一下就行了 然后去注册一个号就行了。 再往下面看。 ``` case 'loadset': if($settings['open_multi_server']){ $server_host = @$db->result_first("select server_host from {$tpf}servers where server_id>1 order by is_default desc limit 1"); } $server_host = $server_host ? trim($server_host) : $settings[phpdisk_url]; $sign = md5($uid.$settings[encrypt_key]); echo 'true'.LF; echo $server_host.LF; echo '0'.LF; echo base64_encode(pd_encode('username='.$username.'&password='.$password.'&sign='.$sign)).LF; echo $settings[client_api_key]; exit; break; ``` 这里调用了pd_encode 不是DECODE 是ENCODE。 看看里面的。 ``` echo base64_encode(pd_encode('username='.$username.'&password='.$password.'&sign='.$sign)).LF; $sign = md5($uid.$settings[encrypt_key]); ``` 。。可以看到 自己把做验证的带入到了pd_encode里面 然后输出了。。 这特么的太爽了。 ### 漏洞证明: 首先注册一个号 [<img src="https://images.seebug.org/upload/201406/0811534358218dd43b562b84cc05f71d391c9240.jpg" alt="p5.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/0811534358218dd43b562b84cc05f71d391c9240.jpg) 由于密码他这里没有md5。 所以自己把自己的密码进行md5一次后再放进去。 然后得到加密字符串。 MjdjNWpzd0lOYTFtQTd6R1l1alkxRlhlS2ZiYnc4azV1VFIyNHFXLzluZ1p1K2JFOVdqZlRTbVJXMXZLL0FYb21ScGlVMU5wcU1hSjZXOHYzZXk4MnpOWU1pdk1oV2Zzb0RTQk9tNHdCYWpjeHNUWG9sZUtMK0s5VzlrMUJhNzkrOXgrSVV2dTZrVitscURFZk16djJtM0lsWjV6OUZvSE9JU0lUZw== 然后在client_sub.php中 ``` $u_info = trim(gpc('u_info','P','')); parse_str(pd_encode(base64_decode($u_info),'DECODE')); ``` 解密后 就注册了$sign变量。 就通过了这个验证 就能继续上传任意文件了。 [<img src="https://images.seebug.org/upload/201406/0811570144aba4016e4e827d0222d18962a3af94.jpg" alt="p10.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/0811570144aba4016e4e827d0222d18962a3af94.jpg) sign错误 把刚才的加密字符串复制进去。 [<img src="https://images.seebug.org/upload/201406/0811575471651db67922f4b44a09e2920a796378.jpg" alt="p11.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/0811575471651db67922f4b44a09e2920a796378.jpg) 上传成功。 [<img src="https://images.seebug.org/upload/201406/08115853e731a7516a168990bfa342429c1be3fe.jpg" alt="p12.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/08115853e731a7516a168990bfa342429c1be3fe.jpg) 嗯。 _ 测试一下demo。 [<img src="https://images.seebug.org/upload/201406/08120008e5ce19cf392b1e3153b9d901b6b438a9.jpg" alt="p13.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/08120008e5ce19cf392b1e3153b9d901b6b438a9.jpg) 成功得到加密字符串。 在测试过程中发现不用这加密的也能上传成功。 看看原因。 [<img src="https://images.seebug.org/upload/201406/08120139827943c0ac8276ff161175424b6ebaed.jpg" alt="p14.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201406/08120139827943c0ac8276ff161175424b6ebaed.jpg) 原来官方竟然都忘记给自己的demo站打补丁了。 出于人道我给官方把补丁弄上去把。 ``` switch ($action){ case 'upload_file': //write_file(PHPDISK_ROOT.'system/2.txt',var_export($_POST,true)); //write_file(PHPDISK_ROOT.'system/3.txt',var_export($_FILES,true)); $sign_md5 = md5($uid.$settings[encrypt_key]); if(!$sign and $sign_md5<>$sign){ echo 'Sign Error!'; exit; } ``` 不过这补丁还得继续修改 因为能像上面那样绕过。