| عنوان | YunaiV (zhijiantianya) ruoyi-vue-pro (Yudao) master-jdk17 @ 7cab3a7 (2026-09-20); master and master-jdk25 are affected as well; no fixed version as of 2026-09-21 Authentication |
|---|
| الوصف | ruoyi-vue-pro (Yudao) ships a backend configuration that pins the SMS verification-code range to a single value: yudao.sms-code.begin-code = 9999 and end-code = 9999 in the profile-independent base application.yaml (lines 344-349, comment: "set to 9999 for testing convenience"). SmsCodeServiceImpl generates the code with RandomUtil.randomInt(beginCode, endCode) and formats it with %04d, so every SMS code is the constant 9999. The official documentation (doc.iocoder.cn/sms/ section 4.1) documents only the send frequency and validity period and never mentions begin-code/end-code, so an operator who follows the documentation keeps the fixed value; neither application-local.yaml nor application-dev.yaml overrides it, so operator-built prod/stage profiles inherit it as well.
At the same time, POST /admin-api/system/auth/reset-password is @PermitAll (AuthController.java:146-152) and performs no CAPTCHA verification at all (AdminAuthServiceImpl.java:296-312). POST /admin-api/system/auth/send-sms-code validates a CAPTCHA only for scene=23, and that check is gated by yudao.captcha.enable, which the shipped application-local.yaml sets to false while local is the default profile (application.yaml:5-6; the project's own docker-compose.yml:39 also sets SPRING_PROFILES_ACTIVE: local). Finally, the code-verification path has no failed-attempt counter and no lockout (SmsCodeServiceImpl.java:93-110; the system_sms_code table has no counter column), and the code row is inserted before the SMS is sent without a transaction, so exploitation works even when no SMS gateway is configured.
Consequently an unauthenticated attacker who knows the mobile number bound to a target account can reset that account's password and log in. The shipped SQL seed publishes the mobile number of the built-in super administrator admin, so no information gathering is required. Reproduction (only two headers are needed: Content-Type: application/json and tenant-id: 1; no Authorization header at any step):
1) POST /admin-api/system/auth/send-sms-code with {"mobile":"<victim mobile>","scene":23} -> {"code":0,"data":true}
2) POST /admin-api/system/auth/reset-password with {"mobile":"<victim mobile>","code":"9999","password":"<attacker password>"} -> {"code":0,"data":true}
3) POST /admin-api/system/auth/login with {"username":"<victim>","password":"<attacker password>"} -> {"code":0,"data":{"userId":1,"accessToken":"..."}}
The same three steps were executed against the built-in admin account (userId=1): the attacker-chosen password logged in successfully and the original admin123 password stopped working. Enabling the CAPTCHA (yudao.captcha.enable=true) is not a mitigation: only step 1 requires it, and the AJ-Captcha 1.4.0 slider can be solved programmatically (pointJson = base64(AES-128-ECB/PKCS5('{"x":x,"y":5}', secretKey)) submitted to /admin-api/system/captcha/check, then captchaVerification = base64(AES-128-ECB/PKCS5(token + "---" + plaintext)) submitted with the send request), which was automated end-to-end successfully. Steps 2 and 3 never require a CAPTCHA.
A related, separately reported issue: the code verification has no attempt limit, so randomizing the code range does not help either (with the range set to 1000-9999, a 10-thread brute force recovered admin's code in tens of seconds and logged in).
Remediation: do not ship a single-value begin-code/end-code and document those keys; provide a CAPTCHA-enable option for every send-sms-code scene (today scene=21 has no place to enable one) while keeping the default off if desired; decouple high-risk endpoints from the global CAPTCHA toggle; add a per mobile+scene failed-attempt counter with lockout and invalidate the code on failure; insert the code and send the SMS transactionally; do not ship real mobile numbers in the seed data.
|
|---|
| المستخدم | Fulucky0 (UID 101511) |
|---|
| ارسال | 21/09/2026 04:54 PM (1 شهر منذ) |
|---|
| الاعتدال | 24/10/2026 01:16 PM (1 month later) |
|---|
| الحالة | تمت الموافقة |
|---|
| إدخال VulDB | 426103 [YunaiV/zhijiantianya ruoyi-vue-pro حتى 2026.09 SMS Code Verification AuthController.java mobile/code توثيق ضعيف] |
|---|
| النقاط | 20 |
|---|