Unleash the Beast!

CTFなどのメモに使います

ツール開発:Extract-PSImage(はじめてのGitHub)

勉強も兼ねて、初めてGitHub上にツールをリリースしました。

Extract-PSImage https://github.com/imurasheen/Extract-PSImage

Invoke-PSImage(https://github.com/peewpw/Invoke-PSImage)によってスクリプトが埋め込まれてしまった画像からスクリプトを抽出するためのツールです。

 

Invoke-PSImageとは

Invoke-PSImageは2017年末にリリースされ、すぐに平昌オリンピックへの攻撃に利用されたことで有名になったツールです。

参考:https://ascii.jp/elem/000/001/615/1615152/

 

日本国内では、その後もしばしば攻撃に利用されているのを見かけます。

Invoke-PSImageは、画像にスクリプトを埋め込む際に、そのスクリプトを抽出するための専用の命令列=ワンライナーを生成します。

巧妙にデータが埋め込まれているため元々気づきにくいのですが、ワンライナーが特定できないとスクリプトを抽出できない、という点でも解析の難易度が高い攻撃です。

Invoke-PSImageの処理を解析

今回は、Invoke-PSImageのスクリプト埋め込み処理を解析することで逆に抽出ができるのではないかと考え、挑戦してみました。

Invoke-PSImage.ps1の動きをトレース
(1)まずスクリプトをロード
(2)埋め込み対象の画像をimgオブジェクトにロードし、幅と高さを取得
(3)メモリ上のキャンバスにビットマップデータを描画
(4)RGBデータを抽出
 $bytes  = [Math]::Abs($bmpData.Stride) * $img.Height
 Absは絶対値。bmpData.Strideはストライド
 ストライド幅:参考 http://neareal.com/470/
 幅が512pxのデータだとすると、1画素がBGRAで構成されているなら512*4=2048バイトがストライド
(5)埋め込みたいペイロードの内容が画像サイズにフィットするかどうかをチェック
(6)ペイロードを埋めない場所に埋め込むランダムストリングを生成
(7)ループ処理でRGB配列にペイロードを埋め込み
(8)(7)で生成したデータをビットマップのキャンバスに戻す
(9)Png形式でファイルを保存
(10)ワンライナーのパラメータ生成
    $rows = [math]::Ceiling($payload.Length/$width)
   →$width = $img.Size.Width Ceilingは小数点以下切り上げ
    $array = ($rows*$width)
    $lrows = ($rows-1)
    $lwidth = ($width-1)
    $lpayload = ($payload.Length-1)
payload.Length以外のパラメータは元画像から取得もしくは生成している。
よって、payload.Lengthを以下のいずれかで決定すればsolverが書けることになる。
①画像に埋め込み可能な最大値
②最大値までインクリメントしながら発見できる箇所を探す
Invoke-PSImageのつくりからして①で行けるはず。

■Extract-PSImageの使用法

作成は基本的にInvoke-PSImageのコードをいじることで行いました。

詳細はリポジトリを参照してください。

https://github.com/imurasheen/Extract-PSImage

 

簡単な使用法だけ。

PS> Import-Module .\Extract-Invoke-PSImage.ps1
PS> Extract-Invoke-PSImage -Image [PNG画像へのパス] -Out [抽出したスクリプトの出力先のファイルパス]
[Oneliner to extract embedded payload]

ペイロードを抽出するワンライナー(実行は行わないような内容)
[First 50 characters of extracted payload]

抽出したペイロードの最初の50文字

 

Extract-PSImageは、対象となる画像に埋め込むことができる最大長のスクリプト文字列の抽出を試みます。そのため、結果には以下の制約が付きます。

(1)埋め込まれたスクリプトが、埋め込み可能な最大長よりも短い場合、抽出したスクリプトの後ろにランダムな文字列がくっついて出力されます。

(2)スクリプトが埋め込まれていない画像に対してExtract-PSImageにより解析を行った場合も、実行はエラーとなりません。ただし、出力された結果はすべて無意味なランダム文字列となります。

 

抽出に成功した際の例は以下の通り。

f:id:imurasheen:20190802021239p:plain

GitHubへの投稿方法

ファイルのアップロードなどは直感的に分かったんですが、Readmeの書き方に少し苦戦しました。以下を参照しました。

https://deeeet.com/writing/2014/07/31/readme/

https://gist.github.com/wate/7072365

■まとめ

・十分な試験を行って作成したツールではありませんので、ロジックの読み間違えなどでバグや問題点があるかもしれません。使ってみた方がいらっしゃればご意見いただきたいです。

・初めてのGitHubへのツールのリリースでしたので、良い経験になったと思います。

・検知等に活用する方法について検討していきたいです。

 

■追記:ライセンス表記

当初はリポジトリにライセンス表記を入れてなかったのですが、 

・ライセンス表記を入れていないと、デフォルトの著作権法に従うことになる

  ⇒表記しないとデフォルトの著作権が適用され、編集、再頒布、商用利用が不可 

Invoke-PSImageもMITライセンスを設定している  

実際にはライセンス表記していないリポジトリも多いのであんまり気にしていない人も多いかもしれませんが、Extract-PSImageにもMITライセンスを設定することにします。 

リポジトリの生成時にライセンスを指定していれば簡単なんですがここまで来てしまったので後付けでライセンスを設定する方法をご紹介いたします。     

設定方法は以下を参照しました。 

https://qiita.com/shibukk/items/67ad0a5eda5a94e5c032

 

リポジトリのトップで[Create new file]をクリック

※一度登録に成功した後に画面キャプチャしたので、画面中に"MIT"のタブが表示されています。登録に成功するとこのように表示されますよ笑

f:id:imurasheen:20190802141756p:plain

②"LICENSE"というファイル名称を入力し、[Choose a license template]をクリック

f:id:imurasheen:20190802141939p:plain

ライセンステンプレートの選択画面が表示される。「MIT License」を選択する

f:id:imurasheen:20190802141943p:plain

 

④MIT Licenseの内容が表示される。ライセンス表記中に記載される"Year" および"Full name"の内容を設定し、[Review and submit]を選択

f:id:imurasheen:20190802142503p:plain

 

⑤作成されたライセンスファイルの内容が表示される。問題なければ、このファイルをコミットすれば作業完了

f:id:imurasheen:20190802142624p:plain