2 Tasteless was a very cool CTF, with very challenging tasks. Although I couldn't contribute too much because
3 I don't have that much experience, the skill level was demanding enough to actually learn a lot of new stuff.
5 The timewarp challenge was also a very interesting (and funny!) one, which I think was the first ctf exploiting the fact of
6 the fact that there's a time change happening during the ctf.
8 Overall it was a very pleasant experience and I was able to learn a lot of new stuff :)
12 Gabbr is a website implementing a simple chat functionality with WebSockets.
13 Every room has a different UUID, e.g. https://gabbr.hitme.tasteless.eu/#8f332afe-8f1d-411f-80f3-44bb2302405d
14 If nothing is specified, a new one is generated.
15 There also is a report functionality where an admin joins into the room, stays for 15 seconds and then posts an image that there's nothing to see.
17 The idea is to post some xss (e.g. `<img src=x onerror="alert('lel')"/>`) which the admin then executes, however the CSP doesn't let us execute Javascript:
18 `Refused to execute inline event handler because it violates the following Content Security Policy directive: "script-src 'nonce-0f9635147014ab5895de26c6'". Either the 'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...') is required to enable inline execution.`
20 So every Javascript code must have the correct <nonce> set in order to be executed.
21 We thought long about how to leak the nonce from the website and eventually found this link: https://sirdarckcat.blogspot.com/2016/12/how-to-bypass-csp-nonces-with-dom-xss.html
23 The idea is to inject some css which sets background-url images to a server we control. When the admin then joins the room he will make calls to our server with the characters of the nonce.
24 This nonce can then be used to inject the JS to send the admin cookie to us. The comrades further tried to solve this challenge and I went on to some stego challenges.
26 Next day I came back to this challenge and we managed to solve it!
27 The full script which was running can be found here: https://w0y.at/writeup/2019/10/27/tasteless-2019-gabbr.html
30 RGB was a stego challenge where a pcap network dump was given in which three flags were hidden: r, g and b.
31 By analysing the network dump with wireshark I found a GET request to `ctf.tasteless.eu/stegano` which responded with a PNG image file.
32 I exported the image out of the dump with wireshark and put it inside Stegsolver. It showed some hints at some color panes which apparently are references to RFC articles:
36 Category: Standards Track
40 Category: Informational
44 Category: Informational
48 I read all the RFC articles trying to find some hints but I couldn't find anything useful.
49 After some time a colleague mentioned that it's kind of weird that the response to the GET request where the image is transmitted is chunked.
50 The flag is hidden in there!
51 Each chunk starts with 1000; followed by a character. By following the TCP stream in wireshark and searching for "1000;" one can easily obtain the flag.
52 It was: `tctf{NoB0dy_3xPec7s_chUnK_ex7En5iOnz}`
54 The next flag was stored in the CRC fields of the IDAT chunks in the PNG image. It was solved by a colleague, however I couldn't gather more information on it from the mattermost channel.
57 There are 2 endpoints on the website, /token and /timewarp.
58 At /token you can get a token which must be submitted at /timewarp, however it is only valid for 5 seconds. When submitting the token the server sends some lines and takes 1 second per line, only after several seconds the message "oh no! too slow! your token is not valid anymore :(" appears.
60 So we somehow have to make this token valid for a longer period. Let's look at the token:
61 ```Z2l2ZUZsYWcvRXVyb3BlL0Jlcmxpbg==.U2F0IE9jdCAyNiAxNzowOTo0NCAyMDE5.zCCIicp0CJdsvvoBiR34JHPeEufJej1MwyzfH3x7O_oyWpjftaMM0RFjd7yb4vVxSKG0CkIRaXrZfCgSIG3ADQ==```
64 ```giveFlag/Europe/Berlin.Sat Oct 26 17:09:44 2019. t
\bl
\ 1\1d$s
\12z=L,
\1f|{:7h4DX&\R(m
\ 2Z^``` when base64 decoded.
66 The structure is something like "giveFlag/Timezone.Timestamp.Gibberish"
68 Obviously we can try to change the time and submit it, however this results in: "oh no! what happened to your integrity?"
69 So there's probably a checksum in the gibberish part of the token which we also must manipulate...
71 So we need to figure out what the gibberish in the token means... but here I got stuck.
73 Another idea is based on the fact that in the night the daylight's saving time ends which means that the clock is gonna be set back one hour.
74 So maybe one can generate a token before the change and then you have a 1-hour window where the token is valid.
75 The challenge description "!!! challenge will shutdown at approx 02:30 UTC !!!" also hints at that...
77 A colleague wrote a script which tried getting a token and submitting it every 10 minutes:
84 from datetime import datetime
87 server = 'http://hitme.tasteless.eu:10101'
88 get_token_path = '/token'
92 r = requests.get(server + get_token_path)
95 match = re.search(r'href=\"(.+)\".*', response)
97 use_token_path = match.groups()[0]
98 return(server + use_token_path)
102 def use_token(use_token_url):
103 r = requests.get(use_token_url)
108 while(datetime.now() < datetime(2019, 10, 27, 4, 15)):
109 if(datetime.now() > datetime(2019,10,27, 1, 50)):
110 new_token = get_token()
112 tokens[datetime.now()] = new_token
114 for timestamp in tokens.keys():
115 response = use_token(tokens[timestamp])
116 print("Token acquired at '{0}' lead to the following response when used at '{1}':\n\n{2}\n".format(timestamp, datetime.now(), response))
118 print('------- Going to sleep ------\n\n')
122 It worked like a charm. However, I think someone else solved it manually first, at least according to the mattermost channel. Unfortunately I wasn't up at that time.