diff --git a/CHANGELOG.md b/CHANGELOG.md index c883b79..ae9bf80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- New '--enable-modifiers' command line option. Enabling this option will case UAC to run artifacts that change the current system state ([#272](https://github.com/tclahr/uac/issues/272)). - UAC now completely skips an artifact file (YAML) that has no artifacts to be collected for the target operating system. You can use '--artifacts list [OPERATING_SYSTEM]' to display artifacts for a specific operating system only. - New output file formats: - none: Collected data will not be archived or compressed. Instead, it will be copied directly to an output directory ([#188](https://github.com/tclahr/uac/issues/188)). @@ -43,6 +44,7 @@ - hash_executables/hash_executables.yaml: Updated to remove max_depth and max_file_size properties. - live_response/containers/jls.yaml: Added collection of jails used on FreeBSD systems [freebsd] ([Herbert-Karl](https://github.com/Herbert-Karl)). - live_response/hardware/dmesg.yaml: Updated collection of console message bufffer [esxi, freebsd, netscaler, openbsd, solaris] ([Herbert-Karl](https://github.com/Herbert-Karl)). +- live_response/modifiers/revel_hidden_processes.yaml: Added command to umount filesystems mounted onto a directory that tipically corresponds to a process ID (PID) [linux] ([halpomeranz](https://github.com/halpomeranz)). - live_response/network/procfs_information.yaml: Added collection of TCP and UDP network details from /proc/net [linux]. - live_response/process/deleted.yaml: Collection of deleted processes will no longer use dd conv=swab. The binary file will be collected in its raw format now [linux]. - live_response/process/deleted.yaml: Updated to fix the collection of open files of (malicious) processes [linux] ([mnrkbys](https://github.com/mnrkbys)). @@ -82,6 +84,7 @@ ### Artifacts Properties Changes +- Introduced a new global 'modifier' property that ensures the artifact runs only if '--enable-modifiers' command line option is used. - Introduced a new 'condition' property that ensures the collection runs only if the specified condition returns true. - The 'output_directory' property is now mandatory for the following collectors: command, find, hash and stat. - The 'file_type' property is now an array. diff --git a/artifacts/live_response/modifiers/revel_hidden_processes.yaml b/artifacts/live_response/modifiers/revel_hidden_processes.yaml new file mode 100644 index 0000000..25fb0c8 --- /dev/null +++ b/artifacts/live_response/modifiers/revel_hidden_processes.yaml @@ -0,0 +1,51 @@ +version: 1.0 +modifier: true +output_directory: /live_response/modifiers +artifacts: + - + description: Lists all mounted filesystems before changing the system state. + supported_os: [linux] + collector: command + command: mount + output_file: mount.txt + - + description: Report a snapshot of the current processes before changing the system state. + supported_os: [linux] + collector: command + command: ps + output_file: ps.txt + - + description: Report a snapshot of the current processes before changing the system state. + supported_os: [linux] + collector: command + command: ps auxwww + output_file: ps_auxwww.txt + - + description: Report a snapshot of the current processes before changing the system state. + supported_os: [linux] + collector: command + command: ps auxwwwf + output_file: ps_auxwwwf.txt + - + description: Report a snapshot of the current processes before changing the system state. + supported_os: [linux] + collector: command + command: ps -ef + output_file: ps_-ef.txt + - + description: List all PIDs with a directory in /proc but hidden for ps command. + supported_os: [linux] + collector: command + foreach: for pid in /proc/[0-9]*; do echo ${pid} | sed -e 's:/proc/::'; done + command: if ps ax | awk '{print $1}' | grep -q %line%; then true; else echo %line%; fi + output_file: hidden_pids_for_ps_command.txt + - + description: Umount all bind mounted directories to /proc/PID. + supported_os: [linux] + collector: command + foreach: mount | awk 'BEGIN { FS=" on "; } { print $2; }' | grep "/proc/[0-9]" | awk '{print $1}' + command: umount "%line%" + output_file: umount_%line%.txt + +# References: +# https://dfir.ch/posts/slash-proc/ \ No newline at end of file diff --git a/artifacts/live_response/process/procfs_information.yaml b/artifacts/live_response/process/procfs_information.yaml index 89f6dc6..b2afd47 100644 --- a/artifacts/live_response/process/procfs_information.yaml +++ b/artifacts/live_response/process/procfs_information.yaml @@ -229,6 +229,13 @@ artifacts: command: cat /proc/%line%/net/unix output_directory: /live_response/process/proc/%line%/net output_file: unix.txt + - + description: List all PIDs with a directory in /proc but hidden for ps command. + supported_os: [linux] + collector: command + foreach: for pid in /proc/[0-9]*; do echo ${pid} | sed -e 's:/proc/::'; done + command: if ps ax | awk '{print $1}' | grep -q %line%; then true; else echo %line%; fi + output_file: hidden_pids_for_ps_command.txt # linux: strings available - description: Collect command line arguments for a process. diff --git a/lib/build_artifact_list.sh b/lib/build_artifact_list.sh index e507c21..3026d86 100644 --- a/lib/build_artifact_list.sh +++ b/lib/build_artifact_list.sh @@ -20,7 +20,11 @@ _build_artifact_list() | while read __ba_item || [ -n "${__ba_item}" ]; do if [ -f "${__ba_item}" ] \ && { grep -q -E "supported_os:.*all|${__ba_operating_system}" "${__ba_item}" 2>/dev/null || [ "${__UAC_IGNORE_OPERATING_SYSTEM:-false}" = true ]; }; then - echo "${__ba_item}" + if grep -q -E "modifier:.*true" "${__ba_item}" 2>/dev/null; then + ${__UAC_ENABLE_MODIFIERS} && echo "${__ba_item}" + else + echo "${__ba_item}" + fi fi done diff --git a/lib/list_artifacts.sh b/lib/list_artifacts.sh index 60f9e1e..272a4e3 100644 --- a/lib/list_artifacts.sh +++ b/lib/list_artifacts.sh @@ -37,14 +37,25 @@ _list_artifacts() __oa_os="${2:-all}" if [ "${__oa_os}" = "all" ]; then + # shellcheck disable=SC2162 find "${__oa_artifacts_dir}"/* -name "*.yaml" -print 2>/dev/null \ - | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + | while read __oa_item || [ -n "${__oa_item}" ]; do + if grep -q -E "modifier:.*true" "${__oa_item}" 2>/dev/null; then + echo "${__oa_item} (modifier)" | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + else + echo "${__oa_item}" | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + fi + done else # shellcheck disable=SC2162 find "${__oa_artifacts_dir}"/* -name "*.yaml" -print 2>/dev/null \ | while read __oa_item || [ -n "${__oa_item}" ]; do if grep -q -E "supported_os:.*all|${__oa_os}" "${__oa_item}" 2>/dev/null; then - echo "${__oa_item}" | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + if grep -q -E "modifier:.*true" "${__oa_item}" 2>/dev/null; then + echo "${__oa_item} (modifier)" | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + else + echo "${__oa_item}" | sed -e "s|^${__oa_artifacts_dir}/||" 2>/dev/null + fi fi done fi diff --git a/lib/parse_command_line_arguments.sh b/lib/parse_command_line_arguments.sh index d72b83e..ede3cff 100644 --- a/lib/parse_command_line_arguments.sh +++ b/lib/parse_command_line_arguments.sh @@ -134,6 +134,9 @@ _parse_command_line_arguments() "-u"|"--run-as-non-root") __UAC_RUN_AS_NON_ROOT=true ;; + "--enable-modifiers") + __UAC_ENABLE_MODIFIERS=true + ;; "--hostname") if [ -n "${2:-}" ]; then __UAC_HOSTNAME="${2}" diff --git a/lib/usage.sh b/lib/usage.sh index 5bc6f7d..c9b7b21 100644 --- a/lib/usage.sh +++ b/lib/usage.sh @@ -65,6 +65,8 @@ Collection Arguments: -u, --run-as-non-root Disable root user check. Note that data collection may be limited. + --enable-modifiers + Enable artifacts that change the system state. --hostname HOSTNAME Specify the target system hostname. --temp-dir PATH diff --git a/lib/validate_artifact.sh b/lib/validate_artifact.sh index 18e2345..3eb2228 100644 --- a/lib/validate_artifact.sh +++ b/lib/validate_artifact.sh @@ -35,6 +35,7 @@ _validate_artifact() __va_max_depth="" __va_max_file_size="" __va_min_file_size="" + __va_modifier="" __va_name_pattern="" __va_output_directory="" __va_output_file="" @@ -74,6 +75,7 @@ _validate_artifact() if [ -n "${__va_output_directory}" ]; then __va_global_output_directory="${__va_output_directory}" fi + __va_modifier="" ;; "collector:") if [ -z "${__va_value}" ]; then @@ -257,6 +259,13 @@ _validate_artifact() fi __va_min_file_size="${__va_value}" ;; + "modifier:") + if [ "${__va_value}" != true ] && [ "${__va_value}" != false ]; then + _error_msg "artifact: 'modifier' must be 'true' or 'false'." + return 1 + fi + __va_modifier="${__va_value}" + ;; "name_pattern:") if echo "${__va_value}" | grep -q -v -E "^\[.*\]$"; then _error_msg "artifact: 'name_pattern' must be an array/list." @@ -420,6 +429,10 @@ _validate_artifact() _error_msg "artifact: invalid 'min_file_size' property for 'command' collector." return 1 fi + if [ -n "${__va_modifier}" ]; then + _error_msg "artifact: invalid 'modifier' property for 'command' collector." + return 1 + fi if [ -n "${__va_name_pattern}" ]; then _error_msg "artifact: invalid 'name_pattern' property for 'command' collector." return 1 @@ -456,6 +469,10 @@ _validate_artifact() _error_msg "artifact: invalid 'foreach' property for '${__va_collector}' collector." return 1 fi + if [ -n "${__va_modifier}" ]; then + _error_msg "artifact: invalid 'modifier' property for '${__va_collector}' collector." + return 1 + fi if [ "${__va_collector}" = "find" ] \ || [ "${__va_collector}" = "hash" ] \ || [ "${__va_collector}" = "stat" ]; then diff --git a/profiles/full.yaml b/profiles/full.yaml index 7113e6e..1bdaace 100644 --- a/profiles/full.yaml +++ b/profiles/full.yaml @@ -1,6 +1,7 @@ name: full description: Full artifacts collection. artifacts: + - live_response/modifiers/* - live_response/process/ps.yaml - live_response/process/lsof.yaml - live_response/process/top.yaml diff --git a/profiles/ir_triage.yaml b/profiles/ir_triage.yaml index 4645302..f5b3284 100644 --- a/profiles/ir_triage.yaml +++ b/profiles/ir_triage.yaml @@ -1,6 +1,7 @@ name: ir_triage description: Incident response triage collection. artifacts: + - live_response/modifiers/* - live_response/process/ps.yaml - live_response/process/lsof.yaml - live_response/process/top.yaml diff --git a/uac b/uac index f6d8860..3738fbf 100755 --- a/uac +++ b/uac @@ -61,6 +61,7 @@ __UAC_HASH_COLLECTED=false __UAC_CONFIG_FILE="${__UAC_DIR}/config/uac.conf" __UAC_MOUNT_POINT="/" __UAC_OPERATING_SYSTEM="" +__UAC_ENABLE_MODIFIERS=false __UAC_RUN_AS_NON_ROOT=false __UAC_PROCESSING_UNITS="" __UAC_HOSTNAME="" @@ -320,7 +321,6 @@ _log_msg INF "UAC directory: ${__UAC_DIR}" _log_msg INF "Command line: ${__ua_command_line}" _log_msg INF "Operating system: ${__UAC_OPERATING_SYSTEM}" _log_msg INF "System architecture: ${__UAC_SYSTEM_ARCH}" -_log_msg INF "Processing units: ${__UAC_PROCESSING_UNITS}" _log_msg INF "Hostname: ${__UAC_HOSTNAME}" _log_msg INF "Mount point: ${__UAC_MOUNT_POINT}" _log_msg INF "Running as: ${__ua_current_user}" @@ -409,6 +409,7 @@ _log_msg INF "Exclude mount points: ${__UAC_EXCLUDE_MOUNT_POINTS}" _verbose_msg "Building artifact list..." _log_msg INF "Building artifact list" +_log_msg INF "Enable modifiers: ${__UAC_ENABLE_MODIFIERS}" # build artifact list based on the operating system # skip artifacts that are not applicable to the target operating system