Nest-Writeup

概述 (Overview)

image

HOST:10.10.10.178 时间: 2021-07-29 机器作者: VbScrub 困难程度: easy MACHINE TAGS: * Windows * VBA * File Misconfiguration * Cryptography

枚举(Enumeration)

老规矩,还是通过 Nmap 对目标服务器进行开放端口扫描。这里我用到的是一个集成脚本:

nmapAutomator - https://github.com/21y4d/nmapAutomator

image

Port 4386 - hqk reporting service v1.2

好吧,基本的端口扫描仅识别出了一个 445 端口,尝试使用 Full 参数继续扫描,这次多出来了一个 4386 端口。

image

从对端口的脚本扫描结果中能看出,运行的服务是 hqk reporting service v1.2 且存在交互。使用 telnet 命令连接下该端口:

image

当我们输入 help 命令后会返回内置命令列表及说明。

Port 445 - SMB 2.*

通过 smbclient 命令列举下是否存在为授权访问:

$ smbclient -L \\10.10.10.178 -N Sharename Type Comment --------- ---- ------- ADMIN$ Disk Remote Admin C$ Disk Default share Data Disk IPC$ IPC Remote IPC Secure$ Disk Users Disk SMB1 disabled -- no workgroup available

可以看到是存不常见的目录的。

立足点(Foothold)

在 SMB 共享目录 User 文件夹中可以得到多个用户名称,简单测试下来均不具备文件操作权限:

image

转而查看 Data 文件夹,在内部的 Shared 文件夹中发现两个可读的 TXT 文件,将它下载至本地:

image

image

从提示的文字中我们获悉到一组 HR 的账号密码:

Username: TempUser Password: welcome2019

使用 CrackMapExec 工具枚举密码的 SMB 命令权限:

CrackMapExec - https://github.com/byt3bl33d3r/CrackMapExec

image

从结果中得到该用户对 Data、Users 文件夹具备读取权限,随后使用了 scaner/smb/smb_login 模块来确认下它的密码是否存在复用的情况:

image

为了查看远端文件方便,我使用如下命令将远端的 smb 挂载到本地:

$ sudo mount -t cifs -o 'username=TempUser,password=welcome2019' //10.10.10.178/Users ./TempUser

image

TempUser 文件夹中发现新的文件,可是并没有内容:

image

随后为了查看远端挂载文件夹内的内容,使用 find . 命令进行文件列表。发现这个命令的特性可以使我直接访问 IT 文件夹内的内容:

image

尝试使用 grep -ri 'pass' 命令搜索文件内容含有 pass 字符串的文件:

image

注意,这里有个坑!当对密码进行 base64 解码时,会存在终端编码对特殊内容的不全导致完整内容被隐藏的情况:

$ echo "fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=" | base64 -d }13=XJBAX*Wcf?βc $ echo "fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=" | base64 -d | xxd 00000000: 7d31 3301 f603 a33d 58ce 4aa1 4241 fa19 }13....=X.J.BA.. 00000010: 0158 2a9d 5763 9866 edb8 ce3f ceb2 6311 .X*.Wc.f...?..c.
  • xxd 命令可以为传递的标准输入或者文件做十六进制的输出;

可以看到在十六进制输出内容和可见的 ascii 码字符串存在区别,所以这样的密码我们是没办法直接使用的。

接着翻文件,通过 IT/Configs/NotepadPlusPlus/ 路径判断用户使用的软件是 Notepad++。在它的配置文件 config.xml 中找到绝对路径。

image

通过绝对路径,它让我知道了还有 Carl 这个文件夹。进入 IT/Carl 发现是一个 .NET 程序的项目源代码:

image

继续搜索文件夹内含有 password 字符串的文件,发现存在对 RU_config.xml 文件内加密密码的还原代码段。

image

image

image

OK,看完代码段后找个一个可以在线编写 VB.Net 代码的站点进行尝试:

https://dotnetfiddle.net

构造完整还原明文密码的 Code:

Imports System Imports System.Text Imports System.Security.Cryptography Public Module Module1 Public Sub Main() Console.WriteLine("Decrypted: " + DecryptString("fTEzAfYDoz1YzkqhQkH6GQFYKp1XY5hm7bjOP86yYxE=")) End Sub Public Function DecryptString(EncryptedString As String) As String If String.IsNullOrEmpty(EncryptedString) Then Return String.Empty Else Return Decrypt(EncryptedString, "N3st22", "88552299", 2, "464R5DFA5DL6LE28", 256) End If End Function Public Function EncryptString(PlainString As String) As String If String.IsNullOrEmpty(PlainString) Then Return String.Empty Else Return Encrypt(PlainString, "N3st22", "88552299", 2, "464R5DFA5DL6LE28", 256) End If End Function Public Function Encrypt(ByVal plainText As String, _ ByVal passPhrase As String, _ ByVal saltValue As String, _ ByVal passwordIterations As Integer, _ ByVal initVector As String, _ ByVal keySize As Integer) _ As String Dim initVectorBytes As Byte() = Encoding.ASCII.GetBytes(initVector) Dim saltValueBytes As Byte() = Encoding.ASCII.GetBytes(saltValue) Dim plainTextBytes As Byte() = Encoding.ASCII.GetBytes(plainText) Dim password As New Rfc2898DeriveBytes(passPhrase, _ saltValueBytes, _ passwordIterations) Dim keyBytes As Byte() = password.GetBytes(CInt(keySize / 8)) Dim symmetricKey As New AesCryptoServiceProvider symmetricKey.Mode = CipherMode.CBC Dim encryptor As ICryptoTransform = symmetricKey.CreateEncryptor(keyBytes, initVectorBytes) Using memoryStream As New IO.MemoryStream() Using cryptoStream As New CryptoStream(memoryStream, _ encryptor, _ CryptoStreamMode.Write) cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length) cryptoStream.FlushFinalBlock() Dim cipherTextBytes As Byte() = memoryStream.ToArray() memoryStream.Close() cryptoStream.Close() Return Convert.ToBase64String(cipherTextBytes) End Using End Using End Function Public Function Decrypt(ByVal cipherText As String, _ ByVal passPhrase As String, _ ByVal saltValue As String, _ ByVal passwordIterations As Integer, _ ByVal initVector As String, _ ByVal keySize As Integer) _ As String Dim initVectorBytes As Byte() initVectorBytes = Encoding.ASCII.GetBytes(initVector) Dim saltValueBytes As Byte() saltValueBytes = Encoding.ASCII.GetBytes(saltValue) Dim cipherTextBytes As Byte() cipherTextBytes = Convert.FromBase64String(cipherText) Dim password As New Rfc2898DeriveBytes(passPhrase, _ saltValueBytes, _ passwordIterations) Dim keyBytes As Byte() keyBytes = password.GetBytes(CInt(keySize / 8)) Dim symmetricKey As New AesCryptoServiceProvider symmetricKey.Mode = CipherMode.CBC Dim decryptor As ICryptoTransform decryptor = symmetricKey.CreateDecryptor(keyBytes, initVectorBytes) Dim memoryStream As IO.MemoryStream memoryStream = New IO.MemoryStream(cipherTextBytes) Dim cryptoStream As CryptoStream cryptoStream = New CryptoStream(memoryStream, _ decryptor, _ CryptoStreamMode.Read) Dim plainTextBytes As Byte() ReDim plainTextBytes(cipherTextBytes.Length) Dim decryptedByteCount As Integer decryptedByteCount = cryptoStream.Read(plainTextBytes, _ 0, _ plainTextBytes.Length) memoryStream.Close() cryptoStream.Close() Dim plainText As String plainText = Encoding.ASCII.GetString(plainTextBytes, _ 0, _ decryptedByteCount) Return plainText End Function End Module

执行后成功得到原文密码:Decrypted: xRxRxPANCAK3SxRxRx

使用 CrackMapExec 工具对明文密码组进行登录枚举,发现具有读取 Secure$ 文件夹的权限:

image

最终在该同名用户的文件夹内,成功找到了 User Flag。

image

权限提升(Privilege Escalation)

通过 HQK Reporting 文件夹名称,推测出该文件夹内的配置应该是与 4386 端口运行的服务存在关联。在 HQK_Config_Backup.xml 文件里可以看到该应用的所在路径。

image

这里有一个文件内容被隐藏了。通过 dir 命令列出 Debug Mode Password.txt 文件字节数为0。但对其追加 allinfo 命令后可以看到它是有内容的:

image

这类文件在 Windows 系统中叫数据流,将文件下载至本地后得到新的密码。

image

还记得连接 4386 后输入 help 命令中存在一个password的提示吗?尝试使用这个密码我成功进入了调试模式:

$ telnet 10.10.10.178 4386 Trying 10.10.10.178... Connected to 10.10.10.178. Escape character is '^]'. HQK Reporting Service V1.2 >help This service allows users to run queries against databases using the legacy HQK format --- AVAILABLE COMMANDS --- LIST SETDIR <Directory_Name> RUNQUERY <Query_ID> DEBUG <Password> HELP <Command> >debug WBQ201953D8w Debug mode enabled. Use the HELP command to view additional commands that are now available >help This service allows users to run queries against databases using the legacy HQK format --- AVAILABLE COMMANDS --- LIST SETDIR <Directory_Name> RUNQUERY <Query_ID> DEBUG <Password> HELP <Command> SERVICE SESSION SHOWQUERY <Query_ID> >

通过运行帮助指令,看到多出了一些debug用指令。运行 service 得到当前服务的绝对路径信息:

image

image

运行 list 命令显示出了一些路径信息和文件信息:

image

通过命令提示,我们可以用过 showquery 命令查看 ID 号对应的文件内容:

Current Directory: HQK >showquery 3 <?xml version="1.0"?> <ServiceSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Port>4386</Port> <DebugPassword>WBQ201953D8w</DebugPassword> <QueryDirectory>C:\Program Files\HQK\ALL QUERIES</QueryDirectory> </ServiceSettings>

好吧,这个文件对我们帮助不大。接着翻其他的文件,Ldap.conf 引起了我的注意:

>list Use the query ID numbers below with the RUNQUERY command and the directory names with the SETDIR command QUERY FILES IN CURRENT DIRECTORY [DIR] ALL QUERIES [DIR] LDAP [DIR] Logs [1] HqkSvc.exe [2] HqkSvc.InstallState [3] HQK_Config.xml Current Directory: HQK >setdir LDAP Current directory set to LDAP >list Use the query ID numbers below with the RUNQUERY command and the directory names with the SETDIR command QUERY FILES IN CURRENT DIRECTORY [1] HqkLdap.exe [2] Ldap.conf Current Directory: LDAP >showquery 2 Domain=nest.local Port=389 BaseOu=OU=WBQ Users,OU=Production,DC=nest,DC=local User=Administrator Password=yyEq0Uvvhq2uQOcWG8peLoeRQehqip/fKdeG/kjEVb4=

这里的密码信息被加密了,使用上面 VB.Net 脚本并不能对它进行还原。在 SMB 中找到了与 HqkLdap.exe 同名的文件将其下载至本地进行分析。

smb: \C.Smith\HQK Reporting\AD Integration Module\> get HqkLdap.exe getting file \C.Smith\HQK Reporting\AD Integration Module\HqkLdap.exe of size 17408 as HqkLdap.exe (2.0 KiloBytes/sec) (average 2.0 KiloBytes/sec)

因为是 exe 文件所以需要拖到 Windows 平台上去,先利用 PE 识别工具查下壳。

PETools - https://down.52pojie.cn/Tools/PEtools/die_winxp_portable_3.03.zip

image

从扫描结果中看到它并没有加壳,识别发语言是 .net 。这就很方便了,我们直接使用 dnSpy 工具载入该执行文件进行代码分析。

dnSpy - https://github.com/dnSpy/dnSpy

image

结合代码的上下文内容,成功找到关键代码段:

ldapSearchSettings.Password = CR.DS(text.Substring(text.IndexOf('=') + 1));

跟踪调用的 class,成功在 HqkLdap 命名空间中找到加解密方法:

image

将它提取出来,在先前的在线运行代码的平台上进行构造:

using System; using System.IO; using System.Security.Cryptography; using System.Text; public class Program { private static string RD(string cipherText, string passPhrase, string saltValue, int passwordIterations, string initVector, int keySize) { byte[] bytes = Encoding.ASCII.GetBytes(initVector); byte[] bytes2 = Encoding.ASCII.GetBytes(saltValue); byte[] array = Convert.FromBase64String(cipherText); Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(passPhrase, bytes2, passwordIterations); checked { byte[] bytes3 = rfc2898DeriveBytes.GetBytes((int)Math.Round((double)keySize / 8.0)); ICryptoTransform transform = new AesCryptoServiceProvider { Mode = CipherMode.CBC }.CreateDecryptor(bytes3, bytes); MemoryStream memoryStream = new MemoryStream(array); CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read); byte[] array2 = new byte[array.Length + 1]; int count = cryptoStream.Read(array2, 0, array2.Length); memoryStream.Close(); cryptoStream.Close(); return Encoding.ASCII.GetString(array2, 0, count); } } public static void Main() { Console.WriteLine(RD("yyEq0Uvvhq2uQOcWG8peLoeRQehqip/fKdeG/kjEVb4=", "667912", "1313Rf99", 3, "1L1SA61493DRV53Z", 256)); } }

最终我们得到还原后的明文密码:XtH4nkS4Pl4y1nGX

再次使用 CrackMapExec 指定密码进行 smb 登录枚举,发现它是 administrator 账号的所属密码。

image

使用该密码成功获取 root flag(也可以直接使用 PSExec 工具得到反弹 shell)。

image

参考

  • https://github.com/byt3bl33d3r/CrackMapExec/wiki/SMB-Command-Reference
  • https://dotnetfiddle.net


版权声明

除非另有说明,本网站上的内容均根据 Creative Commons Attribution-ShareAlike License 4.0 International (CC BY-SA 4.0) 获得许可。