diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec2299c..89ce5d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,19 +4,23 @@ ### Artifacts +- chkrootkit/hidden_etc_ld_so_preload.yaml: Added collection of hidden /etc/ld.so.preload using debugfs and xfs_db tools [linux] ([mnrkbys](https://github.com/mnrkbys)). - files/applications/ark.yaml: Added collection of metadata about recently opened archive files in Ark, the KDE archive manager [freebsd, linux, netbsd, openbsd]. +- files/applications/atftp.yaml: Added collection of atftp history files [all] (by [Pierre-Gronau-ndaal](https://github.com/Pierre-Gronau-ndaal)). - files/applications/dolphin.yaml: Added collection of session data for the Dolphin file manager in the KDE desktop environment. This file contains information about the state of the Dolphin application, such as the currently open directories and their paths and the last accessed locations [freebsd, linux, netbsd, openbsd]. - files/applications/dragon_player.yaml: Added collection of paths to recently opened video files using the Dragon Player [freebsd, linux, netbsd, openbsd]. - files/applications/geany.yaml: Added collection of metadata about recently opened files in Geany text editor [freebsd, linux, netbsd, openbsd]. - files/applications/gedit.yaml: Added collection of metadata about recently opened files in Gedit text editor [freebsd, linux, netbsd, openbsd]. - files/applications/gnome_text_editor.yaml: Added collection of metadata about recently opened files in Gnome Text Editor [freebsd, linux, netbsd, openbsd]. - files/applications/katesession.yaml: Added colleection of metadata about recently opened files in Kwrite and Kate text editors [freebsd, linux, netbsd, openbsd]. +- files/applications/nano.yaml: Added collection of nano history file [all] (by [Pierre-Gronau-ndaal](https://github.com/Pierre-Gronau-ndaal)). - files/applications/okular.yaml: Added collection of metadata related to documents that have been opened or interacted with using Okular, a document viewer for KDE [freebsd, linux, netbsd, openbsd]. - files/system/gvfs_metadata.yaml: Added collection of data from the gvfs-metadata directory to retrieve user-specific metadata, such as file access details, custom properties, and interaction history [freebsd, linux, netbsd, openbsd]. - files/system/kactivitymanagerd.yaml: Added collection of activity tracking data used by KActivityManager (part of KDE) to track and manage user activities, such as recently opened files, applications, and other resources [freebsd, linux, netbsd, openbsd]. - files/system/upstart.yaml: Added collection of system-wide and user-session Upstart configuration files [linux]. - files/system/xdg_autostart.yaml: Added collection of system-wide and user-specific XDG autostart files [linux]. - live_response/packages/0install.yaml: Added collection of the list of installed packages managed by Zero Install package manager [linux] (by [Pierre-Gronau-ndaal](https://github.com/Pierre-Gronau-ndaal)). +- live_response/packages/apk.yaml: Added collection of the list of installed packages managed by the apk package manager [linux] (by [Pierre-Gronau-ndaal](https://github.com/Pierre-Gronau-ndaal)). - live_response/packages/conary.yaml: Added collection of the list of installed packages managed by the Conary package manager [linux] (by [Pierre-Gronau-ndaal](https://github.com/Pierre-Gronau-ndaal)). - live_response/packages/dpkg.yaml: Updated to verify all packages to compare information about the installed files in the package with information about the files taken from the package metadata stored in the dpkg database [linux] ([mnrkbys](https://github.com/mnrkbys)). - live_response/packages/package_owns_file.yaml: Added collection of which installed package owns a specific file or command. Note that this artifact is resource-intensive and time-consuming to execute, so it is disabled by default in all profiles [linux] ([mnrkbys](https://github.com/mnrkbys)). diff --git a/artifacts/chkrootkit/hidden_etc_ld_so_preload.yaml b/artifacts/chkrootkit/hidden_etc_ld_so_preload.yaml new file mode 100644 index 00000000..41dbb442 --- /dev/null +++ b/artifacts/chkrootkit/hidden_etc_ld_so_preload.yaml @@ -0,0 +1,28 @@ +version: 1.0 +output_directory: /chkrootkit +# Collect /etc/ld.so.preload even if it is hidden by LD_PRELOAD Rootkits. +# ref 1: https://www.youtube.com/watch?v=3UrEJzqqPYQ +# ref 2: https://righteousit.com/wp-content/uploads/2024/04/ld_preload-rootkits.pdf +# ref 3: https://www.youtube.com/watch?v=-K9hhqv21P8 +# ref 4: https://righteousit.com/wp-content/uploads/2024/04/xfs_db-ftw.pdf +artifacts: + - + description: Dump /etc/ld.so.preload with debugfs. + supported_os: [linux] + collector: command + condition: command_exists "debugfs" && df -T %mount_point%/etc | tail -n +2 | awk '{print $2}' | grep -q "ext" + command: linux_dump_etc_ld_so_preload.sh -f %mount_point%/etc/ld.so.preload + output_file: etc_ld_so_preload.txt + - + description: Dump /etc/ld.so.preload with xfs_db. + supported_os: [linux] + collector: command + condition: command_exists "xfs_db" && df -T %mount_point%/etc | tail -n +2 | awk '{print $2}' | grep -q "xfs" + command: linux_dump_etc_ld_so_preload.sh -f %mount_point%/etc/ld.so.preload + output_file: etc_ld_so_preload.txt + - + description: Collect file stats for /etc/ld.so.preload even if it is hidden. + supported_os: [linux] + collector: stat + path: /etc/ld.so.preload + output_file: stat_etc_ld_so_preload.txt diff --git a/artifacts/files/applications/atftp.yaml b/artifacts/files/applications/atftp.yaml new file mode 100644 index 00000000..00b66d46 --- /dev/null +++ b/artifacts/files/applications/atftp.yaml @@ -0,0 +1,10 @@ +version: 1.0 +artifacts: + - + description: Collect atftp history files. + supported_os: [all] + collector: file + path: /%user_home% + name_pattern: [".atftp_history"] + max_depth: 4 + diff --git a/artifacts/files/applications/nano.yaml b/artifacts/files/applications/nano.yaml new file mode 100644 index 00000000..148ed3eb --- /dev/null +++ b/artifacts/files/applications/nano.yaml @@ -0,0 +1,9 @@ +version: 1.0 +artifacts: + - + description: Collect nano history files. + supported_os: [all] + collector: file + path: /%user_home% + name_pattern: [".nano_history"] + max_depth: 4 diff --git a/artifacts/live_response/packages/apk.yaml b/artifacts/live_response/packages/apk.yaml new file mode 100644 index 00000000..eaf055f1 --- /dev/null +++ b/artifacts/live_response/packages/apk.yaml @@ -0,0 +1,10 @@ +version: 1.0 +condition: command_exists "apk" +output_directory: /live_response/packages +artifacts: + - + description: Display installed packages. + supported_os: [linux] + collector: command + command: apk info -vv + output_file: apk_info_-vv.txt diff --git a/bin/linux/linux_dump_etc_ld_so_preload.sh b/bin/linux/linux_dump_etc_ld_so_preload.sh new file mode 100755 index 00000000..b98b8bee --- /dev/null +++ b/bin/linux/linux_dump_etc_ld_so_preload.sh @@ -0,0 +1,374 @@ +#!/bin/bash +# Copyright 2024 Minoru Kobayashi (@unkn0wnbit) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# ref 1: https://www.youtube.com/watch?v=3UrEJzqqPYQ +# ref 2: https://righteousit.com/wp-content/uploads/2024/04/ld_preload-rootkits.pdf +# ref 3: https://www.youtube.com/watch?v=-K9hhqv21P8 +# ref 4: https://righteousit.com/wp-content/uploads/2024/04/xfs_db-ftw.pdf + +set -euo pipefail + +usage() { + cat <&2 + fi +} + + +get_real_path() { + local path=$1 + local processed_path=$2 + + if [[ ! "$path" =~ ^(/|./|../) ]]; then + if [[ "${processed_path}" == "/" ]]; then + path="/${path}" + else + path="${processed_path}/${path}" + fi + fi + realpath -s "${path}" +} + + +join_remain_path() { + local index=$1 + local path_array=("${@:2}") + local sub_path + local old_IFS + + old_IFS="${IFS}" + IFS='/' + sub_path="${path_array[*]:${index}}" + IFS="${old_IFS}" + echo "${sub_path}" +} + + +get_device_fstype() { + local path=$1 + local device + local fs_type + local mount_point + + read -r device fs_type mount_point <<< "$(df -T "$(dirname "${path}")" | awk 'NR==2 {print $1, $2, $NF}')" + echo "${device}" "${fs_type}" "${mount_point}" +} + + +get_xfs_inumber_local() { + local device=$1 + local root_inumber=$2 + local path=$3 + + xfs_db -r "${device}" -c "inode ${root_inumber}" -c "print" | awk -v path="${path}" ' + BEGIN { + found_filename = 0; + found_entry = 0; + filetype = 0; + } + + { + if ($0 ~ "u3.sfdir3.list\\[[0-9]+\\].name = \"" path "\"") { + found_filename = 1; + } + + if (found_filename && $0 ~ /u3.sfdir3.list\[[0-9]+\].inumber.i4 = [0-9]+/) { + match($0, /u3.sfdir3.list\[[0-9]+\].inumber.i4 = ([0-9]+)/, arr); + inumber = arr[1]; + } + + # filetype: 1 (regular file), 2 (directory), 7 (symlink) + if (found_filename && $0 ~ /u3.sfdir3.list\[[0-9]+\].filetype = (1|2|7)/) { + match($0, /u3.sfdir3.list\[[0-9]+\].filetype = ([0-9]+)/, arr); + filetype = arr[1]; + found_entry = 1; + exit; + } + } + + END { + if (!found_entry) { + inumber = 0; + } + print inumber, filetype; + } + ' +} + + +get_xfs_inumber_extents() { + local device=$1 + local fsblocks=$2 + local path=$3 + local fsblock + local result + + for fsblock in ${fsblocks}; do + result=$(xfs_db -r "${device}" -c "fsblock ${fsblock}" -c "type dir3" -c "print" | awk -v path="${path}" ' + BEGIN { + found_name = 0; + found_entry = 0; + } + + { + if ($0 ~ /(du|bu)\[[0-9]+\].inumber = [0-9]+/) { + match($0, /(du|bu)\[[0-9]+\].inumber = ([0-9]+)/, arr); + inumber = arr[2]; + } + + if ($0 ~ "(du|bu)\\[[0-9]+\\].name = \"" path "\"") { + found_name = 1; + } + + if (found_name && $0 ~ /(du|bu)\[[0-9]+\].filetype = (1|2|7)/) { + match($0, /(du|bu)\[[0-9]+\].filetype = ([0-9]+)/, arr); + filetype = arr[2]; + found_entry = 1; + exit; + } + } + + END { + if (!found_entry) { + inumber = 0; + } + print inumber, filetype; + } + ') + if [ "$(echo "${result}" | awk '{print $1}')" -ne 0 ]; then + echo "${result}" + return + fi + done + echo 0 0 +} + + +get_xfs_child_inumber() { + local device=$1 + local parent_inumber=$2 + local path=$3 + local fsblocks + local fsblock + + fsblocks=$(xfs_db -r "${device}" -c "inode ${parent_inumber}" -c "bmap" | awk '{print $5}') + if [ -z "${fsblocks}" ]; then + read -r inumber filetype <<< "$(get_xfs_inumber_local "${device}" "${parent_inumber}" "${path}")" + echo "${inumber} ${filetype}" + return + else + read -r inumber filetype <<< "$(get_xfs_inumber_extents "${device}" "${fsblocks}" "${path}")" + echo "${inumber} ${filetype}" + return + fi +} + + +get_xfs_inumber_from_path() { + local processed_path="" + local full_path + local device + local fs_type + local mount_point + local root_inumber + local parent_inumber + local inumber=0 + local filetype=0 + local symlink_target + local sub_path + local idx + local path_array + local old_IFS + + full_path=$(get_real_path "$1" "${processed_path}") + read -r device fs_type mount_point <<< "$(get_device_fstype "${full_path}")" + # Remove mount_point from full_path if it starts with mount_point + if [[ "${mount_point}" != "/" && "$full_path" == "$mount_point"* ]]; then + full_path="${full_path/#${mount_point}/}" + fi + + root_inumber=$(xfs_db -r "${device}" -c "sb 0" -c "print" | awk -F " = " '$1 == "rootino" {print $2}') + parent_inumber=${root_inumber} + + old_IFS="${IFS}" + IFS='/' + read -r -a path_array <<< "${full_path}" + IFS="${old_IFS}" + + for idx in "${!path_array[@]}"; do + if [ "${idx}" -eq 0 ]; then + processed_path="/" + continue + elif [ "${idx}" -ge 1 ]; then + read -r inumber filetype <<< "$(get_xfs_child_inumber "${device}" "${parent_inumber}" "${path_array[$idx]}")" + if [ "${inumber}" -eq 0 ]; then + print_msg "${path_array[$idx]} not found." + break + elif [ "${filetype}" -eq 7 ]; then # If the file is a symlink, get the target file's inode number. + symlink_target=$(xfs_db -r "${device}" -c "inode ${inumber}" -c "print" | sed -n 's/u3.symlink = "\(.*\)"/\1/p') + print_msg "symlink target: ${symlink_target}" + symlink_target=$(get_real_path "${symlink_target}" "${processed_path}") + sub_path=$(join_remain_path $((idx+1)) "${path_array[@]}") + if [ -n "${sub_path}" ]; then + symlink_target="${symlink_target}/${sub_path}" + fi + + read -r inumber filetype <<< "$(get_xfs_inumber_from_path "${symlink_target}")" + if [[ ! "${inumber}" =~ ^[0-9]+$ ]]; then + print_msg "${symlink_target} not found." + return 3 + fi + + echo "${inumber}" "${filetype}" + return + fi + processed_path="${processed_path}/${path_array[$idx]}" + parent_inumber=${inumber} + fi + done + + echo "${inumber}" "${filetype}" +} + + +dump_xfs_ldsopreload() { + local file=$1 + local outputfile=$2 + local device=$3 + local block_count + local inumber + local filetype + local fsblock_items + local fsblock + local agno + local agblock + + # Get an inode number of the file. + read -r inumber filetype <<< "$(get_xfs_inumber_from_path "${file}")" + + if [ "${inumber}" -eq 0 ]; then + print_msg "${file} not found." + exit 3 + fi + + # Get fsblock numbers of the file. + fsblock_items=$(xfs_db -r "${device}" -c "inode ${inumber}" -c "bmap" | awk '{print $5, $8}') + + if [ -z "${fsblock_items}" ]; then + print_msg "${file}: bmap not found." + exit 4 + fi + + while read -r fsblock block_count; do + # Convert fsblock to agno. + agno=$(xfs_db -r "${device}" -c "convert fsblock ${fsblock} agno" | sed -n 's/.*(\([0-9]*\)).*/\1/p') + + if [ -z "${agno}" ]; then + print_msg "${file}: agno not found." + exit 5 + fi + + # Convert fsblock to agblock. + agblock=$(xfs_db -r "${device}" -c "convert fsblock ${fsblock} agblock" | sed -n 's/.*(\([0-9]*\)).*/\1/p') + + if [ -z "${agblock}" ]; then + print_msg "${file}: agblock not found." + exit 6 + fi + + # Dump file data. + sb0_output=$(xfs_db -r "${device}" -c "sb 0" -c "print") + block_size=$(echo "${sb0_output}" | awk -F " = " '$1 == "blocksize" {print $2}') + agblocks=$(echo "${sb0_output}" | awk -F " = " '$1 == "agblocks" {print $2}') + skip_len=$((agno * agblocks + agblock)) + + if [ -z "${outputfile}" ]; then + dd if="${device}" bs="${block_size}" skip="${skip_len}" count="${block_count}" status=none + else + dd if="${device}" of="${outputfile}" bs="${block_size}" skip="${skip_len}" count="${block_count}" status=none + fi + done <<< "${fsblock_items}" +} + + +dump_ext_ldsopreload() { + local full_path=$1 + local outputfile=$2 + local device=$3 + local fs_type + local mount_point + + read -r device fs_type mount_point <<< "$(get_device_fstype "${full_path}")" + + # Remove mount_point from full_path if it starts with mount_point + if [[ "${mount_point}" != "/" && "$full_path" == "$mount_point"* ]]; then + full_path="${full_path/#${mount_point}/}" + fi + + if [ -z "${outputfile}" ]; then + debugfs -R "cat \"${full_path}\"" "${device}" + else + debugfs -R "dump \"${full_path}\" \"${outputfile}\"" "${device}" + fi +} + + +file="/etc/ld.so.preload" +verbose_mode=0 +while getopts "f:ho:v" opts; do + case ${opts} in + f) file=${OPTARG} + ;; + h) usage + ;; + o) outputfile=${OPTARG} + ;; + v) verbose_mode=1 + ;; + *) usage + ;; + esac +done + +# Which device has /etc/ld.so.preload? +file=$(realpath -s "${file}") +read -r device fs_type mount_point <<< "$(get_device_fstype "${file}")" + +# Check filesystem type and dump /etc/ld.so.preload +if [ "${fs_type}" = "xfs" ]; then + dump_xfs_ldsopreload "${file}" "${outputfile:-}" "${device}" +elif [[ "${fs_type}" =~ ^ext ]]; then + dump_ext_ldsopreload "${file}" "${outputfile:-}" "${device}" +else + print_msg "${file} is not on XFS or EXT filesystem." + exit 2 +fi diff --git a/profiles/full.yaml b/profiles/full.yaml index 49c02a3c..5a517dc0 100644 --- a/profiles/full.yaml +++ b/profiles/full.yaml @@ -24,7 +24,7 @@ artifacts: - live_response/storage/* - live_response/containers/* - live_response/vms/* - - chkrootkit/chkrootkit.yaml + - chkrootkit/* - hash_executables/hash_executables.yaml - files/* \ No newline at end of file diff --git a/profiles/ir_triage.yaml b/profiles/ir_triage.yaml index c1fdecbd..a22eee4a 100644 --- a/profiles/ir_triage.yaml +++ b/profiles/ir_triage.yaml @@ -24,7 +24,7 @@ artifacts: - live_response/storage/* - live_response/containers/* - live_response/vms/* - - chkrootkit/chkrootkit.yaml + - chkrootkit/* - hash_executables/hash_executables.yaml - files/applications/git.yaml - files/applications/lesshst.yaml diff --git a/profiles/offline.yaml b/profiles/offline.yaml index 710323f1..0451b770 100644 --- a/profiles/offline.yaml +++ b/profiles/offline.yaml @@ -2,6 +2,6 @@ name: offline description: Offline artifacts collection. artifacts: - bodyfile/bodyfile.yaml - - chkrootkit/chkrootkit.yaml + - chkrootkit/* - hash_executables/hash_executables.yaml - files/* diff --git a/profiles/offline_ir_triage.yaml b/profiles/offline_ir_triage.yaml index eba41292..e45c4c11 100644 --- a/profiles/offline_ir_triage.yaml +++ b/profiles/offline_ir_triage.yaml @@ -2,7 +2,7 @@ name: offline_ir_triage description: Offline incident response triage collection. artifacts: - bodyfile/bodyfile.yaml - - chkrootkit/chkrootkit.yaml + - chkrootkit/* - hash_executables/hash_executables.yaml - files/applications/git.yaml - files/applications/lesshst.yaml