セキュアコンピューティング

分散システム

                                       電子・情報工学系
                                       新城 靖
                                       <yas@is.tsukuba.ac.jp>

このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/coins/dsys-2005/2006-01-17
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.cs.tsukuba.ac.jp/~yas/

バッファ・オーバーフロー

PowerPoint の資料参照

カナリアの飛び越え

ポインタによる攻撃。

snprintf を使った文字列のコピーと結合

   1:	/*
   2:	        strcat-snprintf.c -- snprintf を使った文字列のコピーと結合
   3:	        ~yas/syspro/string/strcat-snprintf.c
   4:	        Created on: 2006/01/17 03:17:24
   5:	*/
   6:	
   7:	#include <stdio.h> /* stderr, snprintf() */
   8:	#include <stdlib.h> /* malloc() */
   9:	#include <string.h> /* strcat() */
  10:	
  11:	
  12:	main( int argc, char *argv[], char *envp[] )
  13:	{
  14:	        if( argc != 2 )
  15:	        {
  16:	            fprintf(stderr,"Usage:%% %s string\n",argv[0] );
  17:	            exit( 1 );
  18:	        }
  19:	        good( argv[1] );
  20:	        bad( argv[1] );
  21:	}
  22:	
  23:	#define DOCUMENT_ROOT "/usr/local/httpd/htdocs/"
  24:	#define BUFFSIZE 100
  25:	
  26:	bad( char *s )
  27:	{
  28:	    char buf[BUFFSIZE];
  29:	    int fd ;
  30:	        strcpy( buf,DOCUMENT_ROOT );
  31:	        strcat( buf,s );
  32:	        printf(" bad: open(\"%s\", O_RDONLY )\n",buf );
  33:	}
  34:	
  35:	good( char *s )
  36:	{
  37:	    char buf[BUFFSIZE];
  38:	    int req ;
  39:	        req = snprintf( buf,sizeof(buf),"%s%s",DOCUMENT_ROOT,s );
  40:	        if( req >= sizeof(buf) )
  41:	        {
  42:	            fprintf(stderr,"good: buffer overflow attack detected.\n");
  43:	            fprintf(stderr,"required: %d, actual: %d, strlen: %d, [%s]\n",
  44:	                    req, sizeof(buf), strlen(buf), buf );
  45:	            return( 0 );
  46:	        }
  47:	        printf("good: open(\"%s\", O_RDONLY )\n",buf );
  48:	        return( 1 );
  49:	}

snprintf() の引数

snprintf() は、決してバッファ・オーバーフローを起こさない。 結果として、必要なバイト数(最後の0は含まない)を返す。 これが、バッファよりも少なければ成功である。 等しいか大きい時には、後ろが切られる。

snprintf() は、%s 以外に %d や %c も使える。

注意:Linux (Glibc を使っている)では、snprintf() の仕様が変更された。 古い仕様(glibc 2.0.6以前)では、snprinf() は、エラーが起きると -1 を返 す。新しい仕様(glibc 2.1以降)では、snprintf() は、必要なバイト数、すな わち、バッファが無限大であるときに書き込まれる文字列の長さ(最後の0を除 く)を返す。これは、strlen() が返すものと同じである。従って、新しい snprintf() では、結果とバッファの大きさを比較して正確に書き込まれたか を検査する。

古い Unix や古い Linux では snprintf は sprintf を呼び出しているだけのことがあり、安全ではないことがある。 coins の環境は問題ない。

snprintf() は、決してバッファ・オーバーフローを起こすことはないが、バッ ファが足りない時には意図していない結果が保存されていることになる。後ろ が切れてもよい場合には、リターンされた結果を比較しない方法がある。 (void とも書かなくてもよい。)

	(void)snprintf( buf,sizeof(buf),"%s%s",DOCUMENT_ROOT,s );

実行例:

% cp ~yas/syspro/string/strcat-snprintf.c . [←]
% make strcat-snprintf [←]
cc     strcat-snprintf.c   -o strcat-snprintf
% ./strcat-snprintf index.html [←]
good: open("/usr/local/httpd/htdocs/index.html", O_RDONLY )
 bad: open("/usr/local/httpd/htdocs/index.html", O_RDONLY )
% ./strcat-snprintf 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 [←]
good: buffer overflow attack detected.
required: 124, actual: 100, strlen: 99, [/usr/local/httpd/htdocs/012345678901234567890123456789012345678901234567890123456789012345678901234]
 bad: open("/usr/local/httpd/htdocs/0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789", O_RDONLY )
Segmentation fault
% []

CGIの考え方

WWW サーバは、普通は、ファイルに保存された HTML データやイメージ・データを 読み込み、WWWブラウザに返する。 CGI (Common Gateway Interface) では、ファイルが読み込まれる代わりにプログラムが実行され、その実行結果 がブラウザに返される。

Gateway とは、WWW で使われている HTTP というプロトコルへの入り口という 意味である。 プログラムの実行結果は、普通は、HTML にすることが多いが、普通のテキス トであることもイメージであることもある。

クライアント、WWWサーバ、CGIによるプロセス、ファイル

図? CGIの仕組み

CGI の利用例

JavaScript

JavaScript は、WWW ブラウザ上で動作するスクリプト言語である。

JavaScript は、文法が少し Java 言語に似ているが、Java とはまったく別の 言語である。WWW ページの中の Javaアプレットは、 「実行可能なインライン・イメージ」に似ている。これに対して JavaScript の記述は、「実行可能なインライン・テキスト」 に似ている。

JavaScrip のプログラムの例:

<SCRIPT LANGUAGE="JavaScript">
<!--
for( i=0 ; i<10; i++ )
  document.writeln("<P>hello,world</P>");
//-->
</SCRIPT>

これは、<P>hello,world</P> を 10 回書いた のと同じ効果がある。<!--//->は、JavaScriptを知らないブラウザにはコメント として扱われる。関数定義などは、ヘッダ部分 <HEAD></HEAD>に書くという方法もよく使われる。

JavaScript の言語としての特徴

JavaScript によるブラウザの制御

JavaScript の記述は、CGI と似ているところもあるが、JavaScript でないと できないものに、ブラウザの制御がある。たとえば、次の例では、ブラウザの (戻る)ボタンと同じ動きをさせることができる。

<A HREF="javascript:history.back();">戻る</A>

次の例は、<FORM></FORM>からパラメタを受け取るも のである。

<SCRIPT LANGUAGE="JavaScript">
function go(s,h,p)
{
	location.href = s.value + "://" + h.value + p.value ;
}
</SCRIPT>

<FORM NAME="form1">
<INPUT NAME="scheme" TYPE="text" VALUE="http"><BR>
<INPUT NAME="host"   TYPE="text"><BR>
<INPUT NAME="path"   TYPE="text" VALUE="/"><BR>
<INPUT TYPE="button" VALUE="go" onClick="go(form1.scheme,form1.host,form1.path)">
</FORM>

onClick 属性の値は、クリックした時に評 価される式であり、関数 go() が呼び出されている。引 数は、<FORM> の値である。関数 go() の中では、 .value フィールドから値が読み出されている。 location.href に代入することで そのページを表示させることができる。

この例では、<INPUT type="text"><INPUT type="button">が使われている。その他に、 <FORM>では、<INPUT TYPE="radio"><INPUT TYPE="checkbox"><SELECT>が使える。イベントとしては、 onClick が主に使われる。その他に、 onFocus が使われることもある。

JavaScript では、document.open() で新しく HTML の ドキュメントを生成して、それをブラウザに表示させることもできる。

クロスサイトスクリプティング攻撃

WWWブラウザでは、信頼しているサイトから送られてくるJavaScriptのプログ ラムだけを実行するようにし、攻撃サイトから送られてくるJavaScriptのプロ グラムを実行しないようにしたい。

WWW ブラウザ、攻撃サイト、信頼しているサイト、JavaScriptのプログラム

図? JavaScripの送信元サイトの区別

脆弱性があるサイトでは、攻撃サイトから送られてきた JavaScript のプログ ラムを中継してしまう。

WWW ブラウザ、攻撃サイト、脆弱性のあるサイト、JavaScriptのプログラム

図? JavaScripの送信元サイトの区別

これがクロスサイトスクリプティング攻撃(cross-site scripting atack, XSS atack)である。

CGI のプログラムをつくる時には、クロスサイトスクリプティング攻撃に気を つける。これは、クライアントから送られてる文字列の中に <SCRIPT>のようなタグが含まれていた場合、それをそのままクラ イアントに送り返すと問題がある。

クライアントから送られてきた文字列は、必ず検査し、安全な状態にして (sanitize)から使う。「<>&"」のようなタグが含まれている場合 には注意する。このような文字列を受け取った場合、不用意に送り返してはい けない。送り返す時には、必ず次のように変換する。

----------------------------------------------------------------------
画面表示	代りに送りだす文字列
&		&amp;
<		&lt;
>		&gt;
"		&quot;
----------------------------------------------------------------------

その他の脆弱性と攻撃

format string bug

[format.c]
   1: 
   2: /*
   3:         f o r m a t .c -- printf() 系の危険な利用方法
   4:  */
   5: main(int argc, char *argv[])
   6: {
   7:         printf(argv[1]);
   8: }

% wget http://www.coins.tsukuba.ac.jp/~yas/coins/dsys-2005/overflow/format.c [←]
% make format [←]
cc     format.c   -o format
% ./format aaa [←]
aaa% [←]
% ./format "%s%s%s%s"  [←]
Bus error
% []
printf() の には、外から来た文字列を与えてはならない。 syslog(3) も同様。

シンボリックリンク攻撃

/tmp や /var/tmp には、誰でも書き込める。テンポラリ・ファイルが作られ る。

そのような誰でも書き込める場所に不用意にのファイルを作るプログラムはシ ンボリックリンク攻撃に対して脆弱性を持つ。

特に危険なものは、root の権限で動作させるプログラム。set-uid だけでな く、root の作業でよく利用するプログラムにも注意する必要がある。

しかし、lstat() で調べるだけでは不十分。

TOCTOU (Time to check to time to use)

チェックした後、書き換えられる可能性がある。
if( check(filename) )
	    fd = open(filename,O_RDONLY)
check() と open() の間に、check() の状態が変化してしまうことがある。

他のプログラムの実行

CGI では、意図していないプログラムを実行しないようにする。

他のプログラムの実行(C言語)

他のプログラムを実行する時には、execve() のようなシステムコールを使い、 かつ、限られたプログラムしか実行しないようにすると安全性が高くなる。ク ライアントから送られてきた文字列をsystem() や popen() に渡してプログラ ムを実行する時には、必ず検査する。特にシェルが解釈する特殊な文字 「| & ; && || `」などが含 まれていた場合、意図しないプログラムが実行されることがある。

char *user ;
...
snprintf(cmd,BUFSIZE,"finger %s",user );
f = poepn(cmd,"r");
もし、user に ";""|" が含まれていたら、、、 
f = poepn("finger yas; /bin/sh","r");

他のプログラムの実行(Perl言語)

Perl には、バッファ・オーバーフローの問題はない。

perl の open() には、危険性がある。 C 言語のライブラリ関数 popen() と同じ動きをすることがある。

open(FILE, "|cmd")
Perl の危険な関数、式、

参考

産業技術総合研究所 グリッド研究センター セキュアプログラミングチーム
http://securit.gtrc.aist.go.jp/
)

IPA セキュア・プログラミング講座
http://www.ipa.go.jp/security/awareness/vendor/programming/
)


Last updated: 2006/01/17 21:21:48
Yasushi Shinjo / <yas@is.tsukuba.ac.jp>