The concept of our exploit is based on Koby’s PCMan buffer overflow exploit with the ftp ‘GET’ command on Windows XP. The exploit and vulnerable application can be found here
In this report, we do not follow Koby’s exploit fully, but we do exploit the same vulnerability. It might be hard to grasp all the things we modified, so we will summarise it here.
- We have tested on 3 platforms, Windows XP, Windows 8.1 and Windows 10. All the exploits we found for PCMan were in either Windows XP(most) or Windows 7(few).
- We added Corelan Team’s mona script in Immunity Debugger of vulnerable machine to obtain ESP and EIP offset instead of through the Metasploit framework’s pattern_offset in attacker machine
- We tested the exploit using python code similar to that of koby’s, and also generated ruby code to use in conjunction with metasploit framework and its meterpreter
- We used different shellcode, and these shellcode were generated from metasploit.
- Instead of ‘GET’, we also generated exploits that used different ftp methods such as ‘LS’,’ABOR’,’MDIR’,’MKDIR’. These gave us different offsets that we could use the same exploit to attack.
- We used ftp_pre_post to fuzz the PCMAN ftp server, and generated an exploit using mona.py. This means that we can send data without using FTP methods (e.g ‘GET’).
- We used mona suggest and mona findmsp for our ‘different’ exploit. This means that we used different tools to build our exploit as compared to koby.
Attacker machine: Kali Linux 2.0
Vulnerable machine: Windows XP SP3, Windows 8.1, Windows 10
Connection: Tested on D-link router connecting separate laptops and also VMware hosting multiple OS.
Koby used metasploit framework’s auxiliary ftp fuzzer module (ftp_pre_post). The PCMan server crashes here.
Fig 1.1 MSF’s auxiliary ftp_pre_post fuzzer
In fact, we can already go to the vulnerable machine to build an exploit as the fuzzer sends data with a pattern where we can set find an offset with.
However, we can try writing our own python code to send 5000 ‘A’s over to see what our code can overwrite.
Fig 1.2 This is how we connect and send data using python sockets.
Fig 1.3 Immunity Debugger CPU Dashboard
We can see the EIP was overwritten here.
Instead of sending 5000 ‘A’ characters, we use msf’s tool, pattern_create.rb to generate a long buffer string so that we can find out where exactly is the EIP overwritten.
Fig 1.4 Immunity Debugger Register view (Pattern)
As we can see from the image above, 0012EDB8 is where ESP is pointing at this point in time. The program is stuck because the EIP address is overwritten by the pattern we generated. At this point in time, we would want to overwrite the EIP with an address that points to a jmp esp instruction. So the Immunity Debugger is very useful at this point. We can do one of the following
- Go back to Kali Linux to use msf to find out the offset by feeding the “address” at EIP, “396F4338” to rb. This is what Koby did in the original exploit.
- We can use the mona script by Corelan.be to find out the offset value. I personally prefer this because I get much more information. This is done by using “!mona findmsp” in the command line of Immunity Debugger.
I will do it the second way. First, I set a working folder for the mona.py script. Then findmsp.
>!mona config –set workingfolder c:\pclog\%p
Findmsp produces a large text file as it walks through the stack, but the most important part lies here.
Fig 1.5 Corelan Team’s Mona script “findmsp” output
From this, we can deduce two important things.
- EIP lies at offset 2006
- ESP lies at offset 2014
The reason these two are important is because we will overwrite EIP after 2006 characters, and then ESP after 2014 characters.
Hence our exploit code should look something like this in python.
Fig 1.6 Python exploit code
We added some no-ops so that we can form a layer of “no-operations” till we start running our shellcode, which is within the payload. The payload will then surely lie after offset 2014. In this case, if we examine the stack, there is no need for 15 No-Ops. For most FTP methods tested, 4 No-Ops is adequate to position the shell code to start exactly at the address ESP is pointing to.
The original exploit by Koby uses this to produce the shellcode.
Fig 1.7 Payload generation via msf
This is extremely simple, and our team have decided to run it in a different way, using more of corelan.be’s mona script.
“!mona suggest” allows us to run findmsp and create an example metasploit exploit.rb at the same time. We also did these in our attempt to exploit on platforms Windows 8.1 and Windows 10, which we will discuss in the later part of this report. The Corelan.be website does not give us a detailed way on how to do a network tcp exploit, so we did it by trial and error, according to how they did a file format exploit (similar to the file reading of abc taught in class).
When !mona suggest is run, an option window appears.
Fig 1.8 Corelan Team’s Mona script “suggest” will prompt this pop-up in Immunity Debugger
We choose tcp because we are exploiting through ftp. We then enter a port number, which can be changed later in msf console when we build.
Once exploit.rb is created, we transfer the exploit.rb from the Vulnerable machine Windows XP to Kali Linux’s
Every time we make an edit to the template exploit, we will
Msf > reload_all
Msf> use exploit/windows/exploitwxp
Where msf is the metasploit framework console.
We then go on to edit the exploit.
Fig 1.9 MSF view, using ruby exploit created by mona‘s method “suggest”
We then set RHOST to be 192.168.153.130 (Address on the vulnerable windows XP machine).
We also set RPORT to 21, because it is the port exposed for ftp.
When we run, it does not seem to work. We need to make more edits. There are three main reasons why the exploit doesn’t work. These were tested through our own testing, and not via any tutorial.
- Our previous exploit tested by the ftp get method, after logging in. Mona did not know that and only created the exploit based on its own knowledge of the stack and offset. So the generated exploit looks like this.
Fig 1.10.1 Ruby code connecting to server without logging in
In order to solve this, we had to add some extra ruby code.
Fig 1.10.2 Ruby code connecting to server without logging in
- The payload might contain bad characters such as null bytes.
Fig 1.11.1 Ruby code generating payload with bad characters present
In order to solve this, we use the bad characters declared in the successful koby exploit. Null bytes and bad characters will cause the exploit to fail as a proper full buffer string cannot be sent over at one go. This will be discussed more in part 3.
Fig 1.11.2 Ruby code generating payload with bad characters present
- Upon opening the exploit.rb file, we find that the jmp esp instruction was already chosen for us, to be
Fig 1.11.1 Ruby code with a jmp esp instruction address starting with a null byte
By default, !mona suggest uses the very first jmp esp instruction found within the app. This is not what we want. In fact, we want to get an address that does not contain any null bytes. In this situation, the program crashes and immunity Debugger shows such a message.
Fig 1.11.2 Error message in Immunity Debugger
We will get to see a different effect in Windows 8 and 10 when they face such an address. Hence, from this we also learn that Windows sets a very good protection mechanism just by having the app’s memory address to start with a null byte. Writing to the stack for an attacker becomes much harder.
Therefore, we look for another jmp esp instruction. We do this with the understanding of koby’s exploit method, to use mona jmp –r esp. This returns possible jmp/call/push esp instructions that the machine is able to run from the PCMan program. This is fundamentally a windows XP vulnerability, and we will see the difference when compared to windows 8 and 10 later.
Fig 1.11.3 Output of mona’s jmp –r esp on Windows XP SP3
Do note that the PCMan app can access system instructions outside of itself.
Here we choose a jmp esp instruction that does not start with a null byte. It does not matter which we choose, as all are accessible, and EIP merely calls a jmp esp and then starts to run the instructions stored in ESP.
Fig 1.11.4 Fig 1.11.1 Ruby code with a jmp esp instruction address from a dll outside of PCMAN
Essentially Upon running
Fig1.12 The directory is created. Meterpreter can also be used for executing other dangerous shellcode
We used the msf framework to generate a payload to replace the reverse tcp payload (If we do not want to use meterpreter). This time, to generate a calculator on the ftp server machine’s side.
Fig 1.13 Generating other payload
We replace the payload in our exploit with this, and the calculator opens on the Windows XP machine. We have also tried it with opening a message box. Basically shellcode in the windows payload from metasploit work. Here are a few snapshots
Fig 1.14 Opening a calculator on the vulnerable machine
Fig 1.15 Initiating a popup message on vulnerable machine
EIP is an instruction pointer. If we overwrite EIP with an instruction, the program will run that instruction. If we overwrite EIP with an address that points to a Jmp esp instruction, the jmp esp instruction will be called and it will start executing whichever instruction is seen at ESP.
This is an image when !mona jmp-r esp is run.
Fig 2.1 Output of mona’s jmp –r esp in Windows 8 and 10
We can notice the big difference between the possible jmp/call/push esp commands as compared to Windows XP (in Fig 1.11.3). PCMan seems to be unable to access instructions outside of itself.
For Windows 8 and 10, jmp esp instructions lie at addresses starting with \x00. Which would mean the strings will automatically terminate upon entry of this null byte into the ftp instruction, be it ‘GET’ or ‘RENAME’ etc. Using jmp esp instructions that started with \x00 in windows XP also failed the exploit. Attempts to set the EIP to random addresses found in the disassembler also met with INT3 commands which caused the program to break or some random non-existent address or unexecutable command. The shellcode will also not be able to be reached.
An example of INT3 is as such
Fig 2.2 Program breaking at INT3 command
Basically the idea is that if I want to send a payload after the EIP overwrite, the address should not have null bytes. Because of little Endian, the last character sent will be a null byte if the address starts with null. The string will be terminated before the payload. We can only allow null bytes in addresses if we insert the shellcode before overwriting EIP with an address that startsnull. This explanation was obtained from the link specified below.
The images below show what happens when we declare badchars in our exploit, so that badchars will not be added. Bad chars include null byte as mentioned above. As we can see, even though we added badchars to be excluded from our shellcode, the strings still break up and are not able to send fully. It does not even crash the program.
Fig 2.3.1 Before adding BADCHARS
Fig 2.3.1 After adding BADCHARS
Hence no matter what, if there are null bytes or bad characters in the JMP ESP instruction address, we will not be able to do it. Therefore since !mona jmp –r esp is unable to find us such an address, we look at all the executable modules that PCMan uses. This can be done in the immunity debugger through
Then we proceeded to find JMP ESP instructions in those executable modules. Below is a picture of what we found.
Fig 2.4 Finding the JMP ESP instruction through attaching executable modules
Therefore we used this address, and we could do a jmp esp at EIP. This address 75F913E7 will then be used to overwrite EIP, and the exploit will now work on Windows 8.1 and 10.
With our futile attempts to edit the buffer size, we were not able to patch the program. However we chanced upon a possible fix. We noticed that in an earlier version of the FTP server, no amount of fuzzing would cause a buffer overflow. We read the server config file, which was largely different from the one in 2.07.
Fig 3.1 Comparison between PCMan 1.0b (left) and PCMan 2.07 (right) Server.ini file
We therefore replaced 2.07’s server config file with the old one, and changed StartupWithGUI = 1 (true) and removed ScreenMaxLine. This was all trial and error, in a desperate attempt to find a patch.
Fig 3.2 Altered PCMan 2.07 Server.ini file
Fig 3.3 AVG prompts breach upon “nc 10.124.3.236 4444” entry from Kali Linux
However, this was not consistent and we could not identify the reason AVG could detect it, because no known attack signatures were indicated on the AVG prompt. This could be just due to luck. Therefore, the best way we could do is to not allow anonymous logins. Other ways we were not able to try was to add stack canaries, increase buffer size allocated (changing buffer size in Server.ini did not help) or disallow executable stack.
Through this project/tutorial, we have learnt how to use assembly instructions such as JMP ESP to redirect the flow of the vulnerable program, and execute our shellcode. This assembly instruction could lie in one of the executable system modules (such as SHELL32.dll). We also learnt how to make use of a powerful debugging tool such as the Immunity debugger, and run custom python scripts(such as the mona script by Corelan team) to reach our end objective. We also saw how to detect and remove bad characters that will hamper our plans of attacking the vulnerable machine. We also noticed how different OSes might protect their users from vulnerable programs, such “startnull” addresses, and hiding system modules from attackers. However, if the vulnerable program configures itself to use lots of system modules, the onus is on the vulnerable program to protect itself from attackers by restrict the calling of modules or instructions that lie outside the program itself.
Do leave a comment for any clarification or feedback!