原型链污染

javascript 原型链

在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。

原型链污染就是:在我们想要利用的代码之前的赋值语句如果可控的话,我们进行 ——__proto__ 赋值,之后就可以利用代码了。

原型链污染一般会出现在对象、或数组的键名或属性名可控,而且是赋值语句的情况下。

例一:

  1. var mds = [];
  2. for (var i=0;i<3;i++){
  3. mds[i]=[null,null,null];
  4. }
  5. Array(3) [ null, null, null ]
  6. var row="__proto__"
  7. undefined
  8. var co="admin"
  9. undefined
  10. mds[row][co]="sunsec"
  11. "sunsec"
  12. var c=[]
  13. undefined
  14. c[co]
  15. "sunsec"

这里我们创建了两个数组,一个是mds,另一个是c,此时我们将mds的mds.__proto__上一条链的[co]为sunsec。

题一:
  1. const express = require('express') //关于require,require是一个函数
  2. var hbs = require('hbs');
  3. var bodyParser = require('body-parser');
  4. const md5 = require('md5');
  5. var morganBody = require('morgan-body');
  6. const app = express();
  7. var user = []; //empty for now
  8. var matrix = [];
  9. for (var i = 0; i < 3; i++){
  10. matrix[i] = [null , null, null];
  11. }
  12. function draw(mat) {
  13. var count = 0;
  14. for (var i = 0; i < 3; i++){
  15. for (var j = 0; j < 3; j++){
  16. if (matrix[i][j] !== null){
  17. count += 1;
  18. }
  19. }
  20. }
  21. return count === 9;
  22. }
  23. app.use(express.static('public'));
  24. app.use(bodyParser.json());
  25. app.set('view engine', 'html');
  26. morganBody(app);
  27. app.engine('html', require('hbs').__express);
  28. app.get('/', (req, res) => {
  29. for (var i = 0; i < 3; i++){
  30. matrix[i] = [null , null, null];
  31. }
  32. res.render('index');
  33. })
  34. app.get('/admin', (req, res) => {
  35. /*this is under development I guess ??*/
  36. console.log(user.admintoken);
  37. if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){
  38. res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');
  39. }
  40. else {
  41. res.status(403).send('Forbidden');
  42. }
  43. }
  44. )
  45. app.post('/api', (req, res) => {
  46. var client = req.body;
  47. var winner = null;
  48. if (client.row > 3 || client.col > 3){
  49. client.row %= 3;
  50. client.col %= 3;
  51. }
  52. matrix[client.row][client.col] = client.data;
  53. for(var i = 0; i < 3; i++){
  54. if (matrix[i][0] === matrix[i][1] && matrix[i][1] === matrix[i][2] ){
  55. if (matrix[i][0] === 'X') {
  56. winner = 1;
  57. }
  58. else if(matrix[i][0] === 'O') {
  59. winner = 2;
  60. }
  61. }
  62. if (matrix[0][i] === matrix[1][i] && matrix[1][i] === matrix[2][i]){
  63. if (matrix[0][i] === 'X') {
  64. winner = 1;
  65. }
  66. else if(matrix[0][i] === 'O') {
  67. winner = 2;
  68. }
  69. }
  70. }
  71. if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'X'){
  72. winner = 1;
  73. }
  74. if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'O'){
  75. winner = 2;
  76. }
  77. if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'X'){
  78. winner = 1;
  79. }
  80. if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'O'){
  81. winner = 2;
  82. }
  83. if (draw(matrix) && winner === null){
  84. res.send(JSON.stringify({winner: 0}))
  85. }
  86. else if (winner !== null) {
  87. res.send(JSON.stringify({winner: winner}))
  88. }
  89. else {
  90. res.send(JSON.stringify({winner: -1}))
  91. }
  92. })
  93. app.listen(3000, () => {
  94. console.log('app listening on port 3000!')
  95. })

对于这道题我们获取flag的条件就是user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken,但是我们无法掌控到admintoken。

但是在api这个接口的地方,是可以接收data,row,col所以此时我们可以进行原型链污染,在这里user.admintoken并没有被赋值,我们可以进行污染,只要将

  1. matrix[client.row][client.col] = client.data

client.row设置为__proto__即可,再将col设为admintoken,此时data就是我们设置的值了。

此时在admin路由处传入querytoken。

题二
  1. 'use strict';
  2. const express = require('express');
  3. const bodyParser = require('body-parser')
  4. const cookieParser = require('cookie-parser');
  5. const path = require('path');
  6. const isObject = obj => obj && obj.constructor && obj.constructor === Object;
  7. function merge(a, b) {
  8. for (var attr in b) {
  9. if (isObject(a[attr]) && isObject(b[attr])) {
  10. merge(a[attr], b[attr]);
  11. } else {
  12. a[attr] = b[attr];
  13. }
  14. }
  15. return a
  16. }
  17. function clone(a) {
  18. return merge({}, a);
  19. }
  20. // Constants
  21. const PORT = 8080;
  22. const HOST = '0.0.0.0';
  23. const admin = {};
  24. // App
  25. const app = express();
  26. app.use(bodyParser.json())
  27. app.use(cookieParser());
  28. app.use('/', express.static(path.join(__dirname, 'views')));
  29. app.post('/signup', (req, res) => {
  30. var body = JSON.parse(JSON.stringify(req.body));
  31. var copybody = clone(body)
  32. if (copybody.name) {
  33. res.cookie('name', copybody.name).json({
  34. "done": "cookie set"
  35. });
  36. } else {
  37. res.json({
  38. "error": "cookie not set"
  39. })
  40. }
  41. });
  42. app.get('/getFlag', (req, res) => {
  43. var аdmin = JSON.parse(JSON.stringify(req.cookies))
  44. if (admindmin == 1) {
  45. res.send("hackim19{}");
  46. } else {
  47. res.send("You are not authorized");
  48. }
  49. });
  50. app.listen(PORT, HOST);
  51. console.log(`Running on http://${HOST}:${PORT}`);

flag获取条件: admin.аdmin == 1)

看到这处路由可以发现admin的admin属性是不存在的。

看到函数merge:

  1. function merge(a, b) {
  2. for (var attr in b) {
  3. if (isObject(a[attr]) && isObject(b[attr])) {
  4. merge(a[attr], b[attr]);
  5. } else {
  6. a[attr] = b[attr];
  7. }
  8. }
  9. return a
  10. }

这里进行了对象的合并,但是键值是可以被控制的。

注意:在创建字典的时候,__proto__,不是作为一个键名,而是已经作为__proto__给其父类进行赋值

所以当我们设置:

var test = {"test":"aa","__proto__":{"admin":1}}

此时:test有两个键值,分别为test以及admin。

那么我们再将其跟另一个test1进行对象的合并,此时看一下test1,会发现他不具备admin属性。

如何避免:

利用JSON.parse,JSON.parse 会把一个json字符串 转化为 javascript的object。

Nodejs-原型链污染的更多相关文章

  1. 【web安全】Nodejs原型链污染分析

    Nodejs原型链污染分析 什么是js原型? 可以将js原型理解为其他OOP语言中的类,但还是有细微区别. 1. function F(){...} 2. var f = new F(); 分析: 1 ...

  2. redpwnctf-web-blueprint-javascript 原型链污染学习总结

    前几天看了redpwn的一道web题,node.js的web,涉及知识点是javascript 原型链污染,以前没咋接触过js,并且这个洞貌似也比较新,因此记录一下学习过程 1.本机node.js环境 ...

  3. 原型链污染(Node.js污染,javasrcipt原型链污染的)

    学习链接: https://www.jianshu.com/p/6e623e9debe3 关于NJS  https://xz.aliyun.com/t/7184 相关题是 GYCTF  ez_expr ...

  4. javascript 原型链污染

    原理①javascript中构造函数就相当于类,并且可以将其实例化 ②javascript的每一个函数都有一个prototype属性,用来指向该构造函数的原型同样的javascript的每一个实例对象 ...

  5. 初探JavaScript原型链污染

    18年p师傅在知识星球出了一些代码审计题目,其中就有一道难度为hard的js题目(Thejs)为原型链污染攻击,而当时我因为太忙了(其实是太菜了,流下了没技术的泪水)并没有认真看过,后续在p师傅写出w ...

  6. JavaScript原型链及其污染

    JavaScript原型链及其污染 一.什么是原型链? 1.JavaScript中,我们如果要define一个类,需要以define"构造函数"的方式来define: functi ...

  7. 【nodejs原理&源码赏析(3)】欣赏手术级的原型链加工艺术

    目录 一. 概述 二. 原型链基础知识 三. Worker类的原型链加工 四. 实例的生成 五. 最后一个问题 六. 一些心得 示例代码托管在:http://www.github.com/dashno ...

  8. 【nodejs原理&源码赏析(3)】欣赏手术级的原型链加工艺术

    [摘要] 学习经典代码中的prototype加工 示例代码托管在:http://www.github.com/dashnowords/blogs 好的代码都差不多,烂的代码却各有各的烂法. 一. 概述 ...

  9. Javascript的原型链图

    90%的前端或者js程序员或者老师们对Javascript懂得不比这个多 给手机看的 但是这个图里的所有褐色单向箭头链就是Javascript的原型链(颜色标注对理解js原型链很关键) 这图中的各个_ ...

随机推荐

  1. linux 强制重启!

    原文链接:https://www.cnblogs.com/wipy/p/4261472.html 有时候,linux 由于硬盘或者其它原因, 某个进程挂住了,怎么也杀不死, 输入 reboot 命令也 ...

  2. 牛客网PAT练兵场-组个最小数

    题解:从小到大输出 题目地址:https://www.nowcoder.com/questionTerminal/86ede762b450404dbab59352963378e9 /** * *作者: ...

  3. jQuery 事件操作

    入口函数 使用$(document).ready(()=>{})作为jQuery入口函数,与window.onload(()=>{})类似,但它不会等待图片等外部资源的加载完毕,而是在HT ...

  4. 类的加载,链接和初始化——1运行时常量池(来自于java虚拟机规范英文版本+本人的翻译和理解)

    加载(loading):通过一个特定的名字,找到类或接口的二进制表示,并通过这个二进制表示创建一个类或接口的过程. 链接:是获取类或接口并把它结合到JVM的运行时状态中,以让类或接口可以被执行 初始化 ...

  5. 以vue+TreeSelect为例,如何将扁平数据转为tree形数据

    // 目标:将后台返回的扁平数据,根据parentId转为下拉tree <el-form-item label='下拉选择数据'> <tree-select v-model='tre ...

  6. redis的集群搭建(很详细很详细)

    说在前面的话 之前有一节说了redis单机版的搭建和使用jedis管理redis单机版和集群版, 本节主要讲一下redis的集群搭建. 跳转到jedis管理redis的使用 认识redis集群 首先我 ...

  7. 每日JS逆向练习之斗鱼登录密码加密,今天你练了吗?

    一切的基本功都是为后期调试滑块验证码准备的. 有兴趣的关注一下知识图谱与大数据公众号,当然不关注也无所谓.今天来看看斗鱼登录密码加密,正所谓熟能生巧,这种简单一点的基本3-5分钟就要能抠出来,有兴趣得 ...

  8. Linux服务器关联Git,通过执行更新脚本实现代码同步

    1.在Linux服务器安装Git yum install git -y   tips: 卸载Git :  yum remove git   2.在Linux生成ssh key   1)创建用户 git ...

  9. WinMTR 网络测试工具-九五小庞

    WinMTR(建议优先使用) 百度下载工具 链接:https://pan.baidu.com/s/19ArKSTA2amsa4p6vHegDIQ 提取码:cy4y WinMTR是mtr工具在Windo ...

  10. 12_进程,线程,协程,IO多路复用的区别

    1.进程 1.进程可以使用计算机多核 2.进程是资源分配的单位 3.进程的创建要比线程消耗更多的资源效率很低 4.进程空间独立,数据安全性跟好操作有专门的进程间通信方式 5.一个进程可以包含多个线程, ...