Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ jobs:
- name: Security scan (tests)
run: bandit -r tests -ll

- name: Attestation fuzz regression gate
env:
RC_ADMIN_KEY: "0123456789abcdef0123456789abcdef"
DB_PATH: ":memory:"
ATTEST_FUZZ_CASES: "10000"
run: python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v

- name: Run tests with pytest (blocking)
env:
RC_ADMIN_KEY: "0123456789abcdef0123456789abcdef"
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ Thumbs.db

# Logs
*.log
.pytest_cache/
pytest-cache-files-*/
tests/.tmp_attestation/

# Windows miner build artifacts
Rustchain/miners/windows/dist/
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ Earn **RTC** by contributing to the RustChain ecosystem!

---

## Testing Notes

- Attestation malformed-input fuzz harness and replayable corpus: [docs/attestation_fuzzing.md](docs/attestation_fuzzing.md)

## 💰 Antiquity Multipliers

Your hardware's age determines your mining rewards:
Expand Down
47 changes: 47 additions & 0 deletions docs/attestation_fuzzing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Attestation Malformed-Input Regression Harness

This repository includes a deterministic malformed-input regression gate for `POST /attest/submit` plus a replayable regression corpus under `tests/attestation_corpus/`.

## Corpus Classes

Current explicit corpus entries cover these malformed input classes:

1. Invalid JSON root: `null`
2. Invalid JSON root: array
3. Miner identifier shape mismatch
4. Device payload scalar/object mismatch
5. Signals payload scalar/object mismatch
6. Signals MAC list shape mismatch
7. Fingerprint checks array/object mismatch
8. Report payload scalar/object mismatch

## Replay One Corpus Entry

```bash
python tests/replay_attestation_corpus.py tests/attestation_corpus/malformed_report_scalar.json
```

The script prints the HTTP status code and parsed JSON response, and exits non-zero if replay causes a server-side `5xx`.

## Quick Regression Gate

```bash
python -m pytest tests/test_attestation_fuzz.py -v
```

## 10,000-Case Mutation Run

PowerShell:

```powershell
$env:ATTEST_FUZZ_CASES = "10000"
python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v
```

Bash:

```bash
ATTEST_FUZZ_CASES=10000 python -m pytest tests/test_attestation_fuzz.py -k mutation_regression_no_unhandled_exceptions -v
```

This is the CI-mode gate for "no unhandled exceptions" in the attestation parsing path. Set `ATTEST_FUZZ_SEED` only when you need to reproduce a specific random sequence locally.
70 changes: 70 additions & 0 deletions docs/rip201_fleet_detection_bypass.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# RIP-201 Fleet Detection Bypass

## Summary

This report demonstrates a black-box bypass of the deployed RIP-201 fleet immune system:

1. The server trusts client-supplied `X-Forwarded-For` as the miner source IP.
2. The fleet scorer treats missing optional fingerprint dimensions as "no evidence" instead of suspicious absence.
3. Timing correlation can be avoided by spacing attestations outside the 30-second window.

With those three behaviors combined, a coordinated 5-miner fleet on shared infrastructure can remain at `fleet_score = 0.0` for consecutive epochs while keeping full reward weight.

## Technique

### 1. Spoof IP clustering

`client_ip_from_request()` prefers the left-most `X-Forwarded-For` value over `REMOTE_ADDR` without validating that the request actually came from a trusted reverse proxy. A client can therefore choose the IP written into:

- `miner_attest_recent.source_ip`
- `ip_rate_limit.client_ip`
- RIP-201 `fleet_signals.subnet_hash`

This lets one host appear to come from many different /24 subnets.

### 2. Keep fingerprint checks valid but sparse

`validate_fingerprint_data()` requires `anti_emulation` and `clock_drift` for modern hardware, but `record_fleet_signals_from_request()` only records four similarity dimensions:

- `clock_drift_cv`
- `cache_latency_hash`
- `thermal_signature`
- `simd_bias_hash`

The similarity engine only flags a pair when there are at least two comparable dimensions and at least two matches. Submitting only the minimum valid checks leaves just one comparable dimension (`clock_drift_cv`), so fingerprint similarity never fires.

### 3. Avoid timing correlation

Spacing attestations by more than 30 seconds keeps the timing ratio below the correlation threshold.

## Reproduction

Run:

```bash
python tools/rip201_fleet_detection_bypass_poc.py
```

The PoC prints:

- a baseline scenario where a same-subnet shared-fingerprint fleet is flagged
- a bypass scenario where five miners remain clean for three consecutive epochs

Run the tests:

```bash
python -m pytest tests/test_rip201_fleet_bypass.py -v
```

## Impact

- A single operator can present a coordinated fleet as five independent miners.
- The fleet can stay under the `0.3` clean threshold.
- Because the PoC keeps `fleet_score = 0.0`, the effective multiplier remains unchanged.

## Recommended Fixes

1. Only trust `X-Forwarded-For` when `REMOTE_ADDR` belongs to an allowlisted reverse proxy.
2. Record the actual peer IP separately from forwarded headers and use the trusted peer IP for fleet detection.
3. Treat missing fingerprint dimensions as suspicious for modern miners instead of neutral.
4. Require a minimum fingerprint feature set for fleet scoring, not just attestation acceptance.
Loading
Loading