본문 바로가기
프로그래밍 놀이터/안드로이드, Java

#3 취약점 항목별 상세 실습 part 1. - 안드로이드 모바일 앱 모의해킹

by 돼지왕 왕돼지 2020. 11. 20.
반응형


취약점 항목별 상세 실습

3.1. 브로드캐스트 리시버 결함


3.1.1. 취약점 소개


-

악의적인 목적을 갖고 수행하는 경우에는 사용자가 받는 알림을 중간에서 가로채는 행위를 할 수 있으며, 특정한 상황에서만 발생하는 작업을 우회하여 수행하도록 조작할 수 있다.




3.1.2. 취약점 진단 과정


* ADB를 이용한 브로드캐스트 생성


-

> am broadcast -a actionName -n pkgName/className —es stringExtraKey stringValue



* 드로저를 이용한 브로드캐스트 생성


-

dz> run app.package.attacksurface pkgName

// exported 된 component 들이 결과값으로 나온다.



-

dz> run app.broadcast.info -a pkgName

// pkgName 에 설정된 broadcast 정보가 나온다.



-

dz> run app.broadcast.send —component pkgName fullQualifiedClassName —extra string key value




3.1.3. 취약점 대응 방안


-

첫번째는 exported = false 로 바꾸는 것.

외부 앱에서 발생하는 인텐트에 영향을 받지 않게 된다.

intent-filter 에 값이 포함되어 있는 경우에는 기본값이 true 로 설정된다.


exported = false 가 되면, 다음과 같이 권한이 없다는 log 를 볼 수 있다.

w/BroadcastQueue(373) : Permission Deinal : broadcasting Intent(flg=0x10 cmp=com.android.insecurebankv2/.MyBroadCastReceiver (has extras) ) from com.mwr.dz (pid=1324, uid=10052) is not exported from uid 10051 due to receiver com.android.insecurebankv2/.MyBroadcastReceiver)



-

두번째는 permission 을 부여하는 것이다.





3.2. 취약한 인증 메커니즘


3.2.1. 취약점 소개


-

취약한 인증 메커니즘(Weak Authorization Mechanism)은 정상적인 인증 절차를 우회하여 잘못된(비정상적인) 인증으로 접근 권한을 취득하는 취약점을 말한다.

Mobile Top 10 2014-M5 에 해당하는 취약점으로, 일반적으로 다음과 같은 경우에 해당한다.


    적절하지 않은 앱 퍼미션 설정 여부

    서비스 권한 상승 행위에 대한 통제 여부

    기능에 대한 제한 또는 우회 금지 여부

    불필요하거나 사용하지 않는 액티비티 제거 여부

    인텐트 사용에 대한 안정성 여부

    마스터 키 취약점 대응 여부




3.2.2. 취약점 진단 과정


-

activity 가 exported=true 로 되어 있다면 인증 없이 접근할 수 있다.



-

> adb shell am start pkgName/fullyQualifiedClassName



-

dz> run app.activity.info -a pkgName

// pkgName 에 해당하는 activity 정보들이 나온다.


dz> run app.activity.start —component pkgName/fulliQualifiedClassName




3.2.3. 취약점 대응 방안


-

첫번쨰는 exported=false 로 설정하는 방법이다.

exported=false 라면 같은 user id 를 가진 앱만 해당 컴포넌트를 불러올 수 있다.





3.3. 로컬 암호화 이슈


3.3.1. 취약점 소개


-

정보를 평문으로 저장하면 정보가 공격자에게 누출될 수 있다.



-

대칭키 알고리즘은 하나의 키로 암호화, 복호화하는 방식이다.

이 알고리즘에서의 문제점은 키를 전달하는 방법과 관리 방법이다.

하나의 키만을 사용하기 때문에 키가 노출될 경우 모든 암호문을 복호화할 수 있다는 문제점이 있고, 암호문을 전달받는 사람이 키를 소유하고 있지 않으면 애용을 확인할 수 없기에 키를 안전하게 전송할 수 있는 방법이 필요하다.



-

공개키 알고리즘은 공개키와 개인키가 있어야 한다.

공개키는 외부에 공개하는 키이고, 이를 이용해 암호화하면 개인키로밖에 복호화하지 못한다.




3.3.2. 취약점 진단 과정


-

// 대칭키 방식으로 암호화
// AES 256bit 암호화는 아주 강력한 암호화 방법
fun aes256Encrypt(ivBytes:ByteArray, keyBytes:ByteArray, textBytes:ByteArray){
    val ivSpec = IvParameterSpec(ivBytes)
    val newKey = SecretKeySpec(keyBytes, “AES”)

    val cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”)
    cipher.init(Cipher.ENCRYPT_MODE, newKey, ivSpec)
    return cipher.doFinal(textBytes)
}




3.3.3. 취약점 대응 방안


-

대칭키를 사용해 암호화하는 알고리즘에서 고유키가 제대로 보호되지 못하면 아무리 강력한 암호화 방법을 사용하더라도 데이터를 안전하게 보관하지 못한다.


키를 관리하는 서버를 별도로 두고, 키는 주기적으로 바꿔주어야 한다.

만약 키를 주기적으로 변경하더라도 최소한의 인원만 키에 대한 정보를 알고 있어야 한다.

이외에도 키를 암호화하여 파일 시스템에서 관리하는 방법도 있다.

그러나 이는 공격자가 프로세스가 동작하는 도중, 메모리 덤프 등을 통해 키를 추출할 수도 있다.





3.4. 액티비티 컴포넌트 취약점


3.4.1. 취약점 소개




3.4.2. 취약점 진단 과정


* ADB 를 이용하여 액티비티의 취약점 확인


-

> adb shell am start -n pkgName/className



* 드로저를 이용한 액티비티 취약점 확인


-

dz> run app.activity.info -a pkgName

// activity 정보가 나온다.


dz> run app.activity.start —component pkgName/fullyQualifiedClassName




3.4.3. 취약점 대응 방안


-

액티비티가 노출되어 있고 소스 코드 분석도 가능하다면 아주 위험할 수 있다.

그래서 우선 exported=false 로 만들어주어야 한다.

그럼 외부에서 launch 하고자 하면 Permission Denial 이 뜬다.



-

액티비티에 permission 을 추가하는 방법도 있다.





3.5. 루팅 탐지 및 우회


3.5.1. 취약점 소개


-

리눅스 커널 2.6을 기반으로 제작된 안드로이드는 리눅스에서 발생하는 취약점들을 내포하고 있다.

루트 노출 및 우회 취약점은 안드로이드 디바이스 시스템 권한을 얻는 것을 말한다.

리눅스 기반 운영체제의 경우, 시스템의 루트 권한 취득이 가능하지만, 안드로이드의 경우 보안상의 이유로 루트 권한을 막아 놓았다.

이 제한을 풀거나 우회하기 위해서는 시스템 권한을 루팅으로 획득해야 한다.



-

기기를 루팅하면 슈퍼 유저의 권한으로 하드웨어 성능 조작, 제조사 및 통신사 기본 앱 삭제, 시스템 권한을 이용한 다양한 디바이스 조작 등이 가능해진다.

루팅된 기기는 시스템 권한을 획득하게 되어 디바이스 내부의 민감한 정보에 접근할 수 있다.

그래서 금융권 앱은 기본적으로 루팅된 기기에서의 앱 실행을 차단한다.

또한 최근 핀테크 기술이 이슈가 되면서 이 기술을 제공하는 기업에서 공식적으로 루팅된 기기는 보안 위협으로 감지하고 기술지원을 하지 않는다는 입장을 밝혔다.




3.5.2. 취약점 진단 과정


-

루팅 체크는 주로 경로 체크를 주로 한다.

/system/bin/su

/system/xbin/su

/system/app/superuser.apk

/data/data/com.noshufou.android.su



-

루팅우회는 위의 파일들 중 일부를 rename 하는 것으로 한다.

/system 디렉터리에 존재하는 파일을 수정하려면 read-only 상태로 마운트된 것을 rw 로 mount 시켜야 한다.

$ su

# mount -o remount,rw /system

권한을 획득한 후 Superuser.apk 를 Superuser.apk1 등으로 이름을 바꾸어 실행되지 않도록 한다.




3.5.3. 취약점 대응 방안


-

루팅된 디바이스로는 접근할 수 없다는 경고 메시지를 띄우고 즉시 앱을 종료한다.





3.6. 안전하지 않은 콘텐츠 프로바이더 접근


3.6.1. 취약점 소개




3.6.2. 취약점 진단 과정


* ADB 를 이용한 콘텐츠 프로바이더 취약점 확인


-

> adb shell content query —uri content://authority…



* 드로저를 이용한 콘텐츠 프로바이더 취약점 확인


-

dz> run app.package.attacksurface pkgName

// 해당 pkg 의 exported true 인 녀석들이 출력된다.


dz> run app.provider.info -a pkgName

// 해당 pkg 의 provider 정보가 나온다.


dz> run scanner.provider.findurls -a pkgName

// 해당 pkgName 에서 제공하는 url 들이 나온다.


dz> run app.provider.query content://authority… —projection “columns"




3.6.3. 취약점 대응 방안


-

첫째로 exported 를 false 로 한다.

그럼 Permission Denial 로 접근이 안 된다.




3.7. 안전하지 않은 웹 뷰 실행


3.7.1. 취약점 소개




3.7.2. 취약점 진단 과정


-

웹에서의 드라이브 바이 다운로드(Drive-By-Download) 공격과 URL 접속만을 통해 안드로이드 디바이스의 엑세스 권한을 취득할 수 있다.

드라이브 바이 다운로드는 웹 브라우저 및 범용 앱의 취약점을 이용하여 사용자가 사이트에 접근하기만 해도 악성코드가 감염되는 형태를 말한다.




3.7.3. 취약점 대응 방안


-

웹 뷰 취약점의 경우 안드로이드 4.3 젤리빈 이하 환경에서의 보안 패치를 중단했다.

따라서 안전한 웹 뷰 구현을 위해서는 안드로이드 4.4 킷켓(Kitkat) 이상의 버전을 사용해야 하며, 웹뷰에서 자바스크립트를 사용할 때에는 기존의  addJavascriptInterface 로 호출하여 사용되는 방식이 android.webkit.JavascriptInterface 를 호출하도록 변경되었다.



-

메서드가 호출되었을 때 WebView 의 주소가 올바른 URL 인지 검증하는 로직이 필요하다.



-

웹 뷰를 구현해야 할 때 웹 뷰 대신 오픈소스 HTML5 런타임 프레임워크인 Crosswalk 를 사용하는 것도 한가지 방법이다.

Crosswalk 는 안드로이드가 제공하는 Native API(WebView)를 사용하지 않고 HTML5 앱을 개발하거나 배포할 수 있게 해준다.





3.8. 취약점 암호화 실행


3.8.1. 취약점 소개


-

Weak Cryptography implementation 은 암호 알고리즘에 대한 취약점이다.

개발 단계에서 자주 발생하는 취약점 중 하나는 인코딩을 통해 계정 비밀번호와 같은 민감한 데이터를 감추어 보내는 것이다.

Base64 와 같은 인코딩은 암호 알고리즘이 아니다.

키값을 갖고 있지 않기 때문에 공격자가 얼마든지 인코딩 함수로 똑같은 데이터를 만들어 변조할 수 있다.



-

암호 알고리즘을 사용할 때는 표준화된 알고리즘을 사용하고, 알고리즘에 결함이 발생한 알고리즘은 사용하지 않는다.

오래된 암호 알고리즘은 무차별 대입 공격과 같은 전수 공격에 취약하기 때문에 키값이 없더라도 해독될 수 있다.

해시 알고리즘 역시 마찬가지인데, 결함이 발생한 해시 알고리즘의 경우에도 개발 시 사용하지 않는 것이 좋다.


취약하다고 알려진 알고리즘은 RC2, RC4, RC5, MD4, MD5, SHA1, DES, 3DES 등이다.




3.8.2. 취약점 진단 과정


-

상수형태로 암호키를 사용할 경우 암호키가 노출된 위험성이 상당히 커진다.

디컴파일을 통해 키값을 노출하고 싶지 않을 때는 해시 함수를 사용하여 키값을 생성하는 것이 안전하며, 키값을 외부 디렉터리에서 불러오는 것도 한 가지 방법이다.



-

초기화 벡터는 프로그램이 실행된 후에 생성하는 것이 안전하다.

초기화 벡터는 운영 방식마다 사용 방법이 다르며, 요구하는 성질도 조금씩 다르지만, 같은 초기화 벡터를 반복적으로 사용하면 안 된다는 공통점을 가진다.

초기화 벡터값이 같은 경우, 비슷한 2개의 평문을 암호화했을 때 앞부분의 블록들이 서로 같아지는 문제점이 있다.


초기화 벡터는 사용되기 직전에 계산하여 사용하고, 사용한 직후에는 0값을 채워 넣는 방식으로 지우는 것이 안전하다.

초기화 벡터값은 블록 암호 알고리즘에서 맨 처음 단 한 번만 사용되므로, 첫 번째 블록을 암호화한 후에 바이트를 0으로 초기화하여 공격자가 초기화 벡터값을 유추할 수 없게 해야 한다.




3.8.3. 취약점 대응 방안


-

대칭키 암호 알고리즘을 사용할 때 주의점은 다음과 같다.

    암호화 모드와 패딩을 명시적으로 지정한다.

    강한 암호화 기술(즉, 안전하다고 알려진 암호 알고리즘)을 사용하며, 암호화 모드와 패딩을 포함한다.

    암호키의 값은 솔트를 사용한다.

    암호키의 값은 적절한 해시 반복 횟수를 지정한다.

    암호화 정도를 보장하기 위해 충분한 키의 길이를 사용한다.



-

안전하게 키를 generate 하는 예시는 아래와 같다.

class AesCryptoPEBKey{
    private const val TRANSFORMATION - “AES/CBC/PKCS7Padding”
    private const val KEY_GENERATOR_MODE = “PBEWITHSHA256AND128BITAES-CBC-BC”
    private const val SAL_LENGTH_BYTES = 20
    private const val KEY_GEN_ITERATION_COUNT = 1024
    private const val KEy_LENGTH_BITS = 128

    private var mIv:ByteArray? = null
    private var mSalt:ByteArray? = null

    constructor(iv:ByteArray, salt:ByteArray){
        mIv = iv
        mSalt = salt
    }

    constructor(){
        initSalt()
    }

    init{
        initSalt()
    }

    private fun initSalt(){
        mSalt = ByteArray(SALT_LENGTH_BYTES)
        val secureRandom = SecureRandom()
        secureRandom.nextBytes(mSalt) 
    }

    fun encrpyt(plain:ByteArray, password:CharArray):ByteArray?{
        try{
            val cipher = Cipher.getInstance(TRANSFORMATION)
            val secretKey = generateKey(password, mSalt)
            cipher.init(Cipher.ENCRYPT_MODE, secretKey)
            mIV = cipher.getIV()
            return cipher.diFinal(plain)
        }catch(e:Throwable){
            e.printStackTrace()
            return null
        }
    }

    fun decrpyt(encrypted:ByteArray, password:CharArray):ByteArray?{
        try{
            val cipher = Cipher.getInstance(TRANSFORMATION)
            val secretKey = generateKey(password, mSalt)
            val ivParameterSpec = IvParameterSpec(mIV)
            cipher.init(Cipher.DECRPYT_MODE, secretKey, ivParameterSpec)
            return cipherdoFinal(encrypted)
        }catch(e:Throwable){
            e.printStackTrace()
            return null
        }
    }

    private fun generateKey(password:CharAarray, salt:ByteArray):SecretKey{
        try{
            val secretKeyFactory = SecretKeyFactory.getInstance(KEY_GENERATE_MODE)
            val keySpec = PBEKeySpec(password, salt, KEY_GEN_ITERATION_COUNT, KEY_LENGTH_BITS)
            return secretKeyFactory.generateSecret(keySpec)
        }catch( e: throwable){
            e.printStackTrace()
            return null
        }
    }
}




3.9. 앱 패칭


3.9.1. 취약점 소개




3.9.2. 취약점 진단 과정


-

디컴파일 후 small 코드를 수정하고, 다시 컴파일한 후에 사이닝 과정을 거치면 정상적으로 설치된다.

재빌드는 apktool 도구를 이용해 b 옵션으로 할 수 있다.




3.9.3. 취약점 대응 방안


-

이 문제에 대응하기 위해서 NDK 를 사용한다.

하지만 NDk 를 사용해도 소스 코드 난독화가 되어 있지 않으면, 프로세스를 분석하여 메모리에서 중요한 제어를 조작할 수 있다.

즉, proguard 를 적용해야 한다.


gradle 에서 minifyEnabled 를 true 로 주어 난독화 시킬 수 있다.



-

프로가드는 오픈소스이기 때문에 상용 솔루션에서의 일부 기능에 대한이 있다.

Sash OPro, Allatori, DexGuard 등과 같이 많이 알려진 상용 난독화 도구보다는 부족하지만 오픈소스이기 때문에 무료로 사용할 수 있다는 장점이 있다.



-

프로가드의 상용 버전인 덱스가드는 String 암호화, Class 암호화, Asset 파일 암호화, 중요 API 숨김, 해킹 시도 기능, 안드로이드 로깅 코드 삭제 기능 등이 포함되어 있다.



-

난독화를 했다고 해서 모두 해결되는 것은 아니다,

무료 버전부터 상용 버전에 이르기까지 몇몇 분석 도구들은 난독화 해제 기능을 갖고 있다.

그렇기 때문에 이를 방어하기 위해 디컴파일 방지를 위한 바이너리 난독화 솔루션이 있고, 조작이 되더라도 사용할 수 없도록 하는 파일 무결성 검증 솔루션도 있다.




3.10. 메모리 내 민감한 정보 저장


3.10.1. 취약점 소개




3.10.2. 취약점 진단 과정


-

안드로이드에서는 JVM 이 아닌 Dalvik 가상 머신을 사용하여 앱을 구현한다.

프로세스가 실행될 때마다 달빅 가상 머신을 사용하여 동작에 필요한 만큼 메모리를 할당하고(Dalvik heap footprint), 더 사용해야 한다면 프로세스당 허용된 메모리 한계 안에서 필요한 만큼의 메모리를 추가 할당한다.

만약 허용된 메모리의 한계 이상을 사용하면 OOM (Out of memory) 에러가 발생한다.



-

메모리 안에 있는 민감한 정보를 얻기 위해서는 “메모리 포렌식”이라는 기법을 사용한다.

메모리 포렌식은 물리적인 메모리인 랩에 기록되어 있는 시스템 이용 정보나 악성코드 감염과 관련된 다양한 흔적을 분석하는 기법이다.

기본적으로 시스템 연산은 CPU 에 의해 이루어지고, 이를 실행하기 위해서는 메모리에 데이터와 코드가 적재되어야 한다.

이러한 과정을 통해 물리적인 메모리에는 하드 디스크와 다른 소프트웨어 또는 파일이 실행되는 과정이나 실행되었던 특유의 정보가 존재한다.



-

메모리 포렌식을 통해 획득 가능한 정보는...

프로세스, 스레드 정보

    프로그램이나 파일이 실행중이거나 이미 종료되었지만 메모리에 남아 있는 정보 추출

모듈, 라이브러리 정보

    프로그램이나 파일이 실행 중이거나 이미 종료된 프로세스 관련 모듈 라이브러리 정보 추출

실행된 파일과 소켓 정보

    실행 중이거나 이미 종료된 파일에 대한 정보와 네트워크 연결을 위해 사용되었거나 사용 중인 소켓 정보 추출

다양한 데이터 구조 정보

    메모리에만 존재하는 운영체제, 소프트웨어 및 파일과 관련된 다양한 데이터의 구조 정보 추출



-

메모리 포렌식을 통해 파일 콘텐츠나 사용자 패스워드, 임시 저장 데이터 등의 메모리에 저장되는 특정 정보를 찾아낼 수 있다.또한 저장되지 않고 메모리에 로드되어 실행되는 악성 프로그램에 대한 분석도 가능하다.



** ADB 를 이용한 진단


-

ADB 명령어와 am 명령어로 메모리에 대한 정보를 추출할 수 있다.

> adb shell

# ps -A | grep pkgName // PID 를 확인한다.

# am dumpheap PID fileName


이후 hprof-conv 를 이용해 MAT (Memory Analyzer Tool) 에서의 분석을 위해 format 변환을 해준다.

> hprof-conv dumpheapFileName newFileName



-

변환된 파일을 Hex Editor 를 이용해 열어보면, 메모리 정보를 볼 수 있다.



-

디버거인 gdb 명령어를 이용하여 메모리 dump 를 하여 분석할 수도 있다.

각 프로세스의 메모리 정보는 proc/processId/maps 에 저장되어 있다.

이 중에서 heap 영역에 저장되므로 이 정보를 덤프하면 중요한 정보 저장 여부를 확인할 수 있다.


# cat maps | more

gdb) dump memory ./dum_01.bin 0x0000a000 0x0072e000


이를 Hex Editor 를 이용하여 보면, 내용물들을 볼 수 있다.



-

이 방법들 이외에도 promem 이나 직접 제작한 메모리 덤프 앱 등을 이용하여 정보를 확인할 수 있다.




3.10.3. 취약점 대응 방안


-

메모리 내에 모든 정보를 암호화하여 저장하기는 어려운 문제가 있다.

메모리 정보에는 중요한 정보를 일체 남기지 말아야 하지만, 사용자들이 입력한 결과는 일시적으로라도 남게 된다.

임시 파일과 캐시 정보들만 삭제한다고 해서 메모리 정보까지 삭제되는 것은 아니다.

메모리 데이터가 언제 유출될지 모르기 때문에 데이터가 100% 안전하다고 할 수 없다.



-

금융권에서는 확장 E2E(End to End)를 권고하고 있다.

이는 키보드로 입력하는 순간부터 암호화되어 최종적으로 체크하는 서버까지 암호가 되는 형태이다.

공격자들이 중간에 메모리값을 가로채더라도 암호화된 상태를 유지하는 것을 권고한다.




반응형

댓글