This CTF challenge consisted of 3 levels. Them being Easy, Medium and Hard. They consisted of a wide variety of cyber security scenarios such as
- Steganography
- Cryptography
- Wireless
- SQLi and etc.
The CTF challenge can be found at this link
To view the writeup for the medium level click here
To view the writeup for the medium level bonus round click here
The landing page for the CTF hard level was
Hard - Challenge 20 - "'Krack' it people!!"
This challenge gives us a flag.zip file and asks us to crack it.
So let's crack it.
We can use any tool such as "fcrackzip" to do this
Also since many CTFs use rockyou.txt because it is an defaulty included in Kali Linux let's use
it as the wordlist
So after a while we can find out that the password is "israelteamo"
We can open the zip to find out the txt file which contains the flag
DSICTS{israelteamo}
Hard - Challenge 21 - "Did you recognize me??"
This challenge is designed to bring the contestents to ground level after spending so much time
in the Sky Level trying advanced hacking techniques
for example I spotted a contestant using SQL injection against a challenge when the password and
the username was simply hidden on the web page by the css white color tag.(easy cahllenge
01)
Answer is simple
It is the letters in the image
So we will get
dsicts{asdljhasduu8d7981*9()-asd=-0=-0asd)_+sdas)dashbjcdljbh*7dasbas89d*(&agaleliolksjdl;knkljsadsl;kmasdmahanaskdajas77&s}
Hard - Challenge 22 - "Serious Transmissions!"
This challenge is somewhat hard. This was inspired by a recent CTF challenge.
And only 20 teams out of 1378 teams solved this.
So good luck to those who tried this.
The challenge tells us..
"Our radio team has intercepted a seemingly weird transmission transmitting from an unidentified radio station located in North Pole. Please decode it for us, it seems important"
and it give us this audio file
When we play this, a male voice greets us but we can't understand a thing that he says. So let's
get into it.
We have to reverse the audio in order to find the next clue.
The reversed audio file is given below
Transcribing this audio will give..
"Dear CTF player, Welcome to D S I C T S hacking challenge HARD level. you have exploited using
SQLi, deciphered dank horse cryptography, hacked your way into WPS protected WIFI and done some
forensic analysis with Googal, now another challenge awaits.
The evil Grinch is holding Santa hostage in an undisclosed location, we don't know his exact
position.
A nice elf has told us the coordinates of nearby points.
The first one is located at
(511716656388765455430016138955706839007890052532 . comma .
1622805609316535864254436412730925222158623332074)
The second one is at
(390390142500834541752332649936545354218395003257 . comma .
176460719206642987153469086794475382972064519404)
And the last one is at
(608097554835704767294367078594102923662585120876 . comma .
195121033653477539025103641752423493583135321761)
He also told us that Santa's home will be located, where the shape formed by these three
points is in complete equilibrium.
Good luck and Happy Hunting.
And also enjoy the DSICTS hacking challenge. We hope that you win."
Plotting those coordinates to world’s map will give us nothing and just a clear rabbit
hole
If you request a hint it will tell you that this is a geometry challenge
So get on Google and search for formulas related to Geometry
You will find analytic Geometry’s formula in calculating the centroid, mainly because it uses
the given coordinates of the three vertices of a triangle
You can also find this "centroid" in the Local A/L combined Maths syllabus in 'co-ordinate
geometry'
centroid = ((x1 + x2 + x3)/3, (y1 + y2 + y3)/3);
We can use the following formula to find the centroid of the given co-ordinates from the audio
x1 = 511716656388765455430016138955706839007890052532
x2 = 390390142500834541752332649936545354218395003257
x3 = 608097554835704767294367078594102923662585120876
y1 = 1622805609316535864254436412730925222158623332074
y2 = 176460719206642987153469086794475382972064519404
y3 = 195121033653477539025103641752423493583135321761
centroid = ((x1 + x2 + x3)/3, (y1 + y2 + y3)/3);
The result is (503401451241768254825571955828785038962956725555, 664795787392218796811003047092608032904607724413)
The value of centroid above is in decimal format (base 10) and we have to convert it to HEX
(base 16) and convert it to ASCII to get the flag.
Flag: X-MAS{An4ly71c_G30m3try_S4v3d_Chr157m4s}
Hard - Challenge 23 - "Hybrid Pads"
The challenge tells us..
"One member of our CTF team made a unforgivable mistake using multi time pad. He definitely knew that the OTP if it was well applied is unbreakable. But this brainless retard went nuts. Not only that, he deleted the original plain text. So now we are screwed. The only thing remaining is the following file: cipher.json It contains multiple cipher text that was created using OTP and the same key So can you figure out the plain text of the cipher_flag ?"
This is a cryptography challenge about one-time pad (OTP). OTP is said to be uncrackable as long as you keep the messages short, use shorthand and abbreviations, remove unnecessary letters, never reuse a pad, and have a good enough random source for data. The challenge gives us a "cipher.json" file which contains 11 instances of cipher_list and a cipher_flag all in hex format.
{ "cipher_list": [ "1b0605000e14000d1b524802190b410700170e10054c11480807001806004e4f1f4f01480d411400531158141e1c100016535a480c000c031a000a160d421e004113010f13451e0c0100100a020a1a4e165f500d0c1e041a090b001d0515521c0a0410000a4f4b4d1d1c184d071600071c0a521d1706540940", "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f0c8d4fc7520211531b0b0c1e4f", "1d0c04451352001a000154431b014109450a0a0b000045490403520a1d16490008535848085942071c0d0c57101c0045111c40430c4e111c0b1b1c451d4f071712010508475518061d00060a1b0a1a4c165d", "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e164e1d060d0d44541a0041031b0b06540d1a070004001d4b074800531c04101d4f", "1a1d524912521548120045021b4e1506490a0859150345531d12521b4e094909030003011148420453074d161e05540b071e4c451b000a084a1d1c04084c0b45060b060a4742070618534218070210484512020043100e191e5956111a1c001c1f0b5c", "1a1d5248000154041a1c47430d0b04000005015900140c4f04534f094e08490103000000045442111b11001b1b1d000917535a48004e021d4a0e0b0044491c03080a001a024c11490748074f02040054451a1d150c1b150d020d0e", "1a1d5249125215481613500a1b0f0d4e4d0d1c0d000700001d1c001b06004f1d0f5a11480745040a011100181c0c540d13000e44085404404a061716014e010c0308104e084e0d4911450506011853540a5304120a1a154c0a1843001b45541c481607051b431f480d001e0400000c531d01011d00124441010200190d0800000000000e54060001100a1b4d0b040d105347", "0a0607000913020d551300041d0f0f0a0003061f154c034f1b53530602004e0c030c541f0454110a1d5a001e0649190419165d00104f104e1b1a101101001b0b1705051b0642040c5341114f0e4b104f0803110b0a060f42", "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e16424a1810110c00060d04440e1c02411c0c00544209001953540d165009021a1542", "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f4201001f534b0b1c074b", "1a49134d4113540a0713490d434e160f541700174f4c11480c53520a1d1100000000190d4549114512544d12000c540402034b4e0d491d40" ], "cipher_flag": "1a4905410f06110c55064f430a00054e540c0a591603174c0d5f000d1b110006414c1848164516111f1100111d1b54001c17474e0e001c011f1d0a4b" }
However, on this challenge something is wrong as same key was reused more than once - which I
then used to break the encryption. The attack for this is called many time pad attack. To
understand how this attack works, below is a short explaination of it.
Note:
⊕ is bitwise xor operator. This symbol means to take the xor individually of each bit pair of
the message. For example, we have two messages (C1 and C2), and you have a key (K). For OTP
encryption, you would simply do:
C1 ⊕ K = Encrypted
And that will encrypt the original message.
If you then send the Encrypted to someone, and given that they have the key, they would simply
do:
Encrypted ⊕ K = C1
And that will give them the original message.
But in case we have:
Encrypted1 = C1 ⊕ K and Encrypted2 = C2 ⊕ K
Now this gets interesting, as we can XOR the two pencrypted text together which will completely
remove the key from the equation.
In short:
Encrypted1 ⊕ Encrypred2 = C1 ⊕ K ⊕ C2 ⊕ K = C1 ⊕ C2
All that is left for us is to guess a part of one of the messages, which would in turn give us
the corresponding part of the other message.
In summary, in order to attack this encryption and uncover the secret plain text message, we
would need to:
- Guess a word that would probably appear in one of the messages
- Encode the word into a hex string
- XOR the two cipher text messages
- XOR the hex string at each position of the XOR of the two cipher text
- When the result is readable text, we guess the word and expand our crib search
- If the result is not readable text, we try a XOR of the crib word at the next position
import string import collections import sets import sys # XORs two string def strxor(a, b): # xor two strings (trims the longer input) return "".join([chr(ord(x) ^ ord(y)) for (x, y) in zip(a, b)]) #11 cipher text from cipher_list c1 = "1b0605000e14000d1b524802190b410700170e10054c11480807001806004e4f1f4f01480d411400531158141e1c100016535a480c000c031a000a160d421e004113010f13451e0c0100100a020a1a4e165f500d0c1e041a090b001d0515521c0a0410000a4f4b4d1d1c184d071600071c0a521d1706540940" c2 = "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f0c8d4fc7520211531b0b0c1e4f" c3 = "1d0c04451352001a000154431b014109450a0a0b000045490403520a1d16490008535848085942071c0d0c57101c0045111c40430c4e111c0b1b1c451d4f071712010508475518061d00060a1b0a1a4c165d" c4 = "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e164e1d060d0d44541a0041031b0b06540d1a070004001d4b074800531c04101d4f" c5 = "1a1d524912521548120045021b4e1506490a0859150345531d12521b4e094909030003011148420453074d161e05540b071e4c451b000a084a1d1c04084c0b45060b060a4742070618534218070210484512020043100e191e5956111a1c001c1f0b5c" c6 = "1a1d5248000154041a1c47430d0b04000005015900140c4f04534f094e08490103000000045442111b11001b1b1d000917535a48004e021d4a0e0b0044491c03080a001a024c11490748074f02040054451a1d150c1b150d020d0e" c7 = "1a1d5249125215481613500a1b0f0d4e4d0d1c0d000700001d1c001b06004f1d0f5a11480745040a011100181c0c540d13000e44085404404a061716014e010c0308104e084e0d4911450506011853540a5304120a1a154c0a1843001b45541c481607051b431f480d001e0400000c531d01011d00124441010200190d0800000000000e54060001100a1b4d0b040d105347" c8 = "0a0607000913020d551300041d0f0f0a0003061f154c034f1b53530602004e0c030c541f0454110a1d5a001e0649190419165d00104f104e1b1a101101001b0b1705051b0642040c5341114f0e4b104f0803110b0a060f42" c9 = "160d074300061d071b524e06190b134e450a0b0a4d4c12411d004f014045491b4649074804001100011d4504520612451e165d53064e16424a1810110c00060d04440e1c02411c0c00544209001953540d165009021a1542" c10 = "1e10524e001f11481c010010070b13024f0704590903094d0c000e4f0711000615001911454217161a1a45040149000a5218404f1e0012060b1b590a1048171741140c01174c0d49174f4201001f534b0b1c074b" c11 = "1a49134d4113540a0713490d434e160f541700174f4c11480c53520a1d1100000000190d4549114512544d12000c540402034b4e0d491d40" ciphers = [c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11] # The target ciphertext we want to crack target_cipher = "1a4905410f06110c55064f430a00054e540c0a591603174c0d5f000d1b110006414c1848164516111f1100111d1b54001c17474e0e001c011f1d0a4b" # To store the final key final_key = [None]*150 # To store the positions we know are broken known_key_positions = set() # For each ciphertext for current_index, ciphertext in enumerate(ciphers): counter = collections.Counter() # for each other ciphertext for index, ciphertext2 in enumerate(ciphers): if current_index != index: # don't xor a ciphertext with itself for indexOfChar, char in enumerate(strxor(ciphertext.decode('hex'), ciphertext2.decode('hex'))): # Xor the two ciphertexts # If a character in the xored result is a alphanumeric character, it means there was probably a space character in one of the plaintexts (we don't know which one) if char in string.printable and char.isalpha(): counter[indexOfChar] += 1 # Increment the counter at this index knownSpaceIndexes = [] # Loop through all positions where a space character was possible in the current_index cipher for ind, val in counter.items(): # If a space was found at least 7 times at this index out of the 9 possible XORS, then the space character was likely from the current_index cipher! if val >= 7: knownSpaceIndexes.append(ind) #print knownSpaceIndexes # Shows all the positions where we now know the key! # Now Xor the current_index with spaces, and at the knownSpaceIndexes positions we get the key back! xor_with_spaces = strxor(ciphertext.decode('hex'),' '*150) for index in knownSpaceIndexes: # Store the key's value at the correct position final_key[index] = xor_with_spaces[index].encode('hex') # Record that we known the key at this position known_key_positions.add(index) # Construct a hex key from the currently known key, adding in '00' hex chars where we do not know (to make a complete hex string) final_key_hex = ''.join([val if val is not None else '00' for val in final_key]) # Xor the currently known key with the target cipher output = strxor(target_cipher.decode('hex'),final_key_hex.decode('hex')) # Print the output, printing a * if that character is not known yet print ''.join([char if index in known_key_positions else '*' for index, char in enumerate(output)]) ''' Manual step ''' # From the output this prints, we can manually complete the target plaintext from: # The secuet-mes*age*is: Wh** usi|g **str*am cipher, nev***use th* k*y *ore than onc* # to: # The secret message is: When using a stream cipher, never use the key more than once # We then confirm this is correct by producing the key from this, and decrpyting all the other messages to ensure they make grammatical sense target_plaintext = "i wanted to end the" print target_plaintext key = strxor(target_cipher.decode('hex'),target_plaintext) for cipher in ciphers: print strxor(cipher.decode('hex'),key) print "\n===========\nRecovered key: "+key+"\n===========\n"
The result is
* *anted to ind t** y*rl*, but i'*l*settle for endin* ****s. i wanted to end the how often have i sa my name is sherlock never trust to gene education never end it is a great thing it has long been an it is a capital mis you have a grand gi education never end my name is sherlock i am a brain, watso =========== Recovered key: sir arthur conan do ===========
By using the script and modifying the target plaintext value to match the correct words by
guessing intelligently, would result on finding out that the target plaintext is a quote by Sir
Arthur Conan Doyle.
I wanted to end the world, but I'll settle for ending yours.
Flag is
radio{i wanted to end the world, but i'll settle for ending yours.}
Level: Medium
This was the landing page for the CTF medium level
Medium - Challenge 12 - "WEP!"
The first challenge in the medium level is to find the password of a WIFI network protected with
WEP encryption.
This can be done easily with Aircrack-ng suit and similar tools, therefore I'm not going to
explain this to you. You may refer one of the numerous articles in the web such as this.
Medium - Challenge 13 - "Packets"
Provided with the captured packets, we can check for the flag in pcap dumps.
Opening the packet dump in Wireshark, we can find 31 packets captured
When we start sifting through the packet we can find a username logging in with username as Jerry
and password as saymynameheisenberg
There you have it
DSICTS{saymynameheisenberg}
Medium - Challenge 14 - "Steg Horse"
The next challenge consists of two parts.
Steganography and cryptography
1st part
You will be given the following image
As the challenge informs us the first part is a steg challenge.
Steganography is the art and science of concealing messages within other messages, documents, or
specific file formats, this is the official definition.
The first step is to find any visible or invisible clues in the picture.
Analyzing the codec comes next.
So import the image into any steg-solving tool such as "Stegsolve", "Quick Stego" etc.
Stegsolve is a java based tool that lets you switch between various colour filters
quickly.
And so we get the following deciphered image.
Somehow 'd' appears as 'c'. So you will have a hard time. So to prevent it I also added a site called cank.horse in the challenge so competitors won't waste time on guessing that 'c' is actually 'd'
So now you can go to ../dank.horse
You may not go straight to '../dank.horse/flag.pdf' you will instead go to
'../dank.horse'
Idk why. But it is tempting to do in a CTF.
So let's go to '../dank.horse'
You will land on a decoy image made to distract you. However you will jump in to Stegsolve and
waste about 10 minutes till you decide that this is actually a hose.
Then you will go to the real url '../dank.horse/flag.pdf'
Don't get discouraged. Just download the pdf and start analyzing.
The subject has a link that leads to a .zip file 😀 Let’s look at it…
'../dank.horse/flag.zip'
When you unzip it you will get another 'flag.zip'. However you will notice that this is
corrupted
Then the most sensible path is to use a tool to compare the two
'flag.pdf' files and find something out. But you won't find anything (guranteed 😀)
Then we must try strings and find anything hidden in the codec. But again you won't find
anything (guranteed again 😀)
So now we only have to analyze the 'flag.zip' file.
Ahoy!. We got something. So let's try to find out the encoding type of this string.
When we analyze is using a service such as "Hash analyzer" we will find out that this is in
base64
But if you decode it using that way you will get Unicode characters.
So the deduction is this is the "WRONG WAY".
So let's take a look at the first steg picture for a clue.
In the second line of the deciphered image we
have
32(16)
32 means base32 and 16 means base16. We know that base16 is hex.
So we need to decode the base32..
Now we just have to decode the base16(HEX) of it
cdc{empty_pdf_zip_hidden_in_plain_sight}
Medium - Challenge 15 - "Flags everywhere!"
The challenge give us this..
While Sira was looking through his letters he came across a very strange one. Help him decode
the the letter in order to fulfill his wish.
and a image.
Following is the last 8 lines in it.
The first look at this will give us an understanding that this is substitution cipher.
So basically you need to find something you know, and then substitute to other lines to deduce
the meaning.
You can just bruteforce this by using a tool, but the real fun in solving a sub-cipher can only
be obtained by solving it in the old fashioned way.
Guessing..!!
The whole challenge depends on one point.
Identifying the line which we already know.
This step will require some hardcore guessing and patience.
How ever that line is the one above the last line.
As we see there are 3 words.
So we can deduce that this is "The flag is"
So now just substitute it to the last line
Flag: X-MAS{youaresogoodatsubstitutionciphers}
Medium - Challenge 16 - "Amnesic"
We get a file named amnesic.txt which is hard to open with a text editor
This is not a text file.
This is actually an image file. we need to rename it to amnesic.jpg to view it
Binwalk the image to get secret.txt embedded inside the image.
With binwalk extract the file embedded in the image,
we get a secret.txt with the file.
The flag will be
DSICTS{wowyouaregoodatthis}
Medium - Challenge 17 - "Hexadecimal"
We are given the cyphertext
2e2f3a2631231e1c2a10450d241320452445022a0a2145210c06112c0a0b24171c18
With the Description, you can decide that it is a Single byte XOR which is encrypted using a key
Hence get the key thus receiving the key and the same key is used to decrypt rest of
string.
flag is
KJ_CTF{yOu hAvE A gOoD DictIonAry}
Medium - Challenge 18 - "Router!"
This is one of the easiest of the whole competition
We get the login page of the router
We can try in the default values such as
- user admin
- admin password
- admin admin
- user user
In this case it is
admin admin
Medium - Challenge 19 - "Decipher"
This challenge gives us a enc.txt file
The ciphertext in enc.txt is only provided with jumbled text, neither of the
keys was available nor the algorithm is known.
Which makes us conclude it maybe Base64 encoding, Caeser cipher or Rot13 encoding.
The ciphertext is not having alphanumeric characters and the = padding at end of
the string is also missing which takes base64 out of guess.
Caesar cipher can be tested with all possible 26 keys but none of the outcomes will be
meaningful.
Coming to Rot13, when we decode the ciphertext, we get another set of meaningless ciphertext but when you search for DSICTS{, you can find the flag in the decoded text.
DSICTS{crypto_first_flag}
Medium - Bonus
This is an exciting challenge since it is a kind of clue hunt
This challenge was taken from http://www.pythonchallenge.com/
The first page gives us
So as the hint says lets change URL to 238..
Oops.. On second thoughts it actually is 238. 238 equals to 274877906944. So lets apply 274877906944
I is correct. And the challenge redirects us to another one named "map"
After some time you will notice that the challenge refers to a substitution cypher.
Where K becomes M and O becomes Q and so on..
If you are familiar with this field you will notice right away this is the caesar's shift on the key 2
Or simply put this scheme simply shifts the original by two characters to encrypt the text. So if
we encrypt "abcd" we will get "cdef".
So if we input
g fmnc wms bgblr rpylqjyrc gr zw fylb. rfyrq ufyr amknsrcpq ypc dmp. bmgle gr gl zw fylb gq glcddgagclr ylb rfyr'q ufw rfgq rcvr gq qm jmle. sqgle wmsp mul ambc gq pcamkkclbcb. lmu ynnjw ml rfc spj.
we will get..
ihopeyoudidnttranslateitbyhandthatswhatcomputersarefordoingitinbyhandisinefficientandthatswhythistextissolongusingyourowncodeisrecommendednowapplyontheurl
Just don't do this by hand. Automate it. It is a simple code
However let's apply it on the URL
So "map" becomes "ocr"
So let's view the page source
There is a mess of characters. But if we look closely, most of them are special characters such
as !@#$ etc.
We just have to filter out the alphabetical characters. Just as before
automate the process
Then we get the answer "equality"
No visible clues. So onto the page source..
This is a bit complex than the previous one. We have to find simple characters which are surrounded by THREE CAPITAL letters on each side. Now I don't need to say to automate it.
We will get "linkedlist"
Once again no visible clues. So...
We can see a link linkedlist.php?nothing=12345 So let's pay a visit
So let's feed it to the GET parameter "nothing". Then we get
If you are interested the PHP code I created for this challenge is as follows
<?php session_start(); if(isset($_GET['nothing'])){ $nothing = $_GET['nothing']; if($nothing == "654654") die("peak.php"); if(!isset($_SESSION['nothings'])) $_SESSION['nothings'] = 0; if($_SESSION['nothings']>=400) die("and the next nothing is 654654"); if(isset($_SESSION['current'])){ $num = rand(10000, 99999); if($nothing == $_SESSION['current']) $_SESSION['nothings']++; echo "and the next nothing is $num"; $_SESSION['current'] = $num; }else if($nothing == "12345"){ $num = rand(10000, 99999); $_SESSION['current'] = $num; echo "and the next nothing is $num"; } } // echo "<br/>" . $_SESSION['nothings'] . "<br/>" . $_SESSION['current']; die; } ?> <html> <head> <title>Follow the chain</title> <style> * { font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif; background-color: rgb(51, 51, 51) } </style> </head> <body> <!-- Try urllib in python. DON'T TRY ALL NOTHINGS, since it will never end. 444 times is more than enough. --> <center> <a href="linkedlist.php?nothing=12345"><img src="./imgs/chainsaw.jpg"/></a> </center> </body> </html>
So the point is you must enter the previous (only the previous not the previous of the previous; Yaa it's a bit confusing) to the current page and so on for 400 times
This challenge was specifically created to block out those contestents who didn't automate the processes and those who tried to bruteforce their way out
Now we got peak.php
The challenge title is peak hell. If we pronounce it repeatedly it sound like
peak hell.. peakhell... peakell ... pickle. Aha pickle
Ooops; dead end. Looks like we are in a bit of a 'pickle' here aren't we??
In the page source of the peak hell page we can find a perl file
Lets open the benner.p
If you look again we know it. "pickle" is the Python's object serialization module. So let's deserialize it using pickle
import pickle data = pickle.load(urlopen("http://127.0.0.1/Hacking_Challenge/Medium/bonus/banner.p")) print(data)
The output is
[[(' ', 95)], [(' ', 14), ('#', 5), (' ', 70), ('#', 5), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 15), ('#', 4), (' ', 71), ('#', 4), (' ', 1)], [(' ', 6), ('#', 3), (' ', 6), ('#', 4), (' ', 3), ('#', 3), (' ', 9), ('#', 3), (' ', 7), ('#', 5), (' ', 3), ('#', 3), (' ', 4), ('#', 5), (' ', 3), ('#', 3), (' ', 10), ('#', 3), (' ', 7), ('#', 4), (' ', 1)], [(' ', 3), ('#', 3), (' ', 3), ('#', 2), (' ', 4), ('#', 4), (' ', 1), ('#', 7), (' ', 5), ('#', 2), (' ', 2), ('#', 3), (' ', 6), ('#', 4), (' ', 1), ('#', 7), (' ', 3), ('#', 4), (' ', 1), ('#', 7), (' ', 5), ('#', 3), (' ', 2), ('#', 3), (' ', 5), ('#', 4), (' ', 1)], [(' ', 2), ('#', 3), (' ', 5), ('#', 3), (' ', 2), ('#', 5), (' ', 4), ('#', 4), (' ', 3), ('#', 3), (' ', 3), ('#', 4), (' ', 4), ('#', 5), (' ', 4), ('#', 4), (' ', 2), ('#', 5), (' ', 4), ('#', 4), (' ', 3), ('#', 3), (' ', 5), ('#', 3), (' ', 3), ('#', 4), (' ', 1)], [(' ', 1), ('#', 3), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 3), (' ', 4), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 6), ('#', 4), (' ', 2), ('#', 4), (' ', 1)], [(' ', 1), ('#', 3), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 10), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 7), ('#', 3), (' ', 2), ('#', 4), (' ', 1)], [('#', 4), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 5), ('#', 2), (' ', 3), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 7), ('#', 3), (' ', 2), ('#', 4), (' ', 1)], [('#', 4), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 10), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 14), (' ', 2), ('#', 4), (' ', 1)], [('#', 4), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 4), ('#', 4), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 12), ('#', 4), (' ', 1)], [('#', 4), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 5), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 12), ('#', 4), (' ', 1)], [(' ', 1), ('#', 3), (' ', 11), ('#', 4), (' ', 5), ('#', 4), (' ', 1), ('#', 4), (' ', 5), ('#', 3), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 12), ('#', 4), (' ', 1)], [(' ', 2), ('#', 3), (' ', 6), ('#', 2), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 3), (' ', 4), ('#', 4), (' ', 4), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 3), (' ', 6), ('#', 2), (' ', 3), ('#', 4), (' ', 1)], [(' ', 3), ('#', 3), (' ', 4), ('#', 2), (' ', 3), ('#', 4), (' ', 5), ('#', 4), (' ', 3), ('#', 11), (' ', 3), ('#', 4), (' ', 5), ('#', 4), (' ', 2), ('#', 4), (' ', 5), ('#', 4), (' ', 4), ('#', 3), (' ', 4), ('#', 2), (' ', 4), ('#', 4), (' ', 1)], [(' ', 6), ('#', 3), (' ', 5), ('#', 6), (' ', 4), ('#', 5), (' ', 4), ('#', 2), (' ', 4), ('#', 4), (' ', 1), ('#', 6), (' ', 4), ('#', 11), (' ', 4), ('#', 5), (' ', 6), ('#', 3), (' ', 6), ('#', 6)], [(' ', 95)]]
OK! So data is a list of lists of tuples... It does not look like a banner(the file name), but more like the result of a simple string compression: convert the repetitive characters to tuples of (character, number of appearance). This level is all about serialization and compression, isn't it. Let's unravel the string:
If you are not in a hurry you can decypher this by hand. (Just the first few, for FUN)
If we get the first list it says ' ' character for 95 times
Then ' ' character for 14 times
Then '#' character for 5 times. an so on...
But let's devise a code..
for line in data: print("".join([k * v for k, v in line]))
The output will be
##### ##### #### #### #### #### #### #### #### #### #### #### #### #### #### #### ### #### ### ### ##### ### ##### ### ### #### ### ## #### ####### ## ### #### ####### #### ####### ### ### #### ### ### ##### #### ### #### ##### #### ##### #### ### ### #### ### #### #### ### ### #### #### #### #### ### #### #### ### #### #### ### #### #### #### #### ### ### #### #### #### #### ## ### #### #### #### #### #### ### #### #### #### #### ########## #### #### #### #### ############## #### #### #### #### ### #### #### #### #### #### #### #### #### #### #### #### ### #### #### #### #### #### #### ### #### #### #### ### #### #### #### #### ### #### ### ## #### #### ### #### #### #### #### #### ### ## #### ### ## #### #### ########### #### #### #### #### ### ## #### ### ###### ##### ## #### ###### ########### ##### ### ######
We got it!! channel. So onto next one
Nothing interesting going on here. Let's view the source
See that <!-- <-- zip --> it must be a clue. So let's apply it to the url. add ".zip" to end
A zip file is downloaded. Let's open it
A readme file is inside. It says to start from 90052. So let's do exactly that
import zipfile, re f = zipfile.ZipFile("resources/channel.zip") num = '90052' while True: content = f.read(num + ".txt").decode("utf-8") print(content) match = re.search("Next nothing is (\d+)", content) if match == None: break num = match.group(1)
output is
Next nothing is 94191 Next nothing is 85503 Next nothing is 70877 ... Next nothing is 68628 Next nothing is 67824 Next nothing is 46145 Collect the comments.
Comments.. what comments?
It turns out that zip file can contain some comments, and they can be accessed by:
ZipFile.comment: comment associated with the ZIP file.
ZipInfo.comment: comment for the individual archive member.
Add a few lines to collect the comments:
num = '90052' comments = [] while True: content = f.read(num + ".txt").decode("utf-8") comments.append(f.getinfo(num + ".txt").comment.decode("utf-8")) content match = re.search("Next nothing is (\d+)", content) if match == None: break num = match.group(1) print("".join(comments))
outout will be
**************************************************************** **************************************************************** ** ** ** OO OO XX YYYY GG GG EEEEEE NN NN ** ** OO OO XXXXXX YYYYYY GG GG EEEEEE NN NN ** ** OO OO XXX XXX YYY YY GG GG EE NN NN ** ** OOOOOOOO XX XX YY GGG EEEEE NNNN ** ** OOOOOOOO XX XX YY GGG EEEEE NN ** ** OO OO XXX XXX YYY YY GG GG EE NN ** ** OO OO XXXXXX YYYYYY GG GG EEEEEE NN ** ** OO OO XX YYYY GG GG EEEEEE NN ** ** ** **************************************************************** **************************************************************
We got an answer "hockey"
Apparently not. Just a little fun
The right answer is in the letters: oxygen