mirror of
https://github.com/usestrix/strix.git
synced 2026-08-22 11:02:08 +02:00
Simplify Python proxy automation
This commit is contained in:
@@ -1,113 +1,74 @@
|
||||
---
|
||||
name: python
|
||||
description: python_action — execute Python in the sandbox with Caido proxy helpers (list_requests, view_request, send_request, repeat_request, scope_rules) pre-bound as awaitables. Stateless per call; persistence via files.
|
||||
description: Run Python through exec_command in the SDK sandbox. Use the image-baked caido_api module for Caido proxy automation from Python scripts.
|
||||
---
|
||||
|
||||
# Python In The Sandbox
|
||||
|
||||
# python_action — when and how
|
||||
Use `exec_command` for Python. There is no separate Strix Python executor.
|
||||
|
||||
Use ``python_action`` for any Python-side work: payload encoding/decoding,
|
||||
parsing/transforming captured HTTP traffic, crypto operations, custom
|
||||
exploit scripts, log/JSON analysis. Use ``exec_command`` for shell tools
|
||||
(nmap, sqlmap, ffuf, agent-browser, package managers, daemons).
|
||||
Prefer writing reusable scripts to `/workspace/scratch/<name>.py` and
|
||||
running them with `python3 /workspace/scratch/<name>.py`. For short
|
||||
one-off transformations, `python3 -c` or a small here-document is fine.
|
||||
|
||||
**Do not** wrap Python in bash heredocs, ``python3 -c`` one-liners, or
|
||||
``echo | python3`` chains via ``exec_command`` — ``python_action`` exists
|
||||
so structured output replaces fragile stdout parsing.
|
||||
## Proxy Automation From Python
|
||||
|
||||
## What's pre-bound (no imports needed)
|
||||
|
||||
All proxy helpers are **async** — call them with ``await``:
|
||||
|
||||
- ``list_requests(httpql_filter=, first=50, after=, sort_by=, sort_order=,
|
||||
scope_id=)`` → cursor-paginated SDK ``Connection``. Iterate
|
||||
``connection.edges``; each edge has ``.cursor`` and ``.node.request`` /
|
||||
``.node.response``.
|
||||
- ``view_request(request_id, part="request")`` → SDK request object.
|
||||
``.request.raw`` and ``.response.raw`` are bytes.
|
||||
- ``send_request(method, url, headers=None, body="")`` → dict with
|
||||
``status``, ``error``, ``elapsed_ms``, ``response_raw`` (bytes or None),
|
||||
``session_id``.
|
||||
- ``repeat_request(request_id, modifications={...})`` → same shape.
|
||||
``modifications`` keys: ``url`` / ``params`` / ``headers`` / ``body`` /
|
||||
``cookies``.
|
||||
- ``scope_rules(action, allowlist=, denylist=, scope_id=, scope_name=)``
|
||||
— same actions as the host-side tool (``list``/``get``/``create``/
|
||||
``update``/``delete``).
|
||||
|
||||
Top-level ``await`` works — the body is wrapped in an async function for
|
||||
you. ``print()`` to emit visible output; the last expression is **not**
|
||||
auto-shown.
|
||||
|
||||
## Stateless model + how to keep state
|
||||
|
||||
Each ``python_action`` call is a **fresh process**: variables, imports,
|
||||
and definitions do not survive. To carry state across steps:
|
||||
|
||||
- **Combine into one call** when the workflow is short — write the full
|
||||
multi-step routine as one ``code`` block.
|
||||
- **Persist to disk** for longer-lived state. ``/workspace/scratch/`` is
|
||||
pentester-writable and survives across calls within a scan.
|
||||
- **Build a script** with ``apply_patch`` to ``/workspace/scratch/<name>.py``
|
||||
and run it via ``exec_command python3 ...`` when you need a file the
|
||||
agent can iterate on.
|
||||
|
||||
## Examples
|
||||
|
||||
### Hunt SQLi candidates by inspecting captured traffic
|
||||
The sandbox image includes an installed `caido_api` module. Import it
|
||||
explicitly when Python code needs Caido traffic or replay access:
|
||||
|
||||
```python
|
||||
# All POSTs that look interesting
|
||||
posts = await list_requests(
|
||||
httpql_filter='req.method.eq:"POST" AND req.path.cont:"/api/"',
|
||||
first=50,
|
||||
from caido_api import (
|
||||
list_requests,
|
||||
repeat_request,
|
||||
scope_rules,
|
||||
send_request,
|
||||
view_request,
|
||||
)
|
||||
candidates = []
|
||||
for edge in posts.edges:
|
||||
body = await view_request(edge.node.request.id, part="request")
|
||||
raw = body.request.raw.decode("utf-8", errors="replace")
|
||||
if "id=" in raw or "user=" in raw:
|
||||
candidates.append(edge.node.request.id)
|
||||
|
||||
print(f"{len(candidates)} candidates")
|
||||
print(candidates[:10])
|
||||
```
|
||||
|
||||
### Replay with a SQLi probe and a tampered cookie
|
||||
All helpers are async. Use them inside `asyncio.run(...)` or an async
|
||||
function:
|
||||
|
||||
```python
|
||||
result = await repeat_request(
|
||||
"req_abc123",
|
||||
modifications={
|
||||
"params": {"id": "1' OR '1'='1"},
|
||||
"cookies": {"session": "ATTACKER_TOKEN"},
|
||||
},
|
||||
)
|
||||
print(result["status"], result["elapsed_ms"], "ms")
|
||||
if result["response_raw"]:
|
||||
print(result["response_raw"].decode("utf-8", errors="replace")[:500])
|
||||
import asyncio
|
||||
|
||||
from caido_api import list_requests, view_request
|
||||
|
||||
|
||||
async def main():
|
||||
posts = await list_requests(
|
||||
httpql_filter='req.method.eq:"POST" AND req.path.cont:"/api/"',
|
||||
first=50,
|
||||
)
|
||||
candidates = []
|
||||
for edge in posts.edges:
|
||||
request_id = edge.node.request.id
|
||||
body = await view_request(request_id, part="request")
|
||||
raw = body.request.raw.decode("utf-8", errors="replace")
|
||||
if "id=" in raw or "user=" in raw:
|
||||
candidates.append(request_id)
|
||||
|
||||
print(f"{len(candidates)} candidates")
|
||||
print(candidates[:10])
|
||||
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
### Decode/encode payloads
|
||||
Available helpers:
|
||||
|
||||
```python
|
||||
import base64, urllib.parse, hashlib
|
||||
- `list_requests(httpql_filter=, first=50, after=, sort_by=, sort_order=, scope_id=)` returns a cursor-paginated Caido SDK `Connection`.
|
||||
- `view_request(request_id, part="request")` returns a Caido SDK request object with raw request/response bytes.
|
||||
- `send_request(method, url, headers=None, body="")` sends an arbitrary raw request through Caido Replay.
|
||||
- `repeat_request(request_id, modifications={...})` replays a captured request after modifying `url`, `params`, `headers`, `body`, or `cookies`.
|
||||
- `scope_rules(action, allowlist=, denylist=, scope_id=, scope_name=)` manages Caido scopes.
|
||||
|
||||
token = "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWxpY2UifQ.sig"
|
||||
header_b64, payload_b64, _ = token.split(".")
|
||||
print(base64.urlsafe_b64decode(payload_b64 + "=="))
|
||||
```
|
||||
## Workflow
|
||||
|
||||
### Iterate an exploit by writing to scratch
|
||||
|
||||
When iterating, prefer writing the script to disk so you can edit-and-rerun
|
||||
without re-sending the whole code each call:
|
||||
For iterative exploit work, put code in a file:
|
||||
|
||||
```text
|
||||
# 1. Use apply_patch to create /workspace/scratch/exploit.py
|
||||
# 2. exec_command: python3 /workspace/scratch/exploit.py
|
||||
# 3. Edit + re-run; repeat until working
|
||||
1. Create or edit `/workspace/scratch/exploit.py` with `apply_patch`.
|
||||
2. Run it with `exec_command`: `python3 /workspace/scratch/exploit.py`.
|
||||
3. Edit and rerun until the proof-of-concept is reliable.
|
||||
```
|
||||
|
||||
For one-shot crypto/encoding work or a single proxy-data analysis,
|
||||
``python_action`` is the cleaner choice.
|
||||
|
||||
Reference in New Issue
Block a user