This artifact extracts Brute Ratel C4 (BRc4) configuration from a byte stream, process or file on disk. BRc4 is an emerging red-teaming and adversarial attack simulation tool.
The User can define bytes, file glob, process name or pid regex as a target. The artifact firstly discovers BruteRatel configuration and extracts bytes, before parsing with Velociraptor Binary Parser.
This content simply carves the configuration and does not unpack files on disk. That means pointing this artifact as a packed or obfuscated file may not obtain the expected results.
name: Windows.Carving.BRc4
author: Matt Green - @mgreen27
description: |
This artifact extracts Brute Ratel C4 (BRc4) configuration from a byte stream,
process or file on disk. BRc4 is an emerging red-teaming and adversarial
attack simulation tool.
The User can define bytes, file glob, process name or pid regex as a target.
The artifact firstly discovers BruteRatel configuration and extracts bytes,
before parsing with Velociraptor Binary Parser.
* BRc4's configuration consits of 8 characters inside several sections.
* Character lists reversed in order
* This list of characters is: either base64 + RC4 encoded or in clear text.
This content simply carves the configuration and does not unpack files on
disk. That means pointing this artifact as a packed or obfuscated file may not
obtain the expected results.
reference:
- https://unit42.paloaltonetworks.com/brute-ratel-c4-tool/
- https://github.com/Immersive-Labs-Sec/BruteRatel-DetectionTools
parameters:
- name: TargetBytes
default:
- name: TargetFileGlob
default:
- name: PidRegex
default: .
type: regex
- name: ProcessRegex
default: .
type: regex
- name: DecodeKey
default: "bYXJm/3#M?:XyMBF"
- name: FindConfig
type: hidden
description: Final Yara option and the default if no other options provided.
default: |
rule BruteRatelConfig
{
strings:
$config_block = { 50 48 b8 [8] 50 68}
$split_marker = { 50 48 b8 [8] 50 48 b8 }
condition:
$config_block and #split_marker > 30
}
sources:
- precondition:
SELECT OS From info() where OS = 'windows'
query: |
-- dynamic functions to reverse list order
LET _Reverse(data) = SELECT *, count() as Count FROM foreach(row=data) ORDER BY Count desc
LET Reverse(data) = _Reverse(data=data)._value
-- binary parse profile to extract BRc4 configuration. NOTE: need reverse dynamic fuctions above
LET PROFILE = '''[
[BRc4Config, 0, [
["__FindConfig",0, "String",{term_hex: "5048b8"}],
["Reversed","x=>len(list=x.__FindConfig) + 3", "String", { term_hex: "5068" }],
["ConfigData",0,"Value",{ "value": "x=>join(array=Reverse(data=split(string=x.Reversed,sep_string=unhex(string='5048b8'))))" }],
["DecodedConfig",0,"Value",{ "value": "x=>crypto_rc4(string=base64decode(string=x.ConfigData),key=DecodeKey)" }],
["Config",0,"Value",{ "value": "x=>if(condition= x.DecodedConfig, then=x.DecodedConfig, else=x.ConfigData)" }],
]
]]'''
-- Bytes usecase: scan DataBytes for BRc4 config
LET ByteConfiguration = SELECT
Rule,
len(list=TargetBytes) as Size,
hash(path=TargetBytes,accessor='data') as Hash,
String.Offset as HitOffset,
parse_binary(accessor="data",filename=String.Data,profile=PROFILE,struct='BRc4Config').Config as _RawConfig
FROM yara(
files=TargetBytes,
accessor='data',
rules=FindConfig,
number=1,
context=1000
)
-- Glob usecase: find target files
LET TargetFiles = SELECT OSPath,Size
FROM glob(globs=TargetFileGlob) WHERE NOT IsDir
-- Glob usecase: Extract config from files in scope
LET FileConfiguration = SELECT * FROM foreach(row=TargetFiles,
query={
SELECT
Rule,
OSPath, Size,
hash(path=OSPath) as Hash,
String.Offset as HitOffset,
parse_binary(accessor="data",filename=String.Data,profile=PROFILE,struct='BRc4Config').Config as _RawConfig
FROM yara(
files=OSPath,
rules=FindConfig,
number=1,
context=1000
)
})
-- find velociraptor process
LET me <= SELECT * FROM if(condition= NOT ( TargetFileGlob OR TargetBytes ),
then = { SELECT Pid FROM pslist(pid=getpid()) })
-- find all processes and add filters
LET processes = SELECT Name as ProcessName, Exe, CommandLine, Pid
FROM pslist()
WHERE
Name =~ ProcessRegex
AND format(format="%d", args=Pid) =~ PidRegex
AND NOT Pid in me.Pid
-- scan processes in scope with our rule, limit 1 hit and extract context to parse
LET ProcessConfiguration = SELECT * FROM foreach(
row=processes,
query={
SELECT
Rule,
Pid, ProcessName, CommandLine,
String.Offset as HitOffset,
parse_binary(accessor="data",filename=String.Data,profile=PROFILE,struct='BRc4Config').Config as _RawConfig
FROM yara(
files=format(format="/%d", args=Pid),
accessor='process',
rules=FindConfig,
number=1,
context=1000
)
})
-- generate results remove any FPs
SELECT *,
{
SELECT _value
FROM foreach(row=split(string=_RawConfig,sep_string='|'))
WHERE _value
} as BRc4Config,
_RawConfig
FROM if(condition=TargetBytes,
then=ByteConfiguration,
else= if(condition=TargetFileGlob,
then= FileConfiguration,
else= ProcessConfiguration))