- 압전식 담배 라이터만으로 노트북 DDR3 메모리 버스에 전자기 결함을 유도해, 비권한 Linux 사용자에서 root 셸까지 얻는 로컬 권한 상승 실험이 성공함
- Samsung S3520 노트북의 SODIMM 데이터 핀에 15Ω 저항과 안테나 역할의 전선을 납땜하고, 근처에서 라이터를 클릭해 특정 비트가 뒤집히는 메모리 오류를 만들었음
- CPython 실험에서는 DQ7 라인 결함으로 객체 포인터의 bit 7을 뒤집어
bytes내부의 가짜bytearray구조를 참조하게 만들고, 임의 메모리 읽기/쓰기 프리미티브를 구성함 - Linux 권한 상승은 페이지 테이블을 물리 메모리에 대량 분사한 뒤, PTE 읽기 중 bit 29 결함을 유도해 사용자 접근 가능한 매핑이 페이지 테이블을 가리키게 하는 방식으로 동작함
- 성공 후
/usr/bin/su의 첫 페이지를 물리 메모리에서 찾아 작은 setuid root ELF로 교체해 페이지 캐시를 오염시키며, 실험 환경에서 신뢰도는 SSH 접속 시 체감 약 50%, 그래픽 셸에서는 약 20%였음
라이터 기반 EMFI 실험의 출발점
- 소프트웨어 버그가 없을 때도 결함 주입(fault injection) 으로 예외적 동작을 만들 수 있음
- 결함 주입 방식에는 소프트웨어 제어 데이터 손상, 전원 글리치, 클록 글리치, 전자기 펄스, 레이저 등이 포함됨
- 하드웨어 결함 주입은 보통 결함을 넣는 시점과 위치의 정밀도가 필요해 전문 장비와 비용이 필요함
- 저비용 접근으로 RP2040 기반 PicoEMP와 "Laser Fault Injection for The Masses" 같은 시도가 있음
- 출발점은 압전식 BBQ 점화기를 인덕터에 결합해 저예산 전자기 결함 주입(EMFI) 도구로 쓰는 접근이었음
- 이전에는 Arduino에서 실행되는 소프트웨어 AES 구현을 DFA로 공격하는 데 성공함
- Nintendo Switch 2 발표가 가까워졌다는 맥락에서, Switch 1과 유사한 시스템 소프트웨어와 소프트웨어 버그 부족을 예상하고 저비용 EMFI 실험을 다시 진행함
테스트 대상과 DDR 버스 결함
- 테스트 대상은 Intel i3-2310M CPU와 1GB DDR3 RAM을 탑재한 2011년 제조 Samsung S3520 노트북임
- 가벼운 데스크톱 Linux 배포판인 Arch를 실행할 수 있고, 손상돼도 부담이 적은 장비로 선택됨
- 목표는 주입된 하드웨어 결함에 기반해 동작하는 로컬 권한 상승 익스플로잇을 작성하는 것이었음
- 가장 물리적으로 취약한 지점으로 DRAM 메모리와 시스템을 연결하는 DDR 버스가 선택됨
- SODIMM에는 읽기/쓰기 데이터 비트를 전달하는 64개 DQ 핀, DQ0~DQ63이 있음
- 실험 장치는 DQ26으로 추정되는 핀에 15Ω 저항과 전선 하나를 납땜한 구조임
- 전선은 안테나처럼 주변 전자기 간섭을 받아 데이터 버스에 전달함
- 저항은 정상 메모리 동작을 계속 방해하지 않도록 간섭 강도를 제한하기 위한 요소이며, 실제로 필요 없을 수도 있음
- 일반 압전식 라이터를 안테나 전선 근처에서 클릭하는 것만으로 memtest에서 메모리 오류가 안정적으로 나타남
- 표시된 오류는 모두 bit 29가 뒤집힌 형태였음
- 납땜한 핀은 DQ26으로 추정됐지만 bit 29가 뒤집힌 이유는 핀을 잘못 세었거나 메인보드가 데이터 라인을 바꿔 배선했을 가능성이 있음
- 손가락 반응 속도 수준이라 결함 주입 타이밍 제어는 정밀하지 않음
- 결함이 발생하면 특정 64비트 읽기 또는 쓰기에서 같은 비트가 뒤집힐 가능성이 높음
CPython에서 비트 플립 악용
- 첫 실험은 CPython에서 샌드박스 탈출 형태의 익스플로잇을 만드는 것이었음
- CPython 자체가 샌드박스가 아니므로 실제 보안 경계 우회는 아니며, 익숙한 내부 구조를 활용한 준비 단계임
- 이 실험에서는 앞서 사진의 DQ26 대신 DQ7에 납땜한 전선을 사용함
- CPython 객체는 가비지 컬렉션 힙에 있고, 객체 헤더에는 refcount와 타입 객체 포인터가 있으며 이후 타입별 필드가 이어짐
bytes객체는 길이 필드 뒤에 데이터 자체가 같은 힙 할당 안에 있음bytearray객체는 길이 필드 뒤에 실제 데이터 저장 버퍼를 가리키는 포인터가 있음
- 핵심 전략은
bytes객체 내부에 가짜bytearray구조를 데이터로 넣는 것임- CPython이 이 가짜 객체에 대한 참조를 주도록 속이면, 공격자가 선택한
bytearray의 길이와 포인터 필드로 임의 메모리 읽기/쓰기가 가능해짐
- CPython이 이 가짜 객체에 대한 참조를 주도록 속이면, 공격자가 선택한
- bit 7을 뒤집는 결함은 64비트 워드에서 포인터에 128을 더하거나 빼는 효과를 냄
- 가짜
bytearray를bytes객체 내부 +128바이트 오프셋에 배치하면,bytes객체 포인터가 결함으로 가짜bytearray포인터가 될 수 있음 - 이 변환은 50% 확률로 원하는 방향이 됨
- 가짜
- 메모리 내용 자체가 아니라 메모리 버스의 읽기/쓰기 작업을 글리치한다는 점이 중요함
- Rowhammer처럼 저장된 데이터가 바뀌는 방식이 아니라, 버스에서 진행 중인 접근이 손상됨
- 원하는 포인터 접근이 버스 활동의 대부분을 차지하도록 같은 객체 참조를 큰 튜플에 채움
- CPU 캐시가 DRAM 접근을 줄이므로, 3MiB 캐시보다 큰 구조를 순차 접근해 매번 DRAM에서 읽도록 만듦
- Python의
is는 포인터 비교처럼 동작해 포인터가 바뀌었는지 확인하는 데 사용됨
- 전체 CPython 익스플로잇 소스는 ddr3_dq7.py에 있음
TESTING변수로 하드웨어 없이 소프트웨어에서 비트 플립을 시뮬레이션할 수 있음
Linux 권한 상승에 필요한 메모리 구조
- Linux 로컬 권한 상승에서는 캐시, 가상 메모리와 페이지 테이블, TLB가 핵심 구조로 사용됨
- DRAM은 상대적으로 느리기 때문에 CPU는 L1, L2, L3 캐시를 사용함
- 실험 노트북의 L3 캐시는 3MiB임
- 캐시 적중 시 DRAM에 접근하지 않고, 캐시 미스가 나야 DRAM 읽기가 발생함
- 캐시 관점의 최소 단위는 캐시 라인이며, 이 노트북에서는 64바이트임
- 한 바이트를 읽어도 캐시되지 않은 데이터라면 64바이트 DRAM 읽기가 발생함
- DDR 데이터 버스는 64비트 폭이므로 이 읽기는 8번의 순차 접근 burst로 처리됨
- x86-64의 가상 메모리는 4KiB 페이지와 트리 형태의 페이지 테이블로 구현됨
- 이 플랫폼에는 4계층 페이지 테이블이 있음
- 각 페이지 테이블은 4KiB 페이지이며, 512개의 64비트 PTE를 담음
- 상위 계층 PTE는 다음 계층 페이지 테이블의 물리 주소를 가리키고, level 0 PTE는 대상 물리 페이지를 가리킴
- 루트 페이지 테이블의 물리 주소는 CR3 CPU 레지스터에 저장됨
- PTE에서 필요한 부분은 물리 주소 필드임
- 플래그 비트를 마스킹하면 페이지 정렬된 물리 메모리 주소가 남음
- TLB는 가상 주소에서 물리 주소로의 페이지 매핑을 캐시하는 CPU 내부 하드웨어임
- 실험 노트북의 정확한 TLB 크기는 알 수 없지만 약 1024 엔트리 수준으로 보임
페이지 테이블을 사용자 메모리로 끌어오기
- Linux 익스플로잇 전략은 Mark Seaborn의 Rowhammer exploit 요소에서 영감을 받음
- 목표는 자기 프로세스의 페이지 테이블을 사용자 접근 가능한 메모리로 매핑하는 것임
- 그렇게 되면 PTE를 수정해 임의 물리 메모리에 접근할 수 있음
- 물리 메모리 배치를 정밀하게 제어하기보다는, 물리 메모리의 정확히 50% 를 level-0 페이지 테이블로 채움
- 이후 R/W 매핑들을 반복 접근해 TLB를 우회함
- 매핑 수가 TLB 크기를 넘기 때문에 각 접근에서 페이지 테이블 순회를 강제할 수 있음
- 그 순회 중 level-0 PTE 읽기에서 bit 29 결함을 일으키는 것이 목표임
- bit 29가 뒤집히면 PTE가 가리키는 물리 주소가 512MiB 오프셋만큼 바뀜
- 운이 좋으면 바뀐 주소가 앞서 분사한 level-0 페이지 테이블 중 하나를 가리킴
- 이 경우 원래 일반 R/W 페이지로 보여야 할 매핑이 페이지 테이블을 일반 R/W 페이지처럼 노출함
- 이론상 bit 29부터 bit 12까지의 어떤 비트 플립도 작동할 수 있음
- bit 12는 4KiB 오프셋에 해당함
- 핵심은 PTE가 “다른 곳”을 가리키고, 물리 메모리의 약 50%가 악용 가능한 페이지 테이블로 채워져 있다는 점임
- 안테나 전선 납땜은 반드시 필요하지 않을 수도 있음
- 충분히 강한 전자기 간섭을 만들 수 있다면 가능성이 있지만, 시스템 크래시나 손상 가능성은 훨씬 높아짐
level-0 페이지 테이블 분사 방식
- 먼저 Linux의 memfd_create로 메모리 기반 파일을 만듦
- Mark Seaborn 익스플로잇의
/dev/shm/파일과 같은 역할을 하지만 파일시스템을 직접 건드리지 않음
- Mark Seaborn 익스플로잇의
- 같은 버퍼를 mmap으로 여러 번 매핑함
MAP_FIXED옵션으로 각 매핑을 가상 메모리에서 2MiB 정렬되게 강제함- 이 정렬은 매번 새 level-0 페이지 테이블 생성을 보장함
- Linux는 프로세스당 매핑 수, 즉 VMA에 약 2^16 제한이 있음
- 각 매핑을 32MiB 길이로 만들어 매핑 하나가 16개의 level-0 페이지 테이블을 생성하게 함
- 각 매핑은 가상 주소 공간에서는 32MiB를 차지하지만, PTE들은 같은 물리 페이지를 가리킴
- 물리 메모리 비용은 level-0 페이지 테이블뿐임
- 이 방식으로 메모리가 찰 때까지 페이지 테이블을 분사할 수 있음
물리 메모리 읽기/쓰기와 su 페이지 캐시 오염
- 결함을 기다리며 R/W 매핑을 반복 접근하다가 예상과 다른 값이 반환되면 결함을 감지함
- 반환 데이터가 PTE처럼 보이면 페이지 테이블에 대한 R/W 접근을 얻은 상태임
- 다음 단계는 이 페이지 테이블이 어떤 가상 주소에 해당하는지 찾는 것임
- PTE를 물리 주소 0을 가리키도록 수정함
- 다시 R/W 매핑을 스캔해 어떤 매핑이 바뀌었는지 찾음
- PTE를 수정해도 MMU는 즉시 알아차리지 않음
- 가상-물리 매핑이 TLB에 캐시되어 있기 때문임
- 사용자 공간에서 직접 TLB를 flush하는 방법을 알 수 없어, 수천 개 R/W 매핑을 반복 접근해 TLB를 새 값으로 채우고 기존 값을 밀어냄
- 이 시점에서 전체 물리 메모리에 대한 읽기/쓰기 접근이 가능해짐
- 이후
/usr/bin/su실행 파일을 읽기 전용으로 열고 첫 페이지를 mmap함/usr/bin/su는 setuid root 실행 파일임- 물리 메모리 전체를 스캔해 같은 페이지를 찾음
- 찾은 물리 페이지에 쓰기 접근해, root 셸을 띄우는 4KiB 미만 ELF payload로 교체함
- Linux는 다음에
su가 실행될 때 첫 페이지가 이미 메모리에 있다고 보고 디스크에서 다시 읽지 않음- 오염된 페이지 캐시를 재사용해 주입된 ELF를 실행함
- 주입된 ELF는
echo 1 > /proc/sys/vm/drop_caches로 페이지 캐시를 비워, 그 다음su실행은 정상 동작하게 함
- 전체 Linux 익스플로잇 소스는 linux_x86_64_lpe.c에 있음
신뢰도와 환경 제약
- 데모 실행에서는 운이 좋아 한 번의 라이터 클릭으로 좋은 글리치가 발생함
- 이전 여러 시도에서는 전체 시스템이 크래시됨
- 전체 익스플로잇 신뢰도는 엄밀히 측정되지 않음
- 노트북 화면을 끄고 SSH로 접속한 상태에서는 체감 약 50%로 보임
- 그래픽 셸에서는 약 20%에 가까움
- 실험 시스템은 통합 그래픽을 사용함
- GPU의 메모리 접근이 익스플로잇을 방해할 가능성이 있음
- pipewire, sshd, systemd 관련 백그라운드 서비스와 swap도 켜져 있었음
- 현실적인 데스크톱 Linux 환경을 유지하려는 선택이었고, 비활성화하면 신뢰도가 올라갈 수 있음
- RAM이 더 많이 설치돼 있었다면 더 높은 비율을 페이지 테이블로 채울 수 있어 전체 신뢰도가 증가할 수 있음
가능한 활용과 남은 질문
- Windows에서 신뢰도 높은 EMFI 로컬 권한 상승이 가능하다면, TPM 기반 안티치트가 시스템에서 허용되는 소프트웨어를 제한하는 상황에 영향을 줄 수 있음
- Android의 SafetyNet 또는 Play Integrity 검사에도 유사한 이야기가 가능하지만, 휴대폰에 글리치 모드칩을 넣는 것은 더 어려움
- 저수준 성능 최적화에서는 페이지 테이블과 TLB 지식이 직접 중요하지 않은 경우가 많지만, 이 익스플로잇에서는 가상 메모리 환상을 유지하는 구조가 직접 공격 대상이 됨
- 적용 범위에는 아직 확인할 부분이 많음
- DDR4, DDR5에서도 동작하는지
- ARM에서도 동작하는지
- ECC의 여러 유형, 특히 DDR5 Link-ECC가 어느 정도 완화하는지
- RP2040 같은 장치로 유사 결함을 전자적으로 트리거하는 가장 단순한 방법은 무엇인지
- 하이퍼바이저 탈출에 사용할 수 있는지
- WebKit 익스플로잇 또는 Nintendo Switch 커널 익스플로잇으로 만들 수 있는지