취약점 개요
안드로이드 앱은 내부 저장소와 외부 저장소로 분류되어 각 데이터를 저장한다.
내부 저장소는 설치된 앱마다 가지고 있는 영역으로 다른 앱이 접근하지 못하는 저장소이다.
반면 외부 저장소는 다른 말로 SD카드라고 부르며 모든 앱이 접근할 수 있는 저장소이다. 만약, 외부 저장소에 중요정보를 저장하도록 구현되어 있다면 모든 앱이 접근하여 민감한 정보가 노출될 가능성이 존재한다.
/sdcard 디렉토리로 이동하면 모든 앱이 접근 가능한 외부 저장소의 디렉토리 목록이 존재한다. 사진, 음악, 영상, 그림 등 각 역할에 맞게 디렉토리가 분류되어 있다. 하단에 나열된 디렉토리 외에도 수많은 디렉토리가 존재한다.
○ /sdcard/DCIM : 사진 파일 저장
○ /sdcard/Download : 다운로드 파일 저장
○ /sdcard/Music : 음악 파일 저장
○ /sdcard/Movies : 영상 파일 저장
○ /sdcard/Pictures : 그림 파일 저장
○ /sdcard/Podcasts : 팟캐스트 파일 저장
취약점 진단
AndroidManifest.xml 파일의 권한을 보면 안드로이드 앱을 실행할 때,
특정한 데이터를 외부 저장소에 저장하려고 WRITE_EXTERNAL_STORAGE 권한이 허용되어 있고, 저장되어 있는 데이터를 읽으려고 READ_EXTERNAL_STORAGE 권한이 허용되어 있다.
만약, 드로저(Drozer)로 설치된 앱의 권한을 확인하려면 run app.package.info –a 명령어를 입력하면 권한 목록을 확인할 수 있다.
getExternalStorageDirectory()는 안드로이드 단말기의 SD카드의 디렉토리를 구하고, 해당 디렉토리에 파일을 저장할 수 있는 메소드이다.
MYFILE 변수에 "/SD카드 경로/Statements_username.html" 문자열을 저장하고, 해당 파일에 송신자 계좌번호, 수신자 계좌번호, 이체 금액을 저장한다.
○ 237행 : Message + Success + From:송신자 계좌번호 + To:수신자 계좌번호 + Amount:이체금액 형색으로 status 객체에 저장
○ 240행 : 안드로이드 단말기의 외부 저장소에 Statements_username.html" 파일을 저장
○ 241행 : 외부 저장소에 저장된 파일을 열고 저장
안드로이드 단말기의 SD카드에 어떤 정보가 저장되고 있는지 확인하려고 /sdcard 디렉토리로 이동했다. 하지만, 소스코드 분석 시 확인한 Statements_user.html 파일이 저장되어 있지 않는다.
소스코드를 조금 더 분석해보면 알겠지만 해당 파일은 사용자가 금융 거래(계좌 이체) 기능을 이용할 때 생성되는 파일이다. 모바일 앱의 계좌 이체 기능을 이용하고 /sdcard 디렉토리 목록을 보면 해당 파일이 생성된다.
파일의 내용을 보면 소스코드 분석 시 확인한 송신자 계좌번호, 수신자 계좌번호, 이체금액이 저장되어 있다.
대응 방안 및 검증
SD카드 저장소 취약점은 금융 거래 등 중요 정보를 암호화하지 않고 외부 저장소에 저장되어 있어 취약점이 발생한다.
중요 정보 저장 시 외부 저장소가 아닌 내부 저장소에 저장한다면 중요 정보 유출을 막을 수 있다. 만약, 외부 저장소에 저장해야 한다면 반드시 암호화를 하여 저장해야 한다.
SD카드 저장소 취약점 |
중요 정보 저장 시 내부 저장소(/data/data/패키지이름/)에 저장 |
중요 정보를 외부 저장소에 저장 시 암호화하여 저장 |
중요 정보를 내부 저장소에 저장하는 방법부터 알아보자.
기존의 중요 정보를 SD카드에 저장하도록 구현되어 있는 코드를 주석처리하고, 내부 저장소(/data/data/패키지이름/files/)에 자신만 접근하도록 저장한다.
소스코드를 보면 openFileOutput() 메소드가 있는데, 해당 메소드의 인자가 MODE_PRIVATE가 아닌 경우 취약점이 발생한다.
○ openFileOutput() : 중요 정보가 저장되어 있는 파일을 내부 저장소에 저장
○ MODE_PRIVATE : 내부 저장소에 자신만 접근 가능, 다른 앱이 접근 불가능하도록 제한
○ MODE_WORLD_READABLE : 내부 저장소에 있는 데이터 읽기 가능
○ MODE_WORLD_WRITEABLE : 내부 저장소에 다른 앱이 접근 가능
openFileOutput() 메소드는 객체(바이트) 형을 사용하므로 바이트 형태로 저장하기 위해 getBytes() 메소드 사용
# DoTransfer.java
FileOutputStream output=openFileOutput("Statement_" + usernameBase64ByteString + ".html", Context.MODE_PRIVATE);
output.write(status.getBytes());
output.write("<hr>",getBytes());
소스 코드를 재빌드하여 앱의 계좌 이체 기능을 수행하면 해당 앱이 설치된 디렉토리 내의 files/Statements_jack.html 파일이 생성된다.
중요 정보를 외부 저장소에 저장하려면 암호화하여 저장해야 한다.
모바일 앱에서 아이디, 비밀번호는 내부적으로 구현되어 있는 CryptoClass를 사용하므로 동일하게 구현하면 된다. CryptoClass 객체를 생성하고, 외부 저장소에 저장할 파일을 AES로 암호화하여 저장한다.
# DoTransfer.java
FileOutputStream output = openFileOutput("Statements_" + usernameBase64ByteString + ".html", Context.MODE_PRIVATE);
CryptoClass crypt = new CryptoClass();
String crypto_status = crypt.aesEncryptedString(output);
output.write(crypto_status);
▶ 안드로이드 컴포넌트 취약점 (Android Activity)
▶ 안드로이드 컴포넌트 취약점 (Android Broadcast Receiver)
▶ 안드로이드 컴포넌트 취약점 (Android Content Provider)
▶ 안드로이드 백업 취약점 (Android Backup)
▶ 안드로이드 루팅 탐지 우회 (Frida Hooking)