题目介绍:
There's a very busy bus we've tapped a port onto,surely there is some juicy information hidden in the device memory...somewhere...
环境搭建
先从GitHub上拉取题目:https://github.com/cromulencellc/hackasat-qualifier-2020题目文件夹结构大致如下
题目采用了docker来搭建环境,其中challenge是题目文件夹,solver是解题文件夹,分别都存在一个Dockerfile来生成对应的镜像。一般来说我们复现时,直接执行如下命令对环境进行测试。由于该系列题目是2020年的,可能搭建环境时存在某些问题,在本题当中没有遇到,后续遇到时再给出对应的方法。
sudo make build
sudo make test
如上图则环境搭建成功复现时,我们只需要使用如下命令启动challenge的服务即可
socat -v tcp-listen:31340,reuseaddr exec:"docker run --rm -i -e SEED=1234 -e FLAG=flag{skIpn1MnWtBC1DkTFhKBO8yMofVTRU8qUuMxc52jzss1XrnNva6Td2Ex84XJZCoNa6RQKoFYhKPNItMpHtocOxD} bus:challenge"
之后使用nc连接服务指定的ip和端口即可
当我们nc连接上去以后,看见一系列的字符,书上的解释是说这是I²C协议,学过I²C的不知道能看出来不
相关背景知识
I²C协议简单了解
I²C仅仅使用两条线在连接到总线的设备间传输信息,一条为串行数据线(SDA),另一条为(SCL)。总线上的地址由唯一地址区分。通常I²C在硬件设备中作为传感器接口和EEPROM存储器的接口使用。I²C协议的基础信号有四种,分别是起始信号(START)、停止信号(STOP)、应答信号(ACK)和非应答信号(NAK)。
-
START信号和STOP信号都由主设备产生。所有信号以START信号开始,以STOP信号结束。START信号和STOP信号之间的时间认为I²C总线处于忙碌(busy)阶段。
-
I²C有着完善的应答机制,每个字节后面必须跟一个ACK信号或者NAK信号。
通信格式
-
由于START和STOP是主设备发出的,所在START后的7bit是从设备地址,然后有1bit的读/写标志位,该标志位说明了主设备要对从设备进行读(“1”)或者写(“0”)操作,如果对应的从设备在总线上,那该从设备将以ACK信号应答。
向某个设备写入数据如下图:
从某个设备读取数据
同时应为主办方在设计题目时,将flag的长度作为一个有意义的变量参数参与了读取EEPROM的内容计算,而EEPROM的大小是有限的,所以可能会出现以下情况(这里参考源码)
当Flag的内容过长时,则flag前后的内存空间显然会变小,这时候将要读取的内容的范围很可能与flag的范围重叠,因此可能会造成Flag的泄露。
分析解题
解法一:
将nc连上去的产生的回显信息中的特殊符号”+^.”去掉,然后将十六进制转换为ASCII字符串。得到如下
可以看见包含flag字符串,将这些字符串拼接去掉重复的有几率可以得到正确的flag。
解法二
对回显信息进行分析,发现都是以^和.来区分数据的。那么认为”^“就是I²C的START信号,而”.“就是I²C的STOP信号,那么我们按照前面的分析对回显进行分析。
^82+00+00+1f+00+00+00+12+47+40+41+c6+97+e1+3f+89+81+3f+c1+99+1d+a1+c0+20+18+a1+40+5e+42+ac+3c+.
^83+00+00+3f+.
^82+00+00+3f+00+00+00+20+fa+3f+41+c8+da+e2+3f+a6+64+3f+c1+ff+33+a1+c0+a4+d2+a0+40+de+50+55+40+.
^b4+01+a9+94+c7+59+78+58+87+6b+d3+8e+04+be+2a+47+d4+cc+f8+6e+6c+26+67+a6+98+5e+4a+75+69+63+79+20+44+61+74+61+20+30+33+.
^83+00+00+1f+.
^82+00+00+1f+00+00+00+1d+ec+3f+41+d8+04+e5+3f+67+4e+40+c1+38+1b+a1+c0+e1+dd+a0+40+b9+91+91+3c+.
我们知道”^“信号是START信号,则紧跟其后的一个字节为从设备地址以及1bit的读写标志位,发现所有的”^“之后只有0x82、0x83、0xb4 3种情况,将其的读写位和地址位分开则有如下
“
这里一开始我本来是怀疑这个从设备地址书上是不是写错了,看了源码之后发现,在源码中它会将我们输入的第一个字节作为设备地址跟0xfe进行AND操作,发现它的确实地址就是0x82、0x83以及0xb4
”
第一个字节
|
从设备地址
|
读/写标志位
|
I²C语义
|
0x82
|
0x82
|
0
|
写
|
0x83
|
0x83
|
1
|
读
|
0xb4
|
0xb4
|
0
|
写
|
按照表中的信息来看,我们的设备只有两台,地址分别是0x82和0xb4。如果按照表中的信息解析的话,则^83的操作是读操作,但是在回显信息当中以^83开头的只有两种情况如下
^83+00+00+3f+.
^83+00+00+1f+.
且有效内容长度仅仅为3个字节,同时1f为非打印字符,对于读取flag来讲,这不太现实。所以本题将实际的I²C语义进行了反转,结果如下
第一个字节
|
从设备地址
|
读/写标志位
|
本题实际语义
|
0x82
|
0x82
|
0
|
读
|
0x83
|
0x83
|
1
|
写
|
0xb4
|
0xb4
|
0
|
读
|
结合上表再进行分析
#从ID:0x82的设备读取内容
^82+00+00+1f+00+00+00+12+47+40+41+c6+97+e1+3f+89+81+3f+c1+99+1d+a1+c0+20+18+a1+40+5e+42+ac+3c+.
#向ID:0x82的设备写入内容
^83+00+00+3f+.
#从ID:0x82的设备读取内容
^82+00+00+3f+00+00+00+20+fa+3f+41+c8+da+e2+3f+a6+64+3f+c1+ff+33+a1+c0+a4+d2+a0+40+de+50+55+40+.
#从ID:0xb4的设备读取内容
^b4+01+a9+94+c7+59+78+58+87+6b+d3+8e+04+be+2a+47+d4+cc+f8+6e+6c+26+67+a6+98+5e+4a+75+69+63+79+20+44+61+74+61+20+30+33+.
#向ID:0x82的设备写入内容
^83+00+00+1f+.
#从ID:0x82的设备读取内容
^82+00+00+1f+00+00+00+1d+ec+3f+41+d8+04+e5+3f+67+4e+40+c1+38+1b+a1+c0+e1+dd+a0+40+b9+91+91+3c+.
顺便画个图
根据对回显信息的分析,猜测读b4的条件就是82的某个参数是否为特定值,大致流程就是,先读82确认某个参数,发现参数没有被设置,则对82进行写操作改变该参数,之后再次对82进行读操作确认参数已经被设置,确认完毕则对b4进行读操作。并且b4的数据一直都是比较长的,所以猜测flag应该在b4这个设备当中。同时回去注意到README当中
* OBC (Onboard computer - bus master)
* EEP (Some kind of device which contains "juicy data" + the flag)
* EPS (Electrical power system controller)
表明了flag存在于EEP当中,则我们这里b4就是EEP设备。我们知道,写的操作只有如下两种
^83+00+00+3f+.
^83+00+00+1f+.
通过之前的分析可以知道,在读EEP的内容时,82的内容为^83+00+00+3f,当我们对b4读完之后,会先对82在进行一次写操作,其操作为^83+00+00+1f。对此总结可知读EEP的整个流程操作大致如下
-
先对82进行读操作,确认其某个参数是否被设置;82初始状态为:^83+00+00+1f
-
发现82某个参数没有被设置,对82进行写操作,将其对应的参数设置
-
再次对82进行读操作,确认该参数被设置
-
对EEP(b4)进行读操作
-
对82进行写操作使其恢复为初始状态,操作为:^83+00+00+1f
-
对82进行读操作确认是否恢复初始状态
很明显可以知道,82控制着EEP的开和关,同样从README当中可知其名为EPS,EPS和EEP的关系如下
EEP电源开/关(82)
|
EPS状态(十六进制)
|
EPS状态(二进制)
|
关
|
1f
|
0001 1111
|
开
|
3f
|
0011 1111
|
很明显可以看见,只需修改第六位就可以控制EEP的开与关而在题目描述中
There's a very busy bus we've tapped a port onto, surely there is some juicy
information hidden in the device memory... somewhere...
The OBC is tying up the bus most of the time.
说明了bus非常的忙,理论上我们这里需要将除了EEP设备其他都关闭,但同时题目也说到,做轮询操作,期间间隔短
By looking at the traffic, we see the OBC enabling power to the device,
reading from it (in random areas that don't contain the flag), and
turning it off afterwards. After that, there's a short pause
(a few seconds) before the polling starts again.
在我们肉眼可见的情况下表现出来的形式就是,会短暂的停一下,所以我们需要在这个时间段发送I²C信号来获得flag。我们知道EEP开启只需要第六位为1即可,则我们应该对82做如下操作:^83+00+00+20.
“
复现过程中发现20并不唯一,存在多个参数可解;
”
输入后成功的关闭了除了EEP的其他设备,接下来只需要将EEP的内容读出来即可,操作如下
“
这里在关闭了除了EEP的其他设备后,b4后面的参数已经不重要了,对b4的读操作会将EEP中的所有内容都读出来。复现过程中尝试搞明白为什么在^83+00+00+20.操作过后就能够关闭其他设备,从而直接泄露EEP的全部内容,但是有些操作好像在源码中不能直接看出来,直接略过。有明白的师傅可以交流一下!!!
”
^b4111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
结果如下,只要将 ^ + 符号去掉,再将十六进制转为字符串即可
参考文章:
https://cloud.tencent.com/developer/article/2332362
|