MeepwnCTF 2017 - injection

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.

Sourcecode (base64)

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 links table
    • 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 BOT:
    • chim.js is a file used in phantomjs (a headless browser), very familar with XSS problem.
    • the secret cookie is HTTPOnly (i'm not very it yet, blind trust), can't easy inject javascript code to steal cookie.
  • The php code:
    • every input entry has been mysqli_real_escape_string
    • 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?

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

Constant Name Description
ENT_COMPAT Will convert double-quotes and leave single-quotes alone.
ENT_QUOTES Will convert both double and single quotes.
ENT_NOQUOTES Will leave both double and single quotes unconverted.
... ...
ENT_HTML401 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.

USE inj_1;
CREATE TABLE `users` (
`user` varchar(50) NOT NULL,
`passwd` varchar(50) NOT NULL,
PRIMARY KEY (`uid`),
UNIQUE KEY (`user`)
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)

With 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,


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:

<input type='text' value='Duplicate entry 'admin onerror=aaa   ' for key 'user'' class='form-control' disabled>

Now 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.

Example: <input autofocus onfocus=alert(1)> will pop an alert box
But not this one <input autofocus onfocus=alert(1) disabled>
Another place that I haven't notice is the links table. It also have uniq key is link, type of varchar(500)

Line 29 of index.php, the manual check did the same thing with register, both have

$msg = mysqli_error($con);
but this time, it is
echo "<input type='text' value='$msg' class='form-control' readonly>";
Yeah, readonly is better than disabled, I can use my favarite autofocus
 The final exploit flow could be (assume this is a XSS challenge):
  1. register
  2. login
  3. send a link that trigger a CSRF exploit to make bot create manual exploit link
  4. resubmit that trigger link
  5. waiting for flag, waka waka

Now this is time for trial error, here
or the original here


PS: WordPress SUCK!!!