CSAW Quals 2016 / wtf.sh (2)
September 18, 2016
Description
WTF.SH(2) Quals WTF.SH(2)
NAME
wtf.sh - A webserver written in bash
SYNOPSIS
wtf.sh port
DESCRIPTION
wtf.sh is a webserver written in bash.
Do I need to say more?
FLAG
You can get the flag to this second part of
the problem by getting the website to run the
get_flag2 command. Sadly, I can't seem to find
anything in the code that does that :( Do you
think you could take a look at it for me?
ACCESS
You can find wtf.sh at http://web.chal.csaw.io:8001/
AUTHOR
Written by _Hyper_ http://github.com/Hyper-
sonic/
SUPERHERO ORIGIN STORY
I have deep-rooted problems
That involve childhood trauma of too many
shells
It was ksh, zsh, bash, dash
They just never stopped
On that day I swore I would have vengeance
I became
The Bashman
REPORTING BUGS
Report your favorite bugs in wtf.sh at
http://ctf.csaw.io
SEE ALSO
wtf.sh(1)
CSAW 2016 September 2016 WTF.SH(2)
The Challenge
This is the second part of the CSAW WTF web challenge.
This challenge consists of 100% bash web server in which we have to try to execute the get_flag2
command on.
Unlike the first challenge wtf.sh (1)
, the get_flag2
is not available in the code, so the goal of the challenge is to obtain RCE.
Obtaining the Source Code
Like in the first challenge wtf.sh (1)
, we need to leak the source code to know what we’re dealing with.
We can do so through a file traversal vulnerability in the post.wtf
page.
Browsing to http://web.chal.csaw.io:8001/post.wtf?post=../
will leak the contents of some of the source code files:
One can notice through the leaked source code some calls to the source
bash command.
This gives us some idea of what the filenames are; we can leak these files directly now.
Here are the available files:
http://web.chal.csaw.io:8001/user_functions.sh
http://web.chal.csaw.io:8001/post_functions.sh
http://web.chal.csaw.io:8001/spinup.sh
http://web.chal.csaw.io:8001/wtf.sh
http://web.chal.csaw.io:8001/lib.sh
Finding the Vulnerability
As we’ve mentionned earlier, the goal of the challenge is to obtain RCE somehow.
Looking through the source files, we see the core function for displaying webpages (include_page
in the wtf.sh
file):
max_page_include_depth=64
page_include_depth=0
function include_page {
# include_page <pathname>
local pathname=$1
local cmd=""
[[ "${pathname:(-4)}" = '.wtf' ]];
local can_execute=$?;
page_include_depth=$(($page_include_depth+1))
if [[ $page_include_depth -lt $max_page_include_depth ]]
then
local line;
while read -r line; do
# check if we're in a script line or not ($ at the beginning implies script line)
# also, our extension needs to be .wtf
[[ "$" = "${line:0:1}" && ${can_execute} = 0 ]];
is_script=$?;
# execute the line.
if [[ $is_script = 0 ]]
then
cmd+=$'\n'"${line#"$"}";
else
if [[ -n $cmd ]]
then
eval "$cmd" || log "Error during execution of ${cmd}";
cmd=""
fi
echo $line
fi
done < ${pathname}
else
echo "<p>Max include depth exceeded!<p>"
fi
}
Starting from the top of the function, we can understand that:
- The URI that we request is sent to the
pathname
variable. - The
pathname
must end with.wtf
- The command that we want to execute must be prefixed with the
$
character. - Later down the road, the command is executed through
eval
Knowing this, we look around the source code to find potential file upload vulnerabilities.
… Which brings us to the reply
function in post_functions.sh
:
function reply {
local post_id=$1;
local username=$2;
local text=$3;
local hashed=$(hash_username "${username}");
curr_id=$(for d in posts/${post_id}/*; do basename $d; done | sort -n | tail -n 1);
next_reply_id=$(awk '{print $1+1}' <<< "${curr_id}");
next_file=(posts/${post_id}/${next_reply_id});
echo "${username}" > "${next_file}";
echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
echo "${text}" >> "${next_file}";
# add post this is in reply to to posts cache
echo "${post_id}/${next_reply_id}" >> "users_lookup/${hashed}/posts";
}
This function adds a reply to a post by creating a file on the filesystem.
It is called through a POST
request at http://web.chal.csaw.io:8001/reply.wtf?post=efG5D
.
Here are the lines of code responsible for creating a file on the filesystem :
next_file=(posts/${post_id}/${next_reply_id});
echo "${username}" > "${next_file}";
echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
echo "${text}" >> "${next_file}";
The interesting part of those lines is the first one, which assigns an array
to the next_file
variable for some reason (notice the ()
).
This line of code is also vulnerable to path traversal, as we control the post_id
value.
With this in mind, we can easily set next_file
to the value of our choice.
Let’s send a POST
request to http://web.chal.csaw.io:8001/reply.wtf?post=../payload.wtf+
(notice the added url-encoded space character at the end).
Inside the reply
function, next_file
will be assigned the following : (posts/../payload.wtf /1)
.
Bash arrays are created by seperating each element in the array by spaces.
If we want to create an array with elements from 1 to 5, we would do array=(1 2 3 4 5)
.
Now if we want to print the first item of an array, we would do echo ${array[0]}
, or simply echo ${array}
.
Therefore, our payload will create an array next_file
with two elements : posts/../payload.wtf
and /1
.
Since ${next_file}
will return the first element of the array, the following lines :
echo "${username}" > "${next_file}";
echo "RE: $(nth_line 2 < "posts/${post_id}/1")" >> "${next_file}";
echo "${text}" >> "${next_file}";
… will redirect output to posts/../payload.wtf
.
Which makes are payload accessible through http://web.chal.csaw.io:8001/payload.wtf
.
Getting the Flag
Now that we can write arbitrary files, we need to execute the get_flag2
command.
With the payload from earlier, the payload.wtf
file will have the following structure :
my_username
RE: the_original_post_title
my_reply
As we have mentionned earlier, the include_page
file only eval
’s our code if the line starts with a $
.
Since we control the username, let’s register a user called $get_flag2
.
Rerunning the payload above, we create the file :
$get_flag2
RE: the_original_post_title
my_reply
Requesting the http://web.chal.csaw.io:8001/payload.wtf
will now run the get_flag2
command and print the flag.