Windows.Carving.BRc4

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.


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))