• 设为首页
  • 点击收藏
  • 手机APP
    手机扫一扫下载
    华域联盟APP
  • 关注官方公众号
    微信扫一扫关注
    华域联盟公众号

ASP下一句话木马的动态拦截技术

  [复制链接]
sterben 发表于 2016-6-7 23:26:51 | 显示全部楼层 |阅读模式 打印 上一主题 下一主题

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册 新浪微博登陆

x
一句话木马如,攻击者通过组织好的参数访问木马,就可以让参数中的恶意脚本在服务器端执行。为了防止执行恶意脚本,可以在IIS脚本引擎中拦截EVAL函数来过滤非法请求。以下分析都在windows server 2003 x86下调试:
+ A6 l0 ~; `; V2 Y9 I3 vw3wp.exe是在IIS(Internet Information Server)与应用程序池相关联的一个进程,如果你有多个应用程序池,就会有对应的多个w3wp.exe的进程实例运行。当需要解析ASP脚本时会加载vbscript.dll,通过IDA搜索vbscript.dll中和“eval”有关的函数,找到
, r0 b! U( h8 H( L0 [2 e* Mview plaincopy to clipboardprint?
6 _. S: M4 o! q
$ ]- ~) x. x$ D. h6 @
  • VbsEval(VAR *,int,VAR *) .text 7337da95* R- ]" \9 A; Y0 ]
: @& t: d# q8 K  j: _: f* P
在windbg中调试发现脚本执行Eval函数时都会调用到VbsEval,见下图:
6 O" u' P" j" O# P6 I3 @5 K( r3 tURL:
' M5 b' W: C1 M1 t2 u* f0 u- `localhost/test.asp?MH=1*2test.asp& G5 S( E, q  O, z3 }5 {' a. q3 Y! J, F
windbg:
# \# Z! A+ U% Y' ?6 I image001.jpg
0 \! q8 P( }9 M0 H3 H  }4 m在内存中可以看到eval函数执行的脚本。现在只需要将此脚本与URL中请求参数内容对比,如果内存中执行的EVAL脚本在URL中存在则可以认为是URL请求试图调用一句话木马并执行。如上图截取的底层执行代码为1*2,判断1*2是否在localhost/test.asp?MH=1*2中存在,如果存在则直接返回恶意代码提示,最终效果如下图:8 }5 S( x" ~& g6 X* E0 S+ K
image003-300x129.png ) ~! Z5 t$ o5 [/ r
HOOK住vbscript!VbsEval很容易截取到要执行的脚本,现在的问题是任何脚本执行eval都要调用vbseval函数,那么怎么才能找到当前执行脚本是由哪个URL触发的呢?如果无法取到URL则不能与EVAL中执行脚本对比,过滤是无从谈起的。
) o  I/ q0 O! E1 }$ F3 W现在从vbseval函数往上看整个脚本解释执行的流程!
' ~* \9 [: |1 m1 l image005.png asp!CViperAsyncRequest::OnCall断点,堆栈回溯如下:0 {1 }) G$ ]. ?3 b2 f
image007.png
# ?$ {* x9 G6 _# W可以看出脚本执行eval函数是通过启动线程调用回调函数来触发的。但是下新建线程的API都断不下来,需要换个思路。通过上图可以看到线程启动后最先执行函数是asp.dll里的asp!CViperAsyncRequest::OnCall,查看asp.dll的导出函数如下图: image009-300x61.png : {4 j' m6 D& L1 s! @3 n
通过这3个导出函数可以判断asp.dll是ISAPI中的ISA。ISA简介如下:8 c5 A: W: N' b  C- k! F# q
ISA(Internet Server Application)也可称为ISAPI DLL,其功能和CGI程序的功能直接相对应3 U7 Z1 X5 ~+ A0 Y1 [, Z
,使用方法和CGI也类似,由客户端在URL中指定其名称而激活。
" r7 j. M, e. ]  \+ Q6 @ISA和服务器之间的接口主要有两个:GetExtentionVersion( )和HttpExtentionProc( )。任何
9 ]1 Z$ M" y, w1 }/ r6 {ISA都必须在其PE文件头的引出表中定义这两个引出函数,以供Web服务器在适当的时候调用。
  b1 P5 f: G' j- d. J$ OISA大概工作流程如下:
6 q4 G4 y0 @' d$ ~# h1、当服务器刚加载ISA时,它会调用ISA提供的GetExtentionVersion( )来获得该ISA所需要的服8 T2 ?1 w( W7 j" A2 n+ c1 C. \
务器版本,并与自己的版本相比较,以保证版本兼容。
, |# y' h$ v( @; N2、ISA的真正入口是HttpExtentionProc( ),它相当于普通C程序的main( )函数,在这个函数中; N' T) t4 h/ s% a+ `- d  y
根据不同的客户请求作不同的处理。服务器和HttpExtentionProc( )之间是通过扩展控制块(; j- d( C0 J& R. I3 K
Extention Control Block)来进行通信的,即ECB中存放入口参数和出口参数,包括服务器提供9 b! s! o  F1 y, {& t# m7 `
的几个回调函数的入口地址。函数原型如下:
& s7 O; h+ f6 k/ m7 Yview plaincopy to clipboardprint?* Y& }- G0 h+ i: h0 j

( l  M6 k/ h& C4 P6 c. u! b8 s
  • DWORD HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB )
    5 }, Y; q" M' a3 C; f

* h, X7 M9 [0 {" `/ H  xECB的结构定义如下(IN表示入口参数,OUT表示出口参数):
3 f* {# H& Q+ p1 r" X3 B4 hview plaincopy to clipboardprint?
3 ]% v& I$ h; j3 X5 F1 Q( X" i  S3 ?' n8 a7 k) ?
  • typedef struct _EXTENSION_CONTROL_BLOCK
  • {
  •    DWORD     cbSize;        //IN,本结构的大小,只读
  •    DWORD     dwVersion      //IN,版本号,高16位为主版本号,低16位为次版本号
  •    HCONN     ConnID;        //IN,连接句柄,由服务器分配,ISA只能读取该值
  •    DWORD     dwHttpStatusCode;                 //OUT,当前完成的事务状态
  •    CHAR      lpszLogData[HSE_LOG_BUFFER_LEN];  //OUT,需要写入到日志文件中的内容
  •    LPSTR     lpszMethod;    //IN,等价于CGI的环境变量REQUEST_METHOD
  •    LPSTR     lpszQueryString;                  //IN,等价于环境变量QUERY_STRING
  •    LPSTR     lpszPathInfo;                     //IN,等价于环境变量PATH_INFO
  •    LPSTR     lpszPathTranslated;               //IN,等价于环境变量PATH_TRANSLATED
  •    DWORD     cbTotalBytes;                     //IN,等价于环境变量CONTENT_LENGTH
  •    DWORD     cbAvailable;                      //IN,缓冲区中的可用字节数
  •    LPBYTE    lpbData;                          //IN,缓冲区指针,指向客户端发来的数据
  •    LPSTR     lpszContentType;                  //IN,等价于环境变量CONTENT_TYPE
  • //回调函数,用于返回服务器的连接信息或特定的服务器详细情况
  •    BOOL ( WINAPI * GetServerVariable )
  •       ( HCONN       hConn,
  •        LPSTR       lpszVariableName,
  •        LPVOID      lpvBuffer,
  •        LPDWORD     lpdwSize );
  •    BOOL ( WINAPI * WriteClient )      //回调函数,从客户端的HTTP请求中读取数据
  •       ( HCONN      ConnID,
  •       LPVOID     Buffer,
  •       LPDWORD    lpdwBytes,
  •       DWORD      dwReserved );
  •    BOOL ( WINAPI * ReadClient )       //回调函数,向客户端发送数据
  •       ( HCONN      ConnID,
  •       LPVOID     lpvBuffer,
  •       LPDWORD    lpdwSize );
  •    BOOL ( WINAPI * ServerSupportFunction )  //回调函数,访问服务器的一般和特定功能
  •       ( HCONN      hConn,
  •       DWORD      dwHSERRequest,
  •       LPVOID     lpvBuffer,
  •       LPDWORD    lpdwSize,
  •       LPDWORD    lpdwDataType );
  • } EXTENSION_CONTROL_BLOCK, *LPEXTENSION_CONTROL_BLOCK;
    - H; J! O! f$ V& F

' A3 n4 ~# h. p在上述ECB中,服务器不但提供了当前HTTP连接的句柄和一些变量,而且提供了4个回调函数给. B# ^% v! m) O# Z& Q* ^
ISA调用,从而使ISA可以获得更详尽的信息。
$ `: f8 Y, t' c) Z" s上述理论看完后直接上调试器看更清楚,每一次URL请求都会经过HttpExtentionProc函数,因此下HttpExtentionProc断点,查看某一次请求中ECB的结构,如下:
' y* H1 X. Q0 R/ J2 u' U, c% d: \ image011.png
0 g/ \/ |! ?. b" b从上图可以看出ECB中包含请求参数及脚本的物理路径,识别一句话木马时需要这两项内容。怎么才能在vbscript!VbsEval函数中取到这个ECB的结构。, F  w0 x! }8 M1 ]
跟着Httpexecutionproc函数往下看
3 j, ?! m0 ]) N: k( \$ f' u image013.png : X* B; F+ U3 z2 g' o3 x- Y$ `
此时V2的地址是2721e90,内存数据如下:) ~/ b8 \* C1 ?5 t- B2 E- a8 l
image015.png ( a" {) n5 V+ c$ w
在windbg里下vbseval断点,F5继续走断在vbseval,6 d0 C( Y4 k2 H" h& C/ P
70dc75c7 8b4624          mov     eax,dword ptr [esi+24h] ds:0023:026a1eb4=02721e90: k6 M# _: m6 L4 c
70dc75d4 8945fc          mov     dword ptr [ebp-4],eax ss:0023:0130fd88=00000000
) F% ^4 q  [  d' h: C" I image017-300x64.png ) q" u% o3 |3 N) p; t' n/ H2 ]1 C6 K
堆栈回溯中可以看到在Httpexecutionproc中封装的类基址2721e90在函数asp!CViperAsyncRequest::OnCall中被用到,下asp!CViperAsyncRequest::OnCall断点继续往后跟。
0 \, ]1 R' L4 M$ ?: k- ?6 a1 v# e) b
  • ChildEBP RetAddr
  • 0309f560 73333f1c vbscript!VbsEval
  • 0309f574 733365d7 vbscript!StaticEntryPoint::Call+0x11
  • 0309f848 733333e0 vbscript!CScriptRuntime::Run+0x1f08
  • 0309f940 733337d1 vbscript!CScriptEntryPoint::Call+0x5c
  • 0309f998 73333b9c vbscript!CSession::Execute+0xb4
  • 0309f9e8 73331849 vbscript!COleScript::ExecutePendingScripts+0x13e
  • 0309fa04 70dc2ada vbscript!COleScript::SetScriptState+0x150
  • 0309fa30 70dc2a9c asp!CActiveScriptEngine::TryCall+0x19
  • 0309fa6c 70dc26d0 asp!CActiveScriptEngine::Call+0x31
  • 0309fa88 70dc25d4 asp!CallScriptFunctionOfEngine+0x5b
  • 0309fadc 70dc24ff asp!ExecuteRequest+0x17e
  • 0309fb44 70dc23f7 asp!Execute+0x249
  • 0309fb98 70dc2753 asp!CHitObj::ViperAsyncCallback+0x3f3
  • 0309fbb4 4a1db5ea asp!CViperAsyncRequest::OnCall+0x92  ;脚本引擎函数执行起始位置
  • 0309fbd0 77560d30 comsvcs!CSTAActivityWork::STAActivityWorkHelper+0x32
  • 0309fc1c 775617dc ole32!EnterForCallback+0xc4
  • 0309fd7c 775303b4 ole32!SwitchForCallback+0x1a3
  • 0309fda8 774dc194 ole32!PerformCallback+0x54
  • 0309fe40 7756433a ole32!CObjectContext::InternalContextCallback+0x159
  • 0309fe60 4a1db78c ole32!CObjectContext:undefinedoCallback+0x1c
    * O' E# C7 q2 g$ X4 p
) ]- \; C; f% S6 M! E/ @
将以上函数设置断点跟踪2721e90位置,最后得到vbseval函数时ECB结构的地址为:
! u. @! f& b( Idda poi(poi(poi(poi(poi(poi(ebp+8)+5c)+5c)+18)+24)+4)5 i% {7 t5 V7 P. @1 m0 i7 b
image019-300x87.png - x  V+ U) t# S  r
只需要将VbsEval函数HOOK住就可以截取到请求参数内容。  e( o9 X# H4 v; O
通过调试找到了想要的数据后就可以动手编码实现拦截框架了。首先要找到vbseval函数的地址,通过特征码定位是个不错的选择,但是在vbseval函数体内并没有合适的特征码,如下图: image021-1024x623.png ! s) J1 Y  f; q
下面是sub_7337D95F函数:
  z6 C; N8 T. b4 \ image023.png
, q  e! U; J& a3 }5 MCompile函数原型为:- ]2 S9 D& [+ g' [, ?2 f' n# [
view plaincopy to clipboardprint?( |, u3 H! A4 f

$ H* O1 F5 C2 b/ Y  ]5 m. f
  • unsigned int __thiscall COleScript::Compile(void *this, int a2, const OLECHAR *psz, int a4, int a5, UINT len, int a7, int a8, int a9, int a10)
    / t& D  j& B" D2 b( c- t
( J, Q7 e/ A; L1 y
通过调试找到Compile函数内部获取ECB及Eval内容的地址,如下:, b6 o1 R# g. w% H9 w2 `
ECB数据地址:- o6 u' w" G5 }4 c$ P6 J
  • dd poi(poi(poi(poi(ecx+5c)+18)+24)+4)/ t& ]& v" q, r2 \/ c
7 N* X" G2 S( n% n* U# H! _
image026-300x112.jpg 1 K8 Z- m; A2 p+ n2 X; D; b
数据地址找完后剩下的就是编码实现拦截,大致思路是创建自己的ISAPI,在加载时将vbscript.dll提前加载到进程(这样可以加载完vbscript.dll后HOOK住vbseval函数,默认情况下vbscript.dll只有在访问asp页面时才会加载)。然后通过在整个内存中查找“eval code”来定位COlescript!Compile函数地址,将找到的函数地址HOOK住,在新函数中使用上面分析的地址获得相应的值。因为调用COlescript!Compile的函数不止vbseval一个,但是可以通过”eval code”来判断,只有vbseval函数会调用COlescript!Compile函数传递”eval code”字符串变量。因此先判断调用COlescript!Compile传递的参数是不是eval code,如果是则继续判断ECB结构中的请求参数(本例中“MH=1*2”)字符串是否包含VbsEval函数中要执行的脚本(本例中“1*2”),如果包含了则是非法操作,调用ECB中回调函数writeclient向浏览器输出“URL中包含EVAL脚本”等信息,最后要中断COlescript!Compile函数以防止脚本引擎继续执行恶意脚本。中断执行脚本可以将执行的脚本如”1*2″修改为”1″,这样函数可以继续执行而不用担心恶意脚本被执行。8 P9 j# F+ v' u- A7 L: g
需要注意的是,在将vbseval欲执行的恶意脚本内容修改成“1”后,当其它请求同一asp页面并且参数合法的情况下,vbseval函数执行的脚本仍是“1”,可能IIS在加载页面时就将脚本缓存在内存中,因此我们将缓存的页面脚本强制修改成“1”,后面再请求同一页面时会继续执行缓存中的脚本,解决办法是在HOOK函数结尾将asp文件中追加空格等无效字符串,这样IIS去动态更新脚本缓存。' G0 f  o) `8 [+ H  [
另:x64平台下iis启动的都是32位的w3wp.exe,因此此过滤URL方法在x86、x64平台均可使用。- i0 z) S6 i. e# y
最后效果图:: |5 T- Q# M* P
非法情况:
+ B0 {* M% e7 K! {# W image031-300x108.png : q! x: H, `/ v6 }  N4 `- r

; [, f9 ^( v, a正常情况:
4 s. H3 E" F, L0 s" N6 c/ ^' S image033-300x113.png " A! @" j: ]" \: I$ U, \% I

# V7 j, B2 S0 }! j- Hx
You look down on me today, tomorrow I can't let you have been

精彩评论29

kanguuyo 发表于 2016-6-10 08:56:54 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
花传贺 发表于 2016-6-25 09:56:44 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
汎阿青 发表于 2016-6-25 10:24:35 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
登洲远 发表于 2016-6-25 20:57:37 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
汎阿青 发表于 2016-6-25 20:58:21 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
{防腐钢管} 发表于 2016-6-25 21:16:53 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
潮务点 发表于 2016-6-25 21:30:02 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
广翠宪 发表于 2016-6-25 21:51:31 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
宏艳昌 发表于 2016-6-27 10:46:06 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

关注0

粉丝6

帖子3328

发布主题
阅读排行 更多
广告位

扫描微信二维码

关注华域联盟公众号

随时了解更新最新资讯

admin@cnhackhy.com

在线客服(服务时间 9:00~18:00)

在线QQ客服

Powered by cnhackhy! © 2015-2019