跨站脚本攻击XSS(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!
xss分类
1.反射性xss 反射型XSS 是非持久性、参数型的跨站脚本。反射型XSS 的JS 代码在Web 应用的参数(变量)中,如搜索框的反射型XSS。在搜索框中,提交PoC[scriptalert(/xss/)/script],点击搜索,即可触发反射型XSS。 注意到,我们提交的poc 会出现在search.php 页面的keywords 参数中。
2.存储型xss
存储型XSS 是持久性跨站脚本。持久性体现在XSS 代码不是在某个参数(变量)中,而是写进数据库或文件等可以永久保存数据的介质中。存储型XSS 通常发生在留言板等地方。我们在留言板位置留言,将恶意代码写进数据库中。此时,我们只完成了第一步,将恶意代码写入数据库。因为XSS 使用的JS 代码,JS 代码的运行环境是浏览器,所以需要浏览器从服务器载入恶意的XSS 代码,才能真正触发XSS。此时,需要我们模拟网站后台管理员的身份,查看留言。
3.DOM的xss
DOM XSS 比较特殊。owasp 关于DOM 型号XSS 的定义是基于DOM 的XSS 是一种XSS 攻击,其中攻击的payload由于修改受害者浏览器页面的DOM 树而执行的。其特殊的地方就是payload 在浏览器本地修改DOM 树而执行, 并不会传到服务器上,这也就使得DOM XSS 比较难以检测。
xss-labs
下载xss-labs
将他转移到xp的www目录下
然后
在xp上创建网站
需要访问时打开apchi和mysql,访问
http://127.0.0.1/xss-labs
level-1
网页源代码
这里能看到网页源码里是有个alert函数的,只要触发这个函数就可以到下一关
这里输入
< svg οnclick="alert(1)" >
SVG(可缩放矢量图形)的根标签,用于定义矢量图形。
οnclick="alert(1)"事件属性,意思是当用户点击这个 SVG 元素时,会执行alert(1)这个 JavaScript 代码,弹出一个显示数字 “1” 的提示框。level-2
第二关是有一个input输入框的输入内容就是test,我们要做的就是payload逃出这个引号内容,然后调用这个alert函数
" οnclick="alert(1)
是将图中test替换为以上内容,后再input里用onclick
οnclick="alert(1)" 是 HTML 中的一个内联事件处理属性,用于在元素被点击时触发 JavaScript 代码
这里用onclick所以我们这里点击input的输入框就会调用alert函数进入下一关
level-3
这个和上一关差不多,先用上一关方法试试
' οnfοcus='alert(1)' autofocus='
onfocus 是 HTML 中的事件属性,当元素获得焦点时会触发后面的脚本;
autofocus 则是让元素自动获得焦点的属性。
两者结合后,页面加载时元素会因 autofocus 自动聚焦,进而触发 onfocus 中的脚本执行,这是典型的利用 HTML 属性注入脚本的 XSS 攻击模式,可能被用于窃取用户信息、篡改页面内容等恶意行为。
成功了,而且不需要点击输入框。level-4
这一关也是有个输入框,继续上一关方法试试,感觉这次不太行了 ``` " οnfοcus="alert(1)" autofocus=" ```
level-5
这个关也是差不多
还用那个方法试试
这次不行了
他在我们o后面加了个_,把我们的方法破坏了
我们换个方法
"><a href=javascript:alert(1)>下一关</a>
<a href=javascript:alert(1)> 是创建了一个超链接,其href属性使用了javascript:伪协议,当用户点击该链接时,会执行alert(1)这个 JavaScript 代码,弹出内容为1的对话框。level-6
依旧输入框试试上一关方法href被过滤了
不行了在我们r后面加_了
onfocus+autofocus也不行也被过滤我们试试将href中的re换成大写
"><a hREf=javascript:alert()>下一关</a>
level-7
先试试上一关方法
"><a href=javascript:alert()>下一关</a>
它吧我们的href和script给删掉了"><a hrhrefef=javasscriptcript:alert()>下一关</a>
把被删了的字段放进原本的字段里,它让刚开始检查不出来,把第一个href和script删掉时,我们直接形成了第二个href和script。成功过关
原型链污染
原型和原型链都是来源于对象而服务于对象的概念,所以我们要先明确一点: JavaScript中一切引用类型都是对象,对象就是属性的集合。 Array类型、Function类型、Object类型、Date类型、RegExp类型等都是引用类型。 也就是说 数组是对象、函数是对象、正则是对象、对象还是对象。
原型和原型链到底是什么呢,我学的时候,也懵逼了好长时间。 定义:每个 JavaScript 函数(构造函数)都有一个 prototype 属性,指向一个对象(原型对象)。 作用就是为了共享属性和方法,所有通过该构造函数创建的实例都能访问原型上的属性和方法。
这里的原型对象是什么,实例对象有是什么?
我们看一个例子
function Person(name) {
this.name = name; // 实例属性(每个实例独立)
}
const person1 = new Person("Alice"); // person1 是实例对象
const person2 = new Person("Bob"); // person2 是另一个实例对象
实例对象就是这么简单,那原型对象呢
function Animal(name) {
this.name = name;
}
Animal.prototype.color = 'white';
var cat1 = new Animal('大毛');
var cat2 = new Animal('二毛');
cat1.color // 'white'
cat2.color // 'white'
这个例子里cat1和cat2是实例对象,图中的animal的prototype就是cat1和cat2的原型对象 JavaScript 规定,每个函数都有一个prototype属性,指向一个对象。就是object
cat1.color--->Animal.prototype---->aaaaaaa.prototype------>xxxxx.------->Object.prototype---->null
如果一层层地上溯,所有对象的原型最终都可以上溯到Object.prototype
,即Object
构造函数的prototype
属性。也就是说,所有对象都继承了Object.prototype
的属性。这就是所有对象都有valueOf
和toString
方法的原因,因为这是从Object.prototype
继承的。 那么,Object.prototype
对象有没有它的原型呢?回答是Object.prototype
的原型是null
。null
没有任何属性和方法,也没有自己的原型。因此,原型链的尽头就是null
。
prototype和__proto__和constructor 有什么区别呢
1,prototype
每个函数(Function)默认拥有的属性,指向一个对象(原型对象)。
当函数被用作构造函数(通过 new 调用)时,新创建的对象会继承原型对象的属性和方法。
后面将他和其他两个结合起来说
2.proto
每个对象(包括数组、函数)默认拥有的属性,指向创建它的构造函数的 prototype 对象。
JavaScript 通过 proto 实现原型链查找,当访问一个对象的属性时,引擎会先查找对象本身,再通过 proto 逐级向上查找。
const person = new Person("Alice");
console.log(person.__proto__ === Person.prototype); // true
3.constructor
每个原型对象默认拥有的属性,指向创建该原型的构造函数 用于标识对象的 “类型”,或通过它创建新实例。
prototype
对象有一个constructor
属性,默认指向prototype
对象所在的构造函数。
function P() {}
P.prototype.constructor === P // true
由于constructor
属性定义在prototype
对象上面,意味着可以被所有实例对象继承。
function P大() {}
var p小 = new P大();
//应该有这个属性吗
p小.constructor === P大 // true
p小.constructor === P大.prototype.constructor // true
这个还是非常清晰的
上面代码中,p
是构造函数P
的实例对象,但是p
自身没有constructor
属性,该属性其实是读取原型链上面的P.prototype.constructor
属性。
constructor
属性的作用是,可以得知某个实例对象,到底是哪一个构造函数产生的。
constructor
属性表示原型对象与构造函数之间的关联关系,如果修改了原型对象,一般会同时修改constructor
属性,防止引用的时候出错。
function Person(name) {
this.name = name;
}
Person.prototype.constructor === Person // true
Person.prototype = {
method: function () {}
};
Person.prototype.constructor === Person // false
Person.prototype.constructor === Object // true
上面代码中,构造函数Person
的原型对象改掉了,但是没有修改constructor
属性,导致这个属性不再指向Person
。由于Person
的新原型是一个普通对象,而普通对象的constructor
属性指向Object
构造函数,导致Person.prototype.constructor
变成了Object
。 应该能看拿出来哪里修改了原型对象吧,Person.prototype = { method 这里修改了(我就不写玩了OVO),这个看懂了后面都清晰的。
所以,修改原型对象时,一般要同时修改constructor
属性的指向。
C.prototype.method1 = function (...) { ... };
这个方法比较好,建议用这个,这个比较清晰,易于理解
//定义基类animal
function Animal(type) {
this.type = type;
}
Animal.prototype.move = function() {
console.log(`${this.type} is moving`);
};
//定义子类dog
function Dog(name) {
this.name = name;
Animal.call(this, "Dog"); // 继承属性
}
// 设置 Dog 的原型为 Animal 的实例
Dog.prototype = Object.create(Animal.prototype);
// 修复 constructor
Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() {
console.log("Woof!");
};
const dog = new Dog("Buddy");
dog.move(); // 输出: Dog is moving(通过原型链访问 Animal.prototype)
dog.bark(); // 输出: Woof!
console.log(dog.constructor === Dog); // true
Animal 构造函数:创建对象时设置 type 属性(如 Dog、Cat)。
Animal.prototype.move:所有 Animal 实例共享的方法,通过原型链访问。
Dog 构造函数: 设置 name 属性(如 "Buddy")。 通过 Animal.call(this, "Dog") 调用父类构造函数,将 type 设置为 "Dog"。 效果:每个 Dog 实例都有 name 和 type 属性。
Object.create(Animal.prototype): 创建一个新对象,其 proto 指向 Animal.prototype。 将该对象赋值给 Dog.prototype,使 Dog 实例可以访问 Animal.prototype 的方法(如 move())。 原型链结构: dog.proto ---> Dog.prototype ---> Animal.prototype ---> Object.prototype。