I have discovered a web form that accepts an email and promo code, and if the code is valid, you would receive an in-game gift. Unfortunately, I had no promo codes, however I've found this form to contain SQL injection vulnerabilities on both fields.
Disclaimer: After discovering the initial vulnerability I had reported it to server owner and asked for the permission to poke around, so he granted the permission to me and removed the vulnerable form from public access.
I have managed to reconstruct remote logic (in pseudocode):
handle(code, email) {
cursor = `SELECT type, date FROM codes WHERE code='${code}' LIMIT 1`;
if (cursor.rows != 0) {
`INSERT INTO codes
(code, type, email, date)
VALUES ('already_used', 0, '${email}', NOW())`;
return 'Error: promo code has already been used';
}
if (cursor.date != NULL) {
`INSERT INTO codes
(code, type, email, date)
VALUES ('invalid', 0, '${email}', NOW())`;
return 'Error: invalid promo code';
}
`UPDATE codes SET email='${email}', date=NOW() WHERE code='${code}'`;
if (cursor.type == 2) {
send_mail(to=email, "<html>You have used promo ${code}, but the cake is a lie.</html>");
}
return 'HUGE SUCCESS! You will now get a cake.';
}
And the schema of codes
table is seemingly as follows:
CREATE TABLE codes (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
code CHAR(16) NOT NULL,
type INT NOT NULL,
email VARCHAR(256),
date DATETIME
);
Injection into code
field is blind — so I can check whether SQL expr
is true ("already used promo") or false ("invalid promo"). I am using ' OR (${expr}) #
vector.
I have expanded this channel to allow passing string results out. First I detect result length via ' OR ((${expr}) REGEXP '^.{N,}') #
checks, using binary search to find proper N
. Then I reveal the result char by char using range queries like ' OR (CONVERT((${expr}) USING BINARY) REGEXP '^id[[.comma.]][f-k]') #
via binary search for each char.
This way I've learned the VERSION()
(MySQL 5.1.73-cll), exact schema of codes
table via information_schema
introspection, names of other databases etc. I don't have FILE
permission, so I cannot use LOAD_FILE()
.
Exploiting injection in email
field, I have managed to register a new promo code via ', NOW()), ('fake-promo', 2, NULL, NULL) #
vector.
Because warnings were not suppressed, I know the absolute path to PHP script that handles these requests. Unfortunately, as I lack FILE
permission, I don't know the way to get its source. From warnings I can also tell that script uses obsolete mysql_query()
and mysql_num_rows()
APIs.
Script sends emails that have X-Source-Args
and X-Source-Dir
headers set (and PHP seems to run under apache2), so I can conclude that emails are sent via PHP mail()
function.
So, what are my further steps in exploring the system?
I am suggesting that mail
might be exploitable with some sort of shell injection via forged recipient email
, however I am not sure where to start from.
Also, any other attack vectors maybe? Making UPDATE
, INSERT
or DELETE
possible on arbitrary tables? Breaking out of my one-bit communication channel? Reading the source of PHP script?