[목차]


[0. 오프라인]

  • 2024년 08월 30일 금요일 - Codegate 컨퍼런스에서 오프라인 1차 공개 되었습니다.
  • 악용을 방지하기 위해 온·오프라인에 공개된 자료는 서로 다르며, 원본은 오프라인에서만 공개됩니다.

upload in progress, 0

[1. 개요]


⚠️
[주의]
- 본 연구는 연구 윤리 준수 규정에 따라 진행되었으며, 우회 목적이 아닌 공인된 목적으로 더 이상의 몰카 피해를 최소화하기 위해 탐지에 대한 연구를 수행한 후 공개하고자 합니다.​

- 우회 또는 악용을 목적으로 방문하셨다면 다시 한 번 생각해 보시기 바랍니다. 저보다 뛰어난 포렌식 분석관과 수사관이 있으니, 형법 및 관련 법률에 어긋나는 행동을 삼가시기 바랍니다

- 해당 연구 장비는 연구 장소 외 반출을 금지하며, 모두 잠금 장치에 보관하고 있습니다. 어떠한 경로로도 반출할 의도가 없으며, 윤리적 사고방식으로 화이트 해커(분석관)의 신념을 지키고 있습니다.

본 글은 몰래카메라(불법 카메라)에 대한 탐지 및 대응 방안에 관한 것이 아닙니다.

분석관의 관점에서 소프트웨어와 하드웨어를 분석하고, 취약점을 통해 관리자 권한을 상승시킨 후(분석을 방해하는 안티 분석 기능이 있어 권한 상승이 필요함) 펌웨어를 분석하여 어떻게 설계되어 있는지 확인하기 위해 작성된 글입니다.

만약 카메라 탐지와 대응 방안에 대해 확인하고 싶으시다면, 아래 1편 글 '몰래카메라(몰카) 탐지 방법에 대한 연구 (Step 1) - Detection of Hidden(Spy) Cam'을 먼저 보시고, 이후 현재 2편 글 순서로 확인하시는 것을 추천드립니다.

몰래카메라(몰카)에 대한 탐지 방법에 대한 연구

몰래카메라(몰카)에 대한 탐지 방법에 대한 연구

탐지 및 대응에 대한 연구

[2. 연구배경 및 관련 이슈]

👮
모든 접촉은 흔적을 남긴다.
- 범죄학자 Edmond Locard -

디지털 포렌식 분석관 및 취약점 분석을 수행하는 화이트해커의 관점에서, 몰래카메라의 H/W(Hardware) S/W(Software)를 분석하겠습니다. H/W 측면에서는 어떤 부품으로 설계되어 있는지, 그리고 해당 부품들에 어떻게 접근하여 분석해야 하는지에 대해 설명하겠습니다. S/W 측면에서는 취약점을 도출하고, 이를 통해 관리자 권한을 획득하여 분석하는 방법을 제시하겠습니다.

1편과 달리, 2편 "몰래카메라(몰카) 분석에 대한 연구 (Step 2) - Analysis of Hidden(Spy) Cam"에서는 몰래카메라를 설계한 프로그래머 및 개발자의 관점에서 분석을 진행합니다.

추후 다시 공개 예정입니다.

⚠️
연구 윤리 준수 규정에 따라, 블로그에 공개된 모든 장비는 필자가 직접 구매하여 소유한 것이며, 연구는 대한민국 법률 규정을 준수하여 문제가 없도록 진행되었습니다.

본 연구는 몰래카메라 피해를 최소화하고 예방하는 것을 목적으로 하여, 분석 내용과 취약점을 공개하기로 하였습니다.

몰래카메라는 집, 회사, 여행 숙소 등 사적인 공간에 자신도 모르게 설치되어 불법 촬영 피해를 초래하는 사례가 많습니다. 이러한 피해를 최소화하기 위해 본 연구를 진행하였습니다.

[2-1. 해외 불법 몰카 개발자 분께]

불법 카메라 개발자에게 (출처: 존경하는 Linus Benedict Torvalds)

불법 카메라(몰래 카메라)에 대해 연구를 진행하면서 이 장비를 왜 만들었는지 모르겠지만, 개발자 분께서 이글을 보러 오셨다면 아마도, 보안 패치 및 우회 목적으로 보러 왔을 거라 생각됩니다. 도무지 개발자(화이트 해커) 관점에서 이해를 할 수 는 없었습니다.

[2-2. 관련 이슈]

'몰카(몰래카메라)' 또는 'SPY Cam(Hidden Camera)' 등의 키워드로 검색하면 관련 카메라 장비와 언론에 보도된 뉴스 내용을 쉽게 확인할 수 있습니다.​

🔎
[보도된 언론을 바탕으로 이슈]
- 디지털 성범죄 외에도 "군사 기밀 유출"과 같은 심각한 범죄에 사용됨
- 설치된 카메라를 통해 집 도어락 비밀번호를 확인한 후 발생한 "살인 사건"과, "가정집 화장실"에 몰래카메라가 설치된 사례

[피해]
- "호텔 및 숙박업소(Airbnb 등)" 이용객을 대상으로 한 몰래카메라 범죄는 지난 10년간 "3만 건" 이상 발생
- 일상, 개인 사생활(집), 사무실 등에서도 이러한 범죄는 계속해서 발생 중

관련 이슈를 보면, 해외에서도 한국에서 보도된 불법 카메라(몰래카메라)에 대한 뉴스가 많이 다루어지고 있습니다

모든 몰래카메라를 분석할 수는 없지만, 큰 이슈가 된 카메라를 분해하고 분석을 진행하였습니다. 최대한 일반인의 관점에서 IT 용어를 쉽게 풀어서 작성했으니, 편하게 읽어보시면 될 것 같습니다.

아래는 카메라에 대한 분석 내용입니다. 현재 ‘화재감지기형 몰래카메라’와 ‘충전기형 몰래카메라’는 동일한 칩셋과 소프트웨어를 사용하고 있어, 외관만 함께 소개드리며, 소프트웨어 분석은 ‘충전기형 카메라’를 중심으로 진행됩니다.


[3. 화재감지기형 디자인 몰래 카메라]

[3-1. 외관]

화재감지기형 카메라를 분해하기 전, 충전기형 카메라와 크기를 비교해보면 실제 화재 탐지기 크기와 매우 유사하다는 것을 확인할 수 있습니다.

구매 당시, 맨 우측 검은색 박스는 위 그림과 같이 '화재감지기형 카메라', '설명서', '천장 설치 가드', '미니 5핀 케이블'로 구성되어 있었습니다.

화재감지기형 카메라’는 전면에서 보아도 실제 화재 탐지기인지 몰래카메라인지 식별하기 어렵습니다. 외관상으로는 카메라 렌즈가 어디에 있는지 자세히 보지 않으면 구별하기 힘듭니다.

설명서에는 마이크로 SD카드의 최대 지원 용량(128GB) 정보와 함께, 모바일 단말기를 통해 몰래카메라에 접속할 수 있는 설정 방법이 간략하게 안내되어 있습니다.


[3-2. APP - LookCam]

화재감지기형 카메라(충전기형 카메라)의 특징 중 하나는 Android 단말기와 iOS 단말기와 연동되는 'LookCam' 애플리케이션입니다. 해당 앱은 가정용 홈 카메라 연동을 위한 앱으로 설명되어 있습니다.

이 앱은 구글 플레이 스토어 및 앱스토어에서 설치하여 몰래카메라와 연동할 수 있으며, 이미 Google Play 스토어에서 100만 건 이상 다운로드된 것을 확인할 수 있습니다.

  • 해당 앱이 몰래카메라 전용으로 개발된 것인지, 아니면 정식 합법적인 보안 CCTV 전용 앱을 몰래카메라 제조사가 리버싱(소프트웨어 역공학)하여 무단으로 몰래카메라 기능에 맞춰 개발한 것인지는 확인할 수 없었습니다.

(해당 앱이 설치되어 있다고 해서 반드시 몰래카메라 전용 앱이라고 볼 수는 없습니다.)


[3-3. 분해]

화재감지기형 카메라의 후면에는 전원 버튼과 충전 단자가 있으며, 외관은 플라스틱 재질로 되어 있습니다.​

외관 크기는 성인 남자의 손바닥 정도입니다.

분석 당시 독특하게도 전원 버튼의 ‘ON/OFF’ 설정이 반대로 되어 있었습니다. ‘ON’ 위치에서는 전원이 꺼지고, ‘OFF’ 위치에 있어야 전원이 켜져 작동하게 되었습니다.

아래에는 Mini 5pin 충전 단자가 있으며, 그 옆에는 'Reset Button'이 숨겨져 있습니다.

가까이서 자세히 봐야만 숨겨진 카메라 렌즈를 확인할 수 있으며, 화재감지기형 디자인을 모방했기 때문에 화재 탐지를 위한 더미 탐지 구멍이 존재했습니다. (아무런 기능이 없음)

숨겨진 카메라 렌즈의 반대쪽 부분입니다.

위쪽 부분으로, 만약 건물 천장에 설치되었을 경우 이와 같은 모습을 볼 수 있습니다.

먼저 나사를 제거하여 분해하면, 메인보드 외에 리튬 배터리, 마이크로 SD 카드, 그리고 무선 네트워크를 위한 안테나가 설치되어 있습니다.

메인보드와 연결된 케이블을 살펴보면 5개의 케이블이 있으며, 그중 2개는 VCC와 GND로, 나머지 3개는 리셋 등의 기능을 수행하는 것으로 확인됩니다.

리셋 스위치를 약 3초간 누르면 몰래카메라 셋팅 설정 초기화 작업이 진행되며(SD Card 데이터 초기화는 되지 않음), 전원 온/오프 기능과 리튬 배터리 충전을 위한 단자가 있습니다

💡
- CPU: ARM ANYKA AK3918 (AK3918EN080 V300S BH3J02I23)
- M/B: AK-L001-V1
- Flash ROM: FM25Q64A13 (2345J3G) 8MB 용량
- WLAN(무선 칩): ALTOBEAM 6012B CD12J04 2.4Ghz
- Camera: A-28P-H63P-52MM-V2.1 28Pin
- 리튬 배터리 : 3.7V
- SD Card Slot: 128GB Max SD Card

우선, 사양은 위와 같으며, 좀 더 자세히 분석하기 위해 아래와 같은 절차로 진행하였습니다.

구체적인 상세 분석을 위해, 위 그림과 같이 돋보기, 현미경, 분해 도구각종 장비를 사용하여 분석을 진행하도록 하겠습니다.

ㅠㅠ 몰래카메라 분석을 위해 분석 장비 수입을 위해 개인 사비 150만원 정도 지출 된 것 같습니다.

임베디드 연구나 취약점 분석은 항상 돈이 많이 발생합니다...

머리 / 몸통 / 다리 처럼 세분화하여 분해할 경우, "외관 / 메인보드 / 카메라"로 나눌 수 있습니다.

'A-28P-H63P-52MM-V2.1 28Pin' 카메라 모듈이 사용되었으며, 28핀 연결 방식을 채택하고 있습니다. 화재감지기형 카메라의 경우, 카메라 모듈 자체가 플라스틱에 접착제로 단단히 부착되어 있어 분해가 불가능했습니다.

메인보드에 연결된 카메라 케이블을 포함한 모든 연결부를 분리한 후, 작업대에서 본격적으로 분석을 진행합니다.

메인보드 앞면에는 여러 모듈 칩셋이 장착되어 있었으나, 후면에는 음성 녹음을 위한 마이크가 탑재되어 있는 것을 확인했습니다.

촬영된 모든 사진은 최대한 자세히 보기 위해 돋보기를 사용해 촬영했으나, 칩셋의 모델명과 정보는 너무 작아 육안으로는 식별이 어려웠습니다

집에 하나쯤 다들 가지고 계시는 전자 현미경 도움을 통해 칩셋에 대한 정보와 메인보드에 연결된 라인을 살펴볼 수 있었습니다.

왼쪽 사진부터 순서대로 CPU(ARM) / 펌웨어가 저장되어 있는 Flash ROM / 무선랜을 사용하기 위한 무선랜 칩 입니다.

여기까지 하드웨어(H/W) 분석이 완료되었으며, 이제 소프트웨어(S/W) 분석을 위해 펌웨어 추출 작업을 진행했습니다.

보다 자세한 외관 및 분석은 아래에서 소개할 '충전기형 디자인 몰래카메라'에서 다시 설명드리도록 하겠습니다. (화재감지기형 몰래카메라와 충전기형 몰래카메라는 동일한 제조사의 동일한 S/W를 사용하고 있으므로, 상세 분석은 충전기형을 기준으로 진행하겠습니다.)


[4. 충전기형 디자인 몰래 카메라]

[4-1. 외관]

충전기형 몰래카메라의 경우 우연인지 모르겠으나, 이번 2024년 3월에 발생한 총선 투표소 40곳에 불법 카메라가 설치된 사건에서 사용된 모델과 동일한 제품으로 판단됩니다.

몰래카메라(몰카)에 대한 탐지 방법에 대한 연구

​충전기형 디자인 카메라 확인하기

탐지 및 대응에 대한 연구

자세한 외관은 1편 '몰래카메라(몰카) 탐지 방법에 대한 연구 (Step 1) - Detection of Hidden(Spy) Cam'의 '충전기형 디자인 카메라' 부분에 작성하였으니, 본 글에서는 분석에 대한 내용을 다루겠습니다.


[4-2. 분해]

앞서 '화재감지기형 몰래카메라'를 분해한 후, 바로 이어서 '충전기형 몰래카메라'의 분해 작업도 진행하였습니다.

충전기형 몰래카메라는 일반적인 방법으로는 분해가 쉽지 않습니다. 이를 분리하려면 파손을 감수하거나, 스마트폰 또는 임베디드 전용 분해 장비(플라스틱 도구)를 사용해 분리 작업을 진행해야 합니다.

플라스틱 도구를 사용하여 플러그 접지 구간을 분리하는 작업을 진행했습니다.

잠시 연결 테스트를 위해 재조립한 후, 두 번째 분해 작업을 진행하던 중 플러그 접지 구간의 양쪽 플라스틱이 파손되었습니다......

분해 작업을 통해 220V 교류 전압이 5V 직류 전압으로 변환되어, 메인보드와 USB 단자 두 곳에 전압이 전달되는 것을 확인하였습니다. 아직까지 육안으로는 숨겨진 카메라가 보이지 않았습니다.

숨겨진 카메라의 뒷부분에서는 메인보드와 카메라 렌즈가 연결되어 있는 것을 확인할 수 있었습니다.


[4-3. 분석 - 메인보드]

CPU 좌측에 위치한 부분이 UART (Tx / Rx)로 추정되었으며, 이후 멀티 테스터, LED 테스트, 그리고 마지막으로 신호 분석기를 사용하여 실제 UART인지 확인하는 테스트를 진행했습니다.

💡
WLAN: ALTOBEAM 6012B CD12J04 (ATBM6012B)

몰래카메라에는 AP 역할을 하거나, 카메라가 무선 AP(무선 공유기)에 연결할 수 있도록 무선랜 칩이 삽입되어 있었습니다.

충전기형 카메라에도 '화재감지기형 카메라'와 마찬가지로 음성 녹음을 위한 작은 마이크가 장착되어 있습니다.


💡
CPU: ANYKA AK3918ENO80 V300s BHSJ02I23
Flash ROM: FM25Q64A13 (8MB)

CPU는 ARM 기반으로 장착되어 있었으며, 펌웨어가 탑재된 Flash ROM은 8MB 용량의 칩으로 확인되었습니다. 그러나 CPU에 대한 데이터 시트는 칩 제조사에서 공개되지 않아 구체적인 정보는 확인할 수 없었습니다.

해당 제품의 모델명은 'AK-V306-V6'으로 확인되며, 전면에는 CPU, Flash ROM, 그리고 카메라 연결 소켓이 존재합니다.

'충전기형 몰래카메라'와 '화재감지기형 몰래카메라'는 같은 제조사의 동일한 CPU를 사용하고 있었으며, 임의로 부여한 번호 13번과 14번은 UART 통신 구간으로 확인되었습니다.

🔒
13번에는 Rx
14번에는 Tx

메인보드 후면에는 소형 마이크, 2.4GHz 무선랜, 리셋 버튼(SD 카드 데이터는 지워지지 않음), 작동 LED, 무선 안테나 연결 소켓, 그리고 무선 안테나가 장착되어 있었습니다.


[4-4. 분석 - UART Tx / Rx 납땜 대신 연결할 방법 찾기]

임베디드 취약점 분석 및 개발을 해보았다면, 해당 디바이스와 통신하기 위해 UART 또는 JTAG 구간을 찾아 납땜 후, USB to UART 모듈에 연결하여 분석이나 디버깅을 진행하는 것이 일반적입니다.

저는 임베디드 장치를 분석할 때 가급적 납땜을 하지 않으며, 아래와 같이 도구를 직접 제작하여 사용하는 편입니다.

(이전에 납땜을 잘못하여, 여러 비싼 장비들을 박살낸 적이 있으며, 특히 얇은 곳에는 납땜이 거의 불가능한곳이 있음)


[4-4-1. Syringe (주사기 이용)]

예시로, 이전에 분석했던 임베디드 장치에서 UART의 Tx와 Rx 구간이 있었으며, 해당 구간이 머리카락만큼 작아 납땜이 어려운 공간이였고, 그때는 주사기를 이용해 통신을 분석했습니다.

주사기 바늘 부분에 구리선을 감아 전기적 신호를 USB to UART TTL 모듈에 연결한 후, 그 신호를 분석하기도 했습니다. (흙수저 에디션 분석 방법)

그 당시 납땜을 잘못하여 연결 부위가 뜯어지는 문제가 발생했고, 그로 인해 더 이상 해당 임베디드 장치를 분석할 수 없었습니다. ㅠㅠㅠㅠㅠㅠ


[4-4-2. Probe Pin (Pogo Pin) 이용]

가끔 빠르게 분석해야 할 때는 Probe Pin을 사용하여 전압(VCC, GND)UART(Tx, Rx) 위에 핀을 올려두고 작업을 진행합니다.

Probe Pin은 고정이 제대로 되지 않아, 작은 충격에도 쉽게 이탈되는 문제가 있지만, 장비에 파손이나 손상을 주지 않고 분석할 수 있다는 강점이 있습니다


[4-4-3. Board Data Recovery Pin 이용]

제가 자주 사용하는 방법은 "Data Pin"를 이용한 방식입니다.

원래 Data Pin의 목적은 SD 카드와 같은 미세한 반도체 복구를 위해, 작은 구간에 납땜 없이 연결해 복구하는 용도입니다. 하지만 이를 개조하여, 작은 IoT 메인보드의 UART 부분이나 분석하고 싶은 구간에 연결하여 사용하고 있습니다.

연결된 데이터 핀은 전기적 신호를 그대로 전달하므로 문제 없이 잘 사용하고 있습니다. 또한, 단단히 고정되어 있어 충격을 받더라도 설정한 자리에서 이탈하지 않는 장점이 있습니다.


[4-5. 분석 - 통전 테스트를 통한 Tx / Rx 구별하기]

[4-5-1. 멀티 테스터기]

가장 일반적으로 사용하는 방식은 VCCGNDTxRx 핀 구간을 찾기 위한 방법입니다.

몰래카메라는 당연히 제조사에서 칩과 메인보드 정보를 공개하지 않기 때문에, 직접 분석하여 식별하고 구분하는 작업을 진행해야 합니다. (또한, 공식 홈페이지도 없어 펌웨어를 다운로드받을 수 없는 상황임)

아까 소개 드린 "Data Recovery Pin"을 이용해 디바이스에 연결하고, 멀티 테스터기에 접지하여 각 구간의 전압을 확인합니다.

당연히 VCC+ 구간에서는 3.3V 또는 5V 전압이 측정되겠지만, 메인보드에 5V 전압이 표기되어 있었습니다. 220V를 사용하는 전압 플러그와 보드를 직접 연결하여 전압 분석을 진행하는 것은 위험하다고 판단되어, USB to UART TTL에서 따로 5V 전원을 케이블로 연결하여 안전하게 분석을 진행했습니다.

대부분의 USB to UART TTL 모듈에서는 3.3V 또는 5V 전압을 제공하므로, 여기에 전압을 케이블로 연결하면 안전하게 분석을 진행할 수 있습니다.

Tx (Transmit Data) 전압 3.456V 측정

Rx (Receive Data) 전압 3.351V 측정


[4-5-2. LED 이용하기]

만약 멀티 테스터기 대신 돌아 댕기는 LED 소켓을 이용해 전압을 확인할 수도 있습니다. LED 소켓을 통해 전압이 흐르는 구간을 시각적으로 확인하는 방법입니다.

이번에는 전압을 리튬 배터리에 연결해 메인보드에 전력을 공급한 후, VCC와 GND 구간에 맞춰 LED를 연결했을 때, LED가 켜지는 것을 확인할 수 있었습니다.

이번에는 리튬 배터리를 메인보드에 연결 후, 전압을 공급하고 VCC와 GND에 맞춰 LED를 연결했을 때, LED가 정상적으로 점등되는 것을 확인할 수 있었습니다

전원을 켜면 UART 통신 구간에서 커널 부팅 과정이 송출된다면, 전압이 측정될 때 LED가 켜졌다 꺼지거나 지속적으로 켜져 있는 현상을 확인할 수 있었습니다.

반면, Rx 구간에서는 LED에 전원이 들어오지 않았습니다.

💡
Rx (데이터 수신): (LED OFF) 3.345V
Tx (데이터 송신): (LED ON) 3.456V
이를 통해 Rx < Tx 가 전압이 더 높다는걸 확인할 수 있었습니다.


[4-5-3. Logic Analysis (로직 분석기) 이용하기]

로직 분석기로는 두 가지 제조사의 제품을 사용했습니다.

첫 번째는 Saleae사의 로직 분석기이며,
두 번째는 아래 사진에 보이는 ALIENTEK사의 제품입니다.

멀티 테스터기로 찾은 Tx와 Rx 구간이 정확한지 확인하기 위해, 'Channel 0'에 Tx를, 'Channel 1'에 Rx를 연결한 후, 부팅 시 발생하는 증상을 분석하였습니다.

출처: 한국철도

로직 분석기에서도 기본적으로 첫 번째는 1이 아닌 0부터 시작하기 때문에, 0번 채널이 첫 번째 채널임으로 헷갈리지 말자! (인덱스는 0이 국룰)

전력이 공급되면 커널 부팅 과정이 출력되며, 무언가 표시되고 있음을 확인할 수 있습니다.

로직 분석기를 사용할 때, UART 통신을 위해 Baud Rate(초당 전송되는 신호 변화 수)를 알아야 합니다. 이 값을 통해 카메라 보드의 Tx/Rx 클럭을 맞춰 연결할 수 있습니다. (클럭을 맞추지 않으면 송수신되는 문자열이 깨져서 나옴.)

현재 8.65µs(마이크로초)로 측정되며, 이를 기반으로 아래 링크에서 확인한 근접한 값인 '115200 bauds'로 추정할 수 있습니다. (이는 사람의 심박 수를 체크해 1초에 몇 번 뛰는지 확인하는 과정과 유사합니다.)

Most common baud rates table | Lulu’s blog
This page presents the most used bauds rates for UART / serial communication. Bauds, bit/s, bit duration and speed in bytes/s are presented | Lulu’s blog

Bauds 정리된 표

또한, 사진 속에서 's', 'u', 'b'라는 문자(char)를 확인할 수 있으며, 문자 'u'에 해당하는 2진법 값인 10101110을 기준으로, 1일 때는 신호가 올라가고 0일 때는 내려가는 방식으로 8개의 전기적 신호가 전송되어 하나의 문자가 화면에 표시되는 과정을 볼 수 있습니다.

Saleae 로직 분석기에서도 전기적 신호를 분석해 화면에 표시하며, 우측에서 송출된 문자열을 취합하여 보여주는 기능을 제공합니다.

출력된 화면을 보면 임베디드 시스템의 부팅 과정이 정상적으로 표시되고 있어, 우리가 찾은 Tx와 Rx 구간이 정확하다는 것을 확인할 수 있습니다.

사실, 펌웨어를 인터넷에서 다운로드할 수 있다면 로직 분석기를 통해 'Baud Rate'를 계산할 필요는 없습니다. (물론, 펌웨어가 암호화되지 않은 경우)

펌웨어를 분석해 '/etc/inittab'의 Kernel Command Line에서 '115200' 값을 직접 확인할 수도 있습니다.


[4-6. USB to UART TTL 연결]

맥북과 USB to UART TTL을 연결한 후, 아래 명령어를 사용하여 몰래카메라에서 송출되는 데이터를 확인합니다:

screen /dev/tty.usbserial-0001 115200

Mac OS Terminal

사진으로만 보면 정상적으로 연결된 것처럼 보일 수 있지만, 아래 영상을 보면 이상한 점이 있다는 것을 확인할 수 있습니다.

0:00
/0:15

아무런 입력이 되지 않고, 멈춰버림

화면이 송출되다가 더 이상 출력되지 않으며, 키보드 입력 또한 중단됩니다.

중단 이후 다음 프로세스가 작동하는 것으로 보아, 무언가 보안 조치가 적용된 것을 확인할 수 있습니다.

일반적으로 'U-Boot'를 사용할 때 'Glitching Attack'을 통해 부팅 시 비정상적인 전압으로 U-Boot Shell에 진입할 수 있지만, 이 몰래카메라 제조사는 커스텀된 U-Boot 모델을 사용하고 있어, 해당 공격을 시도할 때 시스템이 메모리에 로드되기 전에 멈추는 증상(프리징)이 발생했습니다.

제보는 바이오에 인터넷 밈 해명?봇 on X: "129. 마다가스카의 펭귄 밈- 교수님 수업을 듣는 나 저런 이야기가 있을줄은  몰랐습니다,무사히 북극곰에게 선물을 주었나요? https://t.co/VreDIR2XPn" / X
아...


[4-7. Firmware Extaction]

[4-7-1. SOP8 Clip Adapter 이용]

Flash ROM Programmer를 사용하여 정확한 분석을 위해 펌웨어 추출 작업을 진행합니다.

제조사 공식 홈페이지에서 펌웨어를 다운로드할 수 없기 때문에, 펌웨어는 Flash ROM에서 추출 작업을 진행합니다.

Flash Memory(FM25Q64) 데이터 시트를 참조하여 SOP8 Clip을 올바른 방향으로 장착한 후, CH341A Programmer를 사용해 SOP8 데이터를 추출합니다.

SOP8 Clip을 몰래카메라 메인보드의 Flash ROM에 접지 후 연결합니다.
부팅이 완료되어 프로세스가 작동 중일 때 펌웨어를 추출하는 것보다는, 이전에 'Glitching Attack'으로 메모리가 로드되기 전에 시스템이 멈추는 증상을 이용해 이 시점에서 펌웨어를 추출하기로 하였습니다."

[4-7-2. SOP8 다른 Adapter 이용]

SOP8 Clip이 제대로 작동하지 않을 경우, 위 그림과 같은 다른 방법으로 펌웨어를 추출할 수 있습니다!

[4-7-3. SOP8 Flipper Zero 이용]

위 그림은 예시이지만, 펌웨어 추출을 위해 노트북을 사용할 수 없는 상황이나 휴대용으로 펌웨어를 추출해야 할 경우, 직접 제작한 보드를 이용해 Flipper Zero와 연결하여 펌웨어를 추출하기도 합니다. (RedTeam 오프라인 내부망 모의침투할 때 유용할 듯)


[4-8. Firmware Analyze]

[4-8-1. 펌웨어 분석]

추출된 펌웨어를 'binwalk' 도구를 통하여 살펴 보면, U-Boot(uImage), Kernel(ARM), Squashfs FileSystem(2개), JFFS2 FileSystem(1개)가 존재하는걸 확인할 수 있습니다.

포렌식 관점에서 파티션의 생성 시간을 주의 깊게 보도록 하겠습니다.

👮
(Address): 0x1ca000 - Squashfs FileSystem
-> 2022-02-14 06:23:52 (UTC) 날 파티션 생성
-> 중국 시간 (2022-02-14 14:23:52 (UTC+8))
-> 대한민국 시간 (2022-02-14 15:23:52 (UTC+9)

(Address): 0x3dc000 - Squashfs FileSystem
-> 2024-03-07 03:01:44 (UTC) 날 파티션 생성
-> 중국 시간(2024-03-07 11:01:44 (UTC+8))
-> 대한민국 시간(2024-03-07 12:01:44 (UTC+9)

대한민국 표준시 기준: UTC+9
중국 표준시 기준: UTC+8

대한민국은 중국보다 1시간 빠르며, 협정 세계시(UTC) 기준으로 9시간 빠릅니다.

두 개의 파티션 중 하나만 2022년도에서 2024년도로 변경된 것을 보면, 보안 패치나 업그레이드가 있었던 것으로 추측됩니다.

몰래카메라에 장착된 CPU가 MIPS 또는 ARM일 것이라 예상했는데, 역시 ARM CPU를 사용하고 있었습니다.

아까 추출한 펌웨어를 시각화하면, 위 그림과 같이 두 개의 영역이 Squashfs 파일 시스템으로 크게 설정되어 있고, 그 사이에 JFFS2 파일 시스템이 존재합니다.

이번 분석에서는 세 개의 파티션 모두를 분석할 예정입니다.


[4-8-2. Squashfs FileSystem 추출]

DECIMAL       HEXADECIMAL     DESCRIPTION
------------------------------------------------------------------------
1875968       0x1CA000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 1827112 bytes, 289 inodes, blocksize: 131072 bytes, created: 2022-02-14 06:23:52
3719180       0x38C00C        JFFS2 filesystem, little endian
4046848       0x3DC000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4128636 bytes, 287 inodes, blocksize: 131072 bytes, created: 2024-03-07 03:01:44

추출할 파티션 정보

$ dd if=<Firmware> bs=1 skip=<주소 - 1875968> count=<크기 - 1827112> of=dir.squashfs_1
	1827112+0 records in
	1827112+0 records out
	1827112 bytes (1.8 MB, 1.7 MiB) copied, 1.14267 s, 1.6 MB/s

$ dd if=<Firmware> bs=1 skip=<주소 - 4046848> count=<크기 - 4128636> of=dir.squashfs_1
	4128636+0 records in
	4128636+0 records out
	4128636 bytes (4.1 MB, 3.9 MiB) copied, 2.58905 s, 1.6 MB/s

파티션 추출 시 크기 계산

전체 펌웨어에서 Squashfs 파일 시스템 영역만 추출하려면 파티션이 존재하는 위치와 크기 값을 알아야 합니다. 2개의 파티션에 대해 of 옵션으로 각각 파티션을 추출합니다.

skip=에 파티션이 존재하는 위치 값을 삽입합니다 (예: 1875968 또는 Hex 값으로 0x1CA000)
count 파티션 크기 (size으로 표기된 값 삽입 1827112)

$ unsquashfs dir.squashfs_1
	Parallel unsquashfs: Using 20 processors
	240 inodes (264 blocks) to write
	
	[======================================================================================================================================|] 264/264 100%
	
	created 86 files
	created 49 directories
	created 154 symlinks
	created 0 devices
	created 0 fifos
	created 0 sockets

추출된 2개의 파티션에 대해 압축을 해제하여, 어떠한 데이터가 존재하는지 살펴보도록 하겠습니다.

$ ls -la
	total 14028
	drwxrwxr-x  4 hashcat hashcat    4096 Aug 11 13:21  .
	drwxr-x--- 20 hashcat hashcat    4096 Aug 11 12:31  ..
	-rwx------  1 hashcat hashcat 8388608 Aug 11 12:32 '2024.08.11 - jffs2 수정본 원본 공장초기화 (고).bin'
	drwxrwxr-x  3 hashcat hashcat    4096 Aug 11 12:32  back
	-rw-rw-r--  1 hashcat hashcat 1827112 Aug 11 13:19  dir.squashfs_1
	-rw-rw-r--  1 hashcat hashcat 4128636 Aug 11 13:20  dir.squashfs_2
	drwxrwxr-x 14 hashcat hashcat    4096 Feb 14  2022  squashfs-root

squashfs-root 폴더 생성

파티션 압축 해제가 완료되었으니, 'squashfs-root' 폴더로 이동해 어떤 데이터가 존재하는지 확인합니다.


[4-8-3. Squashfs FileSystem 첫 번째 항목]

사진에 모두 나열할 수 없어서, 아래 코드 라인으로 작성하였습니다.

# dir.squashfs_1

squashfs-root/
├── bin
│   ├── arch -> busybox
│   ├── ash -> busybox
│   ├── base64 -> busybox
│   ├── busybox
│   ├── cat -> busybox
│   ├── chmod -> busybox
│   ├── chown -> busybox
│   ├── cp -> busybox
│   ├── cttyhack -> busybox
│   ├── date -> busybox
│   ├── dd -> busybox
│   ├── df -> busybox
│   ├── dmesg -> busybox
│   ├── dnsdomainname -> busybox
│   ├── dumpkmap -> busybox
│   ├── echo -> busybox
│   ├── egrep -> busybox
│   ├── false -> busybox
│   ├── fatattr -> busybox
│   ├── fgrep -> busybox
│   ├── fsync -> busybox
│   ├── getopt -> busybox
│   ├── grep -> busybox
│   ├── gunzip -> busybox
│   ├── hostname -> busybox
│   ├── ionice -> busybox
│   ├── iostat -> busybox
│   ├── ipcalc -> busybox
│   ├── kbd_mode -> busybox
│   ├── kill -> busybox
│   ├── link -> busybox
│   ├── linux32 -> busybox
│   ├── linux64 -> busybox
│   ├── ln -> busybox
│   ├── login -> busybox
│   ├── ls -> busybox
│   ├── minips -> busybox
│   ├── mkdir -> busybox
│   ├── mknod -> busybox
│   ├── more -> busybox
│   ├── mount -> busybox
│   ├── mountpoint -> busybox
│   ├── mtd_debug
│   ├── mv -> busybox
│   ├── netstat -> busybox
│   ├── nice -> busybox
│   ├── nuke -> busybox
│   ├── pidof -> busybox
│   ├── ping -> busybox
│   ├── printenv -> busybox
│   ├── ps -> busybox
│   ├── pwd -> busybox
│   ├── resume -> busybox
│   ├── rm -> busybox
│   ├── rmdir -> busybox
│   ├── scriptreplay -> busybox
│   ├── sed -> busybox
│   ├── setarch -> busybox
│   ├── setpriv -> busybox
│   ├── setserial -> busybox
│   ├── sh -> busybox
│   ├── sleep -> busybox
│   ├── stat -> busybox
│   ├── stty -> busybox
│   ├── sync -> busybox
│   ├── tar -> busybox
│   ├── touch -> busybox
│   ├── true -> busybox
│   ├── umount -> busybox
│   ├── uname -> busybox
│   ├── usleep -> busybox
│   ├── vi -> busybox
│   ├── watch -> busybox
│   └── zcat -> busybox
├── data
├── dev
├── etc
│   ├── config
│   ├── fstab
│   ├── group
│   ├── host.conf
│   ├── hosts
│   ├── init.d
│   │   ├── rc.local
│   │   └── rcS
│   ├── inittab
│   ├── ld.so.conf
│   ├── mdev.conf
│   ├── nsswitch.conf
│   ├── passwd -> config/passwd
│   ├── profile
│   ├── ps_ir_3916.conf
│   ├── ps_ir_3918.conf
│   ├── ps_ir_3919.conf
│   ├── ps_ir_sq38.conf
│   ├── resolv.conf -> config/resolv.conf
│   ├── services
│   ├── shadow -> config/shadow
│   ├── sysconfig
│   │   └── HOSTNAME
│   ├── udhcpd.conf
│   └── Version
├── lib
│   ├── ld-uClibc-1.0.28.so
│   ├── ld-uClibc.so.0 -> ld-uClibc.so.1
│   ├── ld-uClibc.so.1 -> ld-uClibc-1.0.28.so
│   ├── libatomic.so -> libatomic.so.1.1.0
│   ├── libatomic.so.1 -> libatomic.so.1.1.0
│   ├── libatomic.so.1.1.0
│   ├── libcharset.so -> libcharset.so.1.0.0
│   ├── libcharset.so.1 -> libcharset.so.1.0.0
│   ├── libcharset.so.1.0.0
│   ├── libc.so.0 -> libuClibc-1.0.28.so
│   ├── libc.so.1 -> libuClibc-1.0.28.so
│   ├── libgcc_s.so
│   ├── libgcc_s.so.1
│   ├── libiconv.so -> libiconv.so.2.5.1
│   ├── libiconv.so.2 -> libiconv.so.2.5.1
│   ├── libiconv.so.2.5.1
│   ├── libitm.so -> libitm.so.1.0.0
│   ├── libitm.so.1 -> libitm.so.1.0.0
│   ├── libitm.so.1.0.0
│   ├── libiw.so -> libiw.so.30
│   ├── libiw.so.30
│   ├── libstdc++.so -> libstdc++.so.6.0.20
│   ├── libstdc++.so.6 -> libstdc++.so.6.0.20
│   ├── libstdc++.so.6.0.20
│   ├── libstdc++.so.6.0.20-gdb.py
│   ├── libtirpc.so -> libtirpc.so.3.0.0
│   ├── libtirpc.so.3 -> libtirpc.so.3.0.0
│   ├── libtirpc.so.3.0.0
│   ├── libuClibc-1.0.28.so
│   └── modules
│       └── 4.4.192V2.1
│           ├── build -> /home/songmengxing/v500/330/cloud_workbench/os/bd
│           ├── kernel
│           │   ├── drivers
│           │   │   ├── mmc
│           │   │   │   ├── card
│           │   │   │   │   └── mmc_block.ko
│           │   │   │   └── core
│           │   │   │       └── mmc_core.ko
│           │   │   ├── net
│           │   │   │   ├── ppp
│           │   │   │   │   ├── bsd_comp.ko
│           │   │   │   │   ├── ppp_async.ko
│           │   │   │   │   ├── ppp_deflate.ko
│           │   │   │   │   ├── ppp_generic.ko
│           │   │   │   │   ├── ppp_mppe.ko
│           │   │   │   │   ├── pppoe.ko
│           │   │   │   │   ├── pppox.ko
│           │   │   │   │   └── ppp_synctty.ko
│           │   │   │   └── slip
│           │   │   │       └── slhc.ko
│           │   │   ├── scsi
│           │   │   │   ├── scsi_mod.ko
│           │   │   │   ├── sd_mod.ko
│           │   │   │   └── sg.ko
│           │   │   ├── staging
│           │   │   └── usb
│           │   │       ├── class
│           │   │       │   ├── cdc-acm.ko
│           │   │       │   └── cdc-wdm.ko
│           │   │       ├── common
│           │   │       │   └── usb-common.ko
│           │   │       ├── core
│           │   │       │   └── usbcore.ko
│           │   │       ├── gadget
│           │   │       │   ├── function
│           │   │       │   │   └── usb_f_mass_storage.ko
│           │   │       │   ├── legacy
│           │   │       │   │   └── g_mass_storage.ko
│           │   │       │   ├── libcomposite.ko
│           │   │       │   └── udc
│           │   │       │       └── udc-core.ko
│           │   │       ├── serial
│           │   │       │   ├── option.ko
│           │   │       │   ├── usbserial.ko
│           │   │       │   └── usb_wwan.ko
│           │   │       └── storage
│           │   │           └── usb-storage.ko
│           │   ├── fs
│           │   │   ├── configfs
│           │   │   │   └── configfs.ko
│           │   │   ├── lockd
│           │   │   │   └── lockd.ko
│           │   │   ├── nfs
│           │   │   │   ├── nfs.ko
│           │   │   │   ├── nfsv2.ko
│           │   │   │   └── nfsv3.ko
│           │   │   └── nfs_common
│           │   │       └── grace.ko
│           │   ├── lib
│           │   │   └── crc-ccitt.ko
│           │   └── net
│           │       ├── mac80211
│           │       │   └── mac80211.ko
│           │       ├── sunrpc
│           │       │   └── sunrpc.ko
│           │       └── wireless
│           │           └── cfg80211.ko
│           ├── modules.alias
│           ├── modules.alias.bin
│           ├── modules.builtin
│           ├── modules.builtin.alias.bin
│           ├── modules.builtin.bin
│           ├── modules.dep
│           ├── modules.dep.bin
│           ├── modules.devname
│           ├── modules.order
│           ├── modules.softdep
│           ├── modules.symbols
│           ├── modules.symbols.bin
│           └── source -> /home/songmengxing/v500/330/cloud_workbench/os/kernel
├── linuxrc -> bin/busybox
├── mnt
├── proc
├── sbin
│   ├── acpid -> ../bin/busybox
│   ├── adjtimex -> ../bin/busybox
│   ├── ak_mci.ko
│   ├── arp -> ../bin/busybox
│   ├── blockdev -> ../bin/busybox
│   ├── bootchartd -> ../bin/busybox
│   ├── depmod -> ../bin/busybox
│   ├── devmem -> ../bin/busybox
│   ├── fbsplash -> ../bin/busybox
│   ├── fdisk -> ../bin/busybox
│   ├── fstrim -> ../bin/busybox
│   ├── getty -> ../bin/busybox
│   ├── halt -> ../bin/busybox
│   ├── hwclock -> ../bin/busybox
│   ├── ifconfig -> ../bin/busybox
│   ├── ifdown -> ../bin/busybox
│   ├── ifenslave -> ../bin/busybox
│   ├── ifup -> ../bin/busybox
│   ├── init -> ../bin/busybox
│   ├── inotifyd -> ../bin/busybox
│   ├── insmod -> ../bin/busybox
│   ├── ip -> ../bin/busybox
│   ├── ipaddr -> ../bin/busybox
│   ├── iplink -> ../bin/busybox
│   ├── ipneigh -> ../bin/busybox
│   ├── iproute -> ../bin/busybox
│   ├── iprule -> ../bin/busybox
│   ├── iptunnel -> ../bin/busybox
│   ├── klogd -> ../bin/busybox
│   ├── loadkmap -> ../bin/busybox
│   ├── logread -> ../bin/busybox
│   ├── losetup -> ../bin/busybox
│   ├── lsmod -> ../bin/busybox
│   ├── makedevs -> ../bin/busybox
│   ├── mdev -> ../bin/busybox
│   ├── mkdosfs -> ../bin/busybox
│   ├── mke2fs -> ../bin/busybox
│   ├── mkfs.vfat -> ../bin/busybox
│   ├── modinfo -> ../bin/busybox
│   ├── modprobe -> ../bin/busybox
│   ├── mount_reserve_dir.sh
│   ├── nameif -> ../bin/busybox
│   ├── pivot_root -> ../bin/busybox
│   ├── poweroff -> ../bin/busybox
│   ├── reboot -> ../bin/busybox
│   ├── rmmod -> ../bin/busybox
│   ├── route -> ../bin/busybox
│   ├── run-init -> ../bin/busybox
│   ├── setconsole -> ../bin/busybox
│   ├── slattach -> ../bin/busybox
│   ├── swapoff -> ../bin/busybox
│   ├── swapon -> ../bin/busybox
│   ├── switch_root -> ../bin/busybox
│   ├── sysctl -> ../bin/busybox
│   ├── syslogd -> ../bin/busybox
│   ├── tc -> ../bin/busybox
│   ├── tf_card.sh
│   ├── tunctl -> ../bin/busybox
│   ├── udhcpc -> ../bin/busybox
│   ├── uevent -> ../bin/busybox
│   ├── update.sh
│   ├── vconfig -> ../bin/busybox
│   └── watchdog -> ../bin/busybox
├── sys
├── tmp
├── usr
└── var
    └── run

48 directories, 239 files

dir.squashfs_1

1번 Squashfs 파티션에는 아래와 같이 파일로 요약됩니다.

💡
/bin -> busybox 바이너리
/etc/ -> init.d -> rc.local | rcS
-> passwd (config/passwd 링크)
-> shadow (config/shadow 링크)

'init.d' 폴더의 'rc.local'과 'rcS'는 부팅 시 init 단계에서 서비스와 데몬을 실행하기 위해 자동으로 실행되는 스크립트입니다.

몰래카메라 계정에 대한 정보는 'passwd'와 'shadow' 파일이 다른 파티션에서 마운트되어 링크(윈도우의 바로가기와 유사한 방식)로 연결되고 있습니다.


[4-8-4. Squashfs FileSystem 두 번째 항목]

$ unsquashfs dir.squashfs_2
	Parallel unsquashfs: Using 20 processors
	279 inodes (344 blocks) to write
	
	[======================================================================================================================================|] 344/344 100%
	
	created 116 files
	created 8 directories
	created 163 symlinks
	created 0 devices
	created 0 fifos
	created 0 sockets

Squashfs 2 파티션 압축 해제

Squashfs 첫 번째 파티션의 압축 해제 과정과 동일하게 진행하면 됩니다.

# dir.squashfs_2
squashfs-root_2

├── bin
│   ├── [ -> ../../bin/busybox
│   ├── [[ -> ../../bin/busybox
│   ├── anyka_ipc
│   ├── awk -> ../../bin/busybox
│   ├── basename -> ../../bin/busybox
│   ├── bc -> ../../bin/busybox
│   ├── beep -> ../../bin/busybox
│   ├── blkdiscard -> ../../bin/busybox
│   ├── bzcat -> ../../bin/busybox
│   ├── cal -> ../../bin/busybox
│   ├── chrt -> ../../bin/busybox
│   ├── chvt -> ../../bin/busybox
│   ├── cksum -> ../../bin/busybox
│   ├── clear -> ../../bin/busybox
│   ├── cmd_serverd
│   ├── cmp -> ../../bin/busybox
│   ├── crontab -> ../../bin/busybox
│   ├── cut -> ../../bin/busybox
│   ├── dc -> ../../bin/busybox
│   ├── diff -> ../../bin/busybox
│   ├── dirname -> ../../bin/busybox
│   ├── dos2unix -> ../../bin/busybox
│   ├── du -> ../../bin/busybox
│   ├── dumpleases -> ../../bin/busybox
│   ├── eject -> ../../bin/busybox
│   ├── env -> ../../bin/busybox
│   ├── expand -> ../../bin/busybox
│   ├── expr -> ../../bin/busybox
│   ├── factor -> ../../bin/busybox
│   ├── fallocate -> ../../bin/busybox
│   ├── fgconsole -> ../../bin/busybox
│   ├── find -> ../../bin/busybox
│   ├── fold -> ../../bin/busybox
│   ├── free -> ../../bin/busybox
│   ├── ftpget -> ../../bin/busybox
│   ├── ftpput -> ../../bin/busybox
│   ├── fuser -> ../../bin/busybox
│   ├── groups -> ../../bin/busybox
│   ├── hd -> ../../bin/busybox
│   ├── head -> ../../bin/busybox
│   ├── hexdump -> ../../bin/busybox
│   ├── hexedit -> ../../bin/busybox
│   ├── hexmov
│   ├── hostid -> ../../bin/busybox
│   ├── id -> ../../bin/busybox
│   ├── install -> ../../bin/busybox
│   ├── ipcrm -> ../../bin/busybox
│   ├── ipcs -> ../../bin/busybox
│   ├── killall -> ../../bin/busybox
│   ├── less -> ../../bin/busybox
│   ├── logger -> ../../bin/busybox
│   ├── logname -> ../../bin/busybox
│   ├── lrz
│   ├── lsof -> ../../bin/busybox
│   ├── lspci -> ../../bin/busybox
│   ├── lsscsi -> ../../bin/busybox
│   ├── lsusb -> ../../bin/busybox
│   ├── lsz
│   ├── lzcat -> ../../bin/busybox
│   ├── md5sum -> ../../bin/busybox
│   ├── mesg -> ../../bin/busybox
│   ├── mkfifo -> ../../bin/busybox
│   ├── mkpasswd -> ../../bin/busybox
│   ├── nc -> ../../bin/busybox
│   ├── nl -> ../../bin/busybox
│   ├── nmeter -> ../../bin/busybox
│   ├── nproc -> ../../bin/busybox
│   ├── nsenter -> ../../bin/busybox
│   ├── nslookup -> ../../bin/busybox
│   ├── od -> ../../bin/busybox
│   ├── passwd -> ../../bin/busybox
│   ├── paste -> ../../bin/busybox
│   ├── pgrep -> ../../bin/busybox
│   ├── pkill -> ../../bin/busybox
│   ├── pmap -> ../../bin/busybox
│   ├── printf -> ../../bin/busybox
│   ├── pscan -> ../../bin/busybox
│   ├── pstree -> ../../bin/busybox
│   ├── pwdx -> ../../bin/busybox
│   ├── readlink -> ../../bin/busybox
│   ├── realpath -> ../../bin/busybox
│   ├── record_process
│   ├── renice -> ../../bin/busybox
│   ├── reset -> ../../bin/busybox
│   ├── resize -> ../../bin/busybox
│   ├── rx -> ../../bin/busybox
│   ├── rz -> lrz
│   ├── script -> ../../bin/busybox
│   ├── seq -> ../../bin/busybox
│   ├── setfattr -> ../../bin/busybox
│   ├── setkeycodes -> ../../bin/busybox
│   ├── setsid -> ../../bin/busybox
│   ├── sha1sum -> ../../bin/busybox
│   ├── sha256sum -> ../../bin/busybox
│   ├── sha3sum -> ../../bin/busybox
│   ├── sha512sum -> ../../bin/busybox
│   ├── showkey -> ../../bin/busybox
│   ├── shred -> ../../bin/busybox
│   ├── shuf -> ../../bin/busybox
│   ├── smemcap -> ../../bin/busybox
│   ├── sort -> ../../bin/busybox
│   ├── split -> ../../bin/busybox
│   ├── ssl_client -> ../../bin/busybox
│   ├── strings -> ../../bin/busybox
│   ├── sum -> ../../bin/busybox
│   ├── svc -> ../../bin/busybox
│   ├── svok -> ../../bin/busybox
│   ├── sz -> lsz
│   ├── tail -> ../../bin/busybox
│   ├── tcpsvd -> ../../bin/busybox
│   ├── tee -> ../../bin/busybox
│   ├── telnet -> ../../bin/busybox
│   ├── test -> ../../bin/busybox
│   ├── tftp -> ../../bin/busybox
│   ├── time -> ../../bin/busybox
│   ├── timeout -> ../../bin/busybox
│   ├── top -> ../../bin/busybox
│   ├── traceroute -> ../../bin/busybox
│   ├── tree
│   ├── truncate -> ../../bin/busybox
│   ├── tty -> ../../bin/busybox
│   ├── udpsvd -> ../../bin/busybox
│   ├── unix2dos -> ../../bin/busybox
│   ├── unlink -> ../../bin/busybox
│   ├── unshare -> ../../bin/busybox
│   ├── uptime -> ../../bin/busybox
│   ├── uudecode -> ../../bin/busybox
│   ├── uuencode -> ../../bin/busybox
│   ├── vclk.sh
│   ├── volname -> ../../bin/busybox
│   ├── wc -> ../../bin/busybox
│   ├── which -> ../../bin/busybox
│   ├── whoami -> ../../bin/busybox
│   ├── whois -> ../../bin/busybox
│   ├── xargs -> ../../bin/busybox
│   ├── xxd -> ../../bin/busybox
│   ├── xzcat -> ../../bin/busybox
│   └── yes -> ../../bin/busybox
├── lib
│   ├── libakaudiocodec.so
│   ├── libakaudiofilter.so
│   ├── libakmedia.so
│   ├── libak_mrd.so
│   ├── libakv_encode.so
│   ├── libak_vibe.so
│   ├── libapp_aenc_ex.so
│   ├── libapp_ats.so
│   ├── libapp_its.so
│   ├── libapp_md.so
│   ├── libapp_mem_ex.so
│   ├── libapp_osd_ex.so
│   ├── libapp_video.so
│   ├── libcfifo.so
│   ├── libcurl.so.4
│   ├── libhttpAipn.so
│   ├── libiperf.so -> libiperf.so.0.0.0
│   ├── libiperf.so.0 -> libiperf.so.0.0.0
│   ├── libiperf.so.0.0.0
│   ├── libmpi_adec.so
│   ├── libmpi_aenc.so
│   ├── libmpi_demux.so
│   ├── libmpi_mux.so
│   ├── libmpi_osd.so
│   ├── libmpi_venc.so
│   ├── libnm.so
│   ├── libplat_ai.so
│   ├── libplat_ao.so
│   ├── libplat_common.so
│   ├── libplat_dbg.so
│   ├── libplat_isp_sdk.so
│   ├── libplat_log.so
│   ├── libplat_mem.so
│   ├── libplat_osal.so
│   ├── libplat_thread.so
│   ├── libplat_timer.so
│   ├── libplat_vi.so
│   ├── libplat_vpss.so
│   ├── libplat_vqe.so
│   ├── libPPCS_API.so
│   ├── libsqlite3.so.0
│   └── libssl.so.1.0.0
├── local
│   ├── ak_font_16.bin
│   ├── dnsd.conf
│   ├── factory_cfg.ini
│   ├── isp_f37_mipi_1lane_h3b.conf
│   ├── isp_F37p_mipi_2lane_h3b.conf
│   ├── isp_gc2063_mipi_2lane_h3b.conf
│   ├── isp_h63_mipi_1lane_h3b.conf
│   └── isp_h63p_mipi_1lane_h3b.conf
├── modules
│   ├── ak_adc_key.ko
│   ├── ak_efuse.ko
│   ├── ak_eth.ko
│   ├── ak_gpio_keys.ko
│   ├── ak_hcd.ko
│   ├── ak_i2c.ko
│   ├── ak_ion.ko
│   ├── ak_isp.ko
│   ├── ak_leds.ko
│   ├── ak_motor.ko
│   ├── ak_pcm.ko
│   ├── ak_pwm_char.ko
│   ├── ak_ramdisk.ko
│   ├── ak_rtc.ko
│   ├── ak_saradc.ko
│   ├── ak_udc.ko
│   ├── ak_uio.ko
│   ├── atbm6032.ko
│   ├── exfat.ko
│   ├── mac80211.ko
│   ├── power_config.txt
│   ├── rtl8188ftv.ko
│   ├── sensor_f22_f23_f28_f35_f37.ko
│   ├── sensor_gc2063.ko
│   ├── sensor_h63.ko
│   ├── sensor_h63p.ko
│   └── ssv6x5x.ko
├── sbin
│   ├── ak_env_key
│   ├── all_led.sh
│   ├── ap.sh
│   ├── arping -> ../../bin/busybox
│   ├── brctl -> ../../bin/busybox
│   ├── camera.sh
│   ├── capture_led.sh
│   ├── chat -> ../../bin/busybox
│   ├── chroot -> ../../bin/busybox
│   ├── device_save.sh
│   ├── dhcprelay -> ../../bin/busybox
│   ├── dnsd -> ../../bin/busybox
│   ├── eth_manage.sh
│   ├── f37.sh
│   ├── fsfreeze -> ../../bin/busybox
│   ├── ftpd -> ../../bin/busybox
│   ├── hostapd
│   ├── ifplugd -> ../../bin/busybox
│   ├── inetd -> ../../bin/busybox
│   ├── iwconfig
│   ├── iwgetid
│   ├── iwlist
│   ├── iwpriv
│   ├── iwspy
│   ├── key_gpio.sh
│   ├── key.sh
│   ├── killall5 -> ../../bin/busybox
│   ├── loadfont -> ../../bin/busybox
│   ├── main.sh
│   ├── nanddump -> ../../bin/busybox
│   ├── nandwrite -> ../../bin/busybox
│   ├── nbd-client -> ../../bin/busybox
│   ├── nfs_start.sh
│   ├── ntpd -> ../../bin/busybox
│   ├── readahead -> ../../bin/busybox
│   ├── readprofile -> ../../bin/busybox
│   ├── reboot.sh
│   ├── record_led.sh
│   ├── recover_cfg.sh
│   ├── rmshm.sh
│   ├── rtcwake -> ../../bin/busybox
│   ├── sar-adc.sh
│   ├── service.sh
│   ├── setfont -> ../../bin/busybox
│   ├── setlogcons -> ../../bin/busybox
│   ├── station_connect.sh
│   ├── telnetd -> ../../bin/busybox
│   ├── tftpd -> ../../bin/busybox
│   ├── ubiattach -> ../../bin/busybox
│   ├── ubidetach -> ../../bin/busybox
│   ├── ubimkvol -> ../../bin/busybox
│   ├── ubirename -> ../../bin/busybox
│   ├── ubirmvol -> ../../bin/busybox
│   ├── ubirsvol -> ../../bin/busybox
│   ├── ubiupdatevol -> ../../bin/busybox
│   ├── udhcpd -> ../../bin/busybox
│   ├── usb_hcd.sh
│   ├── usb_udc.sh
│   ├── wifi_driver.sh
│   ├── wifi_station.sh
│   ├── wpa_cli
│   └── wpa_supplicant
├── share
│   └── udhcpc
│       └── default.script
└── VER_LOOKCAM

7 directories, 279 files

dir.squashfs_2

2번 Squashfs 파티션에는 아래와 같이 파일로 요약됩니다.

💡
/bin -> anyka_ipc (서비스 IPC 바이너리)
-> cmd_serverd (서비스 IPC 바이너리)
/sbin -> main.sh (부팅시 실행되는 스크립트)
-> service.sh (부팅시 실행되는 스크립트)


[4-8-5. 안티 Debug UART 스크립트 변조]

# rc.S
#! /bin/sh

echo "mount all file system..."
mkdir /dev/pts
/bin/mount -av

runlevel=S
prevlevel=N
umask 022
export runlevel prevlevel

mkdir /var/cache
mkdir /var/run
mkdir /var/log
mkdir /var/spool

echo "**************************"
echo "    Love Linux ! ! ! "
echo "**************************"

/bin/hostname -F /etc/sysconfig/HOSTNAME

#local service
/etc/init.d/rc.local

/etc/init.d/rc.S - 수정 전

해당 스크립트에서는 문자열을 출력한 후, '/etc/init.d/rc.local' 스크립트가 실행되는 것으로 보입니다. UART를 통한 출력되는 문자열은 아래와 같이 수정하였습니다.

mkdir /var/spool

echo "**************************"
echo "    Hacked by DongDongE ! ! ! "
echo "**************************"

echo "DongDongE@d0ngd0nge.xyz v1.1 !!!"

#local service

/etc/init.d/rc.S - 수정 후

#!/bin/sh

function get_partition_dev()
{
	mtdblockid=`cat /proc/mtd | grep -E "\"$1\"$" | grep -E -o "mtd[0-9]+" | grep -E -o "[0-9]+"`
	if [ "$mtdblockid" != "" ];then
		echo "/dev/mtdblock$mtdblockid"
	else
		echo ""
	fi
}

# mount usr file-system.
#/bin/mount -t squashfs /dev/mtdblock7 /usr
partition_app=`get_partition_dev APP`
if [ "$partition_app" != "" ];then
	echo "/bin/mount -t squashfs $partition_app /usr"
	/bin/mount -t squashfs $partition_app /usr
fi

# mount jffs2 file-system.
#/bin/mount -t jffs2 /dev/mtdblock6 /etc/config
partition_config=`get_partition_dev CONFIG`
if [ "$partition_config" != "" ];then
	echo "/bin/mount -t jffs2 $partition_config /etc/config"
	/bin/mount -t jffs2 $partition_config /etc/config
fi

echo "starting mdev..."
/bin/echo /sbin/mdev > /proc/sys/kernel/hotplug
mdev -s

# note: can't recommend running other app before `mount` command.

ifconfig lo 127.0.0.1

#start system service
/usr/sbin/main.sh &

/etc/init.d/rc.local

'rc.local' 스크립트는 파티션 마운팅(Squashfs / JFFS2) 이후 최종적으로 '/usr/sbin/main.sh' 스크립트를 백그라운드에서 실행하므로, 해당 스크립트에 대한 분석을 진행합니다.

#!/bin/sh
FIND_FILE="/proc/cmdline"
FIND_STR="console=ttySAK0"
function get_partition_dev()
{
	mtdblockid=`cat /proc/mtd| grep "$1"| grep -E -o "mtd[0-9]+" | grep -E -o "[0-9]+"`
	if [ "$mtdblockid" != "" ];then
		echo "/dev/mtdblock$mtdblockid"
	else
		echo ""
	fi
}

if [ `grep -c "$FIND_STR" $FIND_FILE` -ne '0' ];then
	echo "MMMMMMMMMMMM open_debug MMMMMMMMMMMMMMMM"
else
	hex_size=$(devmem 0x08000074 32)
	var2=$((hex_size >> 4))
	var2=$((var2 << 4))
	devmem 0x08000074 32  $var2
fi 

echo 4 > /proc/sys/kernel/printk

#start ftp server, dir=root r/w, -t 600s(timeout)
#- /usr/bin/tcpsvd 0 21 ftpd -w / -t 600 &

syslogd -D -n -O /var/log/messages -s 200 -b 3 & # -l prio
klogd -n & # -c prio

ifconfig lo 127.0.0.1

## set min free reserve bytes
echo 2048 > /proc/sys/vm/min_free_kbytes

#echo "/tmp/core_%e_%p_%t" > /proc/sys/kernel/core_pattern

insmod /usr/modules/ak_rtc.ko
hwclock -s

insmod /usr/modules/ak_i2c.ko
insmod /usr/modules/ak_pcm.ko
insmod /usr/modules/ak_gpio_keys.ko
insmod /usr/modules/ak_ion.ko
insmod /usr/modules/ak_uio.ko
/usr/sbin/camera.sh setup
# start network  open by ljc
insmod /usr/modules/ak_saradc.ko
insmod /usr/modules/ak_eth.ko
ifconfig eth0 up
if test ! -f "/etc/config/mac.txt" ; then
    echo $RANDOM | md5sum | sed 's/../&:/g' | cut -c1-17 > /etc/config/mac.txt
else
	cat /etc/config/mac.txt | xargs -i /sbin/ifconfig eth0 hw ether {}
fi

#/usr/sbin/screen.sh

# mount yaffs2 file-system.
# mount -t yaffs2 /dev/mtdblock9 /data
partition_data=`get_partition_dev DATA`
if [ "$partition_data" != "" ];then
	echo "/bin/mount -t yaffs2 $partition_data /data"
	/bin/mount -t yaffs2 $partition_data /data
fi
insmod /usr/modules/ak_leds.ko
#start system service
/usr/sbin/service.sh start &

/sbin/main.sh - 수정 전

'main.sh' 스크립트에서 FIND_FILE="/proc/cmdline"FIND_STR="console=ttySAK0" 변수가 각각 UART 콘솔과 관련된 단어로 설정되어 있었습니다. 스크립트 실행 시 조건문에 따라 UART 콘솔이 작동되지 않도록 하는 로직이 포함되어 있었습니다.


이후 ftpd 서비스 실행 코드가 있었으나, 현재는 주석 처리되어 있었으며, 그다음 카메라 관련 모듈(*.ko)을 순차적으로 로딩하고 최종적으로 'service.sh' 스크립트가 실행됩니다."

# 해당 분기문 주석 처리
# if [ `grep -c "$FIND_STR" $FIND_FILE` -ne '0' ];then
# 	echo "MMMMMMMMMMMM open_debug MMMMMMMMMMMMMMMM"
# else
# 	hex_size=$(devmem 0x08000074 32)
# 	var2=$((hex_size >> 4))
# 	var2=$((var2 << 4))
# 	devmem 0x08000074 32  $var2
# fi 

# ftpd 서비스는 주석 해제, telnetd는 추가적으로 서비스 실행 코드 넣기
#start ftp server, dir=root r/w, -t 600s(timeout)
/usr/bin/tcpsvd 0 21 ftpd -w / -t 600 &
/usr/sbin/telnetd &

#'service.sh' 서비스를 백그라운드 실행 후 UART에서 Shell 실행을 위해 sh 삽입
#start system service
/usr/sbin/service.sh start &
/bin/sh

/sbin/main.sh - 수정 후

devmem 명령어를 통해 메모리 주소 0x08000074의 32비트 값을 가져온 후, 읽어온 값의 하위 4비트에 대한 시프트 연산을 수행하고, 다시 해당 메모리 주소에 데이터를 쓰기 때문에, 이 부분을 주석 처리하였습니다."

디버깅 모드에 따라 tcpsvd 서비스 데몬이 실행되지만, 현재 주석 처리되어 있어 이를 해제하여 데몬 서비스가 실행될 수 있도록 처리합니다. 추가로 telnet 바이너리가 존재하므로 telnetd 서비스도 실행될 수 있도록 해당 코드를 삽입합니다.

마지막으로, service.sh 스크립트가 백그라운드로 작동하므로 UART 콘솔에서 shell 실행을 위해 /bin/sh 한 줄을 추가로 삽입합니다.

실행 순서는 위 그림과 같으며, 현재 분석을 위해 파티션 변조는 빨간색으로 표시된 부분에서 진행되었습니다.

분석 당시, 먼저 Squashf 파티션이 위/변조를 제대로 인식하는지 먼저 테스트한 후, JFFS2 파티션도 수정 작업을 진행하였습니다. 이 부분에 대해서는 뒤에서 다시 설명드리겠습니다.


[4-8-6. Squashf 변조된 파티션 압축]

두 개의 Squashfs파티션에 있는 파일을 수정했으므로, 수정 후 다시 펌웨어에 삽입하기 위해 파티션 압축 작업을 진행합니다.

mksquashfs squashfs-root/ squash_1 -comp xz -b 131072

mksquashfs squashfs-root/ squash_2 -comp xz -b 131072

$ ls -la
	total 19856
	drwxrwxr-x  6 hashcat hashcat    4096 Aug 11 14:20  .
	drwxr-x--- 20 hashcat hashcat    4096 Aug 11 14:18  ..
	-rwx------  1 hashcat hashcat 8388608 Aug 11 12:32 '2024.08.11 - jffs2 수정본 원본 공장초기화 (고).bin'
	-rw-rw-r--  1 hashcat hashcat 1827112 Aug 11 13:19  dir.squashfs_1
	-rw-rw-r--  1 hashcat hashcat 4128636 Aug 11 13:20  dir.squashfs_2
	-rw-r--r--  1 hashcat hashcat 1830912 Aug 11 14:19  squash_1
	-rw-r--r--  1 hashcat hashcat 4128768 Aug 11 14:20  squash_2
	drwxrwxr-x  8 hashcat hashcat    4096 Mar  7 03:01  squashfs-root
	drwxrwxr-x 14 hashcat hashcat    4096 Feb 14  2022  squashfs-root_1

파티션 압축후 생성된 파티션 파일

파티션 압축이 완료되면 squash_1squash_2 두 개의 파티션이 생성됩니다.


[4-8-7. Squashf 파티션 펌웨어에 삽입]

수정된 두 개의 squash 파티션에 대해 적용을 하기 위해 추출한 펌웨어에 적용 준비를 합니다.

1875968       0x1CA000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 1827112 bytes, 289 inodes, blocksize: 131072 bytes, created: 2022-02-14 06:23:52
3719180       0x38C00C        JFFS2 filesystem, little endian
4046848       0x3DC000        Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4128636 bytes, 287 inodes, blocksize: 131072 bytes, created: 2024-03-07 03:01:44

삽입된 Squashfs 파일 시스템 계산

💡
1875968 -> 1827112 (1830912) = Squashfs filesystem 1
4046848 -> 4128636 (4128768) = Squashfs filesystem 2

Hex Editor를 사용해 Start(예: Squashfs 파일 시스템 시작 주소 - 1875968)와 Size(예: Squashfs 파일 시스템 크기 - 1827112) 값을 입력하여 해당 영역을 확인합니다.

확인한 후, 압축한 Squashfs 파일 시스템 1 파티션 데이터를 복사하여 삽입합니다.

첫 번째 Squashfs 파일 시스템이 정상적으로 삽입되었으며, 수정된(위/변조된) 파티션을 삽입한 후 시간이 변경된 것을 확인할 수 있습니다.

동일한 방법으로 나머지 파티션 2에도 수정된 파티션 데이터를 삽입합니다.

💡
(Address): 0x1ca000 - Squashfs FileSystem
-> 2022-02-14 06:23:52 (UTC) 날 파티션 생성
-> 중국 시간 변경 전 (2022-02-14 14:23:52 (UTC+8))
-> 대한민국 시간 변경 전 (2022-02-14 15:23:52 (UTC+9)

(Address): 0x3dc000 - Squashfs FileSystem
-> 2024-03-07 03:01:44 (UTC) 날 파티션 생성
-> 중국 시간 변경 전(2024-03-07 11:01:44 (UTC+8))
-> 대한민국 시간 변경 전(2024-03-07 12:01:44 (UTC+9)

수정된(위/변조된) 파티션의 경우, 수정되기 전 생성 시간은 위와 같으며, 현재 시점에서 파티션을 재압축하는 과정에서 새로 생성되었기 때문에 아래 그림과 같이 시간이 변경된 것을 확인할 수 있습니다.

2024-08-11 14:19:20 (UTC) -> 2024-08-11 23:19:20 (UTC+9)
2024-08-11 14:20:52 (UTC) -> 2024-08-11 23:20:52 (UTC+9)

위 데이터를 보면, 만약 시간이 변조된 상태에서 파티션 생성 작업을 하지 않았다면, 원본 펌웨어에서는 중국 시간 기준 2024-03-07 11:01:44 (UTC+8)에 두 번째 파티션이 생성된 것으로 확인할 수 있습니다.

0:00
/0:28

UART Console 안티 디버깅 우회 성공

위 영상에서 살펴보면, 이전과 다르게 추후 실행되는 데몬 & 프로세스에서 출력되는 로그를 확인할 수 있습니다.

0:00
/0:05

UART Console - ROOT 로그인 성공

이전에 키보드 입력이 되지 않던 부분도 우회되어, UART 콘솔을 통해 로그인 화면이 출력되었고, ROOT 계정으로 정상적으로 로그인하여 관리자 권한을 획득할 수 있었습니다. (ROOT 계정의 패스워드에 대해서는 뒤에서 Hashcat을 통해 설명드리겠습니다.)


[4-8-8. JFFS2 파티션 압축해제]

4-8-6 ~ 4-8-7에서 위/변조된 파티션이 정상적으로 작동하는 것을 확인했으니, 남은 JFFS2 파티션도 분석 후 변조를 시도하겠습니다.

JFFS2 파일시스템도 Squashfs 파일 시스템에서 추출하는것 처럼 동일하게 추출 후 아래와 같이 파티션 압축 해제 작업을 진행하였습니다.

$ jefferson dir.jffs2 -d jffs2-root
	dumping fs to /home/hashcat/firmware/jffs2-root (endianness: <)
	Jffs2_raw_inode count: 12
	Jffs2_raw_dirent count: 12
	writing S_ISREG anyka_cfg.ini
	writing S_ISREG hostapd.conf
	writing S_ISREG hostapd_rda.conf
	writing S_ISREG isp_h63p_mipi_1lane_h3b.conf
	writing S_ISREG lookcam.conf
	writing S_ISREG mac.txt
	writing S_ISREG passwd
	writing S_ISREG resolv.conf
	writing S_ISREG shadow
	writing S_ISREG ssv6x5x-wifi.cfg
	writing S_ISREG udhcpd.conf
	writing S_ISREG wpa_supplicant.conf

JJFS2 파일시스템 압축 해제

.
├── anyka_cfg.ini
├── hostapd.conf
├── hostapd_rda.conf
├── isp_h63p_mipi_1lane_h3b.conf
├── lookcam.conf
├── mac.txt
├── passwd
├── resolv.conf
├── shadow
├── ssv6x5x-wifi.cfg
├── udhcpd.conf
└── wpa_supplicant.conf

JJFS2 파일시스템에 존재하는 데이터

해당 파티션에는 아까 링크된 passwd 파일과 shadow 파일 외 몰래 카메라 설정값 (WiFi, Mac Address) 등 중요 정보가 담겨있는걸 확인할 수 있었습니다.

JJFS2 파일시스템에 존재하는 데이터

[4-8-9. Hashcat - Shadow root hash Crack]

shadow 파일을 확인하여 root 계정에 대하여 MD5 Crypt 알고리즘으로 단방향 암호화 (Hash) 처리가 되어 있는걸 확인할 수 있었으며, 해당 계정에 대해 접근하고자 크랙을 시도하였습니다.

HashCrack를 진행하기 위해, 회사에서 직접 제작한 고성능 H/W 연산을 통해 크랙을 시도합니다. (Hashcat에서 cat은 고양이를 의미하고 있어, 소속된 라온시큐어 공식 캐릭터 라옹이(고양이) 캐릭터를 빌려 라옹 해시캣 서버라고 부르고 있습니다. - 오늘도 열심히 일하는 라옹이...)

패스워드 크랙이 성공적으로 완료되어, 추후 UART 콘솔 통신에서 root 계정으로 로그인이 가능합니다.

크랙된 원본 패스워드 / Hashcat Server는 오프라인 보안 세미나에서만 공개하도록 하겠습니다.


[4-8-10. passwd & shadow 계정 추가]

shadow 파일에 존재하는 MD5 Crypt는 아래와 같이 SALT + Encrypt Hash password가 존재하며, 새로운 계정을 추가 하기 위해 openssl 명령어를 통하여 hash password를 추가 해보도록 하겠습니다.

# 계정 생성을 위한 HASH 추가, PW: toor
$ openssl passwd -1 -salt D0ngD0ngE
  Password:
  $1$D0ngD0ng$b37bx0u/ZKFY.I.8XMf3N0	

shadow hash 생성

Salt 값은 D0ngD0ngE, 패스워드는 toor로 설정하여 단방향 해시 암호화를 생성합니다.

passwd 파일과 shadow 파일에 각각 계정 정보와 생성한 해시 값을 삽입합니다.

추가된 계정의 이름은 'DongDongE'로 설정하였습니다.

$ cat shadow
  root:$1$R9kV6R9M$wZEX5igthSSohDB7EVH0s.:3652:0:99999:7:::
  bin:*:10933:0:99999:7:::
  daemon:*:10933:0:99999:7:::
  nobody:*:10933:0:99999:7:::
  DongDongE:$1$D0ngD0ng$b37bx0u/ZKFY.I.8XMf3N0:3652:0:99999:7:::

shadow 파일


[4-8-11. JFFS2 파티션 압축]

Squashfs 파일 시스템의 압축 및 해당 영역을 펌웨어에 삽입하는 과정은 동일하게 진행한 후, 최종적으로 CH341A Programmer를 사용해 펌웨어 쓰기 작업을 진행합니다.

0:00
/0:20

UART Console - 새로 생성한 DongDongE 로그인

UART 콘솔 통신에서 새로 생성한 계정 'DongDongE'로 로그인이 가능해졌습니다.!

UART Console - DongDongE(root) 로그인

Telnet - DongDongE(root) 로그인

처음에 비활성화되어 있던 Telnet 데몬 서비스도 원활하게 작동하여, 새로 생성한 계정으로 접속 테스트를 완료했습니다!

UART 콘솔 통신 대신, 몰래카메라에서 송출되는 AP를 통해 앞으로의 취약점 분석을 이어서 진행하도록 하겠습니다.


[4-8-12. Dynamic Analysis - Shell]

ROOT 권한을 획득했으므로, 본격적인 분석을 위해 해당 디바이스에서 분석을 진행하도록 하겠습니다.

[root@anyka ~]$ uname -a
  Linux anyka 4.4.192V2.1 #17 Wed Mar 2 14:25:31 CST 2022 armv5tejl GNU/Linux

uname

리눅스 커널 버전을 확인한 결과, anyka 4.4.192V2.1ARM armv5tejl을 사용하고 있습니다.

[root@anyka ~]$ uname -m
  armv5tejl
	
[root@anyka ~]$ cat /proc/cpuinfo
  processor       : 0
  model name      : ARM926EJ-S rev 5 (v5l)
  BogoMIPS        : 351.43
  Features        : swp half fastmult edsp java
  CPU implementer : 0x41
  CPU architecture: 5TEJ
  CPU variant     : 0x0
  CPU part        : 0x926
  CPU revision    : 5
	
  Hardware        : AK39EV330
  Revision        : 0000
  Serial          : 0000000000000000

uname & cpuinfo

CPU 모델명은 ANYKA사의 AK39EV330임을 확인할 수 있습니다.

임베디드 시스템에서 MTD(Memory Technology Device)는 정보를 제공하는 특수 파일이며, 아래와 같이 확인할 수 있습니다.

💡
mtd0 (U-BOOT): 부트로더 저장 영역
mtd1 (ENV): 환경 설정 정보 저장 영역
mtd2 (DTB): Device Tree Blob
mtd3 (KERNEL): 커널 저장 영역
mtd4 (ROOTFS): ROOT File System 저장 영역
mtd5 (CONFIG): 설정 파일 저장 영역
mtd6 (APP): 애플리케이션 저장 영역

이전에 변조한 3개의 파티션이 아래와 같이 마운트되어 있습니다.
/dev/root -> /: Squashfs File System
/dev/mtdblock6 -> /usr : Squashfs File System
/dev/mtdblock5 -> /etc/config : JFFS2 File System

# Loaded Kernel modules
[root@anyka ~]$ lsmod
    Tainted: G
  atbm6032 694555 0 - Live 0xbf1c5000 (O)
  ak_hcd 30355 0 - Live 0xbf1b9000 (O)
  mac80211 329118 0 - Live 0xbf154000
  usbcore 149648 2 atbm6032,ak_hcd, Live 0xbf120000
  usb_common 2455 1 usbcore, Live 0xbf11c000
  cfg80211 226670 2 atbm6032,mac80211, Live 0xbf0d4000
  ak_mci 24606 0 - Live 0xbf0c9000 (O)
  mmc_block 26854 2 - Live 0xbf0be000
  mmc_core 108730 2 ak_mci,mmc_block, Live 0xbf097000
  ak_leds 4469 0 - Live 0xbf092000 (O)
  ak_eth 24455 0 - Live 0xbf088000 (O)
  ak_saradc 6482 0 - Live 0xbf083000 (O)
  sensor_h63p 12507 0 - Live 0xbf07b000 (O)
  sensor_h63 12445 0 - Live 0xbf073000 (O)
  sensor_gc2063 12931 0 - Live 0xbf06b000 (O)
  sensor_f22_f23_f28_f35_f37 12582 0 - Live 0xbf063000 (O)
  ak_isp 170174 0 - Live 0xbf02f000 (O)
  ak_uio 4589 0 - Live 0xbf02a000 (O)
  ak_ion 3577 0 - Live 0xbf026000 (O)
  ak_gpio_keys 8131 0 - Live 0xbf021000 (O)
  ak_pcm 69564 1 - Live 0xbf00c000 (O)
  ak_i2c 9443 0 - Live 0xbf006000 (O)
  ak_rtc 9629 0 - Live 0xbf000000 (O)

load kernel module

현재 시스템에 로드된 커널 모듈들은 몰래카메라 하드웨어 장치기능을 작동시키기 위해 사용됩니다.
모듈에서는 '무선 네트워크', 'ISP 센서', 'SD Card', '오디오' 등 여러 관련된 모듈이 보이네요

[root@anyka ~]$ cat /proc/meminfo
  MemTotal:          38780 kB
  MemFree:            9180 kB
  MemAvailable:      18664 kB
  Buffers:            4388 kB
  Cached:            10616 kB
  SwapCached:            0 kB
  Active:            10336 kB
  Inactive:           8688 kB
  Active(anon):       4052 kB
  Inactive(anon):      556 kB
  Active(file):       6284 kB
  Inactive(file):     8132 kB
  Unevictable:           0 kB
  Mlocked:               0 kB
  SwapTotal:             0 kB
  SwapFree:              0 kB
  Dirty:                 0 kB
  Writeback:             0 kB
  AnonPages:          4044 kB
  Mapped:             5036 kB
  Shmem:               588 kB
  Slab:               5260 kB
  SReclaimable:       1396 kB
  SUnreclaim:         3864 kB
  KernelStack:         736 kB
  PageTables:          332 kB
  NFS_Unstable:          0 kB
  Bounce:                0 kB
  WritebackTmp:          0 kB
  CommitLimit:       19388 kB
  Committed_AS:      60740 kB
  VmallocTotal:     966656 kB
  VmallocUsed:           0 kB
  VmallocChunk:          0 kB

memory info

전체 메모리 크기는 ‘38MB’로 확인됩니다.

[root@anyka ~]$ cat /proc/cmdline
  root=/dev/mtdblock4 rootfstype=squashfs init=/sbin/init mtdparts=spi0.0:180K@0x0(UBOOT),4K@0x2D000(ENV),48K@0x2E000(DTB),1600K@0x3A000(KERNEL),1800K@0x1CA000(ROOTFS),320K@0x38C000(CONFIG),4240K@0x3DC000(APP) mem=64M memsize=64M

cmdline

몰래 카메라 리눅스 커널 부팅시 사용되는 커맨드 라인 매개변수입니다.


[4-8-13. Dynamic Analysis - Cam Password Save File]

[root@anyka /etc/config]$ ls -la
	total 89
	drwxr-xr-x    3 root     root             0 Aug 13 07:14 .
	drwxrwxr-x    5 1000     1000           407 Feb 14  2022 ..
	-rw-r--r--    1 root     root            12 Aug 13 07:14 .devpsd
	-rwxr--r--    1 root     root          6986 Jul  1  2023 anyka_cfg.ini
	-rwxrwxr-x    1 1000     1000           284 Jul  1  2023 hostapd.conf
	-rwxr--r--    1 1000     1000           216 Aug 11 15:50 hostapd_rda.conf
	-rwxr--r--    1 root     root         76112 Jul  1  2023 isp_h63p_mipi_1lane_h3b.conf
	-rwxr-xr-x    1 1000     1000            55 Aug 11 15:50 lookcam.conf
	-rw-r--r--    1 1000     1000            18 Aug 11 15:50 mac.txt
	-rwxrwxr-x    1 1000     1000           159 Aug 11 15:54 passwd
	-rwxr--r--    1 1000     1000            34 Aug 11 15:50 resolv.conf
	-rwxrwxr-x    1 1000     1000           203 Aug 11 15:58 shadow
	-rwxrwxr-x    1 1000     1000          2124 Aug 11 15:50 ssv6x5x-wifi.cfg
	-rwxrwxr-x    1 1000     1000           202 Aug 11 15:50 udhcpd.conf
	-rwxrwxr-x    1 1000     1000           237 Aug 11 15:50 wpa_supplicant.conf

[root@anyka /etc/config]$ cat .devpsd
	112233445566

/etc/config - JFFS2

'/etc/config'에는 몰래카메라의 중요한 설정 정보가 포함되어 있습니다.
기본적으로 카메라의 패스워드는 ‘123456’으로 설정되어 있으며, 사용자는 ‘Lookcam APP’을 통해 카메라를 제어할 수 있습니다.

하지만 누구나 접근하지 못하도록 APP에서 패스워드를 변경할 수 있으며, 설정 시 아래와 같은 파일이 생성됩니다.

기본 패스워드를 변경하면 '/etc/config/.devpsd' 파일이 생성되며, 해당 파일에 사용자가 설정한 패스워드가 저장됩니다.

Ex), '123456'에서 '112233445566'으로 변경된 패스워드가 파일에 저장됩니다.


[4-8-14. Dynamic Analysis - Cam WiFi(AP) Save File]

[root@anyka /etc/config]$ cat wpa_supplicant.conf
ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
network={
        ssid="<무선 공유기 WiFi 이름>"
        key_mgmt=WPA-PSK WPA-EAP IEEE8021X NONE
        pairwise=TKIP CCMP
        group=CCMP TKIP WEP104 WEP40
        psk="<무선 공유기 패스워드>"
	scan_ssid=1
}

/etc/config/wpa_supplicant.conf - WiFi에 연결한적이 있을 경우

포렌식 관점에서 몰래카메라 디바이스가 외부 네트워크(WiFi)에 연결된 적이 있다면, '/etc/config/wpa_supplicant.conf' 경로에 무선 공유기 접속 정보인 SSID(WiFi 이름) PSK(WiFi 패스워드)가 저장되어 있습니다.

물리적인 리셋 버튼을 눌러 공장 초기화를 하여도 이 파일은 부분적으로 지워지지 않고 남아 있었습니다.

위 예시에서는 맥북의 핫스팟 기능을 사용해 'Pentest'라는 이름의 네트워크에 'toortoor' 패스워드를 설정했으며, 몰래카메라가 이 핫스팟에 연결(접속)하면서 남겨진 흔적입니다.


[4-8-15. Dynamic Analysis - Debug Mode Enable]

이번에는 서비스 스크립트를 분석하고, 디버깅 모드를 활성화하여 Telnet 사용 및 Reverse Shell을 통해 root 패스워드를 알지 못하더라도 쉘을 획득하는 방법을 설명드리겠습니다.

이전에 서비스 및 스크립트 실행 순서에 대해 설명드린 바 있습니다. 해당 스크립트는 아래 순서대로 실행되며, 디버깅 모드를 활성화하기 위해 분석한 경로의 스크립트도 함께 참조해주시면 됩니다.

main.sh에서 service.sh 스크립트를 실행하면, 해당 스크립트 내 전역 변수로 이미 'FACTORY_TEST=0' 값이 비활성화된 상태로 삽입되어 있습니다.

그러나 조건문에 따라 /mnt/ (SD Card) 경로에 usbnet 폴더가 존재할 경우, 해당 변수에 값 '1'이 삽입되면서 디버깅 모드가 활성화되는 것을 확인할 수 있었습니다.

💡
FACTORY_TEST = 1 (디버깅 모드 활성화)
FACTORY_TEST = 0 (디버깅 모드 비활성화 - 기본값)

조건문 실행 조건: 활성화를 위해 삽입된 SD Card 경로인 /mnt/에 usbnet 폴더가 존재해야 됨

디버깅 모드가 활성화되면 FTP 서비스가 실행되고, 네트워크가 활성화된 후 Telnet 서비스도 함께 작동됩니다. 마지막으로, SD Card 경로인 /mnt/usbnet/ 폴더에 product_test라는 파일명으로 바이너리 또는 스크립트가 실행되는 것을 확인할 수 있습니다.

SD Card에 미리 /mnt/usbnet/product_test 스크립트 파일을 생성한 후, 몰래카메라의 기본 프로세스 작동을 위해 ipc 서비스를 백그라운드로 실행하고, 리버스 쉘 코드를 삽입하면 쉘 획득이 가능합니다.

분석 과정에서 해당 작업은 모두 root 권한이 필요하기 때문에, 리버스 쉘 획득 시 당연히 root 권한을 가진 쉘을 얻게 됩니다.

순서대로 anyka_ipc 백그라운드 서비스를 실행하고, 잠시 35초간 대기한 후, 분석관 PC로 Reverse Shell을 요청할 수 있도록 설정합니다.

물론, Reverse Shell 스크립트 대신 주석 처리된 passwd 명령어를 사용해 root 패스워드를 변경한 뒤, telnet을 통해 변경된 패스워드로 접근하는 방법도 가능합니다.

쉘을 획득하는 여러 방법이 있으니, 상황과 환경에 맞춰 적절하게 선택하시면 됩니다.

요렇게 또! root 권한을 획득하여, 추가적으로 분석을 이어서 진행이 가능합니다.

만약 /etc/shadow 파일에서 추출한 'root' 패스워드의 해시값을 평문값으로 크랙하지 못했다면, 위와 같은 방법으로 분석을 이어가려고 했지만, 역시 회사에서 제가 만든 hashcat Server 성능은 좋았군요!


[4-8-16. Open Port]

$ sudo nmap -sn 192.168.100.0/24
  Starting Nmap 7.94 ( https://nmap.org ) at 2024-07-02 11:50 KST
  Nmap scan report for 192.168.100.1
  Host is up (0.035s latency).
  MAC Address: 44:29:1E:AE:18:79 (AltoBeam (China))
  Nmap scan report for 192.168.100.20
  Host is up.
  Nmap done: 256 IP addresses (2 hosts up) scanned in 2.67 seconds

오픈된 서비스가 없음

TCP 포트 스캔 결과, 몰래카메라 AP 192.168.100.1에서 오픈된 TCP 포트가 존재하지 않았습니다. 이에 대해 왜 포트가 탐지되지 않았는지에 대한 상세 설명을 아래 APP 분석에서 이어서 진행하도록 하겠습니다.


[4-9. network & APP (LockCam) Analyze]

[4-9-1. WiFi SSID & Device UID Analysis]

몰래카메라가 무선 공유기(AP)에 연결 없이, 자체적으로 송출되는 AP Mode의 경우 아래와 같이 여러 몰래카메라에서 확인된 SSID List 입니다.

위 규칙성을 보면, 첫 글자는 대문자로 시작하고, 6자리 숫자와 5자리 문자열로 조합되어 있는 것을 확인했습니다.


만약 Wi-Fi 리스트에서 이와 유사한 SSID가 발견된다면 근처에 해당 몰래카메라가 설치되어 있을 가능성이 있습니다. 하지만 앞서 설명한 것처럼, 몰래카메라 제조사가 Lookcam APP을 무단으로 사용하여 정상적인 홈 보안 카메라에서 송출되는 SSID와 유사하게 설정할 수 있으므로, 해당 SSID가 반드시 몰래카메라에서 송출된 것이라고 단정할 수는 없습니다.

AP Mode로 송출된 SSID를 분석한 결과, 위 그림과 같이 '제조사 식별 번호' + 'Serial Number 또는 Device ID' + 'Check Code'의 조합으로 설정되는 것으로 보입니다

Prefix는 이미 APP에서 리스트 형식으로 저장되어 있어 쉽게 확인할 수 있었지만, Check Code는 몰래카메라의 외부 서버에서 확인되기 때문에, 어떠한 규칙으로 조합되는지는 파악할 수 없었습니다.


[4-9-2. 분석 환경]

현재 분석은 몰래카메라의 하드웨어(H/W)와 소프트웨어(S/W) 관점에서는 몰래카메라 프로세스 작동을 위한 '바이너리 ipc 서비스', 몰래카메라 연동을 하기 위한 모바일 단말기 APP 'SO 파일 & APK' 그리고 마지막으로 이과정에서 외/내부 통신 패킷을 분석을 진행해야 합니다.

위와 같은 분석은 아래와 같이 진행하였습니다.

바이너리 분석에는 당연히 IDA Pro 망치로 '' 하면 '' 부셔 버리고,
APP에 대해 동적 분석과 메모리 변조(후킹)을 위해 JEB PRO와 FRIDA를 동시에 사용하여 분석을 진행합니다.

분석을 위해 분석용 단말기와 몰래카메라가 1:1로 통신하는 과정에서 네트워크 패킷을 모니터링하려면, ARM Android용 tcpdump 바이너리를 실행하고 ADB를 통해 분석용 MacBook으로 포워딩하면 됩니다. 이를 통해 실시간으로 분석관 PC에서 네트워크 패킷을 모니터링할 수 있습니다.

만약 몰카범이 외부 공간에서 몰래카메라를 설치했다면, 외부 통신이 어려워지므로 근처에서 몰래카메라가 송출하는 무선(AP)에 연결해 제어를 하거나, 몰래카메라를 다시 회수하기 위해 현장에 나타날 가능성이 높습니다.

이번에는 몰래카메라가 주변 무선 공유기(현재 분석관 PC에서 송출하는 무선 AP)에 연결된 상황을 설정하여, 외부 네트워크를 통해 장거리에서도 몰래카메라에 접속할 수 있는 환경을 구성하였으며, 외부 통신 패킷을 추가적으로 분석할 수 있습니다.


[4-9-3. Custom UDP Packet 분석]

'4-9-2' 과정에서 바이너리 분석과 네트워크 패킷 분석을 통해 확인한 결과, 몰래카메라는 Custom UDP Protocol을 사용하여 Peer-to-Peer(P2P) 방식으로 통신하고 있습니다.

예를 들어, 일반적인 UDP 통신과 달리 이 Custom UDP Protocol은 TCP처럼 요청/응답 여부를 확인한 후에 다음 과정이 진행됩니다. 특히 외부에서 몰래카메라에 접근하기 위해 방화벽 차단이 설정된 경우에도, 자체 Custom UDP P2P 프로토콜을 통해 인바운드 포트가 열려 있지 않아도 외부에서 접근이 가능합니다.

아래 통신 과정은 시간관계상 아직 구체적으로 분석이 진행되지 않아 확실하지 않을 수 있습니다.

첫 번째로, 분석에 따르면 단말기에서 Broadcast 방식으로 F1300000 데이터를 전송하면, 몰래카메라가 이를 수신하고 UDP 랜덤 포트로 회신을 보냅니다. 이 회신 데이터에는 Vendor Specific 값인 FHBB와 몰래카메라의 Check Code인 WWJKN 값이 포함되어 있습니다

이때 모바일 단말기(분석용)는 수신된 랜덤 포트를 통해 몰래카메라의 로그인 정보를 전송하게 됩니다.

로그인 정보에 따라 인증 여부가 결정되며, 올바른 인증 정보가 제공되면 'result:0' 값을 수신하게 됩니다. 인증이 성공한 후, APP에서 수신 완료를 알리는 패킷을 전송하고, 이후 양측 간의 연결이 완료됩니다.

이후 '4-9-4'에서 APP 분석에 대해 자세히 설명하겠지만, 위 변조 과정은 정상적인 영상 다운로드 경로를 다른 값으로 변경하여 발생한 파일 다운로드 취약점입니다.

이때 전달되는 패킷의 길이와 페이로드는 위/변조된 데이터 크기에 맞춰 변경 후 전송해야만 취약점이 발생합니다.


[4-9-3. LockCam APP Analyze]

[APK Debug mode enable]

JEB Pro 동적 디버깅 모드를 사용하기 위해, 루팅된 단말기에서 강제로 디버깅 모드를 통해 앱을 실행할 수 있습니다. 해당 앱은 자체적으로 무결성 검증이나 루팅 탐지 로직이 없어, 리패키징 원리를 통해 디버깅 모드를 활성화할 수 있었습니다.

'AndroidManifest.xml' 파일의 'application' 항목에 'android:debuggable="true"' 옵션을 넣었습니다.

각종 activity를 하나씩 호출하며 어떤 기능이 있는지 분석 도중 재미있는 액티비티를 발견하였습니다. 해당 액티비티 이름은 'AboutActivity'였으며, 'com.ppcs.activity.AboutActivity' 경로에 로직이 존재하였습니다.

'onLongClick' 함수가 호출되면 2~3초간 해당 로고를 누르고 있을 때, 팝업 창이 열리면서 로그를 추출할지 묻는 이벤트가 발생합니다. 더 깊이 분석하기 위해 로그를 추출했으며, 아래와 같이 로그 파일이 기록되었습니다.

해당 로그에서 몰래카메라의 고유 'Device UID'와 기본 패스워드 값인 '123456'을 확인할 수 있었습니다. (이전에 모바일 단말기와 몰래카메라 연결했을 경우, 연결했던 시간과 로그인 성공 여부가 로그로 기록되어 포렌식 분석에 도움이 될것같네요)

몰래카메라와 모바일 단말기 연결 시, 패스워드 인증에 대한 응답 데이터가 로그 파일에 JSON 형식으로 기록됩니다

패스워드가 올바르게 입력된 경우에는 result:0 값이 나타나며, 패스워드가 잘못 입력되었을 경우에는 result:-1 값이 기록됩니다.

이번에는 Android Logcat를 모니터링 결과에서도 , 패스워드 인증 여부에 대한 응답 데이터도 실시간으로 확인할 수 있었습니다. 인증 결과는 logcat에 출력되며, 이는 로그 파일에도 기록되는 것으로 확인되었습니다.

Logfile은 Android 단말기의 '/storage/emulated/0/Android/data/com.view.ppcs/files/Log/*' 경로에 저장되며, 로그 추출시 하나로 압축되어 추출됩니다.

모바일 단말기에서 몰래카메라 영상 파일을 다운로드할 때, 'startDownload' 함수가 호출되며, 전달되는 파라미터 인자는 5개 + 비밀번호(pw) 1개가 포함됩니다.

마지막으로 해당 데이터는 JSON 형식으로 전달되고, 이때 어떻게 인자값이 전달되는지 아래와 같이 logcat를 통하여 확인하였습니다.

- cmd: DownloadFile (다운로드를 위한 명령 전달)
- path: 다운로드 하기 위한 절대경로의 파일

만약 몰래카메라 단말기에서 path에 대한 필터링(시큐어코딩)이 없다면, 파라미터 변조를 통하여 다른 파일을 다운로드 가능할것으로 추측 할 수 있습니다. (파일 다운로드 취약점)

0:00
/0:27

동적 디버깅 - /etc/shadow 다운로드

위 영상에서, JEB Pro를 통하여 다운로드 함수가 실행되고, 전달되는 파라미터를 확인하기 위해 브레이크 포인트를 적절하게 지정하고, path(v5) 파라미터를 '/etc/shadow'으로 변조하여, 해당 파일을 다운로드가 가능한 취약점입니다.

이전 anyka_ipcroot 권한으로 바이너리가 실행됨으로 당연히 관리자 권한으로 해당 파일이 다운로드가 가능합니다.

0:00
/0:27

동적 디버깅 - /etc/config/wpa_supplicant.conf 다운로드

파일 다운로드 취약점을 통하여 몰래카메라 이전에 AP(무선 공유기)에 연결한 파일도 다운로드가 가능하였습니다. (공장 초기화하여도 일부 남아있었음)

0:00
/0:48

Frida를 통한 파일다운로드 취약점 & Wireshark를 통한 패킷 확인

위 영상은 APP Hooking을 진행하기 위해 Frida 스크립트를 작성하여, 임의의 파일을 다운로드할 때 '/etc/shadow' 파일로 변조하여 다운로드할 수 있도록 구현하였습니다. 또한, Wireshark를 통해 해당 패킷이 정상적으로 위/변조되어 다운로드되는 과정을 확인할 수 있었습니다."

이번에는 'com.view.ppcs.DataCenter.LuPPCSSession'의 'loginDevice' 함수에서 패스워드 인증 성공 여부는 몰래카메라 단말기가 아닌 모바일 단말기 앱(클라이언트)에서만 검증함으로 , int v = jSONObject1.getInt("result"); result 값을 강제로 '0'으로 변조한다면 패스워드 없이 로그인 우회가 가능합니다.

0:00
/0:36

로그인 인증 우회 하기전

로그인 인증 우회 하기전, 정상적인 패스워드 '123456' 대신 임의 패스워드를 입력시 모바일 단말기에서는 인증 실패로 'Password error' 출력과 함께 로그인이 되지 않는 부분을 볼 수 있습니다.

0:00
/0:15

로그인 인증 우회

Frida Hooking를 통하여, logcat에서 출력된 내용을 보면, 올바르지 않는 패스워드 임으로 'result:-1'를 반환하지만 강제로 '0'를 반환하게 후킹함으로 몰래카메라 인증 성공으로 'Device Online' 출력이 되고 정상적으로 로그인이 가능하였습니다.

0:00
/0:18

Custom UDP Protocol 분석

Custom UDP Protocol P2P를 분석 후, Frida Script를 작성하여, 몰래카메라에 대한 인증없이 로그인도 가능하고, 여러 함수 호출로 Device 정보 확인, 이전 AP 연결 정보 확인, 강제로 SD Card 포맷, 디바이스 초기화, 재부팅 등 다양하게 위 취약점으로 전부 가능하였습니다.

(패스워드 인증 로직 부분은 이후 디지털 포렌식 SD Card 데이터 복구 부분에서 ipc 바이너리 리버싱으로 인증 로직이 없는 부분을 설명드리겠습니다.)


[4-9-4. 외부 클라우드 서버 정보]

💡
외부 클라우드 서버에 대한 정보는 오프라인 보안 세미나에서만 공개하도록 하겠습니다.

외부 클라우드 서버는 싱가폴, 미국, 태국, 중국(본 서버)로 확인되었으며 만약 몰래카메라 촬영된 영상을 외부 클라우드로 저장 시 위 국가 클라우드 서버에 저장될 것으로 확인되고, 클라우드 서버의 소유 브랜드를 공유 하기엔 추후 문제가 발생할것 같아 오프라인 세미나에서만 공개하기로 하겠습니다.


[4-10. Digital Forensic - SD Card Format Recovery]

마지막으로, 몰카범이 범죄 현장에 설치된 단말기가 발각되어 증거물로 수집되기전, 원격으로 SD Card 포맷시 손상된 영상 파일을 복구 하는 방법에 대해 설명드리겠습니다.

촬영된 영상 파일은 몰래카메라에 삽입된 SD Card 경로인 '/mnt/SYC_DV/<년도>/<월>/<일>/*.mp4' 으로 영상 파일이 저장됩니다.

현재 9개의 mp4 샘플 영상 파일이 존재합니다. (본 영상 파일은 진짜 분석을 위한 샘플 파일로 연구실 책상에서 회사 로고랑 회사 인형만 촬영하였습니다.)

몰래카메라와 연결된 모바일 단말기(Android & iOS)에서 앱을 통하여 SD Card 포맷을 진행합니다.

SD Card에 대해 포맷 요청시 송/수신된 패킷입니다. 포맷을 위한 'FormatCard' 명령어가 전달되며, 포맷 되기 전/후의 SD Card 용량을 살펴보면, 포맷 후 전체 사이즈와 사용할 수 있는 사이즈가 같아짐을 확인할 수 있습니다.

몰래카메라 ipc 바이너리를 분석한 결과, 전달되는 cmd 파라미터 데이터가 'FormatCard'가 존재 시 'sscMonFormatCard()' 함수가 실행됨을 확인할 수 있습니다.

분명 SD Card 포맷시 같이 pw(패스워드)도 전달되는데 ipc에서 검증되는 부분은 단순히 'FormatCard' 단어가 있는지 cmd 파라미터만 존재함으로, 패스워드 인증 없이 강제로 SD Card 포맷도 가능합니다.

해당 함수에서는 'mkfs.vfat' 명령어를 통하여 SD Card를 단순히 포맷을 진행하는 로직을 확인할 수 있습니다.

전문적인 포렌식 장비인 쓰기 방지 장치를 구매하기엔 너무 비싸서, 쓰기 방지 모드를 간단하게 사용하기 위해 Micro SD Card를 SD Adapter에서 제공하는 쓰기 방지 기능을 활성화 하여 분석을 진행하였습니다.

시나리오 상, 방금 몰카범이 SD Card를 원격으로 포맷을 진행하여, 포렌식적으로 분석 하기 위해 바로 추출 후 쓰기 방지 모드가 되는 장비에 연결하였다고 보시면 될것 같습니다.

사실 해당 어댑터 원리는 SD Card에서 쓰기 방지가 되는것이 아니라 아래와 같이 일부 SD Card 리더기에서 쓰기 방지 모드를 지원하는 원리라서 실제 포렌식에서는 위 장비 대신 전문적인 쓰기 방지 모드가 되는 장비를 통해 진행됩니다.

다이소에서 5천원 주고 구매한 리더기에서는 다행이 쓰기 방지 모드가 지원되었습니다.

포맷된 SD Card를 연결 시, 저장된 영상 파일은 당연히 포맷되어 전부 지워짐을 확인할 수 있습니다.

정확한 분석을 위해 쓰기 장비 모드로 삽입된 SD Card 전체를 FTK Image를 통하여 이미지 덤프를 진행합니다.

삭제된 9개의 영상 파일을 확인할 수 있으며, 해당 영상 파일 생성 시간 또한 제거되지 않고 원본으로 시간이 남겨져 있었습니다.

영상은 테스트를 위하여 5분 간격으로 촬영된 영상이 새로 저장됩니다.

그 외 데이터 복구 툴에서도 삭제된 영상 파일을 확인할 수 있으나, 손상으로 인하여, 썸네일이 출력되지 않음을 확인할 수 있습니다.

복구된 9개의 파일이 영상 재생이 되지 않아, 삭제 되기전 원본 영상 파일과 복구된 영상 파일의 MD5를 비교해본 결과 서로 달랐습니다.

좌측은 복구된 영상이고, 우측은 원본 영상 파일입니다. 메타데이터 정보가 삭제되어 영상 출력이 되지 않았습니다. (메타데이터에는 영상 해상도, 화질, 인코딩, 프레임 등 정보가 있음)

복구된 영상은 메타 데이터 미 존재로, 영상 재생이 되지 않았습니다.

위 사진은 원본 영상 파일이며, 메타데이터 존재 여부에 따라 썸네일 출력 및 재생이 가능합니다.

메타데이터 복구를 위해, 해당 몰래카메라에서 새로 촬영을 진행하고 촬영된 영상 파일 1개를 추출하여 거기서 메타 데이터만 가져오고 해당 데이터를 손상된 영상 파일에 삽입하면 복구가 됩니다.

-'good.mp4' 파일은 메타 데이터를 추출하기 위한 새로 촬영된 영상 파일

-'2024-0824-*.mp4' 파일은 데이터 복구를 통한 추출된 영상 파일

untrunc를 통하여, 원본 영상 파일과 손상된 영상 파일을 넣어 메타 데이터 복구를 시도합니다.

메타 데이터가 성공적으로 복구가 되어, 이전에 보이지 않았던 썸네일을 확인할 수 있었습니다.

SD Card 포맷으로 삭제된 영상 파일을 추출하여, 손상된 메타데이터 까지 복구 함으로 증거물에 대한 복구가 성공되었습니다.

Tagged in:

RedTeamLAB

Last Update: December 18, 2024