diff --git a/BinBot.py b/BinBot.py index 9132001..1b781c4 100644 --- a/BinBot.py +++ b/BinBot.py @@ -19,12 +19,10 @@ import sys import lib -import gzip import json import yara import codecs from time import sleep -from base64 import b64decode from bs4 import BeautifulSoup from sys import path as syspath from os import path, listdir @@ -115,7 +113,7 @@ def config(configpath): finally: print("\n") return vars_dict -# YARA and Saving Function: +# Matching and Saving Function: def archive_engine(prescan_text, proch, vars_dict): """ This function scans files for YARA matches (if enabled) and saves files. @@ -138,28 +136,7 @@ def archive_engine(prescan_text, proch, vars_dict): lib.print_status(f"Blacklisted term detected: [{components['term']}]") # Otherwise, continue checking rules else: - # The prebuilt rules: - if components['rule'] == 'b64Artifacts': - lib.print_success(f"Base64 Artifact Found: [{components['term']}]") - # If gzipped, decompress: - if components['term'] == "H4sI": - codecs.open(f"{vars_dict['workpath']}{proch}.file", 'w+', 'utf-8').write(gzip.decompress(bytes(b64decode(prescan_text), 'utf-8'))) - # Otherwise, decode and save: - else: - codecs.open(f"{vars_dict['workpath']}{components['id']}_{proch}.txt", 'w+', 'utf-8').write(b64decode(prescan_text)) - elif components['rule'] == 'powershellArtifacts': - lib.print_success(f"Powershell Artifact Found: [{components['term']}]") - codecs.open(f"{vars_dict['workpath']}{components['term']}_{proch}.ps1", 'w+', 'utf-8').write(prescan_text) - elif components['rule'] == 'keywords': - lib.print_success(f"Keyword found: [{components['term']}]") - codecs.open(f"{vars_dict['workpath']}{components['term']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) - elif components['rule'] == 'regex_pattern': - lib.print_success(f"{components['rule']} match found: {components['id']}") - codecs.open(f"{vars_dict['workpath']}{components['id']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) - # Custom rules will be saved by this statement: - else: - lib.print_success(f"{components['rule']} match found: {components['term']}") - codecs.open(f"{vars_dict['workpath']}{components['id']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) + lib.general_matching(vars_dict, prescan_text, proch, components) #If no matches are found, it just writes it with the parameter as a name else: lib.print_status(f"No matches in document: /{proch}") diff --git a/README.md b/README.md index 535a4f8..c85d184 100644 --- a/README.md +++ b/README.md @@ -19,4 +19,4 @@ If no path is passed, binbot will run a manual setup. ## TODO: - API integration -- Binary rules implementation + diff --git a/lib.py b/lib.py index c555338..c020166 100644 --- a/lib.py +++ b/lib.py @@ -18,8 +18,12 @@ # ----------------------------------------------------------------------- import os +import gzip +import codecs import requests from random import choice +from zipfile import ZipFile +from base64 import b64decode from bs4 import BeautifulSoup from datetime import datetime @@ -96,6 +100,52 @@ def print_title(msg): else: print(f"\033[35m {msg}") # +# YARA Functions: +# +def binary_matching(vars_dict, filepath): + matches = vars_dict['binary_rules'].match(data=codecs.open(filepath, 'rb', 'utf-8')) + if matches: + components = {'rule': matches[0].rule, + 'term': ((matches[0]).strings[0])[2] if isinstance(((matches[0]).strings[0])[2], str) else + ((matches[0]).strings[0])[2].decode('UTF-8'), + 'id': (((matches[0]).strings[0])[1])[1:]} + print_success(f"{os.path.split(filepath)[1]} matches for {components['rule']}") + print_success(f"Matched item: {components['term']}") + os.rename(filepath, f"{os.path.split(filepath)[0]}/{components['rule']}.file") +def general_matching(vars_dict, prescan_text, proch, components): + if components['rule'] == 'b64Artifacts': + print_success(f"Base64 Artifact Found: [{components['term']}]") + # If gzipped, decompress: + if components['term'] == "H4sI": + filename = f"{vars_dict['workpath']}{proch}.file" + codecs.open(filename, 'w+', 'utf-8').write(gzip.decompress(bytes(b64decode(prescan_text), 'utf-8'))) + # Otherwise, decode and save: + else: + filename = f"{vars_dict['workpath']}{components['id']}_{proch}.txt" + codecs.open(filename, 'w+', 'utf-8').write(b64decode(prescan_text)) + # If zipped, unzip and pass all files in unzipped directory to binary_matching + if components['rule'] == "UEs": + zip_dir = f"{vars_dict['workpath']}/{os.path.split(filename)[1].split('.')[0]}" + ZipFile(filename, "r").extractall(zip_dir) + for file in [os.path.join(vars_dict['workpath'], f) for f in os.listdir(zip_dir)]: + binary_matching(vars_dict, file) + # If not zipped, pass the singular file to binary_matching + else: + binary_matching(vars_dict, filename) + elif components['rule'] == 'powershellArtifacts': + print_success(f"Powershell Artifact Found: [{components['term']}]") + codecs.open(f"{vars_dict['workpath']}{components['term']}_{proch}.ps1", 'w+', 'utf-8').write(prescan_text) + elif components['rule'] == 'keywords': + print_success(f"Keyword found: [{components['term']}]") + codecs.open(f"{vars_dict['workpath']}{components['term']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) + elif components['rule'] == 'regex_pattern': + print_success(f"{components['rule']} match found: {components['id']}") + codecs.open(f"{vars_dict['workpath']}{components['id']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) + # Custom rules will be saved by this statement: + else: + print_success(f"{components['rule']} match found: {components['term']}") + codecs.open(f"{vars_dict['workpath']}{components['id']}_{proch}.txt", 'w+', 'utf-8').write(prescan_text) +# # Misc Program Functions: # def connect(url): diff --git a/yara_rules/binary_rules/mimikatz.yar b/yara_rules/binary_rules/mimikatz.yar new file mode 100644 index 0000000..a2c5a96 --- /dev/null +++ b/yara_rules/binary_rules/mimikatz.yar @@ -0,0 +1,43 @@ +/* + Credit: https://github.com/airbnb/binaryalert/blob/master/rules/public/hacktool/windows/ + Assembled from multiple mimikatz rules from listed source +*/ +rule windows_mimikatz +{ + meta: + description = "Mimikatz credential dump tool" + reference = "https://github.com/gentilkiwi/mimikatz" + author = "@fusionrace" + SHA256_1 = "09054be3cc568f57321be32e769ae3ccaf21653e5d1e3db85b5af4421c200669" + SHA256_2 = "004c07dcd04b4e81f73aacd99c7351337f894e4dac6c91dcfaadb4a1510a967c" + strings: + $s1 = "dpapisrv!g_MasterKeyCacheList" fullword ascii wide + $s2 = "lsasrv!g_MasterKeyCacheList" fullword ascii wide + $s3 = "!SspCredentialList" ascii wide + $s4 = "livessp!LiveGlobalLogonSessionList" fullword ascii wide + $s5 = "wdigest!l_LogSessList" fullword ascii wide + $s6 = "tspkg!TSGlobalCredTable" fullword ascii wide + $s7 = "Kiwi en C" fullword ascii wide + $s8 = "Benjamin DELPY `gentilkiwi`" fullword ascii wide + $s9 = "http://blog.gentilkiwi.com/mimikatz" fullword ascii wide + $s10 = "Build with love for POC only" fullword ascii wide + $s11 = "gentilkiwi (Benjamin DELPY)" fullword wide + $s12 = "KiwiSSP" fullword wide + $s13 = "Kiwi Security Support Provider" fullword wide + $s14 = "kiwi flavor !" fullword wide + $s15 = "[ERROR] [LSA] Symbols" fullword ascii wide + $s16 = "[ERROR] [CRYPTO] Acquire keys" fullword ascii wide + $s17 = "[ERROR] [CRYPTO] Symbols" fullword ascii wide + $s18 = "[ERROR] [CRYPTO] Init" fullword ascii wide + $s19 = "!@#$%^&*()qwertyUIOPAzxcvbnmQQQQQQQQQQQQ)(*@&%" wide ascii + $s20 = "0123456789012345678901234567890123456789" wide ascii + $s21 = "NTPASSWORD" wide ascii + $s22 = "LMPASSWORD" wide ascii + $s23 = "aad3b435b51404eeaad3b435b51404ee" wide ascii + $s24 = "31d6cfe0d16ae931b73c59d7e0c089c0" wide ascii + $s25 = "kiwifilter.log" fullword wide + $s26 = "kiwissp.log" fullword wide + $s27 = "mimilib.dll" fullword ascii wide + condition: + 2 of ($s*) +} diff --git a/yara_rules/binary_rules/nanocore.yar b/yara_rules/binary_rules/nanocore.yar new file mode 100644 index 0000000..4903700 --- /dev/null +++ b/yara_rules/binary_rules/nanocore.yar @@ -0,0 +1,70 @@ +/* + This Yara ruleset is under the GNU-GPLv2 license (http://www.gnu.org/licenses/gpl-2.0.html) and open to any user or organization, as long as you use it under this license. + + Credited: https://github.com/Yara-Rules/rules/blob/master/malware/RAT_Nanocore.yar +*/ +rule Nanocore_G1 { + meta: + description = "Detetcs the Nanocore RAT and similar malware" + author = "Florian Roth" + reference = "https://www.sentinelone.com/blogs/teaching-an-old-rat-new-tricks/" + date = "2016-04-22" + score = 70 + hash1 = "e707a7745e346c5df59b5aa4df084574ae7c204f4fb7f924c0586ae03b79bf06" + strings: + $x1 = "C:\\Users\\Logintech\\Dropbox\\Projects\\New folder\\Latest\\Benchmark\\Benchmark\\obj\\Release\\Benchmark.pdb" fullword ascii + $x2 = "RunPE1" fullword ascii + $x3 = "082B8C7D3F9105DC66A7E3267C9750CF43E9D325" fullword ascii + $x4 = "$374e0775-e893-4e72-806c-a8d880a49ae7" fullword ascii + $x5 = "Monitorinjection" fullword ascii + condition: + ( uint16(0) == 0x5a4d and filesize < 100KB and ( 1 of them ) ) or ( 3 of them ) +} + +rule Nanocore_G2 { + meta: + description = "Detetcs the Nanocore RAT" + author = "Florian Roth" + score = 100 + reference = "https://www.sentinelone.com/blogs/teaching-an-old-rat-new-tricks/" + date = "2016-04-22" + hash1 = "755f49a4ffef5b1b62f4b5a5de279868c0c1766b528648febf76628f1fe39050" + strings: + $x1 = "NanoCore.ClientPluginHost" fullword ascii + $x2 = "IClientNetworkHost" fullword ascii + $x3 = "#=qjgz7ljmpp0J7FvL9dmi8ctJILdgtcbw8JYUc6GC8MeJ9B11Crfg2Djxcf0p8PZGe" fullword ascii + condition: + ( uint16(0) == 0x5a4d and filesize < 1000KB and 1 of them ) or ( all of them ) +} + +rule Nanocore_S1 { + meta: + description = "Detetcs a certain Nanocore RAT sample" + author = "Florian Roth" + score = 75 + reference = "https://www.sentinelone.com/blogs/teaching-an-old-rat-new-tricks/" + date = "2016-04-22" + hash2 = "b7cfc7e9551b15319c068aae966f8a9ff563b522ed9b1b42d19c122778e018c8" + strings: + $x1 = "TbSiaEdJTf9m1uTnpjS.n9n9M7dZ7FH9JsBARgK" fullword wide + $x2 = "1EF0D55861681D4D208EC3070B720C21D885CB35" fullword ascii + $x3 = "popthatkitty.Resources.resources" fullword ascii + condition: + ( uint16(0) == 0x5a4d and filesize < 900KB and ( 1 of ($x*) ) ) or ( all of them ) +} + +rule Nanocore_S2 { + meta: + description = "Detetcs a certain Nanocore RAT sample" + author = "Florian Roth" + score = 75 + reference = "https://www.sentinelone.com/blogs/teaching-an-old-rat-new-tricks/" + date = "2016-04-22" + hash1 = "51142d1fb6c080b3b754a92e8f5826295f5da316ec72b480967cbd68432cede1" + strings: + $s1 = "U4tSOtmpM" fullword ascii + $s2 = ")U71UDAU_QU_YU_aU_iU_qU_yU_" fullword wide + $s3 = "Cy4tOtTmpMtTHVFOrR" fullword ascii + condition: + uint16(0) == 0x5a4d and filesize < 40KB and all of ($s*) +} diff --git a/yara_rules/binary_rules/placeholder b/yara_rules/binary_rules/placeholder deleted file mode 100644 index e69de29..0000000 diff --git a/yara_rules/binary_rules/pupy.yar b/yara_rules/binary_rules/pupy.yar new file mode 100644 index 0000000..3b335d0 --- /dev/null +++ b/yara_rules/binary_rules/pupy.yar @@ -0,0 +1,21 @@ +/* + Credit: https://github.com/airbnb/binaryalert/edit/master/rules/public/malware/multi/malware_multi_pupy_rat.yara + Renamed for file naming simplicity +*/ + +rule pupy_rat +{ + meta: + description = "pupy - opensource cross platform rat and post-exploitation tool" + reference = "https://github.com/n1nj4sec/pupy" + author = "@mimeframe" + strings: + $a1 = "dumping lsa secrets" nocase wide ascii + $a2 = "dumping cached domain passwords" nocase wide ascii + $a3 = "the keylogger is already started" nocase wide ascii + $a4 = "pupyutils.dns" wide ascii + $a5 = "pupwinutils.security" wide ascii + $a6 = "-PUPY_CONFIG_COMES_HERE-" wide ascii + condition: + 3 of ($a*) +} diff --git a/yara_rules/binary_rules/quasar.yar b/yara_rules/binary_rules/quasar.yar new file mode 100644 index 0000000..4a1dd90 --- /dev/null +++ b/yara_rules/binary_rules/quasar.yar @@ -0,0 +1,35 @@ +/* + Credit: https://github.com/airbnb/binaryalert/edit/master/rules/public/malware/windows/malware_windows_xrat_quasarrat.yara + Renamed for file naming simplicity +*/ +rule windows_quasar +{ + meta: + description = "xRAT is a derivative of QuasarRAT; this catches both RATs." + reference = "https://github.com/quasar/QuasarRAT" + author = "@mimeframe" + strings: + // RemoteShell + $a1 = ">> New Session created" wide ascii + $a2 = ">> Session unexpectedly closed" wide ascii + $a3 = ">> Session closed" wide ascii + $a4 = "session unexpectedly closed" wide ascii + $a5 = "cmd" fullword wide ascii + $a6 = "/K" fullword wide ascii + // Commands + $b1 = "echo DONT CLOSE THIS WINDOW!" wide ascii + $b2 = "ping -n 20 localhost > nul" wide ascii + $b3 = "Downloading file..." wide ascii + $b4 = "Visited Website" wide ascii + $b5 = "Adding Autostart Item failed!" wide ascii + $b6 = ":Zone.Identifier" wide ascii + // System Handler + $c1 = "GetDrives I/O error" wide ascii + $c2 = "/r /t 0" wide ascii + $c3 = "desktop.ini" wide ascii + $c4 = "WAN IP Address" wide ascii + $c5 = "User refused the elevation request." wide ascii + $c6 = "Process already elevated." wide ascii + condition: + 5 of ($a*) or 5 of ($b*) or 5 of ($c*) +}