硬核拆解:OBO 2025大会徽章与挂绳上的硬件CTF挑战

本文深入解析了STAR Labs为Off-By-One 2025安全会议设计的可黑客徽章及其挂绳挑战。内容涵盖徽章的硬件迭代、电路设计,以及多个基于ESP32-C3微控制器的CTF挑战,涉及引导程序、分区表、HMAC硬件外设、红外通信协议等实质性技术细节。

Badge & Lanyard Challenges @ OBO 2025

May 28, 2025 · 14 min · Manzel Seet & Sarah Tan

目录

引言

我们带着第二届Off-By-One大会回来了——在这里,比特遇见面包板,漏洞备受庆祝!🐛⚡ 如果你热衷于硬件和物联网安全,有一件事是肯定的:STAR Labs SG的徽章绝非普通的会议纪念品。今年的徽章不仅是一件收藏品,它更是为好奇者准备的游乐场,满载着由数月研究和黑客技术启发的新挑战。是的,夺旗赛(CTF)也回归了,带来了更丰富的技术内容。

在深入探讨新内容之前,让我们快速回顾一下去年的“章鱼”徽章。那个小家伙很有个性——真的。它有两个圆形的屏幕作为眼睛,与会者可以尽情地赋予他们的章鱼伙伴各种情绪。有些人甚至发挥创意,黑进去自定义了图形。向那两位加拿大人致敬。

与通常那种即插即用、解决谜题的USB徽章不同,我们一直在尝试为其增加学习价值。我们将徽章变成了硬件黑客的实践入门——想想I2C、SPI和MicroPython的“怪癖”。哦,还记得那个小型电压毛刺挑战吗?没错,都在同一块板子上。我们希望这能为那些首次涉足硬件的人带来一些“啊哈!”顿悟时刻。

快进到2025年:我们把技术宅🤓模式调到了最高档——注入了一点“回到未来”的能量(顺便一提,这也是今年T恤设计之一)。是的,我们正式火力全开:挂绳现在也成为了挑战的一部分。你没听错——挂绳黑客现在也是正经事了。

为了让你深入了解幕后,我们有Sarah和Manzel带你走过整个幕后过程——从设计选择到构建可黑客硬件的快乐与混乱。系好安全带。这会是一段有趣的旅程。

挂绳挑战

嗨,我是Sarah,STAR Labs的创意负责人 :)

我的任务是为今年的Off By One 2025设计一个挂绳挑战,说实话,我一开始不知道如何下手。是的,我听说过加密和密码,但我以前从未真正用它们来构建过谜题。

我们想要一个需要多根挂绳按正确顺序匹配和排列才能揭示密钥/旗帜的谜题。总共有四种颜色的挂绳:与会者、演讲者、工作人员和赞助商。棘手的是?我们每种颜色制作的数量并不相等!拥有绿色挂绳的赞助商数量最少——而这正是最终谜题的隐藏之处。

只有按正确顺序排列挂绳,正确的代码才会出现。这是我早期构思的一个版本,它使用了MD5哈希和夜图密码(nyctograph cipher)。最初我想把密码切割开,分散到不同的挂绳上,这样人们就必须按正确的顺序匹配才能得到完整的文本。

我向几位同事(包括Jacob)寻求想法,最终决定使用ROT13替代MD5哈希。我们也放弃了将密码切割成碎片的计划。

我们确实在办公室进行了实地测试以确保谜题是可解的,但工作人员花了一点时间才明白他们看到的是什么;相当一部分人以为那些密码只是设计图案。一个低音谱号被巧妙地隐藏在挂绳中,以帮助指示它们应有的正确顺序。

看着学生们一旦意识到挂绳上那些“随机”符号其实有意义后四处忙碌的样子,既好笑又令人满足。一旦他们获得了旗帜/密钥,就可以用它来解锁今年徽章中隐藏的最终密钥短语。

趣闻: 徽章中埋藏着一个线索——一张《爱丽丝梦游仙境》中白兔子的插图,并附有“跟随白兔”的文字。这既是一个提示也是一个彩蛋,因为夜图密码正是刘易斯·卡罗尔本人发明的!

徽章设计

现在让我们来讨论并看看我们全新的徽章设计!我们的新设计是一个融合了复古主题用户界面的机器人角色。

在硬件组件方面,我们探索了具有相对较高分辨率图形和显示更新率的LCD。有了这个,我们得以加入GIF播放支持,这让我们能够为我们的小机器人朋友带来可爱的动画面部表情。

作为一个不错的点缀,我们还为演讲者添加了定制化的名牌。

现在,让我把时间交给Manzel,这位创造了电路和CTF挑战的电子奇才。

硬件电路

嗨,我是Manzel,STAR Labs SG的硬件工程师。 构思一个新的硬件电路总是很有趣,因为我们旨在融入我们在研究过程中探索过的新功能。

去年的一个关键反馈是徽章太重了,因此我们决定减少到2节AAA电池,有效减重25%。然而,由于电池容量减小,我们换用了更新、更节能的ESP32-C3,它比ESP32-S3更省电。它还有一个单核RISC-V,这也是学习RISC-V架构的一个好平台。

在内部,我们制作了3个原型,它们看起来是这样的:

我们的第一个蓝色原型是一个LED点阵。它获得了不错的反馈,因为它很好地契合了会议的复古主题。然而,我们后来发现失败率很高,因为小LED很脆弱。 用塑料LED进行DIY修复并不容易。这意味着拆卸焊接维修工作可能会损坏/过热周围的LED。

因此我们决定转向LCD显示屏。对于绿色原型,我们考虑了两款不同的显示屏。 虽然较大的矩形显示屏比例更好,但其像素密度和对比度较差,使我们的艺术作品看起来太模糊。 较小的LCD具有更高的像素密度和刷新率。由于会议时间所剩无几,我们决定采用较小的LCD,因为我们可以呈现GIF动画,这在其他会议徽章中并不常见。

总体时间线如下:

  • 1月23日:蓝色原型制造
  • 2月6日:蓝色原型反馈与测试
  • 3月5日:绿色原型制造
  • 3月17日:绿色原型反馈与测试
  • 4月7日:最终徽章批量生产
  • 4月25日:CTF挑战编程
  • 5月8日:大会日!

通常,每个原型的制造需要大约2周时间,硬件和固件测试大约需要3周。

硬件CTF挑战

对于今年的挑战,我们决定重点关注硬件外设,如引导程序和内部寄存器。此外,我们还将简要了解复古无线通信。

1. 欢迎旗帜(类别:欢迎)

挑战: 欢迎来到硬件黑客世界!这里有一面旗帜:

第一个旗帜是一个理智检查,与会议的复古主题相符。条形码的历史可以追溯到20世纪50年代,之后很快被用于库存追踪。 请注意,屏幕的黑色边框在某些手机应用中会干扰扫描。你可以在扫描前拍摄特写照片或裁剪掉屏幕边框。

扫描条形码以获取旗帜。 旗帜: {WelcomeToObo2025}

2. 复古音乐(类别:通信)

挑战: 我的老板让我创作一些音乐。我直接用AI生成了。

在这个挑战中,播放了一段音调序列。仔细聆听,我们可以识别出2种不同的音调。因此,我们可以立即排除莫尔斯电码,因为莫尔斯电码由时长不同(即长或短)的单一音调组成。 相反,我们推断这两种不同的音调可以代表二进制。这是频移键控(FSK)的一个简单例子。具体来说,它是二进制FSK(BFSK),它只用两组不同的[音频]频率来表示二进制0和1。 FSK特别用于早期的无线电通信,如寻呼机和拨号调制解调器。 我们可以通过音频频谱分析仪来识别它们。如果没有电脑,可以使用手机应用——例如Spectroid。 从频谱图中,我们首先识别出幅度最大的两个峰值频率。在这种情况下,随着音乐播放,我们看到2730Hz和3141Hz的脉冲。我们可以放大这个范围,忽略其他频率的噪声。

通过观察,选择低频率音调代表“0”,而高频率音调代表“1”。从这里开始,可以将其转换为旗帜的ASCII表示。

旗帜: {FSK_On_A_Budget}

活动摊位(类别:通信)

挑战: 我找到了一个遥控器,它似乎能做更多事情。我设法用Flipper Zero捕获了信号。

1
2
3
4
5
6
7
8
9
Filetype: IR signals file
Version: 1
# 
name: button_name
type: raw
frequency: 38000
duty_cycle: 0.5
data: 1040 104 208 104 416 104 520 104 104 104 104 104 104 104 104 104 520 104 104 520 104 104 520 208 104 208 104 104 104 104 520 312 104 104 104 104 104 104 520 104 520 104 104 104 520 104 416 208 104 104 520 208 208 104 312 104 520 104 104 104 104 208 208 104 520 208 104 208 312 104 520 104 520 104 104 104 520 104 104 208 104 208 104 104 520 208 312 104 208 104 520 208 208 208 208 104 520 208 104 208 312 104 520 104 104 416 208 104 520 208 104 208 312 104 520 104 104 104 104 208 208 104 520 312 104 208 208 104 520 104 520 104 104 104 520 104 624 208 520 104 104 416 104 208 520 104 624 208 520 104 104 416 104 208 520 104 104 104 520 104
# end of file

这个挑战涉及一个红外(IR)信号转储。红外通信通常用于遥控器向电视或Soundbar等其他设备发送命令。有许多工具可以用来复制这些IR信号并重新播放。 从文档中,我们了解到Flipper Zero将解调后的信号保存为.ir文件:

Flipper Zero通过其内置的IR接收器以38 kHz的载波频率捕获和解调IR信号。对于已知协议的遥控器,Flipper Zero会自动解码IR信号。如果遥控器的协议未知,Flipper Zero将以RAW格式记录信号。

我们还看到.ir文件格式以微秒为单位呈现原始数据。这是逻辑电平变化的时间,它在数字逻辑1和0之间交替。参考Infrared Flipper File Formats。 因此,从提供的数据 data: 1040 104 208 ...,逻辑捕获看起来会像这样:

我们可以先把它转换成比特流。

1
2
3
4
5
ir_data = "1040 104 208 104 ... 520 104"
for i, num in enumerate(ir_data.split(" ")):
    value = 1 if ((i % 2) == 0) else 0
    for x in range(int(num)):
        print(f"{value}")

然而,这个比特流还不能直接解释为数据。通信协议可能包含额外的位,如前导码、校验和或指示帧开始和结束的位。因此,手动解析会很繁琐。 通常,工程师会使用像PulseView这样的逻辑分析仪程序来识别和解码未知信号。这是一个开源程序,支持解码多种不同的协议。 导入数据后,我们可以缩小到仅异步协议,因为只提供了一个信号/通道。同样,我们通过取最短脉冲104us来确定波特率,这给出了大约9600 Hz。 最后,我们可以将其导入为.csv文件,然后信号可以以9600的波特率解码为UART。

放大后,还可以观察到起始位和停止位是如何被检测到的。

旗帜: {UART_Over_Infrared_?!?!}

诊断模式(类别:引导程序)

挑战: 哎呀!工程团队在生产固件中留下了诊断测试模式。可能有个彩蛋。

诊断模式或内部菜单常见于电子设备中,用于检查硬件和软件问题。例如,你可以在打开设备时按住某些组合键以进入智能手机的恢复模式或笔记本电脑的诊断模式。 了解ESP32-C3微控制器的引导程序支持测试模式是很有用的。可以通过在启动时按下按钮来触发测试应用。参考 CONFIG_BOOTLOADER_NUM_PIN_APP_TEST 的说明。 如果我们按下“向下”按钮重启徽章,它会进入测试模式。完成诊断程序以在屏幕上看到旗帜。

旗帜: {Disable_Unnecessary_Diagnostics_Or_Engineering_Test_Modes_If_Possible}

隐藏分区(类别:引导程序)

挑战: 我不知道IoT微控制器可以有多个分区。

要解决这个问题,我们需要了解引导程序支持分区。存在一个分区表。在ESP32-C3中,默认情况下分区表位于闪存的0x8000地址。 https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-guides/partition-tables.html 我们可以使用esptool和gen_esp32part.py从这个闪存偏移量转储分区表。

1
2
3
4
$ esptool.py read_flash 0x8000 0xc00 binary_partitions.bin
$ python3 gen_esp32part.py binary_partitions.bin input_partitions.csv
Parsing binary partition input...
Verifying table...

分区表看起来像这样

1
2
3
4
5
6
7
8
9
$ cat input_partitions.csv
# Name,           Type,   SubType,    Offset,     Size
flag_xorkey,      data,   undefined,  0x9000,     4K
nvs,              data,   nvs,        0xa000,     8K
circuitpython,    app,    ota_0,      0x10000,    1536K
ctf,              app,    ota_1,      0x190000,   760K
user_fs,          data,   fat,        0x24e000,   1416K
diagnostics,      app,    test,       0x3b0000,   316K
flag_enc,         data,   undefined,  0x3ff000,   4K

这里我们注意到分区名称 flag_xorkey 位于偏移量0x9000,而 flag_enc 在偏移量0x3ff000。 以下是使用esptool转储内容的方法

1
2
$ esptool.py read_flash 0x9000 0x1000 flag_xorkey.bin
$ esptool.py read_flash 0x3ff000 0x1000 flag_enc.bin

最后,我们可以将内容进行异或以获得旗帜。

1
2
$ pip3 install xortool
$ xortool-xor -f flag_enc.bin -h "$(xxd -p flag_xorkey.bin)"

ESP分区表将闪存划分为多个段,例如引导程序、应用程序、OTA更新和数据存储。我们可以将多个应用程序嵌入到单个闪存中,加密分区并隐藏一些旗帜 {IoT_Device_Bootloader_Partition_Scheme}旗帜: {IoT_Device_Bootloader_Partition_Scheme}

HMAC Oracle(类别:引导程序)

挑战: 你找到了某人的硬件令牌。显然它可以生成HMAC,你能找到密钥吗?通过USB串口访问。提示:ESP32-C3上的HMAC外设。

首先,这是一个硬件挑战,而不是密码学挑战。我们应该首先了解HMAC硬件外设是如何工作的。

HMAC(基于哈希的消息认证码)模块为SHA256-HMAC生成提供硬件加速,使用烧录到eFuse块中的密钥。HMAC使用预共享的密钥工作,并为消息提供真实性和完整性。

有一些关于eFuse块的引用。它是一个一次性可编程区域,通常用于IoT系统中的永久配置。在这种情况下,它用于存储仅供HMAC外设使用的密钥。 这里的一个配置错误是未启用密钥保护。写入eFuse的HMAC密钥可以通过编程工具espefuse.py读取出来。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    $ espefuse.py dump --port /dev/ttyACM0
        espefuse.py v4.6.2
        Connecting...
        Detecting chip type... ESP32-C3
        BLOCK0          (       ) [0 ] read_regs: 00000100 00000000 08000000 00000000 80000000 00000000
        MAC_SPI_8M_0    (BLOCK1 ) [1 ] read_regs: b0df1be0 000034cd 00000000 03100000 70f0e330 00070ab0
        BLOCK_SYS_DATA  (BLOCK2 ) [2 ] read_regs: 17b68a5c c97867c1 f1c688f4 6a4548b3 99346b61 1aea3e8a d6538926 00000009
        BLOCK_USR_DATA  (BLOCK3 ) [3 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_KEY0      (BLOCK4 ) [4 ] read_regs: 726f467b 5f746f67 5f79654b 64616552 5f74756f 746f7250 69746365 207d6e6f
        BLOCK_KEY1      (BLOCK5 ) [5 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_KEY2      (BLOCK6 ) [6 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_KEY3      (BLOCK7 ) [7 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_KEY4      (BLOCK8 ) [8 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_KEY5      (BLOCK9 ) [9 ] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
        BLOCK_SYS_DATA2 (BLOCK10) [10] read_regs: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000

        BLOCK0          (       ) [0 ] err__regs: 00000000 00000000 00000000 00000000 00000000 00000000
        EFUSE_RD_RS_ERR0_REG        0x00000000
        EFUSE_RD_RS_ERR1_REG        0x00000000

        === Run "dump" command ===

这里我们在 BLOCK_KEY0 (BLOCK 4) 中看到了旗帜。 旗帜: {Forgot_Key_Readout_Protection}

附加说明: 在设置任何一次性可编程(OTP)存储器时,一旦编程完成,应通过启用所有密钥安全功能来确保读取保护。此外,我们应该激活安全启动以防止最终用户执行任意代码,否则这些代码可能被用于通过硬件功能窃取密钥。绕过此类读取保护的挑战是一个重要的研究领域。一个显著且最近的例子是RP2350 Hacking Challenge,其最终目标就是破解OTP保护。

结语

衷心感谢所有参与者使这次大会取得成功。我们希望你们喜欢我们在挂绳和技术徽章中融合的挑战。欢迎提出反馈,我们将以此为基础,为明年的迭代创造新想法!

© 2026 STAR Labs

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计