For this challenge we’re provided two files, “directions.txt” and “yara_samples.7z”. The directions file lays out the rules of the challenge, which is to create a YARA rule that must use the hexadecimal format to match on all, and only, the 56 samples provided. You may use wildcard “?” characters but not hexadecimal jumps (“[1-6]”) and it must be in the below template.

rule yara_challenge

{

strings:

$yara_challenge = { ** ** ** ** ** ** }

condition:

all of them 

}

Additionally, there is a hint that states the answer should contain 308 wildcard “?” characters.

Basically, we need to look for matching code blocks within the samples and create a YARA rule to match it. Knowing there are 308 possible wildcards gives us some indication of the size of this code block, which is a fairly decent size chunk – low end of around 154 bytes or high end of around 616 bytes.

The file sizes range between 94KB and 677KB, so I grab one of the 94KB files (“7f63e65ab460ff8ad607ede5bedb9573263015ba81824c3896f5416969353dba”) to look at first, as it will potentially have the least amount of clutter. I also grab another slighty larger file (121KB “1f0943c56d45ea07c7bd9dd245001aab6566afb64b78c5ebf1fdaebfdbe0e619”) as a base for comparison.

There were roughly 30-40 functions in 7f63e65 that IDA F.L.I.R.T. did not recognize so I started with those. Since it’s a relatively small number, I decided to do a first pass of visual inspection of the functions and their graph overview and then comparing visually to functions found in sample 1f0943. The function at 0x10001250 in 7f63e65 visually matched the very first function in 1f0943 so I began writing a YARA rule based off these.

The function itself is just loading a few DLLs and getting the address for a number of functions but so far so good, they appear to line up.

Flipping over to the Hex View we can see that the code begins to differ when it comes to the addresses for the DLLs or offsets for function names, so we need to match on the instructions and wildcard between them.

In the below image I’ve highlighted the matching areas based on instruction. The general pattern is PUSH (0x57 or 0x68), followed by four bytes that vary, MOV (0x8B or 0xA3), and then CALL (0xFF). Some of the variables are the same, in that they are doing CALL ESI so we can match the entire section and avoid using wildcards for these.

We end up with the below once we match up the bytes.


53 56 8B 35 ?? ?? ?? ?? 57 68 ?? ?? ?? ?? FF D6

68 ?? ?? ?? ?? 8B F8 FF D6 68 ?? ?? ?? ?? FF D6

68 ?? ?? ?? ?? FF D6 8B 35 ?? ?? ?? ?? 68 ?? ??

?? ?? 57 8B D8 FF D6 68 ?? ?? ?? ?? 57 A3 ?? ??

?? ?? FF D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF

D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6 68 ??

?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6 68 ?? ?? ?? ??

57 A3 ?? ?? ?? ?? FF D6 68 ?? ?? ?? ?? 57 A3 ??

?? ?? ?? FF D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ??

FF D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6 68

?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6 68 ?? ?? ??

?? 57 A3 ?? ?? ?? ?? FF D6 68 ?? ?? ?? ?? 53 A3

?? ?? ?? ?? FF D6 68 ?? ?? ?? ?? 53 A3 ?? ?? ??

?? FF D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6

68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF D6 68 ?? ??

?? ?? 57 A3 ?? ?? ?? ?? FF D6 68 ?? ?? ?? ?? A3

?? ?? ?? ?? 57 FF D6 68 ?? ?? ?? ?? 57 A3 ?? ??

?? ?? FF D6 68 ?? ?? ?? ?? 57 A3 ?? ?? ?? ?? FF

D6 5F 5E A3 ?? ?? ?? ?? 5B C3

Throwing this into our YARA template, we can quickly validate whether our function will match across the entire set.

$ yara -s -m yara_challenge.yar ./

yara_challenge [] 
.//155fbef2536ef18fa0164f0ce3c40c3724122c0aef2246a1c8678489d50ec89e

0x400:$yara_challenge: 53 56 8B 35 18 D0 40 00 57 68 F8 F1 40 00 FF
D6 68 08 F2 40 00 8B F8 FF D6 68 18 F2 40 00 FF D6 ...

yara_challenge []
.//10f419f90602e818727a8ce8fe03796ec0094528467942f6a7229477ee55902a

0x400:$yara_challenge: 53 56 8B 35 18 D0 40 00 57 68 F8 F1 40 00 FF
D6 68 08 F2 40 00 8B F8 FF D6 68 18 F2 40 00 FF D6 ...

yara_challenge []
.//14fe68c477fa6c02fe1328dfefc93ded488aa31ad5765c7be339cb83b537587a

0x1b3a:$yara_challenge: 53 56 8B 35 3C 10 01 10 57 68 84 15 01 10 FF
D6 68 74 15 01 10 8B F8 FF D6 68 68 15 01 10 FF D6 ...

yara_challenge []
.//1f0943c56d45ea07c7bd9dd245001aab6566afb64b78c5ebf1fdaebfdbe0e619

<TRUNCATED>

$ yara -s -m yara_challenge.yar ./ |grep "0x" |wc -l

56

Great, so we know that our signature is good but if we count the wildcards used, we’re at 352. The hint suggested we needed 308 so we’ll need to trim it down some more.

As the majority of MOV instructions were MOV r32, it means that the last 4 bytes are the address offset for the data being moved into a register, which incidentally all end with a zero. For example, 0x8B359C200110 is “MOV ESI, 0x1001209C” and 0x8B3520F04000 is “MOV ESI, 0x0040F020”. This allows us to take the last byte before the subsequent PUSH and wildcard only the lower four bits; the value “10” and “00” are now both covered by “?0”. Applying this approach to all of the addresses within the function allows us to get down to the expected 308 wildcards hinted at in the instructions.

Below is the final YARA rule.


$ cat yara_challenge.yar

rule yara_challenge

{

strings:

$yara_challenge = { 53 56 8B 35 ?? ?? ?? ?0 57 68 ?? ?? ?? ?0 FF D6
68 ?? ?? ?? ?0 8B F8 FF D6 68 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 FF D6 8B
35 ?? ?? ?? ?0 68 ?? ?? ?? ?0 57 8B D8 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ??
?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3
?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0
57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ??
?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68
?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF
D6 68 ?? ?? ?? ?0 53 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 53 A3 ?? ?? ??
?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ??
?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 A3
?? ?? ?? ?0 57 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ??
?0 57 A3 ?? ?? ?? ?0 FF D6 5F 5E A3 ?? ?? ?? ?0 5B C3 }

condition:

all of them

}

We can now send our rule to the server and see if it passes the test. Below is the returned output.


Recieved rule is:

rule yara_challenge{ strings: $yara_challenge = { 53 56 8B 35 ?? ??
?? ?0 57 68 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 8B F8 FF D6 68 ?? ?? ?? ?0
FF D6 68 ?? ?? ?? ?0 FF D6 8B 35 ?? ?? ?? ?0 68 ?? ?? ?? ?0 57 8B D8 FF
D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ??
?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ??
?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57
A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ??
?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ??
?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 53 A3 ?? ?? ?? ?0 FF D6
68 ?? ?? ?? ?0 53 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0
FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ??
?? ?0 FF D6 68 ?? ?? ?? ?0 A3 ?? ?? ?? ?0 57 FF D6 68 ?? ?? ?? ?0 57 A3
?? ?? ?? ?0 FF D6 68 ?? ?? ?? ?0 57 A3 ?? ?? ?? ?0 FF D6 5F 5E A3 ?? ??
?? ?0 5B C3 } condition: all of them}

If it looks fragmented please re-submit

SUCCESS! KEY IS: PAN{AllByMyself}

The challenge answer is “PAN{AllByMyself}”.