<< 설치방법 >>
우선 Insecure Bank 를 설치하기 위해선 3가지가 필요합니다.
- Nox 앱플레이어
- Miniconda (python2 사용을 위해)
- Insecure APK (Github 에서 다운로드)
만약 Nox 앱플레이어가 설치 안되어있으시면 아래 링크로 이동하셔서 Nox를 설치하시고 와주세요! 설치를 하셨다면 남은 두개를 설치하는 과정을 아래서 설명해놓았으니 따라오시면 됩니다.
Noxplayer – Fastest and Smoothest Android Emulator for PC & Mac – Free and Safe
Play the most popular mobile games and run apps on PC with NoxPlayer, the best Android Emulator. Supports Android 9. Compatible with Windows & Mac. Much faster and more stable.
www.bignox.com
[Miniconda 설치]
저희가 설치할 Insecure Bank의 서버는 python2 버전을 기반으로 동작합니다. 하지만 저의 컴퓨터에선 python3 가 깔려있기 때문에 python2 버전을 깔면 충돌이 나기도 하고 환경변수 설정도 다시해야하고... 솔직히 여러모로 귀찮습니다. 그래서 프로젝트 별로 패키지를 달리할 수 있는 Anaconda 라는 프로그램이 있습니다. 원하는 Python 버전으로 가상환경을 구축할 수 있는 아주 편리한 프로그램이죠! 이것을 사용하면 저희 컴퓨터에 python3 가 깔려있다 해도 Python2를 사용하는 가상환경을 만들수 있는겁니다!!
하지만 Anaconda 프로그램은 용량이 너무 큽니다.. 5Gb가 훌쩍 넘어가 버리는데 그래서 좀더 라이트한 버전으로 나온것이 바로 이 Miniconda 라는 프로그램입니다. 용량이 1Gb 정도밖에 안되는데 기능은 똑같아서 C드라이브에 용량이 별로 없으신 분들은 큰 부담없이 설치하실 수 있습니다!
말이 길어졌네요! 바로 설치하러 가봅시다. 아래 링크를 클릭하셔서 본인 os에 맞는 Miniconda를 다운로드 받아주세요! 저의경우는 Window라서 사진속 표시한 부분의 파일을 받았습니다.
https://docs.anaconda.com/miniconda/
Miniconda — Anaconda documentation
These three commands quickly and quietly download the latest 64-bit Windows installer, rename it to a shorter file name, silently install, and then delete the installer: curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe -o minic
docs.anaconda.com
설치된 파일을 실행시키시면 다음과 같이 setup 창이 뜨게 됩니다.
이후 넘어가시다 보면 다음과 같은 옵션이 나오는데 Just Me 로 체크해주세요!
설치 경로창이 나오는데 괜히 에러가 날수도 있으니 기본설정된 경로로 설치해주세요 ...
여기서 Add Miniconda3 to my PATH environment variable 부분을 꼭 체크해주세요! 환경변수 설정과 관련있습니다.
이후 검색창에서 Anaconda Prompt 를 실행하세요!
아래 사진과 같이 (base) 라고 시작하는 프롬프트가 뜨면 성공입니다!
[Insecure Bank 관련파일 다운로드]
아래 링크로 들어가주세요.
https://github.com/dineshshetty/Android-InsecureBankv2
GitHub - dineshshetty/Android-InsecureBankv2: Vulnerable Android application for developers and security enthusiasts to learn ab
Vulnerable Android application for developers and security enthusiasts to learn about Android insecurities - dineshshetty/Android-InsecureBankv2
github.com
사진에 표시된 버튼을 누르셔서 Insecure Bank 에 필요한 파일들을 압축한 ZIP 파일을 다운로드 해주세요.
다운로드 받은 ZIP 파일 압축을 풀어주세요. 저는 D드라이브에 따로 Insecure Bank 라는 폴더를 만든 후 풀어줬습니다.
[Miniconda를 통한 Insecure Bank 앱서버 열기]
Anaconda Prompt 를 켜주신후 다음과 같은 명령어를 치시면 현재 Python 버전이 나오게됩니다.
(저는 Python3 버전이네요)
아래 명령어를 입력하셔서 Python 2.7 버전의 가상환경을 만들어줍니다.
>> conda create -n (가상환경 이름) python=(파이썬 버전) -y
다음과 같은 문구가 뜨면 성공입니다!
이후 아래 명령어를 치시면 Python2로 동작하는 환경이 실행됩니다.
>> conda activate testpy2
이제 Miniconda에 Insecure Bank 웹서버파일을 다운로드 시켜줄 것입니다. 설치가 필요한 파일들은 전부 requirements.txt 라는 텍스트 문서에 적혀있습니다. 이 텍스트 문서에 위치는 저희가 아까 압축을 풀었던 Insecure Bank 폴더 안에 AndroLabServer라는 폴더로 들어가 보시면 있으실 겁니다! 일단 그냥 있다는것만 확인하세요!
Insecure Bank 웹서버에 필요한 파일 | requirements.txt 파일 |
이후 Minconda에서 requirements.txt가 있는 폴더로 이동합니다.
들어오셨으면 Dir 명령어로 파일있는지 확인후 아래 명령어로 서버파일을 설치해주세요.
만약 설치중 중간에 멈춘것 같으면 Enter를 여러번 치시면 밀렸던 문구들이 나오면서 다시 진행됩니다!
>> pip install -r requirements.txt
이후 설치가 완료 되었으면 아래의 명령어를 통해 web.py 를 설치해줍니다.
>> pip install web.py==0.37
설치가 다 끝나셨으면 app.py 라는 앱서버 실행파일이 있습니다. 이 실행파일을 아래 명령어로 실행하시면 8888포트로 Insecure Bank 앱서버가 실행되는 것을 확인하실 수 있습니다!
>> python app.py
[Nox에 Insecure Bank 설치 및 앱서버연결]
Insecure Bank 폴더로 들어가서 InsecureBankv2.apk 파일을 드래그&드롭 하여 Nox에 설치해주세요.
설치 이후 들어와 보시면 다음과 같은 창이 뜹니다.
이후 cmd 창에서 ipconfig 를 사용하여 자신의 내부 IP주소를 확인해주세요.
Insecure Bank 로 돌아와서 오른쪽 상단에 설정 버튼에서 Preferences 를 클릭하여 앱서버 연결을 해줘야 합니다. Server IP 란에 방금 확인했던 아이피 주소를 적어주시면 됩니다!
설정 버튼 클릭 | 앱서버 연결 설정 |
Insecure Bank 에 기본적인 아이디 비밀번호 값은 다음과 같습니다.
아이디 비밀번호를 입력하셔서 로그인에 성공하시면 앱서버 연결까지 성공적으로 마무리 된것입니다!
Insecure Bank 계정정보
<1st>
username : dinesh
password : Dinesh@123$
<2nd>
username : jack
password : Jack@123$
아이디 입력 | 로그인 성공(앱서버 연결 성공) |
브로드 캐스트 리시버 취약점
이번 포스팅에선 Insecure Bank 앱을 통해 브로드캐스트 리시버 결함 취약점을 살펴보는 실습을 담아낼 것 입니다! 우선 브로드 캐스트 리시버란 무엇일까요? BroadCast 라는 이름에 걸맞게 앱과 앱간에 알림역할을 합니다. 서로가 지금 어떤 상황이 일어나고 있는지 알림을 통해 공유하는 것이죠!
예를들어 최근에 배달의 민족에서 신한은행 적금을 들면 5000포인트를 준다는 이벤트를 하고있습니다. 배달의 민족어플에서 이 이벤트를 클릭하면 저희 핸드폰에 설치된 신한은행 어플이 켜지게 됩니다. 배민어플이 신한어플에게 "야 너 켜져야해" 라면서 알림을 주게 되고 신한어플은 이 알림에 맞춰서 동작을 시작하게 되는것이죠! 이런식으로 다른 앱들간에 상호작용을 가능하게 만드는것이 브로드캐스트 리시버 입니다!
1. 취약점 개요
브로드 캐스트는 안드로이드 시스템의 중요한 요소 중 하나로 이벤트 발생시 브로드 캐스트 신호를 안드로이드에서 받은후 대상 어플들에게 전달을 합니다. 이 신호를 받기 위하여 앱들은 각각 브로드 캐스트 리시버라는 것이 설정되어야 하는데 , 주로 AndroidManifest.xml 파일의 <receiver></receiver> 태그로 설정되어 있습니다.
브로드 캐스트 리시버의 설정중 export 설정을 ture 로 설정하게 되면 외부 어플리케이션으로 부터 intent 를 받을수 있게 되는데 , 이 점을 통해 외부에서 악의적인 intent 를 보내 정보를 유출하거나 민감한 동작을 시도하는것이 가능해집니다.
2. Insecure Bank 앱 실습
1. 우선 Jadx로 Insecure Bank APK 를 열어봅니다.
2. AndroidManfest.xml 파일을 확인해보시면 <receiver> 태그가 사용된 곳은 두곳으로 확인이 됩니다. 위에있는것은 exported="true" 설정이 되어있고 아래에 있는것은 "false" 로 설정이 되어있네요. 그러면 위에 있는 MyBroadCastReceiver란 이름의 컴포넌트를 리시버를 통하여 외부에서 해당 <receiver> 태그안에 있는 intent action인 theBroadcast 라는 동작으로 활성화 시킬수 있을것같습니다.
이렇게 판단한 근거는 <receiver>태그안에 theBroadcast란 action을 하기위한 아무런 권한설정이 안되어있는것을 확인했기에 권한이 필요없는 action이구나 라고 생각을 하여 시도해봐야겠다고 생각이 들었습니다.
3. 일단 저희가 실행하려는 MyBroadCast 컴포넌트가 무슨동작을 하는지 살펴볼 필요가 있습니다. 보시면 intent를 통해 "phoneumber","newpass" 라는 이름의 변수를 받아주면 phonenumber에 해당하는 password 를 바꿔주는 action 인것같습니다. 변수값이 잘 전달되었을시 "For the changepassword - phonenumber......." 라는 내용이 콘솔창에 출력이 되고 , 변수값이 전달이 안되었을시 "Phone number is null" 이라는 내용이 콘솔창에 출력이 되는것 같습니다.
4. 그러면 이 내용을 토대로 adb를 활용하여 해당 컴포넌트에 대한 액션을 진행해보도록 하겠습니다. 다음과 같은 명령어를 cmd 창에서 작성합니다. 명령어 내용은 "broadcast 알림을 전달할건데 theBroadcast 라는 액션을 MyBroadCastReceiver 라는 컴포넌트를 타겟으로 실행해라" 입니다.
사실 theBroadcast라는 액션을 취하면 자동으로 MyBroadCastReceiver 컴포넌트가 동작하긴 해서 -n com.android.insecurebankv2/.MyBroadCastReceiver를 쓸 필요는 없는데 종종 써야하는 경우도 있기때문에 저는 그냥 습관적으로 라도 적어주는편입니다..
> nox_adb shell am broadcast -a theBroadcast -n com.android.insecurebankv2/.MyBroadCastReceiver
5. 이후 nox_adb logcat 명령어를 사용하여 log 를 살펴보면 Pthone number is null 이라는 메세지가 뜬것을 확인하실 수 있습니다. 이는 저희가 방금 BroadCast로 Intent는 전달했지만 , 안에 파라미터값을 아무것도 넣지않아서 이런 메세지가 뜬것입니다. 그러니 이번엔 Intent 에 위에서 언급한 두가지 파라미터를 삽입해준 후 다시 시도해보겠습니다.
6. 아래와 같은 명령어를 통해 이번엔 Intent 변수값으로 "phonenumber":5555 , "newpass":test 라는 두개의 값을 포함시켜서 action을 진행해보겠습니다.
> nox_adb shell am broadcast -a theBroadcast -n com.android.insecurebankv2/.MyBroadCastReceiver --es phonenumber 5555 --es newpass test
7. 다시 logcat 을 보시면 방금 실행한 액션이 동작하여 password 를 변경했다는 메시지와 변경한 패스워드 , 그리고 이전의 패스워드 까지 평문으로 확인하실 수 있습니다. 만약 공격자가 이 취약점을 이용하여 악의적으로 패스워드를 변경후 이전 패스워드까지 얻어낸다면 다른 사이트에 얻은정보를 시도해봄으로써 브루트포스 공격을 가할수도 있기에 매우 위험한 상황을 초래할 수 있습니다.
3. 대응방안
1. exported="false" 설정
> Intent Fitler를 사용시 기본적으로 exported="true" 설정이 되기때문에 의도적으로 false로 바꿔줄 필요가 있습니다. 그러면 apktool 도구를 사용하여 디컴파일 이후 설정을 false 로 바꿔준 후 리패키징 하여 다시 시도해보겠습니다. 우선 디컴파일 및 리패키징은 아래 링크 내용을 따라하시면 손쉽게 하실 수 있습니다!
https://ejxousiva.tistory.com/30
APK 디컴파일 & 리패키징(APK Tool)
이번에는 디컴파일 후 파일을 수정하고 리패키징까지 할 수 있는 방법을 사용해보려고 한다. 이 방법은 smali 라는 어셈블리 파일을 수정해야 하기에 조금 어려울 수 있고, 이를 통해 수정한 APK를
ejxousiva.tistory.com
1. 우선 디컴파일하여 추출한 폴더에서 AndroidManifest.xml 파일을 메모장으로 열어줍니다.
2. 내용중 저희가 바꿔야할 exported 설정을 false 로 바꿔준 후 저장합니다.
3. 이후 서명까지 완료하신 리패키징한 APK 를 NOX에 드래그 앤 드롭 하여 다운로드 합니다. 여기서 기존의 Insecure Bank 앱을 삭제하시고 진행하셔야 합니다!
4. 리패키징한 APK 파일을 Jadx 로 열어보니 무사히 exported:"false" 설정이 된 것을 확인하실 수 있습니다!
5. 이전과 마찬가지로 theBroadcast 라는 액션을 파라미터값이 포함된 Intent 와 함께 외부에서 동작시켜봅니다.
6. 이전과는 다르게 정상적으로 동작을 안하시는것을 확인하실 수 있습니다.
2. 커스텀 권한 설정
만약 exported:"true" 로 설정해야 하는 상황이라면 커스텀 권한을 설정할 수 있다. 커스텀 권한이란 개발자가 임의로 만든 권한으로써 만약 Broadcast 메시지를 수신했는데 발신지의 어플이 Mooner(개발자가 임의로 지은 이름)라는 권한이 없다면 동작을 하지않도록 만드는 것입니다. 이런 권한 설정은 앱을 설치할때 사용자에게 권한을 허용할 것인지 물어볼 수 있기때문에 사용자가 주의만 한다면 안전하게 exported:"true" 설정을 사용할 수 있게됩니다.
취약한 인증 메커니즘
*취약한 인증 메커니즘 이란?
> 정상적인 인증 절차를 우회하여 잘못된 인증으로 접근권한을 취득하는 취약점으로써, OWASP는 Mobile Top 10 2014-M5에 해당하는 취약점으로 다음과 같은 경우에 해당합니다.
- 적절하지 않은 앱 퍼미션 설정 여부
- 서비스 권한 상승 행위에 대한 통제 여부
- 기능에 대한 제한 또는 우회 금지 여부
- 불필요하거나 사용하지 않는 액티비티 제거 여부
- 인텐트 사용에 대한 안정성 여부
- 마스터 키 취약점 대응 여부
1. 취약점 탐색
> 우선 Insecure Bank 앱을 Jadx 로 디컴파일링 한 후 AndroidManifest.xml 코드를 보게 되면 표시된 부분처럼 anroid:exported = "true" 속성으로 설정되어있는 액티비티들을 볼 수 있습니다. 이게 이번시간 저희의 취약점 포인트가 되겠습니다. 아무런 권한설정없이 저런식으로 설정을 해놓게 되면 핸드폰에 악성앱이 깔렸을때 악성앱이 저 설정을 통해 이 액티비티들을 무단으로 동작시킬 수 있게됩니다.
2. 공격 결과
> nox_adb 를 통해 앱 외부에서 표시된 액티비티들을 실행해보겠습니다.
*공격에 사용된 명령어*
> nox_adb shell am start [앱이 설치된 주소]/[호출하고 싶은 패키지 주소]
1. PostLogin
2. DoTransfer
3. ChangePassword
3. 대응방안
1. android:exported="false" 설정
> 특별한 경우가 아니라면 액티비티 속성은 android:exported="false" 로 설정해야합니다.
2. 커스텀 권한 설정
> 다른 앱에서 액티비티를 호출할때 특정권한이 있어야만 해당 컴포넌트를 호출할 수 있도록 합니다.
DoTransfer 의 xml 소스코드를 예를 들어보자면 아래와 같은 식으로 커스텀 권한 설정을 해야합니다.
<activity
android:name="com.android.insecurebankv2.DoTransfer"
android:exported="true"
android:permission="com.android.insecurebankv2.PERMISSION_ACCESS_DOTRANSFER">
</activity>
로컬 암호화 이슈 취약점
[로컬 암호화 이슈 취약점 이란?]
> 안드로이드 애플리케이션은 편의성을 위해 실행 도중에 저장해야 하는 정보들이 있습니다. 만약 중요한 정보를 저장할 경우 저장한 정보가 평문이나 복호화를 통해 알아낼 수 있게끔 저장이 된다면 비밀번호나 아이디같은 정보가 노출될 뿐더러 이를 통해 다른 웹사이트에 대입해보는 브루트포스 공격으로까지 이어질 수 있게 됩니다.
취약점 진단에 들어가기에 앞서 우선 암호화 방식에 대하여 간단하게 말씀드리고 가겠습니다!
<대칭키 방식>
> 암호화와 복호화에 사용되는 키가 값으며 , 이 키값은 정보를 송,수신하는 사람들 끼리만 알고있어야 합니다. A(송신자)는 보낼 정보를 암호화키를 사용하여 암호화 시킨후 , 메세지를 전달받은 B(수신자)는 암호화키를 사용하여 메세지를 복호화 시킨후 평문을 얻어내서 읽어내는 방식입니다.
<비대칭키 방식>
> 암호화와 복호화에 사용되는 키값이 서로 다르며 , 불특정 다수에게 공개키를 알려주고 공개키로 암호화 한 정보는 비밀키를 가지고 있는 사람만이 복호화 시킬 수 있는 방식입니다. A(송신자)가 공개키로 메세지를 암호화 한 후 B에게 보내면, B는 가지고있던 비밀키로 암호화된 메세지를 복호화 시켜 평문을 얻어내는 방식입니다!
1. 취약점 진단
1. Insecure Bank 앱을 켜보면 Autofill Credentails 이라는 버튼이 있습니다. 이 기능은 이전에 로그인했던 아이디와 패스워드 정보를 자동으로 입력해주는 자동로그인 기능입니다. 자동로그인 기능은 저희가 사용하는 앱에 전반적으로 존재하는 기능이죠. 중요한 점은 로그인 정보가 자동으로 입력된다는 것은 분명 기기 어딘가 이 정보가 저장되어 있다는 말입니다.
2. 그러면 cmd 창에서 nox _adb shell 을 입력후 nox 기기에 접근을 해줍니다. 주로 자동로그인 같이 기기 내부에 저장하는 단순정보는 SharedPreferences 기능을 사용하기 때문에 해당 디렉토리로 접근 해주겠습니다.
저장 경로 : data/user/0/com.android.insecurebankv2/shared_prefs
3. mySharedPreferences.xml 과 com.android.insecurebankv2_preferences.xml 란 두개의 앱에 저장되어 있을 가능성이 유력하네요. 그러면 일단 my SharedPreferences.xml 을 cat 명령어로 어떻게 쓰여져 있나 확인해보겠습니다.
4. 역시나! Username 값과 Paswword 값이 저장되어있습니다! 하지만 얼핏봐도 암호화 되어 있구나 라는것을 바로 파악하실수 있습니다.. 그러면 어떤방식으로 암호화 되어있는지 Jadx를 통한 소스코드를 살펴보며 확인해보겠습니다!
5. Jadx 로 Insecure Bank 앱 소스코드를 디컴파일링 해주시고, 검색창에 EncrytedUsername(xml 파일에서 확인했던 ID값의 key값) 을 적어서 getSharedPreference() 함수가 사용되는 위치를 찾아주세요.
6. LoginActivity 에 해당 내용이 적혀있습니다. 읽어보시면 ID값은 base64로 인코딩 시킨것이고, Password 값은 AES(대칭형 암호화 알고리즘) 으로 암호화 시키는것 같습니다. 일단 base64는 암호화가 아닌 인코딩이기 때문에 손쉽게 ID값을 알아낼 수 있을것 같습니다... 문제는 Password 인데.. 대칭키 방식이기 때문에 key값을 모르면 복호화가 사실상 불가능합니다...
7. 보시면 복호화를 하기위해 aesDeccryptedString() 이라는 함수를 사용하고 있습니다. 해당 함수를 alt 를 누른 상태에서 마우스를 올리면 클릭할 수 있게 변합니다. 이는 해당 함수가 선언된 부분으로 바로 이동할 수 있는 편리한 기능이니 알아두시면 편하실겁니다!
8. 함수 관련 부분으로 이동해서 함수 코드를 살펴보니 복호화에 사용되는 key값의 평문이 소스코드에 저장되어 있는것을 확인하실 수 있습니다! 그리고 AES방식엔 초기화 벡터라는 것이 필요한데 그에 대한 값도 0 으로 이루어진 16 바이트 길이의 바이트열 인것을 확인하실 수 있습니다. 이처럼 키값을 앱에 작성해놓으면 리버싱을 통해 알아낼 수 있으므로 매우 위험합니다! key 값을 찾았으니 취약점 진단을 마치고 해당 내용들을 토대로 ID , Password 값을 알아내보겠습니다.
2. 공격 결과
저희가 위에서 알아낸 정보를 종합해보면 다음과 같습니다.
ID : ZGluZXNo (base64로 인코딩 된 값)
Password : DTrW2VXjSoFdg0e61fHxJg== (AES 방식으로 암호화 된 값)
KEY : This is the super secret key 123 (대칭키 방식에 사용되는 키값)
초기화 벡터 : 0 으로 통일된 16바이트 열
이 정보들을 토대로 복호화를 진행해보겠습니다.
[ID 값 복호화]
Base64 디코딩 및 인코딩 - 온라인
Base64 형식에서 디코딩해보세요. 아니면 다양한 고급 옵션으로 인코딩해보세요. 저희 사이트에는 데이터 변환하기에 사용하기 쉬운 온라인 도구가 있습니다.
www.base64decode.org
1. 인터넷 base64 디코딩 사이트에서 ID값을 복호화 하자 dinesh 라는 아이디 평문이 뜨는것을 확인하였습니다. 여기서 이라는 문자열때문에 뒤에 }t 가 덧붙여서 나오는데 이건 XML 파일에서 줄바꿈이라는 뜻이므로 무시해도 상관없습니다.
[Password값 복호화]
온라인 AES 복호화 도구
tool.hiofd.com
1. 이번엔 AES 암호를 복호화 하기위한 사이트로 접속했습니다. 근데 복호화를 하려니 암호화 mode 와 패딩 방식 , key 값 , 초기화 벡터값을 적어줘야 합니다... 이를 위해서 소스코드를 다시한번 살펴보겠습니다.
2. 소스코드에서 암호화 하는 부분을 다시 보시면 AES/CBC/PKCS5Padding 이라고 적혀있습니다. 이는 AES 방식이며 , 암호화 모드는 CBC , 패딩방식은 PKCS5 라는 뜻입니다. 그러면 방식도 알았으니 다시 복호화 페이지로 이동합니다.
3. 복호화할 문자열에 XML 파일에서 얻어냈던 패스워드를 넣어주시고 , 제가 표시해둔 부분들을 주의해서 복호화를 시켜주시면 Dinesh@123$ 이라는 비밀번호 평문을 얻어낼 수 있습니다!!
3. 대응방안
암호화 방식 자체는 AES-256 의 강한 알고리즘 암호화 방식을 사용하고 있습니다.
허나 문제점은 고유키값 평문이 소스코드에 그대로 노출되는 곳에 있습니다.
1. 고유키 값을 서버에 저장
> 지금 인시큐어 뱅크 어플은 앱 내부에 고유키를 보관하고 있습니다.고유키를 관리하는 서버를 별도로 두고 키를 주기적으로 바꿔주면서 , 키를 알고있는 인원은 최소한의 인원으로 해야합니다.
2. 소스 코드 난독화
> 현재 소스코드는 어디서 어떤 함수가 암호화를 시키는지 복호화를 시키는지 알기가 너무 쉽습니다. 따라서 소스 코드 난독화를 진행하게 된다면 공격자가 디컴파일로 소스코드를 알아내는데 어려움을 줄 수 있습니다.
액티비티 컴포넌트 취약점
[액티비티 컴포넌트 취약점 이란?]
> 안드로이드 랙티비티는 애플리케이션을 구성하는 가장 기본적인 구성단위 중 하나로, 안드로이드 애플리케이션과 사용자 간의 상호 작용에 필요한 기능과 화면을 제공합니다. 이런 액티비티 설정들은 AndroidManifest.xml 이라는 파일에 <activity> 요소로 선언이 되는데, 이 선언이 취약하게 선언이 될 경우 특정 액티비티에 권한 없이 접근하여 특정기능을 사용할 수 있습니다. 예를들면 악성앱에서 취약한 앱의 비밀번호 변경 액티비티에 접근하여, username 과 newpassword 를 적은후 타인의 비밀번호를 바꿀수 있는 경우도 액티비티 컴포넌트 취약점에 해당합니다.
- 점거해야할 취약한 부분은 다음과 같습니다.
- exported="true" 로 설정되어 있는 부분
- Intent-filter 를 사용후 exported="false" 설정을 안해둔 부분
>> 보통 <activity>를 선언하면 exported="false" 가 디폴트 값인데, Intent-filter를 사용하면 exported="true" 설정이 디폴트 값이 되므로, 필요하지 않다면 반드시 exported="false" 로 명시적으로 선언해주어야 합니다. 참고로 false로 설정할땐 동일한 애플리케이션 & 동일한 사용자 ID를 가진 애플리케이션에서만 액티비티를 호출할 수 있지만, true로 설정하게 되면 다른 애플리케이션에서 exported="true" 상태인 액티비티를 호출할 수 있게됩니다!
1. 취약점 탐색
1. 우선 Jadx로 Insecure Bank 앱을 디컴파일링 한 후, AndroidManifest.xml 파일의 코드를 살펴봅시다. exported="true" 설정을 한 액티비티들이 많이 보입니다. 저희가 타겟으로 삼을건 이 액티비티들중 ChangePassword 액티비티를 통해 사용자의 비밀번호 변경으로 까지 공격을 진행해 보겠습니다.
# AndroidManifest.xml 소스코드중 취약한 부분 발췌
<activity
android:label="@string/title_activity_post_login"
android:name="com.android.insecurebankv2.PostLogin"
android:exported="true"/>
<activity
android:label="@string/title_activity_wrong_login"
android:name="com.android.insecurebankv2.WrongLogin"/>
<activity
android:label="@string/title_activity_do_transfer"
android:name="com.android.insecurebankv2.DoTransfer"
android:exported="true"/>
<activity
android:label="@string/title_activity_view_statement"
android:name="com.android.insecurebankv2.ViewStatement"
android:exported="true"/>
<activity
android:label="@string/title_activity_change_password"
android:name="com.android.insecurebankv2.ChangePassword"
android:exported="true"/>
<provider
android:name="com.android.insecurebankv2.TrackUserContentProvider"
android:exported="true"
android:authorities="com.android.insecurebankv2.TrackUserContentProvider"/>
2.다음과 같은 명령어를 nox_adb 로 실행시키자 ChangePassword 가 외부에서 호출된 것을 확인하실 수 있습니다! 액티비티 컴포넌트 취약점이 있는 부분은 확인했으니, 이를 활용한 공격으로 까지 이어보겠습니다.
[사용된 명령어]
> nox_adb shell am start com.android.insecurebankv2/com.android.insecurebankv2.ChangePassword
2. 공격 결과
1. 일단 액티비티로 접근은 했지만 Username 값은 저희가 수정할 수 없는것으로 보입니다. Password 만 입력되는 상태라 소스코드가 어떻게 이루어지는지 한번 확인해봐야 겠습니다.
2. ChangePassword 소스코드를 보면 password 란은 (EditText)를 사용해서 저희가 수정이 가능한데 Username은 (TextView)를 사용해서 고정된 값만 적힌것 같습니다. 그리고 username은 intent에 uname이라는 키값으로 전달되는 것같으니 이를 활용해서 다시 adb 명령어를 작성해보겠습니다.
3. 이번엔 --es 설정을 추가하여 uname 이란 변수에 jack 이라는 값을 넣고 전달해주었습니다. 그랬더니 Username 부분에 해당 값이 적힌것을 확인하실 수 있습니다. 그러면 newpass란에는 Test!123$ 라고 적어주고 Change Password 버튼을 눌러보겠습니다!
[사용된 명령어]
> nox_adb shell am start -n com.android.insecurebankv2/com.android.insecurebankv2.ChangePassword --es uname jack
4. 비밀번호가 성공적으로 바뀐것을 확인하실 수 있습니다!
3. 대응 방안
1. exported="false" 설정
> 정말 필요한 경우가 아니라면 명시적으로 exported="false" 설정을 해두는 것이 보안상 안전합니다.
2. 커스텀 권한 설정
> 만약exported="true" 설정을 꼭 써야하는 상황이라면, 액티비티에 퍼미션(권한)을 추가한 후 특정 권한을 가진 겨웅에만 실행되도록 해야합니다. 다음과 같이 액티비티 설정을 수정해야합니다.
<activity
android:name="com.android.insecurebankv2.DoTransfer"
android:exported="true"
android:permission="com.android.insecurebankv2.PERMISSION_ACCESS_CUSTOM">
</activity>
루팅 탐지 및 우회
[루팅 탐지 및 우회 란?]
> 안드로이드 기반 운영체제의 경우 보안상의 이유로 루트 권한을 막아 놓았습니다. 그래서 이러한 제한을 풀거나 우회하기 위해서는 시스템 권한을 루팅으로 획득해야 합니다. 만일 루팅으로 권한을 획득할 경우 슈퍼 유저 권한으로 하드웨어 성능 조작 , 기본 애플리케이션 삭제 등 여러 기능들을 할 수 있게됩니다. 하지만 금융권 앱들의 경우 이런 루팅된 기기들에서의 앱 실행을 차단하고 있으므로 이런 차단을 우회하는 방법을 뜻합니다. 이런 루팅 탐지를 위해 앱에서 주로 체크하는 경로는 다음과 같습니다.
<금융 앱이나 게임앱에서 루팅 탐지를 위해 주로 체크하는 경로>
- /system/bin/su
- /system/xbin/su
- /system/app/superuser.apk
- /data/data/com.noshufou.android.su
1. 취약점 진단
1. 우선 Insecure Bank 앱에 로그인을 하시고 들어오시게 되면 바로 Rooted Device!! 라는 문구가 뜨게됩니다. 지금 현재 NOX기기는 루팅된 상태인데 이를 Insecure Bank 앱에서 감지했다는 뜻입니다. 그러면 어떻게 감지한 건지 앱을 디컴파일링 하여 소스코드를 살펴보겠습니다.
2. 소스코드를 보니 표시된 1번에서 doesSuperuserApkExist라는 함수로 Superuser.apk 라는 앱이 있는지 여부를 조사하고 있습니다. 또한 표시된 2번에서는 doesSUexist() 라는 함수가 보이는데 화살표를 따라가 보시면 /system/xbin/which 라는 명령어를 이용해 su 라는 명령어가 있는지 찾고있습니다. Superuser.apk 그리고 su 명령어는 기기가 루팅될 때 설치되는 부분이기 때문에 이를 탐지하고 루팅된 기기라 판단을 하는것입니다!
3. 소스코드에서 본 바와 같이 /system/xbin/which su 명령어를 입력해주니 su 명령어가 위치한 디렉토리 경로가 나왔습니다(/system/bin/su) . 이 경로는 루팅된 기기에서만 나오고 순정상태의 기기일경우 저 명령어를 입력해도 아무런 경로가 나오지 않습니다. 이점을 이용하여 루팅탐지를 하고있는 것으로 보입니다!
사용된 명령어
> nox_adb shell (녹스 기기에 명령어창으로 접근)
> /system/xbin/which su (su 라는 명령어를 가진 디렉토리 경로를 찾기)
2. 공격 과정
> [ADB를 이용한 루팅탐지 우회]
1. 우선 저희가 수정해야할 파일은 두가지 입니다./system/bin/su 와Superuser.apk 의 존재유무입니다. 우선 Superuser.apk 의 이름을 바꿔주기 위하여 해당 경로로 이동합니다.
사용한 명령어
>cd /system/app
2. 해당 경로로 이동하셔서 ls 명령어로 살펴보니 여기엔 Superuser.apk 라는 파일이 없습니다?... 그러면 지금 su 명령어의 존재여부로 해당 기기가 루팅된 기기라고 탐지되고 있는것이라고 추측해볼수 있습니다.
3. /system/bin 경로로 이동하여 su 명령어를 찾았습니다. 이제 이 명령어의 이름을 sx 로 바꿔보겠습니다.
4. 밑에 사진에서 보시는 바와 같이 mv su sx 명령어로 su -> sx 로 바꾸려 하면 읽기만 가능하다는 오류가 뜹니다. 우선 system 폴더부터는 읽기만 가능하기 때문에 system 폴더의 쓰기권한을 해당 명령어로 부여해주겠습니다.
사용된 명령어
> su (관리자 권한)
> mount -o remount,rw /system (system 폴더의 읽기쓰기 권한 부여)
5. 이후 다시 이름바꾸기를 시도하니 무사히 su -> sx 로 변경한 것을 확인하실 수 있습니다. 이제 다시 Insecure Bank앱으로 가보겠습니다.
사용된 명령어
> mv su sx
6. 왜인지 아직도 루팅탐지가 되고있습니다...
7. ls -l sx 명령어로 sx 파일을 다시 살펴보니 이 명령어는 실제로 /system/xbin/su 를 가르키고 있는 심볼릭 링크였습니다... 그러면 저 경로에있는 su 명령어 까지 sx 로 바꿔보겠습니다.
8. 이제 xbin 폴더에 있던 su 까지 이름을 바꿔주었습니다. 다시 Insecure Bank 앱으로 가서 확인을 해보겠습니다.
system/xbin/su 명령어 확인 | su -> sx 로 바꾼후 확인 |
9. 루팅 탐지가 우회된 것을 확인하실 수 있습니다!!
>[Frida CodeShare를 통한 루팅탐지 우회]
> 위에서 adb를 이용해 system폴더에 쓰기권한을 부여한 후 해당 명령어명을 바꿔서 우회하는 방법을 보여드렸습니다만...실제 어플에선 여기서 더 나아가 system 폴더에 권한을 부여했는지를 체크해서 루팅탐지를 우회하는 방법이 있는 등 여러가지 탐지 요소가 있습니다. 그래서 이런 여러가지의 탐지요소를 전부 우회할 수 있도록 Frida CodeShare 페이지에서 다른 분들이 작성한 Frida 코드를 공유해놓기 때문에 그 코드를 저희가 사용할 수 있게 해놓았습니다. 이를 사용하여 루팅탐지 우회를 좀 더 손쉽게 해보도록 하겠습니다.
1. 우선 Frida-server 를 실행시켜서 nox와 Frida 를 연동시켜줘야 합니다. 이에 대한 자세한 내용은 제가 작성한 게시물을 참고하시면 좋을 것 같습니다!
[Mobile Hacking] Nox & Frida 설치방법 (환경세팅)
안녕하세요! 이번 포스팅에서는 모바일 앱해킹에서 가장 중요하게 사용되는 Frida 설치 방법에 대하여 알려드리겠습니다. Frida 란? > 모바일 애플리케이션 및 기타 프로그램에서 사용되는 동적
jamesbexter.tistory.com
2. 저희가 사용할 Frida code는 아래 링크에 있는 코드입니다. Frida에선 친절하게도 이 codeshare 페이지에 코드를 바로 명령어창에서 사용할 수 있는 명령어를 제공합니다! 다음과 같은 명령어입니다.
> frida --codeshare [코드작성자]/[코드 이름]
https://codeshare.frida.re/@dzonerzy/fridantiroot/
Frida CodeShare
codeshare.frida.re
3. 다음과 같은 명령어를 입력하시게 되면 Frida 가 인시큐어뱅크 앱을 Frida CodeShare에 있던 코드로 후킹하기 시작합니다. 처음 시작하실땐 "처음이신가요?" 같은 문구가 뜨는데 그냥 y 를 입력하시고 진행하시면 됩니다!
4. 다시 Insecure Bank 앱으로 가서 로그인을 해보면 루팅탐지가 우회된 것을 확인하실 수 있습니다!!
> [Objection을 이용한 루팅탐지 우회]
> Frida에는 좀더 동적으로 분석이 가능할 수 있는 모듈인 Objection이 있습니다. 함수가 반환이 되는지 어떤 함수가 호출이 되는지 실시간으로 바로 보면서 수정할 수 있는 편리한 모듈입니다. 이를 통해 루팅탐지 우회를 시도해보겠습니다!
시작하기에 앞서 해당명령어를 통해 objection 모듈을 설치해줍니다!
> pip install objection
1. 우선 frida-ps -Ua 명령어를 통해 현재 Nox에서 실행되고 있는 앱을 확인합니다. 저희의 타겟은 사진에서 보이는 com.android.insecurebankv2 입니다!
2. 이후 다음 명령어를 통해 Insecure Bank 앱을 objection과 연결합니다.
* objection -g [패키지명] explore → 지정된 APP과 연결한다
> objection -g com.android.insecurebankv2 explore
3. 루팅탐지는 PostLogin 액티비티에서 이루어졌으니 PostLogin 에 존재하는 함수들을 후킹하기 위하여 해당 명령어를 통해 PostLogin에 있는 함수들 리스트를 출력합니다. 가장 윗부분에 저희가 우회해야할 doesSUexist() 함수가 보입니다!
> android hooking list class_methods com.android.insecurebankv2.PostLogin
4. 그러면 이제 doesSUexist() 함수에서 반환되는 값을 확인해봅시다. 다음의 명령어를 이용하면 해당 함수만 타겟팅으로 후킹할 수 있게되며 실시간으로 함수에서 반환되는 값을 확인할 수 있습니다. 해당 명령어를 실행 후 후킹이 완료되었으면 다시 앱으로 돌아가 로그인을 해봅시다.
* anroid hooking watch [관찰한 범위] [액티비티 명/ 액티비티 안에 함수] --dump-args --dump-return
> dump_args = 함수에 들어가는 인자값
> dump-return = 함수에서 반환되는 값
> android hooking watch class_method com.android.insecurebankv2.PostLogin.doesSUexist --dump-args --dump-return
5. 앱에서 로그인하고 다시 돌아와보면 doesSUexist 함수는 true 를 반환하고있습니다. 이를 false 로 바꿔주게 되면 루팅탐지가 우회될 것 같습니다!
앱에서 로그인 () | 반환되는 값 true |
6. 해당 명령어를 통해 doesSUexist 반환값을 false 로 바꿔준 후 다시 로그인을 시도해봅니다.
> android hooking set return_value com.android.insecurebankv2.PostLogin.doesSUexist false
7. 로그인을 하게되면 Device not Rooted!! 가 뜨는것으로 보아 루팅탐지 우회에 성공한 것을 확인할 수 있습니다. 또한 objection 창을 보시면 리턴값이 false 로 반환된 것을 확인하실 수 있습니다!
루팅탐지 우회 성공! | objection 에서 false 리턴값 확인 |
3. 대응방안
> 100%막을수 있다 라는 방법은 존재하지 않지만, 현재로써 가장 좋은방안은 상용솔루션을 사용하는 것 입니다. 그러나 개발자 입장에서 이것을 막아야 한다면 루팅탐지를 우회하는 여러 갈래 길을 전부 막아야합니다. 그물을 촘촘히 깔아둬서 최대한 우회할 수 있는 구멍을 막는것이죠! 구멍을 막는 방법으로는 대표적으로 세가지가 있습니다.
- 루팅 탐지 기능 추가
- 소스코드 난독화
- 무결성 검증
1. 루팅 탐지 기능 추가
1.1) 루팅된 환경에서만 사용할 수 있는 명령어 실행결과를 통해 루팅 탐지
- su,which 같은 명령어는 루팅환경에서만 동작할 수 있기 때문에 이 점을 통해 루팅탐지를 합니다.
private boolean checkSuExists() {
Process process = null;
try {
process = Runtime.getRuntime().exec(new String[]
{"/system /xbin/which", "su"});
BufferedReader in = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = in.readLine();
process.destroy();
return line != null;
} catch (Exception e) {
if (process != null) {
process.destroy();
}
return false;
}
}
1.2) 루팅 시 설치되는 앱의 존재 유무를 통한 루팅 탐지 방법
- 루팅 시 자동으로 설치되는 앱이 존재하는데 , 이 앱의 존재유무를 통해 루팅을 탐지합니다.
public static final String[] knownDangerousAppsPackages = {
"com.koushikdutta.rommanager",
"com.koushikdutta.rommanager.license",
"com.dimonvideo.luckypatcher",
"com.chelpus.lackypatch",
"com.ramdroid.appquarantine",
"com.ramdroid.appquarantinepro",
"com.android.vending.billing.InAppBillingService.COIN",
"com.android.vending.billing.InAppBillingService.LUCK",
"com.chelpus.luckypatcher",
"com.blackmartalpha",
"org.blackmart.market",
"com.allinone.free",
"com.repodroid.app",
"org.creeplays.hack",
"com.baseappfull.fwd",
"com.zmapp",
"com.dv.marketmod.installer",
"org.mobilism.android",
"com.android.wp.net.log",
"com.android.camera.update",
"cc.madkite.freedom",
"com.solohsu.android.edxp.manager",
"org.meowcat.edxposed.manager",
"com.xmodgame",
"com.cih.game_cih",
"com.charles.lpoqasert",
"catch_.me_.if_.you_.can_"
};
public boolean detectPotentiallyDangerousApps(String[] additionalDangerousApps) {
ArrayList<String> packages = new ArrayList<>();
// Add known dangerous apps to the list
packages.addAll(Arrays.asList(knownDangerousAppsPackages));
// Add additional dangerous apps if provided
if (additionalDangerousApps != null && additionalDangerousApps.length > 0) {
packages.addAll(Arrays.asList(additionalDangerousApps));
}
// Check if any package from the list is installed
return isAnyPackageFromListInstalled(packages);
}
1.3) Test Key 확인을 통한 루팅 탐지 방법
- 원래 Android ROM은 release-key로 빌드되는데 루팅이 되면서 개발자가 생성한 사용자 지정 키로 서명이 되면서 Test-key 값을 출력할 수 있게됩니다.
private boolean detectTestKeys() {
String buildTags = android.os.Build.TAGS;
return buildTags != null && buildTags.contains("test-keys");
}
1.4) 루팅된 기기에서만 존재하는 바이너리 파일 존재여부를 통한 루팅 탐지 방법
- 루팅 환경에서만 존재하는 바이너리 파일명 : busybox , su , magisk , superuser.apk 등...
public boolean checkForSuBinary(){
return checkForBinary("su");
}
public boolean checkForBusyBoxBinary(){
return checkForBinary("busybox");
}
public boolean checkForMagiskBinary(){
return checkForBinary("magisk");
}
public boolean checkForSuperuserApkBinary(){
return checkForBinary("Superuser.apk");
}
public static boolean checkForBinary(String filename) {
String[] pathsArray = Constants.suPaths;
boolean result = false;
for (String path : pathsArray) {
String completePath = path + filename;
File f = new File(completePath);
boolean fileExists = f.exists();
if (fileExists) {
result = true;
}
}
return result;
}
1.5) default.prop 설정값을 통한 루팅 탐지 방법
- 안드로이드 기기의 default.prop 파일의 설정값을 통해 확인하는 방법입니다.
- ro.secure = 0 이면 루팅 탐지
- ro.debuggable = 1 이면 루팅 탐지
- service.adb.root = 1 이면 루팅 탐지
private String[] propsReader() {
try {
InputStream inputstream = Runtime.getRuntime().exec("getprop").getInputStream();
if (inputstream == null) return null;
String propVal = new Scanner(inputstream).useDelimiter("\\A").next();
return propVal.split("\n");
} catch (IOException | NoSuchElementException e) {
QLog.e(e);
return null;
}
}
public boolean checkForDangerousProps() {
final Map<String, String> dangerousProps = new HashMap<>();
dangerousProps.put("ro.debuggable", "1");
dangerousProps.put("ro.secure", "0");
boolean result = false;
String[] lines = propsReader();
if (lines == null) {
return false;
}
for (String line : lines) {
for (String key : dangerousProps.keySet()) {
if (line.contains(key)) {
String badValue = dangerousProps.get(key);
badValue = "[" + badValue + "]";
if (line.contains(badValue)) {
QLog.v(key + " = " + badValue + " detected!");
result = true;
}
}
}
}
return result;
}
1.6) Google OTA 인증서를 통한 루팅 탐지
- 루팅된 기기에선 Google OTA 인증서 가 존재하지 않으므로 , 이를 통해 루팅 탐지 가능
public static boolean checkOTACerts() {
String OTAPath = "/etc/security/otacerts.zip";
File f = new File(OTAPath);
return f.exists();
}
1.7) 디렉토리의 쓰기권한을 통한 루팅 탐지 방법
- 위에 실습에서 했듯이 /system 디렉토리에 쓰기권한을 부여했는데 이를 통해 루팅탐지 가능
public static final String[] pathsThatShouldNotBeWrtiable = {
"/system",
"/system/bin",
"/system/sbin",
"/system/xbin",
"/vendor/bin",
//"/sys",
"/sbin",
"/etc",
//"/proc",
//"/dev"
}
public boolean checkForRWPaths() {
boolean result = false;
String[] lines = mountReader();
if (lines == null) {
return false;
}
int sdkVersion = android.os.Build.VERSION.SDK_INT;
for (String line : lines) {
String[] args = line.split(" ");
// Check for correct argument length based on SDK version
if ((sdkVersion <= android.os.Build.VERSION_CODES.M && args.length < 4) ||
(sdkVersion > android.os.Build.VERSION_CODES.M && args.length < 6)) {
QLog.e("Error formatting mount line: " + line);
continue;
}
// Extract mount point and options
String mountPoint;
String mountOptions;
if (sdkVersion > android.os.Build.VERSION_CODES.M) {
mountPoint = args[2];
mountOptions = args[5];
} else {
mountPoint = args[1];
mountOptions = args[3];
}
// Check if the mount point is writable
for (String pathToCheck : Const.pathsThatShouldNotBeWritable) {
if (mountPoint.equalsIgnoreCase(pathToCheck)) {
if (sdkVersion > android.os.Build.VERSION_CODES.M) {
mountOptions = mountOptions.replace("(", "").replace(")", "");
}
for (String option : mountOptions.split(",")) {
if (option.equalsIgnoreCase("rw")) {
QLog.v(pathToCheck + " path is mounted with rw permissions! " + line);
result = true;
break;
}
}
}
}
}
return result;
}
2. 소스코드 난독화
> 안드로이드 앱은 APK 파일을 디컴파일링 하여 소스소드를 직접적으로 확인할 수 있습니다. 이렇게 되면 코드의 흐름을 파악하기가 쉽고 후킹도구로 조작하기도 쉽기 때문에 루팅 탐지 우회를 위한 포인트를 알아내기 쉽습니다. 따라서 소스코드 난독화 프로그램을 이용하여 소스코드를 난독화 시켜야 공격자가 코드흐름을 따라가는데 어려움을 줄 수 있습니다.
3. 무결성 검증 기능 적용
> 만일 무결성 검증을 통해 앱이 수정되었는지 확인을 안한다면 , 앱을 리패키징 하여 아예 루팅탐지 기능이 없게 만들수 있습니다. 따라서 앱의 사인키 검증같은 무결성 검증 방식을 추가하여야 합니다.
[참고]
https://blog.naver.com/is_king/222846114250?trackingCode=rss
[InsecureBankv2] Root Detection and Bypass - objection 도구를 이용한 루팅 탐지 우회 (후킹)
Root Detection and Bypass(루팅 탐지 우회) 취약점은 어플리케이션 내에서 루팅 권한을 가진 기기의 접...
blog.naver.com
https://hagsig.tistory.com/279
[AOS 취약점 진단] 16강 - 루팅 탐지 우회 취약점 점검
안드로이드·갤럭시·AOS 애플리케이션 취약점진단/모의해킹 무료 강의 학식(hagsig) 가. 취약점 정의 - 안드로이드는 사용자가 최상위 권한(root)을 획득할 수 없도록 되어있으나, 루팅(rooting)을 통
hagsig.tistory.com
https://philosopher-chan.tistory.com/355
root detection in android device
Root Detection in Android device 의 번역본입니다. Root Access is the process of allowing users smartphones, tablets and other devices running the Android mobile operating system to attain privileged control (known as root access). 루트 어세스
philosopher-chan.tistory.com
https://stackoverflow.com/questions/44235138/root-check-for-android-device
Root check for Android device
This is an old question and asked answered by many but here I go again.. I want to check whether the device is rooted or not. I don't want my application to be installed on rooted devices. Now I went
stackoverflow.com
안전하지 않은 콘텐츠 프로바이더
['안전하지 않은 콘텐츠 프로바이더 접근' 이란?]
> 안드로이드의 모든 애플리케이션은 기본적으로 다른 애플리케이션이 자신의 데이터에 접근하는 것을 금지하고 있습니다. 그러나 콘텐츠 프로바이더를 이용하여 자신의 데이터에 다른 애플리케이션이 접근하거나 부여한 권한대로 이용하도록 할 수 있습니다. 쉽게 말해서 콘텐츠 프로바이더는 다른 애플리케이션이 자신의 데이터를 사용하기 위한 통로 역할을 제공합니다. 이 프로바이더의 설정중 exported="true" 로 되어있고 외부앱에서 접근하는 것 에대한 별다른 조치가 없다면 외부에서 이 앱의 저장소에 접근하여 민감한 정보들을 빼낼 수 있는 위험성이 존재합니다.
[콘텐츠 프로바이더라는게 뭔데?]
<Provider> 태그를 이용하는 콘텐츠 프로바이더는 액티비티,서비스,브로드캐스트리시버와 함께 앱의 4대구성요소중 하나로, 주소인 URI 와 콘텐츠 리졸버 라는것이 필요합니다.
URI : content://authority/path 형식으로 이루어져 있으며 Authority는 콘텐츠 프로바이더의 고유주소, path는 데이터 위치에 대한 정보가 담겨져있습니다. 이 주소가 A와 B 앱끼리의 데이터(DB) 공유에 필요한 주소값이 됩니다! , 즉 저 주소를 통해 직접적으로 A앱의 DB에 접근할 수 있는것이죠!
콘텐츠 리졸버 : ContentResolver 형식인 콘텐츠 리졸버를 통해 A앱의 콘텐츠 프로바이더와 연결 통로를 이어줍니다. 여기서 어떤 콘텐츠 프로바이더와 연결할 것인지 지정하는 역할이 URI 이고, 연결이 되었으면 B어플은 A의 저장소에 CRUD 기능을 행사할 수 있습니다.
1. 취약점 탐색
1. AndroidManifest.xml 파일에 접근하셔서 검색창에 provider 라고 입력합니다. 그러면 provider 태그가 검색이 되는데 보시면 exported="true" 설정이 되어있는데 커스텀 권한같은 외부접근에 대한 특별한 제한조치가 없습니다. 따라서 이 부분을 취약점 포인트로 생각해볼 수 있습니다.
2. provider의 고유주소(Authority)가 가르키고 있는 TrackUserContentProvider 라는 액티비티로 가서 소스코드를 살펴보니 content:// 형식의 URI 주소가 적혀있는 것을 확인하실 수 있습니다. URI 주소와 외부 접근 설정여부도 알았으니 이제 공격을 시도해보겠습니다.
2. 공격과정
[ADB를 이용한 DB 추출]
1.1 우선 명령어창에서 해당 명령어를 통해 콘텐츠 프로바이더에 접근할 수 있습니다.
> nox_adb shell content query --URI [URI 주소]
1.2 다음 명령어르 입력하시면 인시큐어 뱅크 내부DB 정보가 출력이 됩니다. 보니깐 로그인한 사람의 내역을 저장하고 있는것 같습니다.
> nox_adb shell content query --uri content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers
1.3 이후 --projection 옵션을 사용하여 싱글쿼터를 삽입하니 Error가 뜨면서 select 에 사용된 SQL 구문이 출력이 되었습니다. 저희가 ' 쿼터로 넣은 값이 SELECT [입력값] FROM name ORDER BY name 로 들어가나 봅니다. 그러면 이 점을 이용하여 SQL Injection 을 시도해볼 수 있습니다.
1.4 해당 명령어를 통해 SQL Injection 공격 결과 SQLite DB에 있는 테이블 명을 출력하는 것에 성공하였습니다! 이를 통해 다른 테이블의 정보도 가져올 수 있게 되었습니다.
> nox_adb shell content query --uri content://com.android.insecurebankv2.TrackUserContentProvider/trackerusers --projection \"* FROM SQLITE_MASTER WHERE type='table';--\"
3. 대응방안
1. exported="false" 설정
> 특별한 경우가 아니면 exported="false" 설정을 해두어야 합니다. 이 설정만 해도 외부에서 내부 db에 함부로 접근하는 일은 없어질 것입니다!
2. 권한 설정
> 만약 true 로 설정해야만 하는 상황이라면 권한 설정을 통해 이 provider에 접근할때 특정 권한이 필요하도록 조치하면 안전하게 이용하실 수 있습니다.
<provider
android:name="com.android.insecurebankv2.TrackUserContentProvider"
android:exported="true"
android:authorities="com.android.insecurebankv2.TrackUserContentProvider"
android:permission="com.android.insecurebankv2.ACCESS_TRACK_USER_PROVIDER" />
취약한 웹 뷰
['취약한 웹 뷰' 취약점이란?]
> 웹 뷰는 안드로이드 개발 시 주로 웹 브라우저에서 보이는 화면을 표시하거나 웹 앱 혹은 하이브리드 앱을 개발할때 사용합니다. 웹 뷰는 안드로이드 내부모듈인 웹킷 렌더링 엔진을 사용해서 자바스크립트도 지원할 수 있습니다. 문제는 이런 자바스크립트 사용을 허용하게 된다면 XSS 구문을 실행하여 악의적인 쿠키탈취 및 정보 습득이 가능하다는 취약점이 있습니다.
1. 취약점 탐색
1. 우선 AndroidManifest.xml 파일을 보시면 anroid.permission.INTERNET 이라는 권한이 설정되어 있습니다. 이 권한은 웹 뷰를 사용하기전 인터넷 사용 권한을 허락 받기 위해서 설정해놓는 것이니 웹 뷰가 어디서 쓰였는지 찾아보겠습니다.
2. 액티비티 목록을 보니 MyWebViewClient 라는 액티비티에 선언이 되어있습니다. 우선 MyWebViewClient 라는 이름의 클래스로 선언이 되고 이 클래스 안에서 lodaUrl() 함수를 통해 웹 페이지를 앱에 띄우고 있는것 같습니다. 그러면 이 클래스가 쓰인 부분이 어디인지 찾아보겠습니다.
3. MyWebViewClient 를 검색창에 치면 ViewStatement 라는 액티비티에서 사용중인것을 확인하실 수 있습니다! 그러면 ViewStatement의 소스코드를 살펴보겠습니다.
4. ViewStatement 의 소스코드를 보시면 기기 외부저장소에서 Statements_[this.uname].html 이라는 파일이 있는지 확인한 후 loadUrl() 함수를 통해서 외부저장소에서 이 html 파일을 웹 뷰로 띄워주는 것으로 확인이 됩니다.그리고 그 웹 뷰에서 자바스크립트가 사용이 가능하도록 setJavaScriptEnabled(true) 설정을 해주었습니다. 그러면 html 파일에 저희가 조작을 가할 수 있다면 웹뷰를 통한 XSS가 가능하다는 소리입니다! ... 그러면 Statements_[this.uname].html 라는 파일은 어디서 만들어지는지 한번 찾아보도록 하겠습니다.
5.검색창을 통해 확인해본 결과 DoTransfer 이라는 액티비티가 연관돼서 나온것을 확인할 수 있습니다. 그러면 DoTransfer 소스코드 내용을 확인해보겠습니다.
6. DoTransfer 소스코드중 일부입니다. 이 액티비티는 송,수신자 계좌번호와 금액을 입력하면 송금해주는 액티비티 인데, 송금내역을 Statements_[username].html 라는 파일에다가 적어주는 것으로 확인되고 있습니다. 근데 송금정보가 불확실하여 실패를 해도 그 내역을 html 파일에 기록하고 있는것으로 보입니다. 이를통해서 저희는 html 파일에 접근을 할 수 있는 취약점 포인트를 발견하였습니다.
2. 공격과정
1. Transfer 메뉴로 들어갑니다.
2. 송금페이지에서 계좌번호 입력란에 XSS 구문을 넣은후 Transfer 버튼을 눌러줍니다.
3. 송금에 실패했다는 문구가 뜨지만 html 파일에는 기록되어 있을터이니 View Statement 메뉴를 클릭하여 ViewStatement 액티비티로 이동합니다!
4. ViewStatement 액티비티에 입장하자마자 저희가 의도한 XSS 구문이 동작하는 것을 보실 수 있습니다. 이를통해 쿠키탈취 같은 악의적인 스크립트 구문을 삽입하여 공격이 가능합니다.
3. 대응방안
1. WebView 설정 중 setJavaScriptEnabled 메서드를 false로 설정
> 필요한 것이 아니라면 false 로 설정을 바꾸기만 해도 자바스크립트로 인한 공격은 막을수 있습니다.
2. 사용자 입력값 검증
> XSS 에 사용되는 구문이나 특수문자를 치환하는등 사용자의 입력값을 필터링해야 합니다.
3. HTML5 런타임 프레임워크 Crosswalk 사용
> Crosswalk는 안드로이드가 제공하는 Native API(web view)를 사용하지 않고 HTML5 앱을 개발하거나 배포할 수 있게해줍니다. web view 자체를 안사용해도 되니 이러한 취약점을 방어할 수가 있습니다.
긴 글 읽어주셔서 감사합니다!
'모바일 앱해킹(Android) > Insecure Bank' 카테고리의 다른 글
[Mobile Hacking]APK Easy Tool 설치방법 (0) | 2024.12.21 |
---|---|
[Insecure Bank] 설치방법&환경셋팅 부터 취약점 진단까지 모아보기! (Part 2) (4) | 2024.12.18 |
[Insecure Bank] 하드코딩 취약점 & 개발자 백도어 (0) | 2024.12.15 |
[Insecure Bank] 안전하지 않은 HTTP 통신 & 파라미터 조작 (0) | 2024.12.14 |
[Insecure Bank] 안전하지 않은 SD 카드 저장소 (0) | 2024.12.13 |