티스토리 뷰
취약점 소개
안드로이드 애플리케이션에서 단말기에 주요 정보를 저장하기 위해서는
공격자에게 정보가 노출되지 않도록 평문이 아닌 암호화된 데이터로 저장해야 한다.
이러한 과정에서 발생하는 취약점을 로컬 암호화 취약점이라 한다.
암호화 과정에서 대칭키 암호화 방식은 키 배송 문제가 있어
공개키 암호화를 이용하여 수신자의 공개키로 암호화하고,
수신자의 개인키로 복호화하여 정보의 기밀성을 보장할 수 있다.
(키 배송 문제 : 대칭 키를 보내야만 복호화가 가능한데,
이 키를 어떻게 사전에 안전하게 배송할 것인가의 문제이다.)
취약점 진단 수행
Autofill Credentials 기능 확인
다음 인시큐어뱅크 앱에서의 초기 로그인 화면을 보자.
Autofill Credentials 기능을 사용하면
사용자가 입력한 아이디와 비밀번호 정보를 단말기 어딘가에 저장해두고,
다음부터는 입력하지 않아도 자동으로 채워지게 된다.
즉, 자동 로그인 기능과 유사한 것이다.
정보 저장 디렉터리 탐색
아이디와 비밀번호 정보가 어디에 저장되는지 우리는 탐색해 볼 필요가 있다.
먼저 nox_adb shell 명령어로 쉘에 진입하고
/data/data/com.android.insecurebankv2/ 디렉터리로 접근하여 어떤 데이터가 있는지 알아본다.
/data/data/는 안드로이드 애플리케이션에서 필요한 데이터를 저장해놓는 위치이다.
총 8개의 디렉터리가 확인된다.
디렉터리에 있는 여러 파일을 한 번에 확인하기 위해 PC 단말로 복사한 후에 확인해본다.nox_adb pull /data/data/insecurebankv2 .\
명령어를 사용한다. 총 53개의 파일이 복사되었다.
로컬 PC에 파일 복사
mydb 데이터베이스 파일 확인
databases 디렉터리에 mydb라는 데이터베이스 파일이 있어
SQLite로 3개의 테이블을 확인해보았으나 중요 정보는 존재하지 않는다.
shared_prefs 디렉터리 확인
다음으로 shared_prefs 디렉터리를 탐색한다.
아래와 같이 두 개의 파일이 있는데, 편집기를 통해 확인한다.
이 파일들은 초기 설정값, 자동 로그인 등의 환경 변수를 앱의 저장 공간 내에 파일 형태로 저장하는
안드로이드의 SharedPreferences 객체를 활용한 것이며, 삭제하지 않는 이상 값이 유지되는 특징이 있다.
먼저 com.android.insecurebankv2_preferences.xml 파일을 열어보니,
아래와 같이 서버의 포트와 ip가 노출되고 있다.
다음으로는 mySharedPreferences.xml 파일을 열어보니,
아래 그림처럼 사용자 아이디와 비밀번호로 추정되는 정보가 저장되어 있다.
EncryptedUsername은 암호화된 사용자 아이디, superSecurePassword는 중요 패스워드로 추정된다.
그렇다면 위 xml 파일들은 언제 값이 저장되는 것인지 인시큐어뱅크 앱의 코드를 확인한다.
사용자 아이디와 비밀번호를 저장하는 것은 saveCreds(...) 함수이다.
private void saveCreds(String username, String password) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
SharedPreferences mySharedPreferences;
mySharedPreferences = getSharedPreferences(MYPREFS, Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
rememberme_username = username;
rememberme_password = password;
String base64Username = new String(Base64.encodeToString(rememberme_username.getBytes(), 4));
CryptoClass crypt = new CryptoClass();;
superSecurePassword = crypt.aesEncryptedString(rememberme_password);
editor.putString("EncryptedUsername", base64Username);
editor.putString("superSecurePassword", superSecurePassword);
editor.commit();
}
위 코드를 보면, SharedPreferences 객체를 생성하고, getSharedPreferences 함수로 객체를 받아온다.
xml 파일은 MYPREFS라는 문자열 변수로 선언되어 있던 "mySharedPreferences.xml"의 이름으로 저장된다.
public class DoLogin extends Activity {
...
public static final String MYPREFS = "mySharedPreferences";
...
}
이후, 사용자의 아이디를 Base64 방식으로 인코딩하고
비밀번호를 CryptoClass() 함수를 통해 AES 방식으로 암호화하여
SharedPreferences 객체의 값들을 편집기를 통해 수정한다.
(편집기를 통해야만 값이 수정될 수 있다.)
그렇다면 사용자 아이디를 Base64로 디코딩할 수 있는지 https://www.base64decode.org/에서 테스트해보면,
아래와 같이 jack 이라는 사용자 아이디를 확인할 수 있다.
이제 사용자 아이디를 확인했으니, 비밀번호까지 탈취할 수 있는지 알아본다.
CryptoClass() 함수로 암호화를 시도했으니, CryptoClass() 클래스로 이동한다.
암호화 코드 확인
String key = "This is the super secret key 123";
...
public String aesEncryptedString(String theString) throws UnsupportedEncodingException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, IllegalBlockSizeException, BadPaddingException {
byte[] keyBytes = key.getBytes("UTF-8");
plainText = theString;
cipherData = CryptoClass.aes256encrypt(ivBytes, keyBytes, plainText.getBytes("UTF-8"));
cipherText = Base64.encodeToString(cipherData, Base64.DEFAULT);
return cipherText;
}
위 코드는 다음과 같은 논리로 작성되었다.
AES 대칭키 암호화를 위해서는 키가 필요한데,
그 키를 "This is the super secret key 123"으로 설정하였다.
또한, 사용자가 입력한 비밀번호를 평문(theString: plainText)으로 받아와서,
aes256encrypt 함수를 이용하여 AES로 암호화하였고,
이를 Base64로 다시 인코딩하여 암호화된 비밀번호를 리턴하였다.
이 리턴한 값을 mySharedPreferences.xml 파일에 저장하며,
값은 "v/sJpihDCo2ckDmLW5Uwiw=="이고,
이는 앞서 언급한 "This is the super secret key 123"이라는 키로 AES 암호화된 값이다.
다음 그림을 보자.
위 그림은 https://www.devglan.com/online-tools/aes-encryption-decryption 에서
AES 암호화 값을 해독 시도한 결과이다.
AES 암호화 값을 Base64 출력 값으로 만든 뒤, 평문으로 해독한 결과는 Jack@123$으로,
jack 계정의 비밀번호인 것이다.
이렇듯, 아이디는 Base64로 쉽게 디코딩이 가능하여 알아낼 수 있었고,
대칭키 암호화 방식을 채택한 비밀번호를 암호화한 키가 노출되면서 계정 정보의 습득이 가능하였다.
만약 이중적인 보안 인증 방식이 존재하지 않는다면
본 계정은 이러한 공격에 의해 쉽게 무력화되는 것이다.
대응 방안
아이디가 Base64인코딩을 통해서만 저장되었던 점이 문제이다.
그러므로 비밀번호와 같이 AES 암호화를 적용해야 하며,
대칭키가 있으면 복호화가 가능하므로 키 관리를 따로 해야한다.
먼저 DoLogin 파일을 수정한다.
DoLogin.java 수정
private void saveCreds(String username, String password) throws ... {
SharedPreferences mySharedPreferences;
mySharedPreferences = getSharedPreferences(MYPREFS, Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
rememberme_username = username;
rememberme_password = password;
String base64Username = new String(Base64.encodeToString(rememberme_username.getBytes(), 4));
CryptoClass crypt = new CryptoClass();;
superSecurePassword = crypt.aesEncryptedString(rememberme_password);
editor.putString("EncryptedUsername", base64Username);
editor.putString("superSecurePassword", superSecurePassword);
editor.commit();
}
private void saveCreds(String username, String password) throws ... {
SharedPreferences mySharedPreferences;
mySharedPreferences = getSharedPreferences(MYPREFS, Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = mySharedPreferences.edit();
rememberme_username = username;
rememberme_password = password;
CryptoClass crypt = new CryptoClass();
String encryptedUsername = crypt.aesEncryptedString(rememberme_username);
superSecurePassword = crypt.aesEncryptedString(rememberme_password);
editor.putString("EncryptedUsername", encryptedUsername);
editor.putString("superSecurePassword", superSecurePassword);
editor.commit();
}
위처럼, 로그인 시 사용자 아이디도 비밀번호와 같이
AES 암호화를 수행하여 xml파일에 저장할 수 있도록 하였다.
이제, 자동 로그인 기능에서도 Base64 디코딩을 통한 데이터 채움이 아닌
AES 해독을 통해 데이터를 채울 수 있도록 LoginActivity.java 파일의 코드를 수정한다.
LoginActivity.java 수정
Username_Text = (EditText) findViewById(R.id.loginscreen_username);
Password_Text = (EditText) findViewById(R.id.loginscreen_password);
Username_Text.setText(usernameBase64ByteString);
CryptoClass crypt = new CryptoClass();
String decryptedPassword = crypt.aesDeccryptedString(password);
Password_Text.setText(decryptedPassword);
Username_Text = (EditText) findViewById(R.id.loginscreen_username);
Password_Text = (EditText) findViewById(R.id.loginscreen_password);
Username_Text.setText(usernameBase64ByteString);
CryptoClass crypt = new CryptoClass();
String decryptedId = crypt.aesDeccryptedString(username);
String decryptedPassword = crypt.aesDeccryptedString(password);
Username_Text.setText(decryptedId);
Password_Text.setText(decryptedPassword);
수정이 끝났다. 다음 그림으로 결과를 확인하자.
취약점 진단 수행과 같은 과정을 거친다.
결과 확인
mySharedPreferences.xml 파일을 다시 확인해보면, 이전과 다른 사용자 아이디 값이 보인다.
Base64 디코딩을 시도해본다.
Base64 디코딩을 시도했지만 해독되지 않았다.
코드를 수정한 것처럼 AES 암호화가 정상적으로 수행되었음을 확인할 수 있다.
결론❗은 다음과 같다.
결론
중요 정보를 로컬(단말기)에 저장할 때는 평문으로 저장해서는 안되며,
안전한 암호화 알고리즘을 채택해야 하며,
키의 길이 등을 충분히 안전한 권장되는 길이로 설정해야 한다.
'WEB&APP 진단 > Android' 카테고리의 다른 글
3.4 안드로이드 인시큐어뱅크 앱 취약점 - 중요 정보 하드코딩 취약점 (0) | 2022.09.15 |
---|---|
3.3 안드로이드 인시큐어뱅크 앱 취약점 - 백업 취약점 (0) | 2022.09.05 |
3.1 안드로이드 인시큐어뱅크 앱 취약점 - 브로드캐스트 리시버 결함 (0) | 2022.08.15 |
2.2 취약점 진단 및 분석 도구 실습 - ADB(Android Debug Bridge) 기능 활용 (0) | 2022.04.08 |
2.1 취약점 진단 및 분석 도구 실습 - ADB(Android Debug Bridge) 개요와 기본 사용법 (0) | 2022.04.06 |
- Thanks for comming.
- 오늘은
- 명이 방문했어요
- 어제는
- 명이 방문했어요