CTFtime: https://ctftime.org/event/1842
Team: ⚔️TSJ⚔️
Rank: 5
Here is a part of the source code:
debug = request.args.get("debug")
if request.method == 'POST':
greetType = request.form["type"]
greetNumber = request.form["number"]
if greetType == "greeting_all":
greeting = random_greet(random.choice(NewYearCategoryList))
else:
try:
if greetType != None and greetNumber != None:
greetNumber = re.sub(r'\s+', '', greetNumber)
if greetType.isidentifier() == True and botValidator(greetNumber) == True:
if len("%s[%s]" % (greetType, greetNumber)) > 20:
greeting = fail
else:
greeting = eval("%s[%s]" % (greetType, greetNumber))
The goal is to find the value of the variable FL4G
. The code evals greetType[greetNumber]
if:
greetType
is an identifiergreetNumber
passes botValidator
, which does the following checks:
int(all the 0-9 characters combined)
is at most 5greetType[greetNumber]
is at most 20 charactersWe can set greetType
to FL4G
and greetNumber
to a number to get one character of the flag at a time. Since botValidator
only checks for ASCII characters in the range [58, 122], we can use special Unicode characters like int(debug)
to make it use the value of debug
as the index instead. Therefore, querying /?debug=0
with type=FL4G&number=int(debug)
gives the 0-th character of the flag, etc.
Alternatively, we can use exec(debug)
to make it run arbitrary code.
This is a sourceless a.k.a. guessing web challenge. We are given the database schema and nothing else.
There is a comment in the HTML about send_pic.php
. This endpoint takes two parameters url
and id
, which correspond to the pictures
table. After a bit of testing, we can find out that it takes the row with the same id
and sends it to the url
we give. There is a SQL injection in the id
parameter, and after some testing we know that it blocks the following keywords:
,
(
)
key_cc
Using the payload
curl 'http://172.105.127.104/send_pic.php' \
--data "url=http://" \
--data "id=3 union select KEY_CC from access_key--"
we were able to obtain the secret access key, messi_is_goat__!!!!
.
We can use the access key from the previous step to log in. We can now access the page get_img.php
, which includes the page in the file
parameter. We can achieve LFI using this page, but some substrings are blocked:
sess
../..
pear
The second one can be circumvented by using .././..
, so we can include any file outside the directory. Unfortunately, we cannot find any useful files to read, and usual LFI to RCE techniques like php://filter
, /tmp/sess
, pearcmd.php
, /proc/self/fd
, /proc/self/environ
, etc. don't seem to work.
After being stuck on this step for a while, a teammate suggested that peclcmd.php
can be used to achieve RCE and fing the flag.
This is the unintended version of casino 2.
There are 3 main functions in this challenge:
It is possible to bet a negative amount of money, so we can win enough money by losing a negative bet.
It's the same as casino 1, except we cannot bet negative amounts.
The intended solution is probably to break the hash, which I don't know how to do. However, I found another unintended solution.
Let's look at how the casino's random numbers are generated:
math/rand
module.rand.Intn(2023)
.After reading the source code of Go's math/rand
, we can see that the PRNG is actually seeded with seed % int32max
, which means that there are only
The seed 0 is replaced by 89482311 so there is 1 less possibility. ↩︎