diff --git a/.github/workflows/security_hook_linux.yml b/.github/workflows/security_hook_linux.yml new file mode 100644 index 0000000..9ee75d5 --- /dev/null +++ b/.github/workflows/security_hook_linux.yml @@ -0,0 +1,104 @@ +name: Security Hook on Linux + +on: [pull_request] + +jobs: + capture: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Update cache + run: sudo apt-get update + - name: Install system dependencies + run: | + sudo apt-get install -qq libguestfs0 libguestfs-dev \ + libguestfs-tools pkg-config libvirt-dev libvirt-daemon-system + - name: Allow user to read the kernel for supermin (libguestfs) + run: sudo chmod 644 /boot/vmlinuz-* + - name: Start libvirt + run: sudo systemctl restart libvirtd + - name: Define oswatcher pool in qemu:///session + run: | + virsh -c qemu:///session pool-define-as oswatcher --type dir --target $HOME/images + virsh -c qemu:///session pool-build oswatcher + virsh -c qemu:///session pool-start oswatcher + - name: Set up Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: '3.7' + - name: Install OSWatcher pip dependencies + run: | + python -m pip install --upgrade pip + pip install . + pip install lxml + pip install http://download.libguestfs.org/python/guestfs-1.40.2.tar.gz + - name: Download import_libvirt script + run: | + wget 'https://raw.githubusercontent.com/Wenzel/packer-templates/474ea0fa9c540303c8655b521424a8860872b13d/import_libvirt.py' + chmod +x import_libvirt.py + - name: Download template_domain XML + run: wget 'https://raw.githubusercontent.com/Wenzel/packer-templates/474ea0fa9c540303c8655b521424a8860872b13d/template_domain.xml' + - name: Download ubuntu-6.06-server image + run: wget 'https://www.dropbox.com/s/vv8di5zd2sqhv3k/ubuntu-6.06-server.qcow2?dl=1' -O $HOME/ubuntu-6.06-server.qcow2 + - name: Import ubuntu-6.06-server in Libvirt + run: python import_libvirt.py --uri qemu:///session --domain-type qemu --open-vnc --pool oswatcher --pool-path $HOME/images $HOME/ubuntu-6.06-server.qcow2 + - name: Refresh oswatcher pool + run: virsh -c qemu:///session pool-refresh oswatcher + - name: Configure hooks.json + run: | + cat << EOF > hooks.json + { + "configuration": + { + "neo4j": { + "enabled": true, + "delete": false, + "replace": false + }, + "desktop_ready_delay": 0 + }, + "hooks": + [ + { + "name": "hooks.filesystem.LibguestfsHook" + }, + { + "name": "hooks.filesystem.FilesystemHook", + "configuration": + { + "enumerate": true, + "log_progress": true, + "log_progress_delay": 10 + } + }, + { + "name": "hooks.filesystem.Neo4jFilesystemHook" + }, + { + "name": "hooks.security.SecurityHook", + "configuration": { + "keep_failed_binaries": true, + "keep_failed_dir": "$HOME/checksec_failed" + } + } + ] + } + EOF + - name: display hooks.json + run: cat hooks.json + - name: Update path to include checksec + run: echo "::add-path::$GITHUB_WORKSPACE/tools/checksec" + - name: Capture ubuntu-6.06-server filesystem + run: oswatcher -c qemu:///session ubuntu-6.06-server hooks.json -d + + services: + neo4j: + image: neo4j:3.4 + ports: + - 7687:7687 + env: + NEO4J_AUTH: "neo4j/admin" diff --git a/.github/workflows/security_hook.yml b/.github/workflows/security_hook_windows.yml similarity index 99% rename from .github/workflows/security_hook.yml rename to .github/workflows/security_hook_windows.yml index a605674..54becfb 100644 --- a/.github/workflows/security_hook.yml +++ b/.github/workflows/security_hook_windows.yml @@ -1,4 +1,4 @@ -name: Security Hook +name: Security Hook on Windows on: [pull_request] diff --git a/hooks/filesystem.py b/hooks/filesystem.py index 0cbc05a..ad34089 100644 --- a/hooks/filesystem.py +++ b/hooks/filesystem.py @@ -6,6 +6,7 @@ import time from collections import Counter from pathlib import Path +from typing import Optional import guestfs import magic @@ -49,7 +50,7 @@ def close(self): @property @functools.lru_cache() - def str_path(self): + def str_path(self) -> str: return str(self.path) @property @@ -94,14 +95,14 @@ def inode_type_value(self): @property @functools.lru_cache() - def local_file(self): + def local_file(self) -> str: STATS['local_file'] += 1 self._tmp_local_file = TEMPFILE.NamedTemporaryFile() self._gfs.download(self.str_path, self._tmp_local_file.name) return self._tmp_local_file.name @functools.lru_cache() - def filecmd_output(self, mime_option=False): + def filecmd_output(self, mime_option=False) -> Optional[str]: """Run the file utility and returns the output""" if not self.inode_type == InodeType.REG: return None @@ -113,7 +114,7 @@ def filecmd_output(self, mime_option=False): @property @functools.lru_cache() - def gfs_file(self): + def gfs_file(self) -> Optional[str]: if not self.inode_type == InodeType.REG: return None STATS['guestfs_file'] += 1 @@ -121,7 +122,7 @@ def gfs_file(self): @property @functools.lru_cache() - def file_magic_type(self): + def file_magic_type(self) -> Optional[str]: """this method is faster than py_magic_type, since it doesn't involve downloading the whole file to the host""" STATS['file_magic_type'] += 1 @@ -139,7 +140,7 @@ def file_magic_type(self): @property @functools.lru_cache() - def py_magic_type(self): + def py_magic_type(self) -> Optional[str]: if not self.inode_type == InodeType.REG: return None STATS['py_magic_type'] += 1 diff --git a/hooks/security.py b/hooks/security.py index 1cfa44f..31091a4 100644 --- a/hooks/security.py +++ b/hooks/security.py @@ -66,7 +66,7 @@ def __init__(self, parameters): else: os_id = self.context.domain.name() default_checksec_failed_dir = Path.cwd() / f"{os_id}_checksec_failed" - self.keep_binaries_dir = self.configuration.get('keep_failed_dir', default_checksec_failed_dir) + self.keep_binaries_dir = Path(self.configuration.get('keep_failed_dir', default_checksec_failed_dir)) self.context.subscribe('detected_os_info', self.get_os_info) self.context.subscribe('filesystem_new_file', self.check_file) @@ -82,6 +82,8 @@ def check_file(self, event): # checksec only supports ELF files return mime = inode.file_magic_type + if not mime: + return filepath = inode.path if re.match(r'application/x(-pie)?-(executable|sharedlib)', mime): self.logger.info('Checking security of %s: %s', filepath, mime)