pdfalyzer 1.14.8__tar.gz → 1.14.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pdfalyzer might be problematic. Click here for more details.
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/CHANGELOG.md +6 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/PKG-INFO +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/binary/binary_scanner.py +9 -9
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/decorators/document_model_printer.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/decorators/pdf_object_properties.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/decorators/pdf_tree_node.py +11 -11
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/decorators/pdf_tree_verifier.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/detection/javascript_hunter.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/detection/yaralyzer_helper.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/helpers/number_helper.py +3 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/pdf_object_relationship.py +0 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/yara_rules/PDF.yara +61 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pyproject.toml +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/LICENSE +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/README.md +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/__init__.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/__main__.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/config.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/decorators/indeterminate_node.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/detection/constants/binary_regexes.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/detection/constants/javascript_reserved_keywords.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/font_info.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/helpers/dict_helper.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/helpers/pdf_object_helper.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/helpers/rich_text_helper.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/helpers/string_helper.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/character_mapping.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/layout.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/pdfalyzer_presenter.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/styles/node_colors.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/styles/rich_theme.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/tables/decoding_stats_table.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/tables/font_summary_table.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/tables/pdf_node_rich_table.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/output/tables/stream_objects_table.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/pdfalyzer.py +1 -1
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/util/adobe_strings.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/util/argument_parser.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/util/debugging.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/util/exceptions.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/util/pdf_parser_manager.py +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/yara_rules/PDF_binary_stream.yara +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/yara_rules/__init.py__ +0 -0
- {pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/yara_rules/lprat.static_file_analysis.yara +0 -0
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# NEXT RELEASE
|
|
2
2
|
|
|
3
|
+
### 1.14.10
|
|
4
|
+
* Add `malware_MaldocinPDF` YARA rule
|
|
5
|
+
|
|
6
|
+
### 1.14.9
|
|
7
|
+
* Add [ActiveMime YARA rule](https://blog.didierstevens.com/2023/08/29/quickpost-pdf-activemime-maldocs-yara-rule/)
|
|
8
|
+
|
|
3
9
|
### 1.14.8
|
|
4
10
|
* Handle internal YARA errors more gracefully with error messages instead of crashes (currently seeing `ERROR_TOO_MANY_RE_FIBERS` on macOS on some files for unknown reasons that we hope will go away eventually)
|
|
5
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pdfalyzer
|
|
3
|
-
Version: 1.14.
|
|
3
|
+
Version: 1.14.10
|
|
4
4
|
Summary: A PDF analysis toolkit. Scan a PDF with relevant YARA rules, visualize its inner tree-like data structure in living color (lots of colors), force decodes of suspicious font binaries, and more.
|
|
5
5
|
Home-page: https://github.com/michelcrypt4d4mus/pdfalyzer
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -30,7 +30,7 @@ from pdfalyzer.util.adobe_strings import CONTENTS, CURRENTFILE_EEXEC, FONT_FILE_
|
|
|
30
30
|
|
|
31
31
|
class BinaryScanner:
|
|
32
32
|
def __init__(self, _bytes: bytes, owner: PdfTreeNode, label: Optional[Text] = None):
|
|
33
|
-
"""owner is an optional link back to the object containing this binary"""
|
|
33
|
+
"""'owner' arg is an optional link back to the object containing this binary."""
|
|
34
34
|
self.bytes = _bytes
|
|
35
35
|
self.label = label
|
|
36
36
|
self.owner = owner
|
|
@@ -43,8 +43,8 @@ class BinaryScanner:
|
|
|
43
43
|
self.regex_extraction_stats = defaultdict(lambda: RegexMatchMetrics())
|
|
44
44
|
|
|
45
45
|
def check_for_dangerous_instructions(self) -> None:
|
|
46
|
-
"""Scan for all the strings in DANGEROUS_INSTRUCTIONS list and decode bytes around them"""
|
|
47
|
-
subheader = "Scanning Binary For Anything That Could Be Described As '
|
|
46
|
+
"""Scan for all the strings in DANGEROUS_INSTRUCTIONS list and decode bytes around them."""
|
|
47
|
+
subheader = "Scanning Binary For Anything That Could Be Described As 'sus'..."
|
|
48
48
|
print_section_sub_subheader(subheader, style=f"bright_red")
|
|
49
49
|
|
|
50
50
|
for instruction in DANGEROUS_STRINGS:
|
|
@@ -62,7 +62,7 @@ class BinaryScanner:
|
|
|
62
62
|
self.process_yara_matches(yaralyzer, instruction, force=True)
|
|
63
63
|
|
|
64
64
|
def check_for_boms(self) -> None:
|
|
65
|
-
"""Check the binary data for BOMs"""
|
|
65
|
+
"""Check the binary data for BOMs."""
|
|
66
66
|
print_section_sub_subheader("Scanning Binary for any BOMs...", style='BOM')
|
|
67
67
|
|
|
68
68
|
for bom_bytes, bom_name in BOMS.items():
|
|
@@ -105,11 +105,11 @@ class BinaryScanner:
|
|
|
105
105
|
return self._quote_yaralyzer(QUOTE_PATTERNS[BACKTICK], BACKTICK).match_iterator()
|
|
106
106
|
|
|
107
107
|
def extract_front_slash_quoted_bytes(self) -> Iterator[Tuple[BytesMatch, BytesDecoder]]:
|
|
108
|
-
"""Returns an interator over all strings surrounded by front_slashes (hint: regular expressions)"""
|
|
108
|
+
"""Returns an interator over all strings surrounded by front_slashes (hint: regular expressions)."""
|
|
109
109
|
return self._quote_yaralyzer(QUOTE_PATTERNS[FRONTSLASH], FRONTSLASH).match_iterator()
|
|
110
110
|
|
|
111
111
|
def print_stream_preview(self, num_bytes=None, title_suffix=None) -> None:
|
|
112
|
-
"""Print a preview showing the beginning and end of the embedded stream data"""
|
|
112
|
+
"""Print a preview showing the beginning and end of the embedded stream data."""
|
|
113
113
|
num_bytes = num_bytes or PdfalyzerConfig._args.preview_stream_length or console_width()
|
|
114
114
|
snipped_byte_count = self.stream_length - (num_bytes * 2)
|
|
115
115
|
console.line()
|
|
@@ -134,7 +134,7 @@ class BinaryScanner:
|
|
|
134
134
|
console.line()
|
|
135
135
|
|
|
136
136
|
def process_yara_matches(self, yaralyzer: Yaralyzer, pattern: str, force: bool = False) -> None:
|
|
137
|
-
"""Decide whether to attempt to decode the matched bytes, track stats. force param ignores min/max length"""
|
|
137
|
+
"""Decide whether to attempt to decode the matched bytes, track stats. force param ignores min/max length."""
|
|
138
138
|
for bytes_match, decoder in yaralyzer.match_iterator():
|
|
139
139
|
log.debug(f"Trackings stats for match: {pattern}, bytes_match: {bytes_match}, is_decodable: {bytes_match.is_decodable()}")
|
|
140
140
|
|
|
@@ -185,7 +185,7 @@ class BinaryScanner:
|
|
|
185
185
|
)
|
|
186
186
|
|
|
187
187
|
def _print_suppression_notices(self) -> None:
|
|
188
|
-
"""Print notices in queue in a single panel
|
|
188
|
+
"""Print the notices in queue in a single display panel and then empty the queue."""
|
|
189
189
|
if len(self.suppression_notice_queue) == 0:
|
|
190
190
|
return
|
|
191
191
|
|
|
@@ -195,5 +195,5 @@ class BinaryScanner:
|
|
|
195
195
|
self.suppression_notice_queue = []
|
|
196
196
|
|
|
197
197
|
def _eexec_idx(self) -> int:
|
|
198
|
-
"""Returns the location of CURRENTFILES_EEXEC within the binary stream
|
|
198
|
+
"""Returns the location of CURRENTFILES_EEXEC within the binary stream data (or 0 if it's not there)."""
|
|
199
199
|
return self.bytes.find(CURRENTFILE_EEXEC) if CURRENTFILE_EEXEC in self.bytes else 0
|
|
@@ -47,7 +47,7 @@ class PdfObjectProperties:
|
|
|
47
47
|
self.label = address
|
|
48
48
|
self.type = root_address(address) if isinstance(address, str) else None
|
|
49
49
|
|
|
50
|
-
# Force a string. TODO this sucks.
|
|
50
|
+
# Force self.label to be a string. TODO this sucks.
|
|
51
51
|
if isinstance(self.label, int):
|
|
52
52
|
self.label = f"{UNLABELED}[{self.label}]"
|
|
53
53
|
|
|
@@ -29,9 +29,9 @@ DECODE_FAILURE_LEN = -1
|
|
|
29
29
|
class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
30
30
|
def __init__(self, obj: PdfObject, address: str, idnum: int):
|
|
31
31
|
"""
|
|
32
|
-
obj:
|
|
33
|
-
address:
|
|
34
|
-
idnum:
|
|
32
|
+
obj: The underlying PDF object
|
|
33
|
+
address: The first address that points from some node to this one
|
|
34
|
+
idnum: ID used in the reference
|
|
35
35
|
"""
|
|
36
36
|
PdfObjectProperties.__init__(self, obj, address, idnum)
|
|
37
37
|
self.non_tree_relationships: List[PdfObjectRelationship] = []
|
|
@@ -54,7 +54,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
54
54
|
|
|
55
55
|
@classmethod
|
|
56
56
|
def from_reference(cls, ref: IndirectObject, address: str) -> 'PdfTreeNode':
|
|
57
|
-
"""Builds a PdfTreeDecorator from an IndirectObject"""
|
|
57
|
+
"""Builds a PdfTreeDecorator from an IndirectObject."""
|
|
58
58
|
try:
|
|
59
59
|
return cls(ref.get_object(), address, ref.idnum)
|
|
60
60
|
except PdfReadError as e:
|
|
@@ -90,7 +90,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
90
90
|
log.info(f'Added other relationship: {relationship} {self}')
|
|
91
91
|
|
|
92
92
|
def remove_non_tree_relationship(self, from_node: 'PdfTreeNode') -> None:
|
|
93
|
-
"""Remove all non_tree_relationships from from_node to this node"""
|
|
93
|
+
"""Remove all non_tree_relationships from from_node to this node."""
|
|
94
94
|
relationships_to_remove = [r for r in self.non_tree_relationships if r.from_node == from_node]
|
|
95
95
|
|
|
96
96
|
if len(relationships_to_remove) == 0:
|
|
@@ -104,7 +104,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
104
104
|
self.non_tree_relationships.remove(relationship)
|
|
105
105
|
|
|
106
106
|
def nodes_with_here_references(self) -> List['PdfTreeNode']:
|
|
107
|
-
"""Return a list of nodes that contain this nodes PDF object as an IndirectObject reference"""
|
|
107
|
+
"""Return a list of nodes that contain this nodes PDF object as an IndirectObject reference."""
|
|
108
108
|
return [r.from_node for r in self.non_tree_relationships if r.from_node]
|
|
109
109
|
|
|
110
110
|
def non_tree_relationship_count(self) -> int:
|
|
@@ -120,11 +120,11 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
120
120
|
return list(addresses)
|
|
121
121
|
|
|
122
122
|
def references_to_other_nodes(self) -> List[PdfObjectRelationship]:
|
|
123
|
-
"""Returns all nodes referenced from node.obj (see PdfObjectRelationship definition)"""
|
|
123
|
+
"""Returns all nodes referenced from node.obj (see PdfObjectRelationship definition)."""
|
|
124
124
|
return PdfObjectRelationship.build_node_references(from_node=self)
|
|
125
125
|
|
|
126
126
|
def contains_stream(self) -> bool:
|
|
127
|
-
"""Returns True for ContentStream, DecodedStream, and EncodedStream objects"""
|
|
127
|
+
"""Returns True for ContentStream, DecodedStream, and EncodedStream objects."""
|
|
128
128
|
return isinstance(self.obj, StreamObject)
|
|
129
129
|
|
|
130
130
|
def tree_address(self, max_length: Optional[int] = DEFAULT_MAX_ADDRESS_LENGTH) -> str:
|
|
@@ -144,7 +144,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
144
144
|
return '...' + address[-max_length:][3:]
|
|
145
145
|
|
|
146
146
|
def address_of_this_node_in_other(self, from_node: 'PdfTreeNode') -> Optional[str]:
|
|
147
|
-
"""Find the local address used in from_node to refer to this node"""
|
|
147
|
+
"""Find the local address used in 'from_node' to refer to this node."""
|
|
148
148
|
refs_to_this_node = [
|
|
149
149
|
ref for ref in from_node.references_to_other_nodes()
|
|
150
150
|
if ref.to_obj.idnum == self.idnum
|
|
@@ -189,7 +189,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
189
189
|
SymlinkNode(self, parent=relationship.from_node)
|
|
190
190
|
|
|
191
191
|
def descendants_count(self) -> int:
|
|
192
|
-
"""
|
|
192
|
+
"""Count nodes in the tree that are children/grandchildren/great grandchildren/etc of this one."""
|
|
193
193
|
return len(self.children) + sum([child.descendants_count() for child in self.children])
|
|
194
194
|
|
|
195
195
|
def unique_labels_of_referring_nodes(self) -> List[str]:
|
|
@@ -211,7 +211,7 @@ class PdfTreeNode(NodeMixin, PdfObjectProperties):
|
|
|
211
211
|
write_method(f" {i + 1}. {escape(str(r))}, Descendant Count: {r.from_node.descendants_count()}")
|
|
212
212
|
|
|
213
213
|
def _colored_address(self, max_length: Optional[int] = None) -> Text:
|
|
214
|
-
"""Rich text version of tree_address()"""
|
|
214
|
+
"""Rich text version of tree_address()."""
|
|
215
215
|
text = Text('@', style='bright_white')
|
|
216
216
|
return text.append(self.tree_address(max_length), style='address')
|
|
217
217
|
|
|
@@ -29,7 +29,7 @@ class PdfTreeVerifier:
|
|
|
29
29
|
log.warning(msg)
|
|
30
30
|
|
|
31
31
|
def verify_unencountered_are_untraversable(self) -> None:
|
|
32
|
-
"""Make sure any PDF object IDs we can't find in tree are /ObjStm or /Xref nodes"""
|
|
32
|
+
"""Make sure any PDF object IDs we can't find in tree are /ObjStm or /Xref nodes."""
|
|
33
33
|
if self.pdfalyzer.pdf_size is None:
|
|
34
34
|
log.warning(f"{SIZE} not found in PDF trailer; cannot verify all nodes are in tree")
|
|
35
35
|
return
|
|
@@ -6,7 +6,6 @@ from typing import List, Optional, Union
|
|
|
6
6
|
from PyPDF2.generic import IndirectObject, PdfObject
|
|
7
7
|
from yaralyzer.util.logging import log
|
|
8
8
|
|
|
9
|
-
#from pdfalyzer.he import has_indeterminate_prefix
|
|
10
9
|
from pdfalyzer.helpers.string_helper import bracketed, is_prefixed_by_any
|
|
11
10
|
from pdfalyzer.util.adobe_strings import *
|
|
12
11
|
|
|
@@ -983,3 +983,64 @@ rule PDF_JS_guillemet_close_in_Adobe_Type1_font
|
|
|
983
983
|
$url_js_backtick_close_obj and Adobe_Type_1_Font
|
|
984
984
|
}
|
|
985
985
|
|
|
986
|
+
|
|
987
|
+
rule rule_pdf_activemime {
|
|
988
|
+
meta:
|
|
989
|
+
author = "Didier Stevens"
|
|
990
|
+
date = "2023/08/29"
|
|
991
|
+
version = "0.0.1"
|
|
992
|
+
samples = "5b677d297fb862c2d223973697479ee53a91d03073b14556f421b3d74f136b9d,098796e1b82c199ad226bff056b6310262b132f6d06930d3c254c57bdf548187,ef59d7038cfd565fd65bae12588810d5361df938244ebad33b71882dcf683058"
|
|
993
|
+
description = "look for files that start with %PDF- and contain BASE64 encoded string ActiveMim (QWN0aXZlTWlt), possibly obfuscated with extra whitespace characters"
|
|
994
|
+
usage = "if you don't have to care about YARA performance warnings, you can uncomment string $base64_ActiveMim0 and remove all other $base64_ActiveMim## strings"
|
|
995
|
+
strings:
|
|
996
|
+
$pdf = "%PDF-"
|
|
997
|
+
// $base64_ActiveMim0 = /[ \t\r\n]*Q[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
998
|
+
$base64_ActiveMim1 = /Q [ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
999
|
+
$base64_ActiveMim2 = /Q \t[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1000
|
+
$base64_ActiveMim3 = /Q \r[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1001
|
+
$base64_ActiveMim4 = /Q \n[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1002
|
+
$base64_ActiveMim5 = /Q\t [ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1003
|
+
$base64_ActiveMim6 = /Q\t\t[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1004
|
+
$base64_ActiveMim7 = /Q\t\r[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1005
|
+
$base64_ActiveMim8 = /Q\t\n[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1006
|
+
$base64_ActiveMim9 = /Q\r [ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1007
|
+
$base64_ActiveMim10 = /Q\r\t[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1008
|
+
$base64_ActiveMim11 = /Q\r\r[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1009
|
+
$base64_ActiveMim12 = /Q\r\n[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1010
|
+
$base64_ActiveMim13 = /Q\n [ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1011
|
+
$base64_ActiveMim14 = /Q\n\t[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1012
|
+
$base64_ActiveMim15 = /Q\n\r[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1013
|
+
$base64_ActiveMim16 = /Q\n\n[ \t\r\n]*W[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1014
|
+
$base64_ActiveMim17 = /QW [ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1015
|
+
$base64_ActiveMim18 = /QW\t[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1016
|
+
$base64_ActiveMim19 = /QW\r[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1017
|
+
$base64_ActiveMim20 = /QW\n[ \t\r\n]*N[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1018
|
+
$base64_ActiveMim21 = /QWN[ \t\r\n]*0[ \t\r\n]*a[ \t\r\n]*X[ \t\r\n]*Z[ \t\r\n]*l[ \t\r\n]*T[ \t\r\n]*W[ \t\r\n]*l[ \t\r\n]*t/
|
|
1019
|
+
condition:
|
|
1020
|
+
$pdf at 0 and any of ($base64_ActiveMim*)
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
|
|
1024
|
+
rule malware_MaldocinPDF {
|
|
1025
|
+
meta:
|
|
1026
|
+
author = "Yuma Masubuchi and Kota Kino"
|
|
1027
|
+
description = "Search for embeddings of malicious Word files into a PDF file."
|
|
1028
|
+
created_date = "2023-08-15"
|
|
1029
|
+
blog_reference = "https://malware.news/t/maldoc-in-pdf-detection-bypass-by-embedding-a-malicious-word-file-into-a-pdf-file/72815"
|
|
1030
|
+
labs_reference = "N/A"
|
|
1031
|
+
labs_pivot = "N/A"
|
|
1032
|
+
samples = "ef59d7038cfd565fd65bae12588810d5361df938244ebad33b71882dcf683058"
|
|
1033
|
+
|
|
1034
|
+
strings:
|
|
1035
|
+
$docfile2 = "<w:WordDocument>" ascii nocase
|
|
1036
|
+
$xlsfile2 = "<x:ExcelWorkbook>" ascii nocase
|
|
1037
|
+
$mhtfile0 = "mime" ascii nocase
|
|
1038
|
+
$mhtfile1 = "content-location:" ascii nocase
|
|
1039
|
+
$mhtfile2 = "content-type:" ascii nocase
|
|
1040
|
+
|
|
1041
|
+
condition:
|
|
1042
|
+
(uint32(0) == 0x46445025) and
|
|
1043
|
+
(1 of ($mhtfile*)) and
|
|
1044
|
+
( (1 of ($docfile*)) or
|
|
1045
|
+
(1 of ($xlsfile*)) )
|
|
1046
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pdfalyzer"
|
|
3
|
-
version = "1.14.
|
|
3
|
+
version = "1.14.10"
|
|
4
4
|
description = "A PDF analysis toolkit. Scan a PDF with relevant YARA rules, visualize its inner tree-like data structure in living color (lots of colors), force decodes of suspicious font binaries, and more."
|
|
5
5
|
authors = ["Michel de Cryptadamus <michel@cryptadamus.com>"]
|
|
6
6
|
license = "GPL-3.0-or-later"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pdfalyzer-1.14.8 → pdfalyzer-1.14.10}/pdfalyzer/detection/constants/javascript_reserved_keywords.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -13,8 +13,8 @@ from anytree import LevelOrderIter, SymlinkNode
|
|
|
13
13
|
from anytree.search import findall, findall_by_attr
|
|
14
14
|
from PyPDF2 import PdfReader
|
|
15
15
|
from PyPDF2.generic import IndirectObject
|
|
16
|
-
from yaralyzer.output.file_hashes_table import compute_file_hashes
|
|
17
16
|
from yaralyzer.helpers.file_helper import load_binary_data
|
|
17
|
+
from yaralyzer.output.file_hashes_table import compute_file_hashes
|
|
18
18
|
from yaralyzer.output.rich_console import console
|
|
19
19
|
from yaralyzer.util.logging import log
|
|
20
20
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|