หากใครเคยคุ้นชินเรื่อง IDS/IPS (Intrusion Detection System/Intrusion Prevention System) ซึ่งเป็นระบบในการตรวจจับการโจมตี รวมถึงการใช้งานที่ผิดปกติต่างๆ สิ่งที่เครื่องมือเหล่านี้ใช้เพื่อตรวจจับเป็นด่านแรกคือ Signature นั่นเอง โดย IDS/IPS ที่เป็น Open Source ที่ได้รับความนิยมคือ Snort ซึ่งเราสามารถเขียน Signature ขึ้นมาเพื่อป้องกันหรือดักจับพฤติกรรมใดๆได้เอง แต่ Signature ดังกล่าวเป็นอะไรที่ซับซ้อนและเน้นเรื่อง traffic Rule เป็นหลัก จึงได้มีการคิด Rule ที่ซับซ้อนน้อยลงและใช้สำหรับการค้นหาไฟล์ที่มีลักษณะเป็น malware ตาม Indicator of Compromise (IoC) และมีความทรงประสิทธิภาพนั่นคือ Yara Rule นั่นเอง
Yara Rule เป็น Signature สำหรับการหา”ไฟล์”ที่ตรงกับที่เรากำหนด โดยเราสามารถกำหนด “เงื่อนไข” (Condition) ด้วยลักษณะต่างๆของไฟล์ ไม่ว่าจะเป็น MD5, string, Hex String, ขนาดของไฟล์ โดยเราสามารถเขียน Yara Rule แล้วสามารถนำไปใช้งานในเครื่องมือป้องกันระดับ Enterprise มากมายไม่ว่าจะเป็น FireEye, CarbonBlack, Checkpoint,etc รวมถึงใน Antivirus เองก็เริ่มมีการนำ Yara Rule ไปใช้แล้วอย่าง ClamAV เป็นต้น อีกทั้งเราสามารถเขียน Yara Rule ทีเดียวแล้วนำไปในในระบบปฎิบัติการที่แตกต่างกันอีกด้วย ไม่ว่าจะเป็น Windows, Linux, MacOS เองก็ตาม โดยไม่ต้องพึ่ง Engine ใดๆนอกเหนือจาก Yara binary อีกด้วย
หากถามว่า Yara Rule นั้นเหมาะกับการใช้งานแบบใด ให้เรามองเรื่องของการใช้งาน ณ ปัจจุบันของเราเป็นหลักก่อนดีกว่า เนื่องด้วยการใช้งานเครือข่าย internet ส่วนใหญ่นั้นมีการเข้ารหัส traffic ซะส่วนใหญ่ทำให้การโอนถ่ายไฟล์ระดับ network นั้นยากที่จะอ่านได้ ซึ่งส่งผลให้เกิดการ bypass Network Security perimeter เข้ามาได้ง่าย ก็กลายเป็นว่าต้องไปพึ่งที่ Endpoint Protection อย่าง Antivirus หรือ APT Solution ซะส่วนใหญ่ที่จะคอยจัดการกับไฟล์ที่ผิดปกติ ซึ่งแน่นอนว่าใช่ว่าการป้องกันระดับ Endpoint (Client) นั้นจะรวดเร็วซะทีเดียว ในบางครั้ง Solution เหล่านั้นก็ไม่สามารถ update Signature ของการป้องกัน Malicious file ไม่ทัน(ทำไมถึงไม่ทันลองอ่านบทความตาม link นี้ดูครับ) ดังนั้นหากเรามี SoC (Security Operation Center) ที่มีประสิทธิภาพมากพอ เราสามารถให้ SoC Engineer สามารถวิเคราะห์ malicious file ได้เบื้องต้น โดยดูจากลักษณะของไฟล์ แล้วเขียน Yara Rule ขึ้นมาเพื่อใช้ในการป้องกันหน่วยงานของเราก่อนที่ Vendor แต่ละเจ้าจะทำ signature ขึ้นมาป้องกันไฟล์เหล่านั้นก็สามารถทำได้เช่นกันครับ
เกริ่นนำมาก็ยาวแล้ว มาเริ่มกันเลยดีกว่าว่า Yara Rule สามารถติดตั้งและใช้งานได้อย่างไรครับ
วิธีการติดตั้ง Yara Rule ใน Ubuntu 16.04
1. Update list จาก Repository จากนั้นติดตั้ง Yara
1 2 |
sudo apt-get update sudo apt-get install yara |
วิธีการติดตั้ง Yara Rule ใน Windows 10
หากเป็น Windows ให้ download Yara Binary มาติดตั้งได้เลยครับ
1 |
https://www.dropbox.com/sh/umip8ndplytwzj1/AADdLRsrpJL1CM1vPVAxc5JZa?dl=0 |
วิธีการเขียน Yara Rule
Yara Rule เขียนโดยอิงจากการเขียน Regular Expression โดยจะมี syntax ประมาณด้านล่างนี้
1 2 3 4 5 6 7 8 |
rule RuleName { strings: $test_string1= ”Testing” $test_string2= {E1 D2 C3 B4} Conditions: $test_string1 or $test_string2 } |
strings: จะหมายถึงสิ่งที่เป็นลักษณะของไฟล์ที่เราต้องการค้นหา โดยจากตัวอย่างจะมี 2 ลักษณะคือ: $test_string1 และ $test_string2 โดย $test_string1 หมายถึงหา string คำว่า “Testing” ใน file ใดๆ และ $test_string2 หา hex string ของไฟล์ที่มี “E1 D2 C3 B4” อยู่ในไฟล์
Condition: เป็นการกำหนดลักษณะของเงื่อนไขการค้นหา จากตัวอย่างจะเป็น หากพบ test_string1 หรือ test_string2 ให้ทำการแสดงชื่อไฟล์ออกมา
เมื่อเราเขียนเรียบร้อยแล้วสามารถเรียกใช้งานได้โดยใช้คำสั่งเป็น
1 |
yara <path/of/rule> <path/of/file> |
โดยในที่นี้ผมลองสร้างไฟล์ test.txt ที่มีคำว่า Hello World อยู่ข้างใน จากนั้นสร้าง Rule เป็น
1 2 3 4 5 6 7 8 9 10 |
rule Hello_World_Rule { meta: author = "Sumedt Jitpukdebodin" strings: $test_string1= "Hello World" $test_string2= "End" condition: $test_string1 or $test_string2 } |
จากนั้นใช้คำสั่งเป็น
1 |
yara test.yr test.txt |
หากว่าตรงกับ Rule ที่ตั้งไว้ มันจะทำการแสดงผลเป็น ชื่อ Rule และตามด้วยชื่อไฟล์
Strings
โดยวิธีการเขียน strings ของ Yara สามารถทำได้หลายอย่างดังนี้
– Hexadecimal strings เป็นการหา Byte ที่มันติดๆกัน โดยมีการกำหนดการหาได้อย่าง flexible แทนตัวที่เราไม่รู้หรืออยากจะข้ามการเช็คไป
– Wildcard เป็นการกำหนดว่าตำแหน่งนั้นเป็นอะไรก็ได้ เช่น
1 2 3 4 5 6 7 8 |
rule WildcardExample { strings: $hex_string = { E2 34 ?? C8 A? FB } condition: $hex_string } |
– Jump เป็นการกำหนดให้ข้ามการตรวจสอบไปตามจำนวนที่กำหนด เช่น
1 2 3 4 5 6 7 8 |
rule JumpExample { strings: $hex_string = { F4 23 [4-6] 62 B4 } condition: $hex_string } |
1 2 3 |
F4 23 01 02 03 04 62 B4 F4 23 00 00 00 00 00 62 B4 F4 23 15 82 A3 04 45 22 62 B4 |
– กำหนดใช้ | ข้างในเพื่อให้เป็นการ match byte ทั้งหมดที่ต้องการเช่น
1 2 3 4 5 6 7 8 |
rule AlternativesExample1 { strings: $hex_string = { F4 23 ( 62 B4 | 56 ) 45 } condition: $hex_string } |
– String เป็นการหาไฟล์ใดๆที่มี ASCII string อยู่ในไฟล์ ตามที่เป็นตัวอย่างไปตอนแรกสุด โดยจะมีตัวอักษรพิเศษตามนี้
1 2 3 4 5 |
\" Double quote \\ Backslash \t Horizontal tab \n New line \xdd Any byte in hexadecimal notation |
เราสามารถกำหนดให้มีการตรวจสอบ string แบบไม่ case sensitive ได้โดยกำหนด option เป็น nocase เพิ่มเติม
1 2 3 4 5 6 7 8 |
rule CaseInsensitveTextExample { strings: $text_string = "foobar" nocase condition: $text_string } |
เราสามารถกำหนดการหาการ string จากไฟล์ที่มีเนื้อหาเป็น encode ที่เป็น 2 byte ได้โดยใช้ wide option (เช่นพวก UTF-16 เป็นต้น)
1 2 3 4 5 6 7 8 |
rule WideCharTextExample { strings: $wide_string = "Borland" wide condition: $wide_string } |
หากเราต้องการหาทั้ง encode และ ASCII ให้เพิ่ม option ascii เข้าไปเพิ่มด้วย
1 2 3 4 5 6 7 8 |
rule WideCharTextExample { strings: $wide_and_ascii_string = "Borland" wide ascii condition: $wide_and_ascii_string } |
หากต้องการหาคำโดยเฉพาะจะใช้คำว่า fullword เช่น
1 2 3 4 5 6 7 8 |
rule FullWordExample { strings: $fullword = "domain" fullword ascii nocase condition: $fullword } |
สิ่งที่มันจะ match คือ www.domain.com, www.my-domain.com แต่จะไม่ match กับ www.mydomain.com นั่นเอง
– Regular Expression (Regex)
เราสามารถใช้ Regex ในการหา string ใดๆได้
1 2 3 4 5 6 7 8 9 |
rule RegExpExample1 { strings: $re1 = /md5: [0-9a-zA-Z]{32}/ $re2 = /state: (on|off)/ condition: $re1 and $re2 } |
โดย Regex ทั่วไปที่จำเป็นต้องทราบมีดังนี้
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
\ Quote the next metacharacter ^ Match the beginning of the file $ Match the end of the file | Alternation () Grouping [] Bracketed character class * Match 0 or more times + Match 1 or more times ? Match 0 or 1 times {n} Match exactly n times {n,} Match at least n times {,m} Match 0 to m times {n,m} Match n to m times *? Match 0 or more times, non-greedy +? Match 1 or more times, non-greedy ?? Match 0 or 1 times, non-greedy {n}? Match exactly n times, non-greedy {n,}? Match at least n times, non-greedy {,m}? Match 0 to m times, non-greedy {n,m}? Match n to m times, non-greedy \t Tab (HT, TAB) \n New line (LF, NL) \r Return (CR) \n New line (LF, NL) \f Form feed (FF) \a Alarm bell \xNN Character whose ordinal number is the given hexadecimal number \w Match a word character (aphanumeric plus “_”) \W Match a non-word character \s Match a whitespace character \S Match a non-whitespace character \d Match a decimal digit character \D Match a non-digit character \b Match a word boundary \B Match except at a word boundary |
Condition
– เราสามารถกำหนดเงื่อนไขตามลักษณะของภาษาทั่วได้ นั่นคือการใช้ OR, AND, NOT และ condition เชิงเปรียบเทียบอย่าง >=, <=, <, >, ==, != รวมถึงการกำหนดเงื่อนไขเชิงคณิตศาสตร์ (+, -, *, \, %) ด้วย
1 2 3 4 5 6 7 8 9 10 11 |
rule Example { strings: $a = "text1" $b = "text2" $c = "text3" $d = "text4" condition: ($a or $b) and ($c or $d) } |
เราสามารถเปลี่ยน string กลายเป็น couting of string (จำนวนที่พบ string) ได้โดยเปลี่ยนจาก $ ให้กลายเป็น #
1 2 3 4 5 6 7 8 9 |
rule CountExample { strings: $a = "dummy1" $b = "dummy2" condition: #a == 6 and #b > 10 } |
เราสามารถกำหนดการหาว่าเป็นตำแหน่งใด(offset) ได้โดยใช้คำสั่ง at
1 2 3 4 5 6 7 8 9 |
rule AtExample { strings: $a = "dummy1" $b = "dummy2" condition: $a at 100 and $b at 200 } |
หากต้องการหาตำแหน่งใน range ที่กำหนดจะใช้เป็น in
1 2 3 4 5 6 7 8 9 |
rule InExample { strings: $a = "dummy1" $b = "dummy2" condition: $a in (0..100) and $b in (100..filesize) } |
หากกำหนดหาไฟล์ที่มีขนาดตามที่ต้องการจะใช้เป็น filesize
1 2 3 4 5 |
rule FileSizeExample { condition: filesize > 200KB } |
หากกำหนดเงื่อนไขให้เป็น set ของการตรวจสอบจะเป็น of
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
rule OfExample1 { strings: $a = "dummy1" $b = "dummy2" $c = "dummy3" condition: 2 of ($a,$b,$c) } หรือ rule OfExample2 { strings: $foo1 = "foo1" $foo2 = "foo2" $foo3 = "foo3" condition: 2 of ($foo*) /* equivalent to 2 of ($foo1,$foo2,$foo3) */ } |
จาก Rule ความหมายคือในไฟล์ต้องมี 2 string จาก 3 ตัวที่กำหนด อีกทั้งยังกำหนด condition ของ of ได้ในหลายๆความหมายอีกด้วย
1 2 3 4 5 |
all of them /* all strings in the rule */ any of them /* any string in the rule */ all of ($a*) /* all strings whose identifier starts by $a */ any of ($a,$b,$c) /* any of $a, $b or $c */ 1 of ($*) /* same that "any of them" */ |
การกำหนด Tag และ Metadata ใน Yara
นอกจากนี้เรายังสามารถที่จะใส่ tag ให้กับ rule ได้โดยใช้เป็น
1 2 3 4 5 6 7 8 9 |
rule TagsExample1 : Tag1 Tag2 Tag3 { ... } rule TagsExample2 : Tag1 { ... } |
และหากเราต้องการใส่ Metadata เข้าไปใน Rule สามารถเขียนได้เป็น
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
rule MetadataExample { meta: my_identifier_1 = "Some string data" my_identifier_2 = 24 my_identifier_3 = true strings: $my_text_string = "text here" $my_hex_string = { E2 34 A1 C8 23 FB } condition: $my_text_string or $my_hex_string } |
ซึ่ง Metadata เป็นสิ่งที่เราจะใส่ข้อมูลเข้าไปเอง ไม่ว่าจะเป็น author, severity, Link และอื่นๆ
การใช้ module ใน Yara Rule
– PE Module
เราสามารถใช้ module PE เพื่อทำการตรวจสอบ execution อย่าง Portable Executable (PE) ได้ โดยมีลักษณะการตรวจสอบมากมาย
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import "pe" rule single_section { condition: pe.number_of_sections == 1 } rule control_panel_applet { condition: pe.exports("CPlApplet") } rule is_dll { condition: pe.characteristics & pe.DLL } |
สามารถตรวจสอบ condition รวมถึง string ต่างๆของ module ต่างๆของ PE ได้ตาม LINK นี้
– ELF Module
เราสามารถใช้ module ELF เพื่อทำการตรวจสอบ execution อย่าง ELF ซึ่งเป็น file run ใน Linux ได้ โดยมีลักษณะการตรวจสอบมากมาย
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import "elf" rule single_section { condition: elf.number_of_sections == 1 } rule elf_64 { condition: elf.machine == elf.EM_X86_64 } |
สามารถตรวจสอบ condition รวมถึง string ต่างๆของ module ต่างๆของ ELF ได้ตาม LINK นี้
– Cuckoo Module
เป็นการใช้งาน Yara ผสมผสานกับ Report ของ Yara Rule โดย syntax การใช้งานร่วมกันจะเป็น
1 |
$yara -x cuckoo=behavior_report_file rules_file pe_file |
ซึ่ง behavior_report_file จะเป็นพฤติกรรมที่เราได้จากการตรวจสอบผ่าน cuckoo โดยตัวอย่างของ Rule ไฟล์จะเป็น
1 2 3 4 5 6 7 8 9 10 11 |
import "cuckoo" rule evil_doer { strings: $some_string = { 01 02 03 04 05 06 } condition: $some_string and cuckoo.network.http_request(/http:\/\/someone\.doingevil\.com/) } |
นั่นหมายความว่าหากไฟล์มี hex string { 01 02 03 04 05 06 } และมีพฤติกรรมการติดต่อไปยัง http://someone.doingevil.com
หากเราใช้ร่วมกับ python จะได้เป็น
1 2 3 4 5 |
import yara rules = yara.compile('./rules_file') report_file = open('./behavior_report_file') report_data = report_file.read() rules.match(pe_file, modules_data={'cuckoo': bytes(report_data)}) |
– Magic Module
เป็นการตรวจสอบชนิดของไฟล์ ซึ่งเหมือนกับการใช้คำสั่ง file ใน Linux นั่นเอง โดยมี 2 แบบคือ type() และ mime_type()
1 2 3 4 |
file some.pdf some.pdf: PDF document, version 1.5 file --mime some.pdf some.pdf: application/pdf; charset=binary |
– Hash Module
Hash module เอาไว้สำหรับการสร้าง rule เพื่อคำนวณและตรวจสอบค่า hash ของไฟล์ใดๆ โดยสามารถทำได้ทั้ง MD5, SHA1,SHA256 รวมถึงการคำนวณ checksum() ก็สามารถทำได้เช่นกัน
1 2 3 4 5 6 7 8 9 10 11 12 |
import "hash" rule REALNOTEPAD { meta: description = "REAL NOTEPAD" strings: $m0 = { 4D 5A } // wide ascii condition: $m0 at 0 and filesize < 350KB and hash.md5(0, filesize) == "e30299799c4ece3b53f4a7b8897a35b6" } |
สามารถตรวจสอบ Module อื่นๆได้ที่ Link นี้ครับ
ตัวอย่าง Rule สำหรับตรวจจับ WannaCry จาก FireEye
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
rule FE_RANSOMWARE_WANNACRY { meta:version=".4" filetype="PE" author="Ian.Ahl@fireeye.com @TekDefense" date="2017-05-12" description="Generic detection for most WannaCry variants" strings: // Bitcoin URLs $bcURL1 = "http://www.btcfrog.com/qr/bitcoinPNG.php?address=%" ascii wide nocase $bcURL2 = "https://www.google.com/search?q=how+to+buy+bitcoin" ascii wide nocase // Ransom Message $msg1 = "Congratulations! Succeed to check your payment!" ascii wide $msg2 = "Start decrypting now!" ascii wide $msg3 = "All your files have been decrypted!" ascii wide $msg4 = "Pay now, if you want to decrypt ALL your files!" ascii wide $msg5 = "Send $%d worth of bitcoin to this address:" ascii wide $msg6 = "Ooops, your files have been encrypted!" ascii wide // WANNA Strings $wanna1 = "Wanna Decryptor 1.0" ascii wide $wanna2 = "Wana Decrypt0r" ascii wide $wanna3 = "Wana Decryptor" ascii wide $wanna4 = "WANNACRY" ascii wide nocase $wanna5 = "WanaCrypt0r" ascii wide nocase $wanna6 = "WANACRY!" ascii wide $wanna7 = "WNcry@2ol7" ascii wide $wanna8 = "wcry@123" $wanna9 = "wcry@2016" // File references $fileA1 = "!WannaCryptor!.bmp" ascii wide $fileA2 = "!WannaDecryptor!.exe.lnk" ascii wide $fileA3 = "!Please Read Me!.txt" ascii wide $fileB1 = "@WanaDecryptor@.bmp" ascii wide $fileB2 = "@WanaDecryptor@.exe.lnk" ascii wide $fileB3 = "@Please_Read_Me@.txt" ascii wide // CMDS $cmd1 = "cmd.exe /c start /b vssadmin.exe Delete Shadows /All /Quiet" ascii wide nocase $cmd2 = "wmic shadowcopy delete" ascii wide $cmd3 = "bcdedit /set {default} bootstatuspolicy ignoreallfailures" ascii wide $cmd4 = "bcdedit /set {default} recoveryenabled no" ascii wide $cmd5 = "wbadmin delete catalog -quiet" ascii wide $cmd6 = "icacls . /grant Everyone:F /T /C /Q" ascii wide // MISC $misc1 = "StartTask" wide ascii $misc2 = "b.wry" wide ascii $misc3 = "c.wry" wide ascii $misc4 = "m.wry" wide ascii $misc5 = "inflate 1.1.3 Copyright 1995-1998 Mark Adler" wide ascii $misc6 = "?AVtype_info@@" wide ascii condition: ( ( (uint16(0) == 0x5A4D) ) and ( all of ($fileA*) or all of ($fileB*) or (4 of ($msg*) and 2 of ($bcURL*)) or 2 of ($wanna*) or (2 of ($msg*) and 1 of ($cmd*)) or 4 of ($cmd*) or (1 of ($wanna*) and 1 of ($cmd*)) or (1 of ($wanna*) and 3 of ($misc*)) ) ) } rule FE_RANSOMWARE_WANNACRY_EB { meta:version=".1" filetype="PE" author="Ian.Ahl@fireeye.com @TekDefense" date="2017-05-12" description="Focusing on the WannaCry variants with worm capabilities" strings: // EB related strings in WANNACRY $eb1 = "__USERID__PLACEHOLDER__@" ascii wide $eb2 = "__TREEID__PLACEHOLDER__" ascii wide $eb3 = "LANMAN1.0" ascii wide $eb4 = "LANMAN2.1" ascii wide $eb5 = "\\PIPE\\" ascii wide $eb6 = "\\\\%s\\IPC$" ascii wide $eb7 = "__TREEPATH_REPLACE__" ascii wide $eb8 = "/K__USERID__PLACEHOLDER__" ascii wide condition: ( ( (uint16(0) == 0x5A4D) ) and ( all of ($eb*) ) ) } |
Mac Malware YaraRule
ใช้งาน Yara Rule ไปเป็น Antivirus
Source::