| Titre | jaygajera17 E-commerce-project-springBoot 1.0 Authorization Bypass Through User-Controlled Key |
|---|
| Description | # E-commerce-project-springBoot-master — Vulnerability Assessment
## VULN-1: IDOR — Authorization Bypass in POST /updateuser
### Status: ✅ CONFIRMED (Dynamically Verified)
### Summary
The `POST /updateuser` endpoint in `UserController` accepts a `userid` parameter directly from the HTTP request without verifying that the authenticated user is authorized to modify the target profile. Any authenticated user (including low-privilege `ROLE_NORMAL` users) can supply an arbitrary `userid` to change any other user's password, email, and address — including the administrator's account.
This enables **full account takeover**: a normal customer can silently change the admin's password and seize control of the entire e-commerce platform.
| Field | Value |
|-------|-------|
| **CWE** | CWE-639 (Authorization Bypass Through User-Controlled Key) |
| **CVSS 3.1** | **8.8 (High)** — `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H` |
| **Attack Type** | Authenticated IDOR → Privilege Escalation |
| **Impact** | Full admin account takeover |
---
### CVE 6-Gate Assessment
| # | Criterion | Result |
|---|-----------|--------|
| 1 | Complete Source→Sink data flow | ✅ HTTP `userid` param → `UserController.updateUser()` → `userService.updateUserProfile()` → Hibernate `UPDATE CUSTOMER` |
| 2 | Exploitable in default configuration | ✅ No custom config required — vulnerability exists in source code |
| 3 | Successful dynamic PoC | ✅ Password changed admin→"hacked123", verified via BCrypt hash comparison |
| 4 | Real security impact | ✅ Any user can change admin password → full system compromise |
| 5 | Not a duplicate | ✅ Unique to this codebase |
| 6 | Not a configuration error | ✅ Code-level authorization bypass — missing `userid` ownership check |
---
### Technical Details
**Vulnerable Code** — `UserController.java:updateUser()`:
```java
@PostMapping("/updateuser")
public String updateUser(
@RequestParam("userid") int userid, // ← USER-CONTROLLED, NO AUTH CHECK
@RequestParam("username") String username,
@RequestParam("email") String email,
@RequestParam("password") String password,
@RequestParam("address") String address) {
userService.updateUserProfile(userid, username, email, password, address);
return "redirect:/profileDisplay";
}
```
**Sink** — `userService.updateUserProfile()`:
```java
public void updateUserProfile(int userid, String username, String email, String password, String address) {
User user = this.userDao.getUser(userid);
user.setEmail(email);
user.setPassword(passwordEncoder.encode(password)); // BCrypt encodes attacker-supplied password
user.setAddress(address);
this.userDao.updateUser(user);
}
```
The `userid` flows directly from HTTP request → controller → service → DAO without any authorization check. The service method blindly trusts the caller and updates whichever `userid` is provided.
---
### Attack Chain
```
Attacker (ROLE_NORMAL)
│
├─[1]─ Login as lisa/765 → gets JSESSIONID + CSRF token
│
├─[2]─ POST /updateuser
│ Body: userid=1&username=admin&password=hacked123&...
│ Cookie: JSESSIONID=<lisa's session>
│ → Server updates admin (id=1) password to BCrypt("hacked123")
│
└─[3]─ Admin password is now "hacked123"
→ Attacker can login as admin
→ Full system compromise
```
### Dynamic PoC Verification
| Step | Action | Result |
|------|--------|--------|
| 1 | Reset DB: admin=123, lisa=765 | ✅ |
| 2 | Login as lisa (normal user) | ✅ 302 → / |
| 3 | GET /profileDisplay (extract CSRF) | ✅ 200, userid=2 |
| 4 | POST /updateuser userid=1 password=hacked123 | ✅ 302 → / (accepted) |
| 5 | DB check: admin password | ✅ Now BCrypt hash (was plaintext "123") |
| 6 | BCrypt comparison: checkpw("hacked123", hash) | ✅ **True** — verified |
**PoC script**: `poc_idor_updateuser.py` — reproducible, self-resetting.
---
### Remediation
Add authorization check before updating user profile:
```java
@PostMapping("/updateuser")
public String updateUser(
@RequestParam("userid") int userid,
@RequestParam("username") String username,
// ... other params
Principal principal) { // ← inject authenticated user
// Get the authenticated user's ID from session/security context
int authenticatedUserId = getAuthenticatedUserId(principal);
// Authorization check: user can only modify their own profile
if (userid != authenticatedUserId) {
throw new AccessDeniedException("Not authorized to modify this user");
}
// For admin override, check role:
// if (!hasRole("ADMIN") && userid != authenticatedUserId) { throw ... }
userService.updateUserProfile(userid, username, email, password, address);
return "redirect:/profileDisplay";
}
```
---
### Additional Notes
- **PasswordEncoder**: The application uses `BCryptPasswordEncoder` (configured in `PasswordEncoderConfig.java`), so passwords are stored as BCrypt hashes. The IDOR bypasses this protection because the controller passes the raw password through the legitimate encoding pipeline.
- **CSRF Protection**: Spring Security CSRF is enabled, but the CSRF token only prevents cross-site attacks — it does not prevent same-session IDOR.
- **Dual Filter Chain**: The admin filter chain (`/admin/**`) has a configuration issue where admin login redirects to the user login page, but this does not affect the IDOR exploit — the password change is confirmed via database verification.
- **Default Credentials**: `basedata.sql` ships with admin/123 and lisa/765 in plaintext, making exploitation trivial.
|
|---|
| La source | ⚠️ https://github.com/jaygajera17/E-commerce-project-springBoot/issues/172 |
|---|
| Utilisateur | emiya (UID 100287) |
|---|
| Soumission | 03/08/2026 07:53 (il y a 1 mois) |
|---|
| Modérer | 12/09/2026 20:45 (1 month later) |
|---|
| Statut | Accepté |
|---|
| Entrée VulDB | 403180 [jaygajera17 E-commerce-project-springBoot UserController.java UserController.updateUser userid élévation de privilèges] |
|---|
| Points | 20 |
|---|