Adding jphide analyzer

This commit is contained in:
Zeecka
2026-01-13 22:23:37 +01:00
parent 3d19647041
commit 9b7c205f9e
8 changed files with 111 additions and 9 deletions
+10 -4
View File
@@ -251,10 +251,16 @@ RUN apt-get update && apt-get install -y \
Or for tools requiring compilation:
```dockerfile
RUN git clone https://github.com/tool/repo.git /tmp/tool && \
cd /tmp/tool && \
make && make install && \
rm -rf /tmp/tool
# Builder stage example (jphide)
RUN git clone https://github.com/h3xx/jphs.git /tmp/jphs && \
cd /tmp/jphs && \
make && \
cp jphide jpseek /usr/local/bin/ && \
rm -rf /tmp/jphs
# Runtime stage - copy compiled binaries
COPY --from=builder /usr/local/bin/jphide /usr/local/bin/jphide
COPY --from=builder /usr/local/bin/jpseek /usr/local/bin/jpseek
```
> [!NOTE]
+19 -1
View File
@@ -5,13 +5,26 @@ FROM python:3.14-slim AS builder
# Install wget only to download OpenStego
RUN apt-get update && apt-get install -y --no-install-recommends \
wget \
wget git build-essential \
&& rm -rf /var/lib/apt/lists/*
# Download OpenStego
RUN wget https://github.com/syvaidya/openstego/releases/download/openstego-0.8.6/openstego_0.8.6-1_all.deb \
-O /tmp/openstego.deb
# Builder stage - add after other tool compilations
RUN git clone https://github.com/h3xx/jphs.git /tmp/jphs
RUN cd /tmp/jphs/jpeg-8a && ./configure && make
RUN cd /tmp/jphs && sed -i 's/open(seekfilename,O_WRONLY|O_TRUNC|O_CREAT)/open(seekfilename,O_WRONLY|O_TRUNC|O_CREAT, 0644)/' jpseek.c
RUN cd /tmp/jphs \
&& sed -i 's/^LIBS = .*/JPEG_OBJS = $(filter-out jpeg-8a\/cjpeg.o jpeg-8a\/djpeg.o jpeg-8a\/jpegtran.o jpeg-8a\/rdjpgcom.o jpeg-8a\/wrjpgcom.o,$(wildcard jpeg-8a\/\*.o))/' Makefile \
&& sed -i 's/^LDFLAGS = .*/LDFLAGS =/' Makefile \
&& sed -i 's/^jphide: \(.*\)$/jphide: \1 $(JPEG_OBJS)/' Makefile \
&& sed -i 's/^jpseek: \(.*\)$/jpseek: \1 $(JPEG_OBJS)/' Makefile
RUN cd /tmp/jphs && make all
RUN cd /tmp/jphs && cp jphide jpseek /usr/local/bin/
RUN rm -rf /tmp/jphs
# ==========================
# Stage 2 : Image runtime minimale
# ==========================
@@ -21,6 +34,7 @@ WORKDIR /app
# Install runtime + stego tools
RUN apt-get update && apt-get install -y --no-install-recommends \
expect \
default-jre \
file \
ruby \
@@ -48,5 +62,9 @@ COPY aperisolve/ /app/aperisolve/
# Install Python dependencies
RUN pip install --no-cache-dir -r /app/aperisolve/requirements.txt
# Copy jphide binaries
COPY --from=builder /usr/local/bin/jphide /usr/local/bin/jphide
COPY --from=builder /usr/local/bin/jpseek /usr/local/bin/jpseek
# Commande de lancement
CMD ["gunicorn", "-w", "8", "-b", "0.0.0.0:5000", "--access-logfile", "-", "--error-logfile", "-", "--log-level", "info", "--capture-output", "aperisolve.utils.wsgi:application"]
+2 -1
View File
@@ -45,7 +45,8 @@ Aperi'Solve is an open-source steganalysis web platform that performs automated
- [steghide](https://steghide.sourceforge.net/) (extraction with password)
- [strings](https://pubs.opengroup.org/onlinepubs/9799919799/utilities/strings.html)
- [zsteg](https://github.com/zed-0xff/zsteg) (LSB text/data extraction)
- [file](https://www.darwinsys.com/file/) (MIME type and format detection)
- [file](https://manned.org/file.1) (MIME type and format detection)
- [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.
+74
View File
@@ -0,0 +1,74 @@
# flake8: noqa: E203,E501,W503
# pylint: disable=C0413,W0718,R0903,R0801
# mypy: disable-error-code=unused-awaitable
"""JPSeek Analyzer for Image Submissions."""
from pathlib import Path
from typing import Optional
from .base_analyzer import SubprocessAnalyzer
class JpseekAnalyzer(SubprocessAnalyzer):
"""Analyzer for jpseek."""
def __init__(self, input_img: Path, output_dir: Path) -> None:
super().__init__("jpseek", input_img, output_dir, has_archive=True)
def build_cmd(self, password: Optional[str] = None) -> list[str]:
extracted_dir = self.get_extracted_dir()
assert extracted_dir is not None # since has_archive is True
out = str(extracted_dir / "jpseek.out")
jpseek_cmd = f"jpseek {self.img} {out}"
if password is None:
password = ""
expect_cmd = (
f"expect -c 'spawn {jpseek_cmd}; "
f"expect \"Passphrase:\"; "
f"send \"{password}\\r\"; "
f"expect eof'"
)
command = ["bash", "-c", expect_cmd]
print("Built command:", command)
return command
def _remove_output_artifacts(self, output: str) -> str:
extracted_dir = self.get_extracted_dir()
out_file = str(extracted_dir / "jpseek.out")
o = output
o = o.replace("jpseek, version 0.3 (c) 1998 Allan Latham <alatham@flexsys-group.com>", "")
o = o.replace("This is licenced software but no charge is made for its use.", "")
o = o.replace("NO WARRANTY whatsoever is offered with this product.", "")
o = o.replace("NO LIABILITY whatsoever is accepted for its use.", "")
o = o.replace("You are using this entirely at your OWN RISK.", "")
o = o.replace("See the GNU Public Licence for full details.", "")
o = o.replace("Passphrase:", "")
o = o.replace(f"spawn jpseek {self.img} {out_file}", "")
return o.strip()
def is_error(self, returncode: int, stdout: str, stderr: str, zip_exist: bool) -> bool:
"""Check if the result is an error."""
return bool(returncode) and "File not completely recovered" not in "".join([stdout, stderr])
def process_error(self, stdout: str, stderr: str) -> str:
"""Process stderr."""
output = stdout + stderr
output = self._remove_output_artifacts(output)
return output
def process_output(self, stdout: str, stderr: str) -> str:
"""Process stdout."""
output = stdout + stderr
output = self._remove_output_artifacts(output)
if output == "":
output = "File completely recovered."
return output
def analyze_jpseek(input_img: Path, output_dir: Path, password: Optional[str] = None) -> None:
"""Analyze an image submission using jpseek."""
analyzer = JpseekAnalyzer(input_img, output_dir)
if password:
analyzer.analyze(password)
else:
analyzer.analyze()
+1 -1
View File
@@ -7,7 +7,7 @@ from os import getenv
from pathlib import Path
IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tiff"]
WORKER_FILES = ["binwalk", "foremost", "steghide", "zsteg", "openstego", "pcrt"]
WORKER_FILES = ["binwalk", "foremost", "steghide", "zsteg", "openstego", "pcrt", "jpseek"]
RESULT_FOLDER = Path(__file__).parent.resolve() / "results"
REMOVED_IMAGES_FOLDER = Path(__file__).parent.resolve() / "removed_images"
+1
View File
@@ -8,6 +8,7 @@ const TOOL_ORDER = [
"pngcheck",
"pcrt",
"steghide",
"jpseek",
"openstego",
"zsteg",
"strings"
+2
View File
@@ -18,6 +18,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.jpseek import analyze_jpseek
from .analyzers.openstego import analyze_openstego
from .analyzers.outguess import analyze_outguess
from .analyzers.pcrt import analyze_pcrt
@@ -106,6 +107,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_jpseek, img_path, result_path, submission.password),
(analyze_openstego, img_path, result_path, submission.password),
(analyze_pngcheck, img_path, result_path),
(analyze_pcrt, img_path, result_path),
+2 -2
View File
@@ -5,7 +5,7 @@ This guide shows you how to add a new image analysis tool to the system.
## Quick Start
1. Create a new file in `analyzers/` from template file `template_analyzer.py`.
2. Register it in `workers.py`, `config.py` and `aperisolve.js`.
2. Register it in `workers.py`, `config.py` (if it extracts files) and `aperisolve.js`.
## Basic Template
@@ -24,7 +24,7 @@ def analyze_mytool(input_img: Path, output_dir: Path) -> None:
analyzer.analyze()
```
You can also see `template_analyzer.py`.
You can also see `template_analyzer.py` or check real examples like `jpseek.py`, `steghide.py`, or `strings.py`.
## Common Scenarios