Share
## https://sploitus.com/exploit?id=DB196B4C-A537-5744-8870-B94831744BBC
# Seal Security — Python (pip) Example

A minimal, intentionally vulnerable Flask application used to demonstrate, end‑to‑end, how
[Seal Security](https://www.sealsecurity.io/) remediates a known CVE by replacing a vulnerable
dependency with a **sealed** (backported, drop‑in) version — with **no change to your declared
requirements or your code**.

It is designed as a front‑to‑back smoke test for the Seal CLI in CI/CD: run the app, trigger a
real exploit, run Seal, and watch the same exploit get blocked.

---

## What this example demonstrates

| | |
|---|---|
| **Ecosystem** | Python / pip |
| **Vulnerable package** | `PyYAML==5.1` |
| **CVE** | [CVE‑2020‑14343](https://nvd.nist.gov/vuln/detail/CVE-2020-14343) — `yaml.load` / `FullLoader` deserialization → arbitrary code execution |
| **Sealed (fixed) version** | `pyyaml 5.1+sp1` from Seal's PyPI registry |
| **Integration** | Seal CLI as one build step — shown for both **GitHub Actions** and **Jenkins** |

---

## How the exploit works

The welcome page takes a `name` and parses it through PyYAML's default loader:

```python
parsed = yaml.load(name)   # PyYAML 5.1 → unsafe FullLoader (CVE-2020-14343)
```

On PyYAML 5.1, `yaml.load()` without an explicit `SafeLoader` uses the **FullLoader**, which can
construct arbitrary Python objects from untrusted input. An attacker submits a YAML payload that
evaluates arbitrary Python — here, running a shell command and returning its output.

**Normal request:** enter `alice` → renders `Welcome, alice!`

**Exploit payload** (paste into the name field):

```
!!python/object/apply:tuple [!!python/object/apply:map [!!python/name:eval , ["__import__('subprocess').check_output(['id']).decode()"]]]
```

The page renders the output of the server‑side `id` command — proof that arbitrary code ran on
the server, driven purely by untrusted input.

---

## Repository layout

```
.
├── app.py                         # the vulnerable Flask app
├── requirements.txt               # declares PyYAML==5.1
├── Jenkinsfile                    # example Jenkins (Groovy) pipeline with the Seal stage
└── .github/workflows/
    ├── build-and-run.yml          # run + expose the app for browser testing
    └── seal-security.yml          # run Seal remediation, then start the app
```

---

## Prerequisites

Seal is **SaaS, Seal‑hosted** — nothing is installed inside your environment, and all traffic
is **outbound HTTPS on TCP 443** only. To run the remediation you need:

| Secret / credential | Used for | Where it goes |
|---|---|---|
| **Seal token** | Authenticating the Seal CLI | GitHub Actions secret `SEAL_TOKEN` / Jenkins "Secret text" credential `seal-token` |
| **ngrok token** *(optional)* | Exposing the running app to a browser for testing | GitHub Actions secret `NGROK_TOKEN` |

> Configure these in **Settings → Secrets and variables → Actions** (GitHub) or
> **Manage Jenkins → Credentials** (Jenkins). Never commit tokens to the repo.

Allowlist these Seal hosts for outbound 443:
`app.sealsecurity.io`, `authorization.sealsecurity.io`, `cli.sealsecurity.io`, and — for
sealed pip packages — **`pypi.sealsecurity.io`**. The CLI binary is downloaded from
`github.com` / `objects.githubusercontent.com`.

---

## Run it locally

```bash
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
python app.py               # → http://localhost:5000
```

Open `http://localhost:5000/`, enter a normal name (works), then paste the exploit payload.

---

## Remediate with Seal

The Seal CLI runs as **one extra step**, after `pip install` and before packaging. It scans the
resolved dependencies and rewrites the vulnerable ones to their sealed versions, using **remote**
fix mode (policy is managed centrally in the Seal UI).

### Option A — GitHub Actions

Uses [`seal-community/cli-action`](https://github.com/seal-community/cli-action):

```yaml
- uses: seal-community/cli-action@latest
  with:
    mode: fix
    fix_mode: remote
    token: ${{ secrets.SEAL_TOKEN }}
    target: requirements.txt      # the manifest for this ecosystem
```

Run it via **Actions → “Seal Security Remediation” → Run workflow**. See
[`.github/workflows/seal-security.yml`](.github/workflows/seal-security.yml).

### Option B — Jenkins (Groovy pipeline)

A single added stage, after install and before packaging. See [`Jenkinsfile`](Jenkinsfile):

```groovy
stage('Seal') {
  steps {
    sh '''
      curl -fsSL https://github.com/seal-community/cli/releases/download/latest/seal-linux-amd64-latest -o seal
      chmod +x seal
      ./seal fix --mode remote "$SEAL_MANIFEST"   # SEAL_MANIFEST=requirements.txt
    '''
  }
}
```

`SEAL_TOKEN` comes from the `seal-token` Jenkins credential; set `SEAL_PROJECT` to your Seal
Project ID.

---

## What Seal changes

After `seal fix`, the vulnerable dependency resolves to a sealed build from Seal's PyPI
registry — same package, security fix backported:

| Dependency | Before | After (sealed) |
|---|---|---|
| PyYAML | 5.1 | **5.1+sp1** |

A sealed version is the *same* package with the security fix backported — a drop‑in
replacement, no code changes and no major‑version upgrade.

## Verify the fix

Re‑run the exploit against the remediated app. The sealed `PyYAML` refuses to construct the
malicious objects, so `yaml.load` raises instead of executing the payload, and the app responds
with **“Invalid input — payload rejected.”** Normal names still work.

---

## How to add Seal to your own project

1. Add **one step** to your pipeline, **after** dependencies are installed and **before**
   packaging.
2. Point `seal fix` at the specific manifest — `requirements.txt` for pip. For a repo with
   multiple manifests/lock files, run one `seal fix` per manifest.
3. Use **remote** fix mode so your security team manages remediation policy centrally in the
   Seal UI — nothing is committed to the repo.
4. Provide the Seal token via your CI secret store (GitHub secret / Jenkins credential).

That's the whole integration — one stage, outbound‑only, no changes to application code.