题目链接
https://gslab.qq.com/html/competition/2020/race-pre.htm
RIng3
题目
winmine.exe是一个扫雷游戏程序,winmine.dmp是该程序的一份进程dump, 在这份dump中,winmine.exe的内存映像有指令被篡改,篡改实现了外挂功能。
- 请找出dump中,winmine.exe的内存映像中2处被篡改实现外挂功能的指令(被篡改指令的偏移、篡改前后的指令分别是什么),并分析这些指令篡改所实现的外挂功能是什么
猜测
由于是扫雷外挂,无非是修改时间、修改判定条件,修改雷区等,因此针对这几个函数进行对比即可知道修改处
修改效果
时间不动,点雷不爆炸
第一处修改 偏移0x2fe0
原指令:inc time
,就是把时间+1,修改后时间停止不动
第二处修改 0x358b
原指令压入0,调用sub_100347C,被修改位为jmp winmine+0x35b0 (010035b0),直接跳过爆炸,效果即是点雷也不会结束游戏
Ring0
题目
DriverDemo.sys是一个驱动程序,它内置了一些限制。
-
不能篡改该文件,尝试使驱动成功加载。(3分)
-
该驱动程序成功加载后,突破它的限制,但不允许patch文件或内存,使它成功打印出(用dbgview可接受)调试信息"hello world!".
参考
pizza大佬太强了!
前期准备
DbgView
需要设置Capture->Capture Kernel
才能捕获到@hello world
虚拟机设置
必须设为单核单线程,不然会出现以下情况(T为单步步入调试)
过TP反双机调试
ew KdDisableDebugger 0xc390
知识点
x64 调用约定
64位平台下只有一种变形的__fastcall的调用约定,前4参数则先放入ecx、edx、r8、r9寄存器,更多的参数放入栈区
MmGetSystemRoutine
返回指向系统程序名称指定的函数的指针
语法
c++
PVOID MmGetSystemRoutineAddress(
PUNICODE_STRING SystemRoutineName
);
参数SystemRoutineName需要返回地址的函数名
KdDisableDebugger
KdDisableDebugger例程禁用内核调试器
_UNICODE_STRING
c
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWCHAR Buffer;
}UNICODE_STRING,*PUNICODE_STRING;
ZwOpenKey
ZwOpenKey例程打开现有注册表项
语法
c++
NTSYSAPI NTSTATUS ZwOpenKey(
PHANDLE KeyHandle,
ACCESS_MASK DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes
);
其中:
KeyHandle 指向接收密钥句柄的 HANDLE 变量的指针
DesiredAccess 定确定请求对对象的访问的ACCESS_MASK值
ObjectAttributes 指向指定对象名称和其他属性的OBJECT_ATTRIBUTES结构的指针。
ObjectAttributes
c++
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
PVOID RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
ZwQueryValueKey
例程返回注册表项的值条目
语法
NTSYSAPI NTSTATUS ZwQueryValueKey(
HANDLE KeyHandle,
PUNICODE_STRING ValueName,
KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
PVOID KeyValueInformation,
ULONG Length,
PULONG ResultLength
);
其中
```
KeyHandle
处理从中读取值条目的键。此句柄是通过成功调用ZwCreateKey或ZwOpenKey创建的。
ValueName
指向值条目的名称以获取其数据的指针。
ResultLength
指向接收密钥信息的大小(以字节为单位)的变量的指针。如果ZwQueryValueKey例程返回STATUS_SUCCESS,则调用方可以使用此变量的值来确定返回的数据量。如果例程返回STATUS_BUFFER_OVERFLOW或STATUS_BUFFER_TOO_SMALL,调用方可以使用此变量的值来确定保存密钥信息所需的缓冲区大小。
```
IoCreateNotifyEvent
IoCreateNotifyEvent例程创建或打开命名通知事件,用于通知一个或多个执行线程事件已发生
语法
PKEVENT IoCreateNotificationEvent(
PUNICODE_STRING EventName,
PHANDLE EventHandle
);
KeResetEven
KeResetEvent 例程将指定的事件对象重置为未发出信号状态,并返回该事件对象的上一个状态
语法
LONG KeResetEvent(
PRKEVENT Event
);
PsCreateSystemThread
PsCreateSystemThread例程创建一个系统线程,该线程在内核模式下执行并返回线程的句柄
语法
c++
NTSTATUS PsCreateSystemThread(
PHANDLE ThreadHandle,
ULONG DesiredAccess,
POBJECT_ATTRIBUTES ObjectAttributes,
HANDLE ProcessHandle,
PCLIENT_ID ClientId,
PKSTART_ROUTINE StartRoutine,
PVOID StartContext
);
```
StartRoutine 线程中要执行的函数
StartContext 传递给上述要执行的函数的参数
```
DbgPrint
DbgPrint例程向内核调试器发送消息
KeWaitForSingleObject
KeWaitForSingleObject例程将当前线程置于等待状态,直到给定调度程序对象设置为信号状态或(可选)直到等待超时
语法
c++
NTSTATUS KeWaitForSingleObject (
PVOID Object,
KWAIT_REASON WaitReason,
KPROCESSOR_MODE WaitMode,
BOOLEAN Alertable,
PLARGE_INTEGER Timeout
);
Object 指向初始化调度程序对象(事件、互斥、信号量、线程或计时器)的指针,调用方为其提供存储。
WaitReason 为6的意思
UserRequest 6 线程正在等待用户请求。
分析
反调试
IDA打开sys,发现加了vmp2壳,查看导入表,还是还是有一些导入表的
双机调试,由于驱动没有签名,所以需要开启禁用驱动程序强制签名
,接着用Driver Monitor
加载驱动,断在MmGetSystemRoutineAddress
(断点需要提前设置)
1: kd> bp MmGetSystemRoutineAddress
1: kd> g
Breakpoint 0 hit
nt!MmGetSystemRoutineAddress:
fffff803`95a50510 4053 push rbx
查看参数
1: kd> dt _UNICODE_STRING @rcx
ntdll!_UNICODE_STRING
"KdDisableDebugger"
+0x000 Length : 0x22
+0x002 MaximumLength : 0x24
+0x008 Buffer : 0xffff828a`9df7f7c0 "KdDisableDebugger"
发现调用了KdDisableDebugger
反调试函数,断在KdDisableDebugger
,直接将KdDisableDebugger
改为ret
跳过反调试
1: kd> ew KdDisableDebugger 0xc390
1: kd> bp KdDisableDebugger
1: kd> g
Breakpoint 1 hit
nt!KdDisableDebugger:
fffff803`95620ac0 90 nop
1: kd> u KdDisableDebugger KdDisableDebugger+1
nt!KdDisableDebugger:
fffff803`95620ac0 90 nop
fffff803`95620ac1 c3 ret
也可以采用Pizza大佬的方法,直接修改rip绕过反调试
1: kd> r rip=poi(rsp)+11
1: kd> u @rip @rip
fffff803`94a11158 c3 ret
读取注册表
接着单步跟进,一直到发现有大量的赋值操作。
1: kd> u fffff802`eb8d1189 fffff802`eb8d1189+0x500
fffff802`eb8d1189 b85c000000 mov eax,5Ch
fffff802`eb8d118e 66898424b0000000 mov word ptr [rsp+0B0h],ax
fffff802`eb8d1196 b852000000 mov eax,52h
fffff802`eb8d119b 66898424b2000000 mov word ptr [rsp+0B2h],ax
fffff802`eb8d11a3 b845000000 mov eax,45h
fffff802`eb8d11a8 66898424b4000000 mov word ptr [rsp+0B4h],ax
fffff802`eb8d11b0 b847000000 mov eax,47h
fffff802`eb8d11b5 66898424b6000000 mov word ptr [rsp+0B6h],ax
跳过这段以后,由于基本是可见字符,看一下字符串是什么:
1: kd> du @rsp+A0
ffffa889`49b0f720 "key"
1: kd> du @rsp+B0 @rsp+158
ffffa889`49b0f730 "REGISTRYMACHINESOFTWAREAppDa"
ffffa889`49b0f770 "taLowTencent{61B942F7-A946-458"
ffffa889`49b0f7b0 "5-B624-B2C0228FFEBC}"
既为:
```
key
REGISTRYMACHINESOFTWAREAppDataLowTencent{61B942F7-A946-4585-B624-B2C0228FFEBC}
```
看着像个注册表的位置,继续跟踪进入ZwOpenKey
1: kd> t
nt!ZwOpenKey:
fffff801`d1245300 488bc4 mov rax,rsp
1: kd> dt _OBJECT_ATTRIBUTES @r8
nt!_OBJECT_ATTRIBUTES
+0x000 Length : 0x30
+0x008 RootDirectory : (null)
+0x010 ObjectName : 0xfffff98d`b9e9f6e0 _UNICODE_STRING "REGISTRYMACHINESOFTWAREAppDataLowTencent{61B942F7-A946-4585-B624-B2C0228FFEBC}"
+0x018 Attributes : 0x240
+0x020 SecurityDescriptor : (null)
+0x028 SecurityQualityOfService : (null)
既用ZwOpenKey例程打开现有注册表项
由于我们没有所以要新建一个
打开成功后,用ZwQueryValueKey例程返回注册表项key
的值条目
1: kd> g
Breakpoint 4 hit
nt!ZwQueryValueKey:
fffff801`d12453a0 488bc4 mov rax,rsp
1: kd> dt _UNICODE_STRING @rdx
nt!_UNICODE_STRING
"key"
+0x000 Length : 6
+0x002 MaximumLength : 8
+0x008 Buffer : 0xfffff98d`b9e9f720 "key"
那么我们新建key = 0xA
以供读取
1: kd> r @rax
rax=000000000000000a
查看返回值,可以发现程序已读取了我们的值
即为读取了注册表项 REGISTRYMACHINESOFTWAREAppDataLowTencent{61B942F7- A946-4585-B624-B2C0228FFEBC} 的键 key 的值, 不为1则驱动加载失败
我们回到注册表把key的值改为1
,驱动成功加载,但是没有输出@helloworld,继续跟踪
事件设置
驱动加载成功后创建Event,名称为tp2020
1: kd> g
Breakpoint 2 hit
nt!IoCreateNotificationEvent:
fffff801`e26fa6f0 488bc4 mov rax,rsp
1: kd> dt _UNICODE_STRING @rcx
ntdll!_UNICODE_STRING
"BaseNamedObjectstp2020"
+0x000 Length : 0x30
+0x002 MaximumLength : 0x32
+0x008 Buffer : 0xfffff803`03092140 "BaseNamedObjectstp2020"
根据函数返回指向事件的指针,查看该指针
0: kd> pt
nt!KeResetEvent:
fffff801`e26fa78d c3 ret
1: kd> r @rax
rax=ffffdb8ede8c9fe0
接着把这个事件重置
1: kd> g
Breakpoint 5 hit
nt!KeResetEvent:
fffff801`e21d39e0 48895c2410 mov qword ptr [rsp+10h],rbx
1: kd> r @rcx
rcx=ffffdb8ede8c9fe0
接着创建线程,查看执行函数(StartRoutine)
1: kd> g
Breakpoint 4 hit
nt!PsCreateSystemThread:
fffff803`0093f9f0 4c8bdc mov r11,rsp
kd> u poi(@rsp+8*6)
fffff802`06d91c70 48894c2408 mov qword ptr [rsp+8],rcx
fffff802`06d91c75 4883ec28 sub rsp,28h
fffff802`06d91c79 488b4c2430 mov rcx,qword ptr [rsp+30h]
fffff802`06d91c7e e81d000000 call fffff802`06d91ca0
fffff802`06d91c83 eb00 jmp fffff802`06d91c85
fffff802`06d91c85 33c9 xor ecx,ecx
fffff802`06d91c87 e83d720000 call fffff802`06d98ec9
fffff802`06d91c8c cc int 3
调用KeWaitForSingleObject
等待之前的Event,如果Event被设置则打印helloworld
用process hacker打开system进程,找到句柄中名称tp2020
的Event,直接设置,然后在DbgView看到helloworld
注:本文相关漏洞均已报告 SRC 并完成修复 前言 近几年工作转型,已经很久没挖过漏洞,突然想检验一下自己当前是否还有挖洞能力。因此本次以挖到某个大型厂商一个中危级别以上漏洞作为目标,最终挖到了一个低危 (存储 xss ) + 一个高危 (任意文件读取) ,完…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论