CyberTalents Write-up: Ethiopia National Cybersecurity CTF 2020
How we solved 9 of the 10 challenges in Ethiopia's first national CTF.
Yesterday, I participated in the National Cybersecurity CTF hosted in Ethiopia by CyberTalents. Considering it was my first CTF, as well as my having only one other team member and missing the training sessions given by CyberTalents, I was very satisfied with getting 4th place out of 37 teams.
The format was 10 challenges from easy to hard over 7 hours, testing General Information, Digital Forensics, Web Security, Cryptography, and Malware Reverse Engineering. Here are write-ups for the 8 we solved during the competition plus one we got immediately after. Number 10 was way out of my league.
Cracker — General Information
A simple question asking for the name of a popular Linux tool used as a “packet sniffer, WEP and WPA/WPA2-PSK cracker”.
The answer is obviously aircrack-ng.
Unprotected — Digital Forensics
We were given Unprotected.pcap. Opening it in Wireshark we can see it’s a combination of TCP and HTTP packets.

A simple filter data.data contains "flag" leaves just one TCP packet, which contains the flag flag{cl3ar_t3xt_15_alway35_5asy}.

Encrypted RSA — Cryptography
We were given an RSA-encrypted file secret plus the p, q, and e used to encrypt it. Enough to decrypt. A fast search on crypto.stackexchange.com gave us this script:
def egcd(a, b):
x, y, u, v = 0, 1, 1, 0
while a != 0:
q, r = b // a, b % a
m, n = x - u*q, y - v*q
b, a, x, y, u, v = a, r, u, v, m, n
gcd = b
return gcd, x, y
def main():
p = 11882546252751469607361356421348933496327112595288260315935663917400681403905188808476289112967043136936045873689827577396206505769293138372274271493958287
q = 10374751834382966611285517450958269115435289482194774831009591093240922739864785750413607023913149510232252798244495377789107452564252835088008933746132847
e = 65537
cipher_text = int.from_bytes(open("secret","rb").read(), byteorder="big")
n = p * q
phi = (p - 1) * (q - 1)
gcd, a, b = egcd(e, phi)
d = a
pt = pow(cipher_text, d, n)
print("plain text: " + str(int.to_bytes(pt, 128, byteorder="big")))
main()
The output: a series of null bytes followed by Nice Job, flag is FLAG{Gr3at_J0b_F0r_Th3_D3crypti0n}.

Gu55y — Web Security
We were given a URL to exploit. Its functionality was to take your inputs and store them in your cookies as a serialized PHP list, so they would re-display on refresh.

Crossing out SQL injection as a vector, we inspected the page source and found an HTML comment reading <!-- I love vim~ -->. Knowing vim was involved, we searched for known vim file extensions like .php~ and .php.un~. We managed to download .index.php.swp, which gave us this code:
# try to read fl4g.php
class l33t {
public function __toString() {
return highlight_file($this->source, true);
}
}
This gave us two things: the location of the flag and the vector to get it. We constructed a l33t object with its source set to fl4g.php, serialized it in a list, URL-encoded it, and stuck it in our list cookie.
Serialized: a:1:{i:0;O:4:"l33t":1:{s:6:"source";s:8:"fl4g.php";}}
URL-encoded: a%3A1%3A%7Bi%3A0%3BO%3A4%3A%22l33t%22%3A1%3A%7Bs%3A6%3A%22source%22%3Bs%3A8%3A%22fl4g.php%22%3B%7D%7D


Refreshing the page gives us the flag flag{5w337_PHP_0bj3c7_!nj3c7!0n}.
Habibamod — Digital Forensics
We were given another packet capture, Habibamod.pcap. In Wireshark we can see it’s an HTTP session where a file is being uploaded.

The file contents were a JSON object with data and encoder properties. Decoding the encoder field as base64 (the == at the end was the giveaway) gave us a Python function my_encoder. After analyzing it, we wrote a reverse:
def my_encoder(data):
bin_rep = ''.join(format(ord(i), 'b') for i in x)
return bin_rep.replace('0', '.').replace('1', '!')
def our_decoder(data):
# the previous function converts a string to a bitmap
# and then changes 0 and 1 to . and ! respectively
# reverse the string replacement
data = data.replace('.', '0').replace('!', '1')
# change the bitmap to numbers
data = [int(data[i:i+8], 2) for i in range(0, len(data), 8)]
# change the numbers to a string
return ''.join([chr(i) for i in data])
Running this on data gave us the flag Flag{TMCTFy0urDec0de0f!@nd.Is@ma7ing}.

GoldASM — Malware Reverse Engineering
We were given an assembly file GoldASM.asm that described a function with many repetitions of:
mov rax, QWORD PTR [rbp-24]
mov eax, DWORD PTR [rax]
cmp eax, 70
jne .L2
mov rax, QWORD PTR [rbp-24]
add rax, 4
mov eax, DWORD PTR [rax]
cmp eax, 76
jne .L2
The code was walking across a string and comparing each byte against hardcoded values. Stripping the repetition makes it obvious:
0:
compare eax, 70 ; 'F'
4:
compare eax, 76 ; 'L'
Some positions did arithmetic before comparing:
8:
add eax, eax
compare eax, 130 ; we want 65, or 'A'
24:
subtract eax, 75
compare eax, 2 ; we want 77, or 'M'
After accounting for the edge cases, we got [70, 76, 65, 71, 123, 95, 75, 51, 101, 98, 95, 48, 110, 95, 83, 104, 49, 110, 105, 110, 103, 95, 125]. Converted to characters: FLAG{_K3eb_0n_Sh1ning_}.
I don’t have screenshots of the other challenges — they were web-based and CyberTalents took them down when the hackathon ended.
All in all this hackathon was a lot of fun and I’m interested in doing it again. Next time I’ll make sure to document the whole thing.
First post — no recap to show.