2021HWS硬件安全在线夏令营总结
Day1
IOT
IOT是一个完整的信息系统,它包括软件和硬件两个组成部分,也PC不同的是,他们主要的业务就是操控硬件设备,其实IOT在每个人眼中都有不同的看法,人云亦云,对于软件开发者来说,IOT是业务,是设备的的业务实现,对于网络安全从业者来说,就是硬件设备,它硬件上有我们可攻击的漏洞
那么一个完整的IOT设备它应该有云端,设备硬件端 ,APP端,三个切入点
通常我们都是从设备端或者是APP端来进行攻击,那么关注设备端就是关注这个设备从开始到启动的全过程,就是bios -> bootloader -> 操作系统 -> init.d -> 业务
固件
简介
所谓”固件”,顾名思义,就是”固化在设备中的软件”,通常而言指的是 Flash 中被固化、执行固定功能的软件,需要注意的是固件是软件,是嵌入式设备中会被CPU所执行的代码
固件的获取
固件的解包
固件的解包一般有两种方法,binwalk和mount,binwalk在大多数情况下都管用,binwalk不管用的时候,mount可能会有奇效
binwalk
1 | binwalk -Me *.bin #递归提取 |
mount
1 | mkdir file_systems |
逆向分析
逆向分析的思路都是通用的
分析攻击面
输入就意味着存在着接口函数,既然有接口函数那就必然有实现的代码和相对应的硬件,但是需要注意的,我们分析IOT设备不是简单的read之类的输入,还包括很多物理世界的输入,比如USB口,网口等,这些输入都有可能成为一个IOT设备的攻击入口,所以多发散一下思维,没有什么是不可能的!
对于软件层面的分析,那分为两种情况,有shell和无shell,有shell,很好办了,查看一些它开启了哪些进程哪些服务,哪些端口,没有shell,就只能对它进行端口扫描了
定位程序
在前面我们通过收集IOT设备的信息后,我们就可以通过分析攻击的入口来定位,一般在查找的时候都是去查找厂商的二进制文件或者开源组件的漏洞
总结
第一天的知识点还是比较基础的,从不同的角度阐述了IOT究竟是个什么玩意儿,它对于HWS来说有什么意义,对于不同的从业者来说又有什么意思,课程的后半部分就是在围绕着IOT中的一大基本要素——固件,所有的分析都是基于固件来分析的,所以说固件的提取和分析都是很关键的一环!拿到固件就像拿到IOT设备的蓝图,而后续的漏洞挖掘就是在探索地图当中那些神秘而又未知的宝藏!
Day2
流量分析
流量分析中最重要的四个步骤”收,发,断,改”
收和发是流量中最基本的操作,在PWN中通过发送一些畸形的数据来使得程序的执行发生了一下非预期的结果,最终导致我们能够去做一些正常的操作所不能实现的东西
流量就像水流一样,流经哪里就可能会在某个地方留下一定的痕迹,如果我们在”水流”流动的过程中进行抓取,当然想要抓取更多更全面的信息,我们就得在”水流”的主干道上进行抓取,也就是在路由器干路侧放置抓包工具
所以当我们能够抓取到一些流量的时候,那必然可以进行断和改,就像我把”水流”中的水盛一碗上来,盛上来就是一个断的操作,接下来我在换一碗水再倒回去,便是改的操作
但是并不是所有的通信信道我们都可以去接入的,有一些信道是在内网当中的,我们不可能直接套用PWN的那一套,io.sendline()和io.recvuntil(),这不太可能做到,那就需要通过一下内网的边界进行介入,将外网的设备日穿,使得我们可以进入内网通信,这是一种外接内的情况,那还有一种内接外的情况,就是通过钓鱼邮件或者mqtt消息队列来进入内网通信,不多说,都是边界渗透的事
刚刚将的是现实生活中的抓,但我们不可能真的用手在空气中抓取通信报文,不太现实,所以一般我们都会通过树莓派刷上openwrt的代码,让树莓派变成一个小型的路由器,连接到干路进行抓取数据包
这里复现一下课上的实验——基于UDP信道的中间人攻击
先看看nc的参数
1 | -l #监听模式,就是把nc当做一个server来看待,等待连接请求 |
动态调试
软件被运行在调试底座,也就是gdbserver并在上面暴露出调试接口给调试器,也就是gdb,我们通过gdb发送一些调试命令对软件进行调试
既然要进行调试,那必然要暴露接口出来我们才可以进行调试,而这个接口也有很多种,UART,JTAG(Joint Test Action Group(联合测试行为组织,Coresight等等
Day3
交叉编译
交叉编译何为”交叉”?交叉就指的是不同的平台,就比如说我在X86-64
平台下编译一个ARM
架构的程序,就称之为交叉编译,与此对应的就是本地编译,可是为啥会有交叉编译这个概念呢?你本地编译不行吗?我还要再其他平台上编译,然后再送到你的平台上,费这么大劲干嘛?确实不行,由于嵌入式系统本身的原因,它所拥有的资源极其有限,它自己运行程序都够呛了,还给你编译,显然不太现实,所以才有了交叉编译
我们进行交叉编译的时候会用一个叫arm-linux-gnueabi-gcc
,这包含了四个部分
1 | arch [-vendor] [-kernel] [-system] [-language] |
看似这一个很高级的命令就可以编译出来程序,但是其本质还是gcc的那一套,某个编译的参数不同
而gcc
本质是一个工具包,但是这个工具包不只是单纯的是一个个分散的工具,它里面的工具与工具之间会形成一个联系,也就是后面介绍的工具链,gcc
就像一个老大,有很多小弟帮他做事情,在平时编译程序的时候我们只需要gcc -o xxx xxx.c
,就完成了编译,看似简单的背后,gcc
替我们做的事情却不简单,我们来看一下gcc
编译程序的流程
编译的阶段一共分为四个阶段:
- 预处理(Preprocess)
- 编译(Compile)
- 汇编(Assemble)
- 链接(Link)
一个阶段一个阶段来看
预处理
1 | ➜ gcc -E hello.c -o hello.i |
经过预处理之后,我们打开文件之后看到一些宏定义还有函数的定义,这时我们的文件大小也变的特别大,就是因为预处理把include
的包全部解析进来了
编译
接下来就是编译,编译就是将它转化成汇编代码并进行优化
1 | ➜ gcc -S hello.i -o hello.s |
汇编
汇编器几乎是对着语句一句一句的进行转换,到这一步,我们的代码就已经形成了ELF
文件的雏形,在汇编完成之后,这已经是一个ELF
,但是拿去运行的时候并不能跑起来,从流程来看就是少了链接这个步骤,但这个步骤到底完成了什么呢?为什么没有它就不行呢?接下来慢慢介绍!
1 | ➜ gcc -c hello.s -o hello.o |
链接
链接完成后就成为了一个可执行文件,那为什么要链接,程序员早期的代码都是写在一个文件当中的,但随着时间的迁移发现这样编写代码并不利于程序员管理代码,就演变出了各种文件来写不同的代码,所以当一个程序中需要用到其他程序中的函数的时候,链接就发挥了它的作用,它将此程序要用到的函数进行重定位,也就是做一个链接,让它能够顺利的调用这个函数,这就是链接!,打个比方就是再hello
这个程序当中,有两个符号,一个是main
,一个是hello
,main
函数很明显我们直接定义了,但是printf
呢?很显然我们并没有写它的实现代码,对吧!可是为什么我能够调用它来打印hello
呢?答案前面已经讲过了,前人已经帮我们写好了printf
的实现代码,只需要通过链接将程序当中的printf
的地址指向动态链接库的地址就能够调用了!
在拼接(链接)所有目标文件的同时,编译器会确定各个函数加载到内存中的运行地址,然后 反过来修改所有调用该函数的机器指令,使得该指令能跳转到正确的内存地址。这个过程 就是重定位
总之,用通俗一点的说就是把多个文件给它绑到一起,就叫链接!
1 | ➜ gcc hello.o -o hello |
看完编译的过程,我们回头看看每个过程的由那些程序来完成的,前两个步骤,也就是预处理和编译是由GNU GCC
来完成的,后面两个汇编和链接是由GUN Binutils
来完成的,Binutils
是个啥?它有两个程序组成,一个是汇编(as),一个是链接(ld),前面我们也看到了预处理➜编译➜汇编➜链接,每个过程都是一环接一环的,所以才有了编译工具链的概念
讲到这就有个疑问了,Binutils
是个啥?
Binutils
的全称是GNU Binary Utilities
,GNU
二进制工具集合,所谓二进制就是一些0和1的数字,回忆上面的过程,想到汇编过后是不是整个程序就完全变了样,用编辑器打开都是一些十六进制的数字,所以Binutils
就是对二进制的目标进行操控的一个工具集合
在上面我们知道Binutils
主要有两个特别重要的工具:
as
汇编器,把汇编代码转换成相对于的机器码
ld
链接器,将目标文件进行链接,形成一个可执行文件
Binutils
还有其他次要的工具,但你一定用过的工具
readelf
objdump
strings
strip
看完
gcc
的编译过程,作为一位攻击者,很多时候我们编译的都不是一个嵌入式的二进制文件,而是shellcode
,所以除了用arm-linux-gnueabi-gcc
之类的交叉编译工具来编译一个程序,还可以使用pwntools
的make_elf
来生成一个shellcode
后门
1 | from pwn import * |
运行上面的代码就得到了一个后门文件: