Skip to content

Commit 78a8a1d

Browse files
committed
docs: update project positioning for authorized cloud security validation
1 parent a2d6f02 commit 78a8a1d

21 files changed

Lines changed: 300 additions & 161 deletions

README.md

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,65 @@
11
# CloudToolKit
22

3-
Interactive multi-cloud security assessment framework.
3+
CloudToolKit is an adversary simulation and validation toolkit for assessing the effectiveness of CSPM, CNAPP, and related cloud detection and investigation platforms in authorized environments.
4+
5+
## Overview
6+
7+
CloudToolKit helps defenders reproduce realistic cloud security scenarios in owned labs, approved internal subscriptions, and explicitly authorized customer environments. It is designed for defensive validation through cloud asset inventory, identity and privilege abuse checks, suspicious resource activity review, and authorized instance command checks that generate realistic telemetry for detection and investigation testing.
8+
9+
## Why This Project Exists
10+
11+
Enterprise cloud security teams need repeatable validation workflows that answer practical questions:
12+
13+
- Can the platform detect suspicious cloud behavior in an authorized test environment?
14+
- Does the telemetry preserve enough context for investigation?
15+
- Can analysts correlate identity activity, resource changes, and control failures?
16+
- Where are the visibility gaps across CSPM, CNAPP, and related cloud security tooling?
17+
18+
## What It Validates
19+
20+
CloudToolKit is intended to help defenders verify:
21+
22+
- platform coverage across CSPM, CNAPP, and related cloud detection and investigation tooling
23+
- telemetry quality for triage, investigation, and root-cause analysis
24+
- investigation readiness across identity, compute, storage, and database activity
25+
- control visibility gaps that require tuning, additional telemetry, or compensating detections
426

527
## Features
628

7-
- **Multi-Cloud Support** - Alibaba, Tencent, Huawei, AWS, Azure, GCP, Volcengine, JDCloud
8-
- **Asset Enumeration** - Hosts, databases, storage buckets, domains, IAM users
9-
- **Security Testing** - Backdoor user creation, command execution, bucket dumping
10-
- **Interactive CLI** - Tab completion, session management, credential caching
11-
- **Lightweight Provider Clients** - AWS/Azure/Tencent/Huawei/Alibaba are being gradually decoupled from heavy official SDK paths
29+
- **Multi-Cloud Coverage** - Alibaba, Tencent, Huawei, AWS, Azure, GCP, Volcengine, and JDCloud
30+
- **Cloud Asset Inventory** - Hosts, databases, storage buckets, domains, IAM users, and related cloud resources
31+
- **Defender-Side Validation Payloads** - `iam-user-check`, `bucket-check`, `instance-cmd-check`, `event-check`, and `rds-account-check`
32+
- **Interactive CLI** - Tab completion, session management, and credential caching
33+
- **Lightweight Provider Clients** - AWS, Azure, Tencent, Huawei, and Alibaba integrations are being gradually decoupled from heavy official SDK paths
34+
35+
## Use Cases
36+
37+
- storage exposure checks in authorized environments
38+
- IAM lifecycle checks for identity telemetry and alert validation
39+
- instance execution telemetry checks for detection and investigation workflows
40+
- RDS account validation for database visibility and control verification
41+
- cross-signal investigation testing across identity, compute, storage, and database activity
42+
43+
## Example Validation Workflows
44+
45+
- Use `cloudlist` in an authorized environment to verify whether a CSPM or CNAPP accurately discovers compute, storage, identity, database, and DNS resources.
46+
- Use `iam-user-check` to create or remove a test IAM user and validate identity telemetry, alerting, and persistence detection coverage.
47+
- Use `instance-cmd-check` to generate telemetry for command execution, process correlation, and investigation workflows on a test instance.
48+
- Use `event-check` to review cloud security events and suspicious resource operations for investigation context, enrichment quality, and timeline reconstruction.
49+
- Use `rds-account-check` to provision read-only RDS access in an authorized environment to validate database visibility, control coverage, and investigation readiness.
50+
51+
## Supported Capabilities
52+
53+
| Provider | Inventory Coverage | Validation Payloads |
54+
|:--------:|:-----------:|:--------------------:|
55+
| Alibaba Cloud | ECS, OSS, RAM, RDS, DNS, SLS, SMS | iam-user-check, bucket-check, instance-cmd-check, event-check, rds-account-check |
56+
| Tencent Cloud | CVM, Lighthouse, COS, CAM, CDB, DNSPod | iam-user-check, bucket-check, instance-cmd-check |
57+
| Huawei Cloud | ECS, OBS, IAM, RDS | iam-user-check, bucket-check |
58+
| AWS | EC2, S3, IAM | iam-user-check, bucket-check |
59+
| Azure | Virtual Machines, Blob Storage | - |
60+
| GCP | Compute Engine, Cloud DNS, IAM | - |
61+
| Volcengine | ECS, IAM | - |
62+
| JDCloud | VM, IAM, OSS | - |
1263

1364
## Quick Start
1465

@@ -20,18 +71,18 @@ go build --ldflags "-s -w" -trimpath -o ctk cmd/main.go
2071
./ctk
2172
```
2273

23-
## Supported Capabilities
74+
## Responsible Use
2475

25-
| Provider | Enumeration | Security Testing |
26-
|:--------:|:-----------:|:----------------:|
27-
| Alibaba Cloud | ECS, OSS, RAM, RDS, DNS, SLS, SMS | backdoor-user, bucket-dump, exec-command, event-dump, database-account |
28-
| Tencent Cloud | CVM, Lighthouse, COS, CAM, CDB, DNSPod | backdoor-user, bucket-dump, exec-command |
29-
| Huawei Cloud | ECS, OBS, IAM, RDS | backdoor-user, bucket-dump |
30-
| AWS | EC2, S3, IAM | backdoor-user, bucket-dump |
31-
| Azure | Virtual Machines, Blob Storage | - |
32-
| GCP | Compute Engine, Cloud DNS, IAM | - |
33-
| Volcengine | ECS, IAM | - |
34-
| JDCloud | VM, IAM, OSS | - |
76+
CloudToolKit is intended only for owned environments, lab environments, approved internal subscriptions, and explicitly authorized customer environments. It is designed to help defenders verify detection coverage, telemetry quality, investigation workflows, and control effectiveness. It is not intended for unauthorized access, third-party abuse, or covert real-world intrusion activity.
77+
78+
## Non-Goals
79+
80+
CloudToolKit is not positioned as:
81+
82+
- an unauthorized offensive toolkit
83+
- a stealth or bypass framework
84+
- a weaponized intrusion utility
85+
- guidance for abuse against third-party environments
3586

3687
## Documentation
3788

@@ -41,4 +92,3 @@ See [Wiki](https://github.com/404tk/cloudtoolkit/wiki) for detailed usage.
4192

4293
- [c-bata/go-prompt](https://github.com/c-bata/go-prompt)
4394
- [projectdiscovery/cloudlist](https://github.com/projectdiscovery/cloudlist)
44-
- [rapid7/metasploit-framework](https://github.com/rapid7/metasploit-framework)

pkg/providers/alibaba/rds/accounts.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
)
1212

1313
func (d *Driver) CreateAccount(instanceID, dbName string) bool {
14-
accountName, accountPassword, ok := parseDBAccount()
14+
accountName, accountPassword, ok := parseRDSAccount()
1515
if !ok {
1616
return false
1717
}
@@ -34,7 +34,7 @@ func (d *Driver) CreateAccount(instanceID, dbName string) bool {
3434
}
3535

3636
func (d *Driver) DeleteAccount(instanceID string) {
37-
accountName, _, ok := parseDBAccount()
37+
accountName, _, ok := parseRDSAccount()
3838
if !ok {
3939
return
4040
}
@@ -53,10 +53,10 @@ func grantAccountPrivilege(ctx context.Context, client *api.Client, region, inst
5353
return err
5454
}
5555

56-
func parseDBAccount() (string, string, bool) {
57-
accountName, accountPassword, ok := strings.Cut(utils.DBAccount, ":")
56+
func parseRDSAccount() (string, string, bool) {
57+
accountName, accountPassword, ok := strings.Cut(utils.RDSAccount, ":")
5858
if !ok || strings.TrimSpace(accountName) == "" || strings.TrimSpace(accountPassword) == "" {
59-
logger.Error("DB account metadata is invalid. expected username:password")
59+
logger.Error("RDS account metadata is invalid. expected username:password")
6060
return "", "", false
6161
}
6262
return accountName, accountPassword, true

pkg/providers/alibaba/rds/rds_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,10 @@ func TestCreateAccountCreatesReadonlyUserAndGrantsPrivilege(t *testing.T) {
147147
logger.SetOutput(nil)
148148
})
149149

150-
originalAccount := utils.DBAccount
151-
utils.DBAccount = "readonly:Secret!1"
150+
originalAccount := utils.RDSAccount
151+
utils.RDSAccount = "readonly:Secret!1"
152152
t.Cleanup(func() {
153-
utils.DBAccount = originalAccount
153+
utils.RDSAccount = originalAccount
154154
})
155155

156156
var actions []string
@@ -207,10 +207,10 @@ func TestDeleteAccountUsesConfiguredCredential(t *testing.T) {
207207
logger.SetOutput(nil)
208208
})
209209

210-
originalAccount := utils.DBAccount
211-
utils.DBAccount = "readonly:Secret!1"
210+
originalAccount := utils.RDSAccount
211+
utils.RDSAccount = "readonly:Secret!1"
212212
t.Cleanup(func() {
213-
utils.DBAccount = originalAccount
213+
utils.RDSAccount = originalAccount
214214
})
215215

216216
var actions []string

pkg/schema/schema.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,45 @@ type Provider interface {
1414
Name() string
1515
}
1616

17-
// Enumerator powers the cloudlist payload.
17+
// Enumerator powers the asset-inventory (`cloudlist`) payload.
1818
type Enumerator interface {
1919
Provider
2020
Resources(ctx context.Context) (Resources, error)
2121
}
2222

23-
// IAMManager powers the backdoor-user payload.
23+
// IAMManager powers the iam-user-check payload.
2424
type IAMManager interface {
2525
Provider
2626
UserManagement(action, username, password string)
2727
}
2828

29-
// BucketManager powers the bucket-dump payload.
29+
// BucketManager powers the bucket-check payload.
3030
type BucketManager interface {
3131
Provider
3232
BucketDump(ctx context.Context, action, bucketName string)
3333
}
3434

35-
// EventReader powers the event-dump payload.
35+
// EventReader powers the event-check payload.
3636
type EventReader interface {
3737
Provider
3838
EventDump(action, args string)
3939
}
4040

41-
// VMExecutor powers the exec-command / shell payloads.
41+
// VMExecutor powers the instance-cmd-check / shell payloads.
4242
type VMExecutor interface {
4343
Provider
4444
ExecuteCloudVMCommand(instanceID, cmd string)
4545
}
4646

47-
// DBManager powers the database-account payload.
47+
// DBManager powers the rds-account-check payload.
4848
type DBManager interface {
4949
Provider
5050
DBManagement(action, instanceID string)
5151
}
5252

5353
// Asset is any cloud resource that can be enumerated and rendered. New asset
5454
// types (FaaS, K8s clusters, container registries, etc.) only need to
55-
// implement AssetType() to flow through the existing cloudlist pipeline.
55+
// implement AssetType() to flow through the existing asset-inventory pipeline.
5656
type Asset interface {
5757
AssetType() string
5858
}
@@ -111,8 +111,8 @@ func (r Resources) Err() error {
111111
}
112112

113113
// Grouped returns assets partitioned by AssetType() while preserving insertion
114-
// order within each bucket. Used by the cloudlist printer so each asset type
115-
// renders as its own table.
114+
// order within each bucket. Used by the asset-inventory printer so each asset
115+
// type renders as its own table.
116116
func (r *Resources) Grouped() map[string][]Asset {
117117
out := make(map[string][]Asset)
118118
for _, a := range r.Assets {

runner/console/completer.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,18 @@ import (
1111

1212
var core = []prompt.Suggest{
1313
{Text: "help", Description: "help menu"},
14-
{Text: "use", Description: "use module"},
14+
{Text: "use", Description: "use provider"},
1515
{Text: "sessions", Description: "list cache credential"},
1616
{Text: "note", Description: "add remarks to the session"},
1717
{Text: "clear", Description: "clear screen"},
1818
{Text: "exit", Description: "exit console"},
1919
}
2020

2121
var action = []prompt.Suggest{
22-
{Text: "show", Description: "show options"},
23-
{Text: "set", Description: "set option"},
24-
{Text: "run", Description: "run job"},
25-
{Text: "shell", Description: "run commands"},
22+
{Text: "show", Description: "show options or payloads"},
23+
{Text: "set", Description: "set option or parameter"},
24+
{Text: "run", Description: "run selected payload"},
25+
{Text: "shell", Description: "open instance-cmd-check session"},
2626
}
2727

2828
var modules = func() (m []prompt.Suggest) {
@@ -34,10 +34,10 @@ var modules = func() (m []prompt.Suggest) {
3434

3535
var optionsDesc = map[string]string{
3636
// utils.Provider: "Vendor Name",
37-
utils.Payload: "Module Name (Default: cloudlist)",
37+
utils.Payload: "Validation payload (Default: cloudlist)",
3838
utils.AccessKey: "Key ID",
3939
utils.SecretKey: "Secret",
40-
utils.SecurityToken: "Securit Token (Optional)",
40+
utils.SecurityToken: "Security Token (Optional)",
4141
utils.Region: "Region (Default: all)",
4242
utils.Version: "International or custom edition (Optional)",
4343
utils.AzureClientId: "Key ID",
@@ -95,8 +95,8 @@ func actionCompleter(d prompt.Document) []prompt.Suggest {
9595
}
9696
if len(args) == 3 && args[1] == utils.Payload {
9797
getPayloads := func() (p []prompt.Suggest) {
98-
for k, v := range payloads.Payloads {
99-
p = append(p, prompt.Suggest{Text: k, Description: v.Desc()})
98+
for _, entry := range payloads.Visible() {
99+
p = append(p, prompt.Suggest{Text: entry.Name, Description: entry.Payload.Desc()})
100100
}
101101
return
102102
}()

runner/console/executor.go

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -88,36 +88,36 @@ func Executor(s string) {
8888
}
8989

9090
// confirmIfSensitive prompts the user before dispatching payloads that mutate
91-
// cloud state. Read-only payloads (cloudlist, bucket-dump, event-dump.dump)
92-
// bypass the prompt. exec-command is intentionally skipped here so the shell
93-
// REPL, which enters a single confirmation at session start, does not prompt
94-
// on every keystroke.
91+
// cloud state. Read-only payloads (cloud asset inventory via `cloudlist`,
92+
// bucket-check, and event-check in dump mode) bypass the prompt.
93+
// instance-cmd-check is skipped here so the shell REPL, which enters a single
94+
// confirmation at session start, does not prompt on every keystroke.
9595
func confirmIfSensitive(config map[string]string) bool {
96-
payload := config[utils.Payload]
96+
payload := payloads.ResolveName(config[utils.Payload])
9797
metadata := config[utils.Metadata]
9898
parts := argparse.Split(metadata)
9999
provider := config[utils.Provider]
100100

101101
switch payload {
102-
case "backdoor-user":
102+
case "iam-user-check":
103103
if len(parts) < 2 {
104104
return true
105105
}
106-
return confirm.Ask("backdoor-user."+parts[0], provider, parts[1])
107-
case "database-account":
106+
return confirm.Ask("iam-user-check."+parts[0], provider, parts[1])
107+
case "rds-account-check":
108108
if len(parts) < 2 {
109109
return true
110110
}
111-
return confirm.Ask("database-account."+parts[0], provider, parts[1])
112-
case "event-dump":
111+
return confirm.Ask("rds-account-check."+parts[0], provider, parts[1])
112+
case "event-check":
113113
if len(parts) < 1 || parts[0] != "whitelist" {
114114
return true
115115
}
116-
target := ""
116+
resource := ""
117117
if len(parts) >= 2 {
118-
target = parts[1]
118+
resource = parts[1]
119119
}
120-
return confirm.Ask("event-dump.whitelist", provider, target)
120+
return confirm.Ask("event-check.whitelist", provider, resource)
121121
}
122122
return true
123123
}
@@ -141,10 +141,10 @@ func show(args []string) {
141141
}
142142
}
143143
case "payloads":
144-
fmt.Printf("\n%-10s\t%-60s\n", "Payload", "Details")
145-
fmt.Printf("%-10s\t%-60s\n", "-------", "-------")
146-
for k, v := range payloads.Payloads {
147-
fmt.Printf("%-15s\t%-60s\n", k, v.Desc())
144+
fmt.Printf("\n%-24s\t%-60s\n", "Payload", "Details")
145+
fmt.Printf("%-24s\t%-60s\n", "-------", "-------")
146+
for _, entry := range payloads.Visible() {
147+
fmt.Printf("%-24s\t%-60s\n", entry.Name, entry.Payload.Desc())
148148
}
149149
}
150150
}
@@ -153,28 +153,34 @@ func set(args []string) {
153153
if len(args) < 2 {
154154
return
155155
}
156-
if _, ok := config[args[0]]; ok {
157-
if args[0] != utils.Provider {
158-
config[args[0]] = args[1]
159-
fmt.Printf("%s => %s\n", args[0], args[1])
156+
key := args[0]
157+
if _, ok := config[key]; ok {
158+
if key != utils.Provider {
159+
value := args[1]
160+
if key == utils.Payload {
161+
value = payloads.ResolveName(value)
162+
}
163+
config[key] = value
164+
fmt.Printf("%s => %s\n", key, value)
160165
}
161166
}
162-
if args[0] == utils.Payload {
163-
switch args[1] {
164-
case "backdoor-user":
165-
config[utils.Metadata] = utils.BackdoorUser
166-
case "bucket-dump":
167-
config[utils.Metadata] = utils.BucketDump
168-
case "event-dump":
169-
config[utils.Metadata] = utils.EventDump
167+
if key == utils.Payload {
168+
switch config[utils.Payload] {
169+
case "iam-user-check":
170+
config[utils.Metadata] = utils.IAMUserCheck
171+
case "bucket-check":
172+
config[utils.Metadata] = utils.BucketCheck
173+
case "event-check":
174+
config[utils.Metadata] = utils.EventCheck
170175
default:
171176
config[utils.Metadata] = ""
172177
}
173178
}
174179
}
175180

176181
func run(ctx context.Context) {
177-
if v, ok := payloads.Payloads[config[utils.Payload]]; ok {
182+
if v, name, ok := payloads.Lookup(config[utils.Payload]); ok {
183+
config[utils.Payload] = name
178184
v.Run(ctx, config)
179185
} else {
180186
logger.Error("Please type `show payloads` to confirm the required payload.")

0 commit comments

Comments
 (0)