diff --git a/Dockerfile b/Dockerfile index 2d8fc39..3e28278 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,6 +47,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ binutils \ outguess \ pngcheck \ + graphicsmagick \ + graphicsmagick-imagemagick-compat \ && gem install zsteg \ && rm -rf /var/lib/apt/lists/* diff --git a/README.md b/README.md index 2a4e757..8d3eaf0 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,11 @@ Aperi'Solve is an open-source steganalysis web platform that performs automated ## Key features - Visualize each bit layer (LSB and other layers) per image channel (R/G/B/Alpha). -- Browse and download each bit-layer image. +- Color remapping (random palette remaps with 8 generated variants) - Integrates and displays outputs from: - [binwalk](https://github.com/ReFirmLabs/binwalk) (embedded archives) - [exiftool](https://exiftool.org/) (metadata and geolocation) + - [GraphicsMagick identify](http://www.graphicsmagick.org/identify.html) (image info & properties) - [foremost](https://foremost.sourceforge.net/) (carved files) - [openstego](https://www.openstego.com/) (extraction with password) - [outguess](https://www.rbcafe.com/software/outguess/) (extraction with password) @@ -49,6 +50,7 @@ Aperi'Solve is an open-source steganalysis web platform that performs automated - [jpseek](https://github.com/h3xx/jphs) (JPEG steganography detection and extraction) - Worker queue architecture for offloading heavy/slow analyzers (Redis + background workers). - Results stored for later browsing and download. +- Browse and download each generated images. ## Quick start (Docker) @@ -99,4 +101,4 @@ Thanks to contributors: - [Philip Zimmermann](https://github.com/Philip-Zimmermann) Thanks to the open-source community: -[binwalk](https://github.com/ReFirmLabs/binwalk), [exiftool](https://exiftool.org/), [foremost](https://foremost.sourceforge.net/), [openstego](https://www.openstego.com/), [outguess](https://www.rbcafe.com/software/outguess/), [pngcheck](https://www.libpng.org/pub/png/apps/pngcheck.html), [steghide](https://steghide.sourceforge.net/), [strings](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/strings.html), [zsteg](https://github.com/zed-0xff/zsteg), ... +[binwalk](https://github.com/ReFirmLabs/binwalk), [exiftool](https://exiftool.org/), [GraphicsMagick identify](http://www.graphicsmagick.org/identify.html), [foremost](https://foremost.sourceforge.net/), [openstego](https://www.openstego.com/), [outguess](https://www.rbcafe.com/software/outguess/), [pngcheck](https://www.libpng.org/pub/png/apps/pngcheck.html), [steghide](https://steghide.sourceforge.net/), [strings](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/strings.html), [zsteg](https://github.com/zed-0xff/zsteg), ... diff --git a/aperisolve/analyzers/color_remapping.py b/aperisolve/analyzers/color_remapping.py index 365d438..802e480 100644 --- a/aperisolve/analyzers/color_remapping.py +++ b/aperisolve/analyzers/color_remapping.py @@ -56,7 +56,7 @@ class ColorRemappingAnalyzer(SubprocessAnalyzer): return remapped_img - def get_results(self, _: Optional[str] = None) -> dict[str, Any]: + def get_results(self, password: Optional[str] = None) -> dict[str, Any]: """Analyze an image submission using color remapping.""" img = Image.open(self.input_img) converted = False @@ -65,8 +65,8 @@ class ColorRemappingAnalyzer(SubprocessAnalyzer): img = img.convert("RGB") converted = True - img_np = np.array(img) - img_np, _ = self._normalize_image(img_np) + img_np_tmp = np.array(img) + img_np, _channels = self._normalize_image(img_np_tmp) image_json = [] diff --git a/aperisolve/analyzers/identify.py b/aperisolve/analyzers/identify.py new file mode 100644 index 0000000..005e379 --- /dev/null +++ b/aperisolve/analyzers/identify.py @@ -0,0 +1,26 @@ +# flake8: noqa: E203,E501,W503 +# pylint: disable=C0413,W0718,R0903,R0801 +# mypy: disable-error-code=unused-awaitable +"""Identify Analyzer for Image Submissions.""" + +from pathlib import Path + +from .base_analyzer import SubprocessAnalyzer + + +class IdentifyAnalyzer(SubprocessAnalyzer): + """Analyzer for GraphicMagick identify command.""" + + def __init__(self, input_img: Path, output_dir: Path) -> None: + super().__init__("identify", input_img, output_dir) + self.cmd = ["identify", "-verbose", self.img] + + def process_output(self, stdout: str, stderr: str) -> str | list[str]: + """Process the stdout into a list of lines.""" + return [line for line in stdout.split("\n") if line.strip()] + + +def analyze_identify(input_img: Path, output_dir: Path) -> None: + """Analyze an image submission using GraphicMagick identify.""" + analyzer = IdentifyAnalyzer(input_img, output_dir) + analyzer.analyze() diff --git a/aperisolve/static/js/aperisolve.js b/aperisolve/static/js/aperisolve.js index 0f2afd3..5de1822 100644 --- a/aperisolve/static/js/aperisolve.js +++ b/aperisolve/static/js/aperisolve.js @@ -8,6 +8,7 @@ const TOOL_ORDER = [ "outguess", "pngcheck", "pcrt", + "identify", "steghide", "jpseek", "openstego", diff --git a/aperisolve/workers.py b/aperisolve/workers.py index b5dc3aa..8d525a9 100644 --- a/aperisolve/workers.py +++ b/aperisolve/workers.py @@ -19,6 +19,7 @@ from .analyzers.decomposer import analyze_decomposer from .analyzers.exiftool import analyze_exiftool from .analyzers.file import analyze_file from .analyzers.foremost import analyze_foremost +from .analyzers.identify import analyze_identify from .analyzers.jpseek import analyze_jpseek from .analyzers.openstego import analyze_openstego from .analyzers.outguess import analyze_outguess @@ -109,6 +110,7 @@ def analyze_image(submission_hash: str) -> None: (analyze_exiftool, img_path, result_path), (analyze_file, img_path, result_path), (analyze_foremost, img_path, result_path), + (analyze_identify, img_path, result_path), (analyze_jpseek, img_path, result_path, submission.password), (analyze_openstego, img_path, result_path, submission.password), (analyze_pngcheck, img_path, result_path),