Integrate OpenStego and close #24
This commit is contained in:
+19
-5
@@ -2,20 +2,34 @@ FROM python:3.14-slim AS build
|
|||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
||||||
# Install dependencies
|
# Install build tools and dependencies
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
|
wget \
|
||||||
|
default-jre \
|
||||||
|
ruby \
|
||||||
zip \
|
zip \
|
||||||
7zip \
|
7zip
|
||||||
|
|
||||||
|
# Install steganography and forensics tools
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
binwalk \
|
binwalk \
|
||||||
foremost \
|
foremost \
|
||||||
exiftool \
|
exiftool \
|
||||||
steghide \
|
steghide \
|
||||||
ruby \
|
|
||||||
binutils \
|
binutils \
|
||||||
outguess \
|
outguess \
|
||||||
pngcheck \
|
pngcheck \
|
||||||
&& gem install zsteg \
|
&& gem install zsteg
|
||||||
&& apt-get clean
|
|
||||||
|
# Install 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 && \
|
||||||
|
dpkg -i /tmp/openstego.deb && \
|
||||||
|
rm /tmp/openstego.deb
|
||||||
|
|
||||||
|
# Clean up apt cache and remove useless packages
|
||||||
|
RUN apt-get remove -y wget && \
|
||||||
|
apt-get clean && \
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY aperisolve/ /aperisolve/
|
COPY aperisolve/ /aperisolve/
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ Table of contents
|
|||||||
- zsteg (LSB text/data extraction)
|
- zsteg (LSB text/data extraction)
|
||||||
- steghide (extraction with password)
|
- steghide (extraction with password)
|
||||||
- outguess (extraction with password)
|
- outguess (extraction with password)
|
||||||
|
- openstego (extraction with password)
|
||||||
- exiftool (metadata and geolocation)
|
- exiftool (metadata and geolocation)
|
||||||
- binwalk (embedded archives)
|
- binwalk (embedded archives)
|
||||||
- foremost (carved files)
|
- foremost (carved files)
|
||||||
@@ -197,4 +198,4 @@ CI will run these checks on each PR.
|
|||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
Acknowledgements:
|
Acknowledgements:
|
||||||
- Thanks to contributors and the open-source community for the tools integrated (zsteg, steghide, binwalk, foremost, exiftool).
|
- Thanks to contributors and the open-source community for the tools integrated (zsteg, steghide, openstego, binwalk, foremost, exiftool, ...).
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
"""OpenStego Analyzer for Image Submissions."""
|
||||||
|
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from .utils import MAX_PENDING_TIME, update_data
|
||||||
|
|
||||||
|
|
||||||
|
def analyze_openstego(input_img: Path, output_dir: Path, password: str = "") -> None:
|
||||||
|
"""Analyze an image submission using openstego."""
|
||||||
|
|
||||||
|
image_name = input_img.name
|
||||||
|
try:
|
||||||
|
stderr = ""
|
||||||
|
# Try to extract the embedded file
|
||||||
|
extracted_dir = output_dir / "openstego"
|
||||||
|
extracted_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for algo in ["AES128", "AES256"]:
|
||||||
|
cmd: list[str] = [
|
||||||
|
"openstego",
|
||||||
|
"extract",
|
||||||
|
"-a",
|
||||||
|
"randomlsb",
|
||||||
|
"--cryptalgo",
|
||||||
|
algo,
|
||||||
|
"-sf",
|
||||||
|
"../" + str(image_name),
|
||||||
|
"-xd",
|
||||||
|
str(extracted_dir),
|
||||||
|
"-p",
|
||||||
|
password,
|
||||||
|
]
|
||||||
|
|
||||||
|
data = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=output_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
timeout=MAX_PENDING_TIME,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Check if extraction was successful
|
||||||
|
if data.returncode != 0:
|
||||||
|
stderr += data.stderr.replace(
|
||||||
|
f'"../{image_name}" ', ""
|
||||||
|
) # hide file name
|
||||||
|
err = {
|
||||||
|
"openstego": {
|
||||||
|
"status": "error",
|
||||||
|
"error": stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_data(output_dir, err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Find the extracted file
|
||||||
|
extracted_files = list(extracted_dir.glob("*"))
|
||||||
|
if not extracted_files:
|
||||||
|
err = {
|
||||||
|
"openstego": {
|
||||||
|
"status": "error",
|
||||||
|
"error": "No file extracted, password may be incorrect.",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_data(output_dir, err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Filter stdout for success messages
|
||||||
|
stdout = []
|
||||||
|
for f in extracted_files:
|
||||||
|
stdout.append(f"Recovered file: {f.name}")
|
||||||
|
|
||||||
|
# Zip extracted files
|
||||||
|
zip_data = subprocess.run(
|
||||||
|
["7z", "a", "../openstego.7z", "*"],
|
||||||
|
cwd=extracted_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
timeout=MAX_PENDING_TIME,
|
||||||
|
)
|
||||||
|
|
||||||
|
stderr += zip_data.stderr
|
||||||
|
|
||||||
|
if len(stderr) > 0:
|
||||||
|
err = {
|
||||||
|
"openstego": {
|
||||||
|
"status": "error",
|
||||||
|
"error": stderr,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update_data(output_dir, err)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Remove the extracted directory
|
||||||
|
shutil.rmtree(extracted_dir)
|
||||||
|
|
||||||
|
update_data(
|
||||||
|
output_dir,
|
||||||
|
{
|
||||||
|
"openstego": {
|
||||||
|
"status": "ok",
|
||||||
|
"output": stdout,
|
||||||
|
"download": f"/download/{output_dir.name}/openstego",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
update_data(output_dir, {"openstego": {"status": "error", "error": str(e)}})
|
||||||
|
return None
|
||||||
@@ -3,6 +3,6 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tiff"]
|
IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp", ".tiff"]
|
||||||
WORKER_FILES = ["binwalk", "foremost", "steghide", "zsteg", "image_resize"]
|
WORKER_FILES = ["binwalk", "foremost", "steghide", "zsteg", "image_resize", "openstego"]
|
||||||
|
|
||||||
RESULT_FOLDER = Path(__file__).parent.resolve() / "results"
|
RESULT_FOLDER = Path(__file__).parent.resolve() / "results"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ const TOOL_ORDER = [
|
|||||||
"outguess",
|
"outguess",
|
||||||
"pngcheck",
|
"pngcheck",
|
||||||
"steghide",
|
"steghide",
|
||||||
|
"openstego",
|
||||||
"zsteg",
|
"zsteg",
|
||||||
"strings",
|
"strings",
|
||||||
"image_resize",
|
"image_resize",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .analyzers.decomposer import analyze_decomposer
|
|||||||
from .analyzers.exiftool import analyze_exiftool
|
from .analyzers.exiftool import analyze_exiftool
|
||||||
from .analyzers.foremost import analyze_foremost
|
from .analyzers.foremost import analyze_foremost
|
||||||
from .analyzers.image_resize import analyze_image_resize
|
from .analyzers.image_resize import analyze_image_resize
|
||||||
|
from .analyzers.openstego import analyze_openstego
|
||||||
from .analyzers.outguess import analyze_outguess
|
from .analyzers.outguess import analyze_outguess
|
||||||
from .analyzers.pngcheck import analyze_pngcheck
|
from .analyzers.pngcheck import analyze_pngcheck
|
||||||
from .analyzers.steghide import analyze_steghide
|
from .analyzers.steghide import analyze_steghide
|
||||||
@@ -77,6 +78,7 @@ def analyze_image(submission_hash: str) -> None:
|
|||||||
(analyze_strings, img_path, result_path),
|
(analyze_strings, img_path, result_path),
|
||||||
(analyze_pngcheck, img_path, result_path),
|
(analyze_pngcheck, img_path, result_path),
|
||||||
(analyze_steghide, img_path, result_path, submission.password),
|
(analyze_steghide, img_path, result_path, submission.password),
|
||||||
|
(analyze_openstego, img_path, result_path, submission.password),
|
||||||
(analyze_zsteg, img_path, result_path),
|
(analyze_zsteg, img_path, result_path),
|
||||||
(analyze_image_resize, img_path, result_path),
|
(analyze_image_resize, img_path, result_path),
|
||||||
]
|
]
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 62 KiB |
Reference in New Issue
Block a user