Search

펌웨어 추출 방법

태그
Embedded
Property
그림12.png

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
jhcloos
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 파일에 비슷한 칩 모델의 구조를 분석 후, 코드를 추가 한 뒤, 빌드를 다시 하면 된다
예시
해당 내용은 http://layer7.kr/blog_detail.php?post=embedded 에서 자세히 확인 가능하다.

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 같은 오픈소스를 이용하여 추출할 수 있다. 파일 시스템을 추출하였다면 이제 본격적으로 에뮬레이팅, 리버싱 등을 통하여 취약점 분석을 진행하면 된다.