FAUST CTF 2022: compiler60
The service takes ALGOL60v2 code, compiles it and runs it. In the compile step, the code is parsed to an AST and this AST is used to generate x64 assembly. This assembly is then assembled and linked to a binary, which gets signed and returned to the user. In the execution step, the server expects a signed binary. If the signature is valid, the binary is executed in a sandboxed environment.
When converting the AST to assembly instructions, string literals are not escaped correctly. The ALGOL60v2 code
1 | s := "\\" |
will result in the assembly code
1 | .asciz "\\" |
As demonstrated, it is possible to escape a string literal context and inject (almost) arbitrary assembly code. This can be used to control the behaviour of the resulting binary.
Exploitation
In order to get a flag, we need to read a file from a given file path. The path is /data/<id>
, where <id>
comes from teams.json
. The existing functions for opening files do not allow this, so we will redefine one of them:
1 | s := "\\" |
The new openRO
function will call the open
syscall with a file path and the O_RDONLY
flag and the rw-r--r--
mode. We can then call this method and read the flag from the resulting file descriptor. The final payload is the following:
1 | 'BEGIN' |
During the CTF we used a more verbose version:
1 | 'BEGIN' |
Patch
The best patch would have been to correct the AST parsing, but it is a CTF challenge and we were lazy, so we just blocked \\"
. Since that broke one of the examples, we allowed \\\"
. This was the patch:
1 | diff --git a/main/java/de/faust/compiler60/CompilerServer.java b/main/java/de/faust/compiler60/CompilerServer.java |