[C++] 引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性確認


やりたいこと

作成したアプリが、引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性に対して問題ないことを確認したい。

VisualStudio2019使用、C#/C++で書いたアプリに対してその脆弱性をどう確認すればよいか?調べてもパッとでてこなかったので、調べたことをメモ。
(恐らくこれで理解あってると思うが、誤りあれば指摘いただけるとありがたいです)

引用符で囲まれていないプログラムパス(Unquoted Program Path) の脆弱性 とは?

CreateProcessシリーズの関数を使ってプロセスを起動しようとしたときに起きる脆弱性。
CreateProcessシリーズの関数というのは、下記の関数。→こちら

CreateProcessA関数のページに、その脆弱性について、下記のように書いている。→こちら

これは例えば、

c:\program files\sub dir\programname.exe

というexeをCreateProcessA関数で起動しようとしたときに、
関数の1個目の引数「lpApplicationName」をNULLにして、「lpCommandLine」に
c:\program files\sub dir\programname.exeという文字列を入れて起動することができるが、

こういう場合がNG.cpp
if( !CreateProcessA( NULL,                                     // 1個目の引数lpApplicationName
                    "c:\program files\sub dir\programname.exe",// 2個目の引数lpCommandLine
                    NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ) )

その場合、CreateProcessA関数は

  • c:\program.exe
  • c:\program files\sub.exe
  • c:\program files\sub dir\program.exe
  • c:\program files\sub dir\programname.exe

という順番で解釈をして、(存在しなかったら次、また存在しなかったら次、という感じで)exeを実行しようとする、というのがCreateProcessシリーズの関数の仕様なので、

もし、通常は存在しないc:\program.exeというexeが、悪意あるプログラムとしてそこに置かれていたら、programname.exeを呼び出したかった実装者の意図とは関係なく、c:\program.exeが呼ばれてしまう。

という脆弱性。

大丈夫なパターンメモ
c:\program files\sub dir\programname.exeを呼ぶときに、同じCreateProcessA関数を使う場合でも、1個目の引数「lpApplicationName」をNULLにせずに、そこにプログラムのフルパスを入れて呼び出すこともできる。

こういう場合はセーフ.cpp
if( !CreateProcessA("c:\program files\sub dir\programname.exe",   // 1個目の引数lpApplicationName
                    "/a /b",                                      // 2個目の引数lpCommandLine(exeへの引数になる)
                    NULL,NULL,FALSE,0,NULL,NULL,&si,&pi ) )

その場合は、上のような問題は発生しない。
(Program.exeをC直下などに置いて試した結果、発生しなかった。)

確認のしかた

確認① CreateProcess()シリーズの関数を使っているかどうか調べる

CreateProcess()シリーズの関数の仕様で、上のページに書いてあるようなUnquoted Program Path脆弱性が発生するので、
CreateProcess()シリーズ関数を使用していなければ問題なしとする。

  • CreateProcessマクロ
    • CreateProcessA関数
    • CreateProcessAsUserA関数
    • CreateProcessAsUserW関数
    • CreateProcessW関数

確認② CreateProcess()を使っている場合の対処を実施

lpApplicationNameをNULLにして、lpCommandLineにパスを指定してプロセスを起動するようなコードになっていないか確認。
lpApplicationNameをNULLにしてなければOK。
lpApplicationNameをNULLにして、lpCommandLineでパスを指定していたらNG。

その場合は、lpCommandLineのパスの指定の文字列を、「""」でかこってやる。
こちらのページを参照。

確認③ VisualStudio2019の静的解析で、その脆弱性にあたる指摘が出ていないことを確認する

静的解析の指摘「C6277」がその脆弱性に当たる指摘のため、静的解析を行い、C6277が出ないことを確認。

C6277とは
https://docs.microsoft.com/ja-jp/cpp/code-quality/c6277?view=vs-2019

※今回は、下記の設定で静的解析を実施。(C#は関係ないと思うが、一応)

PJ種類 静的解析ルール
C++ Microsoftネイティブ推奨規則
C++/CLI Microsoft混合(C++/CLR)推奨規則
C# Microsoftマネージド推奨規則

参照

C6277(Unquoted Program Path への静的解析指摘)
https://docs.microsoft.com/ja-jp/cpp/code-quality/c6277?view=msvc-160

NULL application name with an unquoted path in call to CreateProcess
https://help.semmle.com/wiki/display/CCPPOBJ/NULL+application+name+with+an+unquoted+path+in+call+to+CreateProcess

CWE-428: Unquoted Search Path or Element
https://cwe.mitre.org/data/definitions/428.html

易し目の解説
http://securitychecklist.net/security/cyber-attack/Unquoted-Program-Path.html