ソースコードをコンパイルすると、バイナリが生成されます。コンパイル中に、バイナリの特定のプロパティを有効または無効にするフラグをコンパイラに提供できます。これらのプロパティの一部はセキュリティに関連しています。
Checksecは、他の関数の中でも、コンパイル時にバイナリに組み込まれたセキュリティプロパティを識別する気の利いた小さなツール(およびシェルスクリプト)です。コンパイラはこれらのプロパティの一部をデフォルトで有効にする場合があり、他のプロパティを有効にするために特定のフラグを指定する必要がある場合があります。
この記事では、checksecを使用して、バイナリのセキュリティプロパティを識別する方法について説明します。
- checksecがセキュリティプロパティに関する情報を検索するために使用する基本的なコマンド
- サンプルバイナリをコンパイルするときにGNUコンパイラコレクション(GCC)を使用してセキュリティプロパティを有効にする方法
Fedoraおよびその他のRPMベースのシステムにchecksecをインストールするには、以下を使用します:
$ sudo dnf install checksec
Debianベースのディストリビューションの場合、同等のapt
を使用します コマンド。
Checksecは、かなり大きなスクリプトですが、単一ファイルのシェルスクリプトです。利点は、スクリプトをすばやく読み、実行中のすべてのシステムコマンドを理解して、バイナリまたは実行可能ファイルに関する情報を見つけることができることです。
$ file / usr / bin / checksec
/ usr / bin / checksec:Bourne-Againシェルスクリプト、ASCIIテキスト実行可能ファイル、非常に長い行
$ wc -l / usr / bin / checksec
2111 / usr / bin / checksec
おそらく毎日実行するバイナリを備えたドライブのchecksecを実行します:ユビキタスなls
指図。コマンドの形式はchecksec --file=
です。 ls
の絶対パスが続きます バイナリ:
$ checksec --file =は/ usr / binに/ lsの
RELRO STACK CANARY NX PIE RPATH RUNPATHシンボルFORTIFY要塞Fortifiableファイル
全RELROカナリアはNXはPIEはありませんRPATHないRUNPATHない記号を有効に有効になっていはい5 17 / usr / bin / ls
これを端末で実行すると、何が良いのか、何がおそらく悪いのかを示す色分けが表示されます。 「おそらく」と言うのは、何かが赤で表示されていても、必ずしも物事が恐ろしいことを意味するわけではないからです。バイナリをコンパイルするときに、ディストロベンダーがトレードオフを行ったことを意味するだけかもしれません。
最初の行は、RELRO
などのバイナリで通常使用できるさまざまなセキュリティプロパティを提供します 、STACK CANARY
、NX
、など(以下で詳しく説明します)。 2行目は、特定のバイナリ(ls
)のこれらのプロパティのステータスを示しています。 、 この場合)。たとえば、NX enabled
このバイナリに対して一部のプロパティが有効になっていることを意味します。
このチュートリアルでは、次の「helloworld」プログラムをサンプルバイナリとして使用します。
#include
int main()
{
printf( "Hello World \ n");
return 0;
}
gcc
を提供しなかったことに注意してください コンパイル中に追加のフラグを使用する場合:
$ gcc hello.c -o hello
$ file hello
hello:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/ lib64 / ld-linux-x86-64.so.2、BuildID [sha1] =014b8966ba43e3ae47fab5acae051e208ec9074c、GNU / Linux 3.2.0の場合、削除されません
$ ./hello
Hello World
checksecを介してバイナリを実行します。一部のプロパティは、ls
の場合とは異なります 上記のコマンド(画面上では、これらは赤で表示される場合があります):
部分RELROありませんカナリア/>
$
Checksecでは、--output
で指定できるさまざまな出力形式を使用できます。 。 JSON形式を選択し、出力をjq
にパイプします きれいな印刷のためのユーティリティ。
フォローするには、jq
があることを確認してください このチュートリアルではこの出力形式を使用して、出力から特定のプロパティをすばやくgrepし、yes
を報告するためにインストールされました またはno
それぞれに:
$ checksec --file =./ hello --output =json | jq
{
"./hello":{
"relro": "partial"、
"canary": "no"、
"nx": " yes "、
" pie ":" no "、
" rpath ":" no "、
" runpath ":" no "、
" symbol ":" yes " 、
"fortify_source": "no"、
"fortified": "0"、
"fortify-able": "0"
}
}
セキュリティの詳細
- 防御的なコーディングガイド
- ウェビナー:システムのセキュリティと標準オペレーティングシステムへの準拠の自動化
- Linuxコンテナセキュリティの10層
- SELinuxの塗り絵
- その他のセキュリティ記事
上記のバイナリには、いくつかのセキュリティプロパティが含まれています。そのバイナリをls
と比較します 上記のバイナリを使用して、何が有効になっているかを調べ、checksecがこの情報をどのように検出したかを説明します。
1。記号
まずは簡単なものから始めましょう。コンパイル中、主にデバッグのために、特定のシンボルがバイナリに含まれます。これらの記号は、ソフトウェアを開発するときに必要であり、デバッグと修正に複数のサイクルが必要です。
これらのシンボルは通常、一般的な使用のためにリリースされる前に、最終的なバイナリから削除(削除)されます。これは、バイナリの実行にはまったく影響しません。シンボルの場合と同じように実行されます。シンボルが削除されるとバイナリがいくらか軽くなるため、スペースを節約するために削除が行われることがよくあります。クローズドソースまたはプロプライエタリソフトウェアでは、シンボルをバイナリに含めるとソフトウェアの内部動作を簡単に推測できるため、シンボルが削除されることがよくあります。
checksecによると、シンボルはこのバイナリに存在しますが、ls
には存在しませんでした。 バイナリ。 file
を実行して、この情報を見つけることもできます。 プログラムのコマンド-not stripped
が表示されます 終わりに向かって出力で:
$ checksec --file =/ bin / ls --output =json | jq | grepシンボル
"symbols": "no"、
$ checksec --file =./ hello --output =json | jq | grepシンボル
"symbols": "yes"、
$ file hello
hello:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [sha1] =014b8966ba43e3ae47fab5acae051e208ec9074c、GNU / Linux 3.2.0の場合、削除されません
checksecはどのようにしてこの情報を見つけましたか?まあ、それは便利な--debug
を提供します 実行された関数を表示するオプション。したがって、次のコマンドを実行すると、シェルスクリプト内で実行された関数が表示されます。
$ checksec --debug --file=./hello
このチュートリアルでは、この情報を見つけるために使用される基本的なコマンドを探しています。シェルスクリプトなので、いつでもBashの機能を利用できます。このコマンドは、シェルスクリプト内から実行されたすべてのコマンドを出力します:
$ bash -x /usr/bin/checksec --file=./hello
出力をスクロールすると、echo_message
が表示されます。 その後にセキュリティプロパティのカテゴリが続きます。バイナリにシンボルが含まれているかどうかについてchecksecが報告する内容は次のとおりです。
+ readelf -W --symbols ./hello
+ grep -q'\ .symtab'
+ echo_message'\ 033 [31m96)シンボル\ t \ 033 [m'シンボル、'シンボル="yes"'' "symbols": "yes"、'
これを単純化するために、checksecはreadelf
を利用します バイナリを読み取り、特別な--symbols
を提供するユーティリティ バイナリ内のすべてのシンボルを一覧表示するフラグ。次に、特別な値.symtab
を取得します。 、検出したエントリ(シンボル)の数を提供します。上でコンパイルしたテストバイナリで次のコマンドを試すことができます。
$ readelf -W --symbols ./hello
$ readelf -W --symbols ./hello | grep -i symtab
コンパイル後またはコンパイル中にシンボルを削除できます。
- コンパイル後: コンパイル後、
strip
を使用できます シンボルを削除するためのバイナリのユーティリティ。file
を使用して機能したことを確認します コマンド。出力はstripped
として表示されます。 :
$ gcc hello.c -o hello
$
$ file hello
hello:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV) 、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [sha1] =322037496cf6a2029dcdcf68649a4ebc63780138、GNU / Linux 3.2.0の場合、ストリップされない
$
$ strip hello
$
$ file hello
hello:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so .2、BuildID [sha1] =322037496cf6a2029dcdcf68649a4ebc63780138、GNU / Linux 3.2.0の場合、削除
$
コンパイル中にシンボルを削除する方法
コンパイル後に手動でシンボルを削除する代わりに、-s
を指定して、コンパイラに削除を依頼できます。 引数:
$ gcc -s hello.c -o hello
$
$ file hello
hello:ELF 64ビットLSB実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [sha1] =247de82a8ad84e7d8f20751ce79ea9e0cf4bd263、GNU / Linux 3.2.0の場合、削除
$
checksecを再実行すると、symbols
が表示されます。 no
として表示されます :
$ checksec --file =./ hello --output =json | jq | grepシンボル
"シンボル": "no"、
$
2。カナリア
カナリアは、スタック上のバッファと制御データの間に配置される既知の値です。 バッファオーバーフローを監視します。アプリケーションを実行すると、2種類のメモリが割り当てられます。それらの1つはスタックです 、これは2つの操作を含む単純なデータ構造です:push
、データをスタックに配置し、pop
、逆の順序でスタックからデータを削除します。悪意のある入力は、特別に細工された入力でスタックをオーバーフローまたは破損させ、プログラムをクラッシュさせる可能性があります:
$ checksec --file =/ bin / ls --output =json | jq | grep canary
"canary": "yes"、
$
$ checksec --file =./ hello --output =json | jq | grep canary
"canary": "no"、
$
checksecは、バイナリがカナリアで有効になっているかどうかをどのように確認しますか?上記の方法を使用して、シェルスクリプト内で次のコマンドを実行することにより、それを絞り込むことができます。
$ readelf -W -s ./hello | grep -E '__stack_chk_fail|__intel_security_cookie'
これらのケースから保護するために、コンパイラは-stack-protector-all
を提供します フラグ。このようなバッファオーバーフローをチェックするためにバイナリに追加のコードを追加します。
$ gcc -fstack-protector-all hello.c -o hello
$ checksec --file =./ hello --output =json | jq | grep canary
"canary": "yes"、
Checksecは、プロパティが有効になったことを示します。これは次の方法でも確認できます:
$ readelf -W -s ./hello | grep -E'__stack_chk_fail | __intel_security_cookie'
2:0000000000000000 0 FUNC GLOBAL DEFAULT UND __stack_chk_fail @ GLIBC_2.4(3)
8:0000000000000000 _ _ _ _ _ _ _ _ $
3。 PIE
PIEは、位置に依存しない実行可能ファイルの略です。名前が示すように、絶対アドレスに関係なく、実行のためにメモリ内のどこかに配置されるコードです:
$ checksec --file =/ bin / ls --output =json | jq | grep pie
"pie": "yes"、
$ checksec --file =./ hello --output =json | jq | grep pie
"pie": "no"、
多くの場合、PIEはライブラリに対してのみ有効になり、スタンドアロンのコマンドラインプログラムには有効になりません。以下の出力では、hello
LSB executable
として表示されます 、一方、libc
標準ライブラリ(.so
)ファイルはLSB shared object
とマークされています :
$ file hello
hello:ELF 64ビットLSB実行可能、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [ sha1] =014b8966ba43e3ae47fab5acae051e208ec9074c、GNU / Linux 3.2.0の場合、削除されません
$ファイル/lib64/libc-2.32.so
/lib64/libc-2.32.so:ELF64ビットLSB共有オブジェクト、x86-64、バージョン1(GNU / Linux)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [sha1] =4a7fb374097fb927fb93d35ef98ba89262d0c4a4、GNU / Linux 3.2.0用、剥ぎ取られていない
Checksecは、次の方法でこの情報を見つけようとします:
$ readelf -W -h ./hello | grep EXEC
タイプ:EXEC(実行可能ファイル)
EXEC
の代わりに共有ライブラリで同じコマンドを試した場合 、DYN
が表示されます :
$ readelf -W -h /lib64/libc-2.32.so | grep DYN
タイプ:DYN(共有オブジェクトファイル)
テストプログラムでPIEを有効にするには、次の引数をコンパイラに送信します。
$ gcc -pie -fpie hello.c -o hello
checksecを使用してPIEが有効になっていることを確認できます:
$ checksec --file =./ hello --output =json | jq | grep pie
"pie": "yes"、
$
タイプがEXEC
から変更されたPIE実行可能ファイルとして表示されるはずです。 DYN
へ :
$ file hello
hello:ELF64ビットLSBpie実行可能ファイル、x86-64、バージョン1(SYSV)、動的リンク、インタープリター/lib64/ld-linux-x86-64.so.2、BuildID [sha1] =bb039adf2530d97e02f534a94f0f668cd540f940、GNU / Linux 3.2.0の場合、削除されません
$ readelf -W -h ./hello | grep DYN
タイプ:DYN(共有オブジェクトファイル)
4。 NX
NXは「実行不可能」の略です。多くの場合、CPUレベルで有効になっているため、NXが有効になっているオペレーティングシステムは、メモリの特定の領域を実行不可としてマークできます。多くの場合、バッファオーバーフローのエクスプロイトは、コードをスタックに配置してから実行しようとします。ただし、この書き込み可能領域を実行不可にすると、このような攻撃を防ぐことができます。このプロパティは、gcc
を使用した通常のコンパイル中にデフォルトで有効になります :
$ checksec --file =/ bin / ls --output =json | jq | grep nx
"nx": "yes"、
$ checksec --file =./ hello --output =json | jq | grep nx
"nx": "yes"、
Checksecは、以下のコマンドを使用してこの情報を判別します。 RW
終わりに向かって、スタックが読み取りおよび書き込み可能であることを意味します。 E
がないので 、実行可能ではありません:
$ readelf -W -l ./hello | grep GNU_STACK
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW 0x10
推奨されていませんが、NX
を無効にすることができます -z execstack
を使用してプログラムをコンパイルする場合 引数:
$ gcc -z execstack hello.c -o hello
$ checksec --file =./ hello --output =json | jq | grep nx
"nx": "no"、
コンパイル時に、スタックは実行可能になります(RWE
)、悪意のあるコードの実行を許可します:
$ readelf -W -l ./hello | grep GNU_STACK
GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x10
5。 RELRO
RELROはRelocationRead-Onlyの略です。 Executable Linkable Format(ELF)バイナリは、グローバルオフセットテーブル(GOT)を使用して関数を動的に解決します。有効にすると、このセキュリティプロパティにより、GOTがバイナリ内で読み取り専用になり、何らかの形の再配置攻撃が防止されます。
$ checksec --file =/ bin / ls --output =json | jq | grep relro
"relro": "full"、
$ checksec --file =./ hello --output =json | jq | grep relro
"relro": "partial"、
Checksecは、以下のコマンドを使用してこの情報を検索します。ここでは、RELROプロパティの1つが有効になっています。したがって、checksecで検証すると、バイナリは「部分的」と表示されます。
$ readelf -W -l ./hello | grep GNU_RELRO
GNU_RELRO 0x002e10 0x0000000000403e10 0x0000000000403e10 0x0001f0 0x0001f0 R 0x1
$ readelf -W -d ./hello | grep BIND_NOW
完全なRELROを有効にするには、gcc
でコンパイルするときに、次のコマンドライン引数を使用します :
$ gcc -Wl、-z、relro、-z、now hello.c -o hello
$ checksec --file =./ hello --output =json | jq | grep relro
"relro": "full"、
これで、2番目のプロパティも有効になり、プログラムが完全にRELROになります:
$ readelf -W -l ./hello | grep GNU_RELRO
GNU_RELRO 0x002dd0 0x0000000000403dd0 0x0000000000403dd0 0x000230 0x000230 R 0x1
$ readelf -W -d ./hello | grep BIND_NOW
0x0000000000000018(BIND_NOW)
6。要塞化
Fortifyは別のセキュリティプロパティですが、この記事の範囲外です。 checksecがバイナリでfortifyを検証する方法と、gcc
でどのように有効にするかについては学習しません。 あなたが取り組むための練習として。
$ checksec --file =/ bin / ls --output =json | jq | grep -i forti
"fortify_source": "yes"、
"fortified": "5"、
"fortify-able": "17"
$ checksec --file =./ hello --output =json | jq | grep -i forti
"fortify_source": "no"、
"fortified": "0"、
"fortify-able": "0"
セキュリティのトピックは終わりがなく、ここですべてを網羅することはできませんが、checksec
のいくつかの機能について言及したいと思います。 一緒に仕事をするのを楽しくするコマンド。
checksecに個別に各バイナリを提供する必要はありません。代わりに、複数のバイナリが存在するディレクトリパスを指定すると、checksecが一度にすべてのバイナリを検証します。
$ checksec --dir=/usr/bin
バイナリに加えて、checksecは実行中のプログラムでも機能します。次のコマンドは、システムで実行されているすべてのプログラムのセキュリティプロパティを検索します。 --proc-all
を使用できます 実行中のすべてのプロセスをチェックする場合、または名前を使用して特定のプロセスを選択する場合:
$ checksec --proc-all
$ checksec --proc =bash
この記事で説明されているchecksecのユーザーランドアプリケーションに加えて、システムに組み込まれているカーネルプロパティをチェックするためにも使用できます。
$ checksec --kernel
Checksecは、どのユーザーランドとカーネルのプロパティが有効になっているかを理解するための良い方法です。各セキュリティプロパティを詳細に調べ、各機能を有効にする理由と、それが防止する攻撃の種類を理解してください。