Express.js
Express
Express is a minimalist web framework for Node.js which has 31 dependencies.
List of dependencies
- accepts: Return a different typed respond body based on what the client wants to accept.
- array-flatten: Flatten nested arrays (e.g.
flatten([1, [2, 3]])
=>[1, 2, 3]
. - body-parser: HTTP request body parsing middleware (
req.body
property). - content-disposition: Create and parse HTTP
Content-Disposition
header. - content-type: Create and parse HTTP
Content-Type
header. - cookie: Basic HTTP cookie parser and serializer.
- cookie-signature: Sign and unsign cookies.
- debug: JavaScript debugging utility (
debug(...)
,DEBUG='*' node index.js
). - depd: Mark things deprecated.
- encodeurl: Encode a URL to a percent-encoded form, excluding already-encoded sequences.
- escape-html: Escape string for use in HTML (HTML entity, e.g.
&
=>&
). - etag: Generates HTTP ETags in HTTP responses.
- finalhandler: Function to invoke as the final step to respond to HTTP request.
- fresh: Check for caching using
if-none-match
andetag
HTTP headers. - http-errors: Create HTTP errors.
- merge-descriptors: Merge objects using descriptors (call descriptors and merge).
- methods:
- on-finished:
- parseurl:
- path-to-regexp:
- proxy-addr:
- qs:
- range-parser:
- safe-buffer:
- send:
- serve-static:
- setprototypeof:
- statuses:
- type-is:
- utils-merge:
- vary:
Query string parsing
URL | Content of request.query.foo in code |
---|---|
?foo=bar | 'bar' (string) |
?foo=bar&foo=baz | ['bar', 'baz'] (array of string) |
?foo[]=bar | ['bar'] (array of string) |
?foo[]=bar&foo[]=baz | ['bar', 'baz'] (array of string) |
?foo[bar]=baz | { bar : 'baz' } (object with a key) |
?foo[]=bar | ['bar'] (array of string) |
?foo[]baz=bar | ['bar'] (array of string - postfix is lost) |
?foo[][baz]=bar | [ { baz: 'bar' } ] (array of object) |
?foo[bar][baz]=bar | { foo: { bar: { baz: 'bar' } } } (object tree) |
?foo[10]=bar&foo[9]=baz | [ 'baz', 'bar' ] (array of string - notice order) |
?foo[toString]=bar | {} (object where calling toString() will fail) |
Table from OWASP - Node.js Security Cheat Sheet.
Trust proxy
Enabling trust proxy
will have the following impact:
req.hostname
=X-Forwarded-Host
req.protocol
=X-Forwarded-Proto
(https or http or even an invalid name)req.ip
andreq.ips
=X-Forwarded-For
The trust proxy setting is implemented using the proxy-addr package. See docs.
EJS
Embedded JavaScript templates (EJS) is a NodeJS library very often used by Express to create HTML templates.
RCE on render
The following code is inside EJS (lib/ejs.js - v3.1.9). If you control the value of both variables client
and escape
, you can get a RCE.
EJS RCE - Proof of Concept
There are two options for controlling these variables.
- Abuse server-side prototype pollution (SSPP)
- Custom parameters to the render function of EJS
1. SSPP
Full article on mizu.re.
TL;DR:
2. Custom parameters of render
Inspired from the challenge Peculiar Caterpillar of the FCSC2023.
req.query
will be equal to the options
parameter passed to the render
function of Express
.
The render
function of Express
calls the renderFile
function of EJS.
render
=>renderFile
=>...
=>Template.new
=>Template.compile
Here is the code of the renderFile
function of EJS:
The opts
variable is then passed to the Template
object. So, we can get a RCE with the following query string.
This query string is equals to:
Base routing
File: main.js
File: static/index.html
If you visit /example/..%2Findex.html
, your browser will load the JS script index.js
at /example/index.js
.
Similarly, if you visit /1+alert();var[Page]=1//..%2Findex.html
, the browser will load the JS script at /1+alert();var[Page]=1//index.js
. Consequently, the content of the script index.js
will be:
An alert will be then executed.