Nasim平台、攻防博弈
以下内容整理供自己学习。
Nasim场景定义
Nasim中的场景定义了创建一个网络环境所需要的所有必要属性。每个场景定义可以被分解成两部分:网络配置,渗透测试者。
网络配置
网络配置由以下属性定义:
- 子网:包括子网的数量和大小
- 拓扑:网络中子网的连接方式
- 主机配置:网络中所有主机的IP,OS,Services,processes,firewall
- 防火墙:用来阻止子网间的通信
渗透测试者
- 漏洞利用:渗透测试者可以利用的漏洞集合
- 特权利用:渗透测试者可以利用的权限升级动作集合
- 扫描成本:指定每种类型扫描的成本
- 敏感主机:网络中的目标主机和它们的价值
e.g. 示例场景如下,其中渗透测试者的目标是获得敏感子网中的服务器和用户子网中的主机之一的root权限。
从图中,我们可以看到网络具有如下属性:
(1) 子网:网络中存在3个子网——DMZ区域、Sensitive区域、User区域
(2) 拓扑:只有DMZ区域可以连接到Internet,网络中的所有子网都是互联的
(3)主机配置:每个主机的运行地址、操作系统、服务和进程都显示在主机旁,主机的防火墙设置显示在右上角
(4)防火墙:防火墙上方和下面的箭头表示子网之间以及DMZ子网和互联网之间可以在每个方向上通过哪些服务进行通信。
定义渗透测试者
(1)漏洞利用:场景定义了三个漏洞,渗透测试人员可以进行利用
①ssh_exploit: 利用在windows上的ssh服务,(2,0.6,user)
②ftp_exploit: 利用在linux上的ftp服务,(1,0.9,root)
③http_exploit: 利用http服务,(3,1.0,user)
(2)特权利用
①pe_tomcat:利用运行在linux机器上的tomcat进程,(1,1.0,root)
②pe_daclsvc:利用运行在windows机器上的daclsvc进程来,(1,1.0,root)
(3)扫描成本
①service_scan:1
②os_scan:2
③process_scan:1
④subnet_scan:1
(4)敏感主机:这里我们由两个目标主机
①(2,0):1000
②(3,2):1000
在这个场景中,我们定义了攻击模拟所需要的一切。
创建自定义场景
使用Nasim,我们可以通过YAML文件来自定义场景。下一节展开。
自定义网络场景
定义子网的数量和大小,这里表示定义了3个大小均为1的子网,地址中第一个子网的地址为1.
第二个子网的地址为2,地址0为“互联网”子网所保留。
subnets:[1,1,1]
拓扑通过邻接矩阵来定义,其中网络中的每个子网都有一行和一列,以及指定“互联网”子网的
附加行和列,即是与网络外部的连接。第一行第一列为“互联网”子网所保留,子网之间的连接
用1表示,非连接用0表示,我们假设连接是对称的,并且网络与其自身相连接。
topology:
[[ 1, 1, 0, 0],
[ 1, 1, 1, 1],
[ 0, 1, 1, 1],
[ 0, 1, 1, 1]]
主机配置:OS, services, processes
os:
- linux
services: - ssh
processes: - tomcat
主机配置部分是从主机地址到其配置的映射,其中地址是一个(subnet number, host number)元组,配置必须包括操作系统、服务、进程和可选的主机防火墙设置。
在定义主机时需要注意以下几点:
- 为每个子网定义的主机数量需要与子网的大小匹配
- 子网中的主机地址必须从0开始
- 任何操作系统、服务和进程的名称必须与YAML文件的系统、服务和进程中提供的值匹配
- 每个主机都必须由一个操作系统和至少一个服务,没有进程是可以的
主机防火墙允许的流量仍然可能被子网防火墙所过滤。
主机值是代理在破坏主机时将收到的可选值,可以是正或负值,这使得设置额外的主机特定奖励与惩罚成为可能。
主机值是可选的,默认为0,对于任何敏感主机来说,不指定该值,或者它必须与文件中指定的值匹配。
与敏感主机一样,代理只有在破坏主机时才会收到作为奖励的价值。
如下,有一个简单的主机配置:
host_configurations:
(1, 0):
os: linux
services: [ssh]
processes: [tomcat]
firewall:
(3, 0): [ssh]
value: 0
(2, 0):
os: linux
services: [ssh]
processes: [tomcat]
firewall:
(1, 0): [ssh]
(3, 0):
os: linux
services: [ssh]
processes: [tomcat]
防火墙配置
定义网络的最后一部分是防火墙,它被定义为从(子网号,子网号)元组到允许服务列表的映射。
防火墙规则只能在拓扑邻接矩阵中连接的子网之间定义。每个规则定义了哪些服务在一个方向上被允许,如果列表为空,则代表源子网到目标子网的所有流量都将被阻止。
firewall:
(0, 1): [ssh]
(1, 0): []
(1, 2): []
(2, 1): [ssh]
(1, 3): [ssh]
(3, 1): [ssh]
(2, 3): [ssh]
(3, 2): [ssh]
自定义渗透测试者
sensitive_hosts:包含敏感/目标主机地址及其值的字典
exploits:漏洞字典
privilege_escalation:权限提升操作字典
os_scan_cost:使用操作系统扫描的成本
service_scan_cost:使用服务扫描的成本
process_scan_cost:使用进程扫描的成本
subnet_scan_cost:使用子网扫描的成本
step_limit:渗透测试人员在单集中可以执行的最大操作数
敏感主机:
sensitive_hosts:
(2, 0): 100
(3, 0): 100
漏洞利用:
- service:漏洞利用目标的服务名称。 请注意,该值必须与网络定义的服务部分中定义的服务名称相匹配。
- os:漏洞利用目标的操作系统的名称,如果漏洞利用在所有操作系统上都有效,则没有。 如果该值不是 none,则它必须与网络定义的 os 部分中定义的操作系统的名称匹配
- prob:在满足所有先决条件的情况下,利用成功的概率(即目标主机被发现且可达,并且主机正在运行目标服务和操作系统)
- cost:执行动作的成本。这应该是一个非负整数或浮点数,并且可以在任何期望的意义上表示操作的成本(财务、时间、产生的流量等)
- access:如果漏洞利用成功,渗透测试人员将获得目标主机的访问权限。这可以是用户或 root。
漏洞的名称可以是任何你想要的,只要它们是不可变的和可散列的(即字符串、整数、元组)并且是唯一的。
exploits:
e_ssh:
service: ssh
os: linux
prob: 0.8
cost: 1
access: user
权限提升:
- process:操作目标的流程名称。 该值必须与网络定义的进程部分中定义的进程的名称相匹配。
- os:操作目标的操作系统名称,如果漏洞在所有操作系统上都有效,则没有。 如果该值不是 none,则它必须与网络定义的 os 部分中定义的操作系统的名称匹配。
- prob:在满足所有先决条件的情况下操作成功的概率(即渗透测试人员可以访问目标主机,并且主机正在运行目标进程和操作系统)
- cost:执行动作的成本。这应该是一个非负整数或浮点数,并且可以在任何期望的意义上表示操作的成本(财务、时间、产生的流量等)
- access:如果操作成功,渗透测试人员将获得目标主机的访问权限。这可以是用户或 root。
与漏洞利用类似,每个特权漏洞利用操作的名称可以是您想要的任何名称,只要它们是不可变的和可散列的(即字符串、整数、元组)并且是唯一的。
privilege_escalation:
pe_tomcat:
process: tomcat
os: linux
prob: 1.0
cost: 1
access: root
扫描成本:
service_scan_cost: 1
os_scan_cost: 1
subnet_scan_cost: 1
process_scan_cost: 1
步数限制:
步数限制定义了渗透测试者在单集中达到目标的最大步数(即动作)。在模拟过程中,一旦达到步数限制,则认为该情节已完成,代理未能达到目标。
step_limit: 1000
Nasim动作类学习
NASim环境的动作相关类包含ActionSpace类和ActionResult类。每个动作都继承自基类Action,基类定义了一些常见的属性和功能,不能类型的动作被实现为动作类的子类。实现的动作类型有:
Exploit,PrivilegeEscalation,ServiceScan,OSScan,SubnetScan,ProcessScan,NoOp
ActionSpace有两种类型的操作空间,取决于是否使用平面操作:
FlatActionSpace,ParameterisedActionSpace
1
class nasim.envs.action.Action(name, target,cost,prob=1.0,req_access=AccessLevel.USER,**kwargs)
环境中的基本抽象动作类,有多种类型的动作(例如扫描和利用等),但每个动作都有一些共同的属性。
__init__(name, target,cost,prob=1.0,req_access=AccessLevel.USER,**kwargs)
- name ( str ) – 动作名称
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- is_exploit()检查是否是漏洞利用,如果操作被利用,则为True,否则为False
- is_noop()检查动作是否是无所事事的动作,如果action是noop动作,则为True,否则为False
- is_os_scan()检查操作是否是操作系统扫描,如果操作是操作系统扫描,则为True,否则为False
- is_privilege_escalation()检查操作是否是提权操作,如果操作是提权操作,则为True,否则为False
- is_process_scan()检查操作是否是进程扫描,如果操作时进程扫描,则为True,否则为False
- is_remote()检查动作是否是远程动作,远程操作时目标主机是远程主机的操作(即该操作不在目标本地执行),如果操作是远程的,则为True,否则为False。
- is_scan()检查操作是否为扫描,如果动作是扫描则为真,否则为False
- is_service_scan()检查操作是否是服务扫描,如果操作是服务扫描,则为True,否则为False
- is_subnet_scan()检查操作是否是子网扫描,如果操作是子网扫描,则为True,否则为False
2
用于存储动作结果的数据类,然后将这些结果用于更新完整状态和观察结果。
class nasim.envs.action.ActionResult(success, value=0.0, services=None, os=None, processes=None, access=None, discovered=None, connection_error=False, permission_error=False, undefined_error=False, newly_discovered=None)
3
__init__(success, value=0.0, services=None, os=None, processes=None, access=None, discovered=None, connection_error=False, permission_error=False, undefined_error=False, newly_discovered=None)
参数:
- success ( bool ) – 如果利用/扫描成功则为 True,否则为 False
- value ( float , optional ) – 从动作中获得的值 (default=0.0)
- services ( dict , optional ) – 由操作标识的服务 (default=None={})
- os ( dict , optional ) – 由操作标识的操作系统 (default=None={})
- processes ( dict , optional ) – 由操作标识的进程 (default=None={})
- access ( dict , optional ) – 通过操作获得的访问权限 (default=None={})
- discovered( dict , optional ) – 通过操作发现的主机地址 (default=None={})
- connection_error ( bool , optional ) – 如果操作因连接错误而失败,则为 True(默认 = False)
- permission_error ( bool , optional ) – 如果操作因权限错误而失败,则为 True(默认 = False)
- undefined_error ( bool , optional ) – 如果操作因未定义的错误而失败,则为真(默认值=False)
- new_discovered ( dict , optional ) – 首次通过操作发现的主机地址(默认=None)
- info()以字典形式获取结果,返回行动结果信息,dict
4
环境中的Exploit操作,从基本动作类继承。
class nasim.envs.action.Exploit(name, target, cost, service, os=None, access=0, prob=1.0, req_access=AccessLevel.USER, **kwargs)
__init__(name, target, cost, service, os=None, access=0, prob=1.0, req_access=AccessLevel.USER, **kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- service ( str ) – 目标服务
- os ( str , optional ) – 漏洞利用的目标操作系统,如果没有,则漏洞利用适用于所有操作系统(默认=无)
- access ( int , optional ) – 如果利用成功,则在目标上获得的访问级别(默认 = 0)
- prob ( float , optional ) – 成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- class nasim.envs.action.FlatActionSpace(scenario)
5
NASim环境的平面操作空间。继承并实现了gym.spaces.Discrete。
__init__(scenario)
参数:
- scenario (Scenario) – 场景描述
- n(int) – 动作空间中的动作数量
- actions(list of Actions) – 动作空间中的动作列表
- get_action(action_idx)获取action idx对应的Action对象,Action
class nasim.envs.action.NoOp(*args, **kwargs)
在环境中什么都不做,从基本动作类继承。
__init__ (* args , ** kwargs )
参数:
- name ( str ) – 动作名称
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- *class nasim.envs.action.OSScan(target, cost, prob=1.0, req_access=AccessLevel.USER, *kwargs)
6
环境中的OS扫描操作,从基本动作类继承。
__init__(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- class nasim.envs.action.ParameterisedActionSpace(scenario)
7
NASim 环境的参数化动作空间。继承并实现了gym.spaces.MultiDiscrete动作空间,其中每个维度对应一个不同的动作参数。动作参数(按顺序)是:
- 动作类型=[0,5],0为利用,1为权限升级,2为服务扫描,3为系统扫描,4为子网扫描,5为进程扫描
- 子网=[0,#subnets-1] -1因为我们不包括互联网子网
- 主机=[0, max subnets size-1]
- 操作系统=[0, #OS]
- 服务=[0, #services - 1]
- 进程=[0, #processes]
请注意,操作系统、服务和进程仅对漏洞利用和权限提升操作很重要。
__init__(scenario)
参数:
- Scenario(Scenario)—场景描述
- nvec (Numpy.Array) — 每个参数的向量大小
- actions (list of Actions) — 动作空间中所有动作的列表
- get_action ( action_vector ) (整数列表或整数元组或Numpy.Array)获取动作向量对应的动作对象,返回Action。
注意:
- 如果host#指定的动作向量大于指定子网中的主机数,则host#将会被变为host# %子网大小。
- 如果操作是一个漏洞,并且参数与场景描述中的任何漏洞定义不匹配,则返回一个 NoOp 操作,成本为 0
8
环境中的权限提升操作,从基本动作类继承。
class nasim.envs.action.PrivilegeEscalation(name, target, cost, access, process=None, os=None, prob=1.0, req_access=AccessLevel.USER, **kwargs)
__init__(name, target, cost, access, process=None, os=None, prob=1.0, req_access=AccessLevel.USER, **kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- access ( int ) – 权限提升产生的访问级别
- process ( str , optional ) – 目标进程,如果 None 动作不需要进程工作(默认=None)
- os ( str , optional ) – 提权操作的目标操作系统,如果没有,则操作适用于所有操作系统(默认=无)
- prob ( float , optional ) – 成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- class nasim.envs.action.ProcessScan(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
环境中的进程扫描操作,从基本动作类操作。
__init__(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- class nasim.envs.action.ServiceScan(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
环境中的服务扫描操作,从基本动作类继承。
__init__(target, cost, prob=1.0, req_access=AccessLevel.USER,**kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- class nasim.envs.action.SubnetScan(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
环境中的子网扫描操作,从基本动作类继承。
__init__(target, cost, prob=1.0, req_access=AccessLevel.USER, **kwargs)
参数:
- target ( ( int , int ) ) – 目标地址
- cost ( float ) – 执行动作的成本
- prob ( float , optional ) – 给定操作的成功概率(默认=1.0)
- req_access ( AccessLevel , optional ) – 执行操作所需的访问级别(默认=AccessLevel.USER)
- nasim.envs.action.load_action_list(scenario)
加载给定场景的环境操作列表。返回环境中的所有动作列表,list。