1P by GN⁺ | ★ favorite | 댓글 1개
  • gala는 iPhone 4용 iOS 4 탈옥을 직접 구현하는 프로젝트이며, 1부는 오래된 기기에서 SecureROM 취약점을 이용해 초기 코드 실행을 얻는 과정에 집중함
  • iOS 부팅은 SecureROM이 LLB 또는 iBSS를 검증하고, 이후 단계가 다음 단계를 검증하는 신뢰 체인으로 이어지며 SecureROM은 제조 후 업데이트로 교체할 수 없음
  • limera1n은 A4 SoC의 SecureROM을 대상으로 DFU 모드에서 iBSS 전송을 기다리는 장치를 공격하며, USB 제어 메시지 퍼징에서 발견된 충돌이 힙 오버플로와 셸코드 실행으로 이어지는 것으로 보임
  • pod2g의 SecureROM dumper를 참고해 0x84000000 통신 영역과 0xA1:2 USB 읽기 요청을 활용하고, SecureROM 덤프와 디버그 값을 호스트로 가져오는 흐름을 재구현함
  • Rust로 작성한 페이로드를 Mach-O의 __TEXT,__text에서 셸코드로 추출해 실행하는 파이프라인을 만들었지만, 정적 문자열이 __const에 배치되면서 명령 포인터 상대 주소 지정과 어셈블리 배치가 필요해짐

iPhone 4 탈옥 프로젝트의 출발점

  • gala는 iPhone 4용 iOS 4 탈옥을 만드는 프로젝트이며, 이 문서는 그중 1부인 “Gaining Entry”에 해당함
  • 이전 iOS tweak 개발 경험은 Cydia 배포, SpringBoard 기능 변경, Objective-C 런타임 직접 사용, 폐쇄 소스 바이너리 리버스 엔지니어링으로 이어졌음
  • 직접 탈옥을 작성하려는 목표는 탈옥 과정이 실제로 어떻게 작동하는지 이해하는 데 있음
  • 이 작업은 p0sixninja와 axi0mX가 오픈소스로 공유한 지식에 크게 의존함

오래된 iPhone과 Boot ROM 취약점 선택

  • 첫 단계는 eBay에서 iPhone 4iPhone 3GS를 구입하는 일이었음
  • 최신 Xcode가 오래된 iOS 버전 타깃을 허용하지 않아, 2010년대 방식으로 앱을 빌드·설치하는 경로는 곧 막힘
    • 오래된 Mac OS X와 Xcode를 VM에 설치하는 방안도 검토했지만 중단함
    • Apple이 여전히 레거시 iOS 타깃 바이너리를 서명할지도 불명확했음
  • 대안은 Boot ROM 취약점을 직접 노리는 방식임
    • 오래된 툴체인이나 VM 없이 호스트 머신에서 USB로 장치와 상호작용하는 코드만으로 시도할 수 있음
    • iPhone Wiki의 Vulnerabilities and Exploits 섹션에서 limera1n 익스플로잇 코드를 확인함

SecureROM과 iOS 부팅 신뢰 체인

  • Apple 용어의 SecureROM은 iOS 부팅 프로세스의 첫 단계이며, 다음 부팅 단계를 시작함
  • SecureROM은 두 가지 구성 요소를 로드할 수 있음
    • 정상 부팅 시 NOR의 디스크 파티션에서 Low Level Bootloader, 즉 LLB를 부팅함
    • DFU 모드에서 USB로 컴퓨터에 연결되면 Restore iPhone 과정에서 iBoot Single Stage, 즉 iBSS 부트로더를 받을 수 있음
  • SecureROM은 LLB 또는 iBSS가 Apple이 서명한 신뢰 가능한 이미지인지 확인함
  • 이후 LLB와 iBSS도 자신이 로드하는 다음 단계를 검증해 신뢰 체인을 형성함
  • SecureROM은 첫 단계라서 이전 단계의 검증을 받지 않으며, 제조 시 읽기 전용 메모리에 새겨짐
    • 다른 단계는 iOS 업데이트로 교체할 수 있음
    • 특정 SecureROM 버전의 취약점은 그 버전으로 제조된 장치에 영구적으로 남음

limera1n으로 DFU 장치에서 코드 실행 얻기

  • limera1n은 geohot이 2010년에 공개하고 같은 이름의 탈옥 도구로 패키징한 SecureROM 익스플로잇임
  • DFU 모드 장치가 USB를 통해 호스트에서 iBSS를 받기 위해 대기 중일 때 limera1n을 사용할 수 있음
  • A4 SoC에 포함된 SecureROM이 취약하므로 iPhone 4가 적합한 대상이 됨
  • limera1n의 정확한 동작 원리는 공개적으로 완전히 정리되어 있지 않음
    • geohot은 왜 작동하는지 모른다고 말했음
    • p0sixninja는 이론을 추측했음
    • 충돌은 USB 제어 메시지 퍼징으로 발견됐고, 힙 오버플로로 이어지는 레이스 컨디션처럼 보이며 셸코드 주입과 실행을 가능하게 하는 것으로 보임

DFU 모드 장치에서 메모리 읽기

  • pod2g의 SecureROM dumper는 limera1n 구현, 페이로드 예시, USB 기반 메모리 읽기 방식을 함께 보여주는 참고 구현이 됨
  • SecureROM dumper는 SecureROM이 매핑된 0x0 메모리를 USB 수신 영역으로 복사한 뒤, 호스트에서 USB 제어 메시지로 데이터를 읽음
  • 이해한 흐름은 다음과 같음
    • A4의 MMU는 SRAM의 시작을 0x84000000에 매핑함
    • 호스트는 request type 0x21, request ID 1인 USB 제어 패킷으로 iBSS 이미지를 조각 단위로 보낼 수 있음
    • 이 데이터는 0x84000000부터 SRAM에 복사되고, SecureROM은 다음 패킷 복사 위치를 추적하는 내부 카운터를 유지함
    • 장치는 request type 0xA1, request ID 2 제어 패킷에도 응답하며, 0x84000000의 메모리 내용을 호스트로 보냄
  • 이 읽기 기능은 장치에서 코드를 실행할 수 있을 때 특히 유용함
    • 페이로드가 원하는 데이터를 0x84000000으로 복사함
    • 호스트는 A1:2 읽기 요청으로 해당 데이터를 가져올 수 있음
  • p0sixninja의 2013년 Hack In the Box Malaysia 발표 슬라이드에는 pod2g의 SecureROM dumper가 SHAtter 기반이라고 되어 있지만, 실제 유틸리티는 limera1n 구현을 사용함
  • 자체 limera1n 구현으로 SecureROM 덤프에 성공함

0x84000000을 이용한 페이로드 디버깅

  • 코드 실행을 얻은 뒤에는 셸코드가 어디에서 실행되는지, 스택 위치가 어디인지, 셸코드가 어떤 메모리를 덮어쓰는지 확인해야 했음
  • SecureROM dumper의 읽기 흐름을 디버그 출력 용도로 재사용함
    • 페이로드가 명령 포인터(instruction pointer)와 스택 포인터(stack pointer) 값을 0x84000000에 복사함
    • 호스트 측 코드는 같은 방식으로 값을 다시 읽음
    • 이 방식은 메모리 덤프를 통한 간이 print() 역할을 함
  • 자동 스크립트는 익스플로잇 실행 후 0x84000000의 첫 몇 워드를 덤프해 출력 창에 표시함
  • 확인된 값은 셸코드 실행 위치와 스택 위치를 보여줌
    • 명령 포인터는 0x8402b048 근처였음
    • 스택 포인터는 0x8403bfa0였음
    • 스택 포인터는 SecureROM이 설정한 일반 스택 영역 안에 있었고, 명령 포인터는 수신 이미지 영역 안에 있었음

Rust 페이로드와 Mach-O 셸코드 추출

  • 어셈블리만으로 페이로드를 작성하는 대신 Rust 페이로드를 빌드해 셸코드로 변환하는 빌드 시스템을 구성함
  • Rust는 2020년 초 armv7-apple-ios 타깃 지원을 중단했지만, rustup으로 오래된 지원 툴체인으로 전환할 수 있음
  • 고수준 언어로 컴파일하면 원시 머신 코드뿐 아니라 메타데이터, 가상 주소 공간 설정 정보, 심볼 테이블, 링커 정보가 포함된 바이너리가 생성됨
  • 익스플로잇에는 메모리에 주입해 점프할 바이트만 필요하므로 Mach-O 전체가 아니라 __TEXT 세그먼트의 __text 섹션만 필요함
  • strongarm은 Mach-O 분석 라이브러리이며, 빌드 시스템에서 Mach-O를 파싱해 __TEXT,__text 섹션을 파일로 추출하는 데 사용됨
  • 추출된 파일의 바이트가 limera1n으로 장치에서 실행할 셸코드가 됨

링커와 정적 데이터 문제

  • 일반 바이너리는 OS 인프라와 start 또는 _main 같은 진입점 심볼 규약을 사용하지만, 이 셸코드 용도에는 그런 심볼이 필요 없음
  • 링커는 기본적으로 _main 또는 start가 없으면 오류를 냄
  • -U _main, -U start, -static 옵션을 조합해 dyld를 쓰지 않는 Mach-O 파일을 만들 수 있었음
  • strongarm은 처음에 LC_DYLD_INFO 로드 명령이 없는 바이너리를 처리하지 못해 예외를 냈음
    • 해당 바이너리는 dyld를 사용하지 않아 LC_DYLD_INFO가 없음
    • strongarm에 패치를 추가해 처리함
  • Rust 코드에 정적 문자열을 추가하자 페이로드가 깨짐
    • 컴파일된 정적 문자열은 __const에 배치됨
    • 셸코드 추출 과정은 __TEXT,__text만 남기므로 __const 데이터는 메모리에 로드되지 않음
    • 결과적으로 코드는 매핑되지 않은 주소에서 문자열을 읽으려 하며 크래시함
  • 해결책은 정적 데이터를 __TEXT,__text 안에 포함시키고, 안정적인 로드 주소를 가정하지 않도록 명령 포인터 상대 주소 지정을 사용하는 것임
  • 현재 방식은 어셈블리에서 문자열을 정의하고 그 주소를 Rust 페이로드 진입점에 넘김

1부에서 완성한 실행 파이프라인

  • 최종 파이프라인은 다음 순서로 동작함
    • Rust 페이로드를 수정함
    • 버튼을 눌러 페이로드를 컴파일함
    • 바이너리에서 셸코드를 추출함
    • 러너가 연결된 DFU iPhone에서 limera1n으로 페이로드를 실행함
    • 러너가 통신 공간으로 쓰는 0x84000000에서 데이터를 자동으로 읽어 hexdump로 표시함
  • 이 시점에서 장치에서 임의 코드를 실행할 수 있음
  • 임의 코드 실행은 이론상 많은 작업을 가능하게 하지만, 장치가 실제로 흥미로운 일을 하게 만드는 작업은 별개의 단계로 남음
  • 다음 단계는 Part 2: Bypassing the Bootchain로 이어짐

댓글과 토론

Hacker News 의견들
  • 몇 년 동안 눈팅만 하다가 이 말을 남기려고 드디어 Hacker News 계정을 만들었음. 오랫동안 많은 사람에게 신비한 블랙박스였던 일을 이렇게 정리해줘서 고마움
    iOS 4가 올라간 iPod 4G를 탈옥하던 때, 터미널 메시지가 줄줄이 올라가는 걸 보며 엄청 긴장했던 기억이 선명함. 이후 학교 점심시간에 친구들 기기도 해주면서, 실수로 폰을 망가뜨려 수백 달러짜리 벽돌 기기를 만들까 봐 걱정했음
    몇 년이 지난 뒤 돌아보면, Apple의 벽을 뚫고 사용자 코드를 실행하는 그 “마법”이 프로그래밍에 빠져들게 만든 계기였고, 관련된 모두에게 깊이 감사함

    • 기술 업계에서 일하다 보면 목표를 세워도 이해하지 못하거나 해내지 못하는 일 때문에 싫어지는 날이 많음. 그래도 이런 글을 가끔 읽으면 오픈소스나 기술 커뮤니티 전반을 포기하지 말아야겠다는 마음이 듦
    • iPhone은 좋아하지만, 예전에 여러 Android 기기에 CyanogenMod를 올리던 게 그리움. 정말 멋지게 느껴졌음
    • 비슷한 경험을 했음. 결국 직접 탈옥 트윅을 만들기 시작했고, 그게 프로그래밍에 본격적으로 들어가게 된 계기였음
  • 글 정말 고마움. 이렇게 복잡한 개념을 쉽게 설명하려면 깊은 이해가 필요함. 읽으면서 밤늦게까지 탈옥 프로젝트를 해킹하던 좋은 기억이 떠올랐음

  • 읽는 내내 정말 즐거웠음. 특히 아직도 네이티브 코드 역공학에 서툰 입장이라 더 그랬음
    이건 시스템 복구 메커니즘을 이용해 신뢰 사슬을 깨고 수정된 iOS를 부팅하니 테더드 탈옥으로 보임. 그렇다면 언테더드 탈옥은 어떻게 동작하는지 궁금함. 보안 부팅 사슬을 아예 거치지 않고 그대로 둔 채, 실행 중인 시스템의 권한 있는 프로세스나 비권한 프로세스를 익스플로잇한 뒤 별도 권한 상승을 하는 방식이 맞는지 궁금함. 지속성은 어떻게 확보하고, 부트로더와 커널 자체의 서명 검사를 건드리지 않으면서 커널의 서명 검사를 어떻게 패치하는지도 궁금함

    • 보통은 부팅 과정 중이나 직후에 사용자 영역에서 커널을 다시 익스플로잇하도록 구성하는 경우가 많음. 기억나는 기법으로는 새 launch daemon 추가, 개발자 인증서로 앱 서명, 동적 링커의 특이 동작을 악용해 서명 검사를 우회하는 바이너리 배치 등이 있었음
  • 정말 훌륭한 글임. 아직도 커뮤니티에서 활동하는 모습을 보니 반가움

  • 이걸 만들어줘서 고마움. 특별한 사진이 많이 들어 있는 iPhone 4s가 있는데, 어쩌다 PIN을 잊어버려서 몇 년째 기다려야 하는 상태임
    사진만 아니면 그냥 초기화했을 텐데, 이걸로 PIN을 재설정하고 사진을 복사할 수 있는지 궁금함

    • 아마 어려울 것 같음. [0]에 따르면 암호가 파일시스템 암호화 키를 보호하는 데 쓰이므로, 암호 없이는 파일을 복호화할 수 없음. 사진이 암호화되어 저장되는지는 모르겠지만, 그렇다고 보는 게 맞을 듯함
      수정: 내가 틀렸을 수도 있음. [0]에는 한동안 Mail 저장소만 암호화됐고 기본값이 iOS 7에서 바뀌었다고도 나옴. 그래서 iPhone이 iOS <= 6이라면 이 방법으로 기기에 접근해 사진을 복사할 수 있을지도 모름. [1]의 도구가 도움이 될 수 있음
      [1] https://code.google.com/archive/p/iphone-dataprotection/
      [0] https://darthnull.org/ios-encryption/
  • 정말 좋은 글이었음. 다만 예전 iOS 인터페이스가 스큐어모픽 디자인의 영광을 그대로 보여주는 장면은 속이 뒤틀렸음. John Ive가 얼마나 싫은지 새삼 느꼈음

    • 버튼이 클릭 가능한 버튼처럼 보이던 시절 말하는 거지? 너무 형편없는 평면 디자인 때문에 결국 접근성 기능을 추가해 결함을 우회해야 했던 지금과 달리
      Jony는 소프트웨어를 맡기보다 면도날처럼 날카로운 알루미늄 손목 받침대나 계속 디자인했어야 했을지도 모름
    • iPhone은 iPhone 4와 iOS 4에서 정점이었다고 봄. 이후에도 점진적 개선은 있었지만, 새 iPhone이 정말 큰 업그레이드라고 느낀 마지막 순간은 그때였음. 안테나 게이트를 빼면 하드웨어와 소프트웨어 디자인이 모두 그냥 잘 맞아떨어졌음
  • 앞부분 몇 개를 정말 재미있게 읽었음. 이런 관점에서 따라가는 게 멋짐. 나도 다른 사람들이 익스플로잇 같은 걸 어떻게 구현하는지 알아내려고 소스 코드를 엄청 읽는데, 다른 사람도 그렇게 한다니 반가움

  • 아직 읽지는 않았지만 기대됨. 초반에 나열된 트윅은 전부 써봤고, 만들어줘서 고맙다고 말하고 싶었음. 초기 iOS 탈옥은 정말 재미있었음

  • 아주 좋았음. 실행해보려 했지만 안타깝게도 내 오래된 기기가 애초에 부팅되지 않았음
    나도 그때 트윅을 만들었고 탈옥을 흑마법처럼 느꼈음. 이 글을 읽고도 아직 어느 정도는 그렇게 느껴짐

  • 이렇게 정리해줘서 정말 고마움. 이런 내용을 배우는 데 관심이 많음. 특히 dafang-hacks처럼 저렴한 Wi-Fi 보안 카메라를 내 커스텀 펌웨어로 “해방”하거나, Kindle Fire 태블릿을 루팅할 새 익스플로잇을 만드는 법 같은 걸 배우고 싶음
    그런데 그 과정을 자세히 다룬 글은 놀랄 만큼 찾기 어려움