Life
一切都会变,唯有变,才是永恒不变的。
卸载EDR
最近在一次测试中,想用环三干掉EDR的自保,想了好久都没有很合适的切入点,于是便开始了自保相关函数的分析,看看是不是存在白名单什么之类的。分析了半天,白名单确实也发现了些,但是用来干掉自保缺还是差了点意思。 大表哥对关闭自保验证了签名,于是这条路就这么卡住了。 也恰逢到点,便结束了这一天的征程。
再出发
第二天转变了下思路,既然白名单没有很好的切入口,那便从卸载流程入手吧,双击uninst.exe程序,卸载流程跑了起来,居然问我要密码,好吧x100dbg搞起,过去密码弹窗后,又提示我是否需要关闭自保,于是发现了一个有意思的dll,下面是导出的几个关键函数,其中XxSetProtectState便是关闭自保的函数,调用这个函数后便可弹出关闭自保确认窗。
起初也并没有太在意,直到在uninst.exe中发现了一个提示,!!!远程卸载!!!,
if ( *sub_43002E() >= 4
&& (v16 = sub_430034(), !wcsncmp(L"-g:", *((const wchar_t **)*v16 + 2), 3u))
&& (v17 = sub_430034(), !wcsncmp(L"-task:", *((const wchar_t **)*v17 + 3), 6u)) )
{
sub_40BD30()->dword70 = 1;
sub_407E50(v35);
v35[0] = (int)&std::wstringbuf::`vftable';
v36 = 0;
v37 = 0;
sub_4093D0(v38, (int)v35, v28, v31);
sub_40B530((char *)v35, L"卸载模式:远程卸载");
}
else
{
sub_407E50(v35);
v35[0] = (int)&std::wstringbuf::`vftable';
v36 = 0;
v37 = 0;
sub_4093D0(v38, (int)v35, v28, v31);
sub_40B530((char *)v35, L"卸载模式:常规卸载");
}
此时我心想,你总不能远程卸载也让用户确认关闭自保吧,于是乎一个核弹,在我心中默默的升起
突破
于是乎,通过x100dbg,跳转到了这个地方,跟着走了两步,两步,又两步,直到又回到了 上面的那个dll里面,这次调用了 XxDrvStop,然后什么弹窗也没有,就执行完毕了,这么神奇么,我好奇的用procexp杀了下服务,竟然被干掉了。 我又确认了下驱动的状态,竟然真的被卸载了。。。
还能这么玩
心中立马对写这段代码的大表哥,表达了敬意,真乃我逆向路上的明灯,照亮了我一次有一次。分析到现在,基本上就已经很明确了, uninst.exe 调用 XxCardinalxxx.dll::XxDrvStop 函数,便可成功关闭自保。那接下来的路,便是如何让 uninst.exe 调用这个函数了。
1. 要不伪造一个uninst.exe 调用?
结果发现,有签名校验,要我说,大表哥就多余这一步的校验,你都提供这个函数了,还校验个毛线啊
2. 那要不注入一下子试试?
被自保给拦截了,我总不能为了这个破功能先注入lsass.exe 在曲线救国吧。。。。想想头立马就大了。
3. 既然无法注入自保程序,何不启动时注入呢?
拿起我的小工具,injecttool,写个加载并调用XxDrvStop的小功能,走起。驱动竟然真就这么的没了,大表哥,这种行为,难道你真不该检测一下子么!!!
当然后来测试,uninst.exe 拿到外面也是可以的,谁让这是签名的exe呢!!!
但是为了最小限度利用,避免被发现,最后的流程就变成了:
1. 写个explorer.exe ,pdb符号路径照着explorer设置,伪装总要全套么
2. 模仿一个uninst.exe会调用到的xxx.dll,pdb符号也照着那个dll设置,迷惑大表哥,我是复制你的dll出去了,这是合法正当的卸载
3. explorer.exe 启动注入 uninst.exe。加载 xxx.dll,xxx加载调用XxDrvStop。嗯,完美收官
End
当然通过这个反面案例,为我们关掉其它安全防护软件提供了一个思路,
你的防护提供远程卸载的功能了么?
是不是你的签名程序,放到哪里,在你的自保里面都是白名单呢?
如果你的签名程序,父进程不合法,你还会认为他的所有流程都合法吗?
大表哥这里其实不光uninst.exe ,其它exe重复上面的流程都是会成功的。
End End
月末年终,明天又是新的开始.