Sunday, July 18, 2021

Parsing HTTPSENDREQUESTEX'S LPINTERNET_BUFFERSA struct with Frida

 This post is about how to read the LPINTERNET_BUFFERSA struct used by the Windows API HttpSendRequestExA. The background behind this post is answering an StackOverflow question but with a more detailed explanation.

To perform this example, instead of writing an application I will use Internet Explorer and I will use FRIDA on it. That said, the definition of the struct used in HttpSendRequestExA/W is:

typedef struct _INTERNET_BUFFERSA {

    DWORD                     dwStructSize;

    struct _INTERNET_BUFFERSA *Next;

    LPCSTR                    lpcszHeader;

    DWORD                     dwHeadersLength;

    DWORD                     dwHeadersTotal;

    LPVOID                    lpvBuffer;

    DWORD                     dwBufferLength;

    DWORD                     dwBufferTotal;

    DWORD                     dwOffsetLow;

    DWORD                     dwOffsetHigh;

} INTERNET_BUFFERSA, *LPINTERNET_BUFFERSA;

There are two things to note here, the first one is that the first member is the structure size and the second member of the struct is a pointer to the next one. A second thing to take into account is that LPCSTR is going to be a LPCWSTR if HttpSendRequestExW is used instead.

That said, this structure is pretty simple to parse, there is only three tricky members here:

  • LPCSTR size varies depending on the Process.pointerSize()
  • LPVOID size varies depending on the Process.pointerSize()

Next is a pointer to the next structure, so it will also vary depending on the Process.pointerSize()

With this in mind for a 32-bit process where Process.pointerSize=4 the offsets are:

DWORD                     dwStructSize; // 0

struct _INTERNET_BUFFERSA *Next; // 4

LPCSTR                    lpcszHeader; // 8

DWORD                     dwHeadersLength; // 12

DWORD                     dwHeadersTotal; // 16

LPVOID                    lpvBuffer; // 20

DWORD                     dwBufferLength; // 24

DWORD                     dwBufferTotal; // 28

DWORD                     dwOffsetLow; // 32

DWORD                     dwOffsetHigh; // 36

If the calculations are correct, dwStructSize will be 40 for a 32-bit process or 0x28 in hex. The following script is able to intercept correctly the HttpSendRequestW structure for a 32-bit process:

Interceptor.attach(Module.getExportByName(null, "HttpSendRequestExW"), {

onEnter (args) {

let internetBufferStruct = args[1];

console.log("Struct size: " + internetBufferStruct.readPointer());

console.log("*Next: " + internetBufferStruct.add(Process.pointerSize).readPointer());  

console.log("lpcszHeader: " + internetBufferStruct.add(Process.pointerSize * 2).readPointer());  

console.log("dwHeadersLength: " + internetBufferStruct.add(12).readPointer());  

console.log("dwHeadersTotal: " + internetBufferStruct.add(16).readPointer());  

let dwBufferLength = parseInt(internetBufferStruct.add(24).readPointer());

console.log("lpvBuffer: " + internetBufferStruct.add(Process.pointerSize * 5).readCString(dwBufferLength));  

console.log("dwBufferLength: " + dwBufferLength);  

console.log("dwBufferTotal: " + internetBufferStruct.add(28).readPointer());  

console.log("dwOffsetLow: " + internetBufferStruct.add(32).readPointer());  

console.log("dwOffsetHigh: " + internetBufferStruct.add(36).readPointer());  

}

});

And this is the result in a 32-bit process where as predicted the first member dwStructSize is 40(0x28) as predicted:

For a 64-bit process the only changes required are the differences in offsets generated by every pointer member and adjust the other members accordingly.

Zanubis updates with screenshot recording

As always, IOCs and targets at the end. Previous updates of this family at:  https://www.entdark.net/2022/09/zanubis-latam-banking-trojan.ht...