-
-
[原创][Googlectf2022]硬件题weather
-
发表于: 2023-3-1 16:42 14670
-
之前尝试挖iot设备的漏洞大多是基于对固件的分析,对硬件层面的东西还不太熟悉,正好开学数电计组连考积累了一点点这方面最基础的东西,于是就找了这道题来做个人了解硬件的一个入门
当时看着xuanxuan师傅的博客,遇到了很多问题也学到了许多零散的知识,总结如下:
(1)I2C总线
(2)SPI协议
(3)EEPROM
(4)8051的特殊寄存器sfr
下载下来的赛题环境里有个docker,从dockerfile里可以看出运行在1337端口:
使用如下命令运行docker:
然后使用nc指令就能连上了:
附件通过一个pdf提供了赛题的集成电路图:
可以看出:
(1)存在5个传感器,通过IIC总线连接
(2) EEPROM与8051通过SPI协议相连,并且都有IIC总线接口
(3)Flag位于8051的FlagROM
分析题目提供的源码:
通过标志性的语法“sfr at”可以分析出这个是基于SDCC开发的
可以下载一个SDCC的官方编译器,甚至默认安装选项里就会自动添加环境变量(windows环境),非常方便,在windows的cmd中输入如下命令便可编译:
编译得到如下文件
其中ihx是可逆向的文件,笔者使用的ida7.7貌似不能直接识别为8051,需要手工选择:
程序提供了两个形式的输入指令:
其中对port有个检查:
发现有个明显的漏洞,就是对于匹配的“101”字符,输入“101xxxxxxx”居然也能通过这个匹配
结合后面的str_to_int8可以得到最终的端口号为"101xxxx % 256"
又因为:
所以:
又因为IIc总线最多支持128个设备,于是我们从101120~101120+128爆破扫描128次即可爆破出所有端口
爆破脚本:
扫出来一个题目没有的33号端口,十分可疑
用r指令读一下(读最大的128字节)试试:
结合ida逆向的ihx文件:
发现这两者高度吻合,那么基本可以确定33号port就是EEPROM了,也就是EEPROM挂载到了8051的I2C总线上了
但是存在一个问题——ihx文件里面从0x40开始是有东西的,但是输出里显示从"\x44\x00"过后就没东西了
从pdf里可以看出只输出了一页的内容
那么如果把8051的固件全部dump下来,我们需要对EEPROM的页进行切换
注意到给的pdf的这里:
"W"指令有一个PageIndex,选择写EEPROM是写具体的哪一页,那么我们应该能够通过如下指令切换EEPROM的页:
也就是在不提供wirtekey的情况下实现EPPROM页的切换:
把这64页都读一遍就可以把8051的固件dump下来了
dump脚本:
把dump下来的文件放入ida发现和ihx文件几乎没有差别
紧接着我们就要考虑如何通过往EEPROM里擦写,劫持8051的运行流,读取FlagROM里的flag
基于EEPROM的物理特性,对它进行写操作只能将bit的1写为0,而不能把0写为1(有点类似于画沙画)。
而且它的擦写规则是写入目标数据取反,也就是0xFF减这个数据
从ihx逆向里可以发现一个风水宝地:
从0x9E9开始的很大一段区域都是0xFF
但实际通过r指令读第40页的内容,发现并不是从0x9E9开始,而应该是0xA02(也可以用winhex等软件查看):
那么我们就可以在这段区域布置shellcode,并修改前面的某个跳转指令劫持8051的运行流到这块shellcode
对于这题,运行流劫持最方便的手法肯定是绝对地址跳转
逆向发现一共有两种绝对地址跳转:
(1)ljmp:
(2)lcall:
指令形式非常好理解:
我们希望得到一个0x[b~f]00的地址,那么只需要考虑aa的情况(因为bb异或bb就直接清零了)
经过寻找发现这里比较合适,因为这里正好有个0x7E,改改就能改成0x0E(完全参考了xuanxuan师傅的思路):
(ps:这里用的是dump下来的文件,和题目给的编译出来的会有差别)
改成这个样子:
然后在0xe00处布置好shellcode就行了
写这个shellcode主要是对FLAGROM_ADDR和FLAGROM_DATA以及SERIAL_OUT_DATA这三个SFR的操作:
如果用c语言写的话应该是这样:
直接编译然后用它的汇编有点长,所以考虑对着这篇博客的手册手搓汇编和机器码:
https://blog.csdn.net/qq_30787727/article/details/111239582
汇编:
编译出的机器码:
那么我们就可以根据机器码然后转成10进制(题目格式)数了
学到了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直播授课
赞赏
- 西湖论剑2024 IOT赛后复盘及mqtt rce详解 13830
- 对某嵌入式设备声波配网的研究 11919
- DAS10月月赛PWN出题心路&&CVE-2023-40930的介绍 11355
- [原创]关于Nokelock蓝牙锁破解分析 21860
- [原创]基于树莓派的蓝牙调试环境搭建 24487