During the last two days, the Hacklu CTF 2015 was held. It’s a jeopardy-style CTF and Sebastian joined to have some fun ;) Here’s the writeup of the following challenges:
- Module Loader (Web, 100)
- PHP Golf (Coding, 75)
- Guessthenumber (Coding, 150)
- Bashful (Web, 200)
First of all I want to say that CTFs are fun. If you haven’t participated in one yet, go to ctftime.org to find a list of upcoming CTFs. During this CTF I teamed up with Denis and Mazen chimed in for bashful.
Module Loader
This was an easy warm-up challenge: A web application which took a $_GET['module']
parameter an then executed the given module. Having a quick look into the sourcecode of the website tells us where the modules are located.
The /modules/
folder greets us with a nice directory listing and all available modules:
You could even click on the modules and see their full sourcecode, but that didn’t seem to help a lot. So let’s see if this is a local file inclusion and if we can manipulate the path:
Okay, that’s cool. Denis came up with the idea of including the .htaccess
-file from the document root.
The last step was to include the flag.php
file in the hidden directory.
Done :)
PHP Golf
This challenge was pretty cool, because you had to write a php program for the following task and conditions:
I first started out to just implement the functionality without looking at the length of the code. The code did what it should, but it was way too long, so it became obvious that you can’t solve this challenge without regular expressions.
The second version used regular expressions and preg_replace
with the e
modifier to convert the matches to upper/lower case:
1
|
|
However, this version was still too long with about 90 characters. This was when I started to look for ways to replace the long strtoupper
/ strtolower
calls. The solution are so called unicode character properties. I somehow didn’t manage to get them to work with the replacement
parameter of preg_match
, so I tested them with perl:
1
|
|
The trick is, that everything between \U
and \E
will be converted to it’s uppercase representation. \L
will convert to lowercase. Unfortunately, the submission server didn’t offer perl
and this version was again too long (~80), but we were allowed to use exec
and other commands. My next thought was about using sed
. However, this was at around 2 o’clock in the morning and I posted this to our mailing list. I decided to go to sleep and recharge for the next day.
The next morning came and before I was able to continue on the challenge, I received an email from Denis with a working solution:
1
|
|
Exactly 62 characters! Some notes about this:
[^\w]
is the same as\W
<?=
is the same as<? echo
- You can omit the trailing
?>
if the code ends with a semicolon.
This solution still had problems with underscores (_
) in the input string, but we had luck to get one without these and successfully recovered the flag:
Done
Guessthenumber
The task of this challenge was to guess 100 numbers in the correct order.
The following hints were given:
- The server uses a Linear congruential generator
- Uses the standard glibc parameters
- Initialized with the python strftime format
YmdHMS
- Numbers are between 0 and 99 (included)
I wanted to solve the challenge with python. I googled for a LCG implementation/library and found an example. The next step was to change the parameters to the glibc standards and to write the basic server/client communication code. So far, easy going.
The server always told us his current date and time. (See screenshot above) This had to do something with the initialization format YmdHMS
. After extracting the values with a regular expression, I concatenated them into the given format:
1 2 |
|
The last important thing was to apply a modulo operation on the generated randon numbers. As both 0 and 99 are in the range of possible numbers, rnd() %100
was used.
Unfortunately, this didn’t solve the challenge as the first guess was always wrong. It turned out that you had to generate 100 numbers and send them in reversed order:
The full quick & dirty code: Pastebin
Done :)
Bashful
This was the challenge, I’ve spent the most time on, because I was trying way too hard. Mazen joined me on this one. But okay, let’s start slowly. Bashful was a web application, written in pure bash, that could be used to store notes (simple strings).
I’m going to post my final solution right away and write about other possibilites and chances afterwards. I’m still not sure if this solution was the intended one or not, but it worked very well :)
As you can see, I was able to use the most standard Shellshock payload in the request headers:
1
|
|
Psst: There was even a XSS by sending a http header with a XSS payload :D
I have to say, that I was kind of dissapointed by this solution. There was so much more fun within the code. While going through it, I came across the following three functions:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
These functions were later used to parse user input:
1 2 3 |
|
The first interesting thing is the sed
command in filter_nonalpha
, because it replaces all characters except the ones in the square brackets. So our values can contain .!$;?
which may be useful in the context of bash. The second interesting thing is the following line from parse
:
1
|
|
Note that only the environment variable’s name is filtered, but not the value. Additionally we can use the parse
function to set abitary environment variables. E.g. the query string DEBUG=1
will set the variable $DEBUG
to 1
.
A bit further down in the sourcecode, we find the following lines:
1 2 3 4 5 |
|
As said above, putting DEBUG=
into the url will print us all current environment variables:
Knowing that I can set other variables and/or overwrite existing ones, made the following two code block really interesting:
1 2 3 4 5 6 7 8 9 10 11 |
|
Controlling $SESSION_DIR
and $sessid
to set an abitary file path as the $sessfile
variable and reading the content from it, sounded like a nice way to get the flag. Long story short: Setting $SESSION_DIR
wasn’t the problem, but rather the length check of the $sessid
variable. I was able to bypass it with $IFS$IFS...$IFS
, but that failed the file existance check ([ -f $sessfile ]
).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
This seemed even more interesting, because that may directly lead to a remote code execution. Again, we should be able to control/set the values of $DOCUMENT_ROOT
and $page
. The combination of both would then be sourced (= executed). We only need to put our commands into a file with the ending .sh
in the document root or somewhere else and change it.
I could think of two ways to trigger the RCE:
The first one was to set $SESSION_DIR=/var/www/
and $sessid=aaaa...aaa.sh
(60+ times a
and .sh
). This should set $sessfile=/var/www/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.sh
. Saving a note with these parameters in the url should create the $sessfile
. Unfortunately, this didn’t work due to missing write permissions. The only thing the server did was returning a 500 error. :(
The other idea was to set $SESSION_DIR=/var/sessions
and $sessid
as above, to create a session file with .sh
at the end. The second step would consist of setting DOCUMENT_ROOT=/var/sessions
and $page=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
, but this failed with a 500 error again.
Maybe there’s some other bash magic that can be used to modify the parameters and trigger the RCE. I’m quite disappointed that I couldn’t get this to work. However, it’s funny to see that the solution was really easy and I just somehow tried too hard to find another way.
Other challenges
I had a look at other challenges and can only post some ideas that I had. Probably all in the wrong direction and senseless:
Grading-Board (Web):
- Some kind of SQL Injection
- Probably need to use the
grant options
to give other people access to your own table to bypass the request limit - No time to test this
Dr.Bob (Forensic):
- Mounting .vdi image with
qemu-nbd
- LVM volume, but encrypted and password unknown
- Use VirtualBox to start the saved state, but no password for users
- Try/Use volatility to extract some (useful) information
- Boot disk and use
init=/bin/bash rw
kernel parameters to drop into root-shell. Look for suspicious/useful files. No luck :( - Edit saved-state with hexeditor to change
/etc/passwd
contents. This didn’t come into my mind at 5 o’clock :(
Teacher’s Pinboard (Web):
- Bottom of
pickle.js
says thatsplice/slice
are mixed up and need to be fixed - Pickle is some kind of encoding. Information from the cookie
accountinfo
will be decoded and used - Idea: Save the single-page app and fix pickle.js. Hope that this helps to extract some information/flag from the ‘default’ notes.
- No time to test this
Future CTFs
I think, I’ll participate more often in jeopardy-style CTFs if my spare time allows it. It’s really really fun and lets you discover new stuff/get fresh ideas. Let me know if you want to team up for a particular challenge/CTF
Hope that helps, Sebastian