Bypassing DEP - The Savant way

Those who follow my twitter account might have noticed in the last few weeks an increasing activity regarding writable addresses, 2k3 servers, and DEP bypasses. Well, today I can bring the concluding explanation about all this recent fuzz. For the last 3 or 4 weeks I've been trying to pick a bug in some code and dissect it to build my own exploit from it. Even thought I came across several good choices, I got hooked with this one. To find out what the bug was all about, I also came across a couple of public releases that exploited the bug to help on the way. Unfortunately, or I should say fortunately, all the publicly available releases were meant to be used against XP SP2 or older versions of Windows. Since re-covering somebody elses work seemed pretty boring to me, I decided to take the bug for a spin myself and take it a step further. In essence, the bug is a classical example of a buffer overflow bug in the stack. The user performs some HTTP requests to the server and this answers with the content the user asked for or...doesn't he? As it turns out, if we manage to craft a specially designed non-standard HTTP request, we can manage to sneak some juicy code. But as I said earlier, other publicly available exploits already do so, why bother writing another one then? Thing is, as far as I can tell, no one has yet developed a exploit for one of the most extended Server versions of Windows, this being 2003. I won't delve into the details of what DEP is and how it works. Before getting any further, remember the conditions to which you agree by reading this content. The bug on which I elaborate in this post is yet to be fixed. The project seems to be abandoned at the time of this writing and even thought a bug tracking entry has was opened for this very issue almost 3 months ago, there is no apparent activity in the matter. Also, I don't find ethic to provide a working exploit for the bug for two reasons:
  • Anybody with the knowledge to understand this, can create his own exploit from this.
  • The bug still isn't fixed in the last release version.
Mandatory warnings aside, lets begin. If we begin our research setting a breakpoint in calls to recv() and recvfrom(), we will notice that everytime we receive a request it get's handled by a wrapper at 0x00401023 that performs some basic checks and returns. Now the server checks to see wether our request is a proper HTTP request or not. Since it isn't a valid HTTP request execution flows all the way up to a function in 0x0040BFA0 which is where the meat of the bug lies. Here, a call is made to 0x00401285 to concate the string we provided as a path in our packet with the instance's working directory. At this point, some checks are performed with our path such as checking if it's asking for a /cgi-bin directory and so on. Afterwards, several dangerous uses of strcpy() are made, which in this case, don't trigger the bug. The bug is triggered in this code sequence:
.text:00411A6A                 mov     edx, [ebp+arg_14]
.text:00411A6D                 add     edx, [ebp+var_8]
.text:00411A70                 mov     eax, [ebp+Str1]
.text:00411A73                 add     eax, [ebp+var_C]
.text:00411A76                 mov     cl, [eax]
.text:00411A78                 mov     [edx], cl       ; PWN stackDespite the fact that the bug is a fairly classic stack overflow, as we will soon see, the problem lies in exploiting it. Anyhow, after we exit from
0x00401285, execution of 0x0040BFA0 continues up to the end where our good old friend ret provides us control of execution. Now let's establish the constraints of the area of work. Our request will resemble the shape of a traditional HTTP request:

METHOD | /

Regarding available spaces:
  • recv() allows us a maximum of 0x4000 bytes.
  • METHOD can have 24 bytes max.
  • can contain up to 250 bytes until (and not including) the return address + 32 more bytes after it.
As I said earlier, the difficulty in exploiting this bug in Windows 2003 Server SP1 lies in the existence of DEP. In both current releases of Server version of Windows (2003 and 2008), the default policy regarding DEP is the so-called OptOut in which by default, all processes running adhere to DEP unless they're manually and explicitly added to a list of excluded apps. In our case this means that unless the system administrator has stated that Savant should run with DEP disabled, it's on. Under this circumstances, the first thing we need to do is bypass DEP and then take care of running our code. The most common and extended way of doing so is using the technique described in this article by skape and Skywing in Vol.2 of Uninformed magazine. This article describes a method by which instead of redefining the memory protection settings of a certain area, we will disable DEP for the entire process by returning to sections of code that would allow us to do so, instead of a classic ret2libc. In my particular quest to achieve this, I found that the exact situation described in the paper wasn't possible in my version of the platform (Spanish). The suggested code in ntdll!LdrpCheckNXCompatibility wasn't all the same. Hence, I searched in memory for similar code patterns that would allow me to do the trick. This is the code I found:
.text:7C83E413                 mov     [ebp+var_4], 2
.text:7C83E41A
.text:7C83E41A loc_7C83E41A:                           ; CODE XREF: sub_7C83592A+45j
.text:7C83E41A                 push    4
.text:7C83E41C                 lea     eax, [ebp+var_4]
.text:7C83E41F                 push    eax
.text:7C83E420                 push    22h
.text:7C83E422                 push    0FFFFFFFFh
.text:7C83E424                 call    ZwSetInformationProcess
.text:7C83E429                 jmp     finishThis code performs the same thing as the code described in the article. The problem lies in the code that executes after the jump:
.text:7C835975                 or      byte ptr [esi+37h], 80h
.text:7C835979                 pop     esi
.text:7C83597A
.text:7C83597A locret_7C83597A:                        ; CODE XREF: sub_7C83592A+Dj
.text:7C83597A                 leave
.text:7C83597B                 retn    4As you can see, there a
leave instruction right before aret. This effectively sets esp and ebp registers back to what the stack frame of the caller in a single instruction. Unfortunately for us, this is how the code in 0x0040BFA0 returns (and gives us control of execution):
.text:0040C102                 pop     edi
.text:0040C103                 pop     esi
.text:0040C104                 pop     ebx
.text:0040C105                 mov     esp, ebp
.text:0040C107                 pop     ebp
.text:0040C108                 retnIn our effort to build a sequence of ret2code calls, we need to setup a fake frame in the stack with our return values. However ebp gets pop'd right before our ret value therefore our future fake-frame will be the one pointed by ebp. This might sound like a juicy but keep in mind that stack address generally look like
0x00XXXXXX and thus, contain null bytes. This means that we need to somehow put a stack value with null bytes in ebp but without interfering with the rest of the exploit. After testing several methods, the one that finally worked works as follows. If we cannot provide an address with NULL bytes, why not use some sort of logical operations to compute the value we desire? What I came up with is this sequence of code inside shell32.dll that after XOR-ing the value in ebp with a constant value, will allow us to have a value we want:
.text:7CA0E9BC                 or      cl, ch
.text:7CA0E9BE                 xor     ebp, 9090FFFFh
.text:7CA0E9C4                 nop
.text:7CA0E9C5                 nop
.text:7CA0E9C6                 nop
.text:7CA0E9C7
.text:7CA0E9C7 loc_7CA0E9C7:                           ; DATA XREF: .text:off_7C90FAE4o
.text:7CA0E9C7                 sub     dword ptr [esp+4], 20h
.text:7CA0E9CC                 jmp     sub_7CA0DF22Which jumps to:
.text:7CA0DF22                 mov     edi, edi
.text:7CA0DF24                 push    ebp
.text:7CA0DF25                 mov     ebp, esp
.text:7CA0DF27                 push    [ebp+arg_8]
.text:7CA0DF2A                 push    [ebp+arg_4]
.text:7CA0DF2D                 push    offset off_7C8DC0A0
.text:7CA0DF32                 push    [ebp+arg_0]
.text:7CA0DF35                 call    SHLWAPI_219
.text:7CA0DF3A                 pop     ebp
.text:7CA0DF3B                 retn    0ChDespite the second chunk of code calling other functions a push ebp in the prolog and a pop ebp in the epilog will keep our newly created value safe. Now we can return to the code sequence that we described earlier that will effectively disable DEP and, since we now control ebp, can continue executing wherever we want without protection restrictions. Following this we simply return to a
jmp esp address and continue traditional exploitation from then on. The only problem with this particular environment is the fact that we only have 250 bytes of space and the shellcode needs to be alphanumerically encoded. The final scheme of what we should build is the following: + + / + + + + + + + + + + '\r\n\r\n' There a couple of things worth to mention in this method. First, after the DEP disabling code sequence return value, there are three addresses named referenceable. At this three offsets, placing 4 byte long writable addresses is mandatory in order to redirect flow where we intend, otherwise some violations happen in the way. Second, remember that the value in is the value that we will XOR in the code. Also, clearly this exploit relies a lot in hardcoded addresses, I understand that portability might be an issue porting the exploit to other versions of the same platform but this was just a proof of concept exercise and not a market exploit. To finish this lengthy post, I would like to greet Ruben and Joxean from 48bits for their altruistic support and patience. You can find a more fun oriented, spanish version of this post here.