Merge pull request #173 from Zeecka/worker/jphide
Adding jphide analyzer
This commit is contained in:
+10
-4
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -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"
|
||||
|
||||
@@ -8,6 +8,7 @@ const TOOL_ORDER = [
|
||||
"pngcheck",
|
||||
"pcrt",
|
||||
"steghide",
|
||||
"jpseek",
|
||||
"openstego",
|
||||
"zsteg",
|
||||
"strings"
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user