Initial commit

This commit is contained in:
Warezpeddler
2026-04-25 23:09:31 +01:00
commit 3325436017
92 changed files with 18397 additions and 0 deletions

363
README.md Normal file
View File

@@ -0,0 +1,363 @@
# ApplePY
A macOS security review and attack-surface scanner. It can be run on a Mac to produce a Markdown report** and a multi-worksheet XLSX spreadsheet covering CIS benchmark controls, privilege escalation paths, MDM posture, NIST mSCP compliance, living-off-the-land catalogues, and much more. Optionally exports a BloodHound CEcompatible graph JSON file for visual attack-path analysis (courtesy of JAMF Hound).
### Disclaimer
For **authorised** security assessments only. Results are indicative; confirm material findings in your environment before acting on them. Third-party sources and licences are listed in [CREDITS.md](CREDITS.md). All rights to original authors.
---
## Quick start
```bash
# Unprivileged scan (no root required), outputs ./applepy-out/report.md and findings.xlsx
./dist/applepy/applepy
# Full scan (unprivileged + privileged checks)
sudo ./dist/applepy/applepy
# Choose a custom output folder
sudo ./dist/applepy/applepy --output-dir ~/Desktop/assessment
```
See [Getting the binary](#getting-the-binary) below if you do not yet have `dist/applepy/applepy`.
---
## What it produces
| File | Description |
|------|-------------|
| `report.md` | Narrative summary grouped into themes (e.g. Hardening, MDM, Compliance). Each finding shows severity, risk, impact, and remediation steps. |
| `findings.xlsx` | Multi-worksheet spreadsheet. The **Contents** tab links to every category sheet. Each sheet is colour-coded by severity (critical → informational) and includes a MITRE ATT&CK mapping worksheet. |
| `graph_findings.json` | *(Optional — `--export-graph-json`)* JamfHound-compatible BloodHound CE OpenGraph JSON for visual attack-path analysis. |
---
## Privilege model
ApplePY splits its work into two phases so that you do not need root for a partial scan:
| How you run | What runs |
|-------------|-----------|
| `applepy` (no `sudo`) | **Unprivileged phase only.** Checks that do not require root. A warning is printed reminding you that the privileged phase was skipped. |
| `sudo applepy` | **Both phases.** Unprivileged checks first, then privileged checks (sudoers, SUID/SGID binaries, TCC permissions, world-writable LaunchDaemon binaries, mSCP compliance generation, and so on). |
Optional overrides:
| Flag | Effect |
|------|--------|
| `--unprivileged-only` | Run the unprivileged phase only, even when invoked with `sudo`. |
| `--privileged-only` | Run the privileged phase only (combine with `sudo`; without root, no checks run). |
---
## Getting the binary
### Option A — Build the PyInstaller bundle (recommended)
The **primary deployment method** is a self-contained one-folder bundle built with PyInstaller. It can then transferred to the target host as a single self extracting archive. The bundle embeds the Python runtime, dependencies and optionally the NIST mSCP compliance corpus and Lynis.
**Requirements (build machine only):** a macOS workstation, Python ≥ 3.11 and git.
```bash
cd /path/to/security-review
chmod +x scripts/build_bundle.sh scripts/vendor_compliance_assets.sh
./scripts/build_bundle.sh
```
This will:
1. Shallow-clone **NIST mSCP** and **Lynis** into `applepy/data/`.
2. Install ApplePY with the `bundle` extra (PyInstaller, PyYAML, xlwt).
3. Run PyInstaller using `applepy.spec`.
Output:
- **`dist/applepy/applepy`** — the binary you run on the target.
- **`dist/applepy/_internal/`** — bundled libraries, data, and Python runtime (keep alongside the binary).
> **Do not run `build/applepy/applepy`.** The `build/` tree is PyInstaller's working directory — it is missing the `_internal/` folder and will fail. Always use `dist/applepy/`.
**Rebuilding without re-cloning** (e.g. offline):
```bash
SKIP_VENDOR_COMPLIANCE=1 ./scripts/build_bundle.sh
```
**If the build fails with a `PermissionError` removing `dist/applepy`:** a previous `sudo applepy` run left root-owned files in `_internal/.../macos_security/build/`. Clear them, then rebuild:
```bash
sudo rm -rf dist/applepy
./scripts/build_bundle.sh
```
### Option B — Install from source (development / library)
```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
applepy --help
```
To include compliance reporting in this workflow, run `applepy --bootstrap-compliance` (see [Compliance](#compliance-mscp-and-lynis)).
---
## Running on the target Mac
### Clear the quarantine flag
When you transfer `dist/applepy/` by browser download, email, or certain archive tools, macOS applies a quarantine attribute. Clear it before running:
```bash
xattr -dr com.apple.quarantine /path/to/dist/applepy
```
Verify nothing remains:
```bash
xattr -lr /path/to/dist/applepy
```
### Run the scan
```bash
# Show all available options
/path/to/dist/applepy/applepy --help
# Unprivileged scan (safe to run without sudo)
/path/to/dist/applepy/applepy --output-dir ./out-user
# Full scan (both phases)
sudo /path/to/dist/applepy/applepy --output-dir ./out-full
# Full scan + graph JSON for BloodHound CE
sudo /path/to/dist/applepy/applepy --output-dir ./out-full --export-graph-json
```
### Unsigned binary on a managed Mac
If macOS shows a security warning on first launch, **Control-click → Open** in Finder, or go to **System Settings → Privacy & Security** and click **Open Anyway**. This is expected for programs that have not been signed with an Apple Developer ID.
---
## What it checks
ApplePY runs registered checks in the unprivileged and privileged phases. Below is a capability overview grouped by area.
### Core platform
Host summary, **SIP** status (`csrutil`), **Gatekeeper**, **Application Firewall**, system extensions listing, LaunchAgent/LaunchDaemon inventories, common **interpreters** on `PATH`.
### CIS benchmark controls
Over 80 CIS macOS Benchmark Level 1 and Level 2 checks covering: FileVault, screen saver lock, password policy, guest account, remote login, Bluetooth, mDNS, IPv6, Safari settings, Handoff, iCloud services, Siri, AirDrop, Time Machine, printer sharing, wake settings, and many more. Each check emits PASS, FAIL, or WARN with evidence.
### Privilege escalation
Read-only detection of common privilege escalation paths:
- **Sudoers NOPASSWD** — `/etc/sudoers` and `/etc/sudoers.d/` entries that allow password-free `sudo`.
- **Unexpected SUID/SGID binaries** — files with setuid or setgid bits outside the expected macOS baseline.
- **Writable LaunchDaemon binaries** — LaunchDaemon plists that reference binaries writable by non-root users.
- **Writable PATH directories** — directories on `PATH` (from `/etc/paths`) that are writable by non-root users.
- **High-risk TCC permissions** — applications granted full-disk access, camera, microphone, and similar sensitive TCC services.
### Dylib hijacking
RPATH ordering and writable-dylib-directory checks for installed applications.
### Filesystem posture
World-writable files under LaunchAgent and LaunchDaemon directories (caps configurable via environment variables).
### Living-off-the-land catalogues
**LOOBins** (live with cache; fallback data), **GTFOBins** (live API with cache), **lolapps**, **lottunnels**, cloud CLI presence on `PATH`, credential path hints (presence only — no secret reads).
### Compliance
**CIS worksheet** pointer, FileVault, pwpolicy, boot/uptime, Time Machine destinations, admin group, default umask, **NIST mSCP** status and (privileged) baseline generation + compliance script execution with per-rule worksheet output, **Lynis** quick audit when available.
### Common paths
Mounts, guest login, remote login / ARD plists, MDM enrolment hints, browser extension directories, local users (`dscl`), SSH public key filenames, cron/periodic/at surface, Automator workflow directories, user Keychains filenames.
### Extended surface
Routing table, `/etc/hosts`, DNS settings, per-service HTTP proxies, AirDrop/sharing defaults, shell startup file metadata, kernel extension list, USB/Bluetooth `system_profiler` snapshots, `last` logins, Wi-Fi preferences, NFS exports, `pmset`, CUPS/printing surface, configuration profiles, Spotlight, `launchctl print-disabled`, `sysctl kern` snapshot.
### Network listeners
TCP LISTEN and UDP sockets via `lsof`, excluding loopback-only and OS ephemeral port ranges; correlated with process names.
### Application surface
Entitlements sample (`codesign`), SSH directory, TCC / login items / background task store paths, Electron app ASAR and writable parent-directory checks.
### MDM posture (read-only)
- **Jamf** — local plists, receipts, LaunchAgents, helpers, extension attribute cache, listener hints, configuration profile stores, filesystem caps.
- **Kandji** — Application Support tree, LaunchAgents, receipts, preferences, helpers, listeners, configuration profile store.
### AI tools and IDEs
AI CLI tools on `PATH` (e.g. Claude Code, Cursor, GitHub Copilot CLI), AI desktop apps, local model storage, and IDE surface.
### Interpreters and package managers
Python, Node.js, Ruby, Go, Rust, and other runtimes found on `PATH` or in standard locations, plus Homebrew, pip, npm, and conda distributions.
---
## CLI reference
```text
applepy [options] # from pip install / editable install
python -m applepy [options] # from source tree (without install)
./dist/applepy/applepy [options] # PyInstaller bundle
```
| Short | Long | Description |
|-------|------|-------------|
| | `--version` | Print version and exit. |
| `-h` | `--help` | Show help and exit. |
| `-o PATH` | `--output-dir PATH` | Directory for `report.md`, `findings.xlsx`, and optional JSON (default: `./applepy-out`). |
| `-v` | `--verbose` | Increase log verbosity. Use `-vv` for debug output. |
| `-q` | `--quiet` | Suppress all output except errors on stderr (disables progress reporting). |
| | `--unprivileged-only` | Run the unprivileged phase only, even with `sudo`. |
| | `--privileged-only` | Run the privileged phase only (use with `sudo`). |
| | `--dry-run` | Run all checks but do not write any output files. |
| | `--export-graph-json` | Write `graph_findings.json` in JamfHound-compatible BloodHound CE OpenGraph format. Requires the SpecterOps Jamf extension in BloodHound CE. |
| | `--sequential` | Disable parallel check execution within each phase (useful for debugging). |
| | `--bootstrap-compliance` | Clone NIST mSCP and Lynis into `./vendor`, print hints, then exit (requires `git` and internet access). |
| `HEX` | `--heading-color HEX` | XLSX heading background colour as a 6-digit hex code (default: `871727`). |
### Progress output
By default, ApplePY prints a running progress display to stderr:
- **Each check** shows a progress bar, percentage, check name, and either `running` or the elapsed time and finding count once complete.
- **At the end of each phase**, a severity breakdown is shown (critical / high / medium / low / informational counts).
- **`-q` / `--quiet`** suppresses all of this.
- **`NO_COLOR=1`** disables ANSI colour codes. **`FORCE_COLOR=1`** forces colour even when stderr is not a terminal.
---
## Environment variables
| Variable | Description |
|----------|-------------|
| `APPLEPY_MACOS_SECURITY_ROOT` | Override the path to the `macos_security` clone (takes precedence over the bundled copy). |
| `APPLEPY_MSCP_BASELINE` | Baseline YAML stem or filename under `baselines/` (e.g. `cis_level1`). |
| `APPLEPY_MSCP_SKIP_GENERATE=1` | Skip `generate_guidance.py`; only run an existing `*_compliance.sh --check`. |
| `APPLEPY_MSCP_COMPLIANCE_LOG_MAX` | Maximum characters retained per stream in compliance evidence (default: 524288). |
| `APPLEPY_MSCP_FORCE_SUBPROCESS=1` | *(Frozen bundle only)* Use a system `python3` instead of re-invoking the bundle for `generate_guidance.py`. |
| `APPLEPY_PYTHON` | Python 3 interpreter to use for `generate_guidance.py` (when not frozen, or with `APPLEPY_MSCP_FORCE_SUBPROCESS=1`). |
| `APPLEPY_DECK_EXPORT_TXT` | Path to an optional SpecterOps-style deck reference text file. |
| `APPLEPY_KANDJI_MAX_FILES` | Cap for Kandji Application Support file listing. |
| `APPLEPY_FS_MAX_SCAN` / `APPLEPY_FS_MAX_HITS` | Caps for world-writable filesystem walks. |
| `APPLEPY_CATALOG_INCLUDE_NOISE=1` | Emit per-binary LOOBins rows for common UI binaries (Dock, Finder, etc.) that are normally summarised only. |
| `APPLEPY_GTFO_DETAILED=1` | One finding per on-host GTFOBins match (default: a single summary row). |
| `APPLEPY_GTFO_LIST_ABSENT=1` | Add a finding listing every GTFOBins name not found on the host (large; off by default). |
| `APPLEPY_GTFO_ROLLUP_MAX_LINES` | Maximum lines in the default GTFOBins summary row (default: 120). |
| `SKIP_VENDOR_COMPLIANCE=1` | Skip compliance cloning in `build_bundle.sh` (use when `applepy/data/` is already populated). |
| `NO_COLOR` | Disable ANSI colours in progress output (see [no-color.org](https://no-color.org/)). |
| `FORCE_COLOR` | Force ANSI colours even when stderr is not a TTY. |
---
## Compliance — NIST mSCP and Lynis
### NIST mSCP (macOS Security Compliance Project)
The NIST mSCP corpus lives at [usnistgov/macos_security](https://github.com/usnistgov/macos_security). ApplePY does **not** commit it; instead it resolves the corpus in this order:
1. `APPLEPY_MACOS_SECURITY_ROOT` (environment variable).
2. Bundled `applepy/data/macos_security` (populated by `scripts/vendor_compliance_assets.sh` at build time).
3. `./vendor/macos_security`, `./macos_security`, then common locations under the user's home directory.
When found, ApplePY runs `generate_guidance.py` against the selected baseline YAML (auto-selected, or set via `APPLEPY_MSCP_BASELINE`), then executes the generated `*_compliance.sh --check` script and parses the resulting audit plist into findings. Temporary artefacts (`build/` and the audit plist) are cleaned up after each scan.
### Lynis
Lynis is used from `PATH` if available, then from the bundled `applepy/data/lynis/lynis`, then from `./vendor/lynis/lynis`. Lynis is GPL-licensed — see [CREDITS.md](CREDITS.md).
### Bootstrap workflow (source install without bundled data)
```bash
# Clone mSCP and Lynis into ./vendor, print export hints, then exit
applepy --bootstrap-compliance
# Set paths for subsequent scans
export APPLEPY_MACOS_SECURITY_ROOT="$PWD/vendor/macos_security"
export PATH="$PWD/vendor/lynis:$PATH"
# Install mSCP Python dependencies
pip install pyyaml xlwt
# Run the full scan
sudo applepy --output-dir ./out
```
---
## Graph JSON (BloodHound CE)
With `--export-graph-json`, ApplePY writes `graph_findings.json` to the output directory. This file is in the **JamfHound-compatible BloodHound CE OpenGraph format** (v1.1.2), suitable for import into BloodHound CE with the SpecterOps Jamf extension installed.
To ingest: open BloodHound CE → **File Ingest** → upload `graph_findings.json`.
See [JamfHound on GitHub](https://github.com/SpecterOps/JamfHound) for extension installation instructions.
---
## Output files in detail
### `report.md`
A narrative summary organised into **overarching themes** (Hardening, Compliance, MDM, Catalogues, etc.). Each theme section lists findings from critical down to informational. Every finding includes:
- **Title** and **severity** (CRITICAL / HIGH / MEDIUM / LOW / INFORMATIONAL / WARN)
- **Description** of what was found
- **Risk** — what an attacker could do with this
- **Impact** — what remediation affects
- **Remediation** steps
- **MITRE ATT&CK technique IDs** where applicable
- **Evidence** — raw command output or parsed data supporting the finding
### `findings.xlsx`
A multi-worksheet spreadsheet:
- **Contents** — index tab with hyperlinks to each category sheet and a finding count.
- **Findings list** — all non-informational findings sorted by severity, then ID, with a link to the relevant category sheet.
- **Category sheets** — one sheet per check category, all findings including informational.
- **MITRE** — all findings with MITRE technique IDs, for import into threat-modelling tools.
- **CIS index** — CIS benchmark section cross-reference.
Cells are colour-coded by severity (red for critical through grey for informational). Empty optional fields show an em dash (—) rather than a blank cell.
---
## Developer reference
### Running the test suite
```bash
pip install -e ".[dev]"
./scripts/verify.sh # Ruff, pytest, ty (if installed), Semgrep (if installed)
```
Or individually:
```bash
ruff check applepy/
pytest
```
### Portable `PYTHONPATH` layout (no venv, no PyInstaller)
```bash
mkdir -p vendor && pip install -t vendor 'openpyxl>=3.1'
PYTHONPATH=vendor:. python -m applepy --output-dir ./out
```
Note: PyObjC is required on macOS for the full check set and is not available on Linux or Windows.
### PyObjC dependency
ApplePY depends on `pyobjc-core` and `pyobjc-framework-Cocoa` on macOS for native platform introspection. These packages are macOS-only; `pip install` on Linux or Windows simply skips them. Run the full scan on a Mac.
---
## Licence
See [CREDITS.md](CREDITS.md) for third-party references and licences.