-
Notifications
You must be signed in to change notification settings - Fork 164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add 38 new registry-based persistence techniques #954
Add 38 new registry-based persistence techniques #954
Conversation
We currently have both, most test files are actual malware samples though. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, great enumeration of persistence mechanisms here. I've not seen most of them used in malware so I'm very curious to their prevalence in the wild.
As before comments and suggestions inline.
I'd also like to get another reviewer on all of these because these are quite big updates.
Thank you!
Updates look great to me, thank you very much! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow, awesome work @jorik-utwente ! These updates look good but I did notice that the dynamic
scope should be thread
instead of call
for rules that match against both the registry subkey and value, as the subkey and value strings are likely going to be referenced in separate function calls.
That raises another issue with capa's existing rule set registry value. The dynamic
scope for this rule should also be thread
instead of call
otherwise the optional
match for create or open registry key can never match (it's optional
so it's not show stopper). Thoughts @mr-tz ?
static: function | ||
dynamic: call | ||
att&ck: | ||
- Persistence::Boot or Logon Autostart Execution::Port Monitors [T1547.010] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the rule name use Port Monitors instead of Print Monitors?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for your input, @mike-hunhoff !
To my understanding, the cape report always contains the registry path and value name within one registry write function call.
E.g.
{
...
"category": "registry",
"api": "RegSetValueExW",
"status": true,
"return": "0x00000000",
"arguments": [
{
"name": "Handle",
"value": "0x00000228"
},
{
"name": "ValueName",
"value": "DefaultNotificationsSetting"
},
{
"name": "Type",
"value": "4",
"pretty_value": "REG_DWORD"
},
{
"name": "Buffer",
"value": "1"
},
{
"name": "BufferLength",
"value": "4"
},
{
"name": "FullName",
"value": "HKEY_LOCAL_MACHINE\\SOFTWARE\\Policies\\Google\\Chrome\\DefaultNotificationsSetting"
}
],
...
},
In what case do the registry path and value name spread out over multiple Windows API calls?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not familiar with the underlying representation used by CAPE for API calls, but it appears that it may be adding some syntactic sugar e.g. FullName is a field specific to CAPE.
Other sandboxes may not do this, e.g. VMRay:
<fncall ts="126662" fncall_id="28904" process_id="1" thread_id="1" name="RegOpenKeyExW" addr="0x7ff8e7439670" from="0x7ff8a1570edf">
<in>
<param name="hKey" type="void_ptr" value="0xffffffff80000001"/>
<param name="lpSubKey" type="ptr" value="0x2c7d58c">
<deref type="str" value="Software\\Microsoft\\Windows\\CurrentVersion\\Run"/>
</param>
<param name="ulOptions" type="unsigned_32bit" value="0x0"/>
<param name="samDesired" type="unknown" value="0x2001f"/>
<param name="phkResult" type="void_ptr" value="0xbbdb78"/>
</in>
<out>
<param name="phkResult" type="ptr" value="0xbbdb78">
<deref type="unsigned_64bit" value="0x75c"/>
</param>
<param name="ret_val" type="unknown" value="0x0"/>
</out>
</fncall>
[...]
<fncall ts="126664" fncall_id="28906" process_id="1" thread_id="1" name="RegSetValueExW" addr="0x7ff8e743b080" from="0x7ff8a1574d2c">
<in>
<param name="hKey" type="void_ptr" value="0x75c"/>
<param name="lpValueName" type="ptr" value="0x2c7b5b4">
<deref type="str" value="Windows Defender Updater"/>
</param>
<param name="Reserved" type="unsigned_32bit" value="0x0"/>
<param name="dwType" type="unsigned_32bit" value="0x1"/>
<param name="lpData" type="ptr" value="0x2c7d33c">
[...]
</param>
<param name="cbData" type="unsigned_32bit" value="0x7a"/>
</in>
[...]
</fncall>
You can see the subkey Software\Microsoft\Windows\CurrentVersion\Run
and value Windows Defender Updater
are referenced in separate API calls RegOpenKeyExW
and RegSetValueExW
, respectively (0x75c
is the handle to the opened registry subkey).
Although this does not appear to be the case for CAPE, we assume most sandboxes will log API calls, including parameter and return names and values, according to their documentation, e.g. https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regsetvalueexw.
@mr-tz do you know if the CAPE extractor is extracting the FullName field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think CAPE is doing some enhancements to the API trace by enhancing values. We cannot assume this for all sandbox backends so to be flexible rules should account for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, you are right, CapeVM resolves the FullName, which is not a function argument.
To support other sandbox backends that only log the raw arguments, there will be an impact on the performance of the rules:
- Extra false positives: Reading a registry key in a rule and writing to a completely different key will result in a false positive. Some registry paths are frequently read from, so rules mentioning these paths will result in many false positives.
- For other sandbox backends, detection can still easily be evaded. E.g. if you want to write to HKCU\a\b\c, you can do:
a = RegOpenKey(HKCU, "a")
b = RegOpenKey(a, "b")
c = RegOpenKey(b, "c")
RegSetValue(c, key, val)
In this example HKCU\a\b\c
will never be passed as an argument to a Windows API call, so we cannot detect it.
I expect that for some rules, changing the scope from call to threat will result in many false positives. I think that as a result we will have to remove these rules, which is a pitty.
When writing rules for the threat scope you have to be very careful, e.g., look at the current run registry key rule: https://github.com/mandiant/capa-rules/blob/master/persistence/registry/run/persist-via-run-registry-key.yml
It has many false positives because it matches the following:
- HKEY_CURRENT_USER / HKEY_LOCAL_MACHINE mentioned anywhere (e.g., to read a key, which happens a lot), and
- "Software\Microsoft\Windows\CurrentVersion" mentioned anywhere (common registry path), and
- "run" mentioned anywhere. (Common word, occurs a lot)
As a result, there are a bunch of false positives for this rule.
My point is: I understand you want to support different sandbox backends, but it makes writing accurate registry write-rules more challenging and less accurate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good points. For much better handling moving forward we need a different approach (like a new scope within a certain range, #951 (comment)).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good discussion here!
For much better handling moving forward we need a different approach (like a new scope within a certain range, #951 (comment)).
Agree.
I expect that for some rules, changing the scope from call to threat will result in many false positives. I think that as a result we will have to remove these rules, which is a pitty.
In spirit, I agree with you. Though, I'm curious about this in practice. It seems like there might be FPs, but could we test this a bit? Maybe the behavior isn't quite as bad as we worry about.
I only suggest this (which doesn't feel very good) because we think we have a plan for a better implementation (#951) so many a little inaccuracy in the meantime is acceptable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That raises another issue with capa's existing rule set registry value. The dynamic scope for this rule should also be thread instead of call otherwise the optional match for create or open registry key can never match (it's optional so it's not show stopper). Thoughts @mr-tz ?
@mike-hunhoff mandiant/capa#2124 raises the issue that capa doesn't validate scope dependencies correctly, so there are probably more of these hidden mistakes lurking around.
In the meantime, I agree this isn't a showstopper, since it's only an optional term that can't match. We have a bit of work to do to get all these scopes straightened out...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Willi and I discussed this offline:
- even if there are some minor issues, we can address those when we have better thread/region/call scope
- if we run into many FPs (or any FNs) we can adjust on a case by case basis
- let's see how common these are in practice
Add 38 new registry-based persistence techniques.
All new rules have references to online resources describing the technique.
This PR requires #952 to be merged.
I am planning to add examples later. Would it be preferred to have POC implementations, or real-world malware that uses the technique?