1.개요
임베디드 장비에 존재하는 취약점을 분석하기 전에 가장 먼저 선행되어야 하는 작업은 바로 펌웨어 추출이다. 펌웨어를 얻지 못한다면, 아무리 블랙박스 기반으로 취약점을 찾는다 해도 문제에 봉착할 수 밖에 없다. 따라서 오늘은 임베디드 장비에 존재하는 펌웨어를 어떻게 추출할 수 있는지 알아보고자 한다.
2. 펌웨어 추출 방법
•
UART, SoC, Flash Memory를 우선 식별한다.
•
일반적으로 UART 포트는 제거되어있는 경우가 대부분이므로 우선적으로 GND, RX, TX, VCC 핀을 식별하여 핀 헤더를 납땜하는게 편하다.
2.1 UART 이용
•
보드에서 VCC, RX, TX, GND 핀을 각각 식별해야한다. 핀 식별 방법은 통전 테스트나 로직 분석기를 이용할 수 있다.
UART 부분에 핀 정보가 기입되어 있는 장비도 있고 없는 장비도 있다. 만약 없는 경우 직접 식별하여 구분해줘야 한다.
2.1.1 통전 테스트
통전 테스트를 위해 flash memory 핀 맵을 이용할 수 있다.
해당 보드는 실제 UART 핀이 기입되어 있지만, 없다고 가정하고 설명하겠습니다
•
우선적으로 GND를 찾기위해 멀티테스터기 리드선을 flash memory의 GND에 대고 나머지 리드선을 UART 핀에 하나씩 갖다대, 소리가 나는 핀을 식별한다. 소리가 나는 핀이 바로 GND이다.
•
UART에서 GND를 찾았으니 VCC를 찾을 차례이다. 멀티 테스터기의 - 리드선(검은색)을 GND에 대고 나머지 핀들의 전압을 측정한다.
•
보통 공유기의 경우 3.2~3.4V 사이의 전압이 고정적으로 출력되는 핀이 VCC이다. RX의 경우는 전압이 거의 측정되지 않으며 Tx 핀의 경우 VCC와는 다르게 전압이 고정적으로 출력되지 않는다.
•
RX의 경우 전압이 측정되지 않는다.
•
따라서 현재 GND, VCC, RX 핀을 식별했으며 자동적으로 나머지 핀이 TX 핀이라는 것을 알 수 있다.
•
만약 통전 테스트를 통해 GND는 식별 했지만 나머지 핀들이 전부 전압이 출력되는 경우 핀 식별이 헷갈릴 수 있다. 이런 경우는 로직 분석기를 이용하여 보다 쉽게 핀 식별을 할 수 있다.
2.1.2 Logic Analyzer
•
로직 분석기를 이용하면 좀 더 간편하게 UART 핀을 식별할 수 있다. GND를 제외한 3개의 핀을 각각 CH1,CH2,CH3에 연결한다.
•
saleae사의 Logic 분석 프로그램을 이용하였다. 선택한 CH1,CH2,CH3은 실제 Channel 0,1,2에 각각 매핑되기 때문에 헷갈리면 안된다.
•
우측 상단의 Analyzers 탭에서 Async Serial을 각각의 3개 채널에 추가한다음 실행시켜 패킷을 잡아본다.
•
현재 Channel 0에 부팅 로그가 담긴 패킷 캡쳐된다 . 따라서 0번 채널이 Tx 핀이라는 것을 알 수 있다.
•
추가적으로 로직 분석기를 이용하면 UART에서 사용되는 baud rate를 확인할 수 있다. 예를 들어 위 사진의 경우 119.403으로 나오는데 대략적으로 해당 범위와 가장 근접한 115,200을 baud rate로 사용하면 된다.
UART 핀 예시
UART 핀 식별이 끝났으면 이제 직접 연결하여 확인한다.
- TX : 데이터 송신
- RX : 데이터 수신
- GND : 그라운드
- VCC : 전압
•
TX와 RX는 항상 반대이다. 공유기 UART의 TX는 공유기에서 데이터 송신이므로 실제 PC에서의 RX와 연결시켜야 한다.
•
공유기의 RX는 공유기에서 데이터를 수신하므로 실제 PC에서 TX와 연결시켜야 한다.
•
위 그림과 같이 UST to TTL 장비를 이용해서 공유기의 UART와 PC를 연결할 수 있다.
•
현재 공유기 파워를 연결했기 때문에 따로 VCC는 연결하지 않고 나머지 핀들만 연결하였다.
•
위 사진 처럼 PC와 공유기 UART를 연결하고 baud rate를 맞춘 뒤 putty나 xshell을 이용하여 부팅 로그를 확인하면 된다.
•
baud rate를 잘 맞춰주면 위 그림과 같이 부팅로그가 나온다. 여기서 크게 3가지 경우로 나뉜다.
2.1.3 특정 키를 입력하거나, 쉘이 떨어는 경우
•
특정 키를 입력하여 인터럽트를 걸거나 그냥 바로 쉘이 떨어지는 경우가 있다.
•
내부에 ftp나 telnet 바이너리가 있다면 디버깅 환경 구축과 원하는 바이너리를 추출할 수 있다.
•
해당 케이스가 가장 이상적이다.
2.1.4 부트로더 쉘(?)에 접속하여 펌웨어 덤프
예시 화면
•
부팅 로그 도중 인터럽트를 발생시키거나 그냥 부트로더 쉘이 떨어지는 경우가 있다.
•
사용할 수 있는 명령어를 보면 특정 주소에서 특정 사이즈 까지 read 할 수 있다는 것을 알 수 있다. 이를 이용해서 부팅 로그를 확인하여 펌웨어 덤프를 뜰 수 있다.
•
위 사진은 하나의 예시이며 다른 형식으로 부트로더 쉘이 떨어질 수 도 있다.
•
해당 케이스인 경우 부팅 시에 flash memory → memory로 펌웨어가 적재되는 것을 이용하여 파이썬으로 시리얼 통신 코드를 작성 후 펌웨어를 추출할 수 있다.
2.1.5. 글리칭을 통해 부트 쉘 접속 후 2번 항목 시도
•
uart 로그를 확인해봐도 로그만 출력되고 아무 반응이 없는 경우가 있다. 그 중 만약 부팅 로그에 펌웨어 체크섬을 계산하는 로그가 확인되는 경우, (없어도 시도해보쟈)
•
글리칭 어택을 통해 강제로 전압 공급을 유입시켜 부트로더 쉘을 띄울 수 있는 경우가 있다.
•
해당 내용은 위 링크에서 확인 가능하다.
2.2 Flashrom 프로그램 이용
•
8핀 짜리 Flash memory는 위 그림과 같은 장비를 이용해서 다리에 후크를 걸고 flashrom 프로그램을 이용하여 덤프를 뜰 수 있다.
•
flashrom은 flash memory를 쉽게 읽을 수 있게 도와주는 프로그램으로 라즈베리파이에 설치하여 바로 사용 가능하다.
flashrom 지원 가능한 칩일 경우
•
flash memory chip을 자세히 보면 위에 모델명이 적혀있다. 해당 모델의 datasheet를 검색하여 핀맵을 확인한 뒤 라즈베리파이 연결하여 flashrom을 이용하면 된다.
Flash memory pin map
라파 - Flash memory 매핑
•
데이터 시트를 검색하여 핀 맵을 확인한 뒤, 라파의 SPI 핀과 매핑하여 다음 명령어를 실행시키면 된다.
$ sudo flashrom -r [생성할 이름] -p linux_spi:dev=/dev/spidev0.0 , spispeed=8000
Bash
복사
flashrom에서 지원하는 칩이 아닌 경우
•
flashchips.h 코드를 보면 flashrom에서 지원 가능한 모델 명을 확인할 수 있다.
•
만약 추출하려는 칩이 flashrom에서 지원하지 않는 경우도 있다. 그런 경우 datasheet를 분석하여 필요한 정보를 획득한 후 flashchips.c 파일에 비슷한 칩 모델의 구조를 분석 후, 코드를 추가 한 뒤, 빌드를 다시 하면 된다
예시
•
2.3 spi 통신 코드이용
만약 flashrom을 이용한 펌웨어 덤프가 실패한 경우, spi 통신을 이용하여 직접 플래시메모리에서 데이터를 읽어오는 방법을 시도할 수 있다. 해당 방법의 경우 직접 대상 플래시메모리의 데이터 시트를 분석하여 코드를 작성해야 한다.
•
데이터 시트를 잘 보면 특정 명령에 따른 opcode를 확인 할 수 있다. Read Data 의 경우 Opcode는 3이며 제조사 ID를 읽는 Opcode는 0x9F이다.
•
예를 들어 제조사 ID를 읽고 싶으면 0x9F 클럭 주기에 맞춰 Opcode 한바이트와 더미 값 2바이트 총 3바이트를 보내면 된다.
•
그렇게 되면 Data Output 라인으로 한바이트의 제조사 ID를 읽을 수 있다.(뒤에 디바이스 ID 2바이트도 딸려온다는 것을 확인할 수 있다.)
import sys
from spidev import SpiDev
...
def probe(self):
try:
data = self.__spi.xfer([0x9f, 0, 0, 0])
return data[1], data[2] << 8 | data[3]
Python
복사
•
데이터 시트를 통해 필요한 정보를 어느정도 파악했다면 파이썬을 이용하여 spi 통신을 구현할 수 있다.
•
위 코드는 플래시메모리의 제조사 ID와 디바이스 ID를 읽어오는 코드이다. spidev 모듈을 사용하였으며 spi.xfer 함수를 이용하여 spi 통신 구현을 할 수 있다.
•
xfer을 통해 0x9f와 한바이트의 더미값을 보낸 후 그 결과 값을 data 변수에 담는다.
•
정상적으로 0x9F Opcode가 전송되면 data[1]에 제조사 ID, data[2]-data[3]에 디바이스 ID가 담긴다.
•
이런식으로 데이터 시트를 분석하여 Spi 통신을 이용하여 read하는 코드를 구현하면 된다.
•
2.4 Rom Writer 사용
만약 위 방법이 다 실패하거나 혹은 해당 장비를 가지고 있을 경우 쉽게 펌웨어를 추출 할 수 있다.
Rom Writer는 플래시메모리를 쉽게 읽거나 쓰거나 수정할 수 있는 장비로 저가부터 고가까지 다양한 금액대가 존재한다.
•
롬라이터로 가장 유명한 Beeprog가 있다.
•
해당 장비는 대략 100만원 ~ 150만원 정도 하며 비싼 장비인 만큼 지원하는 모델이 많다는 장점이 있다. 하지만 8핀이냐 onboard 형식이냐 혹은 크기에 따라 각각에 맞는 소켓을 추가적으로 구매해서 사용해야 한다.
•
저가형 롬라이터로 xgecu 가 있다.
•
가격은 대략 10만원 안팍이며 해당 장비 역시 추가적인 소켓이 필요하다.
내가 사용해본 롬라이터는 위 두개이다. 이 외에도 찾아보면 다양한 종류의 롬라이터가 존재한다. 롬라이터를 이용하면 해당 장비에서 지원하는 소프트웨어를 설치하여 read하면 끝이다. 예를 들어 beeprog를 이용하는 경우 다음과 같다.
•
사이즈에 맞는 소켓을 구매하여 플래시 메모리를 디솔더링하여 뽑아낸다.
•
뽑아낸 칩을 소켓에 넣은 후 beeprog와 연결한다.
•
beeprog와 pc와 연결한 뒤 beeprog 프로그램을 연다.
•
메뉴얼 대로 실행하여 플래시메모리 read를 한다.
•
정상적으로 read가 되면 위 사진과 같이 플래시 메모리 정보가 읽힌다.
•
해당 내용을 저장하여 파일 시스템 추출을 진행하면 된다.
•
beeprog에서 read 기능을 바로 사용하여 되는 경우도 있고 몇가지 설정을 변경해야지 되는 경우도 있기 때문에 주의해서 사용해야 한다.
3. 결론
위 방법으로 펌웨어는 대부분 추출 가능하다. 하지만 펌웨어 추출에 성공을 하여도 실제 분석을 진행하기 위해선 펌웨어 내부에 들어있는 파일 시스템을 추출해야 한다. 파일 시스템은 binwalk 같은 오픈소스를 이용하여 추출할 수 있다. 파일 시스템을 추출하였다면 이제 본격적으로 에뮬레이팅, 리버싱 등을 통하여 취약점 분석을 진행하면 된다.