Open m0unta1ner opened 1 year ago
模板注入 src/controller/admin/template.js /**
@returns {} / async homeAction() { const gid = await this.model('temp_group').where({isdefault: 1}).getField('gid', true); const map = { module: 'home', controller: 'index', action: 'index', type: this.para('type') || 1, gid: gid }; const temp = await this.model('temp').where(map).find(); let temppath; if (temp.type == 2) { temppath = ${think.ROOT_PATH}/view/${temp.module}/mobile/; } else { temppath = ${think.ROOT_PATH}/view/${temp.module}/; } const templateFile = ${temppath}${temp.controller}${this.config('view.nunjucks.sep')}${temp.action}${this.config('view.nunjucks.extname')}; if (this.isPost) { const data = this.post(); data.id = temp.id; data.module = map.module; data.controller = map.container; data.action = map.action; data.name = temp.name; data.type = temp.type; data.gid = temp.gid; console.log(data); // await this.model("temp").add(data); temp.pid = temp.id; delete temp.id; temp.baktime = new Date().getTime(); temp.lastuser = this.user.uid; console.log(temp); // return false; // 修改前先备份 if (data.html != temp.html) { const bak = await this.model('temp_bak').add(temp); const res = await this.model('temp').update(data); if (!think.isEmpty(res)) { fs.writeFileSync(templateFile, data.html); return this.success({name: '添加成功!'}); } } else { return this.fail('请先修改模板!'); } } else { // 首页网站编辑 // console.log(this.adminmenu["10"]); this.meta_title = '首页模板';
${think.ROOT_PATH}/view/${temp.module}/mobile/
${think.ROOT_PATH}/view/${temp.module}/
${temppath}${temp.controller}${this.config('view.nunjucks.sep')}${temp.action}${this.config('view.nunjucks.extname')}
if (think.isFile(templateFile)) { const tempcon = fs.readFileSync(templateFile, 'utf8'); temp.html = tempcon; } // console.log(temp); this.assign('temp', temp); return this.display(); } } 可以看到通过nunjucks模板对前端进行渲染,对传入的post请求的html参数只有判空校验,所以可以通过模板注入命令执行来进行RCE,直接构造调用child_process的命令执行,我们这里进行弹计算器演示 然后访问首页进行渲染 成功弹出计算器,其他的模板也存在该注入问题
模板注入 src/controller/admin/template.js /**
@returns {} / async homeAction() { const gid = await this.model('temp_group').where({isdefault: 1}).getField('gid', true); const map = { module: 'home', controller: 'index', action: 'index', type: this.para('type') || 1, gid: gid }; const temp = await this.model('temp').where(map).find(); let temppath; if (temp.type == 2) { temppath =
${think.ROOT_PATH}/view/${temp.module}/mobile/
; } else { temppath =${think.ROOT_PATH}/view/${temp.module}/
; } const templateFile =${temppath}${temp.controller}${this.config('view.nunjucks.sep')}${temp.action}${this.config('view.nunjucks.extname')}
; if (this.isPost) { const data = this.post(); data.id = temp.id; data.module = map.module; data.controller = map.container; data.action = map.action; data.name = temp.name; data.type = temp.type; data.gid = temp.gid; console.log(data); // await this.model("temp").add(data); temp.pid = temp.id; delete temp.id; temp.baktime = new Date().getTime(); temp.lastuser = this.user.uid; console.log(temp); // return false; // 修改前先备份 if (data.html != temp.html) { const bak = await this.model('temp_bak').add(temp); const res = await this.model('temp').update(data); if (!think.isEmpty(res)) { fs.writeFileSync(templateFile, data.html); return this.success({name: '添加成功!'}); } } else { return this.fail('请先修改模板!'); } } else { // 首页网站编辑 // console.log(this.adminmenu["10"]); this.meta_title = '首页模板';if (think.isFile(templateFile)) { const tempcon = fs.readFileSync(templateFile, 'utf8'); temp.html = tempcon; } // console.log(temp); this.assign('temp', temp); return this.display(); } } 可以看到通过nunjucks模板对前端进行渲染,对传入的post请求的html参数只有判空校验,所以可以通过模板注入命令执行来进行RCE,直接构造调用child_process的命令执行,我们这里进行弹计算器演示 然后访问首页进行渲染 成功弹出计算器,其他的模板也存在该注入问题