2016년 10월 27일 목요일

미라이 봇넷(MIRAI BOTNET) Attack 분석


MIRAI BOTNET 


요새 MIRAI BOTNET이 한창 말이 많아서

과연 내 IoT 제품은 안전할까? 걱정되시는 분들이 많으실 것 같습니다.


그런데 벌써 MIRAI 소스코드가 다 공개되었기 때문에

MIRAI가 실제로 IoT 기기 공격을 어떻게 하는건지 분석했고

주요 부분만 간추려서 정리하였습니다.




일단 바쁘신 분들을 위해 결론만 적어보자면



MIRAI BOTNET에 취약한가?


1. 23번 포트(Telnet)를 쓰고 있다

2. ID/PW가 추측하기 쉽다.


이러면 기본적으로 MIRAI BOTNET 에 취약하다 볼 수 있습니다.

반대로 1, 2 둘 중 하나만 만족하지 않아도 MIRAI BOTNET에 취약하지 않습니다.



그러나 금방 변형이 등장할 것 이므로.....


해결책은?


1. 최대한 (특히 쉘을 제공하는) 포트를 열지 않는다.

2. 아이디 / 패스워드를 어렵고 길게 만든다.



정도가 되겠네요.


MIRAI -> IoT 기기 공격 분석입니다.



1. Injection

공격 방식은 아이디/패스워드 테이블에서 랜덤하게 뽑아서 대입해 보는 것으로,

Burpsuite 의 Intruder로 따지자면 Pitchfork 형태가 되겠습니다.


이 Parameter는 add_auth_entry("아이디", "패스워드", "가중치") 로 설정하는데,

가중치가 높을수록 공격시 랜덤으로 뽑힐 가능성이 높아집니다.



가장 가중치를 높게( 10 ) 설정 한 부분의 난독화를 풀면 

add_auth_entry("root", "xc3511", 10)  입니다.

아이디가 root이면서 패스워드가 xc3511 인 IoT 기기가 특히 많나 보네요.



2. Port



보시면 Packet Header 를 설정하는데, dest 는 23번 (Telnet) 포트로 고정하고,

src는 1024 이하에서 랜덤으로 설정합니다.

이 부분이 중요합니다. MIRAI BOTNET은 23번 포트만 스캐닝 합니다. 



1, 2 번이 끝나고 3번부터는 계속 루프를 돌면서 스캐닝을 합니다.



3. IP

IP는 랜덤하게 가져옵니다.

다만 정말 마구잡이로 랜덤하게 설정하는 것은 아닙니다.

아래처럼, 



Class별로 IP처럼 생긴 숫자만 뽑아서 랜덤하게 만듭니다.



4. Scan

이제 Syn Raw Packet을 만들어 쏘고, Syn +Ack 패킷 받는 걸 계속합니다.

방식은 하나씩 시도 해보는게 아니라

커넥션을 128개까지 만들어서 syn 패킷을 보내놓고,

한꺼번에 처리하는 방식입니다.



5. Attack

Connection이 성립하면 공격을 하는데,

이 부분을 BruteForce라고 부르기가... 좀 애매합니다.

왜냐하면 62개 정도 밖에 안되는 ID / PW 셋을 

모두 다 시도 해보는 것도 아니고 

아래처럼 가중치에 따라 랜덤하게 뽑아서 시도 해보기 때문입니다.






다만 Connection 이 살아있다면, 10번 정도 더 시도 해보긴 합니다.....







마치며...


일단 오늘 MIRAI Botnet 공격( MIRAI -> IoT )의 실체를 파헤쳐 보았는데요.

소스 코드를 보면 복잡하고 어려운 공격이라기 보다

최대한 빠르게 취약한 기기들을 장악하려는 제작자의 의도를 알 수 있습니다.






성공 후 감염은 어떻게 시키는 건지 

CNC 서버와 통신은 어떻게 하는건지 등등


더 볼만한 부분이 많습니다.

MIRAI의 소스는 다음 GitHub주소 에서 보실 수 있습니다.

https://github.com/jgamblin/Mirai-Source-Code



감사합니다.






2016년 10월 4일 화요일

[Android] Frida 사용해보기


입소문으로만 들었던 Frida. 직접 사용해 보기로 했다.
(프리더 아니고 프라다 아니다.)

이상하게도 한국에는 소개된 블로그가 없었다..


기본적으로 DBus 타고 PC쪽이 client, 앱쪽이 server 구조를 갖고 있다.

PC에서 스크립트를 작성해서 실행하면 된다.



다른 것보다 빌드가 제일 힘들었다... 프록시에.. VS2015... Python Path 공백 없어야 되고..
잘되던 nodejs npm 도 안되서 Python 등등 그냥 집에서  빌드 해서 가져 왔다.

바로 frida-ps 치면 에러가 나올텐데 chcp 65001 등으로 콘솔 코드 페이지 바꿔 줘야 한다.

-안드로이드-
인터넷에 영어로 찾아보면 다 있는 내용인데 정리해 본다.

원하는 앱을 실행시키고, {경로}/frida-server (예) /data/local/tmp/frida-server를 실행하면 된다.
윈도에서는 frida-trace -U -i open "원하는 패키지명" 하면 바인딩 된다

일단 프로세스에 Attach 하는 방법은, frida-ps 으로 pid 찾고,
frida -U -p {pid} Attach 하면 된다.
또는 app 이름 알면 앱으로 바로 Attatch 가능하다.
frida -U com.example.seccon2015.rock-paper-scissors

(실제 내가 해본 예제는 블로그에 올릴 수 없는 내용이라서...
글로만 서술했다. 죄송..)

아래는 인터넷에 돌아다니는 예제들인데,

작성 -> Attach 후 -> %load Examples.js 하면 앱 실행하면서 같이 돌아간다.




Hello SyntaxHighlighter

Examples.js

//http://blog.mdsec.co.uk/2015/04/instrumenting-android-applications-with.htmlD
alvik.perform(function () {
   
    var LockManager = Dalvik.use("com.github.orangegangsters.lollipin.lib.managers.LockManager");
    var LockManagerInstance = LockManager.getInstance();
    var AppLock = LockManagerInstance.getAppLock();
 
    for(var i=1230; i<1235; i++)
    {
            var result = AppLock.checkPasscode(i+"");
        send(i + ": " + result);
    }
});





/* Check if a Java/Dalvik/ART VM is available */
if (Java.available) {
    /* enumerate loaded classes */
    Java.enumerateLoadedClasses({
        /* when a class is found send it to the client */
        onMatch: function(className) {
            send(className);
        },
        /* when we are done enumerating classes send "done" to the client */
        onComplete: function() {
            send("done");
        }
    });
/* if a Java/Dalvik/ART VM is not available */
} else {
    send("Java not available in this process");
}



//https://cedricvb.be/post/seccon-2015-reverse-engineering-android-apk-2-400-writeup/#prettyPhoto
Dalvik.perform(function () {
    var c = Dalvik.use("kr.repo.h2spice.yekehtmai.c")
    c.a.implementation = function (str1, str2) {
        console.log("String1: " + str1)
        console.log("String2: " + str2)
    }
});





Java.perform(function () {
    // Function to hook is defined here
    var MainActivity = Java.use('com.example.seccon2015.rock_paper_scissors.MainActivity');

    // Whenever button is clicked
    MainActivity.onClick.implementation = function (v) {
        // Show a message to know that the function got called
        send('onClick');

        // Call the original onClick handler
        this.onClick(v);

        // Set our values after running the original onClick handler
        this.m.value = 0;
        this.n.value = 1;
        this.cnt.value = 999;

        // Log to the console that it's done, and we should have the flag!
        console.log('Done:' + JSON.stringify(this.cnt));
    };
});



//https://gist.github.com/flankerhqd/c621f5aee06dad53e37c
Dalvik.perform(function () {
    var Activity = Dalvik.use("com.example.myapp.MyActivity");
    Activity.hookMe.overload("java.lang.String").implementation = function () {
        Activity.hookMe.overload("java.lang.String").call(args[0], "foo");//comment out this line won't crash
        return "foo";
    };
});
// This result in immediate crash
// Question: Is args[0] refer to this? How do I refer args, like accessing arg in hookMe?




//http://rotlogix.com/2015/09/13/defeating-ssl-pinning-in-coin-for-android/
Dalvik.perform(function () {

    var SSLPinning = Dalvik.use("com.onlycoin.android.secure.SSLPinning");

        SSLPinning.a.overload("android.content.Context", "[Ljava.lang.String;", "java.lang.String").implementation = function (c, s, ss) {
            send("SSLPinning");
            send(c.toString());
            send(s.toString());
            send(ss.toString());
            this.a.overload("android.content.Context", "[Ljava.lang.String;", "java.lang.String").call(this, c, s, ss);
        };

});




//http://www.welivesecurity.com/la-es/2016/02/04/instrumentando-apps-android-frida/




//https://github.com/frida/frida/issues/49
Dalvik.perform(function () {
var application = Dalvik.use("com.test.test").currentApplication();
var context = application.getApplicationContext();
var package_name = context.getPakcageName();
var package_info = context.getPackageManager().getPakcageInfo(package_info, 64);
send(package_info);
send(context):
send(package_name):
var sign = package_info.signatures[0];
send(signs);
});