Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for China KingSoft Cloud Provider #1521

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ Refer to the [wiki](https://github.com/nccgroup/ScoutSuite/wiki/Setup).

## Usage

- ksyun
```
python scout.py aliyun --access-keys -k <access key> -s <secret access key> --cookie <access cookie>

cookie: kscdigest=xxxxxxxxxxxxxxxxxxxxx
```

- aws
```
$ python scout.py aws --access-keys --access-key-id <AKIAIOSFODNN7EXAMPLE> --secret-access-key <wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY> --session-token <token>
```

- aliyun

```
python scout.py aliyun --access-keys -k <access key> -s <secret access key>
```

Scout Suite is run through the CLI:

![Running Scout Suite](https://user-images.githubusercontent.com/13310971/78389085-22659d00-75b0-11ea-9f22-ea6fcaa6a1cd.gif)
Expand Down
35 changes: 19 additions & 16 deletions ScoutSuite/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ def run_from_cli():
# GCP
project_id=args.get('project_id'), folder_id=args.get('folder_id'),
organization_id=args.get('organization_id'), all_projects=args.get('all_projects'),
# Aliyun
# Aliyun & KsYun
access_key_id=args.get('access_key_id'), access_key_secret=args.get('access_key_secret'),
access_key_cookie=args.get('access_key_cookie'),
# General
report_name=args.get('report_name'), report_dir=args.get('report_dir'),
timestamp=args.get('timestamp'),
Expand Down Expand Up @@ -96,8 +97,8 @@ def run(provider,
# GCP
service_account=None,
project_id=None, folder_id=None, organization_id=None, all_projects=False,
# Aliyun
access_key_id=None, access_key_secret=None,
# Aliyun & KsYun
access_key_id=None, access_key_secret=None, access_key_cookie=None,
# General
report_name=None, report_dir=None,
timestamp=False,
Expand Down Expand Up @@ -148,8 +149,8 @@ async def _run(provider,
# GCP
service_account,
project_id, folder_id, organization_id, all_projects,
# Aliyun
access_key_id, access_key_secret,
# Aliyun & KsYun
access_key_id, access_key_secret, access_key_cookie,
# General
report_name, report_dir,
timestamp,
Expand Down Expand Up @@ -178,9 +179,10 @@ async def _run(provider,
print_info('Launching Scout')

print_info('Authenticating to cloud provider')
# 获取认证策略
auth_strategy = get_authentication_strategy(provider)

try:
# 调用云服务商的authentication_strategy的authenticate方法,获取credentials
credentials = auth_strategy.authenticate(profile=profile,
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
Expand All @@ -198,15 +200,16 @@ async def _run(provider,
username=username,
password=password,
access_key_id=access_key_id,
access_key_secret=access_key_secret)
access_key_secret=access_key_secret,
access_key_cookie=access_key_cookie)

if not credentials:
return 101
except Exception as e:
print_exception(f'Authentication failure: {e}')
return 101

# Create a cloud provider object

try:
cloud_provider = get_provider(provider=provider,
# AWS
Expand All @@ -229,9 +232,9 @@ async def _run(provider,
except Exception as e:
print_exception(f'Initialization failure: {e}')
return 102

# Create a new report
try:
# 报告名称
report_name = report_name if report_name else cloud_provider.get_report_name()
report = ScoutReport(cloud_provider.provider_code,
report_name,
Expand All @@ -257,9 +260,9 @@ async def _run(provider,
# Complete run, including pulling data from provider
if not fetch_local:

# Fetch data from provider APIs
try:
print_info('Gathering data from APIs')
# Fetch data from provider APIs ScoutSuite.providers.ksyun.provider.KsyunProvider
await cloud_provider.fetch(regions=regions, excluded_regions=excluded_regions)
except KeyboardInterrupt:
print_info('\nCancelled by user')
Expand All @@ -272,11 +275,12 @@ async def _run(provider,
if update:
try:
print_info('Updating existing data')
#Load previous results
# Load previous results
last_run_dict = report.encoder.load_from_file('RESULTS')
#Get list of previous services which were not updated during this run
previous_services = [prev_service for prev_service in last_run_dict['service_list'] if prev_service not in cloud_provider.service_list]
#Add previous services
# Get list of previous services which were not updated during this run
previous_services = [prev_service for prev_service in last_run_dict['service_list']
if prev_service not in cloud_provider.service_list]
# Add previous services
for service in previous_services:
cloud_provider.service_list.append(service)
cloud_provider.services[service] = last_run_dict['services'][service]
Expand All @@ -293,8 +297,7 @@ async def _run(provider,
setattr(cloud_provider, key, last_run_dict[key])
except Exception as e:
print_exception('Failure while updating report: {}'.format(e))

# Pre processing
# Preprocessing
try:
print_info('Running pre-processing engine')
cloud_provider.preprocessing(ip_ranges, ip_ranges_name_key)
Expand Down
43 changes: 40 additions & 3 deletions ScoutSuite/core/cli_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
class ScoutSuiteArgumentParser:

def __init__(self):
self.parser = argparse.ArgumentParser(epilog='To get addtional help on a specific provider run: scout.py {provider} -h')
self.parser = argparse.ArgumentParser(
epilog='To get addtional help on a specific provider run: scout.py {provider} -h')

# People will still be able to use the old --provider syntax
self.parser.add_argument("--provider",
Expand All @@ -28,6 +29,7 @@ def __init__(self):
self._init_gcp_parser()
self._init_azure_parser()
self._init_aliyun_parser()
self._init_ksyun_parser()
self._init_oci_parser()

def _init_aws_parser(self):
Expand Down Expand Up @@ -241,6 +243,41 @@ def _init_aliyun_parser(self):
dest='access_key_secret',
help='Access Key Secret')

def _init_ksyun_parser(self):
parser = self.subparsers.add_parser("ksyun",
parents=[self.common_providers_args_parser],
help="Run Scout against an KingSoft Cloud account")

ksyun_parser = parser.add_argument_group('Authentication modes')
ksyun_auth_params = parser.add_argument_group('Authentication parameters')

ksyun_auth_modes = ksyun_parser.add_mutually_exclusive_group(required=True)

ksyun_auth_modes.add_argument('--access-keys',
action='store_true',
help='Run Scout with user credentials')

ksyun_auth_params.add_argument('-k',
'--access-key-id',
action='store',
default=None,
dest='access_key_id',
help='Access Key Id')

ksyun_auth_params.add_argument('-s',
'--access-key-secret',
action='store',
default=None,
dest='access_key_secret',
help='Access Key Secret')

ksyun_auth_params.add_argument('-cookie',
'--access-key-cookie',
action='store',
default=None,
dest='access_key_cookie',
help='Access Key Cookie')

def _init_oci_parser(self):
oci_parser = self.subparsers.add_parser("oci",
parents=[self.common_providers_args_parser],
Expand All @@ -254,7 +291,6 @@ def _init_oci_parser(self):
default=None,
help='Name of the profile')


def _init_common_args_parser(self):
parser = self.common_providers_args_parser.add_argument_group('Scout Arguments')

Expand Down Expand Up @@ -392,7 +428,8 @@ def parse_args(self, args=None):
'and Secret Access Key.')
# Azure
elif v.get('provider') == 'azure':
if v.get('tenant_id') and not (v.get('service_principal') or v.get('user_account_browser') or v.get('user_account')):
if v.get('tenant_id') and not (
v.get('service_principal') or v.get('user_account_browser') or v.get('user_account')):
self.parser.error('--tenant can only be set when using --user-account-browser or --user-account or '
'--service-principal authentication')
if v.get('service_principal') and not v.get('tenant_id'):
Expand Down
10 changes: 7 additions & 3 deletions ScoutSuite/core/conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,15 @@
def pass_conditions(all_info, current_path, conditions, unknown_as_pass_condition=False):
"""
Check that all conditions are passed for the current path.
检查当前路径的所有条件是否都已通过。

:param all_info: All of the services' data
:param all_info: All of the services' data 所有服务的数据
:param current_path: The value of the `path` variable defined in the finding file
查找文件中定义的“路径”变量的值
:param conditions: The conditions to check as defined in the finding file
调查结果文件中定义的要检查的条件
:param unknown_as_pass_condition: Consider an undetermined condition as passed
将未确定的条件视为已通过
:return:
"""

Expand Down Expand Up @@ -185,7 +189,7 @@ def pass_condition(b, test, a):
elif test == 'matchInList':
if type(a) != list:
a = [a]
if type(b) !=list:
if type(b) != list:
b = [b]
for c in a:
for d in b:
Expand Down Expand Up @@ -246,7 +250,7 @@ def pass_condition(b, test, a):
if bottom_limit_port <= port <= upper_limit_port:
result = True
break
else: #A single port
else: # A single port
for port in a:
if port == port_range:
result = True
Expand Down
1 change: 1 addition & 0 deletions ScoutSuite/core/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

def set_logger_configuration(is_debug=False, quiet=False, output_file_path=None):
"""
配置是否应在控制台输出中转储完整堆栈跟踪
Configure whether full stacktraces should be dumped in the console output
"""

Expand Down
9 changes: 8 additions & 1 deletion ScoutSuite/core/processingengine.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
from ScoutSuite.core.utils import recurse


# 整个 ScoutSuite 工具的核心,该类实现了对所有规则的遍历和执行。
class ProcessingEngine:
"""

"""

# 构造函数,接收一个ruleset作为参数
def __init__(self, ruleset):
# Organize rules by path
# 将其按路径整理并储存于self.rules属性中。
self.ruleset = ruleset
self.rules = {}
for filename in self.ruleset.rules:
Expand All @@ -23,6 +26,7 @@ def __init__(self, ruleset):
except Exception as e:
print_exception(f'Failed to create rule {rule.filename}: {e}')

# 遍历所有资源类型和该资源类型下的所有资源,在执行每个规则之前,将清理现有的发现。
def run(self, cloud_provider, skip_dashboard=False):
# Clean up existing findings
for service in cloud_provider.services:
Expand All @@ -31,7 +35,7 @@ def run(self, cloud_provider, skip_dashboard=False):
# Process each rule
for finding_path in self._filter_rules(self.rules, cloud_provider.service_list):
for rule in self.rules[finding_path]:

# 检查规则是否启用,如果未启用,则跳过规则。
if not rule.enabled: # or rule.service not in []: # TODO: handle this...
continue

Expand All @@ -48,10 +52,12 @@ def run(self, cloud_provider, skip_dashboard=False):
cloud_provider.services[service][self.ruleset.rule_type][rule.key][attr] = getattr(rule, attr)
try:
setattr(rule, 'checked_items', 0)
# 构建查找路径并处理规则。为此,它使用了recurse()方法,在该方法中将检查资源以及子资源是否符合规则。
cloud_provider.services[service][self.ruleset.rule_type][rule.key]['items'] = recurse(
cloud_provider.services, cloud_provider.services, path, [], rule, True)
if skip_dashboard:
continue
# 将发现的问题以规定的格式保存在CloudProvider对象的services属性中。
cloud_provider.services[service][self.ruleset.rule_type][rule.key]['dashboard_name'] = \
rule.dashboard_name
cloud_provider.services[service][self.ruleset.rule_type][rule.key]['checked_items'] = \
Expand All @@ -73,6 +79,7 @@ def run(self, cloud_provider, skip_dashboard=False):
cloud_provider.services[service][self.ruleset.rule_type][rule.key]['checked_items'] = 0
cloud_provider.services[service][self.ruleset.rule_type][rule.key]['flagged_items'] = 0

# ProcessingEngine类的一个静态方法。它的作用是根据services列表中的资源名称过滤规则,并返回一个过滤后的规则字典。
@staticmethod
def _filter_rules(rules, services):
return {rule_name: rule for rule_name, rule in rules.items() if rule_name.split('.')[0] in services}
3 changes: 2 additions & 1 deletion ScoutSuite/core/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
]


# 规则类,用于执行规则的检测
class Rule:

def to_string(self):
Expand All @@ -51,7 +52,7 @@ def get_attribute(name, rule, default_value):
def set_definition(self, rule_definitions, attributes=None, ip_ranges=None, params=None):
"""
Update every attribute of the rule by setting the argument values as necessary

通过根据需要设置参数值来更新规则的每个属性
:param rule_definitions: TODO
:param attributes: TODO
:param ip_ranges: TODO
Expand Down
4 changes: 3 additions & 1 deletion ScoutSuite/core/rule_definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
from ScoutSuite.core.console import print_error, print_exception


# 规则定义类,用于定义规则的名称、描述、严重程度、检测条件等信息。
class RuleDefinition:

def __init__(self, data_path, file_name=None, rule_dirs=None, string_definition=None):
# 初始化方法,用于初始化规则定义类的各个属性。
rule_dirs = [] if rule_dirs is None else rule_dirs
self.rules_data_path = data_path
self.file_name = file_name
Expand Down Expand Up @@ -35,7 +37,7 @@ def __str__(self):
def load(self):
"""
Load the definition of the rule, searching in the specified rule dirs first, then in the built-in definitions

加载规则的定义,首先在指定的规则目录中搜索,然后在内置定义中
:return: None
"""
file_name_valid = False
Expand Down
Loading