영구 비활성화한 디버그가 다시 켜졌다: 레이저로 뚫린 RP2350 보안의 전말

한눈에 보는 요약
라즈베리파이 RP2350의 A4 리비전에서, 한 번 설정하면 되돌릴 수 없어야 할 "영구 디버그 비활성화"가 레이저 결함 주입(LFI) 으로 무력화되었습니다. 연구진은 Secure 영역의 메모리에 접근하고 코어를 직접 제어하는 데 성공했으며, 최종적으로 해킹 챌린지에 걸려 있던 128비트 비밀값 전체를 추출했습니다.
공격의 뼈대는 세 단계입니다.
- 광자 방출 현미경(PEM) 으로
DEBUGEN비트가 반응하는 발광 위치를 수 마이크로미터 단위까지 좁힘 - 레이저 펄스를 두 지점에 반복 조사해
PROC1과PROC1_SECURE비트를 설정, Secure 디버그를 활성화 - 복구 리셋(rescue reset) 으로 펌웨어 실행 전에 칩을 멈춰 세워, OTP 런타임 잠금이 걸리기 전에 비밀값을 읽어냄
중요한 건 마지막 한 줄입니다. 이 공격에는 파괴적 물리 접근, 하드웨어 보안 전문성, 약 25만 달러 상당의 실험 장비가 필요합니다. 원격으로 재현될 성질의 것이 아니라는 뜻이죠.
RP2350은 원래 어떻게 막고 있었나
RP2350은 부팅 시 소켓마다 Arm Cortex-M33 또는 RISC-V Hazard3를 고를 수 있는 듀얼코어 MCU입니다. 보안 기능도 제법 촘촘합니다.
- 보안 부팅: OTP에 저장된 공개키 지문으로 서명된 펌웨어만 인증
- Armv8-M TrustZone: Secure / Non-secure 영역 분리
- 영구 디버그 비활성화:
CRIT1.DEBUG_DISABLE - 글리치 감지기: 클록·전원 조작에 따른 타이밍 교란 탐지
OTP 메모리는 각 비트를 0에서 1로 단 한 번만 바꿀 수 있고, 중요한 플래그는 연속된 8개 행에 걸친 3-of-8 투표 방식으로, 잠금 비트는 3중 중복 다수결로 인코딩됩니다. 물리적 비트 뒤집기를 이미 염두에 둔 설계죠.
라즈베리파이는 이런 설계를 검증받기 위해 RP2350 Hacking Challenge를 열어 왔습니다. 첫 챌린지는 2024년 8~12월에 원본 칩을 대상으로 진행됐고, 거기서 나온 발견들을 수정해 내놓은 것이 이번 실험 대상인 A4 리비전입니다.
공격의 실마리: DEBUGEN
디버그 차단의 구조는 이렇습니다. CRIT1.DEBUG_DISABLE은 두 코어의 Mem-AP 활성화 신호를 0으로 만들어 버스 접근을 전부 끊습니다. 그런데 Secure 소프트웨어는 메모리 매핑 레지스터 DEBUGEN 을 통해 이를 다시 켤 수 있습니다. 모든 비트를 세우면 영구 비활성화 설정조차 완전히 덮어쓸 수 있죠.
여기서 결정적인 비대칭이 드러납니다.
OTP 보안 필드와 달리,
DEBUGEN에는 비트 중복·패리티·다수결 보호가 데이터시트에 명시되어 있지 않습니다.
즉 OTP는 3중으로 지켜지는데, 그 OTP 설정을 덮어쓸 수 있는 레지스터는 단일 비트라는 이야기입니다.
1단계 — 어디를 쏠지 찾는 문제
레이저 결함 주입의 진짜 난관은 "쏘는 것"이 아니라 "어디를 쏠지"입니다.
흔히 쓰는 명령어 건너뛰기 공격은 코어 파이프라인의 수많은 플립플롭 중 하나만 교란해도 성립하기 때문에 민감 영역이 넓고, 무작위 스캔으로도 찾아집니다. 반면 DEBUGEN의 특정 비트 하나가 저장된 위치를 맹목적으로 스캔하는 건 현실적으로 불가능합니다.
그래서 등장한 것이 광자 방출 현미경(PEM) 입니다. 스위칭하는 트랜지스터가 내뿜는 미약한 근적외선을 반복 측정해 활동 위치를 찾아내는 기법이죠. 절차는 이렇습니다.
- Secure 소프트웨어로 특정
DEBUGEN비트를 반복해서 켜고 끄는 루프를 돌림 - 서로 다른 비트 마스크(
0x3vs0xc)를 쓰는 두 루프를 각각 200회 촬영·평균 - 두 평균 영상의 부호 있는 차이를 구해 공통 배경, 센서 오프셋, 열 방출, 무관한 스위칭 활동을 제거
- 두 설정을 번갈아 측정하고 획득 순서를 균형 있게 섞어 온도 같은 느린 환경 변화의 영향을 상쇄
이 비교를 반복해 카메라 시야의 세 영역에서 비트 0~3과 관련된 작은 발광 지점을 찾아냈고, 이후 레이저 탐색 범위를 수 마이크로미터 규모로 좁혔습니다.
📌 다만 발광 지점은 "비트와 연관된 활동 위치"일 뿐, 저장 셀 자체를 직접 짚어낸 것은 아닙니다. 한 비트에 여러 발광 지점이 나타날 수 있고, 저장 소자인지 주변 논리인지는 레이아웃 데이터 없이 구분할 수 없습니다.
물리적 준비
- 칩을 후면 디캡슐레이션해 패키지를 제거 — 적외선이 전면 금속층에 막히지 않고 실리콘 기판을 통과해 트랜지스터에 닿도록
- 도터보드에 재납땜한 뒤, Ledger Donjon의 오픈소스 플랫폼 Scaffold에 연결
- 리드 프레임 제거로 끊어진 GND는 구리선으로 복구
2단계 — 레이저로 비트 두 개 뒤집기
한 코어에서 Secure 디버그를 쓰려면 Mem-AP 활성화 비트와 Secure 접근 허용 비트가 둘 다 필요합니다.
| 비트 | 이름 | 역할 |
|---|---|---|
| 0 | PROC0 | 코어 0의 Mem-AP 활성화 |
| 1 | PROC0_SECURE | 코어 0 Mem-AP의 Secure 접근 허용 |
| 2 | PROC1 | 코어 1의 Mem-AP 활성화 |
| 3 | PROC1_SECURE | 코어 1 Mem-AP의 Secure 접근 허용 |
| 8 | MISC | CTI, RISC-V 디버그 접근 포트 등 |
실험 조건은 다음과 같았습니다.
- 파장 980nm, 최대 광출력 2.97W 펄스 레이저
- 출력 약 40%인 1.2W, 펄스 폭 100ns, 대물렌즈 50배
- 각 펄스 직후 SWD로 디버그 접근 포트를 검사해 피드백 확보
PEM으로 좁힌 영역을 스캔하며 수 마이크로미터 떨어진 두 지점을 보정했습니다. 첫 지점에서는 코어 1 Mem-AP의 버스 접근이 열리며 PROC1 설정이 확인됐고, 다른 지점에서는 Mem-AP의 Control/Status Word에 SDeviceEn = 1이 나타나 PROC1_SECURE가 섰다고 판단했습니다.
까다로웠던 지점
한 비트를 세우는 펄스가 다른 비트를 지워버리는 현상이 있었습니다. 그래서 상태 피드백에 기대어 왔다 갔다 하는 반복 조사가 필요했습니다.
- 버스 접근이 열릴 때까지
PROC1지점을 조사 SDeviceEn = 1이 될 때까지PROC1_SECURE지점을 조사- 버스 접근이 사라지면 다시 1번으로
위치와 펄스 매개변수를 한 번 보정하고 나니, 수 초 안에 Secure 디버그가 활성화됐습니다. 흥미롭게도 20배 대물렌즈로는 재현되지 않았는데, 조사점이 넓어지면서 비트를 세우는 영역과 지우는 영역을 동시에 건드렸을 가능성이 제기됐습니다.
설정된 두 비트는 추가 펄스나 소프트웨어 쓰기 없이 그대로 유지됐고, Secure 전용인 DEBUGEN을 읽어 0xc를 확인했습니다. 이 읽기가 성공했다는 사실 자체가 Secure 속성 트랜잭션이 통과했다는 증거입니다.
이게 정확히 무엇을 뚫은 건가
오해하지 않도록 선을 그어 둡시다.
- ❌ 보안 부팅이 무너져 서명 없는 펌웨어가 실행되는 것은 아닙니다. 정상 부팅 시 인증은 그대로 유지됩니다.
- ❌ Mem-AP를 열었다고 모든 메모리가 열리는 것도 아닙니다. 직접 접근에는 여전히
ACCESSCTRL과 대상별 권한이 적용됩니다. - ✅ 하지만 디버거는 Secure 코어를 정지시키고, 단일 단계 실행하고, 레지스터를 읽고 수정할 수 있습니다. 코어를 우회로 삼아 데이터를 빼낼 수 있다는 뜻이고, 이는 TrustZone 런타임 격리의 침해입니다.
다시 말해, 인증은 지켜지지만 인증 이후의 런타임 상태는 보호되지 못했습니다.
3단계 — 복구 리셋으로 펌웨어보다 먼저 선수치기
여기서 한 가지 벽이 남습니다. 챌린지 펌웨어는 시작하자마자 페이지 48의 영구 잠금을 확인한 뒤, otp_hw->sw_lock[48]에 0b1111을 써서 Secure/Non-secure 접근을 모두 차단하는 런타임 잠금을 겁니다. 정상 부팅 후라면 Secure 디버그를 얻어도 비밀값은 읽을 수 없습니다.
핵심은 런타임 잠금(소프트 잠금)의 성질입니다.
- OTP 리셋 시 영구
LOCK_S/LOCK_NS값으로 초기화됨 - 펌웨어는 다음 OTP 리셋까지 권한을 강화만 할 수 있고 완화는 불가
- 강화한 값은 리셋 후 유지되지 않음
그렇다면 펌웨어가 잠그기 전에 접근하면 됩니다. 이때 쓰인 것이 상시 접근 가능한 RP-AP의 CTRL.RESCUE_RESTART입니다. 이 비트를 세우면 전체 시스템이 리셋되고, 부트 ROM이 사용자 소프트웨어 실행 전에 대기 상태로 멈춥니다.
- 부트 ROM은 워치독·플래시·USB 부팅보다 먼저
POWMAN_CHIP_RESET.RESCUE_FLAG를 확인하고 지움 - 코어 0은 인터럽트를 끈 대기 루프에, 코어 1은 벡터 대기 경로에 머무름
- 코드상으로는
src/main/arm/varm_boot_path.c의 첫 단계, 그리고arm8_bootrom_rt0.S의varm_wait_rescue→varm_dead_quietWFI 루프 - 데이터시트에는
CTRL.RESCUE_RESTART사용에 대한 제한이 명시되어 있지 않음
최종 추출 순서
- RP-AP로
CTRL.RESCUE_RESTART를1로 썼다가0으로 해제 → 복구 리셋 수행 - 펌웨어가 실행되지 않아
sw_lock[48]이 Secure 읽기/쓰기 허용 상태인 동안, 레이저로PROC1·PROC1_SECURE를 세워DEBUGEN = 0xc확보 - Secure Mem-AP로
DHCSR에 접근해 코어 1을 정지 - 보호된 읽기 인터페이스로 OTP 행
0xc08~0xc0f의 비밀값 판독
128비트 전량 추출 완료. 펌웨어 기반 감시도, 접근 제한도, 이 "부팅 전 추출"을 막지 못했습니다.
DEBUGEN_LOCK은 왜 막지 못했나
DEBUGEN_LOCK은 대응하는 DEBUGEN 비트에 대한 소프트웨어 쓰기를 차단하고, 한 번 세운 잠금 비트는 지울 수 없습니다. 데이터시트가 밝힌 목적은 "우발적인 쓰기 방지" 입니다.
그리고 실험 결과가 그 목적의 한계를 정확히 보여줍니다.
DEBUGEN비트가0이고 잠금 비트가1인 상태에서도, 레이저 펄스는 잠금을 유지한 채DEBUGEN을 설정할 수 있었습니다.
잠금은 "쓰기 경로"를 막지만, 레이저는 쓰기 경로를 거치지 않고 저장 소자를 직접 건드립니다. 논리적 방어가 물리적 공격 앞에서 무력해지는 전형적인 사례죠.
그래서 이건 얼마나 심각한가
균형 잡힌 판단을 위해 전제 조건을 다시 봅시다.
| 필요한 것 | 내용 | |---|---| | 물리 접근 | 파괴적 접근 — 후면 디캡슐레이션 필수 | | 전문성 | 하드웨어 보안 및 실리콘 분석 역량 | | 장비 | 약 25만 달러 규모의 PEM + LFI 설비 |
인터넷 너머에서 날아오는 공격이 아닙니다. 대량 기기를 원격으로 털 수 있는 종류도 아니고요. 하지만 디바이스가 공격자 손에 물리적으로 넘어가는 시나리오(분실 기기, 회수된 하드웨어, 고가치 타깃)를 위협 모델에 포함한다면 이야기가 달라집니다.
이번 연구가 남긴 설계 교훈은 분명합니다.
- 방어 체인은 가장 약한 고리로 평가된다. OTP를 3중 중복으로 지켜도, 그 설정을 덮어쓸 수 있는
DEBUGEN이 단일 비트라면 거기가 표적이 됩니다. - "우발적 방지"와 "악의적 방지"는 다른 요구사항이다.
DEBUGEN_LOCK은 제 목적에는 충실했지만, 물리 공격을 막는 물건이 아니었습니다. - 부팅 전 상태도 공격 표면이다. 펌웨어가 걸어주는 런타임 잠금은, 펌웨어가 실행되지 않으면 존재하지 않습니다. 복구 리셋 같은 "정상적인" 기능이 그 틈을 열어줄 수 있습니다.
- 검토 범위는 OTP 설정부터 제어 레지스터, 리셋 동작까지 — 보안 강제 경로 전체여야 합니다. 어느 한 지점만 단단해서는 의미가 없습니다.
라즈베리파이가 해킹 챌린지를 열어 이런 검증을 장려해 온 것 자체는 좋은 방향입니다. 공개된 실패에서 배우는 편이, 조용히 남아 있는 취약점보다 훨씬 낫기 때문이죠.