<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/tools/12680

                    Author:[email protected]

                    0x00 前言


                    對于protobuf over-HTTP的數據交互方式Burpsuite不能正確的解析其中的數據結構,需要Burpsuite擴展才能解析,筆者使用mwielgoszewski的burp-protobuf-decoder【1】擴展實踐了protobuf數據流的解析,供有需要的同學學習交流。筆者實踐使用的環境: burpsuite+python2.7+protobuf2.5.0。

                    0x01 安裝burp-protobuf-decoder擴展


                    burp-protobuf-decoder【1】擴展是基于protobuf庫(2.5.x版本)開發的burpsuite python擴展,可用于解析、篡改 request/response中protobuf數據流。從https://github.com/mwielgoszewski/burp-protobuf-decoder下載該擴展源碼,然后解壓。

                    該擴展是基于protobuf和jython實現的。先下載protobuf 2.5.0【2】源碼進行編譯,編譯方法請參考其README.txt文件。需求在burpsuite的Extender中配置Jython【3】的路徑:

                    p1

                    Burpsuite中添加擴展:

                    1. 在Burpsuite的Extender窗口中點擊“Add”按鈕,彈出的“Load Burp Extension”窗口中選擇如下信息:

                      p2

                    2. 然后Next,當看到如下信息時表示擴展加載成功:

                      p3

                    Tips:

                    1. 加載擴展時提示“Error calling protoc: Cannot run program "protoc" (in directory "******"): error=2, No such file or directory”錯誤

                      解決辦法:修改protoburp.py中調用protoc命令的路徑,有多處,如:

                      process = subprocess.Popen(['protoc', '--version']'protoc'改為'/home/name/protobuf/src/protoc'

                    2. 加載擴展碰到cannot import name symbol_database錯誤

                      可能是你使用的protoc與擴展所使用protobuf python庫版本不一致原因,一種解決辦法是下載protobuf 2.5.0源碼編譯后,修改protoburp.py中對應的路徑,再加載擴展。

                    3. 擴展加載成功了,但不能解析protobuf數據流

                      該擴展通過判斷頭部“content-type”是否為“'application/x-protobuf'”來決定是否解析數據,你可以修改protoburp.py中的isEnabled()方法讓其工作。

                    0x02 protobuf簡介


                    protobuf是Google開源的一個跨平臺的結構化數據存儲格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。

                    protobuf通過定義“.proto”文件來描述數據的結構。.proto文件中用 “Message”來表示所需要序列化的數據的格式。Message由Field組成,Field類似Java或C++中成員變量,通常一個Field的定義包含修飾符、類型、名稱和ID。下面看一個簡單的.proto文件的例子:

                    #!cpp
                    syntax = "proto2";
                    package tutorial;
                    message Person {
                      required string name = 1;
                      required int32 id = 2;
                      optional string email = 3;
                    
                      enum PhoneType {
                        MOBILE = 0;
                        HOME = 1;
                        WORK = 2;
                      }
                    
                      message PhoneNumber {
                        required string number = 1;
                        optional PhoneType type = 2 [default = HOME];
                      }
                    
                      repeated PhoneNumber phone = 4;
                    }
                    message AddressBook {
                      repeated Person person = 1;
                    }
                    

                    使用下面的python代碼生成二進制數據流:

                    #!python
                    import addressbook_pb2
                    address_book = addressbook_pb2.AddressBook()
                    person = address_book.person.add()
                    person.id = 9
                    person.name = 'Vincent'
                    person.email = [email protected]'
                    phone = person.phone.add()
                    phone.number = '15011111111'
                    phone.type = 2
                    f = open('testAb', "wb")
                    f.write(address_book.SerializeToString())
                    f.close()
                    

                    序列化后的二進制數據流如下:

                    p4

                    有關Protobuf的語法網上已有很多文章了,你可以網上搜索或參考其官網【4】說明。

                    2.1Varint編碼

                    Protobuf的二進制使用Varint編碼。Varint 是一種緊湊的表示數字的方法。它用一個或多個字節來表示一個數字,值越小的數字使用越少的字節數。這能減少用來表示數字的字節數。

                    Varint 中的每個 byte 的最高位 bit 有特殊的含義,如果該位為 1,表示后續的 byte 也是該數字的一部分,如果該位為 0,則結束。其他的 7 個 bit 都用來表示數字。因此小于 128 的數字都可以用一個 byte 表示。大于 128 的數字,比如 300,會用兩個字節來表示:1010 1100 0000 0010。

                    下圖演示了protobuf如何解析兩個 bytes。注意到最終計算前將兩個 byte 的位置相互交換過一次,這是因為protobuf 字節序采用 little-endian 的方式。

                    p5 (圖片來自網絡)

                    2.2數值類型

                    Protobuf經序列化后以二進制數據流形式存儲,這個數據流是一系列key-Value對。Key用來標識具體的Field,在解包的時候,Protobuf根據 Key 就可以知道相應的 Value 應該對應于消息中的哪一個 Field。

                    Key 的定義如下:

                    (field_number << 3) | wire_type

                    Key由兩部分組成。第一部分是 field_number,比如消息 tutorial .Person中 field name 的 field_number 為 1。第二部分為 wire_type。表示 Value 的傳輸類型。Wire Type 可能的類型如下表所示:

                    Type Meaning Used For
                    Varint int32, int64, uint32, uint64, sint32, sint64, bool, enum
                    1 64-bit fixed64, sfixed64, double
                    2 Length-delimi string, bytes, embedded messages, packed repeated fields
                    3 Start group Groups (deprecated)
                    4 End group Groups (deprecated)
                    5 32-bit fixed32, sfixed32, float

                    以數據流:08 96 01為例分析計算key-value的值:

                    #!bash
                    08 = 0000 1000b
                        => 000 1000b(去掉最高位)
                        => field_num = 0001b(中間4位), type = 000(后3位)
                        => field_num = 1, type = 0(即Varint)
                    96 01 = 1001 0110 0000 0001b
                        => 001 0110 0000 0001b(去掉最高位)
                        => 1 001 0110b(因為是little-endian)
                        => 128+16+4+2=150
                    

                    最后得到的結構化數據為:

                    1:150

                    其中1表示為field_num,150為value。

                    2.3手動反序列化

                    p6

                    以上面例子中序列化后的二進制數據流進行反序列化分析:

                    #!bash
                    0A = 0000 1010b => field_num=1, type=2;
                    2E = 0010 1110b => value=46;
                    0A = 0000 1010b => field_num=1, type=2;
                    07 = 0000 0111b => value=7;
                    

                    讀取7個字符“Vincent”;

                    #!bash
                    10 = 0001 0000 => field_num=2, type=0;
                    09 = 0000 1001 => value=9;
                    1A = 0001 1010 => field_num=3, type=2;
                    10 = 0001 0000 => value=16;
                    

                    [email protected]

                    #!bash
                    22 = 0010 0010 => field_num=4, type=2;
                    0F = 0000 1111 => value=15;
                    0A = 0000 1010 => field_num=1, type=2;
                    0B = 0000 1011 => value=11;
                    

                    讀取11個字符“15011111111”;

                    #!bash
                    10 = 0001 0000 => field_num=2, type=0;
                    02 = 0000 0010 => value=2;
                    

                    最后得到的結構化數據為:

                    #!bash
                    1 {
                      1: "Vincent"
                      2: 9
                      3: "[email protected]"
                      4 {
                        1: "15011111111"
                        2: 2
                      }
                    }
                    

                    2.4使用protoc反序列化

                    實現操作經常碰到較復雜、較長的流數據,手動分析確實麻煩,好在protoc加“decode_raw”參數可以解流數據,我實現了一個python腳本供使用:

                    #!python
                    def decode(data):
                        process = subprocess.Popen(['/usr/local/bin/protoc', '--decode_raw'],
                        stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                    
                        output = error = None
                        try:
                            output, error = process.communicate(data)
                        except OSError:
                            pass
                        finally:
                            if process.poll() != 0:
                                process.wait()
                        return output
                    
                    f = open(sys.argv[1], "rb")
                    data = f.read()
                    print 'data:\n',decode(data)
                    f.close()
                    

                    使用python decode.py <proto.bin>即可反序列化,其中proto.bin為protobuf二進制數據流文件。得到結構化的數據后我們可以逐步分析,猜測每個Field的名稱,輔助協議、數據結構等逆向分析。

                    0x03 burpsuite+protobuf實戰


                    用webpy模擬protobuf over-HTTP的web app。

                    服務端overHttp_server.py內容如下:

                    #!python
                    #!/usr/bin/env python
                    #coding: utf8
                    #author: Vincent
                    import web
                    import time
                    import os
                    
                    urls = (
                        "/",  "default",
                        )
                    app = web.application(urls, globals())
                    
                    class default:
                        def GET(self):
                            return 'hello world.'
                        def POST(self):
                            reqdata = web.data()
                    
                            print 'client request:'+reqdata
                            resdata = reqdata.split(':')[-1]
                            web.header('Content-type', 'application/x-protobuf')
                            return resdata
                    if __name__ == "__main__":
                    app.run()
                    

                    客戶端overHttp_client.py內容如下:

                    #!python
                    #!/usr/bin/env python
                    #coding: utf8
                    #author: Vincent
                    import urllib
                    import urllib2
                    import json
                    import addressbook_pb2
                    import sys
                    
                    proxy = 'http://<ip>:8888'
                    target = "http://<ip>:8080/"
                    enable_proxy = True  
                    proxy_handler = urllib2.ProxyHandler({"http" : proxy})  
                    null_proxy_handler = urllib2.ProxyHandler({})  
                    if enable_proxy:  
                        opener = urllib2.build_opener(proxy_handler)  
                    else:  
                        opener = urllib2.build_opener(null_proxy_handler)  
                    urllib2.install_opener(opener)
                    
                    def doPostReq():
                        url = target
                        address_book = addressbook_pb2.AddressBook()
                        f = open('testAb', "rb")
                        address_book.ParseFromString(f.read())
                        ad_serial = address_book.SerializeToString()
                        f.close()
                        data = ad_serial
                        opener = urllib2.build_opener(proxy_handler, urllib2.HTTPCookieProcessor())
                        req = urllib2.Request(url, data, headers={'Content-Type': 'application/x-protobuf'})
                        response = opener.open(req)
                        return response.read()
                    
                    resp = doPostReq()
                    print 'response:',resp
                    

                    3.1proto文件逆向分析

                    啟動服務端:python overHttp_server.py <ip>:8080

                    客戶端請求:python overHttp_client.py

                    此時burp中已解析出protobuf數據,如下圖:

                    p7

                    但是這個結構的可讀性還是比較差,我們可以通過逆向分析逐步猜測字段名稱、類型,然后再解析,方便實現協議的逆向、安全測試等。

                    對這個結構我們可以還原成以下proto文件:

                    #!cpp
                    syntax = "proto2";
                    package reversed.proto1;
                    
                    message Msg {
                      optional string _name = 1;
                      optional int32 field2 = 2;
                      optional string _email = 3;
                    
                      message subMsg1 {
                        required string _phone = 1;
                        optional int32 sub1_field2 = 2;
                      }
                    
                      repeated subMsg1 field4 = 4;
                    }
                    
                    message Root {
                      repeated Msg msg = 1;
                    }
                    

                    然后使用右鍵的“Load .proto”加載該文件:

                    p8

                    再看解析結果:

                    p9

                    3.2數據篡改

                    打開request攔截:

                    p10

                    運行python overHttp_client.py發送請求。攔截到request后,把sub1_field2改為999。

                    p11

                    “Forward”后看request數據,已被篡改:

                    p12

                    0x04 參考


                      <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>

                                      这里只有精品视频