@@ -1064,6 +1064,15 @@ class QuarantinedFiles(Module):
1064
1064
]
1065
1065
1066
1066
1067
+ @register_module ("--edr" )
1068
+ class EDR (Module ):
1069
+ DESC = "various Endpoint Detection and Response (EDR) logs"
1070
+ SPEC = [
1071
+ # Carbon Black
1072
+ ("dir" , "sysvol/ProgramData/CarbonBlack/Logs" ),
1073
+ ]
1074
+
1075
+
1067
1076
@register_module ("--history" )
1068
1077
class History (Module ):
1069
1078
DESC = "browser history from IE, Edge, Firefox, and Chrome"
@@ -1655,7 +1664,7 @@ def _add_modules_for_profile(choice: str, operating_system: str, profile: dict,
1655
1664
return modules_selected
1656
1665
1657
1666
1658
- def acquire_target (target : Target , args : argparse .Namespace , output_ts : Optional [str ] = None ) -> list [str ]:
1667
+ def acquire_target (target : Target , args : argparse .Namespace , output_ts : Optional [str ] = None ) -> list [str | Path ]:
1659
1668
acquire_gui = GUI ()
1660
1669
files = []
1661
1670
output_ts = output_ts or get_utc_now_str ()
@@ -1863,16 +1872,18 @@ def acquire_target(target: Target, args: argparse.Namespace, output_ts: Optional
1863
1872
return files
1864
1873
1865
1874
1866
- def upload_files (paths : list [Path ], upload_plugin : UploaderPlugin , no_proxy : bool = False ) -> None :
1875
+ def upload_files (paths : list [str | Path ], upload_plugin : UploaderPlugin , no_proxy : bool = False ) -> None :
1867
1876
proxies = None if no_proxy else urllib .request .getproxies ()
1868
1877
log .debug ("Proxies: %s (no_proxy = %s)" , proxies , no_proxy )
1869
1878
1879
+ log .info ('Uploading files: "%s"' , " " .join (map (str , paths )))
1870
1880
try :
1871
1881
upload_files_using_uploader (upload_plugin , paths , proxies )
1872
1882
except Exception :
1873
- log .error ("Upload %s FAILED. See log file for details." , paths )
1874
- GUI ().message ("Upload failed." )
1875
- log .exception ("" )
1883
+ log .error ('Upload FAILED for files: "%s". See log file for details.' , " " .join (map (str , paths )))
1884
+ raise
1885
+ else :
1886
+ log .info ("Upload succeeded." )
1876
1887
1877
1888
1878
1889
class WindowsProfile :
@@ -2054,29 +2065,30 @@ class VolatileProfile:
2054
2065
}
2055
2066
2056
2067
2068
+ def exit_success (default_args : list [str ]):
2069
+ log .info ("Acquire finished successful" )
2070
+ log .info ("Arguments: %s" , " " .join (sys .argv [1 :]))
2071
+ log .info ("Default Arguments: %s" , " " .join (default_args ))
2072
+ log .info ("Exiting with status code 0 (SUCCESS)" )
2073
+ sys .exit (0 )
2074
+
2075
+
2076
+ def exit_failure (default_args : list [str ]):
2077
+ log .error ("Acquire FAILED" )
2078
+ log .error ("Arguments: %s" , " " .join (sys .argv [1 :]))
2079
+ log .error ("Default Arguments: %s" , " " .join (default_args ))
2080
+ log .error ("Exiting with status code 1 (FAILURE)" )
2081
+ sys .exit (1 )
2082
+
2083
+
2057
2084
def main () -> None :
2058
2085
parser = create_argument_parser (PROFILES , VOLATILE , MODULES )
2059
2086
args , rest = parse_acquire_args (parser , config = CONFIG )
2060
2087
2061
- # start GUI if requested through CLI / config
2062
- flavour = None
2063
- if args .gui == "always" or (
2064
- args .gui == "depends" and os .environ .get ("PYS_KEYSOURCE" ) == "prompt" and len (sys .argv ) == 1
2065
- ):
2066
- flavour = platform .system ()
2067
-
2068
- acquire_gui = GUI (flavour = flavour , upload_available = args .auto_upload )
2069
- args .output , args .auto_upload , cancel = acquire_gui .wait_for_start (args )
2070
-
2071
2088
# Since output has a default value, set it to None when output_file is defined
2072
2089
if args .output_file :
2073
2090
args .output = None
2074
2091
2075
- if cancel :
2076
- parser .exit (0 )
2077
-
2078
- # From here onwards, the GUI will be locked and cannot be closed because we're acquiring
2079
-
2080
2092
try :
2081
2093
check_and_set_log_args (args )
2082
2094
except ValueError as err :
@@ -2093,65 +2105,118 @@ def main() -> None:
2093
2105
2094
2106
setup_logging (log , log_file , args .verbose , delay = args .log_delay )
2095
2107
2096
- log .info (ACQUIRE_BANNER )
2097
- log .info ("User: %s | Admin: %s" , get_user_name (), is_user_admin ())
2098
- log .info ("Arguments: %s" , " " .join (sys .argv [1 :]))
2099
- log .info ("Default Arguments: %s" , " " .join (args .config .get ("arguments" )))
2100
- log .info ("" )
2108
+ acquire_successful = True
2109
+ files_to_upload = [log_file ]
2110
+ acquire_gui = None
2111
+ try :
2112
+ log .info (ACQUIRE_BANNER )
2113
+ log .info ("User: %s | Admin: %s" , get_user_name (), is_user_admin ())
2114
+ log .info ("Arguments: %s" , " " .join (sys .argv [1 :]))
2115
+ log .info ("Default Arguments: %s" , " " .join (args .config .get ("arguments" )))
2116
+ log .info ("" )
2101
2117
2102
- plugins_to_load = [("cloud" , MinIO )]
2103
- upload_plugins = UploaderRegistry ("acquire.plugins" , plugins_to_load )
2118
+ # start GUI if requested through CLI / config
2119
+ flavour = None
2120
+ if args .gui == "always" or (
2121
+ args .gui == "depends" and os .environ .get ("PYS_KEYSOURCE" ) == "prompt" and len (sys .argv ) == 1
2122
+ ):
2123
+ flavour = platform .system ()
2124
+ acquire_gui = GUI (flavour = flavour , upload_available = args .auto_upload )
2125
+
2126
+ args .output , args .auto_upload , cancel = acquire_gui .wait_for_start (args )
2127
+ if cancel :
2128
+ log .info ("Acquire cancelled" )
2129
+ exit_success (args .config .get ("arguments" ))
2130
+ # From here onwards, the GUI will be locked and cannot be closed because we're acquiring
2131
+
2132
+ plugins_to_load = [("cloud" , MinIO )]
2133
+ upload_plugins = UploaderRegistry ("acquire.plugins" , plugins_to_load )
2104
2134
2105
- try :
2106
2135
check_and_set_acquire_args (args , upload_plugins )
2107
- except ValueError as err :
2108
- log .exception (err )
2109
- parser .exit (1 )
2110
2136
2111
- if args .upload :
2137
+ if args .upload :
2138
+ try :
2139
+ upload_files (args .upload , args .upload_plugin , args .no_proxy )
2140
+ except Exception as err :
2141
+ acquire_gui .message ("Failed to upload files" )
2142
+ log .exception (err )
2143
+ exit_failure (args .config .get ("arguments" ))
2144
+ exit_success (args .config .get ("arguments" ))
2145
+
2146
+ target_paths = []
2147
+ for target_path in args .targets :
2148
+ target_path = args_to_uri ([target_path ], args .loader , rest )[0 ] if args .loader else target_path
2149
+ if target_path == "local" :
2150
+ target_query = {}
2151
+ if args .force_fallback :
2152
+ target_query .update ({"force-directory-fs" : 1 })
2153
+
2154
+ if args .fallback :
2155
+ target_query .update ({"fallback-to-directory-fs" : 1 })
2156
+
2157
+ target_query = urllib .parse .urlencode (target_query )
2158
+ target_path = f"{ target_path } ?{ target_query } "
2159
+ target_paths .append (target_path )
2160
+
2112
2161
try :
2113
- upload_files (args .upload , args .upload_plugin , args .no_proxy )
2162
+ target_name = "Unknown" # just in case open_all already fails
2163
+ for target in Target .open_all (target_paths ):
2164
+ target_name = "Unknown" # overwrite previous target name
2165
+ target_name = target .name
2166
+ log .info ("Loading target %s" , target_name )
2167
+ log .info (target )
2168
+ if target .os == "esxi" and target .name == "local" :
2169
+ # Loader found that we are running on an esxi host
2170
+ # Perform operations to "enhance" memory
2171
+ with esxi_memory_context_manager ():
2172
+ files_to_upload = acquire_children_and_targets (target , args )
2173
+ else :
2174
+ files_to_upload = acquire_children_and_targets (target , args )
2114
2175
except Exception :
2115
- log .exception ("Failed to upload files" )
2116
- return
2117
-
2118
- target_paths = []
2119
- for target_path in args .targets :
2120
- target_path = args_to_uri ([target_path ], args .loader , rest )[0 ] if args .loader else target_path
2121
- if target_path == "local" :
2122
- target_query = {}
2123
- if args .force_fallback :
2124
- target_query .update ({"force-directory-fs" : 1 })
2176
+ log .error ("Failed to acquire target: %s" , target_name )
2177
+ if not is_user_admin ():
2178
+ log .error ("Try re-running as administrator/root" )
2179
+ acquire_gui .message ("This application must be run as administrator." )
2180
+ raise
2125
2181
2126
- if args .fallback :
2127
- target_query .update ({"fallback-to-directory-fs" : 1 })
2182
+ files_to_upload = sort_files (files_to_upload )
2128
2183
2129
- target_query = urllib .parse .urlencode (target_query )
2130
- target_path = f"{ target_path } ?{ target_query } "
2131
- target_paths .append (target_path )
2184
+ except Exception as err :
2185
+ log .error ("Acquiring artifacts FAILED" )
2186
+ log .exception (err )
2187
+ acquire_successful = False
2188
+ else :
2189
+ log .info ("Acquiring artifacts succeeded" )
2132
2190
2133
2191
try :
2134
- target_name = "Unknown" # just in case open_all already fails
2135
- for target in Target .open_all (target_paths ):
2136
- target_name = "Unknown" # overwrite previous target name
2137
- target_name = target .name
2138
- log .info ("Loading target %s" , target_name )
2139
- log .info (target )
2140
- if target .os == "esxi" and target .name == "local" :
2141
- # Loader found that we are running on an esxi host
2142
- # Perform operations to "enhance" memory
2143
- with esxi_memory_context_manager ():
2144
- acquire_children_and_targets (target , args )
2145
- else :
2146
- acquire_children_and_targets (target , args )
2147
- except Exception :
2148
- if not is_user_admin ():
2149
- log .error ("Failed to load target: %s, try re-running as administrator/root" , target_name )
2150
- acquire_gui .message ("This application must be run as administrator." )
2192
+ # The auto-upload of files is done at the very very end to make sure any
2193
+ # logged exceptions are written to the log file before uploading.
2194
+ # This means that any failures from this point on will not be part of the
2195
+ # uploaded log files, they will be written to the logfile on disk though.
2196
+ if args .auto_upload and args .upload_plugin and files_to_upload :
2197
+ try :
2198
+ log_file_handler = get_file_handler (log )
2199
+ if log_file_handler :
2200
+ log_file_handler .close ()
2201
+
2202
+ upload_files (files_to_upload , args .upload_plugin )
2203
+ except Exception :
2204
+ if acquire_gui :
2205
+ acquire_gui .message ("Failed to upload files" )
2206
+ raise
2207
+
2208
+ if acquire_gui :
2209
+ acquire_gui .finish ()
2151
2210
acquire_gui .wait_for_quit ()
2152
- parser .exit (1 )
2153
- log .exception ("Failed to load target: %s" , target_name )
2154
- raise
2211
+
2212
+ except Exception as err :
2213
+ acquire_successful = False
2214
+ log .exception (err )
2215
+
2216
+ if acquire_successful :
2217
+ exit_success (args .config .get ("arguments" ))
2218
+ else :
2219
+ exit_failure (args .config .get ("arguments" ))
2155
2220
2156
2221
2157
2222
def load_child (target : Target , child_path : Path ) -> None :
@@ -2167,7 +2232,7 @@ def load_child(target: Target, child_path: Path) -> None:
2167
2232
return child
2168
2233
2169
2234
2170
- def acquire_children_and_targets (target : Target , args : argparse .Namespace ) -> None :
2235
+ def acquire_children_and_targets (target : Target , args : argparse .Namespace ) -> list [ str | Path ] :
2171
2236
if args .child :
2172
2237
target = load_child (target , args .child )
2173
2238
@@ -2185,20 +2250,20 @@ def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> No
2185
2250
if (args .children and not args .skip_parent ) or not args .children :
2186
2251
total_targets += 1
2187
2252
counter += 1
2188
- acquire_gui .shard = ( progress_limit // total_targets ) * counter
2253
+ acquire_gui .shard = int (( progress_limit / total_targets ) * counter )
2189
2254
try :
2190
2255
files .extend (acquire_target (target , args , args .start_time ))
2191
2256
2192
2257
except Exception :
2193
- log .exception ("Failed to acquire target" )
2258
+ log .error ("Failed to acquire main target" )
2194
2259
acquire_gui .message ("Failed to acquire target" )
2195
2260
acquire_gui .wait_for_quit ()
2196
2261
raise
2197
2262
2198
2263
if args .children :
2199
2264
for child in target .list_children ():
2200
2265
counter += 1
2201
- acquire_gui .shard = ( 100 // total_targets ) * counter
2266
+ acquire_gui .shard = int (( progress_limit / total_targets ) * counter )
2202
2267
try :
2203
2268
child_target = load_child (target , child .path )
2204
2269
except Exception :
@@ -2210,29 +2275,11 @@ def acquire_children_and_targets(target: Target, args: argparse.Namespace) -> No
2210
2275
child_files = acquire_target (child_target , args )
2211
2276
files .extend (child_files )
2212
2277
except Exception :
2213
- log .exception ("Failed to acquire child target" )
2278
+ log .exception ("Failed to acquire child target %s" , child_target . name )
2214
2279
acquire_gui .message ("Failed to acquire child target" )
2215
2280
continue
2216
2281
2217
- files = sort_files (files )
2218
-
2219
- if args .auto_upload :
2220
- log_file_handler = get_file_handler (log )
2221
- if log_file_handler :
2222
- log_file_handler .close ()
2223
-
2224
- log .info ("" )
2225
- try :
2226
- upload_files (files , args .upload_plugin )
2227
- acquire_gui .finish ()
2228
- acquire_gui .wait_for_quit ()
2229
- except Exception :
2230
- log .exception ("Failed to upload files" )
2231
- acquire_gui .message ("Failed to upload files" )
2232
- acquire_gui .wait_for_quit ()
2233
- else :
2234
- acquire_gui .finish ()
2235
- acquire_gui .wait_for_quit ()
2282
+ return files
2236
2283
2237
2284
2238
2285
def sort_files (files : list [Union [str , Path ]]) -> list [Path ]:
0 commit comments