diff --git a/README.md b/README.md index 7e7a59a..69e0d5b 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ LC(List Cloud)是一个多云攻击面资产梳理的工具,使用 LC 可 | 10 | 百度云 | BCC 云服务器 | | 11 | 联通云 | OSS 对象存储 | | 12 | 七牛云 | Kodo 对象存储 | +| 13 | 移动云 | EOS 对象存储 | ## 使用手册 diff --git a/cmd/configFile.go b/cmd/configFile.go index 5db8eee..9288915 100644 --- a/cmd/configFile.go +++ b/cmd/configFile.go @@ -68,4 +68,12 @@ const defaultConfigFile = `# # lc (list cloud) 的云服务商配置文件 # id: qiniu_cloud_default # access_key: # secret_key: + +# # 移动云 +# # 访问凭证获取地址:https://console.ecloud.10086.cn/api/page/eos-console-web/CIDC-RP-00/eos/key +# - provider: yidong +# id: yidong_cloud_default +# access_key: +# secret_key: +# session_token: ` diff --git a/pkg/inventory/inventory.go b/pkg/inventory/inventory.go index eb2ede9..3db7e98 100644 --- a/pkg/inventory/inventory.go +++ b/pkg/inventory/inventory.go @@ -9,6 +9,7 @@ import ( "github.com/wgpsec/lc/pkg/providers/qiniu" "github.com/wgpsec/lc/pkg/providers/tencent" "github.com/wgpsec/lc/pkg/providers/tianyi" + "github.com/wgpsec/lc/pkg/providers/yidong" "github.com/wgpsec/lc/pkg/schema" "github.com/wgpsec/lc/utils" ) @@ -50,6 +51,8 @@ func nameToProvider(value string, block schema.OptionBlock) (schema.Provider, er return liantong.New(block) case utils.QiNiu: return qiniu.New(block) + case utils.YiDong: + return yidong.New(block) default: return nil, fmt.Errorf("发现无效的云服务商名: %s", value) } diff --git a/pkg/providers/liantong/liantong.go b/pkg/providers/liantong/liantong.go index c49fde5..e3cbf06 100644 --- a/pkg/providers/liantong/liantong.go +++ b/pkg/providers/liantong/liantong.go @@ -29,9 +29,12 @@ func New(options schema.OptionBlock) (*Provider, error) { return nil, &utils.ErrNoSuchKey{Name: utils.SecretKey} } id, _ := options.GetMetadata(utils.Id) - sessionToken, _ := options.GetMetadata(utils.SessionToken) - - gologger.Debug().Msg("找到联通云访问永久访问凭证") + sessionToken, stsOk := options.GetMetadata(utils.SessionToken) + if stsOk { + gologger.Debug().Msg("找到联通云临时访问凭证") + } else { + gologger.Debug().Msg("找到联通云永久访问凭证") + } config := providerConfig{ accessKeyID: accessKeyID, @@ -50,7 +53,7 @@ func (p *Provider) ID() string { } func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { - ossProvider := &s3Provider{config: p.config, id: p.id, provider: p.provider} + ossProvider := &ossProvider{config: p.config, id: p.id, provider: p.provider} buckets, err := ossProvider.GetResource(ctx) if err != nil { return nil, err diff --git a/pkg/providers/liantong/oss.go b/pkg/providers/liantong/oss.go index 5dcd780..f624fa2 100644 --- a/pkg/providers/liantong/oss.go +++ b/pkg/providers/liantong/oss.go @@ -11,7 +11,7 @@ import ( "sync" ) -type s3Provider struct { +type ossProvider struct { id string provider string config providerConfig @@ -24,7 +24,7 @@ type regions struct { var list = schema.NewResources() -func (d *s3Provider) GetResource(ctx context.Context) (*schema.Resources, error) { +func (d *ossProvider) GetResource(ctx context.Context) (*schema.Resources, error) { var ( threads int err error @@ -68,7 +68,7 @@ func (d *s3Provider) GetResource(ctx context.Context) (*schema.Resources, error) } -func (d *s3Provider) listBuckets(ch <-chan regions, wg *sync.WaitGroup) error { +func (d *ossProvider) listBuckets(ch <-chan regions, wg *sync.WaitGroup) error { defer wg.Done() var err error for region := range ch { diff --git a/pkg/providers/yidong/eos.go b/pkg/providers/yidong/eos.go new file mode 100644 index 0000000..e5baf9f --- /dev/null +++ b/pkg/providers/yidong/eos.go @@ -0,0 +1,171 @@ +package yidong + +import ( + "context" + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/projectdiscovery/gologger" + "github.com/wgpsec/lc/pkg/schema" + "strings" + "sync" +) + +type eosProvider struct { + id string + provider string + config providerConfig +} + +type regions struct { + region string + endpoint string +} + +var list = schema.NewResources() +var resourcePools = []regions{ + {region: "shanghai1", endpoint: "eos-shanghai-1.cmecloud.cn"}, + {region: "shanghai2", endpoint: "eos-shanghai-2.cmecloud.cn"}, + {region: "shanghai3", endpoint: "eos.shanghai-3.cmecloud.cn"}, + {region: "wuxi1", endpoint: "eos-wuxi-1.cmecloud.cn"}, + {region: "suzhou4", endpoint: "eos-suzhou-4.cmecloud.cn"}, + {region: "fenhu1", endpoint: "eos.fenhu-1.cmecloud.cn"}, + {region: "wuxi5", endpoint: "eos-wuxi-5.cmecloud.cn"}, + {region: "ningbo1", endpoint: "eos-ningbo-1.cmecloud.cn"}, + {region: "ningbo6", endpoint: "eos.ningbo-6.cmecloud.cn"}, + {region: "jinan1", endpoint: "eos-jinan-1.cmecloud.cn"}, + {region: "jinan4", endpoint: "eos.jinan-4.cmecloud.cn"}, + {region: "guangzhou1", endpoint: "eos-guangzhou-1.cmecloud.cn"}, + {region: "dongguan1", endpoint: "eos-dongguan-1.cmecloud.cn"}, + {region: "dongguan7", endpoint: "eos-dongguan-7.cmecloud.cn"}, + {region: "dongguan8", endpoint: "eos-dongguan-8.cmecloud.cn"}, + {region: "chengdu1", endpoint: "eos-chengdu-1.cmecloud.cn"}, + {region: "chengdu6", endpoint: "eos-chengdu-6.cmecloud.cn"}, + {region: "guiyang1", endpoint: "eos-guiyang-1.cmecloud.cn"}, + {region: "guiyang4", endpoint: "eos-guiyang-4.cmecloud.cn"}, + {region: "chongqing1", endpoint: "eos-chongqing-1.cmecloud.cn"}, + {region: "chongqing3", endpoint: "eos-chongqing-3.cmecloud.cn"}, + {region: "xian1", endpoint: "eos-xian-1.cmecloud.cn"}, + {region: "xian2", endpoint: "eos.xian-2.cmecloud.cn"}, + {region: "beijing1", endpoint: "eos-beijing-1.cmecloud.cn"}, + {region: "beijing2", endpoint: "eos-beijing-2.cmecloud.cn"}, + {region: "beijing4", endpoint: "eos-beijing-4.cmecloud.cn"}, + {region: "beijing7", endpoint: "eos-beijing-7.cmecloud.cn"}, + {region: "huhehaote1", endpoint: "eos-huhehaote-1.cmecloud.cn"}, + {region: "huhehaote6", endpoint: "eos-huhehaote-6.cmecloud.cn"}, + {region: "hunan1", endpoint: "eos-hunan-1.cmecloud.cn"}, + {region: "zhuzhou1", endpoint: "eos-zhuzhou-1.cmecloud.cn"}, + {region: "zhengzhou1", endpoint: "eos-zhengzhou-1.cmecloud.cn"}, + {region: "zhengzhou4", endpoint: "eos.zhengzhou-4.cmecloud.cn"}, + {region: "tianjin1", endpoint: "eos-tianjin-1.cmecloud.cn"}, + {region: "tianjin2", endpoint: "eos.tianjin-2.cmecloud.cn"}, + {region: "jilin1", endpoint: "eos-jilin-1.cmecloud.cn"}, + {region: "jilin2", endpoint: "eos.jilin-2.cmecloud.cn"}, + {region: "hubei1", endpoint: "eos-hubei-1.cmecloud.cn"}, + {region: "hubei2", endpoint: "eos.hubei-2.cmecloud.cn"}, + {region: "jiangxi1", endpoint: "eos-jiangxi-1.cmecloud.cn"}, + {region: "jiangxi2", endpoint: "eos.jiangxi-2.cmecloud.cn"}, + {region: "gansu1", endpoint: "eos-gansu-1.cmecloud.cn"}, + {region: "gansu2", endpoint: "eos.gansu-2.cmecloud.cn"}, + {region: "shanxi1", endpoint: "eos-shanxi-1.cmecloud.cn"}, + {region: "shanxi2", endpoint: "eos.shanxi-2.cmecloud.cn"}, + {region: "shanxi3", endpoint: "eos.shanxi-3.cmecloud.cn"}, + {region: "liaoning1", endpoint: "eos-liaoning-1.cmecloud.cn"}, + {region: "liaoning2", endpoint: "eos.liaoning-2.cmecloud.cn"}, + {region: "yunnan", endpoint: "eos-yunnan.cmecloud.cn"}, + {region: "yunnan2", endpoint: "eos-yunnan-2.cmecloud.cn"}, + {region: "hebei1", endpoint: "eos-hebei-1.cmecloud.cn"}, + {region: "hebei2", endpoint: "eos.hebei-2.cmecloud.cn"}, + {region: "fujian1", endpoint: "eos-fujian-1.cmecloud.cn"}, + {region: "fujian2", endpoint: "eos.fujian-2.cmecloud.cn"}, + {region: "fujian3", endpoint: "eos.fujian-3.cmecloud.cn"}, + {region: "guangxi1", endpoint: "eos-guangxi-1.cmecloud.cn"}, + {region: "anhui1", endpoint: "eos-anhui-1.cmecloud.cn"}, + {region: "anhui2", endpoint: "eos.anhui-2.cmecloud.cn"}, + {region: "hainan1", endpoint: "eos-hainan-1.cmecloud.cn"}, + {region: "heilongjiang1", endpoint: "eos-heilongjiang-1.cmecloud.cn"}, + {region: "heilongjiang2", endpoint: "eos.heilongjiang-2.cmecloud.cn"}, + {region: "xinjiang1", endpoint: "eos-xinjiang-1.cmecloud.cn"}, + {region: "xinjiang2", endpoint: "eos.xinjiang-2.cmecloud.cn"}, + {region: "ningxia1", endpoint: "eos.ningxia-1.cmecloud.cn"}, + {region: "qinghai1", endpoint: "eos.qinghai-1.cmecloud.cn"}, + {region: "xizang1", endpoint: "eos.xizang-1.cmecloud.cn"}, +} + +func (d *eosProvider) GetResource(ctx context.Context) (*schema.Resources, error) { + var ( + threads int + err error + wg sync.WaitGroup + buckets []string + ) + + config := aws.NewConfig() + config.WithRegion("beijing1") + config.WithEndpoint("https://eos-beijing-1.cmecloud.cn") + config.WithCredentials(credentials.NewStaticCredentials(d.config.accessKeyID, d.config.accessKeySecret, d.config.sessionToken)) + session, err := session.NewSession(config) + if err != nil { + return nil, err + } + s3Client := s3.New(session) + + listBucketsOutput, err := s3Client.ListBuckets(nil) + if err != nil { + return nil, err + } + for _, bucket := range listBucketsOutput.Buckets { + buckets = append(buckets, *bucket.Name) + } + gologger.Debug().Msgf("找到 %d 个移动云 EOS 资源", len(buckets)) + + threads = schema.GetThreads() + + taskCh := make(chan string, threads) + for i := 0; i < threads; i++ { + wg.Add(1) + go func() { + err = d.listBuckets(taskCh, &wg, s3Client) + if err != nil { + return + } + }() + } + for _, item := range buckets { + taskCh <- item + } + close(taskCh) + wg.Wait() + return list, nil + +} + +func (d *eosProvider) listBuckets(ch <-chan string, wg *sync.WaitGroup, s3Client *s3.S3) error { + defer wg.Done() + var err error + for bucket := range ch { + bucketLocation, err := s3Client.GetBucketLocation(&s3.GetBucketLocationInput{ + Bucket: aws.String(bucket), + }) + if err != nil { + continue + } + gologger.Debug().Msgf("%s 的 Location 值为 %s", bucket, *bucketLocation.LocationConstraint) + endpointBuilder := &strings.Builder{} + endpointBuilder.WriteString(bucket) + for _, resourcePool := range resourcePools { + if *bucketLocation.LocationConstraint == resourcePool.region { + endpointBuilder.WriteString("." + resourcePool.endpoint) + } + } + + list.Append(&schema.Resource{ + ID: d.id, + Public: true, + DNSName: endpointBuilder.String(), + Provider: d.provider, + }) + } + return err +} diff --git a/pkg/providers/yidong/yidong.go b/pkg/providers/yidong/yidong.go new file mode 100644 index 0000000..145abf4 --- /dev/null +++ b/pkg/providers/yidong/yidong.go @@ -0,0 +1,66 @@ +package yidong + +import ( + "context" + "github.com/projectdiscovery/gologger" + "github.com/wgpsec/lc/pkg/schema" + "github.com/wgpsec/lc/utils" +) + +type Provider struct { + id string + provider string + config providerConfig +} + +type providerConfig struct { + accessKeyID string + accessKeySecret string + sessionToken string +} + +func New(options schema.OptionBlock) (*Provider, error) { + accessKeyID, ok := options.GetMetadata(utils.AccessKey) + if !ok { + return nil, &utils.ErrNoSuchKey{Name: utils.AccessKey} + } + accessKeySecret, ok := options.GetMetadata(utils.SecretKey) + if !ok { + return nil, &utils.ErrNoSuchKey{Name: utils.SecretKey} + } + id, _ := options.GetMetadata(utils.Id) + sessionToken, stsOk := options.GetMetadata(utils.SessionToken) + + if stsOk { + gologger.Debug().Msg("找到移动云临时访问凭证") + } else { + gologger.Debug().Msg("找到移动云永久访问凭证") + } + + config := providerConfig{ + accessKeyID: accessKeyID, + accessKeySecret: accessKeySecret, + sessionToken: sessionToken, + } + return &Provider{id: id, provider: utils.YiDong, config: config}, nil +} + +func (p *Provider) Name() string { + return p.provider +} + +func (p *Provider) ID() string { + return p.id +} + +func (p *Provider) Resources(ctx context.Context) (*schema.Resources, error) { + eosProvider := &eosProvider{config: p.config, id: p.id, provider: p.provider} + buckets, err := eosProvider.GetResource(ctx) + if err != nil { + return nil, err + } + gologger.Info().Msgf("获取到 %d 条移动云 EOS 信息", len(buckets.GetItems())) + finalList := schema.NewResources() + finalList.Merge(buckets) + return finalList, nil +} diff --git a/utils/const.go b/utils/const.go index e98a09f..0d8ca33 100644 --- a/utils/const.go +++ b/utils/const.go @@ -16,4 +16,5 @@ const ( Baidu = "baidu" LianTong = "liantong" QiNiu = "qiniu" + YiDong = "yidong" )