summaryrefslogtreecommitdiff
path: root/what-the-flag.md
blob: 0ab67c48893456f5160a34555b9bdc592342e197 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# What-the-flag

The provided webpage tells us that the "KITCTF FlagGenerator" is
currently not available to the public. There are no buttons or links,
leading us to investigate the source-code.

We found the comment
`<!-- TODO prevent google from leaking our source code -->` in the
source code. Well, how can you prevent Google (or web crawlers in
general) from crawling your webpage? Using the `/robots.txt`!

The `robots.txt` file contains the string
`User-agent: * Disallow: /source`. Soo the next step was obviously to
investigate the `/source` file. This was the moment we understood the
name of the challenge "wtf" -- the code mainly consists of "(", ")", "="
and "+", all wrapped in an `exec` call. The existence of the `exec`
function made us believe that the source is written in Python (lucky
guess, to be honest). As the string inside the `exec` function would
represent some kind of source-code, we could potentially retrieve it by
replacing it with a `print` function -- which gave us a (weirdly
formatted) Flask backend!

Two routes were especially interesting:

``` python
@app.route('/3ng1n33r1ng-s4mpl3',methods=['POST'])
def flag():
    A={A[0]:A[1]for A in request.headers.items()};B=request.args.to_dict()
    if subprocess.check_output(['node',_A,str(A),str(B),str(request.json)]).decode().strip()=='Valid':return os.environ.get('FLAG')
    return redirect('/')
```

and

``` python
@app.route('/source/js')
def js_source():
    with open(_A)as A:return A.read()
```

After some investigation it seems like POSTing some data to
`/3ng1n33r1ng-s4mpl3` would return the flag *if* the provided JS code
(applied with the given request data) would return "valid".

The `/source/js` code, however, looks even weirder than the previous
Python code.

``` javascript
[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]](([][[]]+[])[!+[]+!+[]]+([]+[])[(![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])...
```

Luckily we immediatly realized that this encoding must be "jsfuck". By
executing and reformatting the code, we arrived at a NodeJS application
that uses its arguments to decide whether they match the interpretation
of some brainfuck codes (wtf?):

``` javascript
const headers = JSON.parse(process.argv[2].split("'").join('"')),
  args = JSON.parse(process.argv[3].split("'").join('"')),
  body = JSON.parse(process.argv[4].split("'").join('"'));
headers[interpret(d.header_key)] === interpret(d.header_value) &&
args[interpret(d.query_key)] === interpret(d.query_value) &&
body[interpret(d.body_key)] === interpret(d.body_value) &&
headers.Cookie.split("=")[0] === interpret(d.cookie_key) &&
headers.Cookie.split("=")[1] === interpret(d.cookie_value)
  ? console.log("Valid")
  : console.log("Fake");
```

By calling the `interpret` function directly on the values in the `d`
object and logging the results, we achieved the data that we would have
to put in the request parameters:

-   path "/3ng1n33r1ng-s4mpl3"
-   cookie with "nda=true"
-   json body with {"resource": "flag"}
-   header with "X-Early-Access: kitctf-certified-tester"
-   query with "key=5468697320697320612076616c6964206b6579"

Plugging all this into a `curl` request finally gave us the flag:

``` bash
curl --cookie "nda=true" --data '{"resource":"flag"}' -H "X-Early-Access: kitctf-certified-tester" -H "Content-Type: application/json" -X POST "https://wtf-0.chals.kitctf.de/3ng1n33r1ng-s4mpl3?key=5468697320697320612076616c6964206b6579"
```