2016.12.27
シェルコードとは
書籍『セキュリティコンテストチャレンジブック』2章 pwnの関連記事としてシェルコードに関する記事を3回にわたって掲載します。
はじめに
この連載では『セキュリティコンテストチャレンジブック』2章「pwn」の関連記事としてシェルコードに関する記事を以下計3回
・1 シェルコードとは/2 シェルコードの基礎知識
・3 シェルコードを書いてみる
・4. シェルコードを圧縮する
で掲載します(書籍未掲載記事です!) pwnについてはこちら
1 シェルコードとは
シェルコードは攻撃の際に使う機械語で書かれたプログラムの断片のことで、主にシェルを起動するために作られている場合が多いことからシェルを起動するかに関わらずシェルコードと呼んでいます。シェルコードは基本的に外部のライブラリを必要とせず、単体で動作するように作られています。また、シェルコードの分野は書籍『セキュリティコンテストチャレンジブック』の第2章「pwn」で紹介した脆弱性とは独立した技術ですので、CTFにおいてはシェルコードを書く能力を問う問題からバッファオーバーフローからシェルコードを使った攻撃に発展させる問題などで幅広く登場します。
また、シェルコードをいちいち書かなくとも、"shellcode" で検索するとシェルを起動するシェルコードや特定のIP, ポートに接続してシェルを立ち上げるシェルコードなど、多くの種類のシェルコードを入手することができます。
2 シェルコードの基礎知識
シェルコードは機械語で書かれていますが、さすがに人間がいきなり機械語を書くのは難しいので、普通はアセンブラを書いてそれを機械語にアセンブルするという方法で作成します。感覚としては普段バイナリ解析の時に読んでいるアセンブラを自分の手で書くという作業になります。
アセンブルするときに使うツールとして、 GNU Assembler (GAS) と Netwide Assembler (NASM) の 2 種類がメジャーですが、本書では標準で Intel 記法を採用している NASM を使って解説を進めていきます。
シェルコードは基本的に外部ライブラリを使わずにアセンブラを書くため、私たちが普段使っている puts
や printf
、 fgets
といった標準ライブラリ (libc) の関数を呼び出すことはありません。その代わりに、アセンブラから直接システムコールを使って OS の機能を直接呼び出すことになります。
システムコール
機械語から CPU に対して割り込みをかけることで OS の機能を呼び出すことができます。システムコールには非常に多くの種類があり、入出力を行う read
, write
を始め、異なるプログラムを起動する execve
やファイルをオープンする open
などもシステムコールで提供されている機能です。
これらの機能は、レジスタに値をセットしたあと、システムコール命令を実行することで呼び出すができます。例えば、x86 環境で標準入力からアドレス 0x804b000
に 0x100 バイトの文字列を読み込む場合は、次のようなアセンブラを書き機械語にアセンブルします。
mov eax, 3 mov ebx, 0 mov ecx, 0x804b000 mov edx, 0x100 int 0x80
勘のよい方は既にお気づきかも知れませんが、 ebx, ecx, edx
は read
の 3 つの引数になっていて、最後の int 0x80
が CPU に対するシステムコールの割り込み処理です。eax
を 3
にセットしていますが、これが多くの種類のシステムコールを区別するためのシステムコール番号となっています。すべてを列挙すると使わないものも多く含まれるため、CTF でよく使うシステムコールの番号一覧を表にしました。
種別 | x86 | x86_64 | 引数1 | 引数2 | 引数3 |
---|---|---|---|---|---|
read | 3 | 0 | fd | buf | count |
write | 4 | 1 | fd | buf | count |
execve | 11 | 59 | filename | argv | envp |
dup2 | 63 | 33 | old | new | - |
mprotect | 125 | 10 | start | len | prot |
また、 x86 と x86_64 においてもシステムコールの呼び出し方に差異が存在するため、システムコールの引数と命令の対応も表にまとめました。
アーキテクチャ | 命令 | 番号 | 引数1 | 引数2 | 引数3 | 引数4 |
---|---|---|---|---|---|---|
x86 | int 0x80 | eax | ebx | ecx | edx | esi |
x86_64 | syscall | rax | rdi | rsi | rdx | r10 |