qBittorrent中的远程代码执行漏洞CVE-2024-51774
qBittorrent 是一款轻量级 BitTorrent 客户端,可运行于Linux、windows及其他系统,被视为一个良好的替代其他 BitTorrent 软件的客户端。
一、基本情况
qBittorrent 是一款跨平台的自由 BitTorrent 客户端,,用于下载和分享资源文件,图形用户接口由Qt4所写成,使用 libtorrent 作为后端。
栋科技漏洞库关注到qBittorrent 中存在一个超14年的时间未被修复的严重的远程代码执行(RCE)漏洞,现已被追踪为CVE-2024-51774。
该漏洞是由于其DownloadManager类中缺乏SSL证书验证导致的,由网络安全公司Sharp Security的网络安全研究员J. Sharp发现并报告。
该漏洞于2010年04月通过提交引入,可能允许攻击者利用应用程序使用的硬编码 URL 进行 RCE 攻击,并通过中间人 (MITM)发起攻击。
该漏洞于2024年10月12日时得以修复,但2024年10月28日发布的 qBittorrent 5.0.1 版本是包括该修复CVE-2024-51774补丁的最早版本。
栋科技漏洞库此前已就此漏洞进行简单分析,彼时漏洞暂未获得CVE漏洞编号,通过 CVE 官网 Program可看到编号分配时间2024-11-02。
今天栋科技漏洞库关注到 Sharp Security 对CVE-2024-51774的详尽解读,由于此前分析不够细致,因此决定结合官网更新日志重新整理。
二、漏洞分析
漏洞源于qBittorrent中DownloadManager类,该类绕过跨多个功能(包括 torrent 下载、RSS 源和搜索引擎功能)任何下载文件SSL验证。
这样的漏洞存在意味着 qBittorrent 跨多个关键功能信任任何SSL证书(无论是过期的、自签名的还是恶意的),从而产生了大量攻击媒介。
qBittorrent 中的 DownloadManager 在整个程序中的使用非常广泛,并且会影响导搜索、.torrent 下载、RSS 源以及favicon下载等等功能。
漏洞必须通过 MITM 访问或 DNS 欺骗才能利用,所有代码路径都接受任何证书,无论过期、自签名还是两者兼而有,按严重性降序排列:
(1)具有隐形功能的恶意可执行加载程序
如果您运行的是Windows且未安装足够新的Python版本, qBittorrent启动时将提示从硬编码 URL 安装/更新 Python便于使用搜索插件:
1、源代码
#ifdef Q_OS_WIN
const QMessageBox::StandardButton buttonPressed = QMessageBox::question(this, tr("Missing Python Runtime")
, tr("Python is required to use the search engine but it does not seem to be installed.\nDo you want to install it now?")
, (QMessageBox::Yes | QMessageBox::No), QMessageBox::Yes);
if (buttonPressed == QMessageBox::Yes)
installPython();
...
#ifdef Q_OS_WIN
void MainWindow::installPython()
{
setCursor(QCursor(Qt::WaitCursor));
// Download python
const auto installerURL = u"https://www.python.org/ftp/python/3.12.4/python-3.12.4-amd64.exe"_s;
Net::DownloadManager::instance()->download(
Net::DownloadRequest(installerURL).saveToFile(true)
, Preferences::instance()->useProxyForGeneralPurposes()
, this, &MainWindow::pythonDownloadFinished);
}
2、代码来源
https://github.com/qbittorrent/qBittorrent/blob/2d185dc1c7932e775ecf5f2b0a7b7639ed8228f7/src/gui/mainwindow.cpp#L1579
如果在自动选择的“是”的选项上单击或者是按下回车键而选择允许,那么qBittorrent 将**下载、执行,然后删除 .exe**
1、源代码
void MainWindow::pythonDownloadFinished(const Net::DownloadResult &result)
{
if (result.status != Net::DownloadStatus::Success)
{
...
}
setCursor(QCursor(Qt::ArrowCursor));
QProcess installer;
qDebug("Launching Python installer in passive mode...");
const Path exePath = result.filePath + u".exe";
Utils::Fs::renameFile(result.filePath, exePath);
installer.start(exePath.toString(), {u"/passive"_s});
// Wait for setup to complete
installer.waitForFinished(10 * 60 * 1000);
qDebug("Installer stdout: %s", installer.readAllStandardOutput().data());
qDebug("Installer stderr: %s", installer.readAllStandardError().data());
qDebug("Setup should be complete!");
// Delete temp file
Utils::Fs::removeFile(exePath);
// Reload search engine
if ...
}
#endif // Q_OS_WIN
2、源文件链接
https://github.com/qbittorrent/qBittorrent/blob/2d185dc1c7932e775ecf5f2b0a7b7639ed8228f7/src/gui/mainwindow.cpp#L1889
qBittorrent 从2015年06月至今的版本中一直存在此行为,影响版本包括 v3.2.1 到 v5.0.0(含),其他操作系统变体似乎不会复制该行为。
但其他操作系统只是在未找到 Python 或 Python 不够新的情况下禁用搜索小部件,而当可执行文件下载完成后,它将存储在例如:
C:\Users\_user_\AppData\Local\Temp\\ is-G61QK.tmp
这可能是由于“QProcess”实例的行为方式,exe 的两个线程正在运行,并且只有其中一个在执行后被杀死,而另一个则持续处于睡眠状态。
使用 mitmproxy 注入的 calc.exe 的强制屏幕截图:
(2)任意URL注入+可执行下载(软件升级上下文)
如果在 Windows 或 Linux 上运行已安装的 qBittorrent 版本,而不是“appImage”文件,那么它在启动时默认执行更新检查,这需要提取文档
具体来说,就是需要以 XML 形式的硬编码 URL下载 RSS 提要,并解析程序发布信息的文档:
void ProgramUpdater::checkForUpdates() const
{
const auto RSS_URL = u"https://www.fosshub.com/feed/5b8793a7f9ee5a5c3e97a3b2.xml"_s;
// Don't change this User-Agent. In case our updater goes haywire,
// the filehost can identify it and contact us.
Net::DownloadManager::instance()->download(
Net::DownloadRequest(RSS_URL).userAgent(QStringLiteral("qBittorrent/" QBT_VERSION_2 " ProgramUpdater (www.qbittorrent.org)"))
, Preferences::instance()->useProxyForGeneralPurposes(), this, &ProgramUpdater::rssDownloadFinished);
}
然后,它将解析 XML,如果版本高于当前运行的版本,则提取 URL,并提示用户访问此 URL,而不进行任何过滤或验证:
...
if (type.compare(variant, Qt::CaseInsensitive) == 0)
{
qDebug("The last update available is %s", qUtf8Printable(version));
if (!version.isEmpty())
{
qDebug("Detected version is %s", qUtf8Printable(version));
if (isVersionMoreRecent(version))
{
m_newVersion = version;
m_updateURL = updateLink;
}
}
break;
}
...
如果用户接受提示,则在默认浏览器中打开此URL。用户期望收到一个exe文件且上下文是信任之一,因为链接/下载来自他们运行的软件:
bool ProgramUpdater::updateProgram() const
{
return QDesktopServices::openUrl(m_updateURL);
}
因此,如果用户被定向到任何指定文件共享站点(例如 mediafire 等),他们就可以下载攻击者控制的 exe,他们希望其功能类似于升级。
由于该项目是 OSS,因此可以轻松通过附加后门功能重新编译最新版本,此过程中开发人员提供了一个密钥用于检查二进制文件的签名。
这是一项十分必要的工作,必须对其进行检查,并且必须考虑验证失败以保护用户。
(3)RSS Feeds(任意URL注入)
应用程序解析的所有 RSS 提要都会经过 DownloadManager,因此它们可能会被劫持。
受害者访问的 URL 可以被观察和编目,并且本质上它们是静态的、长期存在的 URL。
每个条目中的“link”元素会被直接解析,双击就会下载。
即使没有 MITM 篡改,这也适用 - 只需双击即可插入您关注的任何 RSS 提要中的任何 URL,无论是作者还是毒害该提要的攻击者。
else if (name == u"link")
{
const QString link = (xml.attributes().isEmpty()
? xml.readElementText().trimmed()
: xml.attributes().value(u"href"_s).toString());
if (link.startsWith(u"magnet:", Qt::CaseInsensitive))
{
article[Article::KeyTorrentURL] = link; // magnet link instead of a news URL
}
else
{
// Atom feeds can have relative links, work around this and
// take the stress of figuring article full URI from UI
// Assemble full URI
article[Article::KeyLink] = (m_baseUrl.isEmpty() ? link : m_baseUrl + link);
}
}
让该漏洞更具破坏性的是CVE-2019-13640,因为CVE-2019-13640允许通过 torrent 名称或当跟踪器参数中的 shell 元字符执行远程命令。
这种漏洞叠加组合使用,意味着真正的 RSS 作者不需要发送恶意数据,因为 MITM 攻击者也可以这样做。
(4)解压库攻击面(0-click)
启动时,默认情况下,程序会自动从硬编码 URL 下载 .gz 扩展名二进制 MaxMind GeopIP 数据库,然后将其解压缩。
如果 zlib 解压中存在任何漏洞,例如CVE-2022-37434 the CVSS 9.8 Critical buffer Overflow from 2022,
由于未调用“inflateGetHeader()”函数,因此此处不适用,
攻击者可使用以下方法来瞄准此攻击面最大 64MB 的任意文件,记录并在出现任何错误后返回;
...
bool ok = false;
const QByteArray data = Utils::Gzip::decompress(result.data, &ok);
if (!ok)
{
LogMsg(tr("Could not decompress IP geolocation database file."), Log::WARNING);
return;
}
截至 2024 年,解压后解析二进制 MaxMind 数据库的代码受到严格保护,但过去看起来有所不同,可能会提供更多的攻击面。
还有一个有趣的提交,其中贡献者对“gzip::decompress()”函数进行了调整,这暗示了堆栈溢出,
目标缓冲区从堆栈上的静态分配更改为堆上的动态分配,尽管它由于在写入之前进行了检查,因此无法利用:
-105 char tmpBuf[BUFSIZE] = {0};
+107 std::vector<char> tmpBuf(BUFSIZE);
三、漏洞影响
所有Python安装程序exe URL和更新RSS源URL都是硬编码,因此可轻松枚举且MITM上下文中的恶意脚本可以攻击每个易受攻击的版本。
而好消息是,RSS源URL为软件提供了指纹,因为除非 qBittorrent 正处于运行状态,否则没有其他理由访问这些URL之一。
这意味着 PRISM 等大规模监控程序可以通过被动流量监控轻松检测 qBittorrent 用户。
但由于没有证书验证,因此不需要像QUANTUM这样昂贵且复杂的部署来执行Man-On-The-Side攻击 - 而只需欺骗目标服务器即可。
URL 被硬编码的另一个方面,是攻击者可以有选择地仅拦截来自 qBittorrent 的不验证连接的请求,
而不是在受害者的浏览器/其他应用程序无法安全连接到互联网时提醒受害者。
由于该软件是开源的,因此添加后门并重新编译是微不足道的,因此可以为受害者提供功能齐全的软件,从而可以避免引起受害者的怀疑。
脚本编写的可能性,当与 mitmproxy 结合使用时,使用 `-s` 来提供 python 脚本:
1、用任意 exe 自动替换所有 Python exe:只需单击一下即可进行 RCE
2、自动替换 RSS 源中的所有 qBittorrent 更新 URL:浏览器劫持/RCE,具有适度的用户交互
3、自动替换 qBittorrent RSS 查看器中的所有/特定链接:RCE 直至 2019 年,下载劫持
人们将需要 MITM 访问权限的攻击视为理论,忘记近代历史上的教训,但显然这一攻击在中国、阿联酋和哈萨克斯坦等国家已变成现实。
四、修复建议
使用浏览器手动下载升级到 v5.0.1 ,而并非通过应用内更新提示。或者使用其他客户端,因为 Deluge 和 Transmission 等没有这个漏洞。
五、参考链接
https://www.qbittorrent.org/news
https://news.ycombinator.com/item?id=42004219
https://sharpsec.run/rce-vulnerability-in-qbittorrent/
https://bugzilla.redhat.com/show_bug.cgi?id=2323594
https://access.redhat.com/security/cve/CVE-2024-51774