Another injection? Awesome, another ART I want to learn so much. Learning *injection*-fu is hard, keep learning, practicing, ctf all the time.
According to Saitama, after a year and half of 100 daily push-ups, sit-ups, and squats, plus 10 km daily running, he had achieved some level of superhuman strength.
Spoiler alert: In the previous post, I once said something about SQLi (aka SQL injection) and XSS.
I will write up as my timeline trying to solve this task.
I have sourcecode, yay, at least I known what the heck i'm facing with. At this time, I could use RIPS but I already have experience in source code audit, so, kill the bear with bare hand, why not (I'm not support killing wild animals).
Exploring source code is just time-comsuming task, after that, I have some notes:
- Database structure:
- The flag is the
- charset LATIN1
- all string field is varchar(500) - that's mean only 500 char stored in that column. I have some ideas about SQL truncate attack
- The flag is the
- The BOT:
- chim.js is a file used in phantomjs (a headless browser), very familar with XSS problem.
- The php code:
- every input entry has been
- every output has been
htmlentities- the problem is this function by design/default didn't escape single quote '
- CSRF, not have any CSRF check, likely XSS.
SET sql_mode=ANSI ?
function `filterLink` remove single quote ', double quote ", backtick `
`mysqli_error` for debug only?
- every input entry has been
The very likely exploitable for a XSS vuln is htmlentities
string htmlentities ( string
$string [, int
$flags = ENT_COMPAT | ENT_HTML401 [, string
$encoding = ini_get("default_charset") [, bool
$double_encode = true ]]] )
Default flag is ENT_COMPAT | ENT_HTML401, but if I want to convert ', look at the flag table
||Will convert double-quotes and leave single-quotes alone.|
||Will convert both double and single quotes.|
||Will leave both double and single quotes unconverted.|
||Handle code as HTML 4.01.|
Not bad, expected by design 😀
In a CTF challenge, most of task are solvable or exploitable. From my point of view, everything echo-ed back to us is something useful. such as `mysqli_error`.
In some point, I had tried to exploit the register function to make a new Admin but UNIQUE KEY (`user`) in table structure prevented it.
Luckily, when I try to insert a duplicated user into table, it warn me, with the most beautiful char, a single quote.
Example Database, notice the space at user column.
CREATE DATABASE inj_1; USE inj_1; CREATE TABLE `users` ( `uid` int(11) NOT NULL AUTO_INCREMENT, `user` varchar(50) NOT NULL, `passwd` varchar(50) NOT NULL, PRIMARY KEY (`uid`), UNIQUE KEY (`user`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; ALTER TABLE `users` AUTO_INCREMENT=1; INSERT INTO `users` VALUES (0, 'admin', 'a'); INSERT INTO `users` VALUES (0, 'admin ', 'a'); INSERT INTO `users` VALUES (0, 'admin onerror=aaa', 'a'); INSERT INTO `users` VALUES (0, 'admin onerror=aaa ', 'a');
One more thing with SELECT is, it didn't do bound check on input, look at this example
mysql> select * from users; +-----+-------------------+--------+ | uid | user | passwd | +-----+-------------------+--------+ | 1 | admin | a | | 3 | admin onerror=aaa | a | +-----+-------------------+--------+ 2 rows in set (0,00 sec) mysql> select * from users where user = 'admin' -> ; +-----+-------+--------+ | uid | user | passwd | +-----+-------+--------+ | 1 | admin | a | +-----+-------+--------+ 1 row in set (0,01 sec) mysql> select * from users where user = 'admin '; +-----+-------+--------+ | uid | user | passwd | +-----+-------+--------+ | 1 | admin | a | +-----+-------+--------+ 1 row in set (0,00 sec) mysql> select * from users where user = 'admin '; +-----+-------+--------+ | uid | user | passwd | +-----+-------+--------+ | 1 | admin | a | +-----+-------+--------+ 1 row in set (0,00 sec) mysql> select * from users where user = 'admin 1'; Empty set (0,00 sec)
SET sql_mode=ANSI , I can bypass SELECT, also able trigger error in INSERT.
If you test in your db; make sure you have change your sql_mode,
by default (mysqld --verbose --help | grep mode) give ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
STRICT_TRANS_TABLES will give error
1406 (22001): Data too long for column
In the code, there is a lot of space content
$msg = mysqli_error($con); but I look the register function first. Then a lot of time trying to finger out how can I bypass the disable tag to have a functional onfocus tag (line 61 file login.php)
echo "<input type='text' value='$msg' class='form-control' disabled>";
After mysql return error string, I may have the following text:
onerror has been escaped from the single quote, it is a event and can be develop with real payload such as onfocus, but it is disabled in this input form.
Line 29 of index.php, the manual check did the same thing with register, both have
$msg = mysqli_error($con);
- send a link that trigger a CSRF exploit to make bot create manual exploit link
- resubmit that trigger link
- waiting for flag, waka waka
PS: WordPress SUCK!!!