diff --git a/Dockerfile b/Dockerfile index 3e28278..fb0ce36 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,13 +5,19 @@ 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 git build-essential \ + wget git build-essential golang-go \ && 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 +# Clone and build jsteg from source +RUN git clone https://github.com/lukechampine/jsteg.git /tmp/jsteg +RUN cd /tmp/jsteg && \ + go build -o /usr/local/bin/jsteg ./cmd/jsteg && \ + rm -rf /tmp/jsteg + # 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 @@ -64,9 +70,10 @@ COPY aperisolve/ /app/aperisolve/ # Install Python dependencies RUN pip install --no-cache-dir -r /app/aperisolve/requirements.txt -# Copy jphide binaries +# Copy jphide and jsteg binaries COPY --from=builder /usr/local/bin/jphide /usr/local/bin/jphide COPY --from=builder /usr/local/bin/jpseek /usr/local/bin/jpseek +COPY --from=builder /usr/local/bin/jsteg /usr/local/bin/jsteg # 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"] diff --git a/aperisolve/analyzers/jsteg.py b/aperisolve/analyzers/jsteg.py new file mode 100644 index 0000000..98eb1ac --- /dev/null +++ b/aperisolve/analyzers/jsteg.py @@ -0,0 +1,28 @@ +# flake8: noqa: E203,E501,W503 +# pylint: disable=C0413,W0718,R0903,R0801 +# mypy: disable-error-code=unused-awaitable +"""Jsteg Analyzer for Image Submissions.""" + +from pathlib import Path + +from .base_analyzer import SubprocessAnalyzer + + +class JstegAnalyzer(SubprocessAnalyzer): + """Analyzer for jsteg.""" + + def __init__(self, input_img: Path, output_dir: Path) -> None: + super().__init__("jsteg", input_img, output_dir) + self.cmd = ["jsteg", "reveal", self.img] + + def process_output(self, stdout: str, stderr: str) -> str | list[str]: + """Process the stdout into lines.""" + if stdout.strip(): + return [line for line in stdout.split("\n") if line.strip()] + return [] + + +def analyze_jsteg(input_img: Path, output_dir: Path) -> None: + """Analyze an image submission using jsteg.""" + analyzer = JstegAnalyzer(input_img, output_dir) + analyzer.analyze() diff --git a/aperisolve/static/js/aperisolve.js b/aperisolve/static/js/aperisolve.js index 5de1822..17c88bc 100644 --- a/aperisolve/static/js/aperisolve.js +++ b/aperisolve/static/js/aperisolve.js @@ -11,6 +11,7 @@ const TOOL_ORDER = [ "identify", "steghide", "jpseek", + "jsteg", "openstego", "zsteg", "strings" diff --git a/aperisolve/workers.py b/aperisolve/workers.py index 8d525a9..98c6b5a 100644 --- a/aperisolve/workers.py +++ b/aperisolve/workers.py @@ -21,6 +21,7 @@ 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.jsteg import analyze_jsteg from .analyzers.openstego import analyze_openstego from .analyzers.outguess import analyze_outguess from .analyzers.pcrt import analyze_pcrt @@ -112,6 +113,7 @@ def analyze_image(submission_hash: str) -> None: (analyze_foremost, img_path, result_path), (analyze_identify, img_path, result_path), (analyze_jpseek, img_path, result_path, submission.password), + (analyze_jsteg, img_path, result_path), (analyze_openstego, img_path, result_path, submission.password), (analyze_pngcheck, img_path, result_path), (analyze_pcrt, img_path, result_path),