Protobuf 分析與重建案例分享

前言

這陣子在做 IoT 設備資安檢測的時候發現受測設備的設定檔是 Protobuf 的序列化格式,剛好先前在 使用 protobuf inspector 分析未知結構緩衝資料 一文中介紹了如何對未知結構的 Protobuf 進行分析,今天我們就來把分析結果轉成可用的程式吧!

文中的設定檔非當事設定檔,是依照概念與所發現的真實問題重新設計的檔案。

你可以在 Github – protobuf-analysis-sample 下載到本文用到的檔案。

0x00 使用 Protobuf inspector 分析資料格式

先照著 使用 protobuf inspector 分析未知結構緩衝資料 的說明對資料進行初步分析,可以發現結構意外單純。

透過上圖結果,我們可以觀察到以下資訊

  • 1: 設定檔案的編號
  • 2: 韌體版本
  • 3: 硬體序號
  • 4: 網路設定
  • 5: 使用者帳號

項目 4,5 後方寫著 <chunk> = message: ,代表這兩個項目是指向另一個 message 封裝(可以把它當作另外一個物件看待)

0x01 安裝 Protobuf 編譯環境

這邊不再贅述,請參閱 在 macOS上安裝 Protobuf 編譯環境

0x02 重建 protobuf 結構描述檔

0x02.1 打地基

根據 步驟 0x00 的分析結果,我們大致上知道這個設定檔存在三個 Message,一個是根結構,兩個參考結構(網路設定/使用者帳號)

因此一開始我們可以先把基本架構蓋出來

File: config.proto

syntax = "proto2";

message NetworkSetting {
}

message Account {
}

message Config {
}

0x02.2 放入變數

message NetworkSetting {
    required string ip = 1;
    required string mask = 2;
    required string gateway = 3;
    required string dns1 = 4;
    optional string dns2 = 5;     # 在 0x00 的分析中並沒有這個項目,因為他是可選的,只有在設備設定了 DNS 2 後才會出現
}

message Account {
    required string username = 1;
    required string password = 2;
    required int32 level = 3;
}

message Config {      #這是我們的根結構
    required string config_version = 1;
    required string firmware_version = 2;
    required string device_identification = 3;
    required NetworkSetting network_setting = 4;
    repeated Account accounts = 5;      # 從 0x00 的分析中觀察到 項目 5 有複數個,因此設定為 repeated(陣列)
}

0x03 編譯 .proto 結構描述檔

完成描述檔後,便可以使用 protoc 將 .proto 轉換為我們想要使用的語言

編譯方法如下:

keniver@MacBook-Pro ~ $ protoc -I=`pwd` --python_out=`pwd` `pwd`/config.proto
#  --python_out 是因為我要編給 Python 使用,你也可以編譯給其他程式語言使用

編譯後會生成 檔案名稱_pb2.py 的檔案,這是供我們引入 Python 使用的物件

0x04 讀取 Protobuf 檔案

成功編譯 .proto 後,我們只需要在 Python 中引入編譯後的物件,就可以開始讀取 protobuf 的緩衝檔案

File: config_reader.py

import config_pb2   # 步驟 0x03 編譯後產生的物件

config = config_pb2.Config() #   在定義結構的階段,我們將 Config 定為根結構,故此處呼叫 Config 物件

with open("exported1.config") as f: 
    config.ParseFromString(f.read())  # 將讀進來的檔案轉換為物件

接著我們就可以依照我們的需求對檔案進行讀取

File: config_reader.py

print( config.accounts[0].username, config.accounts[0].password )
# 輸出為: admin admin
print( config.accounts[1].username, config.accounts[1].password )
# 輸出為: user user

請不要使用明文儲存使用者密碼,請用 Hash

Glances – 超強大資源使用監視工具

只要說到伺服器的資源監視想到的不外乎就是 top 與 進階版的htop, 今天要介紹一個更厲害的資源使用監視工具 Glances.

Glances 是一套使用 python 構成的資源監視工具, 除了基本的資源監視外還提供了 Web 介面以及遠端查看的功能.

Requirements

Python > 2.7, >= 3.3
psutil>=2.0.0

Installation

使用 pip 安裝

pip install glances

使用 shell script 安裝

curl -L https://bit.ly/glances | /bin/bash

Usage

基本使用

就跟 top 一樣, 直接在畫面上顯示資訊

glances

啟動 Web 介面

Glances 可以提供 web 介面, 但在使用前要補安裝相依套件 bootle

pip install bottle

接著啟動

glances -w

接著就可以使用喜歡的瀏覽器訪問 http://127.0.0.1:61208

遠端模式

server 模式

glances -s

client 連接

glances -c ip

Access Control

預設模式下 Glances 是免帳密驗證的, 但是為了安全起見我們還是設定一組帳號進行驗證
加入驗證的方法很簡單, 只需要帶入參數 --username--password

glances -s --username  --password

Glances 便會在啟動過程確認要設置的帳號密碼

Define the Glances server username: keniver
Define the Glances server password (keniver username):
Password (confirm):

最後, Glances 會詢問是否紀錄帳號與密碼, 之後啟動就不會再詢問了

Do you want to save the password? [Yes/No]:

當我們為 Glances 啟動驗證後, 我們連線時需要帶入 --username , --password, 並輸入先前設定的密碼才能連線

Python pip 發生 TypeError: __call__() takes exactly 2 arguments (1 given) 的解決辦法

在某次 apt-get update 後發現 pip install 出現 TypeError: call() takes exactly 2 arguments (1 given), 所有操作都無法繼續進行

追查了一下錯誤資訊, 發生錯誤的原因是 /Library/Python/2.7/site-packages/packaging/requirements.py 的 59 行

原始程式碼如下

MARKER_EXPR = originalTextFor(MARKER_EXPR())("marker")

比對其他版本的 pip 後, 發現這行應該是

MARKER_EXPR = originalTextFor(MARKER_EXPR)("marker")

於是乎就用正確的程式碼替換掉原來的程式碼, 重新運行 pip install 之後就正常了

pip 官方後續也修復了這個 Bug, 可以使用下列指令進行強制更新

python -m pip install --upgrade --force pip