看雪的号注册了将近4年,不过从来没在这里发过帖子,实在是遗憾。因为个人技术成长路线的选择,当初没有选择直接杀入安全领域,但是业余一直都在做这方面的事情。开发出身,当年的想法是先走开发,因为觉得开发是作为一个计算机从业者的必备的基本能力,等开发的功底积累的差不多了再转型专攻安全领域应该是比较不错的发展路线,而周围几乎清一色的好朋友都是直接踏上了安全的不归路。走到现在,觉得该转型了,这两天就做了一些尝试,想在公司内部转岗去做安全,可是却被公司的一些硬性规定阻拦了,有点失落。
但是终归说不是因为能力的原因,我也只能静静的等待了,等到我的个人条件达到了我还是要勇敢的朝着安全的道路前进的。
第一篇帖子不知道该发什么内容好,发一些奇技淫巧的小技术在这里也只能让各位大牛觉得陈芝麻烂谷子了,所以就把最近在做的一个小的项目拿出来跟大家分享一下。
看过张东辉写的各种0Day挖掘的文章资料,其中有讲Fuzzer工具的开发,于是就整理了一下资料里所介绍的Fuzz工具的原理,决定自己实现一个Fuzz工具,但是真正实现起来的时候发现文中所讲的MITM中间人Fuzz工具的逻辑实现有点问题,比如说MITM Fuzz中主要以Process Name或者PID,DeviceName或者DriverName,IoControlCode三个因素作为一个Fuzz条目,但是如果有两个Fuzz条目都具有一个相同要素(比如PID),而其他两个要素不同,那在做Fuzz的时候到底要以哪个Fuzz条目为准来进行Fuzz呢?思考了很久,最初决定配个优先级,后来觉得这个问题属于同质化的工作,个人觉得MITM原理是否可以再简化一下呢?所以直接导致我的Fuzz工具走向了Driver Fuzz的分支。
TsFuzzer的设计思路:
驱动层TsFuzzerk.sys
1.InlineHook NtDeviceIoControlFile函数(必须的...)
2.用一个单链表维护所有的Fuzz Item
3.通过IoControlCode对应用层提供Fuzz Item链表的管理维护,以及Fuzz功能的开关
其中Fuzz Item根据Device Name作为索引,
可以进行的Fuzz的参数包括InputBuffer的内容和InputBuffer的长度,
Fuzz的方式有随机,固定值,保持不变三种。
应用层TsFuzzer
用WTL框架实现一个GUI的管理控制界面,与TsFuzzerk.sys进行通信。
目前的大致思路就是这样的,开发进度是TsFuzzerk的基本框架已经搭建完毕,并且实现了一个Console的应用层测试程序。
在这里贴出Fuzz处理函数的实现:
/********************************************************************
created: 2013/02/26
created: 25:2:2013 20:29
filename: h:\TsFuzzerk\TsFuzzerk\TsFuzzerkFuzzHandler.cpp
file path: h:\TsFuzzerk\TsFuzzerk
file base: TsFuzzerkFuzzHandler
file ext: cpp
author: tishion@163.com
purpose: The implementation of fuzz handler
*********************************************************************/
#include "TsFuzzerk.h"
#include "SysTypedef.h"
#include "TsFuzzerkFuzzItemList.h"
extern PVOID
g_pNtDeviceIoControlFile;
extern CTsFuzzerkFuzzItemList*
g_pFuzzItemList;
/*
* A stub to jmp to the original NtDeviceIoControlFile function after we
* made it into our fake function.
*/
NTSTATUS __declspec(naked) NtDeviceIoControlFile_Stub(
IN HANDLE FileHandle,
IN HANDLE Event,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength)
{
_asm
{
mov edi, edi
push ebp
mov ebp, esp
mov eax, g_pNtDeviceIoControlFile
add eax, 5
jmp eax
}
}
#define _MAX_NAME_STRING 1024/sizeof(WCHAR)
typedef struct _NAME_STRING {
UNICODE_STRING Name;
WCHAR Buffer[_MAX_NAME_STRING];
} NAME_STRING, *PNAME_STRING;
//PsGetProcessPeb 取得指定EPROCESS的Peb地址
typedef PPEB (__stdcall *FUNP_PSGETPROCESSPEB)(PEPROCESS pEProcess);
PPEB PsGetProcessPebWrapper(PEPROCESS pEProcess)
{
UNICODE_STRING ustrFunName;
FUNP_PSGETPROCESSPEB fpPsGetProcessPeb = NULL;
RtlInitUnicodeString(&ustrFunName, (PWCHAR)L"PsGetProcessPeb");
fpPsGetProcessPeb = (FUNP_PSGETPROCESSPEB)MmGetSystemRoutineAddress(&ustrFunName);
if (fpPsGetProcessPeb == NULL)
{
return NULL;
}
return fpPsGetProcessPeb(pEProcess);
}
#define STRING_POOL_BUFFER_LEN 512*1024
CHAR g_Format[] = {
"[=======================================TsFuzzer=======================================]\n" \
"\tProcess:%wZ PID:0x%08X\n" \
"\tDevice:%wZ Driver:%wZ\n" \
"\tIOCTL Code:0x%08X Method:%s\n" \
"\t++++++Parameters Before Fuzz+++++\n" \
"\tIBuffer:0x%08X Length:0x%08X\n" \
"\tOBuffer:0x%08X Length:0x%08X\n" \
"\tInput Data:"
};
CHAR g_Format_after[] = {
"\t------Parameters After Fuzz------\n" \
"\tIBuffer:0x%08X Length:0x%08X\n" \
"\tOBuffer:0x%08X Length:0x%08X\n" \
"\tInput Data:"
};
PCHAR g_IoMethod[] = {
"METHOD_BUFFERED", // 0
"METHOD_IN_DIRECT", // 1
"METHOD_OUT_DIRECT", // 2
"METHOD_NEITHER", // 3
};
NTSTATUS Fake_NtDeviceIoControlFile(
IN HANDLE FileHandle,
IN HANDLE Event,
IN PIO_APC_ROUTINE ApcRoutine,
IN PVOID ApcContext,
OUT PIO_STATUS_BLOCK IoStatusBlock,
IN ULONG IoControlCode,
IN PVOID InputBuffer,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer,
IN ULONG OutputBufferLength)
{
NTSTATUS status = STATUS_SUCCESS;
//KdPrint(("[TsFuzzerk]:++++Fake_NtDeviceIoControlFile()\n"));
ULONG ulControlCode = 0;
ULONG ulPid = 0;
NAME_STRING ImageName;
ImageName.Name.Buffer = ImageName.Buffer;
ImageName.Name.Length = 0;
ImageName.Name.MaximumLength = sizeof(ImageName.Buffer);
NAME_STRING devName;
devName.Name.Buffer = devName.Buffer;
devName.Name.Length = 0;
devName.Name.MaximumLength = sizeof(devName.Buffer);
NAME_STRING drvPath;
drvPath.Name.Buffer = drvPath.Buffer;
drvPath.Name.Length = 0;
drvPath.Name.MaximumLength = sizeof(drvPath.Buffer);
// 获取DeviceName DriverName DriverPath
PFILE_OBJECT pFileObject = NULL;
ULONG ulReturnedLength = 0;
status = ObReferenceObjectByHandle(
FileHandle,
STANDARD_RIGHTS_ALL,
*IoFileObjectType,
KernelMode,
(PVOID*)&pFileObject,
NULL);
if (NT_SUCCESS(status))
{
status = ObQueryNameString(
pFileObject->DeviceObject,
(POBJECT_NAME_INFORMATION)&devName,
devName.Name.MaximumLength,
&ulReturnedLength);
if (!NT_SUCCESS(status))
{
RtlStringCchPrintfW(devName.Buffer, _MAX_NAME_STRING, L"Unknown");
RtlInitUnicodeString(&(devName.Name), devName.Buffer);
}
PLDR_DATA_TABLE_ENTRY pLdr = (PLDR_DATA_TABLE_ENTRY)
(pFileObject->DeviceObject->DriverObject->DriverSection);
if (NULL != pLdr && NULL != pLdr->FullDllName.Buffer && 0 != pLdr->FullDllName.Length)
{
RtlCopyUnicodeString(&(drvPath.Name), &(pLdr->FullDllName));
}
else
{
RtlStringCchPrintfW(drvPath.Buffer, _MAX_NAME_STRING, L"Unknown");
RtlInitUnicodeString(&(drvPath.Name), drvPath.Buffer);
}
ObDereferenceObject(pFileObject);
}
PFUZZ_ITEM pFuzzListItem = g_pFuzzItemList->GetItemByDeviceName(&(devName.Name));
if (NULL != pFuzzListItem)
{
// 获取IoControlCode
ulControlCode = IoControlCode;
if ((0 == pFuzzListItem->IoControlCode) || (ulControlCode == pFuzzListItem->IoControlCode))
{
// 获取进程镜像文件路径和PID
ulPid = (ULONG)PsGetCurrentProcessId();
PPEB pPeb = NULL;
PEPROCESS pEprocess = NULL;
pEprocess = PsGetCurrentProcess();
if (NULL != pEprocess)
{
pPeb = PsGetProcessPebWrapper(pEprocess);
if (NULL != pPeb)
{
RtlCopyUnicodeString(&(ImageName.Name), &(pPeb->ProcessParameters->ImagePathName));
}
else
{
RtlStringCchPrintfW(ImageName.Buffer, _MAX_NAME_STRING, L"Unknown");
RtlInitUnicodeString(&(ImageName.Name), ImageName.Buffer);
}
}
PCHAR pszStringBuffer = (PCHAR)ExAllocatePoolWithTag(
NonPagedPool, STRING_POOL_BUFFER_LEN, 'FUBS');
if (NULL != pszStringBuffer)
{
RtlStringCchPrintfA(pszStringBuffer, STRING_POOL_BUFFER_LEN, g_Format,
&(ImageName.Name), ulPid,
&(devName.Name), &(drvPath.Name),
ulControlCode, g_IoMethod[METHOD_FROM_CTL_CODE(ulControlCode)],
InputBuffer, InputBufferLength,
OutputBuffer, OutputBufferLength);
}
ULONG ulDataStringLength = (InputBufferLength * 4);
ulDataStringLength = ulDataStringLength + (((InputBufferLength / 0x10) + 1) * 0x10);
PCHAR pszDataBuffer = (PCHAR)ExAllocatePoolWithTag(
NonPagedPool, ulDataStringLength, 'FUBD');
if (pszDataBuffer != NULL)
{
pszDataBuffer[0] = '\0';
CHAR ByteData[] = {"00 "};
CHAR AddressData[] = {"00000000 "};
PUCHAR pInputBuffer = (PUCHAR)InputBuffer;
for (ULONG i = 0; i < InputBufferLength; i++)
{
if (0 == i % 0x10)
{
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, "\n\t");
RtlStringCchPrintfA(AddressData, sizeof(AddressData), "%08x ", (ULONG)InputBuffer + i);
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, AddressData);
}
RtlStringCchPrintfA(ByteData, sizeof(ByteData), "%02X ", pInputBuffer[i]);
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, ByteData);
}
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, "\n");
}
DbgPrint(pszStringBuffer);
DbgPrint(pszDataBuffer);
// 在此处进行参数Fuzz
if (FuzzMethod_FillWithRandomData == pFuzzListItem->fmInputBuffer)
{
LARGE_INTEGER liSystemTime;
KeQuerySystemTime(&liSystemTime);
for (ULONG i = 0; i < InputBufferLength / sizeof(ULONG); i++)
{
((PULONG)InputBuffer)[i] = RtlRandomEx(&(liSystemTime.LowPart));
}
}
else if (FuzzMethod_FillWithFixedDWord == pFuzzListItem->fmInputBuffer)
{
for (ULONG i = 0; i < InputBufferLength / sizeof(ULONG); i++)
{
((PULONG)InputBuffer)[i] = pFuzzListItem->FixedDWord;
}
}
if (pszDataBuffer != NULL)
{
pszDataBuffer[0] = '\0';
CHAR ByteData[] = {"00 "};
CHAR AddressData[] = {"00000000 "};
PUCHAR pInputBuffer = (PUCHAR)InputBuffer;
for (ULONG i = 0; i < InputBufferLength; i++)
{
if (0 == i % 0x10)
{
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, "\n\t");
RtlStringCchPrintfA(AddressData, sizeof(AddressData), "%08x ", (ULONG)InputBuffer + i);
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, AddressData);
}
RtlStringCchPrintfA(ByteData, sizeof(ByteData), "%02X ", pInputBuffer[i]);
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, ByteData);
}
RtlStringCchCatA(pszDataBuffer, ulDataStringLength, "\n");
}
if (FuzzMethod_ReplaceByRandomLength == pFuzzListItem->fmInputLength)
{
LARGE_INTEGER liSystemTime;
KeQuerySystemTime(&liSystemTime);
InputBufferLength = RtlRandomEx(&(liSystemTime.LowPart));
}
else if (FuzzMethod_ReplaceByFixedLength == pFuzzListItem->fmInputLength)
{
InputBufferLength = pFuzzListItem->FixedLength;
}
if (NULL != pszStringBuffer)
{
RtlStringCchPrintfA(pszStringBuffer, STRING_POOL_BUFFER_LEN, g_Format_after,
InputBuffer, InputBufferLength,
OutputBuffer, OutputBufferLength);
}
DbgPrint(pszStringBuffer);
DbgPrint(pszDataBuffer);
if (NULL != pszStringBuffer)
{
ExFreePoolWithTag(pszStringBuffer, 'FUBS');
}
if (NULL != pszDataBuffer)
{
ExFreePoolWithTag(pszDataBuffer, 'FUBD');
}
}
}
status = NtDeviceIoControlFile_Stub(
FileHandle,
Event,
ApcRoutine,
ApcContext,
IoStatusBlock,
IoControlCode,
InputBuffer,
InputBufferLength,
OutputBuffer,
OutputBufferLength);
//KdPrint(("[TsFuzzerk]:----Fake_NtDeviceIoControlFile()\n"));
return status;
}
程序还有很多可以挑剔的地方,不足之处只能慢慢来弥补,在开发到现在的进度之后我拿出了IOCTL_Fuzzer的源码来对比参考,看完之后瞬间觉得自己的代码写的太无脑了,没有考虑到的问题太多了!!!就拿上述代码中的Fake_NtDeviceIoControlFile中没有考虑KerneMode和UserMode变换,再比如在Hook的时候没有考虑到去从原始Image文件获取目标函数地址等等。看来自己在驱动开发的道路上还有很大的学习空间,只能加油了。不过作为一个开发出身的人,在代码风格和文件模块划分上自认为还是比较规范的。
================工程文件==========
TsFuzzerk.zip
TsFuzzerTest.zip可能下一次发篇文章介绍visual studio 2008 + WDK搭建驱动开发环境,敬请期待。
[课程]Linux pwn 探索篇!