# SoulSearching — SoulEyez's custom all-in-one vulnerable box.
#
# Phase 0: SSH.  Phase 1: + a SQL-injectable web app (Apache/PHP/MariaDB) that
# drives the headline chain  katana/web_fuzzer -> SQLMap --dump -> hashcat ->
# web login test.  Phase 2: + the rest-of-web doors (RCE, upload, discovery,
# leaks).  Phase 3: + service doors (FTP/SMB + network MySQL) with credential
# reuse — the cracked web creds also log in over SSH/FTP/SMB.  Phase 4: + a
# named-CVE door (Apache httpd 2.4.49, CVE-2021-41773 path traversal -> RCE) on
# :8081.  A later phase adds the cloud pivot — see docs/team/EPIC_SOULEYEZ_TARGET.md.
#
# Base is a modern, multi-arch Debian slim so it builds and runs natively on
# both amd64 and arm64 (no qemu emulation, unlike the legacy meta2/meta3 boxes).
#
# ⚠️  Lab use only — intentionally weak credentials and deliberately vulnerable
#     web code. Runs on the isolated souleyez-lab bridge; never expose it.

# ── Builder stage: compile Apache httpd 2.4.49 (the CVE-2021-41773 door) ─────
# Built from source so it's native on amd64/arm64 (no prebuilt/qemu image). The
# compilers stay in this throwaway stage; only the finished install is copied
# into the final image, keeping it lean.
FROM debian:12-slim AS httpd-vuln-build
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        build-essential wget ca-certificates \
        libapr1-dev libaprutil1-dev libpcre3-dev \
    && rm -rf /var/lib/apt/lists/*
RUN wget -qO /tmp/httpd.tar.gz \
        https://archive.apache.org/dist/httpd/httpd-2.4.49.tar.gz \
    && mkdir -p /tmp/httpd \
    && tar xzf /tmp/httpd.tar.gz -C /tmp/httpd --strip-components=1 \
    && cd /tmp/httpd \
    && ./configure --prefix=/opt/httpd-vuln --with-mpm=event --enable-cgid \
         --with-apr=/usr/bin/apr-1-config --with-apr-util=/usr/bin/apu-1-config \
    && make -j"$(nproc)" && make install \
    # Turn the stock config into the vulnerable one:
    #  - listen on 8081 (the packaged apache2 owns :80)
    #  - loosen the filesystem-root <Directory /> from "denied" to "granted"
    #    (this is exactly the misconfig CVE-2021-41773 needs, together with the
    #    default ScriptAlias /cgi-bin/ + mod_cgid, to reach /bin/sh).
    && sed -i 's/^Listen 80$/Listen 8081/' /opt/httpd-vuln/conf/httpd.conf \
    && sed -i '/^<Directory \/>/,/^<\/Directory>/ s/Require all denied/Require all granted/' \
        /opt/httpd-vuln/conf/httpd.conf \
    # Load mod_cgid so the traversal to /bin/sh EXECUTES (RCE), not just reads.
    && sed -i 's/^#LoadModule cgid_module/LoadModule cgid_module/' \
        /opt/httpd-vuln/conf/httpd.conf \
    && echo 'ServerName soulsearch' >> /opt/httpd-vuln/conf/httpd.conf

# ── Builder stage: fetch WordPress + a vulnerable plugin + wp-cli ────────────
# WordPress is PHP (arch-independent); a recent PHP-8.2-compatible core, plus a
# deliberately outdated wp-file-manager (CVE-2020-25213 unauth RCE) for WPScan
# to flag. Downloaded here so wget/unzip don't ship in the final image.
FROM debian:12-slim AS wp-build
ENV DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
    && apt-get install -y --no-install-recommends wget unzip ca-certificates \
    && rm -rf /var/lib/apt/lists/*
RUN wget -qO /tmp/wp.tar.gz https://wordpress.org/wordpress-6.4.3.tar.gz \
    && mkdir -p /opt/wp && tar xzf /tmp/wp.tar.gz -C /opt/wp --strip-components=1 \
    && wget -qO /tmp/wpfm.zip \
        https://downloads.wordpress.org/plugin/wp-file-manager.6.0.zip \
    # This old release is double-wrapped: the outer zip holds an inner
    # wp-file-manager-6.O.zip that has the actual plugin — extract the inner one.
    && mkdir -p /tmp/wpfm && unzip -q /tmp/wpfm.zip -d /tmp/wpfm \
    && unzip -q "$(find /tmp/wpfm -name '*.zip' | head -1)" \
        -d /opt/wp/wp-content/plugins/ \
    && wget -qO /opt/wp-cli.phar \
        https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar

# ── Final image ─────────────────────────────────────────────────────────────
FROM debian:12-slim

# Deliberately weak SSH credentials (lab door). Override at build time with
# --build-arg if you need different values.
ARG SOULSEARCH_USER=souleyez
ARG SOULSEARCH_PASS=souleyez

ENV DEBIAN_FRONTEND=noninteractive

RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        openssh-server ca-certificates \
        apache2 libapache2-mod-php php-mysqli php-cli php-xml php-curl \
        mariadb-server mariadb-client \
        iputils-ping \
        vsftpd samba smbclient \
        libapr1 libaprutil1 libpcre3 \
        sudo \
        # Common interpreters/tools a real Apache/WordPress server carries.
        # The slim base ships only mawk (no networking) and no nc/perl/python,
        # which made post-exploitation payloads (reverse/bind shells) fail — an
        # UNREALISTICALLY stripped box. Installing these makes it look like a
        # real server AND lets the standard MSF payloads land a shell:
        #   gawk -> /inet networking (cmd/unix/*_awk), and we make it the default
        #          `awk` below (mawk can't network);
        #   netcat-traditional -> `nc -e` (cmd/unix/*_netcat, the MSF defaults);
        #   perl / python3 -> cmd/unix/*_perl / *_python payloads.
        gawk netcat-traditional perl python3 \
    && rm -rf /var/lib/apt/lists/* \
    # Make `awk` GNU awk — mawk (the slim default) can't open sockets, so awk
    # network payloads silently fail; gawk supports /inet/tcp used by them.
    && update-alternatives --set awk /usr/bin/gawk \
    # sshd needs its privilege-separation dir to exist at runtime.
    && mkdir -p /run/sshd \
    # A normal login user with a weak password (the SSH door).
    && useradd -m -s /bin/bash "${SOULSEARCH_USER}" \
    && echo "${SOULSEARCH_USER}:${SOULSEARCH_PASS}" | chpasswd \
    # Allow password auth so weak-cred brute-force chains (hydra, ssh_login) work.
    && sed -ri 's/^#?PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config \
    && sed -ri 's/^#?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config \
    # Generate host keys at build time (regenerated at runtime if missing).
    && ssh-keygen -A \
    # MariaDB: listen on the network (Phase 3 "direct DB" door) instead of
    # localhost-only, so the network root/@% users are reachable.
    && sed -ri 's/^bind-address\s*=.*/bind-address = 0.0.0.0/' \
        /etc/mysql/mariadb.conf.d/50-server.cnf

# ── Phase 3: credential-reuse accounts (Unix + Samba) ───────────────────────
# The cracked web-app creds (users table) also work here — one identity across
# SSH / FTP / SMB. Passwords match seed.sql's users plaintexts.
#
# BRIDGE: admin's password is `admin123` — the SAME credential recovered from
# the external app (Juice Shop admin@juice-sh.op) in Act 1. So the cracked
# external password reuses straight onto this box's SSH `admin` account, which
# is the external -> internal foothold that opens the internal assessment.
RUN groupadd -f staff \
    && for entry in \
         admin:admin123 \
         luke_skywalker:letmein \
         han_solo:monkey \
         darth_vader:dragon \
         leia_organa:admin123; do \
         u="${entry%%:*}"; p="${entry##*:}"; \
         useradd -m -s /bin/bash -G staff "$u" && echo "$u:$p" | chpasswd; \
         printf '%s\n%s\n' "$p" "$p" | smbpasswd -s -a "$u"; \
       done

# ── Self-contained privesc: the reused `admin` account can sudo php ─────────
# The cracked/reused web-admin (admin:admin123) may run php as root with NO
# password — a realistic leftover "maintenance" sudo rule on a PHP app box.
# GTFOBins:  ssh admin@host -> sudo -l -> sudo php -r 'system("/bin/bash");' -> root
# This is the on-prem root path that does NOT depend on the cloud pivot (that
# path is the `prod` user + sudo find, added in Phase 5). Two users, two
# distinct GTFOBins privescs — the deep-dive reaches root without the cloud.
RUN echo 'admin ALL=(ALL) NOPASSWD: /usr/bin/php' > /etc/sudoers.d/admin \
    && chmod 0440 /etc/sudoers.d/admin

# ── Phase 3: FTP (vsftpd) — anon loot dir + local-user login ────────────────
COPY vsftpd.conf /etc/vsftpd.conf
COPY ftp-loot/ /srv/ftp/
RUN mkdir -p /var/run/vsftpd/empty \
    && chown -R root:root /srv/ftp && chmod -R a-w,a+rX /srv/ftp

# ── Phase 3: SMB (Samba) — guest public share + staff-only share ────────────
COPY smb.conf /etc/samba/smb.conf
COPY smb-loot/public/  /srv/smb/public/
COPY smb-loot/private/ /srv/smb/private/
RUN chmod -R a+rX /srv/smb/public \
    && chgrp -R staff /srv/smb/private && chmod -R 750 /srv/smb/private

# Web app (deliberately vulnerable) + DB seed.
COPY web/ /var/www/html/
COPY seed.sql /opt/soulsearch/seed.sql
RUN rm -f /var/www/html/index.html \
    # Writable, world-served upload dir (the web-shell drop target). Debian's
    # default <Directory /var/www/> keeps Indexes on, so /uploads/ also lists.
    && mkdir -p /var/www/html/uploads \
    && chown -R www-data:www-data /var/www/html/uploads

# ── Phase 4: named-CVE door — Apache httpd 2.4.49 (CVE-2021-41773) on :8081 ──
# Copy the compiled-from-source vulnerable server in from the builder stage.
COPY --from=httpd-vuln-build /opt/httpd-vuln /opt/httpd-vuln

# ── Phase 2b: WordPress CMS door (WPScan -> hydra -> theme-editor shell) ─────
COPY --from=wp-build /opt/wp /var/www/html/wordpress
COPY --from=wp-build /opt/wp-cli.phar /usr/local/bin/wp
COPY wp-config.php /var/www/html/wordpress/wp-config.php
RUN chmod +x /usr/local/bin/wp \
    && chown -R www-data:www-data /var/www/html/wordpress \
    # Pretty permalinks (mod_rewrite + .htaccess) so WPScan's author-archive
    # user enumeration works for every user, not just post authors.
    && a2enmod rewrite >/dev/null \
    && printf '<Directory /var/www/html>\n    AllowOverride All\n</Directory>\n' \
        > /etc/apache2/conf-available/soulsearch-htaccess.conf \
    && a2enconf soulsearch-htaccess >/dev/null

# ── Phase 5: Stratosphere pivot landing box ─────────────────────────────────
# soulsearch is the on-prem box the cloud loot lands on: the S3-leaked ed25519
# key authorizes the `prod` user (injected at runtime by the soulsearch-pivot
# post_start hook), a planted secret is the loot, and a sudo misconfig is the
# privesc to root — closing the internal -> cloud -> same-internal-box loop.
RUN useradd -m -s /bin/bash prod \
    # Privesc: NOPASSWD sudo on find (GTFOBins: sudo find ... -exec /bin/sh -> root).
    && echo 'prod ALL=(ALL) NOPASSWD: /usr/bin/find' > /etc/sudoers.d/prod \
    && chmod 0440 /etc/sudoers.d/prod \
    # Planted prod secret (ssh_pivot's default loot cats ~/*secret* / ~/*.txt).
    && printf '%s\n' \
        'PROD environment secrets (do not distribute)' \
        'db_root_password=root123' \
        'internal_api_token=soulsearch-prod-9f3a1c-DEMO' > /home/prod/prod_secret.txt \
    && chown prod:prod /home/prod/prod_secret.txt \
    && chmod 600 /home/prod/prod_secret.txt

COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh

# 21 = FTP (+30000-30009 passive), 22 = SSH, 80 = web, 139/445 = SMB,
# 3306 = MySQL, 8081 = the vulnerable Apache 2.4.49 (CVE-2021-41773).
EXPOSE 21 22 80 139 445 3306 8081 30000-30009

ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]
