#584·litemall

[vulnerability] Admin login lacks rate limiting and captcha enforcement (CWE-307)

Author: s1mple-topCreated Sep 12, 2026Updated Sep 12, 2026

Summary

The /admin/auth/login endpoint provides no anti-automation protection: the captcha verification has been removed (commented out), repeated failures do not trigger any account lockout, and there is no IP/account-level rate limiting. An attacker can therefore perform unlimited online brute-force attacks against administrator credentials and eventually take over the admin backend.

  • Type: Authentication protection weakness / Improper Restriction of Excessive Authentication Attempts
  • CWE: CWE-307
  • Severity: Medium (High when combined with default credentials)
  • Component: litemall-admin-api

Affected Endpoint

POST /admin/auth/login Source file:

litemall-admin-api/src/main/java/org/linlinjava/litemall/admin/web/AdminAuthController.java

Details

1. Captcha verification is commented out

In AdminAuthController.login(...), both the parsing and the comparison of the code field are commented out. The captcha is still generated and written to the session, but it is never read or checked during login ("generate only, never verify"):

java
public Object login(@RequestBody String body, HttpServletRequest request) {
    String username = JacksonUtil.parseString(body, "username");
    String password = JacksonUtil.parseString(body, "password");
//  String code = JacksonUtil.parseString(body, "code");

    if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
        return ResponseUtil.badArgument();
    }
//  if (StringUtils.isEmpty(code)) {
//      return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA_REQUIRED, "验证码不能为空");
//  }
//
//  HttpSession session = request.getSession();
//  String kaptcha = (String)session.getAttribute("kaptcha");
//  if (Objects.requireNonNull(code).compareToIgnoreCase(kaptcha) != 0) {
//      return ResponseUtil.fail(ADMIN_INVALID_KAPTCHA, "验证码不正确", doKaptcha(request));
//  }

    Subject currentUser = SecurityUtils.getSubject();
    try {
        currentUser.login(new UsernamePasswordToken(username, password));
    } ...
}

Note: the frontend was changed consistently — in litemall-admin/src/views/login/index.vue the captcha form item is also wrapped in an HTML comment, and loginRules contains only username / password. So this is not a frontend/backend inconsistency; the captcha feature has been disabled on both ends. The security consequence is what matters here: there is no human-interaction barrier left on the login endpoint.

2. No failure lockout

AdminAuthorizingRealm.doGetAuthenticationInfo(...) only throws UnknownAccountException on a bad password. There is no failed-attempt counter, no lockout, and no RetryLimit/HashedCredentialsMatcher configuration in the Shiro chain.

3. No rate limiting

Neither the endpoint nor the Shiro filter chain applies any IP- or account-level rate limiting.

4. Default seed credentials (impact amplifier)

litemall-db/sql/litemall_data.sql ships default admin accounts:

admin123 / admin123
mall123  / mall123
promotion123 / promotion123

Steps to Reproduce

All tests were performed against a local instance (http://localhost:8080/litemall).

A. Login without sending code (10 attempts)

bash
for i in $(seq 1 10); do
  curl -s -X POST http://localhost:8080/litemall/admin/auth/login \
    -H 'Content-Type: application/json' \
    -d '{"username":"admin123","password":"admin123"}'
  echo
done

Result: 10/10 returned "errno":0 (success). B. Login with a fixed wrong code (10 attempts) for i in $(seq 1 10); do curl -s -X POST http://localhost:8080/litemall/admin/auth/login
-H 'Content-Type: application/json'
-d '{"username":"admin123","password":"admin123","code":"WRONG123"}' echo done Result: 10/10 returned "errno":0 (success). If the captcha were enforced, a dedicated error code would be returned instead.

C. Cross-check — response depends only on password, not on code

correct password + empty code errno:0
correct password + wrong code errno:0
wrong password + empty code errno:605
wrong password + wrong code errno:605

The response code varies only with the password, never with code.

D. Brute-force feasibility (no lockout)###

bash
# 10 attempts with wrong passwords, then the 11th with the correct one

Result: all 10 wrong attempts returned errno:605, no lockout occurred, and the 11th request with the correct password returned errno:0 — the account was never locked.

Impact

  • Administrator credentials can be brute-forced online without any delay, captcha, or lockout constraint.
  • Once a valid admin session is obtained, the attacker can reach other admin endpoints, e.g.:
    • SQL injection in /admin/order/list (nickname/consignee/orderSn);
    • Arbitrary file deletion in /admin/storage/delete (executed with root privileges).
  • With the shipped default credentials, no brute-force is even required — the accounts are directly accessible.

Root Cause

The login endpoint has the captcha verification removed, and there is no failed-authentication rate limiting or account lockout mechanism.

Suggested Fix

  1. Restore the captcha verification in AdminAuthController.login(...) and re-enable the corresponding field in the frontend login form.
  2. Add failed-attempt counting + account lockout, or IP-level rate limiting (e.g. Shiro HashedCredentialsMatcher with a retry limit, or a servlet filter / gateway limiter).
  3. Force a password change on first login for the seed accounts, and remove the hardcoded default credentials from the shipped SQL.