Day 6 of 100 Discontiguous Days of YARA
Improving YARA writing skills by writing more YARA rules.
Summary
Partaking in Greg’s #100DaysOfYARA, but to be honest it’s more likely to be #100DiscontiguousDaysOfYARA for me - if I make it that far.
I doubt that the rules shared these 100 days will contain any truly original ideas, but I’d still like to share what I’ve learned.
Day 6
This is the first of at least a 3 (maybe 4) part series targeting SFX 4.x files. Yes, I’m absolutely stretching my #100DaysOfYARA content.
The 4 parts are:
- Identifying RAR 4.x file headers
- Targeting RAR 4.x embedded file names
- Targeting RAR 4.x embedded file content (non-compressed)
- Targeting RAR 4.x sfx script content (maybe)
Credit
First, want to call out @notareverser’s excellent #100DaysOfYARA YARA rule targeting WinRAR_SFX files.
Sometimes 'file' just doesn't cut it...#100DaysOfYARA
— French (@notareverser) January 7, 2022
rule WinRAR_SFX
{
strings:
$chk = {80 7? 01 61 75 ?? 80 7? 02 72 75 ?? 80 7? 03 21 75 ?? 80 7? 04 1a 75 ?? 80 7? 05 07 75}
$mag = {52 61 72 21 1a 07}
condition:
all of them
}
Yes, I skipped byte 0
I really like the opcode ($chk) usage in the rule to target the Rar! check in the assembly.
I’m going to incorporate this check into my rule (Thank You @notareverser!) versus targeting the presence of the pdb string:
pe.pdb_path icontains "d:\\Projects\\WinRAR\\sfx\\build\\sfxrar32\\Release\\sfxrar.pdb"
Interesting side note, WinRAR SFX files created against version 4.x have a lower case
d
drive letter. Version 5 uses the same pdb string but the drive letter is an upper case:D
The Rule
import "math"
rule sfx_4x_file {
meta:
author = "xorhex"
credit = "Credit for $chk goes to @notareverser"
Description = "Search for RAR File Headers"
HundredDaysOfYARA = "Day 6"
strings:
$rar4x = { 52 61 72 21 1A 07 00 }
$file_header = { 74 [12] 02 [8] (0f | 14 | 1A | 1D | 24 | 30 ) ( 30 | 31 | 32 | 33 | 34 | 35) [2] 20 00 00 00 }
$chk = {80 7? 01 61 75 ?? 80 7? 02 72 75 ?? 80 7? 03 21 75 ?? 80 7? 04 1a 75 ?? 80 7? 05 07 75}
condition:
$chk
and
#rar4x > 0
and
for any i in (1..math.min(500, #file_header)) : (
@file_header[i] > @rar4x
)
}
This rule identifies WinRAR SFX files targeting RAR version 4.x. RAR version 5.x has a different signature: 0x52 0x61 0x72 0x21 0x1A 0x07 0x01 0x00
. Whereas version 4.x signature is: 0x52 0x61 0x72 0x21 0x1A 0x07 0x00
. This distinction is important as the RAR File Header differs between the two.
File Header Format
Each file embedded in a RAR file is preceded with a File Header structure that we can abuse use to target embedded file metadata and at times the file content itself.
This part still needs work, but this is what I’ve pieced together so far about the RAR version 4.x File Header:
struct rar_4_file_header{
u16 header_crc;
Header_Type header_type;
File_Header_Flags header_flags;
u16 header_size;
u32 PackSize;
u32 UnpSize;
OSType HostOS;
u32 FileCRC;
u32 FileTime;
Unpack_Algo UnpVer;
PackMethod Method;
u16 NameSize;
if (HostOS == OSType::Win32){
WinFileAttributes FileAttr;
}
else {
u32 FileAttr;
}
if (header_flags.LHD_LARGE == 1){
u32 HighPackSize;
u32 HighUnpSize;
}
char FileName[NameSize];
if (header_flags.LHD_SALT == 1){
u64 Salt;
}
if (header_flags.LHD_EXTTIME == 1){
u16 ExtTime;
}
u8 PackedData[PackSize];
};
Certain fields are particulary interesting from a YARA rule perspective, allowing us to target the RAR File Header.
Header Type
The possible header types are:
enum Header_Type : u8 {
Mark_Head = 0x72,
Main_Head = 0x73,
File_Head = 0x74,
Comm_Head = 0x75,
AV_Head = 0x76,
Sub_Head = 0x77,
Protect_Head = 0x78,
Sign_Head = 0x79,
NewSub_Head = 0x7a,
EndArc_Head = 0x7b
};
So to target File Headers use 0x74
.
OS Type
The possible OS types for RAR version 4 are:
enum OSType : u8 {
MS_DOS = 0x0,
OS_2 = 0x1,
Win32 = 0x2,
Unix = 0x3,
Mac_OS = 0x4,
BeOS = 0x5
};
Going to assume the OS Type is Win32 so 0x2
Unpack Version
The possible values for the Unpack Version are:
enum Unpack_Algo : u8 {
Unpack15 = 15,
Unpack20 = 20,
unpack20 = 26,
Unpack29 = 29,
Unpack29 = 36,
NotPacked = 48
};
To handle this in the YARA rule do: (0f | 14 | 1A | 1D | 24 | 30 )
Pack Method
The possible pack method values are:
enum PackMethod : u8 {
Store = 0x30,
Fastest = 0x31,
Fast = 0x32,
Normal = 0x33,
Good = 0x34,
Best = 0x35
};
This is represented in the YARA rule as ( 30 | 31 | 32 | 33 | 34 | 35)
FileAttr - WinFileAttributes
Assuming this is mapped out correctly 20 00 00 00
is WinFileAttributes.ARCHIVE
bitfield WinFileAttributes {
padding : 19;
ENCRYPTED : 1;
NOT_CONTENT_INDEXED : 1;
OFFLINE : 1;
COMPRESSED : 1;
REPARSE_POINT : 1;
SPARSE_FILE : 1;
READONLY : 1;
HIDDEN : 1;
SYSTEM : 1;
VOLUME : 1;
DIRECTORY : 1;
ARCHIVE : 1;
DEVICE : 1;
NORMAL : 1;
};
This value, 20 00 00 00
, should probably be experimented with some to make sure it doesn’t filter out any entries of interest.
Pay Dirt
File Header Match One
File Header Match Two
We can confirm all files were found by dumping the files to see if the results match: