PR

Excel VBAコーディングの安全性とは「(2)EXEやソースの隠蔽」

Excel VBAの安全性
この記事は約18分で読めます。

Excelのマクロやアドオンなどを利用する際に「これ使っても大丈夫なんだよね」と気になる事は無いでしょうか?

ではそうなると、「悪くない安全なマクロやアドオンとは?どのようなコーディンクになるのか?」という疑問が沸いてくるのですが、Microsoft ドキュメントの中を探してみても、これに答えてくれるページを見つける事はできませんでした。

そこでシリーズで「Excel VBAコーディングの安全性」について考えて行く事にしました。

今回は、EXEがワークシートなどの中に隠蔽されているケースやコーディング自体が「隠蔽されている」ようなケースについて取り上げます。
※前回予告しました「外部から取得している」ケースにつきましては、インターネットからのデータ取得として次回ご説明いたします。

※動作は32bit版Excel 2016と64bit版Excel 2021の バージョン2304(ビルド 16327.20248)を使用して検証しています。

スポンサーリンク

ワークシートにバイナリーファイルを格納する事ができる

別の記事になりますが「Excel VBAでバイナリーエディタを作る(SHIFT-JIS編)」と「Excel VBAでバイナリーエディタを作る(UTF-8編)」でバイナリーファイルをバイナリ配列に読み込んで、ワークシート上に16進表記させるコーディング事例とそれを書き出すコーディング事例をご紹介しています。

当該記事は「バイナリーエディタ」をVBAで実装する事が趣旨なのですが、これを悪用すると様々な形に変えて、VBAでハンドリングできるさまざまなオブジェクトにバイナリファイルを格納する事ができてしまう事を意味しています。

当然、このことは危険性のある話になるので、当該記事の中でも注意喚起をさせていただいていますが、Excelでは、このような危険性を発見し処置するためのツール「ドキュメントの検査」を提供してくれています。

なお、このツールの情報はMicrosoft 365サポートの下記ページで説明されています。

「ドキュメントの検査」が対象にしているものすべてが「直接VBAでハンドリングできるか?」は正直解っては居ませんが、例えば別起動したプロセスから隠蔽されているデータを読み出す事ができれば、わざわざインターネットにつなげずにオフラインであっても悪意があるデータを取得できてしまうはずです。

なお今回は、「どこに隠すか?」についてのコーディング事例には触れない事にいたします。
この理由としては「VBAで扱えるオブジェクトの種類はいくつかあり対応策としての確認ポイントの数が増えてしまう」ためです。

そのため次の章では「入り口ではなく出口」に話を移します。

VBAでバイナリデータを書き出す方法

ここでご紹介するのは2つの方法です。ひとつはVBAのOpenステートメントを使用する方法で、もう一つはMicrosoft ActiveX Data Objects (ADO)のStreamオブジェクトになります。

後者はCreatObject関数で呼び出す事もできますし、Visual Basic Editorの参照設定から設定する事も出来ます。

なおMicrosoft ActiveX Data Objects (ADO)のStreamオブジェクトは、前回の記事『Excel VBAコーディングの安全性とは「(1)要点と参照設定」』の中の「参照設定とTypeLib(COMクラス)」の章の『良く使われる「参照可能なライブラリ ファイル」』で一番最初に上げたライブラリです。

ただし、「これ以外に存在しないのか?」と聞かれても、結果的にはご使用のパソコンの中にどのようなCOM コンポーネント(オブジェクト)が導入されているか?によって、質問に対する答えは変わってきてしまうので断定する事はできません。

前回もご説明いたしましたが、本シリーズでお伝えしていのは「一般的に使われているコーディング事例を使って悪意のある人が悪意のあるコーディングをするとしたら、どのような点に注意をしなければならないのか?」という点になりますので、あらかじめお含み置きいただければ幸いです。

Openステートメントを使用する方法

このコーディング事例「Excel VBAでバイナリーエディタを作る(SHIFT-JIS編)」でご紹介していまが、再度掲載いたします。

バイナリファイルを書き出すためのファンクションとして実装しています。そうする事で実際に書出しのためにOpenステートメントを使用する箇所は1つに集約できますが、逆にこのユーザ関数「WriteFileBianary」をどこで呼び出しているか?を確認する事が必要になります。

  • stFileName引数
    • 書き出す時のファイル名をセットします。
  • bytbuf引数
    • 書き出すバイナリデータを格納したバイナリ配列をセットします。
Public Sub WriteFileBianary(stFileName As String, bytbuf() As Byte)
Dim fn As Integer
     fn = FreeFile
     Open stFileName For Binary Access Write As #fn
     Seek #fn, LOF(fn) + 1&
     Put #fn, , bytbuf
     Close #fn
End Sub

ADOのStreamオブジェクトを使用する方法

実はこの方法は「Excel VBAで文字コード表を作る(SHIFT-JIS編)」の記事の「Streamオブジェクトで文字コード変換をするコーディング事例」で、文字コードをSHIFT-JISにメモリ上で変換するためのユーザ関数としてご紹介しています。

今回はそれをメモリ上ではなく、バイナリファイル形式で保存するためのファンクションとして作り変えています。

なお引数はOpenステートメントの場合と同じに合わせています。

Public Sub AdoWriteFileBianary(stFileName As String, bytAry() As Byte)
Const ADTYPEBINARY As Integer = 1
Const ADTYPETEXT As Integer = 2
Const ADSAVECREATEOVERWRITE = 2
    With CreateObject("ADODB.Stream")
'引数のバイト配列をストリームにバイナリーで書き込む
        .Type = ADTYPEBINARY
        .Open
        .Write bytAry  '引数で指定されたバイト配列をストリームに書き込む
        .SaveToFile stFileName, ADSAVECREATEOVERWRITE
        .Close
    End With
End Sub

それぞれの方法でどこを確認すべきか?

ご説明した2つの方法で確認すべき点はつぎのようになります。
※一つのNoに2行以上ある時は、それぞれのポイントや設定値を確認する必要があります。

No方法確認すべきポイント・設定値
1Open ステートメントFor Binary Access Write
※Accessは「Read Write」になる場合もある
Put ステートメントバイナリデータを格納したバイナリ配列の変数
※変数にどのようなデータをセットしているのか?
2Type プロパティ値が「1」
※Stream内のデータ型を設定している
Write メソッドバイナリデータを格納したバイナリ配列の変数
※Stream内にバイナリデータをセットする
SaveToFile メソッドStream内のバイナリデータをファイルに保存
※SaveOptionsのパラメータ
1:adSaveCreateNotExists(新規作成)
2:adSaveCreateOverwrite(新規作成・上書き)

残念ながら確認ポイントだけでは不十分

悪意のある人は、このような確認ポイントを挙げるとそれ以外の方法でバイナリデータにしようと考えるはずです。

例えば16進表記のままでテキストファイルに書き出して、それをPowerShellなどでバイナリ変換するかもしれません…迂回する方法はいくらでもあるはずです。
※なお、このような外部アプリケーションを起動する場合の話はシリーズの中で今後取り上げる予定です。

結局、一番確認しなければならない事は「どのようなデータを書込もうとしているか?」になると思いますが、でも、それを理解するためにはコーディングを読み解く必要があるので、「そんな面倒くさいことはしたくない」のであれば、ファイルを出力するようなコーディングが書かれているだけで「黒と判定する」事になってしまうかもしれません。

従って、どうしてもVBAでバイナリデータを書き出す必要がある場合には、どのような内容のデータになるのか?を解り易くコーディングして置く必要がある認識です。

そうは言っても、前回でもお伝えしましたが「解り易い構造で、読み易いコーディングになっていれば安全なコーディングである」とは言えないので気を抜いてはいけません。


簡単には答えが見つからないからこそ、この話題は遣り甲斐があるのだと思います。

VBAでプログラムのソースコードを追加するコーディングはできるのか?

この章からは「ソースコードの隠蔽」の話をいたします。

まず最初に、現在はブロックされている「ScriptControl」についてご説明して置きたく存じます。

ScriptControlで出来た事

今も参照設定を見ると参照可能なライブラリとしては出て来るのですが、前回の記事の『良く使われる「参照可能なライブラリ ファイル」』には「ScriptControl」は載せていません。

この理由としては「ScriptControl」は「Microsoft Script Control 1.0」というライブラリ名で存在はするのですが、VBAでは実際に処理をするためのメソッドが動作していないためです。

例えば参照設定で「Microsoft Script Control 1.0」を有効にしてから、オフジェクト ブラウザー(Visual Basic Editorの表示→オブジェクト ブラウザー)で「MSScriptControl」にフィルターして見てみると、左図のようなメソッドが存在している事がわかります。

しかしながら、「Run」や「ExecuteStatement」なとのメソッドを実行しようとするとエラーが発生します。


「今は使えない」という話を先にしてしまいましたが、このライブラリで「何ができたか?」というと、addcodeメソッドを使ってモジュールにソースコードを動的に追加する事ができました。

そして、その追加したソースコードを「Run」や「ExecuteStatement」などのメソッドで動かす事ができたのでした。

「何のためにそのような事をする必要があったのか?」、本来の使用目的は良くは解らないのですが、悪意のある人からすれば「コーディングだけ見ても悪い動作をしなさそうなのに…」というように悪意のあるソースコードの場所を隠蔽する事ができてしまいました。

隠蔽先はVBAで扱う事ができる様々なオブジェクトの中だったり、インターネット上から入手する事もできるはずです。

振り返って考えると、『昔は「悪意がある行為」自体に対してのガードが低かった』という話になるはのかもしれません。

モジュールを追加する

では、VBAで「後からソースコードを追加する事はできないのか?」というと、トラストセンター(もしくはセキュリティ センター)で「開発者向けのマクロ設定」を変える事でモジュールを追加できてしまうので注意が必要です。

今回ご紹介する方法とは別に、先にモジュールを追加して、そこに外部ファイルを挿入することもできますので、ご注意ください。
※その方法は別の回でご紹介いたします。

「開発者向けのマクロ設定」とは

下図の「マクロの設定」は一般的に良く知られているとは思いますが、その下に配置されている「開発者向けのマクロの設定」は「開発者対象なので触るべきでない」と考えられているかもしれません。

「フアイル」メニュタブ→「オプション」サイドバー→「トラストセンター」サイドバー→「トラストセンターの設定」ボタン→「マクロの設定」サイドバー

左図赤枠の所のチェックボックスはデフォルトではOffになっていますが、Onにする事でできる内容が、下記のMicrosoft 365サポートのページに記載されています。

上記ページの一番最後のところで「開発者向けのマクロ設定」が書かれていますが、重要な部分だけを次に引用いたします。

このセキュリティ オプションは、Microsoft 365 プログラムを自動化し、VBA 環境とオブジェクト モデルを操作するように記述されたコード用です。 既定では、アクセスを拒否して、許可されていないプログラムが有害な自己複製コードを組み込むことを阻止します。

ここをOnにすると「コードを組み込むこと」つまり「モジュールを追加」する事ができるようになります。

なお一番最後にMicrosoft Accessに関しての下記の注意書きが記載されています。

注: Microsoft Access には、 VBA プロジェクト モデル オブジェクト オプションへの信頼アクセス 権がありません。

実はAccessはModulesオブジェクト自体にAddFromFileメソッドゃ、AddFromStringメソッド、InsertLinesメソッドが設定されていて、能動的にコーディングを変更できる仕様になっているので「信頼アクセス権」は存在していないようです。

それからすると、ExcelのVBAの方が安全性は高いと言えるのですが、気を付けなければならないのは 「開発者向けのマクロ設定」がセキュリティの要になっているという事です。

このガードを解放してしまったらAccessと同じセキュリティレベルになってしまう点は注意をするべきです。

「開発者向けのマクロ設定」をOnにした時のコーディング事例

さすがにVBAのコーディングによって、このチェックボックスをOnにする事はできません。

ここは手動でOnにするしか方法はないのですが、例えば「レジストリデータを取得するためにOnにしてください」見たいな、「機能提供上Onにする必要がある」と思わせるメッセージを表示して誘導するかもしれません。

そのようなメッセージが表示された時は「まずは疑ってかかる」必要がありそうです。

実行しているExcelファイルと同じフォルダーに置かれたモジュールを追加する

まずは実行しているExcelファイルと同じフォルダーに置かれている モジュール「Module2.bas」を追加するサンプル コーディングです。
※標準モジュールModule1に下記コーディングをセットする事を想定してModule2にしています。

Sub threat1()
 With ThisWorkbook.VBProject.VBComponents
    .Import ThisWorkbook.Path & "\Module2.bas"
 End With
End Sub

ちなみにModule2.basの中身は何でも良いのですが、今回はつぎのようにしています。

Attribute VB_Name = "Module2"
Sub main()
MsgBox "Hallow World!"
End Sub

なお、サブルーチン「threat1」のなかにサブルーチン「main」を呼び出すようすると「コンパイル エラー SubまたはFunctionが定義されていません。」になって実行する事ができません。

従って追加したモジュールを実行するためには、「追加するのは別に何かしらのマクロを実行する」という行為が必要になるはずです。
※「絶対に必要か?」と言われると、ご使用のパソコンの中にどのようなCOM コンポーネント(オブジェクト)が導入されているか?すべが分かっている訳ではなので断定はできない認識です。

VBProject.VBComponentsとは

サブルーチン「threat1」のなかに記述されている「VBProject.VBComponents」の内のVBProjedtはVisual Basic エディターの環境を拡張するために使用できるオブジェクト モデルでMicrosoftのドキュメントでは「Visual Basic アドイン モデル」と呼ばれています。

VBComponentsコレクションについてはMicrosoftドキュメントのVisual Basic For Applicationsの次のページで説明がされています。

いろいろあるコレクションの内の1つとして説明されているのでポイントを下記に引用いたします。

VBComponents コレクション使用して、プロジェクト内のコンポーネントへのアクセス、追加、または削除を行えます。

ここで注意しなければならないのは、追加だけではなく削除もできる点です。

つまり、一度追加した後に追加した中のメインの部分を削除してしまうと、「何が動いたのか?」すら追跡できない事になります。

【補足】参照設定の「Microsoft Visual Basic for Applications Extensibility」

VBProjedtオブジェクトを使用するために参照設定で「Microsoft Visual Basic for Applications Extensibility」をチェックする必要があるか?というと、どうやらチェックしていなくてもVBProjedtオブジェクトは使用できるようです。

なお参照設定をしなければ使用できなきない機能もあるかもしれませんが、VBProjedtオブジェクトを使用する使用する分には必要がなさそうです。

実行しているモジュールにコーディングを追加する

前の章事例では、実行しているモジュールとは別に新たにモジュールを追加しましたが、今回は実行しているモジュールにサブルーチンを追加するコーディング事例になります。

Sub threat2()
 With ThisWorkbook.VBProject.VBComponents("Module1").CodeModule
    .AddFromString ThisWorkbook.Sheets("Sheet1").Range("A1").Value
 End With
End Sub

VBComponentsコレクションのCodeModuleオブジェクトは、コンポーネントに関連付けられているコードを変更する場合に使用します。

今回はSheet1のA1セルに書かれている内容をコードとして追加しています。

書かれている内容は例えば左図のようなもので、サブルーチン「main2」になります。

この状態でサブルーチン「threat2」を実行すると下図のようにモジュールの先頭にコーディングが追加されます。

追加されたサブルーチン「main2」はCallステートメントになどで実行する事ができます。

なおCodeModuleオブジェクトにも削除のメソッドは存在するので、追加したモジュールのメイン部分を消す事は可能です。

それぞれの方法でどこを確認すべきか?

そもそも「開発者向けのマクロ設定」にチェックが付いていなければ問題は無いはずなのですが、付いているとした時は次のような点になります。

No方法確認すべきポイント・設定値
1importメソッド何をimportしようとしているのか?
※場合によっては、何かをした時にimport対象が作られる
可能性もあります。
2AddFromStringメソッド何をモジュールに追記しようとしていねか?
※場合によってはインターネットから取得する事もあり得ます。

「開発者向けのマクロ設定」がOffの場合

最後に「開発者向けのマクロ設定」にチェックが付いていない時のメッセージをご紹介いたします。

実行時エラー’1004’が表示されて、その理由としては「’VBProject’メソッドは失敗しました:’_Workbook’オブジェクト」になります。

 

 

「threat2」を実行して、上記メッセージの「デバッグ」をクリックすると当該行にマークが付きます。

従って「開発者向けのマクロ設定」にチェックが付いていなければ、今回のようなコーディングはブロックされるはずです。

なお、今回も文章の単語数が1万字に近づいてきてしまったので、この章で今回は終わりにしたいと思います。

「今回のまとめ」と「次回の予告」

今回はExcel VBAコーディングでEXEやコーディング自がファイルやワークシートなどの中に隠されているケースについてご説明いたしました。

Excel VBAも段々と悪意のある行為に対する対策が強化されてきていますが、過去を振り返って見ると今回ご説明したような事がScriptControlを使って普通に実行できた時代もありました。

EXEが隠蔽されているケースではExcelファイルサイズが他に比べて大きくなっているはずですので、そんなちょっとした違和感に気付けるかどうかでリスクを回避する事ができる場合もあるはずです。

なおExcel VBAの安全性は配慮されて強化されてきている認識ですが、本来必要な機能でも使い方によっては脅威となるものが存在をしていますので、安心する事はできません。

また折角追加された機能も有効に使わなければ宝の持ち腐れになってしまいます。

次回は「インターネットからデータを取得するケース」についてお話いたします。

以上、最後までご一読いただき誠にありがとうございました。