A Mythic C2 Profile that uses AWS S3 for command and control communication with per-execution credential isolation.
Many organizations allowlist *.s3.amazonaws.com for cloud-dependent services — this profile leverages that common egress exception.
For authorized security testing and research only.
Unlike other cloud storage C2 approaches that use shared credentials, this profile provisions unique IAM credentials per execution at runtime:
- Server IAM credentials never leave the Mythic server
- Bootstrap credentials (build-time) can only write registration requests — no bucket listing, no data access
- Bootstrap auto-revocation — bootstrap IAM user is deleted after
max_registrations(default: 10), preventing captured payloads from registering unlimited agents - Per-execution IAM users are created at runtime with prefix-scoped policies
- Full isolation — compromised agent cannot read, write, list, or disrupt other agents
- Same payload executed on multiple hosts gets independent credentials
cd /path/to/Mythic
sudo ./mythic-cli install folder /path/to/s3_storagepython3 setup_aws.py --region eu-west-1This will:
- Create an S3 bucket (with public access blocked and defense-in-depth bucket policy)
- Create an SQS queue with S3 event notifications for near-instant message detection
- Create a server IAM user with S3 + IAM + SQS permissions
- Generate access keys and write the C2 config
Options:
python3 setup_aws.py --profile myprofile # use specific AWS CLI profile
python3 setup_aws.py --region eu-west-1 # override region
python3 setup_aws.py --bucket my-c2-bucket # specify bucket name
python3 setup_aws.py --teardown # remove all created resources- Create an S3 bucket with public access blocked
- Create a server IAM user with the permissions listed in Server IAM Permissions
- Add a bucket policy denying
ListBucketfors3-agent-*ands3-bootstrap-*users - Configure
C2_Profiles/s3_storage/s3_storage/c2_code/config.json - Build a payload — the profile handles all credential provisioning automatically
Encryption (AES-256-CBC + HMAC-SHA256) is optional, controlled via Mythic's AESPSK parameter, and handled at the C2 layer (mythic_encrypts = False).
See full documentation at documentation-c2/s3_storage/_index.md
BUILD TIME:
generate_config() creates bootstrap IAM user (register/ prefix only)
Builder stamps bootstrap credentials + payload prefix + PSK into agent
RUNTIME (per execution):
Agent Server
────── ──────
1. Generate runtime UUID
2. PUT register/{payload}/{uuid}.req ──► 3. Detect .req (SQS event or LIST poll)
4. Create IAM user (exec-{uuid}/ scoped)
5. PUT .creds file
6. GET .creds file ◄──
7. DELETE .creds, verify creds
8. Checkin via exec-{uuid}/ats/ ──► 9. SQS event triggers instant pickup
Forward to Mythic
◄── 10. Response via exec-{uuid}/sta/
The server supports two modes for detecting new S3 objects:
| Mode | Config | Avg Detection | Use Case |
|---|---|---|---|
| SQS event-driven | sqs_queue_url set |
~1.5s | Default, recommended for all use cases |
| LIST polling | sqs_queue_url empty |
~3-5s | Fallback when SQS is unavailable |
SQS mode uses S3 event notifications → SQS long polling for near-instant message detection. The server automatically selects SQS mode when sqs_queue_url is configured, falling back to periodic LIST polling otherwise.
s3://bucket/
register/ # Bootstrap registration
payload-{uuid[:12]}/
{runtime-uuid}.req # Agent registration request
{runtime-uuid}.creds # Server-provisioned credentials (ephemeral)
exec-{runtime-uuid[:12]}/ # Per-execution prefix (isolated)
ats/{message-id}.obj # Agent-to-Server messages
sta/{message-id}.obj # Server-to-Agent responses
This repository includes Dolios, a minimal Python agent for testing and as a reference implementation.
- Runtime credential bootstrap with IAM propagation verification
- Optional AES-256-CBC + HMAC-SHA256 encryption with Encrypted Key Exchange (EKE)
- AWS SigV4 signing using stdlib only (no boto3 on target)
- UUID-based message correlation (ats/sta object structure)
- Built-in commands:
shell,ls,cd,cat,ps,env,sleep,whoami,pwd,hostname,exit
- Single PUT uploads (no multipart) — practical limit ~50-100 MB per message
- All messages loaded in memory (agent and server side)
- 30s request timeout
The server IAM user needs:
S3:
s3:PutObject,s3:GetObject,s3:DeleteObject,s3:ListBucketon the bucket
SQS (event-driven detection):
sqs:ReceiveMessage,sqs:DeleteMessage,sqs:GetQueueUrl,sqs:GetQueueAttributes- Scoped to the C2 event queue ARN
IAM (agent provisioning):
iam:CreateUser,iam:DeleteUser,iam:PutUserPolicy,iam:DeleteUserPolicyiam:ListUserPolicies,iam:CreateAccessKey,iam:DeleteAccessKey,iam:ListAccessKeys- Scoped to
s3-agent-*ands3-bootstrap-*user ARNs
STS:
sts:GetCallerIdentity
| Component | Path |
|---|---|
| C2 server | C2_Profiles/s3_storage/s3_storage/c2_code/server.py |
| C2 config RPC | C2_Profiles/s3_storage/s3_storage/c2_functions/s3_storage.py |
| Agent code | Payload_Type/dolios/dolios/agent_code/agent.py |
| Builder | Payload_Type/dolios/dolios/agent_functions/builder.py |
| AWS setup | setup_aws.py |
| Latency benchmark | test_latency.py |
