[KakaoTalk+] LOCO 프로토콜 분석 (4)

LOCO 프로토콜 분석 세번째 시리즈에 이은 네번째 시리즈이자, 마지막 시리즈 입니다.

 

4.5. LOCO 프로토콜 – sKey 추출하기

요전 포스팅의 댓글에도 달았듯이 패킷 구성이나 보내는 방법등에 대해서 열심히 설명해놓고.. 정작 중요한 세션키 추출 방법에 대해서 설명하지 않았다는걸 깨달았습니다 ㅠㅠ
그.래.서. 준비했습니다! 섹션 4.5, sKey 추출하기~

요즘 다른 OS용 앱에서는 어떻게 하는지 모르겠으나, 윈폰용에서는 세션키가 두 가지 종류가 있습니다. 딱히 ‘종류’라고 하기엔 뭐하지만 어느 용도로 사용되냐에 따라서 살짝 다른 방식으로 세션키를 생성합니다. 무슨 말이고 하니, POST request를 사용하는 커맨드에 사용하는 세션키 헤더 (HTTP_S)에 들어가는 값과 LOCO 커맨드 패킷에 들어가는 세션키 (sKey)값이 다르다는 것입니다 :)

LOCO 커맨드에서 사용하는 세션키를 sKey라고 할때, HTTP_S에 할당되는 세션키 값은 ‘sKey-duuid’ 형식을 가집니다. 예를 들어, 처음 앱을 설치하여 핸드폰 번호 인증을 할때 생성된 sKey가 93fe4b0a127d4798b9385313253b36deac811c12b5814e4b28efd75dc52ca07b 이고 기기 고유값을 나타내는 duuid가 C0DC4F201BA7F02381CE431292A0C9D0478B929E라고 할때, HTTP_S 헤더에는 93fe4b0a127d4798b9385313253b36deac811c12b5814e4b28efd75dc52ca07b-C0DC4F201BA7F02381CE431292A0C9D0478B929E 라고 넣어줘야한다는 것이죠.

자 그럼 이제 각각 상황에서 어떤 식으로 세션키를 만들어야하는지 알았으니, 세션키의 기본이자 전부인 (-_-;;) sKey 추출방법에 대해서 이야기 해봅시다.

사실 sKey 추출에도 폰/앱 스토리지 분석, 동적 분석 (디버깅), POST request 캡쳐, LOCO 커맨드 패킷 캡쳐 등 여러가지 방법이 있는데요…
에뮬레이터라 데이터 뽑아내기가 귀찮으므로, POST request 패킷 캡쳐를 통한 추출 방법과 LOCO 커맨드 패킷 캡쳐를 통한 방법 두가지를 알려드리도록 하겠습니다.

[1] POST request 패킷 캡쳐하기

사실 이 부분에 대한 기술적인 내용은 이미 첫번째 시리즈에서 다루었습니다. 앱 상에서 HTTP request를 위한 HttpScheme을 정할때 https 대신 http를 사용하도록 패치한 뒤, 패치된 앱을 사용하면서 네트워크 덤프를 한다면 원래는 기본적으로 https request를 하던 API들이 http request를 사용하여 SSL encryption 없이 패킷을 볼 수 있게 됩니다. 만약 바이너리 코드 내에서 이러한 부분을 찾기 힘들다면, 제가 예전에 안드로이드용 앱으로 분석할때 실제 사용했던 방법 중 하나인 https 스트링 자체를 http로 모두 치환하는 방법이 있습니다 ㅎㅎ

윈폰용 앱에서 이 방법으로 request들을 캡쳐해서 보면 다음과 같습니다.

HTTP_S 헤더 부분은 삭제해놓았지만, 실제로 해보시면 저 부분에 ‘sKey-duuid’ 포맷의 세션키를 발견하실 수 있습니다.

 

[2] LOCO 커맨드 패킷 캡쳐 및 decrypt 하기

에뮬레이터에서 내보내는 패킷에 암호화되어 첨부되있는 sKey를 추출하기 위해서는 앱 프로세스에 어태치하여 디버깅하면서 메모리에 담겨있는 sKey 값을 가져오거나… 또는 그냥 나가는 패킷을 캡쳐한 다음 복호화하여 보면 됩니다. 하지만, 복호화 하기 위해서는 RSA로 암호화 되어있는 AES key를 알아내야 하죠.. ㅡ_ㅡ.. 그럴려면 또 메모리에서 AES Key를 가져와야하고… 여간 귀찮은게 아닙니다. 그렇다고 RSA private key를 아는것도 아니구요 (만약 알았다면.. 제가 이 블로그 글을 쓰는대신… 다른 아주 재미난걸 하고 있었겠죠..? 흐흐..)

하지만 이 역시 코드 분석을 조금만 해보면 쉬운 해결책이 있습니다. 다음 코드를 보시죠.

아니.. 이건..? 제 블로그에서만 벌써 세번째로 보는 코드죠?
이 C# 코드에 해당하는 MSIL 코드를 보시면 다음과 같습니다.

파라미터들을 순서대로 로드해 준 뒤 생성된 Guid의 처음 16바이트를 aes_key 바이트 버퍼에 복사하기 위해 BlockCopy 메소드를 호출합니다.
여기서 초록색 화살표로 표시해둔 부분이 0x10 바이트, 즉 16바이트만큼 카피하라는 length를 가르키는 상수를 로드하는 부분입니다.
C#에서 new byte[0x10]와 같이 byte array를 생성하게 되면 기본적으로 각 바이트가 0으로 초기화 됩니다. 자 그럼, 여기서 생성된 랜덤 Guid를 16바이트 대신 0바이트를 카피하면 어떻게 될까요? 그렇죠. aes_key는 변하지 않고 0의 값을 가지게 됩니다 ㅋㅋ..

사실 이 방법 말고 따로 코드를 인젝트해서 Guid로 생성되는 값이 아닌 자신이 알고 있는 특정 값으로 aes_key 값을 업데이트 시켜줘도 됩니다. 사용된 aes_key가 무엇인지만 알면 같은 키로 복호화할 수 있기 때문이죠. 하지만 MSIL 코드 인젝트하고 다시 패키징하고 등등.. 귀찮으니까 최소의 패치만으로 최고의 편리함을 얻을 수 있는.. (무려 1바이트 패치!) ldc.i4.s 0 으로 만들기 방법을 사용하였습니다. 이렇게 되면, 서버에게 handshake 패킷을 보낼때 항상 16 byte 길이의 0 스트링을 aes_key로 사용하라고 말하고, 서버에서는 이 키를 이용하여 앞으로의 통신을 암호화/복호화 하게 됩니다.

이 방법은 분석을 하면서도 매우 유용하고 중요한 부분이었습니다. 이렇게 패치를 해둠으로써 앱이 서버와 LOCO 프로토콜로 통신하게되면 어떤 메세지든지 같은 aes_key로 decrypt해서 어떤 내용이 오고가는지 확인할 수 있기 때문이죠.

—-
이걸로써 HTTP API 및 LOCO API request들을 마음대로 생성할 수도 있고, 캡쳐/decrypt 할 수도 있게 되었습니다.
만약 더 많은 기능을 지원하는 클라이언트를 만들 예정이신 분들께는 유용하면서도 거의 필수 조건이라고 할 수 있겠습니다.

 

5. LOCO 프로토콜 – CWRITE 커맨드

후후. 이제 드디어 메세지를 보낼 시간입니다 :p
우린 LOCO 프로토콜이 어떤식으로 돌아가는지 훤히 알기 때문에… (맞죠? 그렇죠?) 빠르게 중요한 부분만 집고 넘어가도록 하겠습니다.

카톡 메세지를 보내는데 사용되는 커맨드는 CWRITE과 WRITE이 있습니다. 여기서 흥미로운 것은 CWRITE은 (제 생각엔) Create and WRITE이라는 점이죠. 그래서 WRITE API 같은 경우에는 메세지를 보낼 특정 채팅방 아이디를 인자로 받지만, CWRITE 같은 경우에는 채팅방 아이디 대신 유져 아이디를 받습니다. 작년에 포스팅한 글을 읽어보셨다면 카카오톡에서의 ‘채팅’은 1:1이든 그룹챗이든 모두 ‘채팅방’의 개념이라는것을 기억하실거에요. 1:1 대화라면 유져가 두명이 존재하는 채팅방일 뿐인것이죠.

그래서 내부적으로 (e.g. DB) 어떤 대화창이 열려있는지 기록하고 있다면, 그리고 그 채팅방에 메세지를 보내는것이라면 굳이 CWRITE을 할 필요 없이 WRITE을 하면 되는것이죠. 또한, 앞서 말했던 성공적 LOGIN 커맨드 후에 바로 보내지는 NCHATLIST 커맨드를 통해 현재 열려있는 대화방 리스트를 받아올 수 있습니다. 특정 사람들에게 메세지 보내는 것만 구현하려고 하므로 CWRITE을 하던 그 사람과의 대화방에 WRITE을 하던 마찬가지입니다ㅎㅎ

다음은 CWRITE 파라미터에 해당하는 JSON 입니다.

    • memberIds: 수신자 userId들을 담고 있는 list
    • msg: 보낼 메세지
    • extra: 추가 정보 (기본: None)
    • pushAlert: 푸시 알람을 켤지 끌지 (기본: True)

여기서 흥미로운것은 memberIds 파라미터입니다. 특정 아이디 하나만 리스트에 넣어 보내게 되면 1:1대화창이 생기는 효과가 있고, 여러 아이디를 리스트에 넣어 보내면 그 사람들을 포함한 그룹채팅창이 만들어져서 모두에게 메세지를 보내게 됩니다. 다들 눈치채셨겠지만, Loco Packet내의 method 값은 ‘CWRITE’ 입니다. SecureMode API이기 때문에 SecureNormalPacket에 담아서 보내야 합니다 ㅎㅎ

 

5.5. LOCO 프로토콜 – 친구추가 하기

사실 섹션 5까지만 따라해도 메세지를 임의의 유져에게 메세지를 전송할 수 있어야하지만, 서버단에서 체크하는것이 한가지 더 있습니다.

전 테스팅할때 항상 제 원래 아이디를 친구 추가해놓고 테스팅해서 몰랐다가, 마지막에 릴리즈하기 위한 POC 코드를 작업하면서 알게된 사실이 있었으니.. 메세지를 보내기 위해서는 그 상대방이 자신의 친구리스트에 추가되어 있어야한다는 점이었습니다. 그래서 급하게 친구추가 루틴을 추가했습니다 ㅋㅋ..

친구 추가 API는 POST request를 통하여 이루어 집니다.
URL은 https://fr-talk.kakao.com/wp/friends/add.json 이며, POST 데이터는 유져 고유 아이디 (64-bit integer)를 나타내는 ‘id’라는 필드 하나입니다.

아시다시피 카톡의 ‘친구 시스템’은 매우 일방적(?) 이기 때문에…
상대방이 원하든 안원하든 그냥 유져아이디만 알면 추가 가능합니다 ㅡ_ㅡ;

 

친구추가를 하기 위해서는 유져 아이디가 필요합니다. 상대방의 카카오톡 아이디 (uuid)를 알 경우에는 첫번째 시리즈에 공개했던 find_by_uuid.py를 사용하여 그 유져의 고유아이디를 알 수 있습니다. 하지만, 상대방의 핸드폰 번호는 알지만 카카오톡 아이디를 모를 시에는 어떻게 해야 유져아이디를 알아낼 수 있을까요?

고민고민하지 마시길~ㅋㅋ 카톡에서는 수시로 (또는 manual 하게) 폰의 전화번호부 리스트를 참조하여 자동 친구 추가/삭제 및 친구 추천 등의 기능을 제공합니다. 저희도 이 기능을 사용하여 핸드폰 번호만 알고있다고 해도 그 사람들에게 카톡 메세지를 보낼 수 있습니다. 개인적으로 생각하기엔 이 기능은 양날의 검이 될 수 있겠지만… 그건 카카오팀의 결정이니 우린 일단 있는 기능을 이용하도록 하겠습니다 :)

저희가 사용할 기능은 친구목록 동기화 API이고, 이 역시 POST request를 통하여 이루어집니다.
URL은 https://fr-talk.kakao.com/wp/friends/update.json 이며, POST 데이터는 다음과 같습니다.

    • contacts: 핸드폰 번호 리스트 (예: [“01012345678″,”01056781234”])
    • removed_contacts: 주소록에서 지워진 번호 리스트
    • reset_contacts: 친구 리스트를 리셋할지 안할지 (초기화시 true)
    • phone_number_type: 전화번호 타입 (NORMALIZED_CONTACT=0, NSN_CONTACT=1; 기본 NSN_CONTACT)
    • token: 동기화 토큰 (초기화시 0)
    • type: ..? (항상 ‘f’)

contacts나 removed_contacts에 들어가는 번호 포맷을 조금 신경써야하는데, 제가 테스팅할때는 폰인증할때 미국 번호로 되어있어서 그런지 국가코드를 겸비한 폰번호를 넣어야 제대로 인식하더군요. 예를들어서, “0101234578”이라는 한국 번호를 찾고 싶다면 “+821012345678″이라고 넣어야 제대로 추가됩니다 (번호를 찾아서 싱크할때마다 서버사이드의 주소록에 추가해서 현재 주소록에 있어야하는 친구들 리스트를 돌려줍니다). 어찌되었든, 다음 코드를 이용하면 번호만으로도 쉽게 유져 프로필 정보를 알 수 있습니다.

사용은 ./search_by_number.py 01012345678,01056781234  이런식으로 콤마로 나눠진 번호 리스트를 인자로 넘기면 한번에 가져옵니다.

 

6. 마치며

실제 분석시간은 총합 5시간정도 밖에 안되는것 같은데.. 블로그 글 준비 및 작성에 훨~~씬 많은 시간을 쏟아버렸네요;;
1년전쯤에 분석하고, ‘겁나빠른황소 프로젝트’ 이후로는 처음 분석한건데 하는 내내 재미있었습니다. 개발자분들에게도 직접 말씀 드린적도 있지만, 꽤 탄탄한 소프트웨어 아키텍트를 가지고 있고 덕분에 확장성 및 scalability 또한 크다고 봅니다. 해커의 입장에서는 .NET 기반 리버싱 및 프로토콜 리버싱/재구현을 해볼 수 있는 좋은 기회였고, 디벨로퍼의 입장에서는 국내 최다 유져보유 모바일 메신져 분석을 통해 디자인 패턴이나 결정들을 많이 배울 수 있는 계기가 되었습니다. 실제로 이것저것 들춰보면서 ‘나라면 어떻게 했을까?’ 라는 질문을 간간히 던지면서 솔루션을 생각해보기도 했구요.

사실, 카카오톡이 지원하는 많은 기능들 중에 아주 극소한 부분만 재구현해본 것이기 때문에 무언가 아주 대단한걸 한것은 아니지만.. 분석과정이나 방법, 그리고 이로 인해 생기는 ‘흥미’가 사실 더 중요한 결과물이라고 생각합니다. 이번 프로토콜 분석 시리즈를 통해서 카카오톡 뿐만 아니라 일반적으로 어떤식으로 네트워크/메신져류 앱 분석을 해야할지에 대한 이해에 조금이라도 도움이 되었으면 좋겠습니다. 또한, 카카오톡의 기능을 모바일 뿐만 아니라 웹이나 PC와 같은 다른 플랫폼에서도 구현해보고 싶으신 분들에게 유익한 출발점이 되었길 바랍니다.

이 시리즈를 읽으신 후에도 ‘아 도대체 니가 뭔소리를 하는지 난 모르겠다~~ 난 카톡 PC를 원한다고!! 그것뿐이야~~’ 하시는 분들은 참을성을 가지고 조금만 기다리시면 되겠습니다 ㅋㅋ 저도 최근에 안 사실이지만 (이번 시리즈를 작업하던 도중에..) 카카오측에서 공식적으로 PC 버전을 준비중이고 이르면 내년 초부터 서비스 지원을 할 것이라고 하는군요. 개인적으로는.. 사실 이번 분석시리즈를 마지막으로 카톡에 대한 분석을 마치려고 했었는데… PC버전이 나온다니.. 왠지 또 분석해봐야할것 같은 느낌이 마구마구 듭니다. 아무래도 아직 모바일보다는 재미난 attack vector들이 훨씬 많기 때문이죠!! 후후..

다만 한가지 궁금한것은, 현재 모바일번호가 ‘계정’ 역할을 하는 지금의 카카오톡 모델이 어떻게 PC 버전의 카카오톡과 쉽게 연동할 것이냐는건데.. 개인적으로는 아무래도 최근 도입한 ‘이메일 계정’을 사용해야만 PC버전 사용/연동이 가능하도록 하게 하지 않을까 싶습니다. 나와보면 알겠죠?!

여하튼, 길다면 길고 짧다면 짧은 카카오톡 LOCO 프로토콜 분석 시리즈를 읽어주시고 관심가져 주신 분들께 감사의 말씀 드립니다 :)
아무래도 소셜미디어를 사용하니 홍보효과가 좋군요!! 핫핫핫.. 하드코어분들만 어떻게 알고 찾아오시는 블로그였었는데 방문자수가 확 늘었습니다 _ _)ㅋ

다음 번에 또 재미난 소재를 들고 찾아오도록 하겠습니다~

 

7. Proof-of-Concept (POC)

여기서는 저희가 네 시리즈를 통해 알아낸 정보로, 상대방의 고유아이디인 userId를 알고 있을때 메세지를 보낼 수 있는 스크립트를 공개합니다. 한번에 한 아이디에 보내는 기능만 해두었지만 위에서 설명한대로 원하면 그룹챗 메세징도 가능합니다. userId는 역시 앞서 설명한 방법으로 가져올 수 있습니다. 이전 포스팅에서도 언급했지만 저는 스팸을 지향하지 않습니다. 이 블로그에서 제공된 코드를 사용 또는 수정하여 사용하다가 발생하는 법적 문제나 불이익에 대해서 저는 일체 책임지지 않습니다.

위의 코드에서 사용된 PKCS7Encoder 코드입니다.
원본은 여기에서 찾으실 수 있지만, 기존코드에 버그가 있어서 수정한 코드는 아래에 첨부합니다.

 

다음은 샘플 아웃풋입니다. 분석을 하다가 윈폰용 앱 개발자 중 한명으로 보이는 분의 개인정보를 발견하였습니다.. (앱 내부에서 개인정보로 초기화를 하고 시작하더군요;;;) 그래서 그분 카톡에 인사를 보내보았는데.. 아쉽게도 답장이 오지 않았네요 ㅠㅠ (지금 테스팅중이던 계정은 삭제했습니다) 알아보실 분들은 알아보시라구 그분의 ‘오늘의 한마디’ 부분은 필터하지 않도록 하겠습니다 :p  다음 버전 릴리즈에서는 앱 내부의 개인정보를 지우시기 바래요~

 

You may also like...

59 Responses

  1. dijehs says:

    안녕하세요 블로그 잘 보았습니다.
    보안쪽에서 공부를 하고 있는 학생인데,
    리버싱에도 관심이 생겨서 염치없지만 소스요청드립니다..

  2. minmoong says:

    안녕하세요! 네트워크 공부중인 학생입니다! POC 코드 가능할까요!!
    [email protected] 입니다 ㅠㅠ!

  3. 형선 says:

    안녕하세요!~
    포스팅 감명깊게 잘봤습니다. 채팅프로그램 분석을 하고 있는 대학원생입니다.
    카카오톡 PoC를 연구해보고 싶은데 가능하시다면 메일로 부탁드리겠습니다.
    [email protected]

  4. icky7080 says:

    앞선 분석글부터 잘 보았습니다.
    로코프로토콜 공부하며 조금씩 만들어보고싶은데 얼마없는 한글자료중 하나였습니다!!
    게시시기로부터 시간이 많이 지나 보실지모르겠지만.. 가능하시면 해당 프로토콜 분석에 사용되었던 소스코드들을 받아보고싶습니다.
    가능하시다면 [email protected]으로 보내주시면 감사하겠습니다.
    좋은글 감사합니다!!

Leave a Reply to 김백희 Cancel reply

Your email address will not be published. Required fields are marked *