Skip to content

E-commerce-project-springBoot-master — Vulnerability Assessment #172

Description

@qrn12580

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():

@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():

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.

poc_idor_updateuser.py


Remediation

Add authorization check before updating user profile:

@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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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