コード内で変数を使った文字列の置換を定期的に行っている場合もあるでしょう。文字列挿入の機能を理解するだけでなく、その脆弱性の事例や可能性も把握することが大切です。
例えば、Cプログラムやその他多くの言語では、フォーマット文字列攻撃によってプログラムが応答しなくなることがあります。本記事では、フォーマット文字列脆弱性の仕組みや対策方法について解説します。
主にC言語のプログラムで見られる、この脆弱性はprintf()関数に存在するバグを指します。printf()は、ASCIIテキストなどのデータを標準出力へ送るためによく用いられます。正しく使えば効果的な自動変換が可能ですが、設定ミスがあると問題が発生します。
printf()に与えられた入力データがソフトウェア上でコマンドとして解釈または実行されると、フォーマット文字列攻撃が発生します。その結果、攻撃者は入力文字列に悪意あるコードを挿入したり、スタック領域に不正アクセスするなどして、一時的または恒久的な実行障害を引き起こす恐れがあります。
攻撃の深刻度により、システムの異常な動作や停止が起こる可能性があります。特に、print()関数で%dや%sを誤って使用すると、攻撃が成功しやすくなります。
この脅威の影響を受けやすいprintf系の関数には、fprintf、vsprintf、vsnprintf、sprint、vfprintfなどがあります。
以下にサンプルコードを示します。
#include <stdio.h>
int main(int arg1, char **arg2){
printf(arg2[1]);
}
このコードでは攻撃者が任意のフォーマット指定子を挿入する仕組みがありません。この問題を回避するため、安全な方法としてprintf文を次のように書き換えるとよいでしょう。
printf("%s", arg2[1]);
ここではフォーマット指定子 %s が使用されているため、攻撃の余地がありません。
Tymm Twillman氏は1999年9月、入力文字列のフォーマットに潜む可能性に初めて気付いた人物です。当時、C言語で作られたサーバであるProFTPDのセキュリティ監査に従事していた際、正確な文字列が指定されていないprintf()関数を発見し、ユーザー生成のデータがサーバへ送信される事象に気付いたのです。
事例をより正確に把握するため、利用可能な全てのprint系関数を徹底的にテストし、その結果、文字列に関する抜け穴が複数の脅威につながると結論付けました。これにより、攻撃者は特権やroot権限を得たり、システムの不具合を引き起こす恐れがあります。
長時間放置されると、既存の文字列フォーマットに関するセキュリティ抜け穴が多くの脅威を生む可能性があります。例えば、
C言語では出力時に様々な種類の引数を受け入れますが、ユーザ制御のプログラムが意図的または偶発的な入力を受けると脆弱性が発生することがあります。
C言語のフォーマット文字列は非常に一般的で、フォーマット指定子が欠如すると、ハッカーがその特性を利用して出力を操る危険性があります。
例えば、次のコードを考えてみてください。
printf("%x%x%x%x");
この場合、テキストはなく、複数のフォーマット指定子のみが存在します。
関数が実行されると、最初のスタックとそれに対応する変数のみが考慮され、残りの%x指定子は順次処理されます。
C言語のモジュールを含むWebアプリは、本攻撃に対して脆弱です。大部分のWebサーバがCまたはC++で構築されているため、脆弱なコードがWebアプリに流入しやすい状況です。
Javascriptコードにおけるフォーマット文字列も、問題となる場合があります。
さらに、sprintfを利用するPHPアプリでは、熟練したハッカーによりsprintfのフォーマット文字列脆弱性が悪用される可能性があります。この抜け穴は、過去にSQLインジェクション攻撃にも利用されました。
この攻撃はCやC++だけに限らず、Pythonにおけるフォーマット文字列も非常に一般的です。実際、調査記事ではPythonがCやC++よりも脆弱であることが示されています。特に、すべてのPython文字列がformat()メソッドを持つ点は注目に値します。
例えば、次のようなコードを考えてみてください: print("Directory {} contains {} files".format("Office", 32))
この例では、各{}がformat()メソッドの隣接する引数で置き換えられます。しかし、format()はオブジェクトを受け取り、その属性を利用して文字列整形を行う可能性もあります。ここではDirDataクラスを用い、同一モジュール内に秘密の値を保持するグローバル変数が存在すると仮定します。
SECRET_VALUE = "getintosystem"
class DirData:
def __init__(self):
self.name = "Office"
self.noOfFiles = 32
print("Directory {dirInfo.name} contains {dirInfo.noOfFiles} files".
format(dirInfo=DirData()))
この場合、Pythonオブジェクトは内部属性にアクセス可能となります。これらの属性を連結すると、次のような出力が得られる可能性があります。
秘密は: getintosystem
安全で安心なソフトウェア開発のためには、いかなる脆弱性も見逃してはなりません。早期に対策を講じるため、次のポイントに留意してください。
関数文字列はCやその他主要なプログラミング言語に欠かせない要素であり、そのエラーや脆弱性は大きな運用障害を引き起こす恐れがあります。このため、脆弱性の可能性や対策方法についての知識を深め、被害の最小化に努めるべきです。
最新情報を購読