[Android] 다른 앱의 EncryptSharedPreference 값 복호화하기

이 글은 읽는데 약 8분이 걸립니다.

개요

최근 어떤 앱을 뜯어보던 중 SharedPreference 파일의 값이 이상하게 나타나는 것을 찾았다.

지금까지 보던 것과는 다르게 모든 키와 값이 암호화되어있다. 이게 뭔가하고 찾아보니 EncryptSharedPreference라는 놈을 이용한 것이었다.

이를 이용해서 암호화를 하게되면 위와같이 SharedPreference 파일에 직접 접근해도 알 수 없는 값들만 잔뜩 구경할 뿐이다. 어떤 방식으로 작동하는걸까?

EncryptSharedPreference의 작동 방식

간단히 말하면 안드로이드의 키 저장소 시스템을 이용하는 것이다. 값을 암호화/복호화 하는데 쓰이는 키를 키 저장소라는 곳에 넣어두고 앱이 요청하면 값을 처리해서 넘겨주는 방식이다.

그럼 키 저장소라는 곳을 털어서 키를 빼낸 후 값을 해독하면 되겠네요!

아니다.

정확하게는 못한다. 키 저장소는 보통 하드웨어 적으로 분리되어있으며, 키 데이터 또한 그 안에 들어있고, 밖으로 나오지 않는다. 암/복호화 과정은 해당 하드웨어 안에서 이루어지며, 결과만 밖으로 나올 뿐이다. 그렇기에 키 자체를 빼내는 것은 불가능하다고 보면 된다. (더 자세한 내용은 해당 문서 참고)

그렇다면 어떻게 해야할까?

앱 변조를 통한 값 추출

Android 키 저장소 시스템 문서에는 이런 내용이 있다.

앱의 프로세스가 손상된 경우 공격자가 앱의 키를 사용할 수는 있지만 키 자료를 추출(예: Android 기기 외부에서 사용하기 위해)할 수는 없습니다.

데이터를 주고받는 과정을 건드리면 복호화 된 값을 알아낼 수 있다. 예를 들어 타겟 앱을 변조하여서 값을 가로챌 수 있다.

A앱이 생성한 EncryptSharedPreference는 오직 그 앱만 복호화를 요청할 수 있다. 다른 앱은 당연히 접근하지 못한다. 하지만 A 앱을 디컴파일 후 smali 코드 패치 등의 방법을 이용한다면? 당연히 A 앱이 요청한 것이니 데이터는 복호화되어 반환될 것이고, 삽입된 smali 코드는 그 값을 파일로 저장하거나 로그로 출력하는 등의 방법으로 유출하는 것이 가능하다.

이 방법의 장점은 루팅 여부와 관계없이 추출이 가능하며, 비교적 간단하다. 그러나 앱에 보호 솔루션이 적용되어있어 디컴파일을 막거나 여러 이유로 앱이 정상 설치 되지 않는 문제들이 발생할 수 있다.

이런 경우에는 다른 방법이 있다.

앱의 UID를 변조하여 값 추출

안드로이드는 UID와 PID를 가진다. PID는 프로세서 단위로 할당되며, UID는 패키지 단위로 할당된다. 그래서 PID는 수시로 바뀔 수 있지만, UID는 앱을 지웠다 다시 설치하지 않는 이상 바뀌지 않는다.

UID는 앱마다 다르다는 것을 이용하여 어떤 앱이 다른 앱의 데이터 디렉토리에 접근하지 못하도록 하는데에 쓰이기도한다. 실제로 /data/data 아래의 각 앱의 데이터 폴더 및 파일의 소유 권한을 확인하면 앱마다 다 다른것을 알 수 있다.

그런데 UID를 바꾸는게 가능한가요?

루팅되어 있다면 그렇다. root란 무엇인가? 시스템 전체에 접근 가능한 슈퍼 울트라 권한이 아닌가?

아래에서 UID를 변조하여 다른 앱의 EncryptSharedPreference 값을 가져오는 것을 시도해보겠다.

UID 변조하기

이 글에서는 시스템 값을 수정합니다.

루팅된 기기에서 시스템 값을 수정하는 것은 경우에 따라 기기에 손상을 초래할 수 있습니다. 아래 글이 모든 기기에서 정상 동작한다는 보장은 없으므로, 충분한 사전 지식을 습득 후, 이에 유의해서 작업하시길바랍니다.
작업간에 발생한 문제에 대해서 글쓴이는 책임지지 않습니다.

  • 갤럭시 S21 5G (SM-G991N)
  • 안드로이드 14
  • Magisk v27

먼저 타깃 앱은 암호화된 값을 알고자 하는 타인의 앱, 더미 앱은 값 추출에 사용할 목적으로 만든 자신의 앱이라고 가정하자.

이 과정의 작동 원리를 정리하자면 타깃 앱과 더미 앱의 UID를 바꿔치기 한 후, 더미 앱으로 키 저장소에 복호화를 요청한다. 그러면 시스템은 UID를 보고 더미 앱이 타깃 앱인줄 알고 데이터를 복호화 하여 더미앱에 갖다 바치는 것이다.

대충 이런 식이겠다…

더미 앱 준비

GitHub - yuria0309/GetEncryptSharedPreference
Contribute to yuria0309/GetEncryptSharedPreference development by creating an account on GitHub.

위 앱은 내가 간단히 만든 더미 앱이다. EditText에 가져올 EncryptSharedPreference 파일 이름을 입력하고 버튼을 누르면 값들을 모두 추출해서 json으로 저장한다.

직접 만들어도 좋으니 어쨌든 더미 앱이 완성이 되면 기기에 설치한다.

복호화 할 SharedPreference 파일 추출

타깃 앱에서 복호화하고 싶은 EncryptSharedPreference 파일(xml 파일)을 복사하여서 더미 앱의 데이터 폴더로 옮긴다.

/data/data/[타깃 앱의 패키지명]/shared_prefs/[파일명].xml
—- 복사 —->
/data/data/[더미 앱의 패키지명]/shared_prefs/[파일명].xml

옮기는 이유는 더미 앱에서 해당 파일을 읽어올 수 있어야하기 때문이다. 복사 후 해당 파일의 소유자가 더미 앱이 맞는지 확인 후, 아니면 chown으로 수정해준다.

packages.xml 열기

이제 UID를 교체 할 차례이다. 이 과정이 제일 중요하니 차근차근 따라오길 바란다.

/data/system/packages.xml

위 파일을 작업하기 편한 적당한 장소로 복사한다. 백업도 하나 만들어 두자.

해당 파일을 열어보면 바로 xml로 열릴 수도 있고, 바이너리 파일로 열릴 수도 있다. xml로 열렸다면 바로 수정하면 되고, 아니라면 변환 과정이 필요하다.

ABX XML 변환

후자의 경우 이런식으로 ABX로 시작하는 바이너리 파일일 것이다. 이는 Android Binary Xml파일로, 기존 XML 파일을 압축하여 더 빠른 작업을 위해 만들어졌다고한다.

이 상태로는 수정을 할 수 없으므로 일반적인 xml로 바꿔야하며, 다행이도 android 자체에 변환 툴이 내장되어있다.

C:\Users\admin\Workspace\uid>adb shell
o1s:/ $ su
o1s:/ # /system/bin/abx2xml /storage/emulated/0/packages.xml /storage/emulated/0/dec.xml

adb 쉘에서 root로 전환한 후, /system/bin 안에 있는 abx2xml을 이용하여 abx를 xml로 변환한다.
수정 후 다시 abx로 변환 할 때는 xml2abx를 이용하면 된다.

packages.xml 수정

xml 형식의 packages.xml을 얻었으면 한 번 둘러보자.

이번에 사용할 타깃 앱은 패키지가 com.example.encsp2이다. 내가 직접 만들어서 미리 EncryptSharedPreference 값을 생성해두었다.

xml을 잘 보자. 저기 userId가 보이는가? 10323이다. 기억해두자.

다음은 더미 앱이다. 패키지는 com.example.encsptest이며, userId는 10324이다.

이제 두 앱의 userId를 서로 교환할 것이다. 더미 앱의 UID를 타깃 앱의 UID로, 타깃 앱의 UID를 더미 앱의 UID로 바꿔친다.
만약 하나만 바꿔서 중복되는 userId가 생기면 이후 재부팅 과정에서 앱이 삭제되는 등 오작동이 발생하니 주의하자.

xml 수정은 이걸로 끝이다. 해당 xml을 저장한다. 만약 원래 ABX 형식이었다면 ABX로 컨버팅 해준다.

수정된 packages.xml의 사본을 만들고 이름을 packages.xml.reservecopy로 변경한다.

이 두 파일을 다시 /data/system에 덮어쓰고, 두 파일의 소유자를 chown을 이용해 system으로 설정한다.

앱 데이터 폴더 권한 변경

chown -Rhv [UID].[UID] /data/user/0/[패키지명]/

단순히 앱 UID 값만 바꿨다고 끝이 아니다. 완전히 옮기려면 두 앱의 데이터 폴더의 소유자도 서로 바꿔줘야한다. chown으로 데이터 폴더 하위의 모든 파일의 소유주를 변경한 UID로 설정한다.

재부팅

모든 과정이 완료되면 재부팅한다. 재부팅 후 앱의 UID가 변경되었을 것이다.

만약 잘못한 부분이 있다면 재부팅 후 앱이 삭제되는 등의 현상이 있을 수 있다. 이러면 백업해둔 packages.xml로 다시 복구 후 재부팅한 다음 처음부터 다시하자.

오류가 있다면 /data/system/uiderrors.txt 파일에 로그가 기록되어 있을테니 한 번 읽어보자.

값 가져오기

여기까지 다 되었다면 현재 상태는 아래와 같아야 한다.

  • 더미 앱과 타깃 앱의 UID가 서로 바뀐 상태
  • 더미 앱의 shared_prefs 폴더 안에 복호화 하고싶은 타깃 앱의 SharedPreference xml 파일이 있으며, 소유주가 더미 앱과 같을 것

이제 아까 만든 더미 앱을 열고 미리 옮겨 둔 암호화 된 EncryptSharedPreference 파일의 이름을 입력 후 버튼을 눌러 json으로 저장 후 열어보면 복호화된 값들이 나올 것이다.

댓글