• 一次失败的svchost hacking


      事缘前几天需要做HOOK LPC通信拦截服务的创建等相关服务操作。而后好奇svchost share类型的服务是如何动态启动的,这里强调动态,只要是相对静态来说,大家都知道svchost.exe是在启动的时候读取注册表

    HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SvcHost,取得各个组和相关的服务名,接着用服务名到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services

    获取相关的服务配置信息,最后把这些服务加载启动起来。

      为了了解svchost.exe如何动态加载share类型的服务,调试并查找了相关资料后,发现是service.exe通过pipe通信触发svchost动态加载的。所以总的来说,从我们的程序到svchost加载我们的dll服务过程的通信是这样的:myapp.exe->service.exe是rpc base lpc通信的,而service.exe->svchost是通过pipe通信的,在这里只详解下后者。

      myapp.exe通信触发service.exe的ScStartService()函数,ScStartService函数则取得服务名testsrv,和服务的imagepath文件路径,ScStartService函数中判断testsrv这个服务的类型是不是share,如果是,继续调用函数ScGetImageFileName(unsigned __int16 *ServiceName, unsigned __int16 **ImageNamePtr)判断这个imagepath是不是曾经启动过(service.exe会有一个全局链表slist1保存启动过的imagepath),这里分2种情况:

    1。为了下面述说方便,我先说如果木有启动过的情况,当service.exe在那个slist1中找不到的话,service.exe就会ScLogonAndStartImage()函数使用相应的身份如local system调用createprocess来启动imagepath这个服务,一般是"%SystemRoot%\system32\svchost.exe -k xxxx" 。回过头来,在ScLogonAndStartImage函数里面会首先调用ScCreateControlInstance创建一管道

    #define CONTROL_PIPE_NAME           L"\\\\.\\pipe\\net\\NtControlPipe"
    status = ScWriteCurrentServiceValue((unsigned int *)&dwServiceID);
    if ( status )
    goto ExitAccountError;
    v16 = ScCreateControlInstance(&pipeHandle, dwServiceID, ServiceSid);
    status = v16;
    

    这个管道的名字是CONTROL_PIPE_NAME拼上dwServiceID,如\\\\.\\pipe\\net\\NtControlPipe1,\\\\.\\pipe\\net\\NtControlPipe2。。。。

    后面的数据是记录在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceCurrent中,每用完一次,则会加1。

    创建这个管道后,就得到了一个pipe handle ,service.exe会把imagepath和这个pipe handle对应的保存在那个slist中。

    回到上面说的,createprocess启动svchost.exe后,svchost会默认到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ServiceCurrent中也读取这个值,也拼出service.exe的那个 pipename。

    最后打开,并连接上去。

    2。这是第二种service.exe在slist中找到imagepath的情况,动态加载的话,一般是这种情况,既然进入第二种情况的话,那第一种情况必须已经发生过了,所以这时候svchost.exe已经连接上那个pipe并且在监听等待service.exe传过去的指令。所以在这种情况下,service.exe只需要根据imagepath搜索slist找出对应的pipe handle。

      无论哪种情况,service.exe最后就得到了对应的pipe并且对应的svchost.exe已经在waitfor这个pipe信息的到来,接着service.exe就发送加载服务的命令即可。下面是发送的协议内容

        PWCHAR    pTestServices    =    L"thisistest";
        PCTRL_MSG_HEADER    pSendBuffer;
        DWORD    dwSendSize=210;
        pSendBuffer    =    (PCTRL_MSG_HEADER)new char[dwSendSize];
        ZeroMemory(pSendBuffer, dwSendSize);
        pSendBuffer->Count    =    sizeof(CTRL_MSG_HEADER)+4+sizeof(WCHAR)+wcslen(pTestServices)*2;
        pSendBuffer->NumCmdArgs    =    0;
        pSendBuffer->ArgvOffset    =    0;
        pSendBuffer->ServiceNameOffset    =    sizeof(CTRL_MSG_HEADER);
        pSendBuffer->OpCode    =    SERVICE_CONTROL_START_OWN;
        wcscpy((WCHAR*)pSendBuffer->szdata, pTestServices);
    /    char szPipe[256];
    
        HANDLE hfile =gethandle();
        if (hfile==0)
        {
            return 1;
        }
    printf("write handle %x\n", hfile);
        DWORD    dwSended=0;
        BOOL bRet = WriteFile(hfile, pSendBuffer, dwSendSize, &dwSended, NULL);
        printf("dwSended,%d, handle %x, bret=%d,error:%d\n", dwSended, hfile, bRet,GetLastError());

    但最后还是失败了,因为在svchost接收到这个指令,得到服务名后,还会用openservice()尝试打开这个服务,当然,我这个服务是伪造的,不存在,所以就失败了。

    PS:那个pipe在获取的时候还是有点曲折的,想直接调用createfile打开的话,会返回错误“拒绝打开“,权限不够,最后我是通过DuplicateHandle才获取成功。

    ==================================


    //难道service.exe判断是否使用一个新的svchost.exe,是根据 -k 后面的服务组名判断的??
    //简单做了一个测试,在一个已经建立好的服务里面修改type字段值为SERVICE_WIN32_OWN_PROCESS,然后再启动服务的时候
    //发现服务启动失败,提示找不到模块。但是对应的服务的dll已经被加载了。
    //如果-k后面是一个新组,即使指定了SERVICE_WIN32_SHARE_PROCESS,也是会启动一个新svchost进程的

    //结论, 只有指定了SERVICE_WIN32_SHARE_PROCESS而且ImagePath已经运行过才会用老的svchost.exe来加载
    //因为services.exe的一个链表中保存着这些运行过的imagepath信息,同时里面对应着那个imagepath的pipe句柄
    //所以,如果是用老的svchost的话,直接取出这个pipe句柄即可通信
    //这个pipe是在最开始的时候,services.exe和svchost.exe同步去注册表currentservice中取出的一个数字拼上NtControlFile
    //得到的一个namedpipe,然后调用createfile打开的句柄

    //注意:如果启动的服务组 (-k 指定 )已经运行过了,但服务创建的时候指定了SERVICE_WIN32_OWN_PROCESS的话,
    //还是会创建一个新进程svchost.exe,所以在2个情况下,一定会启动新进程,1。是指定SERVICE_WIN32_OWN_PROCESS,
    //2。指定的imagepath未运行过


    //
    // Is the image file for that service already running?
    // If not, call StartImage.
    //
    // If the Image Record was NOT found in the database, then start the
    // image file.
    /********************************
    结合上面分析,如果想直接通过svchost来加载一个dll达到注入绕过主防的话,
    1。先找到那个pipe句柄
    2。通信协议
    3.刚发现还有一个检验,就是当通过pipe把消息发送到svchost,svchost收到服务名srv1后,它会检验srv1是否在注册表服务组里面的
    键值链表里面,如果是的话,则才会去读取注册表servicedll获取路径,然后加载

    后期搜索到的不错的paper

    SVChost hacking a-z

    在W2K中提升权限的几个攻击实例之成败心得

  • 相关阅读:
    ajax
    文件下载--getOutputStream输出二进制字符
    文件上传功能实现代码
    java动态生成验证码
    项目中用到的jar包简介(2)
    python字符串的常见操作
    python切片使用方法(超详细)
    for循环结合range使用方法
    python使用while循环实现九九乘法表
    石家庄云修科技有限公司
  • 原文地址:https://www.cnblogs.com/kkindof/p/2540164.html
Copyright © 2020-2023  润新知