<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/tips/13239

                    drozer是MWR Labs開發的一款Android安全測試框架。是目前最好的Android安全測試工具之一。drozer提供了命令行交互式界面,使用drozer進行安全測試,用戶在自己的console端輸入命令,drozer會將命令發送到Android設備上的drozer agent代理程序執行。drozer采用了模塊化的設計,用戶可以定制開發需要的測試模塊。編寫drozer模塊主要涉及python模塊及dex模塊。python模塊在drozer console端運行,類似于metasploit中的插件,可以擴展drozer console的測試功能。dex模塊是java編寫的android代碼,類似于android的dex插件,在android手機上運行,用于擴展drozer agent的功能。

                    0x00 簡單的drozer模塊demo代碼


                    首先看看drozer wiki給出的demo,該模塊的功能就是在android設備上反射調用java.util.Random類生成一個隨機數并返回:

                    #!python
                    from drozer.modules import Module
                    
                    class GetInteger(Module):
                    
                        name = ""
                        description = ""
                        examples = ""
                        author = "Joe Bloggs (@jbloggs)"
                        date = "2012-12-21"
                        license = "BSD (3-clause)"
                        path = ["exp", "test"]
                    
                        def execute(self, arguments):
                            random = self.new("java.util.Random")
                            integer = random.nextInt()
                    
                            self.stdout.write("int: %d\n" % integer)
                    

                    GetInteger類就是一個簡單的drozer模塊,它繼承自drozer提供的模塊基類Module。每個繼承了Module的類都對應著一個drozer模塊,模塊具體實現的功能則是在類中重寫excute函數,實現新的功能。在drozer console中執行以下命令就可以運行該模塊了:

                    #!bash
                    dz> run exp.test.getinteger
                    

                    運行效果如下:

                    1

                    drozer 通過Module類的metadata來配置和管理每個模塊,因此模塊編寫時需要包含以下 metadata信息:

                    name          模塊的名稱
                    description   模塊的功能描述 
                    examples      模塊的使用示例
                    author        作者
                    date          日期
                    license       許可
                    path          描述模塊命令空間
                    

                    這些信息中比較重要的就是path變量,它描述了模塊在drozer namespace中的路徑,結合對應的classname可以唯一確定drozer中的模塊。例如demo中的path = ["exp", "test"],類名為GetInteger,那么在drozer console中該模塊就以exp.test.getinteger唯一確定。需要注意的是,盡管類的名字有大小寫之分,但運行該模塊的時候,drozer console中的名字都為小寫。

                    0x01 drozer模塊倉庫的創建及模塊安裝


                    drozer模塊安裝有兩種方法,一種是直接在repository中按照python包管理的方法新建目錄結構,將python文件放入相應目錄中,另一種是在drozer console中通過module install命令直接安裝模塊。

                    這兩種方法都必須先在本地創建一個drozer 的repository目錄,可以直接在drozer console中通過命令創建:

                    #!bash
                     dz> module repository create [/path/to/repository]
                    

                    也可以在~/.drozer_config文件中指定本地repository目錄

                    #!bash
                    [repositories]  
                    /path/to/repository  =  /path/to/repository
                    

                    創建好本地repository后就可以安裝自己的模塊了。兩種安裝方法:

                    1) 按照python包管理的方式,在本地repository目錄下創建目錄exp,新建__int__.py空白文件,然后將Python模塊源碼放入exp目錄即可。例如將test.py放入exp目錄下,test.py的內容如下:

                    #!python
                    from drozer.modules import Module
                    
                    class GetInteger(Module):
                    
                        name = ""
                        description = ""
                        examples = ""
                        author = "Joe Bloggs (@jbloggs)"
                        date = "2012-12-21"
                        license = "BSD (3-clause)"
                        path = ["exp", "test"]
                    
                        def execute(self, arguments):
                            random = self.new("java.util.Random")
                            integer = random.nextInt()
                    
                            self.stdout.write("int: %d\n" % integer)
                    

                    安裝好模塊之后即可在drozer console端通過命令run exp.test.getinteger運行該模塊了。

                    2) 通過drozer console中的命令module install 安裝。首先將編輯好的python模塊源文件命名為 exp.test2,文件的內容同上。在drozer console中執行

                    #!bash
                    dz> module install  [/path/to/exp.test2]      
                    

                    執行成功后則可以在本地repository目錄下exp目錄中看到生成了test2.py文件,內容和原來的exp.test2文件一致。安裝成功后及可執行該模塊了。module install除了可以安裝本地倉庫的模塊外,還可以遠程安裝gitbub上的模塊,地址為

                    https://raw.github.com/mwrlabs/drozer-modules/repository/

                    例如運行

                    #!bash
                    dz>module install jubax.javascript     
                    

                    將遠程下載并安裝scanner.misc.checkjavascriptbridge模塊,安裝完成后執行

                    #!bash
                    dz> run scanner.misc.checkjavascriptbridge
                    

                    就可以運行該模塊,該模塊的功能是檢查webview中addJavascriptInterface的使用是否存在安全隱患。

                    0x02 利用drozer提供的API擴展功能


                    drozer封裝了android中大部分API功能,使得能夠在python中方便的使用這些API擴展功能,發揮drozer及python的強大威力。

                    1)利用反射直接與Dalvik虛擬機交互,其實就是Python直接在寫android代碼,非常簡單方便。drozer主要是利用了drozer agent代理實現相關功能,實例化某個類的代碼如下:

                    #!python
                    my_object = self.new("some.package.MyClass")
                    

                    例如drozer.android模塊中封裝了Intent類,用戶可以通過如下方式構造需要的Intent:

                    #!python
                    someintent = android.Intent(action=act, category=cat, data_uri=data, component=comp, extras=extr, flags=flgs)
                    

                    然后通過intent打開某個activity:

                    #!python 
                    self.getContext().startActivity(someintent)
                    

                    2) drozer針對比較常用的功能還二次封裝了很多python的mixins工具類,提供了更簡單易用的API,這些mixins都在drozer.modules.common包中:

                    例如FileSystem類提供了訪問android手機文件系統的接口,可以方便地讀寫、創建及刪除andoid手機上的目錄和文件。ZipFile類提供了解壓zip文件的功能。 為了使用這些mixin類提供的功能,在模塊中可以直接繼承這些類就可以了:

                    #!python
                    from drozer.modules import common, Module
                    
                    class MyModule(Module, common.FileSystem, common.ZipFile):
                               ……
                               ……
                           self.deleteFile(“somepath”)
                               ……
                               ……
                           dex_file = self.extractFromZip("classes.dex", path, self.cacheDir())
                    

                    其中,self.deleteFile來自FileSystem類,self.extractFromZip來自ZipFile類。

                    0x03 實現find port及find IP模塊


                    1) app開放端口查找模塊

                    Android app通常會監聽某些端口進行本地IPC或者遠程網絡通信,但是這些暴露的端口卻代表了潛在的本地或遠程攻擊面,具體可以參考大牛的文章:

                    淺談Android開放網絡端口的安全風險

                    文章中提供了查找開放端口及對應app的python腳本,我們將其重寫為drozer模塊,方便測試時使用:

                    #!python
                    from drozer.modules import Module,common
                    import re
                    
                    class findport(Module,common.Shell):
                    
                        name = ""
                        description = "find open port in android"
                        examples = "run exp.work.findport"
                        author = "[email protected]"
                        date = "2015-12-02"
                        license = "BSD (3-clause)"
                        path = ["exp","work"]
                    
                        def toHexPort(self,port):
                            hexport = str(hex(int(port)))
                            return hexport.strip('0x').upper()     
                    
                        def finduid(self,protocol, entry):
                            if (protocol=='tcp' or protocol=='tcp6'):
                                uid = entry.split()[-10]
                    
                            else: # udp or udp6
                                uid = entry.split()[-6]
                    
                            try: 
                                uid = int(uid)
                            except:
                                return -1   
                            if (uid > 10000): # just for non-system app
                                return 'u0_a'+str(uid-10000) 
                            else:
                                return -1
                    
                        def execute(self, arguments):        
                    
                            proc_net = "/proc/net/"      
                            ret = self.shellExec("netstat -anp | grep -Ei 'listen|udp*'")
                            list_line = ret.split('\n')
                            apps = []
                            strip_listline = []
                            #pattern = re.compile("^Proto") # omit the first line
                    
                            for line in list_line:              
                                    if (line != ''):               
                                       socket_entry = line.split()
                                       protocol = socket_entry[0]  
                                       port = socket_entry[3].split(':')[-1]
                                       grep_appid = 'grep  '+ self.toHexPort(port) + ' ' + proc_net + protocol                    
                    
                                       net_entry = self.shellExec(grep_appid)                         
                                       uid = self.finduid(protocol, net_entry)
                    
                                       if (uid == -1): 
                                           continue
                    
                                       applist = self.shellExec('ps | grep ' + uid).split()    
                                       app = applist[8]
                                       apps.append(app)
                                       strip_listline.append(line)
                    
                            itapp= iter(apps)
                            itline=iter(strip_listline)
                    
                            self.stdout.write("Proto  Recv-Q Send-Q  Local Address        Foreign Address        State            APP\r\n")
                            try:
                                while True:
                                    self.stdout.write( itline.next() + ' '*10 + itapp.next() + '\n')
                    
                            except StopIteration:
                                pass
                    
                            self.stdout.write('\n')
                    

                    該模塊的主要功能都是在findport類中的execute函數中實現,查找開放端口及app的方法和原來文章中的一樣,這里主要用到了drozer提供的common.Shell類,用于在android設備上執行shell命令:

                    #!python 
                    ret = self.shellExec("netstat -anp | grep -Ei 'listen|udp*'") 
                    

                    在drozer console中直接運行如下命令即可:

                    #!bash 
                    dz> run exp.work.findport
                    

                    運行效果如下:

                    image

                    2)app中IP地址掃描模塊

                    drozer的scanner.misc.weburls提供了掃描app中http及https URL地址的功能,仿照該模塊的功能,我們實現了app中IP地址的掃描模塊,這些收集到的IP地址可以在web滲透測試中使用:

                    #!python
                    import re
                    from pydiesel.reflection import ReflectionException
                    from drozer.modules import common, Module 
                    
                    class findips(Module, common.FileSystem, common.PackageManager, common.Provider, common.Strings, common.ZipFile):
                    
                        name = "Find IPs specified in packages."
                        description = """
                        Find IPs in apk files
                        """
                        examples = ""
                        author = "[email protected]"
                        date = "2015-12-9"
                        license = ""
                        path = ["exp", "server"]
                        permissions = ["com.mwr.dz.permissions.GET_CONTEXT"]
                    
                        def add_arguments(self, parser):
                            parser.add_argument("-a", "--package", help="specify a package to search")
                    
                        def execute(self, arguments):
                            self.ip_matcher = re.compile(r"((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))")
                            if arguments.package != None:
                                self.check_package(arguments.package, arguments)
                            else:
                                for package in self.packageManager().getPackages(common.PackageManager.GET_PERMISSIONS):
                                    try:
                                        self.check_package(package.packageName, arguments)
                                    except Exception, e:
                                        print str(e)
                    
                        def check_package(self, package, arguments):
                            self.deleteFile("/".join([self.cacheDir(), "classes.dex"]))
                            ips = []
                    
                            for path in self.packageManager().getSourcePaths(package):
                                strings = []
                                if ".apk" in path:
                                    dex_file = self.extractFromZip("classes.dex", path, self.cacheDir())
                                    if dex_file != None:
                                        strings = self.getStrings(dex_file.getAbsolutePath())
                    
                                        dex_file.delete()
                                        strings += self.getStrings(path.replace(".apk", ".odex")) 
                                elif (".odex" in path):
                                    strings = self.getStrings(path)
                                else:
                                    continue
                    
                                for s in strings:
                                    m = self.ip_matcher.search(s)
                                    if m is not None:
                                        ips.append(s)
                    
                                if len(ips) > 0:
                                    self.stdout.write("%s\n" % str(package))
                    
                                for ip in ips:
                                        self.stdout.write("  %s\n" % ip)
                    
                                if len(ips) > 0 :
                                    self.stdout.write("\n")
                    

                    add_arguments函數是drozer提供的接口,用于添加命令行參數,這里我們添加了--package參數,用于指定app名稱,如果沒有指定--package參數,那么默認會查找所有app中的IP地址,比較耗時。check_package函數主要實現指定app掃描IP地址的功能,該函數首先從app相關目錄中查找apk文件、odex文件,如果是apk文件則從apk文件中解壓出classes.dex文件:

                    #!python
                    for path in self.packageManager().getSourcePaths(package):
                        strings = []
                        if ".apk" in path:
                            dex_file = self.extractFromZip("classes.dex", path, self.cacheDir())
                    

                    然后從得到的dex、odex文件中獲取到所有的strings:

                    #!python
                    strings = self.getStrings(path)
                    

                    這里的getStrings是drozer提供的API,實現了類似linux下strings命令的功能。

                    找到app中的所有strings后再用re匹配得到相應的IP地址:

                    #!python
                    for s in strings:
                        m = self.ip_matcher.search(s)
                        if m is not None:
                            ips.append(s)
                    

                    ip_matcher的正則表達式為:

                    #!python
                    self.ip_matcher = re.compile(r"((?:(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d)))\.){3}(?:25[0-5]|2[0-4]\d|((1\d{2})|([1-9]?\d))))")
                    

                    最后,在drozer console中通過如下命令運行該模塊:

                    #!bash
                    dz> run exp.server.findips -a com.dianping.v1
                    

                    運行效果如下所示:

                    image

                    0x04 編寫dex插件


                    除了利用drozer以python代碼形式提供的API,用戶還可以用java代碼編寫dex插件。 例如下面的java代碼就可以編譯為drozer的dex插件:

                    #!python
                    import java.io.File;
                    import java.io.FileInputStream;
                    import java.io.FileOutputStream;
                    import java.io.IOException;
                    
                    import android.util.Base64;
                    import android.util.Log;
                    import android.widget.Toast;
                    import android.net.Uri;
                    import android.content.ContentResolver;
                    import android.database.Cursor;
                    import android.provider.ContactsContract;
                    import android.content.Context;
                    
                    
                    public class dextest {
                    
                      private static final int BUFFER_SIZE = 4096;
                    
                    
                        public static String test(Context c, String number) {
                    
                                String name = null;
                                Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + number);
                                ContentResolver resolver = c.getContentResolver();
                                Cursor cursor = resolver.query(uri, new String[]{android.provider.ContactsContract.Data.DISPLAY_NAME}, null, null, null);
                                if (cursor.moveToFirst()) {
                                    name = cursor.getString(0);
                                    Log.d("drozer", name);
                                }
                                cursor.close();
                    
                    
                             Log.d("drozer","this is a drozer dex module!");
                             return "hello world! this is a test! " + number + ": " + name;
                      }
                    
                    }
                    

                    首先我們將該java文件編譯為class文件:

                    #!bash
                    javac -cp lib/android.jar dextest.java
                    

                    然后用android sdk提供的dx工具將class文件轉換為dex文件:

                    #!bash     
                    dx  --dex --output=dextest.apk dextest*.class
                    

                    最后將生成的dextest.apk文件放到drozer的modules/common目錄下,在編寫drozer模塊時可以通過以下方式調用該dex插件:

                    #!python
                    dextest = self.loadClass("common/dextest.apk", "dextest")
                    self.stdout.write("[color red]get string from dex plugin: %s  [/color]\n" % dextest.test(self.getContext(),"181" ) )   
                    

                    該測試插件根據提供的部分電話號碼去匹配手機通訊錄中的聯系人,并返回匹配到的聯系人姓名,執行效果如下:

                    2

                    dex插件是由drozer上傳到android手機上加載執行,它的作用還是為drozer模塊提供更方便易用的接口,擴展更多的功能。由于dex插件是Java編寫的原生android代碼,在執行效率上比通過反射調用更高。drozer的modules/common目錄下包含了多個dex插件的源碼,有興趣的同學可以自己查看。

                    0x05 drozer模塊的reload及動態加載問題


                    編寫drozer module難免會涉及到調試的問題,drozer console提供了debug選項,會在console中打印異常信息,但是比較麻煩的是,修改module源碼后必須要重啟drozer console才能生效。

                    查看drozer源碼,發現drozer在debug模式下提供了reload命令,但是測試了下,在mac下并沒有用,還是要重啟console才能生效。仔細研究drozer loader.py的相關源碼:

                    #!python
                    def all(self, base):
                         """
                         Loads all modules from the specified module repositories, and returns a  collection of module identifiers.
                         """
                    
                        if(len(self.__modules) == 0):
                            self.__load(base)
                    
                        return sorted(self.__modules.keys())
                    
                    def get(self, base, key):
                        """
                        Gets a module implementation, given its identifier.
                        """
                    
                        if(len(self.__modules) == 0):
                            self.__load(base)
                    
                        return self.__modules[key]
                    
                    def reload(self):
                        self.__modules = {}
                    

                    reload命令將self.__modules置為空,在get中按理說就會重新加載所有的drozer模塊。但是在mac下始終無法實現該功能,其他平臺未做測試。這里就涉及到python模塊的import及reload機制問題,在網上查找到python的reload機制一些解釋:

                    reload會重新加載已加載的模塊,但原來已經使用的實例還是會使用舊的模塊, 而新生產的實例會使用新的模塊, reload后還是用原來的內存地址;不能支持from。。import。。格式的模塊進行重新加載。
                    http://blog.csdn.net/five3/article/details/7762870

                    猜測可能就是這個問題,雖然用python的reload機制可以重新加載模塊,但是以前使用的模塊可能還是在使用中,導致修改的源碼沒有生效。

                    為什么不在執行時動態加載模塊呢?這樣可以保證加載的模塊源碼是最新的。

                    分析了drozer相關的所有源碼,終于在session.py中找到實例化模塊類的代碼:

                    #!python
                    def __module(self, key):
                        """
                        Gets a module instance, by identifier, and initialises it with the
                        required session parameters.
                        """
                    
                        module = None
                    
                        try:
                            module = self.modules.get(self.__module_name(key))
                        except KeyError:
                            pass
                    
                        if module == None:
                            try:
                                module = self.modules.get(key)
                            except KeyError:
                                pass
                    
                        if module == None:
                            raise KeyError(key)
                        else:
                            return module(self)
                    

                    該函數的功能就是根據模塊類的key實例化該模塊,從而運行該模塊。因此,我們可以在這里實現動態加載要運行的模塊類,放棄已經加載的模塊:

                    #!python
                     def __module(self, key):
                    
                        """
                        Gets a module instance, by identifier, and initialises it with the
                        required session parameters.
                        """
                    
                        module = None
                    
                        try:
                            module = self.modules.get(self.__module_name(key))
                        except KeyError:
                            pass
                    
                        if module == None:
                            try:
                                module = self.modules.get(key)
                            except KeyError:
                                pass
                    
                        if module == None:
                            raise KeyError(key)
                        else:
                    
                            #reload module
                            mod = reload(sys.modules[module.__module__])
                    
                            module_class_name = module.__name__
                            module_class = getattr(mod,module_class_name)  #get module class object
                            return module_class(self)
                    

                    關鍵的代碼如下:

                    #!python
                    #reload module   
                    mod = reload(sys.modules[module.__module__])
                    
                    module_class_name = module.__name__
                    module_class = getattr(mod,module_class_name)  #get module class object
                    return module_class(self)
                    

                    首先使用python的reload函數重新加載指定的模塊,然后再在重新加載的模塊中查找到drozer模塊關聯的類,最后實例化并返回。只需添加幾行代碼便可實現動態加載模塊類,這樣調試的時候就不用每次重啟drozer console了。這里只是提供了一種簡單的實現動態加載模塊的方法,主要是方便模塊的編寫及測試。

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频