From 7b546bc5535e287bf7438200e2b68d6b3f45cb41 Mon Sep 17 00:00:00 2001 From: Maxim Ashin <53023744+MaximAshin@users.noreply.github.com> Date: Sun, 7 Apr 2024 00:51:03 +0300 Subject: [PATCH] Prototype Pollution (Server Side) + Email Injection (#307) A client side endpoint that is vulnerable to prototype pollution --------- Co-authored-by: Tamir Gershberg <47638346+tamirGer@users.noreply.github.com> --- README.md | 17 +- client/public/assets/css/style.css | 7 + client/src/api/ApiUrl.ts | 3 +- client/src/api/httpClient.ts | 12 ++ client/src/pages/main/Contact.tsx | 25 ++- client/src/pages/main/Header/Nav.tsx | 2 +- client/src/pages/marketplace/Marketplace.tsx | 65 ++++++- client/src/utils/url.js | 74 ++++++++ docker-compose.local.yml | 12 ++ docker-compose.yml | 12 ++ package-lock.json | 126 ++++--------- package.json | 1 + src/app.module.ts | 3 + src/email/email.controller.swagger.desc.ts | 5 + src/email/email.controller.ts | 137 ++++++++++++++ src/email/email.module.ts | 10 ++ src/email/email.service.ts | 179 +++++++++++++++++++ src/main.ts | 2 + src/utils/url.ts | 76 ++++++++ 19 files changed, 668 insertions(+), 100 deletions(-) create mode 100644 client/src/utils/url.js create mode 100644 src/email/email.controller.swagger.desc.ts create mode 100644 src/email/email.controller.ts create mode 100644 src/email/email.module.ts create mode 100644 src/email/email.service.ts create mode 100644 src/utils/url.ts diff --git a/README.md b/README.md index fa2c7359..10c20ec0 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The full API documentation is available via swagger or GraphQL: npm ci && npm run build # build client -npm ci --prefix public && npm run build --prefix public +npm ci --prefix client && npm run build --prefix client #build and start dockers with Postgres DB, nginx and server docker-compose --file=docker-compose.local.yml up -d @@ -177,4 +177,19 @@ Additionally, the endpoint PUT /api/users/one/{email}/photo accepts SVG images, 3. The endpoint GET `/api/partners/query` is a raw XPATH injection endpoint. You can put whatever you like there. It is not referenced in the frontend, but it is an exposed API endpoint. 4. Note: All endpoints are vulnerable to error based payloads. +* **Prototype Pollution** - The `/marketplace` endpoint is vulnerable to prototype pollution using the following methods: + 1. The EP GET `/marketplace?__proto__[Test]=Test` represents the client side vulnerabillity, by parsing the URI (for portfolio filtering) and converting + it's parmeters into an object. This means that a requests like `/marketplace?__proto__[TestKey]=TestValue` will lead to a creation of `Object.TestKey`. + One can test if an attack was successful by viewing the new property created in the console. + This EP also supports prototyp pollution based DOM XSS using a payload such as `__proto__[prototypePollutionDomXss]=data:,alert(1);`. + The "legitimate" code tries to use the `prototypePollutionDomXss` parameter as a source for a script tag, so if the exploit is not used via this key it won't work. + 2. The EP GET `/api/email/sendSupportEmail` represents the server side vulnerabillity, by having a rookie URI parsing mistake (similiar to the client side). + This means that a request such as `/api/email/sendSupportEmail?name=Bob%20Dylan&__proto__[status]=222&to=username%40email.com&subject=Help%20Request&content=Help%20me..` + will lead to a creation of `uriParams.status`, which is a parameter used in the final JSON response. + * **Date Manipulation** - The `/api/products?date_from={df}&date_to={dt}` endpoint fetches all products that were created between the selected dates. There is no limit on the range of dates and when a user tries to query a range larger than 2 years querying takes a significant amount of time. This EP is used by the frontend in the `/marketplace` page. + +* **Email Injection** - The `/api/email/sendSupportEmail` is vulnerable to email injection by supplying tempred recipients. + To exploit the EP you can dispatch a request as such `/api/email/sendSupportEmail?name=Bob&to=username%40email.com%0aCc:%20bob@domain.com&subject=Help%20Request&content=I%20would%20like%20to%20request%20help%20regarding`. + This will lead to the sending of a mail to both `username@email.com` and `bob@domain.com` (as the Cc). + Note: This EP is also vulnerable to `Server side prototype pollution`, as mentioned in this README. diff --git a/client/public/assets/css/style.css b/client/public/assets/css/style.css index 30ade4f2..ff50d4b2 100644 --- a/client/public/assets/css/style.css +++ b/client/public/assets/css/style.css @@ -573,6 +573,13 @@ section { padding-top: 30px; } +.marketplace-gem-filter-input { + margin-left: 20%; + margin-right: 20%; + max-width: -webkit-fill-available; + margin-top: 15px +} + .section-title h2 { font-size: 32px; font-weight: bold; diff --git a/client/src/api/ApiUrl.ts b/client/src/api/ApiUrl.ts index 4050fc1b..36bee57a 100644 --- a/client/src/api/ApiUrl.ts +++ b/client/src/api/ApiUrl.ts @@ -11,5 +11,6 @@ export enum ApiUrl { Spawn = '/api/spawn', File = '/api/file', NestedJson = '/api/nestedJson', - Partners = '/api/partners' + Partners = '/api/partners', + Email = '/api/email' } diff --git a/client/src/api/httpClient.ts b/client/src/api/httpClient.ts index ba948a2b..aaed3e1f 100644 --- a/client/src/api/httpClient.ts +++ b/client/src/api/httpClient.ts @@ -319,3 +319,15 @@ export function searchPartners(keyword: string): Promise { method: 'get' }); } + +export function sendSupportEmailRequest( + name: string, + to: string, + subject: string, + content: string +): Promise { + return makeApiRequest({ + url: `${ApiUrl.Email}/sendSupportEmail?name=${name}&to=${to}&subject=${subject}&content=${content}`, + method: 'get' + }); +} diff --git a/client/src/pages/main/Contact.tsx b/client/src/pages/main/Contact.tsx index d26f3c9c..26587e83 100644 --- a/client/src/pages/main/Contact.tsx +++ b/client/src/pages/main/Contact.tsx @@ -1,4 +1,5 @@ import React, { useEffect } from 'react'; +import { sendSupportEmailRequest } from 'src/api/httpClient'; export const Contact = (props: { mapTitle: string | null }) => { useEffect(() => { @@ -10,6 +11,21 @@ export const Contact = (props: { mapTitle: string | null }) => { } }, []); + const sendSupportRequestEmailAction = () => { + const formName = document.getElementById('name')?.value; + const formEmail = document.getElementById('email')?.value; + const formSubject = document.getElementById('subject')?.value; + const formMessage = document.getElementById('message')?.value || ''; + + if (!(formName && formEmail && formSubject)) { + return alert( + 'The email form is incomplete - Please fill out all required sections.' + ); + } + + sendSupportEmailRequest(formName, formEmail, formSubject, formMessage); + }; + return (
@@ -105,6 +121,7 @@ export const Contact = (props: { mapTitle: string | null }) => {