Vuln-SSCMS-2026-1-18

Uncategorized
2.2k words

此处用户传入的 RelatedFileName 参数没有进行路径遍历字符的过滤或清洗,导致持有 /api/admin/cms/templates/templatesEditor/actions/settings 接口访问权限的用户,可以构造如 "relatedFileName":"/../../../../../../../../.././etc/cron.d/e2scrub_all" 进行传参,请求的中 relatedFileName 参数将会进入到如下位置。

cms/src/SSCMS.Web/Controllers/Admin/Cms/Templates
/TemplatesEditorController.Settings.cs

image

TemplatesEditorController.Settings.csL100,含有路径穿越的内容的 RelatedFileName 的值又赋给了 template.RelatedFileName 中,然后 template 被传入了到 WriteContentToTemplateFileAsync() 函数.

1
await _pathManager.WriteContentToTemplateFileAsync(site, template, request.Content, _authManager.AdminId);

image

WriteContentToTemplateFileAsync 函数位于如下路径。

cms/src/SSCMS.Core/Services
/PathManager.Template.cs

传入的 template 又被传入到了 GetTemplateFilePathAsync() 函数中

image

GetTemplateFilePathAsync()template.RelatedFileName 又被传入到 GetSitePathAsync() 函数。

src/SSCMS.Core/Services/PathManager.Template.cs

image

GetSitePathAsync() 定义如下

cms/src/SSCMS.Core/Services
/PathManager.Site.cs

image

传入的 template.RelatedFileName 首先进入到了 PathUtils.Combine(returnPath, path);

image

在这里与siteId项目路径路径进行拼接

比如

1
2
3
url1 = "/var/www/html/111"

url2 = "index.html"

最终将得到 /var/www/html/111/index.html

但因为我们传入的是 pathtemplate.RelatedFileName 内容为 ../../../../../../../../../etc/cron.d/test,所以此处返回的是

1
returnPath = "/var/www/html/111/../../../../../../../../../etc/cron.d/test"

然后 returnPath 进入到了 DirectoryUtils.IsInDirectory(_settingsManager.WebRootPath, returnPath) 函数

image

IsInDirectory() 函数定义如下

cms/src/SSCMS/Utils
/DirectoryUtils.cs

image

此处 IsInDirectory 是该漏洞的主要成因,该函数用于判断传入的 path 是否在 web 规定的目录沙箱内,但是此处仅使用 path.StartsWith 来进行判断。

1
return parentDirectoryPath == path || path.StartsWith(parentDirectoryPath);

但我们传入的 path 此时如下。

1
2
path = "/var/www/html/111/../../../../../../../../../etc/cron.d/test"
parentDirectoryPath = "/var/www/html/"

这恰好符合了 path.StartsWith(parentDirectoryPath); 从而导致我们的路径穿越被认为在合法web路径范围内。

然后 path 的值返回到了 cms/src/SSCMS.Core/Services /PathManager.Template.csWriteContentToTemplateFileAsync() 函数,并被被赋予到其中 filePath 参数。

image

路径穿越的值又进入到了文件写入函数 FileUtils.WriteTextAsync(filePath, content); ,并于此处触发漏洞,文件被写入到系统指定位置下。

image

于linux系统下,可以通过此种方式将文件写入到定时任务最终实现rce。

image

查看 /etc/cron.d/e2scrub_all ,确认写入成功。

image

等待1分钟之后触发rce.

image