WP-2021绿盟杯-藏宝图
WP
藏宝图
非常有意思,将这个题记下来吧。首先进去是一个藏宝图的界面,然后界面上没啥东西。源代码和包是这样的:
这里是第一关已经解出来的一个包。它的要求比较简单,将这个secret连接user再md5就可以了。
传进去,如上图,就重定向到了cangbaoxiang.php
藏宝箱
藏宝箱第二关就难一些了:
以下是它的源码:
<link rel="stylesheet" href="css\cangbaoxiang.css">
</head>
<body>
<form id="login-box" method="POST">
<h1>藏宝箱</h1>
<p>找到个上锁的藏宝箱,但是密码没被清除干净,前面几个还能看的清楚,看看能不能从这几个密码中推演出完整的密码</p>
<div class="form">
<div class="item">
<i class="fa fa-user" aria-hidden="true"></i>
<input type="text" name="password" placeholder=qfGIFAPbEfSr>
</div>
</div>
<p><input class="button" type="submit" name="submit" value="提交"></p>
<p>
<!--
session_start();
if(!isset($_SESSION['seed'])){
$_SESSION['seed']=rand(0,999999999);
}
mt_srand($_SESSION['seed']);
$table = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
$pass='';
for ( $i = 0; $i < 24; $i++ ){
$pass.=substr($table, mt_rand(0, strlen($table) - 1), 1);
}
if(isset($_POST['password'])){
if($pass==$_POST['password']){
echo "<script >alert('恭喜你
} -->
</p>
</form>
</body>
</html>
简单解释一下这个工作方式,通过0-999999999
的随机播种,然后再进行24次随机数产生,然后用产生的结果来对应table表,进行一个替换,就完成密码了。
然后产生的密码仅有前半部分,即:qfGIFAPbEfSr
既然能够得到现有密码,我一开始的思路是爆破…
不过数据量过大,使用脚本浪费了挺长时间的
这里引用一段资料:
资料链接:https://blog.csdn.net/qq_45521281/article/details/107302795
PHP的自动播种
我们已经知道每一次mt_rand()被调用都会根据seed和当前调用的次数i来计算出一个伪随机数。而且seed是自动播种的:Note: 自 PHP 4.2.0 起,不再需要用 srand() 或 mt_srand()
给随机数发生器播种,因为现在是由系统自动完成的。那么问题就来了,到底系统自动完成播种是在什么时候,如果每次调用mt_rand()都会自动播种那么破解seed也就没意义了。
不幸的是,php每次调用mt_rand()函数时,都会先检查是否已经播种。如果已经播种就直接产生随机数,否则调用php_mt_srand来播种。也就是说每个php
cgi进程期间,只有第一次调用mt_rand()会自动播种。接下来都会根据这个第一次播种的种子来生成随机数。php_mt_seed
我们已经知道随机数的生成是依赖特定的函数,上面曾经假设为 rand = seed+(i*10)
。对于这样一个简单的函数,我们当然可以直接计算出一组解来,但 mt_rand()
实际使用的函数可是相当复杂且无法逆运算的。有效的破解方法其实是穷举所有的种子并根据枚举出的种子生成随机数序列再跟已知的随机数序列做比对来验证种子是否正确。php_mt_seed就是这么一个工具,它的速度非常快,跑完2^32位seed也就几分钟。
它可以根据单次mt_rand()的输出结果直接爆破出可能的种子,当然也可以爆破类似mt_rand(1,100)这样限定了MIN
MAX输出的种子。很多国内开发者在程序中使用了mt_rand()来生成安全令牌、核心加解密key等等导致严重的安全问题。
工具链接:http://www.openwall.com/php_mt_seed
使用之前要先make一下。
我用1234567890播种了一下,效果就是这样:
后面跟的是已经随机后一次的值
速度可以说是非常快了。
这里将这几个字母先替换回来,然后就可以使用这个工具了。
将字母替换的脚本如下:
str1='abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
str2='qfGIFAPbEfSr'
str3 = str1[::-1] # 倒序
length = len(str2) # 12
res=''
for i in range(len(str2)):
for j in range(len(str1)):
if str2[i] == str1[j]:
res+=str(j)+' '+str(j)+' '+'0'+' '+str(len(str1)-1)+' '
break
print (res)
#前两个str(j)代表第一个mt_rand()输出的界限,后两个参数0和str(len(str1)-1)表示传递到 mt_rand()的范围为0到61
执行后得到
16 16 0 61 5 5 0 61 42 42 0 61 44 44 0 61 41 41 0 61 36 36 0 61 51 51 0 61 1 1 0 61 40 40 0 61 5 5 0 61 54 54 0 61 17 17 0 61
放入工具:
seed=771353621
算出种子,接下来再把种子放回去跑一遍就行。
这样就得到密码了
带走宝藏
以下为源码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>藏宝图</title>
<link rel="stylesheet" href="css\treasure.css">
</head>
<body>
<form id="login-box" method="POST">
<h1>宝藏</h1>
<p>通过藏宝图的指引,破解出藏宝箱的密码,但是由于人数限制,一人只能选择带走一个宝藏。</p>
<!-- ZmxhZ+WcqGZsYWcucGhw5Lit -->
<div class="form">
<div class="item">
<i class="fa fa-user" aria-hidden="true"></i>
<input type="text" name="treasure" placeholder='请选择你想要的宝藏'>
</div>
</div>
<p><input class="button" type="submit" name="submit" value="提交"></p>
<!-- function is_php($data){
return preg_match('/[flag].*[php]/is', $data);
}
if($_POST['treasure'])
{
if(is_php($_POST['treasure'])) {
echo "<script >alert('这个不能拿走');</script>";
} else {
if(preg_match('/flag.php/is', $_POST['treasure'])){
highlight_file('flag.php');
}
}
} -->
</form>
</body>
<ml>
这里属实是把我整不会了
关键在于,你需要绕过/[flag].*[php]/is
但是同时又要命中 /flag.php/is
看起来相当矛盾,试了无数次从正则的方向来进行绕过都不太行,事实是,这里要使用preg_match的匹配机制来绕过。
除了一般的%00绕过啥的,稍微高级一点的正则绕过可以看这个:
https://www.cnblogs.com/20175211lyz/p/12198258.html
这里面就提到了 PHP利用PCRE回溯次数限制绕过某些安全限制
正则原理就是遇到不匹配时就进行回溯,尝试使用其他的解决办法。当我们使用非常长的字符串,就可以突破限制,触发preg的保护机制(防止dos),令匹配直接返回false,不再返回常规的1或者0。
所以这里的结果就是:
>>> resp=requests.post(url,data={'treasure': 'flag.php' + 'A' * 1000000})
>>> print resp.text
flag{C0ngratu1aTion2-0n-gEtting-the-treAsure}