Skip to content

Commit

Permalink
Support for detecting some loaders (#113)
Browse files Browse the repository at this point in the history
* Support for detecting some loaders

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>

* Support for detecting some loaders

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>

* Support for detecting some loaders

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>

---------

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
  • Loading branch information
prabhu committed Jul 29, 2024
1 parent 9759641 commit d574ea1
Show file tree
Hide file tree
Showing 14 changed files with 208 additions and 40 deletions.
2 changes: 1 addition & 1 deletion Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
<key>CFBundleName</key>
<string>blint</string>
<key>CFBundleVersion</key>
<string>2.2.0</string>
<string>2.2.1</string>
</dict>
</plist>
46 changes: 37 additions & 9 deletions blint/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
check_virtual_size, check_authenticode,
check_dll_characteristics, check_codesign,
check_trust_info)
from blint.config import PII_WORDS, get_int_from_env
from blint.config import FIRST_STAGE_WORDS, PII_WORDS, get_int_from_env
from blint.logger import LOG, console
from blint.utils import (create_findings_table, is_fuzzable_name, print_findings_table)

Expand Down Expand Up @@ -54,12 +54,20 @@
review_symbols_dict = defaultdict(list)
review_imports_dict = defaultdict(list)
review_entries_dict = defaultdict(list)
review_rules_cache = {"PII_READ": {
"title": "Detect PII Read Operations",
"summary": "Can Retrieve Sensitive PII data",
"description": "Contains logic to retrieve sensitive data such as names, email, passwords etc.",
"patterns": PII_WORDS
}}
review_rules_cache = {
"PII_READ": {
"title": "Detect PII Read Operations",
"summary": "Can Retrieve Sensitive PII data",
"description": "Contains logic to retrieve sensitive data such as names, email, passwords etc.",
"patterns": PII_WORDS
},
"LOADER_SYMBOLS": {
"title": "Detect Initial Loader",
"summary": "Behaves like a loader",
"description": "The binary behaves like a loader by downloading and executing additional payloads.",
"patterns": FIRST_STAGE_WORDS
},
}

# Debug mode
DEBUG_MODE = os.getenv("SCAN_DEBUG_MODE") == "debug"
Expand Down Expand Up @@ -309,7 +317,10 @@ def print_reviews_table(reviews, files):
def json_serializer(obj):
"""JSON serializer to help serialize problematic types such as bytes"""
if isinstance(obj, bytes):
return obj.decode('utf-8')
try:
return obj.decode('utf-8')
except UnicodeDecodeError:
return ""

return obj

Expand Down Expand Up @@ -494,7 +505,7 @@ def run_review(self, metadata):
or self.review_entries_list
):
return self._review_lists(metadata)
return {}
return self._review_loader_symbols(metadata)

def _review_lists(self, metadata):
"""
Expand All @@ -516,6 +527,7 @@ def _review_lists(self, metadata):
if self.review_entries_list:
self._review_entries(metadata)
self._review_pii(metadata)
self._review_loader_symbols(metadata)
return self.results

def _review_imports(self, metadata):
Expand Down Expand Up @@ -562,6 +574,22 @@ def _review_pii(self, metadata):
results["PII_READ"].append({"pattern": e, "function": e})
self.results |= results

def _review_loader_symbols(self, metadata):
"""
Reviews loader symbols.
Args:
metadata (dict): The metadata to review.
Returns:
dict: The results of the review.
"""
entries_list = [f.get("name", "") for f in metadata.get("first_stage_symbols", [])]
results = defaultdict(list)
for e in entries_list[0:EVIDENCE_LIMIT]:
results["LOADER_SYMBOLS"].append({"pattern": e, "function": e})
self.results |= results

def _review_symbols_exe(self, metadata):
"""
Reviews symbols in the metadata.
Expand Down
71 changes: 55 additions & 16 deletions blint/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import lief

from blint.config import PII_WORDS, get_float_from_env, get_int_from_env
from blint.config import FIRST_STAGE_WORDS, PII_WORDS, get_float_from_env, get_int_from_env
from blint.logger import DEBUG, LOG
from blint.utils import camel_to_snake, calculate_entropy, check_secret, cleanup_dict_lief_errors, decode_base64

Expand Down Expand Up @@ -270,7 +270,7 @@ def parse_strings(parsed_obj):
if (entropy and (entropy > MIN_ENTROPY or len(s) > MIN_LENGTH)) or secret_type:
strings_list.append(
{
"value": (decode_base64(s) if s.endswith("==") else s),
"value": decode_base64(s) if s.endswith("==") else s,
"entropy": entropy,
"secret_type": secret_type,
}
Expand Down Expand Up @@ -788,7 +788,6 @@ def add_elf_metadata(exe_file, metadata, parsed_obj):
metadata["has_runpath"] = False
elif runpath:
metadata["has_runpath"] = True
# This is getting renamed to symtab_symbols in lief 0.15.0
symtab_symbols = parsed_obj.symtab_symbols
metadata["static"] = bool(symtab_symbols and not isinstance(symtab_symbols, lief.lief_errors))
dynamic_entries = parsed_obj.dynamic_entries
Expand All @@ -797,6 +796,10 @@ def add_elf_metadata(exe_file, metadata, parsed_obj):
metadata["notes"] = parse_notes(parsed_obj)
metadata["strings"] = parse_strings(parsed_obj)
metadata["symtab_symbols"], exe_type = parse_symbols(symtab_symbols)
rdata_section = parsed_obj.get_section(".rodata")
text_section = parsed_obj.get_section(".text")
if not metadata["symtab_symbols"]:
add_elf_rdata_symbols(metadata, rdata_section, text_section)
if exe_type:
metadata["exe_type"] = exe_type
metadata["dynamic_symbols"], exe_type = parse_symbols(parsed_obj.dynamic_symbols)
Expand Down Expand Up @@ -1114,10 +1117,16 @@ def add_pe_metadata(exe_file: str, metadata: dict, parsed_obj: lief.PE.Binary):
break
rdata_section = parsed_obj.get_section(".rdata")
text_section = parsed_obj.get_section(".text")
if not rdata_section and text_section:
rdata_section = text_section
if (not metadata["symtab_symbols"] or metadata["exe_type"] != "gobinary") and rdata_section:
add_pe_rdata_symbols(metadata, rdata_section)
# If there are no .rdata and .text section, then attempt to look for two alphanumeric sections
if not rdata_section and not text_section:
for section in parsed_obj.sections:
if str(section.name).removeprefix(".").isalnum():
if not rdata_section:
rdata_section = section
else:
text_section = section
if rdata_section or text_section:
add_pe_rdata_symbols(metadata, rdata_section, text_section)
metadata["exports"] = parse_pe_exports(parsed_obj.get_export())
metadata["functions"] = parse_functions(parsed_obj.functions)
metadata["ctor_functions"] = parse_functions(parsed_obj.ctor_functions)
Expand Down Expand Up @@ -1247,33 +1256,39 @@ def add_pe_optional_headers(metadata, optional_header):
return metadata


def add_pe_rdata_symbols(metadata, rdata_section: lief.PE.Section):
def add_pe_rdata_symbols(metadata, rdata_section: lief.PE.Section, text_section: lief.PE.Section):
"""Adds PE rdata symbols to the metadata dictionary.
Args:
metadata: The dictionary to store the metadata.
rdata_section: .rdata section of the PE binary.
text_section: .text section of the PE binary.
Returns:
The updated metadata dictionary.
"""
if not rdata_section or not rdata_section.content:
return metadata
file_extns_from_rdata = r".*\.(go|s|dll|exe|pdb)"
rdata_symbols = set()
pii_symbols = []
first_stage_symbols = []
for pii in PII_WORDS:
for vari in (f"get{pii}", f"get_{camel_to_snake(pii)}"):
if rdata_section.search_all(vari):
for vari in (f"get{pii}", f"get_{pii}", f"get_{camel_to_snake(pii)}", f"Get{pii}"):
if (rdata_section and rdata_section.search_all(vari)) or (text_section and text_section.search_all(vari)):
pii_symbols.append(
{"name": vari.lower(), "type": "FUNCTION", "is_function": True, "is_imported": False})
continue
str_content = codecs.decode(rdata_section.content.tobytes("A"), encoding="utf-8", errors="ignore")
for sw in FIRST_STAGE_WORDS:
if (rdata_section and rdata_section.search_all(sw)) or (text_section and text_section.search_all(sw)):
first_stage_symbols.append(
{"name": sw, "type": "FUNCTION", "is_function": True, "is_imported": True})
str_content = codecs.decode(rdata_section.content.tobytes("A"), encoding="utf-8",
errors="ignore") if rdata_section and rdata_section.content else ""
for block in str_content.split(" "):
if "runtime." in block or "internal/" in block or ".go" in block or ".dll" in block:
if "runtime." in block or "internal/" in block or re.match(file_extns_from_rdata, block):
if ".go" in block:
metadata["exe_type"] = "gobinary"
for asym in block.split("\x00"):
if re.match(r".*\.(go|s|dll)$", asym):
if re.match(file_extns_from_rdata + "$", asym):
rdata_symbols.add(asym)
if not metadata["symtab_symbols"]:
metadata["symtab_symbols"] = []
Expand All @@ -1285,7 +1300,31 @@ def add_pe_rdata_symbols(metadata, rdata_section: lief.PE.Section):
"is_imported": True
} for s in sorted(rdata_symbols)
]
metadata["pii_symbols"] = pii_symbols
if pii_symbols:
metadata["pii_symbols"] = pii_symbols
if first_stage_symbols:
metadata["first_stage_symbols"] = first_stage_symbols
return metadata


def add_elf_rdata_symbols(metadata, rdata_section: lief.PE.Section, text_section: lief.PE.Section):
"""Adds ELF rdata symbols to the metadata dictionary.
Args:
metadata: The dictionary to store the metadata.
rdata_section: .data section of the ELF binary.
text_section: .text section of the ELF binary.
Returns:
The updated metadata dictionary.
"""
first_stage_symbols = []
for sw in FIRST_STAGE_WORDS:
if (rdata_section and rdata_section.search_all(sw)) or (text_section and text_section.search_all(sw)):
first_stage_symbols.append(
{"name": sw, "type": "FUNCTION", "is_function": True, "is_imported": True})
if first_stage_symbols:
metadata["first_stage_symbols"] = first_stage_symbols
return metadata


Expand Down
43 changes: 42 additions & 1 deletion blint/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -1230,7 +1230,7 @@
"email": [re.compile(r"(?<=mailto:)[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+.[a-zA-Z0-9.-]+")],
"ip": [
re.compile(
r"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])$"
r"^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9]).){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(:[0-9]+)?$"
)
],
}
Expand Down Expand Up @@ -1268,6 +1268,7 @@ def get_int_from_env(name, default):
return int(get_float_from_env(name, default))


# PII related symbols
PII_WORDS = (
"FirstName",
"LastName",
Expand All @@ -1293,3 +1294,43 @@ def get_int_from_env(name, default):
"AgentStatus",
"LastLoginTime"
)

# Some symbols to look for in a first-stage payload
FIRST_STAGE_WORDS = (
"System.ServiceProcess",
"System.IO.Compression",
"System.Reflection.Emit",
"ICryptoTransform",
"LoadAssembly",
"GetEncodedData",
"add_AssemblyResolve",
"CreateDecryptor",
"GetExecutingAssembly",
"GetModules",
"get_IsFamilyOrAssembly",
"/proc/%d/cmdline",
"/proc/%s/exe",
"/proc/self/exe",
"/proc/net/route",
"/etc/resolv.conf",
"/usr/lib/systemd/systemd",
"/usr/compress/bin/",
"/usr/libexec/openssh/sftp-server",
"/usr/sbin/reboot",
"/usr/bin/reboot",
"/usr/sbin/shutdown",
"/usr/bin/shutdown",
"/usr/sbin/poweroff",
"/usr/bin/poweroff",
"/usr/sbin/halt",
"/usr/bin/halt",
"virtualboxemulunit",
"virtualboximportunit",
"virtualboxunit",
"qvirtualboxglobalsunit",
"Uvirtualboxdisasm",
"loaderx86.dll",
"ZwProtectVirtualMemory",
"shlwapi.dll",
"DeleteCriticalSection"
)
1 change: 1 addition & 0 deletions blint/data/annotations/review_exe_go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ group: EXE_REVIEWS
exe_type:
- gobinary
- x86_64-executable
- x86_64-exec
rules:
- id: FILE_IOUTIL
title: IO util functions used
Expand Down
39 changes: 39 additions & 0 deletions blint/data/annotations/review_imports_pe.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ rules:
- CreateSocketHandle
- CreateThread
- CreateThreadpool
- GetCurrentThread
- GetThreadLocale
- ExitProcess
- FreeLibrary
- ExitThread
- id: PROCESS_STATUS_API
title: Process status api used
summary: Queries about processes and device drivers
Expand Down Expand Up @@ -464,6 +469,8 @@ rules:
- CreateHardLink
- BackupEventLog
- CreateFileMapping
- SetFileAttributes
- SetFilePointer
- id: WIN_BCRYPT_API
title: Win bcrypt api functions used
summary: Can Encrypt Files
Expand Down Expand Up @@ -1973,6 +1980,14 @@ rules:
description: |
Shell Windows API allows applications to access functions provided by the operating system shell, and to change and enhance it.
patterns:
- DllInstall
- GetProcessReference
- ParseURLA
- ParseURLW
- PathFindFileName
- PathFindOnPath
- PathIsSystemFolder
- PathIsUNCServerShare
- FindExecutableA
- FindExecutableW
- InitNetworkAddressControl
Expand All @@ -1994,6 +2009,30 @@ rules:
- GetManagedApplications
- InstallApplication
- UninstallApplication
- SHCreateThread
- SHCreateStreamOnFile
- SHOpenRegStream
- SHRegCreateUSKey
- SHQueryValueEx
- SHQueryInfoKey
- UrlCreateFromPath
- PathMatchSpec
- SHBindToFolderIDListParent
- SHBrowseForFolder
- SHCreateDefaultContextMenu
- SHCreateShellItem
- SHFormatDrive
- SHGetDesktopFolder
- SHGetFolderPath
- SHGetFolderPathAndSubDir
- SHGetKnownFolder
- SHGetSpecialFolderLocation
- SHILCreateFromPath
- SHPathPrepareForWrite
- SHSetKnownFolderPath
- SignalFileOpen
- Win32DeleteFile
- shlwapi.dll
- id: KERNEL_API
title: Kernel api functions used
summary: Manipulates Windows Kernel & Drivers
Expand Down
1 change: 1 addition & 0 deletions blint/data/annotations/review_monero_go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ group: SYMBOL_REVIEWS
exe_type:
- gobinary
- x86_64-executable
- x86_64-exec
rules:
- id: MONERO_API_GO
title: Detect use of Monero wallet
Expand Down
2 changes: 1 addition & 1 deletion blint/data/annotations/review_rootkits_win.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ text: Review for Windows rootkits
group: METHOD_REVIEWS
exe_type:
- x86_64-executable
- x86_64-exec
- PE32
- PE64
rules:
Expand Down Expand Up @@ -67,7 +68,6 @@ rules:
- BeIsStringNull
- BeIsStringTerminated
- BeUnSupportedFunction

- id: NIDHOGG
title: Detect Nidhogg
summary: Provides Tools for Gaining Privileged Access and Injecting Malicious Code
Expand Down
Loading

0 comments on commit d574ea1

Please sign in to comment.