🚩 Insomni'hack 2022 CTF write-up
Tags: ctf, assembly, cryptography, challenge
Published: 2022-03-28 18:59:00

[**Original post**](https://krypton.ninja/Insomnihack-2022-CTF-write-up)

The Insomni'hack 2022 CTF is a CTF hosted during the Insomni'hack conference in Geneva, Switzerland. You had to register yourself so that you can attend to the on-site CTF. There was a total of 31 challenges. As a beginner in CTFs I decided to mostly take the easy challenges. The CTF was from March 25 (6:15pm local time) to March 26 (4am local time) 2022.
As my first on-site CTF it was nice to see how many people were participating. It was also pretty amazing to see how some people were prepared; some people even decided to transport their own _big_ monitor(s) 🤯 It was an overall amazing CTF, so many thanks to the organizers!
All flags had the same format which was `INS{...}`. Anways, enough talking, here are the write-ups:

## 🐛 GDBug - 120 points
We were given a binary file (<a href="https://krypton.ninja/static/Insomnihack-2022-CTF-write-up/files/gdbug.bin">Download the file</a>) that we had to execute to get the right flag. Let's start by running it with basic `./gdbug`:

When executing the file, a serial number to check needs to be given as an argument. After providing one as argument it would check the serial and return if it was a valid one or not. So let's open up the binary in a disassembler and see how it works.

<img alt="There also was another, invalid, flag in the strings" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6kakuxobtpw0uhhf9pt8.png" height="60%" width="60%">

Looking at the top there was `--debug`, after running the binary with that serial it wasn't a success; <code>INS{Th1<span>$</span>Fl4gSuck<span>$</span>}</code>. There also was a call to `ptrace()`, typical anti-debugging trick. If the binary was to be run from any debugger it would give this flag <code>INS{W0ULDNT-1T-B3-T00-34SY}</code>. Simply NOPing the entire ptrace check was enough to bypass it, now the binary can be ran in a debugger if needed. So let's look at how the check for the serial works:

<img alt="A loop that will iterate over the length of the given serial" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmiaul2ttikwfbeh8wjk.png" height="60%" width="60%">

At first there is a variable `x` initialized with the value `0x539`, then a new variable `i` initialized with the value `0x0`. After that, a loop starts:
- It loops forever until `i` has reached the length of the serial, so it goes over all characters in the serial
- The hexadecimal value of the character is then added to the `x` value
- `i` is incremented by one

***gdb*** also confirmed that it was the hexadecimal value of each character that was added to `x`:

<img alt="The binary was ran with 'AAAAAAA' as serial, 0x41 is the hexadecimal value of A" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/onu8bfbn0r8inb3ttz1z.png">

At the end of that loop there were more checks as you can see from this:

<img alt="This is the last assembly picture for this challenge, no worries.." src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1r47c3bl0iozcel2xp0b.png" height="40%" width="40%">

It will be checked if `x` is equals `0xb38`, so the sum of all hexadecimal values of all characters in the serial must result to `0xb38`. After this check, there are 5 more checks that are really easy to understand:
- The length of the serial must be `0x18`
- At index `0x4`, `0x9`, `0xe` and `0x13` of the serial, there should be a character with hexadecimal value `0x2d`, which is the hexadecimal value of `-`.

A valid serial pattern would look like `xxxx-xxxx-xxxx-xxxx-xxxx`.
Based on these restrictions, we could simply create a bruteforce tool in Python:
```python
from string import ascii_uppercase
import itertools

def iter():
	for s in itertools.product(ascii_uppercase, repeat=24):
		s = list(s)
		s[4] = "-"
		s[9] = "-"
		s[14] = "-"
		s[19] = "-"
		yield "".join(s)

def solve():
    while True:
        for serial in iter():
            x = 0x539 # Setup the variables
            i = 0
            while (True):
                if (i >= len(serial)): # Go over the length of the string
                    break
                x += ord(serial[i]) # Append the hexadecimal value of the current character to 'x'
                i += 1
            print(f"Trying {serial}", end="\r")
            if (hex(0xb38)==hex(x)): # Check if the serial is a valid serial or not
                print(f"Found: INS{{{serial}}}")
                return # Remove this to see all possible flags

solve()
```
[Code](https://gist.github.com/kkrypt0nn/ecd624597e00424bd462e2de583bc6f3)

The output was quite interesting...
```
Found: INS{AAAA-AAAA-AAAA-AAAA-AFZZ}
Found: INS{AAAA-AAAA-AAAA-AAAA-AGYZ}
Found: INS{AAAA-AAAA-AAAA-AAAA-AGZY}
Found: INS{AAAA-AAAA-AAAA-AAAA-AHXZ}
Found: INS{AAAA-AAAA-AAAA-AAAA-AHYY}
Found: INS{AAAA-AAAA-AAAA-AAAA-AHZX}
...
```
The reason was quite simple, **all of them were valid flags**!

<img alt="According to another player, the number is accurate" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mm5cgh2a93q6guxioimv.png" height="50%" width="50%">

## 🤖 Bot Telegram - 75 points
This challenge was my personal favorite. We were given a Telegram bot to chat with, and we had as challenge to exploit it. So at first I tried to execute some of the commands it has. None of them were really surprising when being executed. Then I tried with some random arguments after the command, and boom! When executing the command `/challs leet` the bot returned
> Oops an error occured : (1054, "Unknown column 'leet' in where clause")

The challenge is about an SQL injection. After trying to get the list of tables to perform an `UNION` attack the bot said that whitespaces were not allowed, only one argument was allowed. Fortunately there is an easy bypass, which is to replace all whitespaces with **`/**/`** (comments), it will then be interpreted the same as a whitespace.
The first discovery was that some fields are too long to be sent along in some fields, so using `SUBSTR()` did the trick. The bot ended up leaking the `users` table. 

<img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mykddr1yn6i428mgt0o4.png" height="60%" width="60%">
<img alt="I was unable to fit everything in one single picture..." src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k3p9hw7yx8lat5lz9g7l.png" height="20%" width="20%">

Then we can leak the columns inside that table, there was the column `username` and `password`:

<img alt="There were only 2 columns that could've been guessed" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ck87ozqfmd7cn7vk929k.png" height="80%" width="80%">

So now we can simply leak the credentials for the existing row(s). There was 3 users, one of them had the username `admin` and the password, the flag, <code>INS{C0ngr@tz_YoU_d3<span>$</span>eRvE_T3iS_Fl@g}</code>.

<img alt="woohoo!" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqbokgt86b70ywynkfxj.png" height="80%" width="80%">

Also, dear staff members, sorry for bullying the bot :(

<img alt="They sent these messages exactly when the challenge was solved" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ljcxq8hsfftdc86tkku8.png" height="50%" width="50%">

## 🅰️ Wordle - 9 points

This challenge was based on the popular [worlde game](https://en.wikipedia.org/wiki/Wordle) where you give a word of 5 characters and then you know if a character is in the word to guess, at the correct place or not in it at all.
We were given a netcat server that was listening on port `1337` and we had to exploit the game. We also had a binary file that did exactly the same, so that we can debug it (<a href="https://krypton.ninja/static/Insomnihack-2022-CTF-write-up/files/wordle.bin">Download the file</a>).
My first try was to actually spam the execution of the wordle binary and get the right word at some point since we were given an output that said what the correct word was.
Even when getting the right word, it gave the error message. So I opened the file in a disassembler. The assembly code for the logic was very easy:

<img alt="Some easy to understand Assembly code" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g1e81oj3c1l8coktinhq.png" height="60%" width="60%">

The program moves to the address `0xcb1` if the value in `data_2020e0` is equals `0xdeadbeef`. It will then call the `system()` function with a randomly taken string from the possible words. One of these words was `/bin/sh`, so it was clear there was shell access at the end.

So let's put `deadbeef` as user input? Well, the user input is stored in `data_2020c0`, so that won't work. However, the user input is parsed with the unsafe `gets()` method, which means it is possible to overflow that value.
There is a difference of 32 bytes between both addresses. So a simple overflow of 32 bytes can let me put anything inside `data_2020e0`.
The payload for that overflow would be the following:
```shell
(python2 -c 'print "A"*32 + "\xef\xbe\xad\xde"'; cat) | nc wordle.insomnihack.ch 1337
```
Since there was not only `/bin/sh` in the list of words, the program had to be executed multiple times.
In the end the flag was <code>INS{C0ngr@tulat1on5!_th3_word_was_d3adBeef!}</code>.

## 🌊 Weak Rivest 4 - 7 points
Weak Rivest 4 reminded me of the [RC4 cipher](https://en.wikipedia.org/wiki/RC4). The cipher is using a keystream and is, of course, considered as insecure.
We were given 3 strings, 1 plaintext and 2 ciphertexts:
```
pt1 = I_do_not_like_ponies
ct1 = D058DECB037A10916E9D8B0E5345BB381AE8D8EE
ct2 = D049E9DF182420AB5EC6BD101229940517B59CE0
```
All the strings had the same length, we simply had to convert the `pt1` to its hexadecimal representation `495F646F5F6E6F745F6C696B655F706F6E696573` to also get a length of 40. We had one plaintext, which was encrypted with a key and resulted to one of the cipher text. The flag was most likely the second plaintext (`pt2`), which is unknown.

To solve the challenge there is the [reused key attack](https://en.wikipedia.org/wiki/Stream_cipher_attacks#Reused_key_attack) on stream ciphers, such as RC4. To explain is shortly, XORing both cipher texts will remove the key, which then results to the same when XORing the both plaintexts. So for the challenge:

> ct1 ⊕ ct2 = pt1 ⊕ pt2

So to get `pt2` we simply need to:
- Take every time 2 characters from both `ct1` and `ct2`
- Convert them to a hexadecimal number
- XOR them together to get a `result`
- Get 2 characters from `pt1`
- Find a hexadecimal number that, if XORed with the 2 characters taken from `pt1`, leads to the same `result`
- Append the character corresponding to that value to a `flag` variable.

I ended up making the following Python script:
```python
import time

pt1 = "495F646F5F6E6F745F6C696B655F706F6E696573"
ct1 = "D058DECB037A10916E9D8B0E5345BB381AE8D8EE"
ct2 = "D049E9DF182420AB5EC6BD101229940517B59CE0"

flag = ""

for i in range(0, len(ct1) - 1, 2):
    ct1_pair = int("0x" + ct1[i:i+2], 16) # Get two characters from ct1
    ct2_pair = int("0x" + ct2[i:i+2], 16) # Get two characters from ct2
    result = ct1_pair ^ ct2_pair
    for k in range (0x0, 0x7f): # Loop over all characters in the ASCII table
        if (int("0x" + pt1[i:i+2], 16) ^ k) == result: # Check if the XORed number is the same as the result
            flag += chr(k)
            print(flag, end="\r")
            time.sleep(0.5) # That is just to look cool 😎
            break
```
[Code](https://gist.github.com/kkrypt0nn/b59f814cc800a402ab510a693efcda6c)

This resulted in the following flag: <code>INS{D0_No7_u<span>$</span>3_Rc4!}</code>

## 🔮 Magic Words - 7 points
For this challenge we were given a link to a website. The website asked for a sentence, so basically we had to get this sentence. When looking at the source code there was some checks, so the first thing to do was to create the same file on the local machine.
Then simply take each check one by one and get the words in each paragraph. Then recreate the checks locally and execute the script.
```js
// More code above...

// This is the needed code
p1 = 'Later, on my walk, I wondered why I felt I had to be suspicious of ‘normality’. The striking thing about the normal is that there is nothing normal about it: normality is the gentrification of ordinary madness – ask an Surrealist. In analysis ‘the normal child’ is often synonymous with the obedient good child, the one who only wants to please parents and develops what Winnicott called ‘a false self’';
p2 = 'In the hospital men’s room, as I’m washing my hands, I glance in the mirror. The man I see is not so much me as my father. When did he show up? There is no soap; I rub hand sanitizer into my face–it burns. I nearly drown myself in the sink trying to rinse it off.';
p3 = 'Your only chance of survival, if you are sincerely smitten, lies in hiding this fact from the woman you love, of feigning a casual detachment under all circumstances. What sadness there is in this simple observation! What an accusation against man! However, it had never occurred to me to contest this law, nor to imagine disobeying it: love makes you weak, and the weaker of the two is oppressed, tortured and finally killed by the other, who in his or her turn oppresses, tortures and kills without having evil intentions, without even getting pleasure from it, with complete indifference; that’s what men, normally, call love.';
p4 = 'Looking back on those incidents, he was always appalled by the memory of his passivity, hard though it was to see what else he could have done. He could have refused to pay for such gravy damage to his room, could have refused to change his shoes, could have refused to kneel to supplicate for his B.A.';
p5 = 'The way he went after that plump sister in the lace tucker, was an outrage on the credulity of human nature. Knocking down the fire-irons, tumbling over the chairs, bumping against the piano, smothering himself among the curtains, wherever she went, there went he. He always knew where the plump sister was. He wouldn’t catch anybody else. If you had fallen up against him (as some of them did), on purpose, he would have made a feint of endeavouring to seize you, which would have been an affront to your understanding, and would instantly have sidled off in the direction of the plump sister. She often cried out that it wasn’t fair; and it really was not.';

var response = '';

var words = p1.split(' ');
for (var i = 0; i < words.length; i += 1) {
    if (words[i] == 'please') response += words[i];
}

var words = p2.split(' ');
for (var i = 0; i < words.length; i += 1) {
    if (beautify(words[i]) == 'siqz') response += ' ' + words[i];
}

var words = p3.split(' ');
for (var i = 0; i < words.length; i += 1) {
    if (uglify(words[i]) == 'MDA=') response += ' ' + words[i];
}

var words = p4.split(' ');
for (var i = 0; i < words.length; i += 1) {
    if (digitalize(words[i]) == '746865') response += ' ' + words[i];
}

var words = p5.split(' ');
for (var i = 0; i < words.length; i += 1) {
    if (mystify(words[i]) == 'c83b72dd001482ce10f0b106c7a0ed0e') response += ' ' + words[i];
}

console.log(response);
```
[Code](https://gist.github.com/kkrypt0nn/8743a257eed02449857313a819632bdc)

Then the result was `please show of if in of is in an it to me to to of is by in or the way`. There was just to find a good word that would fit in the sentence for `pleas show [of if in of is in an it to me to to of is by in or] the way`, the best is definitely `please show me the way`.

When submitting that, the flag (<code>INS{jQu3ry_1<span>$</span>_Fr3@kiNg_C0oL}</code>) was given as response.

## 👋 Welcome challenge - 6 points
We get a link to a GitLab repository where there are instructions to follow to actually solve the challenge which included commiting changes.
After folowing the steps the first time my shell looked kind of, weird:
```
You are infected 🧟 $
```
But I wasn't the only one who got this joke:

<img alt="The staff was building a botnet, no worries" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vw0xbf9ak06ijmgqnz8q.png">

After checking where this came from, it was located in `inso/.circleci/pre-commit`, there also was a `exec $SHELL` command at the end of the file, to make sure you see that. By simply removing the lines that changed my `PS1` variable and removing the `exec $SHELL` command, I was able to get the flag in return.
The flag was given with a response to a failed commit like the following: <code>remote: INS{S0_F4r_S0_g00d}</code>.