首页
社区
课程
招聘
[原创][Googlectf2022]硬件题weather
发表于: 2023-3-1 16:42 14670

[原创][Googlectf2022]硬件题weather

2023-3-1 16:42
14670

之前尝试挖iot设备的漏洞大多是基于对固件的分析,对硬件层面的东西还不太熟悉,正好开学数电计组连考积累了一点点这方面最基础的东西,于是就找了这道题来做个人了解硬件的一个入门

当时看着xuanxuan师傅的博客,遇到了很多问题也学到了许多零散的知识,总结如下:
(1)I2C总线
(2)SPI协议
(3)EEPROM
(4)8051的特殊寄存器sfr

下载下来的赛题环境里有个docker,从dockerfile里可以看出运行在1337端口:
img
使用如下命令运行docker:

img
然后使用nc指令就能连上了:

img

附件通过一个pdf提供了赛题的集成电路图:
img
可以看出:
(1)存在5个传感器,通过IIC总线连接
(2) EEPROM与8051通过SPI协议相连,并且都有IIC总线接口
(3)Flag位于8051的FlagROM

分析题目提供的源码:

通过标志性的语法“sfr at”可以分析出这个是基于SDCC开发的
可以下载一个SDCC的官方编译器,甚至默认安装选项里就会自动添加环境变量(windows环境),非常方便,在windows的cmd中输入如下命令便可编译:

编译得到如下文件
img
其中ihx是可逆向的文件,笔者使用的ida7.7貌似不能直接识别为8051,需要手工选择:
img
程序提供了两个形式的输入指令:

其中对port有个检查:
img
发现有个明显的漏洞,就是对于匹配的“101”字符,输入“101xxxxxxx”居然也能通过这个匹配
结合后面的str_to_int8可以得到最终的端口号为"101xxxx % 256"
又因为:
img
所以:
img
img
又因为IIc总线最多支持128个设备,于是我们从101120~101120+128爆破扫描128次即可爆破出所有端口
爆破脚本:

img
扫出来一个题目没有的33号端口,十分可疑
用r指令读一下(读最大的128字节)试试:
img
结合ida逆向的ihx文件:
img
发现这两者高度吻合,那么基本可以确定33号port就是EEPROM了,也就是EEPROM挂载到了8051的I2C总线上了
但是存在一个问题——ihx文件里面从0x40开始是有东西的,但是输出里显示从"\x44\x00"过后就没东西了
img
从pdf里可以看出只输出了一页的内容
img
那么如果把8051的固件全部dump下来,我们需要对EEPROM的页进行切换
注意到给的pdf的这里:
img
"W"指令有一个PageIndex,选择写EEPROM是写具体的哪一页,那么我们应该能够通过如下指令切换EEPROM的页:

也就是在不提供wirtekey的情况下实现EPPROM页的切换:
img
把这64页都读一遍就可以把8051的固件dump下来了
dump脚本:

把dump下来的文件放入ida发现和ihx文件几乎没有差别

紧接着我们就要考虑如何通过往EEPROM里擦写,劫持8051的运行流,读取FlagROM里的flag

基于EEPROM的物理特性,对它进行写操作只能将bit的1写为0,而不能把0写为1(有点类似于画沙画)。
而且它的擦写规则是写入目标数据取反,也就是0xFF减这个数据
从ihx逆向里可以发现一个风水宝地:
img
从0x9E9开始的很大一段区域都是0xFF
但实际通过r指令读第40页的内容,发现并不是从0x9E9开始,而应该是0xA02(也可以用winhex等软件查看):
img
那么我们就可以在这段区域布置shellcode,并修改前面的某个跳转指令劫持8051的运行流到这块shellcode

对于这题,运行流劫持最方便的手法肯定是绝对地址跳转
逆向发现一共有两种绝对地址跳转:
(1)ljmp:
img
(2)lcall:
img
指令形式非常好理解:

我们希望得到一个0x[b~f]00的地址,那么只需要考虑aa的情况(因为bb异或bb就直接清零了)
经过寻找发现这里比较合适,因为这里正好有个0x7E,改改就能改成0x0E(完全参考了xuanxuan师傅的思路):
img
(ps:这里用的是dump下来的文件,和题目给的编译出来的会有差别)
改成这个样子:
img
然后在0xe00处布置好shellcode就行了

写这个shellcode主要是对FLAGROM_ADDR和FLAGROM_DATA以及SERIAL_OUT_DATA这三个SFR的操作:

如果用c语言写的话应该是这样:

直接编译然后用它的汇编有点长,所以考虑对着这篇博客的手册手搓汇编和机器码:

https://blog.csdn.net/qq_30787727/article/details/111239582

汇编:

编译出的机器码:

img

那么我们就可以根据机器码然后转成10进制(题目格式)数了

img

学到了SDCC的基础语法和汇编指令,对硬件层面又有了新的认识

https://xuanxuanblingbling.github.io/iot/2022/11/02/8051/

docker build -t hardware:weather .
docker run --privileged -p xxxx:1337 -d hardware:weather
docker build -t hardware:weather .
docker run --privileged -p xxxx:1337 -d hardware:weather
nc 127.0.0.1 1337
nc 127.0.0.1 1337
 
#include <stdint.h>
#include <stdbool.h>
 
#ifndef NULL
#define NULL ((void*)0)
#endif
 
// Secret ROM controller.
__sfr __at(0xee) FLAGROM_ADDR;
__sfr __at(0xef) FLAGROM_DATA;
 
// Serial controller.
__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 controller.
__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 address
__sfr __at(0xe7) I2C_READ_WRITE;
 
// Power controller.
__sfr __at(0xff) POWEROFF;
__sfr __at(0xfe) POWERSAVE;
 
const char *ALLOWED_I2C[] = { //五个传感器
  "101"// Thermometers (4x).
  "108"// Atmospheric pressure sensor.
  "110"// Light sensor A.
  "111"// Light sensor B.
  "119"// Humidity sensor.
  NULL
};
 
int8_t i2c_write(int8_t port, uint8_t req_len, __xdata uint8_t *buf) {
  while (I2C_STATUS == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  I2C_BUFFER_XRAM_LOW = (uint8_t)(uint16_t)buf;
  I2C_BUFFER_XRAM_HIGH = (uint8_t)((uint16_t)buf >> 8);
  I2C_BUFFER_SIZE = req_len;
  I2C_ADDRESS = port;
 
  I2C_READ_WRITE = 0// Start write.
 
  int8_t status;
  while ((status = I2C_STATUS) == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  return status;
}
 
int8_t i2c_read(int8_t port, uint8_t req_len, __xdata uint8_t *buf) {
  while (I2C_STATUS == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  I2C_BUFFER_XRAM_LOW = (uint8_t)(uint16_t)buf;
  I2C_BUFFER_XRAM_HIGH = (uint8_t)((uint16_t)buf >> 8);
  I2C_BUFFER_SIZE = req_len;
  I2C_ADDRESS = port;
 
  I2C_READ_WRITE = 1// Start read.
 
  int8_t status;
  while ((status = I2C_STATUS) == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  return status;
}
 
const char *i2c_status_to_error(int8_t err) {
  switch (err) {
    case 0: return "i2c status: transaction completed / ready\n";
    case 1: return "i2c status: busy\n";
    case 2: return "i2c status: error - device not found\n";
    case 3: return "i2c status: error - device misbehaved\n";
  }
 
  return "i2c status: unknown error\n";
}
 
void serial_print(const char *s) {
  while (*s) {
    while (!SERIAL_OUT_READY) {
      // Busy wait...
    }
 
    SERIAL_OUT_DATA = *s++;
  }
}
 
char serial_read_char(void) {
  while (1) {
    if (SERIAL_IN_READY) {
      return (char)SERIAL_IN_DATA;
    }
 
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
}
 
struct tokenizer_st {
  char *ptr;
  int replaced;
};
 
void tokenizer_init(struct tokenizer_st *t, char *str) {
  t->ptr = str;
  t->replaced = 0x7fff;
}
 
char *tokenizer_next(struct tokenizer_st *t) {
  if (t->replaced != 0x7fff) {
    *t->ptr = (char)t->replaced;
  }
 
  while (*t->ptr == ' ') {
    t->ptr++;
  }
 
  if (*t->ptr == '\0') {
    return NULL;
  }
 
  char *token_start = t->ptr;
  for (;;) {
    char ch = *t->ptr;
    if (ch != ' ' && ch != '\0') {
      t->ptr++;
      continue;
    }
 
    t->replaced = *t->ptr;
    *t->ptr = '\0';
    return token_start;
  }
}
 
uint8_t str_to_uint8(const char *s) {
  uint8_t v = 0;
  while (*s) {
    uint8_t digit = *s++ - '0';
    if (digit >= 10) {
      return 0;
    }
    v = v * 10 + digit;
  }
  return v;
}
 
void uint8_to_str(char *buf, uint8_t v) {
  if (v >= 100) {
    *buf++ = '0' + v / 100;
  }
 
  if (v >= 10) {
    *buf++ = '0' + (v / 10) % 10;
  }
 
  *buf++ = '0' + v % 10;
  *buf = '\0';
}
 
bool is_port_allowed(const char *port) { //前缀匹配
  for(const char **allowed = ALLOWED_I2C; *allowed; allowed++) {
    const char *pa = *allowed;
    const char *pb = port;
    bool allowed = true;
    while (*pa && *pb) {
      if (*pa++ != *pb++) {
        allowed = false;
        break;
      }
    }
    if (allowed && *pa == '\0') {
      return true;
    }
  }
  return false;
}
 
int8_t port_to_int8(char *port) {
  if (!is_port_allowed(port)) {
    return -1;
  }
 
  return (int8_t)str_to_uint8(port);
}
 
#define CMD_BUF_SZ 384
#define I2C_BUF_SZ 128
int main(void) {
  serial_print("Weather Station\n");
 
  static __xdata char cmd[CMD_BUF_SZ];
  static __xdata uint8_t i2c_buf[I2C_BUF_SZ];
 
  while (true) {
    serial_print("? ");
 
    int i;
    for (i = 0; i < CMD_BUF_SZ; i++) {
      char ch = serial_read_char();
      if (ch == '\n') {
        cmd[i] = '\0';
        break;
      }
      cmd[i] = ch;
    }
 
    if (i == CMD_BUF_SZ) {
      serial_print("-err: command too long, rejected\n");
      continue;
    }
 
    struct tokenizer_st t;
    tokenizer_init(&t, cmd);
 
    char *p = tokenizer_next(&t);
    if (p == NULL) {
      serial_print("-err: command format incorrect\n");
      continue;
    }
 
    bool write;
    if (*p == 'r') {
      write = false;
    } else if (*p == 'w') {
      write = true;
    } else {
      serial_print("-err: unknown command\n");
      continue;
    } //首先获取操作符
 
    p = tokenizer_next(&t);
    if (p == NULL) {
      serial_print("-err: command format incorrect\n");
      continue;
    }
 
    int8_t port = port_to_int8(p); //获取操作端口
    if (port == -1) {
      serial_print("-err: port invalid or not allowed\n");
      continue;
    }
 
    p = tokenizer_next(&t);
    if (p == NULL) {
      serial_print("-err: command format incorrect\n");
      continue;
    }
 
    uint8_t req_len = str_to_uint8(p);//获取操作长度
    if (req_len == 0 || req_len > I2C_BUF_SZ) {
      serial_print("-err: I2C request length incorrect\n");
      continue;
    }
 
    if (write) { //将写入数据从str转换为uint8数组
      for (uint8_t i = 0; i < req_len; i++) {
        p = tokenizer_next(&t);
        if (p == NULL) {
          break;
        }
 
        i2c_buf[i] = str_to_uint8(p);
      }
 
      int8_t ret = i2c_write(port, req_len, i2c_buf);
      serial_print(i2c_status_to_error(ret));
    } else {
      int8_t ret = i2c_read(port, req_len, i2c_buf);
      serial_print(i2c_status_to_error(ret));
 
      for (uint8_t i = 0; i < req_len; i++) {
        char num[4];
        uint8_to_str(num, i2c_buf[i]);
        serial_print(num);
 
        if ((i + 1) % 16 == 0 && i +1 != req_len) {
          serial_print("\n");
        } else {
          serial_print(" ");
        }
      }
 
      serial_print("\n-end\n");
    }
  }
 
  // Should never reach this place.
}
#include <stdint.h>
#include <stdbool.h>
 
#ifndef NULL
#define NULL ((void*)0)
#endif
 
// Secret ROM controller.
__sfr __at(0xee) FLAGROM_ADDR;
__sfr __at(0xef) FLAGROM_DATA;
 
// Serial controller.
__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 controller.
__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 address
__sfr __at(0xe7) I2C_READ_WRITE;
 
// Power controller.
__sfr __at(0xff) POWEROFF;
__sfr __at(0xfe) POWERSAVE;
 
const char *ALLOWED_I2C[] = { //五个传感器
  "101"// Thermometers (4x).
  "108"// Atmospheric pressure sensor.
  "110"// Light sensor A.
  "111"// Light sensor B.
  "119"// Humidity sensor.
  NULL
};
 
int8_t i2c_write(int8_t port, uint8_t req_len, __xdata uint8_t *buf) {
  while (I2C_STATUS == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  I2C_BUFFER_XRAM_LOW = (uint8_t)(uint16_t)buf;
  I2C_BUFFER_XRAM_HIGH = (uint8_t)((uint16_t)buf >> 8);
  I2C_BUFFER_SIZE = req_len;
  I2C_ADDRESS = port;
 
  I2C_READ_WRITE = 0// Start write.
 
  int8_t status;
  while ((status = I2C_STATUS) == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  return status;
}
 
int8_t i2c_read(int8_t port, uint8_t req_len, __xdata uint8_t *buf) {
  while (I2C_STATUS == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  I2C_BUFFER_XRAM_LOW = (uint8_t)(uint16_t)buf;
  I2C_BUFFER_XRAM_HIGH = (uint8_t)((uint16_t)buf >> 8);
  I2C_BUFFER_SIZE = req_len;
  I2C_ADDRESS = port;
 
  I2C_READ_WRITE = 1// Start read.
 
  int8_t status;
  while ((status = I2C_STATUS) == 1) {
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
 
  return status;
}
 
const char *i2c_status_to_error(int8_t err) {
  switch (err) {
    case 0: return "i2c status: transaction completed / ready\n";
    case 1: return "i2c status: busy\n";
    case 2: return "i2c status: error - device not found\n";
    case 3: return "i2c status: error - device misbehaved\n";
  }
 
  return "i2c status: unknown error\n";
}
 
void serial_print(const char *s) {
  while (*s) {
    while (!SERIAL_OUT_READY) {
      // Busy wait...
    }
 
    SERIAL_OUT_DATA = *s++;
  }
}
 
char serial_read_char(void) {
  while (1) {
    if (SERIAL_IN_READY) {
      return (char)SERIAL_IN_DATA;
    }
 
    POWERSAVE = 1// Enter power save mode for a few milliseconds.
  }
}
 
struct tokenizer_st {
  char *ptr;
  int replaced;
};
 
void tokenizer_init(struct tokenizer_st *t, char *str) {
  t->ptr = str;
  t->replaced = 0x7fff;
}
 
char *tokenizer_next(struct tokenizer_st *t) {
  if (t->replaced != 0x7fff) {
    *t->ptr = (char)t->replaced;
  }
 
  while (*t->ptr == ' ') {
    t->ptr++;
  }
 
  if (*t->ptr == '\0') {
    return NULL;
  }
 
  char *token_start = t->ptr;
  for (;;) {
    char ch = *t->ptr;
    if (ch != ' ' && ch != '\0') {
      t->ptr++;
      continue;
    }
 
    t->replaced = *t->ptr;
    *t->ptr = '\0';
    return token_start;
  }
}
 
uint8_t str_to_uint8(const char *s) {
  uint8_t v = 0;
  while (*s) {
    uint8_t digit = *s++ - '0';
    if (digit >= 10) {
      return 0;
    }
    v = v * 10 + digit;
  }
  return v;
}
 
void uint8_to_str(char *buf, uint8_t v) {
  if (v >= 100) {
    *buf++ = '0' + v / 100;
  }
 
  if (v >= 10) {
    *buf++ = '0' + (v / 10) % 10;
  }
 
  *buf++ = '0' + v % 10;
  *buf = '\0';
}
 
bool is_port_allowed(const char *port) { //前缀匹配
  for(const char **allowed = ALLOWED_I2C; *allowed; allowed++) {
    const char *pa = *allowed;
    const char *pb = port;
    bool allowed = true;
    while (*pa && *pb) {
      if (*pa++ != *pb++) {
        allowed = false;
        break;
      }
    }
    if (allowed && *pa == '\0') {
      return true;
    }
  }
  return false;
}
 
int8_t port_to_int8(char *port) {
  if (!is_port_allowed(port)) {
    return -1;
  }
 
  return (int8_t)str_to_uint8(port);
}
 
#define CMD_BUF_SZ 384
#define I2C_BUF_SZ 128
int main(void) {
  serial_print("Weather Station\n");
 
  static __xdata char cmd[CMD_BUF_SZ];
  static __xdata uint8_t i2c_buf[I2C_BUF_SZ];
 
  while (true) {
    serial_print("? ");
 
    int i;

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-3-22 10:00 被Nameless_a编辑 ,原因:
上传的附件:
收藏
免费 4
支持
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回
//