华为HCIE的第一课 Writeup

admin 2024年12月24日13:31:02评论5 views字数 4131阅读13分46秒阅读模式

目录

  1. 说在前面
  2. 解题过程
    1. 题目简览
    2. 简单审计
    3. 原型链污染
    4. hbs模板注入导致信息泄露
  3. 总结

说在前面

周末是XCTF的HarmonyOS和HMS专场比赛,起床后就开始看,因为这一道题花了比较长的时间才做出来,所以觉得还是有记录一下的必要。

题目考点:

  • Nodejs简单审计
  • 原型链污染漏洞
  • hbs模板注入导致信息泄露

解题过程

题目简览

题目打开后需要输入用户名登录:

华为HCIE的第一课 Writeup

登录之后是一个计算ip地址的一些信息的工具:

华为HCIE的第一课 Writeup

点击去开发会跳转到/admin去,显示forbidden

华为HCIE的第一课 Writeup

简单审计

在打开题目后链接就会跳转到http://124.71.204.195:31799/?f=login.html,简单尝试一下就可以发现这里是一个任意文件读取:

华为HCIE的第一课 Writeup

可以把所有题目源码与相关依赖文件都下载下来,本地可以直接调试。需要注意的是这里没法下载.env文件,这个文件是用于dotenv模块加载环境变量配置的。

简单审计一下可以了解到这些文件的基本用途:

  • app.js 配置并启动express(其中包括设置session、静态文件、模板解析引擎、路由等等)
  • login.js 用于设置req.session.name为传入的username
  • calc.js 用于计算ip相关信息的代码(用于混淆用的,实际解题毫无用处)
  • admin.js 会经过一个ip等信息的判断,可以设置计算ip页下发的内容,后面详细分析
  • util.js 一些处理函数

主要来看admin.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
module.exports = (app, env) => {

const {htmlencode, replaceAll, md5} = require("./util")
const fs = require("fs")
const path = require("path")

app.get('/admin', (req, res) => {
let user
try {
user = JSON.parse(`{"name" : "${req.session.name}", "time" : "${Math.ceil(new Date().getTime() / 1000)}", "ip" : "${req.ip}"}`)
} catch (e) {
res.end("error")
return
}
let userinfo = {}
Object.keys(user).forEach((key) => {
if (key.trim() === "isAdmin")
userinfo[key] = 0
else userinfo[key] = user[key]
})

if (req.session.ip === '127.0.0.1')
userinfo.isAdmin = 1;

req.session.name = userinfo.name
req.session.time = userinfo.time
req.session.ip = userinfo.ip
req.session.isAdmin = userinfo.isAdmin

if (req.session.isAdmin !== 1) {
res.end("forbidden")
return;
}

res.render("admin", {"name":req.session.name})
})

app.post("/admin", async (req, res)=>{
if (!req.session.isAdmin || !req.body.code) {
res.status(403).end("forbidden")
return
}

let html = "name : {{name}}, time : {{time}}, ip : {{ip}} \ntips: {{env.banner}}<br><a href='/admin'>返回</a><br><br>\n\n" + fs.readFileSync(path.resolve(__dirname, "../views/calc.html"))
let list = ['secret', 'env', 'flag', 'if', 'unless', 'for', 'lookup', '[', ']', '@' ]
let code = req.body.code + ""
let padd = `<p class="t-big-margin no-margin-b flex-center">这里开发中...&nbsp; <a href="/admin" target="_blank">去开发</a></p>`

await list.forEach((black) => {
code = replaceAll(black, htmlencode(black), code)
})

html = html.replace(padd, code)
let filename = md5(html) + ".html"
let filepath = path.resolve(__dirname, "../views/users/"+filename)
if (fs.existsSync(filepath))
fs.unlinkSync(filepath)
fs.writeFile(filepath, html, err => {
if (err) {
res.end("error")
} else {
res.render("users/"+filename, {
"name" : req.session.name,
"time" : Math.ceil(new Date().getTime() / 1000),
"ip" : req.ip,
"env" : env.parsed
})
}
})

})

}

把GET请求的过程理一下:

  1. 通过JSON.parse()解析了一个json数据,一共有三个键:nametimeip,而这个name是可控的,并且拼接到json中可以添加键值
  2. 通过一次循环,将user的值传递给userinfo,但是当键名为isAdmin时,会把这个值置0
  3. 判断req.session.ip是否为127.0.0.1,如果是的话就把userinfo.isAdmin设置为1
  4. userinfo的值传给session
  5. 判断userinfo.isAdmin是否为1,如果不为1,则返回forbidden

这里我们的主要目的就是怎么让isAdmin1,大概有这么几个思路:

  1. 在上方第二步那里通过在键名里加一些特殊字符的trickisAdmin无法被置为0 (fuzz了一下00-ff,失败)
  2. 伪造ip地址
    1. 队友说可以通过伪造req.ip,设置app.set('trust proxy', true);就可以通过X-Forwarded-For伪造。参考 (trust proxy默认为false 失败)
    2. 我想的是通过控制JSON.parse()的参数值,让后面的ip不解析,而解析一个我们所控制的ip值来绕过ip检测 (没找到可以成功解析的方式 失败)
  3. SESSION伪造 (之前说过无法读取.envsessionsecret在这个文件里,不知道secret也不能任意上传文件的情况下无法伪造 失败)
  4. 是否有原型链污染可以利用?

原型链污染

前三种思路都失败了,后面发现是在JSON.parse这里存在一个原型链污染问题。

华为HCIE的第一课 Writeup

通过构造username为:Threezh1","__proto__":{"isAdmin":1},"test":"就可以污染到isAdmin。通过req.session.isAdmin = userinfo.isAdmin这句命令传递后,我们再访问/admin页面就不会是forbidden了。

华为HCIE的第一课 Writeup

这里提交的是一个code参数,POST发包给/admin页面。

hbs模板注入导致信息泄露

接着来看一下POST请求是怎么处理的:

  • 判断req.session.isAdmincode参数
  • code参数值进行了过滤,list列表里的内容都会过滤转义
  • 最后拼接了一个html页面源码写到本地文件再回显到页面上

这里是一个比较明显的模板注入,搜了一下关于hbs的相关模板注入,找到了一篇关于Handlebars库导致RCE的文章:漏洞挖掘:Handlebars库 模板注入导致RCE 0day

但很快啊,就发现RCE行不通,题目环境中的是最新版,文章中的payload已经不管用了。只能想其他的办法。

注意到传入模板变量时候的一个问题:这里传入的env是把包含所有环境变量的变量传入进去了。又因为页面中的一部分是我们能传入code参数控制的。所以我们是可以通过一些模板定义好的语句读出所有的env所包含的环境变量的。

1
2
3
4
5
6
res.render("users/"+filename, {
"name" : req.session.name,
"time" : Math.ceil(new Date().getTime() / 1000),
"ip" : req.ip,
"env" : env.parsed
})

首先是寻找到了这么一篇帖子:2020 Defenit CTF Writeup 里面的payload形式是这样的:

1
{{#each this}}{{@key}} => {{this.toString}}<br>{{/each}}

由于这里的@key由于@被过滤了,以及这样简单的遍历没法得到环境变量中的内容,最后构造一了一个可以拿到所有环境变量的语句参考

1
{{#each this}}{{#with this}}{{#each this}}<h3>{{this.toString}}</h1><br>{{/each}}{{/with}}{{/each}}

submit之后就可以在页面上看到flag了:

华为HCIE的第一课 Writeup

当时我写的时候比较匆忙,这个payload可以变得更短,payload@p1g3:

1
{{#each this}}{{#each this}}{{this.toString}}{{/each}}{{/each}}

总结

感觉题目不算特别难,但就是有很多地方意想不到。对各种细节还需要再注意一些…

- By:threezh1.com

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年12月24日13:31:02
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   华为HCIE的第一课 Writeuphttps://cn-sec.com/archives/3547578.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息