Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
zipdefrag — Presented at Recon Montreal 2018 | Kitploit
도구/GitHubGitHub/nccgroup/zipdefrag
Embedded Systems SecurityMemory ForensicsReverse EngineeringData RecoveryDigital ForensicsFirmware Analysis
GitHubnccgroup/zipdefrag

zipdefrag

Presented at Recon Montreal 2018

저장소 보기
748년 전아직 검토되지 않음

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →
공유

이 덤프는 퍼즐이다

또는 바보와 반항아를 위한 고급 샷건 파싱

옛날 옛적에

옛날옛적에, 사랑하는 독자여, 어둡고 폭풍우 치던 밤, 충실한 저자는 당혹스럽고 신비로운 상황을 우연히 마주쳤습니다. 칩 오프 메모리 분석과 리버싱을 가로막는 유일한 것은, 임베디드 자바 사용으로 인한 압축과 결합된 미지의 단편화된 독점 파일 시스템이었으며, 그 압축 때문에 기존 파일 카빙 도구의 효율이 떨어졌습니다.

그 당시에는 서로 맞물려 보이는 청크들을 수동으로 이어 붙이는 임시방편을, 지저분한 파이썬 콘솔 작업과 못생긴 임시 bash 스크립트로 대충 조합했을 수도 있습니다. 그럭저럭 쓸 만했지만 시간이 많이 들었습니다.

칩 오프 분석에서 압축되지 않은 평문 데이터를 추출하는 것은 하드웨어 해커의 일상에서 무척 평범한 작업이지만, 압축된 조각들이 비합리적이고 불쾌한 방식으로 사방에 흩어져 있으면, 실제로 추출을 막는 다른 보호 조치가 전혀 없더라도 심각한 문제가 됩니다.

하지만 분명 더 나은 방법이 있어야 하지 않나?

특히 Zip 파일에는 흥미로운 점이 몇 가지 있습니다 (JAR 파일에 사용되는 기본 형식이기도 합니다). 한동안 International Journal of PoC||GTFO의 성실한 독자였고, 특히 Ange Albertini의 파일 포맷 스턴트 작업을 따라가면서, zip 파일 안에는 그 zip 파일 자체에 대해 충분한 데이터가 들어 있어서 이를 다시 꿰맞추는 일을 꽤 훌륭히 해낼 수 있겠다고 생각했습니다.

참고 자료를 언급했으니, 이제 기술적 세부 사항으로 들어가 봅시다.

우선, 파일 시스템의 세부 사항은 모를 수 있습니다 (그리고 제 연구 관점에서는, 문제를 발견한 시스템과 무관하게, 그냥 신경 쓰지 않는 편이 낫다고 판단했습니다). 하지만 대부분의 파일 시스템이 구현되는 방식에 대해 우리는 한두 가지를 알고 있습니다. 특히 파일 시스템은 청크 단위로 기록되는 경향이 있다는 점입니다. 청크에는 페이지라고 알려진 일종의 최소 크기가 있으며, 덤프를 훑어보고 기록된 최소 블록 크기를 식별하면 그 페이지 크기를 알아낼 수 있습니다.

일부는 연속적으로 이어져 있을 수 있고, 일부는 그렇지 않으며, 블록이 언제 연속적인지에 대한 명확한 패턴은 없습니다.

다시 말해, 우리가 풀어야 할 문제는 추출하려는 파일의 유효한(또는 충분히 근접한) 이미지를 얻을 수 있도록 데이터 페이지를 어떻게 재배열하느냐입니다.

Zip 파일은 일종의 역순 계층 구조로 작성됩니다. 먼저 압축된 파일 데이터(이를 설명하는 로컬 파일 헤더로 감싸여 있음)가 옵니다. 그다음 중앙 디렉터리(로컬 파일 헤더의 오프셋을 나열)가 오고, 그다음 중앙 디렉터리 끝(EOCD) 레코드(무엇보다도 zip에 저장된 파일 수, 중앙 디렉터리가 시작되는 오프셋, 중앙 디렉터리의 크기를 나타냄)가 옵니다.

이제 이를 거꾸로 뒤집어 조금 더 자세히 파고들어 봅시다:

  • 중앙 디렉터리 끝(EOCD) 레코드는 다음을 알려줍니다:

    • CD의 오프셋과 EOCD 앞에 위치하는 CD의 길이를 더한, zip 파일 내 EOCD 레코드의 정확한 위치
    • 찾아야 할 파일 수(따라서 CD 레코드 수도)
    • zip 파일 내 첫 번째 CD 레코드의 정확한 위치
  • 각 중앙 디렉터리 레코드는 다음을 알려줍니다:

    • 압축된 파일 데이터의 CRC32
    • 타임스탬프
    • 그 밖의 많은 메타데이터(압축 방식, 플래그, 사용/필요한 OS 버전...)
    • 해당 LF 청크에 대한 파일 내 인덱스
    • 결정적으로: 해당 LF 청크의 이미지를 구성할 수 있는 충분한 데이터
  • 각 로컬 파일 레코드는 다음을 알려줍니다:

    • 덤프에서 파일이 시작하는 위치
    • 파일이 충분히 작다면 같은 페이지 안에서 전체 파일을 얻을 수 있고, 또는 다음 파일 헤더가 zip 파일의 다음 페이지 청크에 나타나기 때문에 그것도 얻을 수 있습니다!
    • 충분한 수의 작은 파일들이 충분한 페이지에 채워져 있다면, 페이지의 위치와 알려진 디렉터리 값을 사용해 페이지 순서를 만들 수 있습니다(알려진 갭 포함!)

위의 모든 과정을 통해 파일의 대부분을 재구성할 수 있습니다.

반전 - 현실적으로 둘 이상의 JAR 펌웨어를 처리해야 합니다!

우선 서로 다른 펌웨어에서 나온 데이터를 구별하는 단계가 필요합니다. 그 이유는 모든 오프셋이 각자의 zip 파일 안에서만 유효하기 때문입니다. 어떤 충돌이든 zip 스트림이 서로 어긋나고 손상으로 이어지며, 우리는 손상되지 않은 데이터를 가능한 한 많이 빼내기를 강력히 원합니다. 또한 예를 들어 대상 펌웨어에서 진단한 취약점이 평소 실행되는 그 펌웨어에 영향을 미치며, 어딘가에 그냥 방치된 다른 파일에는 영향을 미치지 않는다는 확실한 보장도 원합니다.

여기서 필요한 해법은 kmeans 알고리즘(일명 "Lloyd 알고리즘")입니다. 작동 방식을 설명하는 훌륭한 영상이 있습니다. SciPy에는 바로 쓸 수 있는 좋은 버전이 있었지만, Rust 구현에서 이 알고리즘을 작동시키려면 그 알고리즘을 구현한 유일한 클러스터링/분석 크레이트를 찾아내서/패치해야 했습니다. 다행히 처음부터 작성할 필요는 없었습니다.

그다음은 식은 죽 먹기입니다.

이를 위해 여러 특징을 사용할 수 있습니다. Flags, Method, Version 필드는 모두 파일을 압축하는 데 사용된 Zip 스택에 따라 달라집니다. 게다가 헤더에는 타임스탬프가 있으며, 모든 펌웨어가 정확히 같은 시간에 컴파일되고 압축되었을 가능성은 일반적으로 낮습니다.

덧붙이자면, Zip 파일은 MS-DOS 형식 타임스탬프를 사용한다는 점을 알아 둘 가치가 있습니다. 이는 년-월-일과 시-분-2초를 나타내는 비트 압축 short입니다. 이것을 분류 데이터로 사용하기 전에 절대 스칼라 값으로 변환하지 않는다면, 1년 차이와 1초 차이에 똑같은 가중치를 부여하게 될 수 있는데, 그건 전혀 좋지 않습니다!

우리는 이것들을 유클리드 벡터(Euclidean Vector)로 변환합니다. 이는 ℝ의 값들(혹은 float 좌표)로 이루어진 n차원 배열을 뜻하는 멋진 표현이지만, 위에 링크한 영상이 아마 가장 직관적인 설명일 것입니다. 그러면 클러스터링 알고리즘이 사실상 나머지 모든 작업을 처리하여, 파싱된 모든 헤더를 우리가 기대하는 버킷 수만큼 모아 줍니다.

파싱에 관한 간단한 참고 사항

이 방법의 프로토타입을 위해 상당히 조잡한 파이썬 스크립트를 작성했지만, PoC로 JAR 콘텐츠 복구율이 대략 70~80%에 도달한 시점에서 거기서 멈추고 Rust로 빠른 버전을 구현하는 쪽으로 방향을 돌리기로 결정했습니다.

Rust에는 파서 검증기를 작성하는 데 아주 환상적인 nom이라는 크레이트가 있습니다. 참고로, 이것이 Rust로 다시 작성할 때의 주요 매력 중 하나였습니다. 명확하고 매우 엄격한 파서를 작성할 수 있다는 점은, 이 모든 것을 파이썬으로 처리하려 했을 때보다 어떤 면에서는 훨씬 쉽게 만듭니다. 파이썬은 훨씬 관대한 경향이 있어서, 엣지 케이스를 놓치면서 파싱 실패를 대충 넘기고 있지 않은지 확인하기가 때때로 어려울 정도입니다.

멋지고 빠르며 읽기 쉬운 파서가 흥미롭게 느껴진다면 다음을 확인해 보세요.

  • Writing Parsers like it's 2017
  • Nom Benchmarks - 거기서 누군가 Rust로 http 파서를 처음부터 작성했는데, 버퍼 오버플로 없이 매우 빠른 C 구현보다 약간 더 빨랐습니다.

무엇보다도 이런 종류의 분석을 파이썬에서 실행하는 것은 본질적으로 느린 편이며, 이 접근 방식의 실현 가능성을 탐구하기 위한 PoC로 가는 경로 이상을 의미한 적은 없습니다.

어쨌든, 이쯤에서 그만하고...

계속 진행하기

남은 청크를 재구성하는 접근 방식으로는 먼저 남은 페이지를 높은 엔트로피 후보로 필터링하거나, 작은 갭부터 먼저 채우는 방법이 있습니다. 후자는 검색 목록에서 가능한 한 많은 페이지를 제거하는 방식입니다. 이 페이지들에 대한 순열을 테스트하는 것은 최악의 경우 지수 시간이 걸리는 작업이므로, 쉬운 경우를 빠르게 해결하는 것이 우선순위이며 진행하면서 문제를 기하급수적으로 단순화합니다!

또한 페이지 경계 때문에 로컬 파일 헤더를 파싱할 수 없는 사례를 찾아 빠른 성과를 얻을 수도 있습니다. 적어도 유사한 정렬이 유일하고 다른 손상 아티팩트와 충돌하지 않는 경우에는, 동일하게 정렬된 대응물과 매칭할 수 있을 것입니다.

누락된 페이지의 후보를 어떻게 확인할까요? 중앙 디렉터리에 파일의 CRC32 체크섬이 바로 있습니다! 파일 전체에 대해 CRC32를 계산하는 대신, 아마 가장 좋은 방법은 이미 알고 있는 청크에 대해 CRC32를 계산하는 것입니다. 갭이 시작되기 전 페이지 끝에 있는 데이터에서 순방향으로, 그리고 데이터(혹은 deflate 스트림 뒤의 DataDescriptor 청크)에서 역방향으로 계산한 다음, 이를 바탕으로 누락된 페이지 블록 각각에 대해 어떤 중간 CRC32를 기대해야 하는지 알아내는 것입니다.

본질적으로 가장 간단하고 빠른 문제를 선택해 풀 때마다, 불필요한 데이터를 제거함으로써 더 어려운 문제들이 훨씬 단순해집니다. 이것이 섀넌 엔트로피를 사용해 빈 페이지나 거의 빈 페이지를 아예 걸러 버리는 이유입니다. 높은 엔트로피의 zip 페이지만 있으리라는 보장은 없지만, 이상치가 있더라도 처음부터 그런 복잡성을 다루지 않아도 되어 속도가 크게 빨라집니다.

그냥 이걸 빌드하고 싶을 뿐인데, 도대체 어떻게 하라는 거야?

만져보고 싶다면 rust를 설치하세요 (멋진 rustup nightly를 권장합니다). 그런 다음:

root@kitploit:~
$ git clone [repo]
...
$ cd zipdefrag
...
$ cargo build --release

디버깅을 활성화하려면 release 플래그를 빼면 됩니다.

빌드 산출물은 /target/{debug,release}에 있습니다.

cargo doc으로 문서를 빌드하세요 (이 크레이트는 문서화가 충실합니다. 저는 글쓰기를 좋아합니다.)

현재 Rust 버전은 기본적으로 터미널 출력이 없습니다. cli 하네스를 실행하려면 환경 변수 RUST_LOG=zipdefrag를 설정해야 하며, 그러면 지금까지의 분석을 보여주는 상세 터미널 로깅이 활성화됩니다.

앞으로 추가될 것:

  • 미지의 파일 시스템에서 나온 퍼즐 같은 zip 덤프를 풀기 위한 빠르고 휴대 가능한 네이티브 실행 파일 (python 훅 포함).

  • 데모 덤프

알려진 문제

  • 현재 Rust 구현은 대응하는 LFH 청크를 검색할 때 낭비적인 동작 때문에 성능이 형편없습니다. 이것을 고치고 교훈을 얻겠습니다.

  • 이 기법은 JAR 안의 많은 파일이 페이지 크기보다 상당히 클 때는 제대로 작동하지 않습니다. zip 파일에 내재된 구조를 많이 활용하기 때문에, 데이터가 많은 파일은 그다지 잘 풀리지 않습니다.

편리하게도 J2ME 미들릿의 클래스 파일은 일반적으로 상당히 작은 편이지만, 내부에 패키징된 대형 바이너리는 복구가 어려울 가능성이 높습니다.

또한 파이썬 PoC에는 여러 산술 버그가 있습니다.

도구 다운로드