STM32 IOT CTF 清华校赛

一、前情提要

昨天带我学习固件安全的轩师傅给我一个他上大学时自己制作的stm32固件,让我按照IOT CTF的形式打一打。第一次尝试IOT CTF,感觉和网安CTF还是有些区别的。过程踩了很多坑,但对我这个初入二进制和固件的新手来说还是很有价值的,因此想记录下来。题目信息和附件手册见轩师傅的Github:STM32 IoT CTF 清华校赛版

二、分析已知信息,梳理逻辑

已有信息和物件

芯片手册、接线图;
固件伪代码;
STM32 F103C6T6A 芯片等相关物理设备。

梳理逻辑


根据芯片手册,我们的代码在flash中存储,芯片上电之后,pc需要指向其中的某个地址。因此在打的时候,全程需要使boot0引脚接地,使芯片执行flash中的代码;
外部晶振全程都要接;
对于每个固件,主函数中必然存在死循环,而代码则正是死循环体中的内容,芯片在不加干预的条件下会无限循环执行下去;
printf这类函数的输出可以使用串口工具接收(UART)。
STM32 F103C6T6A含义参考下图:

三、开打

GPIO:摩斯电码

GPIO控制LED灯的亮灭,伪代码定义了flag每个字符的摩斯代码,通过灯的闪烁规律可获得flag。

UART:接出串口

这里一开始接线的时候掉了个大坑,串口连接不上,半天没搞定。最后问了陈兄才知道芯片48个引脚中所有电源都要接在一块(以前打过的固件看来是全部连通的)。
接好线打开串口工具接收就完事了:

USB_1:模拟键盘

这个USB是一个键盘设备,因此在人机接口设备中。分析伪代码:

void start_usb(){
  if(!digitalRead(PB6) && !digitalRead(PB7)  ){
    while(!USBComposite);
    Keyboard.println("THUCTF{FLAG_USB1}");
    delay(5000);
  }
}

必须要使PB6和PB7接地,然后等待设备初始化完成,鼠标光标放在任何一处可以键入的地方就能自动接收flag。

USB_2:USB上还有1个FLAG你能找到在哪么?


无语,找了半天......反正就当学习了USB设备吧。

SWD:调试和彩蛋

这个有点意思:静态分析和动态调试(先提取出32KB的固件)。
看代码:

void start_swd(){
    char a[200] = "THUCTF{FLAG_SWD2}";
    delay((int)a % 1000);
    int i;
    int j;
    if((int)&i == (int)&j + i){
      Serial1.println("THUCTF{FLAG_SWD1}");
    }
}

既有栈上定义的数组,也有打印函数,说明同时需要ST-Link和UART设备。
这里又踩坑了,需要注意只需ST-Link供电即可,UART除了RX、TX和GND其他不接。
OK了,IDA,启动!

函数很多,想找到很难,但我们看到代码中有个立即数1000,其他函数中都没有,因此可以搜索它的16进制进行定位:

也能看到汇编指令:

现在问题来了,如何找到a数组在内存中的栈?
汇编代码中有一条指令SDIV R0, R5, R3,猜测是delay((int)a % 1000),而前一条指令中另R3为1000,那么作为源操作数之一的R5应该就是a的起始地址。

最后对于条件判断,有两条可行的绕过方法,跳过判断语句,或者让左右两边相等。
核心在这:



绕过方法一:断点断在0x0800069c,执行到这停止,设置pc为0x,再次往后执行就可以串口接收flag了。
绕过方法二:断点断在0x0800069c,修改R2的值,使其等于R3的值,继续执行即可输出并接收。
(一开始0x20000DE0和0x20000094这两个立即数是i和j,太菜了555~)

posted @ 2025-08-28 18:36  小梦贴贴  阅读(92)  评论(0)    收藏  举报