Capture The Flag Challenges from Cyber Security Base with F-Secure 2017/2018
Setup and Environment
As already last year (2016/2017), the University of Helsinki (Finland) created an online course on mooc.fi called Cyber Security Base with F-Secure. You could get 10 ECTS credits for participating. "By the end of the course, hard working participants are expected to possess the skills required from those who work as Junior Security Consultants in the industry." I participated last year and I liked it. The last part of course is a CTF (Capture The Flag) challenge in jeopardy style.In order to pass, you had to solve 80% of the "easy" challenges and 60% of the "medium" challenges. There were 12 "easy" challenges, 9 "medium" ones and five in the "hard" category. In order to see the challenges of the next level, you had to solve 80% of the previous level first. The "easy" ones were released on a Monday evening, the "medium" ones on a Tuesday evening and the "hard" ones on a Wednesday. The CTF was running for 8 days in total. On each release-evening, the different challenges were released in about 10-minute delays, because each challenge has the name of the first solver next to it and the delay helps to give everyone a chance to get their name into one.
My goals
Last year I solved 100% of the challenges (with extensive effort) and I did not get my name into any of the challenges as first-solver, because there were people who solved some easy challenges in seconds. This year I hoped it would be a bit easier for me, especially after knowing much more and having solved similar challenges. In this past year I also participated in a "real" sit-in CTF. Unfortunately I have to say that only the hardest challenge here is comparable to the difficulty level that you will find on such CTFs. On the other hand, this makes it more fun to solve these CTF challenges here, because having easy challenges motivates you. One challenge was really hard.My goal was to finish again with 100% success rate and get my name into at least one challenge and also finish with more ease and earlier than last year. Well, I did achieve the 100% success rate (as one of six people that made it). I also got my name as "first-solver" into two easy challenges. For some more challenges I was second solver with just a few seconds behind. I was not at home on the second day for the "medium" ones and too slow for the "hard" ones. The "easy" challenges were indeed much easier for me and some challenges were repetitions of last year. But overall it was not totally easy for me, especially the Last Dr. Kyberzig, where I struggled until the last minute. I solved that last one just a few minutes before I thought the CTF was over. Due to time zone differences (the CTF didn't finish on Finnish time zone as it started, but in AoE time zone instead), it ran half a day longer. It took three days until someone managed to solve the Last Dr. Kyberzig and at the end only six people solved it at all.
Repetitions and Spoiling
One hour before the CTF started, I posted the link to my last year's write-up in the CTF chat channel and some people condemned this, saying "I having spoilt the CTF", because the rest of the course was identical as the content last year. Actually there were some almost identical challenges as last year, but I don't think this has spoilt anything. My last year's write-up is also easily findable if you google for it. The organizers knew about my write-up since last year. So I don't think this was a problem. Let me know if you participated and disagree. This also means that you can and should use this write-up here to prepare for next year or for other CTFs. Only two of the 26 challenges were to be made online (Cyber Bank) and all others can still be solved 100% offline and are available here for you.Conclusion
There were three easy "rot13" related challenges. One of those would have been enough. In total, there were too many repetitions from last year. The bank challenges, the UPX reversing trick, some password reversing challenges, one hard stego, some hashing ones and maybe some more.I would appreciate some more realistic challenges, like a SQL injection, maybe even one that cannot be exploitet with sqlmap. Or an application with a CSRF or XSS vulnerability or something like that (similar to the Cyber Bank application, but something new).
I really did like the Dr. Kyberzig challenges, although this shows that I lack knowledge in JavaScript frameworks. It also was very frustrating until I solved it. But that's actually a good thing.
Overall I liked this CTF a lot, although it did cost much of my health again. I'm looking forward in participating next year again. I'd like to thank the organizers and F-Secure for organizing this great education initiative.
Did you participate as well? If yes, did you take a completely different approach somewhere? Are there any mistakes in my write-up? Let me know in the comments below or contact me on Twitter @e4ch.
Title image for all solved |
Stegosaurus
Stegosaurus |
This is the image:
stegosaurus01.png |
So the easiest thing for this one would be that there is something in the binary at the end of the image, but unfortunately opening this in a hex editor didn't show anything interesting, except "tEXtComment.Secret", but this was not accepted as the flag. As it said "comment" there, I also looked at the file properties, but couldn't find anything interesting.
Then I tried some online tools and found the site https://29a.ch/photo-forensics that found something in the image itself:
Site 29a.ch showing that there is something written in lower-right corner |
Image enhancing with Photoshop |
Dr. Kyberzig's Key Validation Service Mk I
Dr. Kyberzig's Key Validation Service Mk I |
Here are the files from the Kyberzig challenges 1-4, compressed (7z format) and converted into base64. If you want to run it locally, then you need to fix the problem with creating the service worker (see a later Kyberzig-challenge on how to do that), or put it onto a website instead.
Input screen of Dr. Kyberzig's Key Validation Service Mk I |
Debugging Dr. Kyberzig 1 |
Stegosaurus II
Stegosaurus II |
This is the image:
stegosaurus03.jpg |
If we open the file with a hex editor, we see this at the end:
Hex editor of image stegosaurus03.jpg |
Dr. Kyberzig Mk II
Dr. Kyberzig Mk II |
Here are the files from the Kyberzig challenges 1-4, compressed (7z format) and converted into base64. If you want to run it locally, then you need to fix the problem with creating the service worker (see a later Kyberzig-challenge on how to do that), or put it onto a website instead.
While this challenge is pretty similar to the first Dr. Kyberzig, it did take me some time. First we open the page in Chrome, search for "not valid" in the prettified js file and find after a while the same "validateKey" area again.
Debugging Dr. Kyberzig Mk II |
There is a "validator", which sets the valid state that is later used to set the result. So we set a breakpoint on the validator function and step in to it and we get to an interesting function:
Inside the validator function |
Entering the validator function into Console window |
Stegosaurus III
Stegosaurus III |
This is the image we see:
stegosaurus04.jpg |
Stegosaurus III in HxD |
Converting the found text from Stegosaurus III from base64 to text |
Alice's crypto party
Alice's crypto party |
So we have to solve a standard RSA challenge. As we don't have any big numbers, this should be easily solvable with online tools. I found this RSA tool usable for this challenge.
So let's enter the two values p and q (and select decimal from the two dropdowns). It then already shows for both values that the bitsize is 26, so let's select key size=56 bits from the dropdown (26+26=52, just a bit less than 56). This already calculates that Euler's phi(n)=2050001820000400.
In step 2 we enter the public exponent 11 (or switch to decimal and enter 17).
In step 3 we select decimal for both dropdowns and click "Generate keys". Then we get an error message that p>q is now fulfilled. So we need to swap the two value in the first step and repeat everything done so far. Then we get the modulus n=2050001911000441.
In the next step we get the private key 844118396470753, which is already our flag.
During the CTF it was actually a bit more complicated. I did get about 20 different "candidate keys" or something, which I all had to try manually if they fulfill some criteria. Either I did something wrong or they improved the website since then.
Cyber monkies
Cyber monkies |
So there's some encrypted message. We notice the '=' at the end, so it's clear that this is base64 encoded. Any online base64 to text decoder quickly returns this text: Flag{DidNotTakeLong876}.
modhash
modhash |
This is one of the two challenges where I was first. It's so easy that it could be solved in a few seconds. Well, actually you had to read it carefully, which already took the most of the time.
So they explain how their simple "hash" function works and that we should do the same with a different text. So let's do what they have done with the text "hash".
The text "FlagSix" converted to hex bytes is 46 6c 61 67 53 69 78, or in big endian 466c6167536978. If we convert this hex number into decimal (Windows calculator can do this) we get the decimal value 19822413970893176. Now this value modulo 16 (Windows calculator can do this) is 8. This is already our flag.
If you understand how this all works, then you understand that this is not really a real hash function; you could change any character in that text (except the last one) and it would not change the hash. And this understanding would also improve the solution speed; you could just convert the last string character to hex: "FlagSix" -> 'x' -> 0x78 and take the lower digit (8), which is the solution. This would be much faster to solve, but during the CTF I also took the first variant.
Last year there was an identical challenge, just with a different text.
Advanced Encryption?
Advanced Encryption? |
So there's some encryption, probably AES as the name suggests. I used Cryptomathic:
AES decryption |
text conversion |
The last year there was a similar challenge. There they used CBC instead of ECB and many tools default to ECB. So this year this challenge was much simpler.
Admin panel
Admin panel |
This is one of the few challenges that was exactly the same as last year. It is one of the two online challenges this year, so you cannot retry it again.
When you went to that link, you get a login page:
Admin Panel start page |
The challenge says "there's something hidden" and talks about search engines, so let's have a look at the robots.txt file:
robots.txt |
Opening that link shows this admin panel without authentication:
admin panel |
This is indeed our flag.
Password checker
Password checker |
If you download the file (link here as base64 encoded on Pastebin) you get a Linux binary. I was a bit reluctant to install Linux, so I simply opened it in a hex editor and found the password there:
HxD view of the executable |
I haven't done that during the CTF for time reasons, but as an alternative, you could also simply open this executable in BinaryNinja, an inexpensive but very useful tool and get this directly in the "main" function:
BinaryNinja view of the password check |
Banana vault
Banana vault |
This text already looks like rot13 encoded, so let's try rot13.com:
rot13 of Banana vault |
And indeed, entering yellowbananaaaa confirms the flag. Straightforward and I was first in solving this challenge.
Stegosaurus IV
Stegosaurus IV |
Downloading the image gives us this one:
stegosaurus02.jpg |
thumbnail |
extracting the thumbnail |
Windows Explorer with our flag |
better extraction of all thumbnails |
2 extracted thumbnail files |
But we can do the same again. As these are normal JPG images, they could also contain thumbnails again. And indeed, the second one does contain another thumbnail:
extracting the thumbnails of the second thumbnail |
thumbnail of the thumbnail was extracted |
thumbnail of the thumbnail |
Dr. Kyberzig strikes back!
Dr. Kyberzig strikes back! |
Here are the files from the Kyberzig challenges 1-4, compressed (7z format) and converted into base64.
If we open that web page and then open the debugger, the execution is being stopped by a function calling debugger, which acts like setting a breakpoint. This does not happen when we close the development tools. So there is some anti-debugging built-in and we need to find a way to remove this. In order to comment out this function, we need a way to change it. So I copied all files locally (with IE, which copies all dependent files and modifies any references). I also ran the JavaScript file through jsbeautifier to have it nicely formatted (without the need to rely on Chrome to do that every time). When we now open the web page locally, we get another anti-debugging feature presented. It shows a friendly box "illegal hacking detected" for a few seconds and then redirects to Google.
Thanks to this friendly error message, we can search in the JavaScript file for this error message and immediately find it:
source code for hacking detection |
patching the hostname detection |
And we also need to patch the breakpoint falling into the debugger (at the end of the js file):
path to fall into the debugger all the time |
console errors |
source of error during service worker registration |
patch for service worker URL |
code for localhost detection |
So now everything should be patched and working and we can start debugging this thing.
If we run it and enter some characters into the input field, we get some strange math values in the console log:
math values in console log |
The most interesting thing there is probably the call to window.v(r) which controls the this.setState(), which is later used to set the key to valid or not. So let's set a breakpoint there.
location of our breakpoint |
showing the flag in the debugger |
So this works and it is the correct flag.
Password checker II
Password checker II |
If you download the file (link here as base64 encoded on Pastebin) you get a Linux binary. I was a bit reluctant to install Linux, so I wanted to use other methods. If we open the binary in BinaryNinja and go to the main function, we see this:
Password checker II in BinaryNinja |
And this is already our flag, "CorrectPasswrdDAFA". I think during the CTF I didn't even open any disassembler and already found that string in the hex editor, which yielded an even faster result.
In-laws of cyber monkeys
In-laws of cyber monkeys |
This looks again like rot13, but it is not. The page rot13.com can be used for that. Luckily this web page has a nice dropdown, so we can try out other shift values too:
rot22 |
Perfect encryption scheme?
Perfect encryption scheme? |
So there is some code that needs to be cracked. The first idea is that this is binary with ASCII values. But as there are zeroes of different length and other things that wouldn't make sense in binary, this can be ruled out quickly.
The next logical thing to try would be Morse code. I looked up the wiki page for it and found that it doesn't give any useful results; there were even codes that didn't match any character. So it was not Morse code.
Then I didn't know any further and gave up for the day (having solved all other "Medium" challenges and opened up the "Hard" ones already. The next day during work I thought a lot about this code. My next guess was that it's something like RLL or MFM or something like that, but I couldn't get anywhere with that either.
I then thought more about this and as the codes or letters were all a different length, I thought it must be something like a Huffman coding (which is actually not wrong). At that time someone already spoiled in the chat that the solution starts with "THIS". I already knew that if it is a Huffman code, that there must be some association between the digit groups and the letters. So even if I don't know the code exactly, I can always do some association. So I started with the "THIS" and quickly got "THIS IS THE VERY SECRET..." Unfortunately I didn't get the rest. So I googled more fore the code and found this challenge from Kevin Mitnicks book (Chapter 17). The code described there is Morse code with alternating bits. I tried to apply the same for our challenge and found out that I must have done something wrong initially, and it was simple and plain Morse code.
I decoded this manually by using the code from Wikipedia, but there are also online tools available (just replace the 0 with a . and 1 with a - first):
online decoding |
Hashing
Hashing |
So there's some hash of a password or something that is to be cracked. Let's try some online tool. I used Crackstation and it immediately found the hash:
Using on online hash lookup |
Cyber monkeys' better encryption scheme
Cyber monkeys' better encryption scheme |
This looks again like rot13, but it is not. The page rot13.com can be used for that. Luckily this web page has a nice dropdown, so we can try out other shift values too:
rot11 decrypted |
Password expiration policy
Password expiration policy |
From the length and through a quick test we find that the given hash is SHA-1:
online tool for hash generation |
hashcat in action |
Hack a bank
Hack a bank |
This is the same online banking application as the earlier banking application (Admin panel). Now that the "Medium" challenges are open, you can now log in (with your TMC account). There is a page with "Recent transactions" and it shows that you own 1000.00 Euros. In the screenshot below you see a different value, because that's after playing the CTF.
Cyber Bank, Recent transactions |
Then there's a page where you can convert the Euros you own to some cyber money:
Cyber Bank, Convert page |
Excel with win strategy |
I already noticed that this is the same or similar challenge as last year, but I didn't want to look up what I did last year. I simply used Burp to move the conversion of 0.17 Euros into Burp's Repeater (Burp Free Edition is sufficient) and after some minutes of converting (I also increased the number of threads to the maximum allowed), the bank account balance increased significantly.
After reaching 1200 Euros, I could go to the Marketplace page and buy that special product.
Cyber Bank Marketplace |
Dr. Kyberzig: Last stand
Dr. Kyberzig: Last stand |
Here are the files from the Kyberzig challenges 1-4, compressed (7z format) and converted into base64.
There were many hundred people participating in this course, 134 users solved the Stegosaurus II challenge, but this one was solved only by 6 people and the first solver took 3 days. This tells a lot about the difficulty of this challenge.
The challenge is similar to the previous Dr. Kyberzig challenge. There is an input field where you have to provide the correct password and somehow in the code there is a verification. The JavaScript is minified and has anti-debugging features. The most interesting code is heavily obfuscated and some code is dynamically generated.
In order to debug this thing, we had to apply the same techniques as previously used with the "Dr. Kyberzig strikes back!". I won't describe this here again, so please read the above one first. So I also here made the following changes:
- beautified the js code
- fixed link to manifest.json in htm file
- fixed link to favicon.ico in htm file
- call to debugger commented out
- comparison for hostname.endsWith(".now.sh") commented out and replaced with true
- in key: "hackingDetected" removed all the actions (set flag to hacking detected and redirect to Google)
- The last function in the js file is testing against hostname being local. I replaced this also with Boolean(false), although this is probably never used anywhere.
In addition to all the above, I attempted to understand the code a bit. For example there is some code eval(function(e, t, n, r, i, o){...}. This is evaluating (running) the following code (moved out of eval and formatted for better understanding):
one of the obfuscated functions |
As I didn't know what the requestAnimationFrame(loop) function does, I left that one in there and just replaced this whole part with the following code:
replacement code |
second deobfuscated function |
Looking through the code, I found this variable assignment:
$74 variable |
$74 variable replaced |
Then there is another heavily obfuscated function that looks like this:
another obfuscated function |
The string there (cut off in the screenshot) is actually 739674 characters long and many text editors have difficulties with this.
I replaced the above code with this better-looking deobfuscated code:
deobfuscated code |
Additionally I saved this array of bytes into a binary file, as this is the WebAssembly code.
I never heard of WebAssembly before, so I had to read into this quite a bit. Essentially you can compile C code (or other languages) to assembler (WebAssembly) and let it run within the web page. Not all browsers support this yet, but Chrome does. Chrome also nicely displays the WebAssembly (wasm) functions in the debugger, but stepping through JavaScript calls that step into wasm code doesn't break on the wasm code. If you set a breakpoint in the wasm code, it stops there, but removing the breakpoint doesn't stop Chrome from breaking there until you refresh everything and reload the page and cache. It also often crashes Chrome with the "Ups" message, so that you have to start over again. And in the debugger, the global variables are not displayed, only the local ones and the data stack. The wasm code had over 900 functions in it, so no chance to randomly find the one that is used for verifying the password. I did some stepping through, but got nowhere.
After the first 1-2 users solved this challenge, there was a hint given publicly that we should use some tools for static analysis. So I searched for wasm tools and quickly found The WebAssembly Binary Toolkit. A few days later this link was also posted in the public chat channel. Like many Linux tools, it was quite a challenge to get all dependencies (cmake) and get this code to compile, but I finally made it and got the tools on a Linux VM. I converted the wasm file from the binary from above into C code (see wasm.c and wasm.h), but I didn't get anything useful. It was then also told that the tools do preserve function names, but in the C-code I only saw the external names, which were not helpful. I also converted the code to wat, but didn't see any function names there either. Also the objdump (fullcontents, details, disassemble, headers) didn't help me much. The "canonical" flat format desugar was not much better either.
As I couldn't progress any further - I analyzed this code for many days - there was this public hint from the organizer (Henrik Nygren) where he told me to have a look at the function names again. There should be some hint in the function names that would simplify debugging. So I started with the wat file. First I renamed all the __extjs_xxx functions with the names from the JavaScript code. These functions in wasm are actually implemented in JavaScript and there is some mechanism for calling JavaScript. So for example I replaced __extjs_9f22d4ca7bc938409787341b7db181f8dd41e6df with __extjs_increment_refcount, which is much more helpful in understanding the code. After replacing these 10 names, there wasn't much change. So I started commenting the validator::validate function, as this had the most promising name. There was another function called main::validator though, but I had to start somewhere, so I started commenting it. That was a bit difficult on paper (editor actually), so I started looking at this with the debugger.
Just the validator::validate function was quite big (several hundred lines of WebAssembly), here is an example of how this function looks like:
WebAssembly (excerpt) of validator::validate |
I got some more hints for debugging this big piece of junk: The "blocks" above are actually not loops, but more like coding blocks for scope. And the C-style function memcmp above is something I should take a closer look at.
Debugging this function, I noticed that the input password gets MD5 hashed, but the memcmp function is not comparing the hash. The memcmp is comparing 32 bytes and there is a lot going on in those loops or blocks or whatever that is there. I didn't understand all this, until I noticed what this all is. There is a conversion from binary MD5 to hex (ASCII) being done and the memcmp compares the hex (ASCII) converted strings!
Here's the critical point in JavaScript where the whole wasm code is called:
Chrome debugging wasm |
Chrome debugging hash comparison |
- 262456
- 262408
- 32
Chrome wasm memory view |
MD5 hex ASCII password in memory at 262408 |
So now we "just" had to crack that MD5 hash.
Unfortunately all tools I tried didn't work:
- Google search
- https://crackstation.net/
- http://reverse-hash-lookup.online-domain-tools.com/
- https://hashkiller.co.uk/md5-decrypter.aspx
- http://md5.my-addr.com/md5_decrypt-md5_cracker_online/md5_decoder_tool.php
- https://isc.sans.edu/tools/reversehash.html
- http://www.md5online.org/
crackhash.com |
Trying this password with the original site also works:
Correct password |
This challenge was really challenging to me. I thought I know JavaScript, but a lot has changed since the old Netscape 4.03 days. I now know in what areas I'm lacking knowledge:
- fully understanding Promises
- Closures
- Ant framework
- React framework
- WebPack require
- Babel runtime regenerator
- WebAssembly (done)
Stegosaurus V
Stegosaurus V |
This is the downloaded image:
stegosaurus05.jpeg |
First we find that again the thumbnail image isn't matching and contains a different image. We can extract that as in the earlier challenge with the exiftool. We get this image:
Thumbnail of the stegosaurus05.jpeg image |
So I thought that it might be some password-protected hiding with the tool steghide, which was also used last year. Last year there was a problem that the Windows version of the tool didn't work, but someone mentioned that there is an online version of steghide, so I tried that. No luck there. I now know that I typed in the password with some wrong letters, but it wasn't the solution anyway.
So I googled around a bit and stumbled over this write-up of a CTF stego challenge. While our challenge is completely different, it did mention the tool Stegsolve, which I then also tried. This is a very nice tool indeed and it displayed the following information:
Extra information found in the image |
Showing that the extra information is a ZIP file |
extracted file |
Password checker III
Password checker III |
So I downloaded the binary (here base64 encoded) and opened it in BinaryNinja. Here's the main function, already with some comments I added:
Main function in BinaryNinja |
There we see that there is some separation between good and bad and then in the "good" case, there is some magic going on and the flag printed out. I couldn't quite understand the logic there in detail, so I thought I need to debug this. So I installed a Linux VM (Kali provides a VM ready for use) and I started debugging it with gdb. There might be better tools, but I'm not familiar with them. Please suggest me something if you know about some good debuggers.
Anyway, the first thing I tried was to manipulate the jne instruction that differentiates between good and bad, so that the execution jumped to the "good" path, even with a wrong password. That didn't work out as intended and just gave me a wrong result.
So I had to document this code a bit more and I found that in the top block a random value is generated, which is stored in [rbp-0x28] and then the user has to enter a password, which is converted into a DWORD with atoi and then both values are compared. I'm not sure why this random input is required for giving the correct result, but with this knowledge I could influence the execution. These are the commands in gdb that I had to issue:
- gdb
- file secret_c
- break main
- run
- layout asm
- break *0x400891 (just after the rand call)
- continue
- (enter some user name)
- info registers
- (rax=0x503aa9f6 1346021878, a different value each time)
- q
- continue
- (enter the password 1346021878 from above)
- (We get the output "secret flag is Flag4537".)
Stegosaurus VI
Stegosaurus VI |
"Something is not quite as it should be in this image. Can you find it? http://sec-mooc-1.cs.helsinki.fi/stegosaurus/6fvnr54/stegosaurus06ar.bmp"
This is the image:
stegosaurus06ar.bmp |
We already notice that the file is quite large - it's a bmp file with 41MB in size. There was a similar challenge last year, so we use this knowledge and solve it with zsteg:
Running zsteg |
And there we already have our flag "Randompart347856Flag".
Password checker IV
Password checker IV |
"Can you figure out the password? The binary file should work in 64-bit Linuxes. http://sec-mooc-1.cs.helsinki.fi/password-checker/l43f4/PasswordChecker_l"
So I downloaded the binary (here base64 encoded) and opened it in BinaryNinja.
Actually here a static disasssembler failes. There were many things built-in, like jumping to a subrouting and in the subroutine as first thing was that the return address was removed from the stack, so that it will never return and other nice obfuscations. I debugged this code for several hours with gdb until I remembered that last year there was a similar challenge. Could this be the same again? Yes, this file was simply UPX packed! A Linux command line tool unpacks it and returns the unpacked executable. Looking at this unpacked executable again, we now have a much nicer picture in BinaryNinja:
View at the unpacked binary |
And there we already see our flag; "CorrectPasswrdABBAB".
***
***
Comments
Post a Comment