[LLeaves] Patch Preloader to TA with Mtkclient

[LLeaves] Patch Preloader to TA with Mtkclient

2025년 2월 13일

서론 #

quarkslab의 갤럭시 A 시리즈 취약점 재현 과정에서 스택과 힙 주소를 알아와야 했는데, PoC 를 작성한 사람도 lk 패치 후 함수 콜 상황의 힙을 덤프해서 에뮬레이터의 환경을 실제 기기와 맞춰줬다고 했다.

필요한 위치에서의 힙, 스택주소를 덤프하기만 해도 해결될 것으로 생각돼서 로그를 남기도록 lk를 패치하는 방향으로 결정하게 됐다.

이 글 역시 SC-56B 를 기준으로 작성된다.

참고 글. LLeaves 블로그
Quarkslab 정리 - SC-56B


Mtkclient #

BROM 모드 접근 방법 #

mtkclient는 BROM 모드로 접근 후 취약점을 이용해서 원하는 파티션에 플래시하는 도구인데 삼성 기기는 BROM 모드에 접근하려면 BROM TP를 알아야 한다.

멀티미터를 연속성모드로 설정하고 usb를 연결하지 않은 상태에서 확실한 GND (노란색 원으로 표시한 쉴드커버나 프레임 금속 하우징) 와 연결해보면 작은패드 중 아래쪽은 0 옴으로 수렴하며 GND인 것을 알 수 있고, 위쪽은 1800옴 정도로 어딘가(SoC 신호라인 = BROM TP)에 연결된 것으로 파악할 수 있다.

이때 이 두개를 핀셋으로 연결한 상태로 usb를 연결하면 PC에서 미디어텍 usb 연결을 인식하고 이 상태가 BROM 모드이며 mtkclient를 사용할 수 있는 상태가 된다.

c5deae12-ab42-45b6-82c2-df72a8ca10f1


부팅 순서 #

부팅프로세스 - secure boot


공격 순서 #

공격의 목표는 TA를 수정하는 것인데, 바이너리가 램에 로드되는 과정마다 한번씩 무결성 검사를 수행해서 검증된 바이너리만 로드되도록 구현되어 있기 때문에 단계별로 모든 이미지를 수정해야한다.

3edb1511-23c4-4da7-a465-1c9f4be21c91

PreLoader #

C 환경 설정, 타이머, GPIO, PMIC, UART, I2C, DRAM 등의 초기화 작업을 수행하며, LK(Little Kernel) 이미지를 DRAM으로 로드하여 기본적인 실행 환경을 구성한다.

LK 이미지에 대한 무결성 검사를 우회하기 위해 패치해야한다.

Little Kernel #

수정된 Android boot.img를 부팅할 수 있도록 안드로이드의 Secure Boot의 검사를 비활성화한다.

Android 시스템의 boot.img #

root 권한을 부여하고, 공급업체(vendor) 파티션에 있는 Gatekeeper 를 수정해야 한다.

TEE #

  • TEEGRIS: 신뢰할 수 있는 애플리케이션(Trusted Application, TA)의 검증을 비활성화 한다.
  • TA(Trusted Applet): Gatekeeper 애플리케이션을 패치하는 것이 목표입니다.

코드 패치 과정 #

1. Preloader #

preloader 획득 및 확인 #

python mtk.py dumppreloader

mtkclient의 parse_preloader 코드에서 확인해보면, 0x014D4D4D 가 preloader의 시그니쳐이고, daaddr과 dadata 를 찾아서 리턴해준다.

 1# mtkclient/Library/mtk_class.py
 2def parse_preloader(self, preloader):
 3  if magic == 0x014D4D4D:
 4    self.info("Valid preloader detected.")
 5    daaddr = unpack("<I", data[0x1C:0x20])[0]
 6    # dasize = unpack("<I", data[0x20:0x24])[0]
 7    # maxsize = unpack("<I", data[0x24:0x28])[0]
 8    # content_offset = unpack("<I", data[0x28:0x2C])[0]
 9    # sig_length = unpack("<I", data[0x2C:0x30])[0]
10    jump_offset = unpack("<I", data[0x30:0x34])[0]
11    daaddr = jump_offset + daaddr
12    dadata = data[jump_offset:]
13  else:
14    self.warning("Preloader detected as shellcode, might fail to run.")
15    daaddr = self.config.chipconfig.da_payload_addr
16    dadata = data
17  return daaddr, dadata

#

2. lk #

3. … #

comments powered by Disqus