94
8 a p i d
によるソフトウェア解析技法れる。プログラム全体のコードの走査、相互の関連によるセキュリティ・チェ ックは困難な作業である。チェックが行われるのも、変更が難しい最終フェー ズである場合が多く、効率的なソフトウェア開発と厳密なセキュリティは両立 しにくい
Sapid
はソフトウェア開発者のセキュリティに対する取り組みを支援し、ソ ースコードに基づく良質な検査ツールの開発を可能にする。セキュリティ・チ ェック後のコード変更がもたらす影響を分析するために、式や変数の値や依存 関係の走査、検証も容易に行うことができる。1 . 2
危険な関数の出現( p r i n t f
、s c a n f
など)危険な関数をピックアップし、クラス
i d e n t i f i e r
を用いて当該関数の出現を チェックする。関数当該関数名のリストをファイルc r i t i c a l ̲ F u n c
に格納して おく。ターゲットプログラム1
は関数p r i n t f
、s t r c p y
といった危険な関数を持 つ。s t r c p y
は典型的なセキュリティホールであるバッファオーバーランの原因 となる。p r i n t f
がもたらすセキュリティホールには、FormatS t r i n g A t t a c k
といった 攻撃がある。FormatS t r i n g Attack
は、p r i n t f
の書式指定子列の弱点を突き、標準入力からの入力で、
%x
といった書式指定子列を入力することでクラッキ ングを行う。FormatS t r i n g A t t a c k
の代表的な例として、wu
・f t p d
の脆弱性が ある。wu
・f t p d
が デ バ ッ グ モ ー ド で 実 行 さ れ て い る と き 、 標 準 入 力 がsyslogO
命 令 経 由 で ロ グ フ ァ イ ルs y s l o g
にメッセージを書き出す点がセキュリティ・ホールとなる。また、関数
p r i n t f
は、標準出力に変数の値を書き出すことから、重要な情報の漏洩といった危険性が存在する。
メモリ操作関数
memcpy
,s t r c p y
,s p r i n t f
,s s c a n f
入力関数s c a n f
,r e a d
,g e t c
,g e t w
,g e t s
出力関数p r i n t f
,w r i t e
,p u t c
,putw
,p u t s 08
呼び出し関数等s y s t e m
,s e t u i d
,s e t g u i d
ファイル処理関数f p r i n t f
,f s c a n f
ネットワーク等 Ii
s t e n
,c o n n e c t
,r e a d
,w r i t e
[危険な関数一覧]解析対象プログラム6‑
1
int main(int argc. char
・
a培v[]){ char str[ 20];if(argc < 2) retum; strcpy(str. "aaa");
prinぜ("%s¥n",s廿); strcpy(str. a培v[tD;
prinぜ("%s¥n
ぺ
str);第
6
章 ソフトウェアのリスク分析 95サンプルプログラム6‑
1
一危険な関数の出現を求める アルゴリズムあらかじめ、危険な関数の名前のリストをファイルcritical Funcに格納しておく。
1.クラスidentifierで属性50此が関数
αD]UNCTION)
であるものを選ぶ 2.危険な関数のリストと比較3.危険な関数であった場合は情報を出力
#include <stdio.h>
#incJude <Sapid/Sapid.h>
#defineMAX NUMBER 100
#de白neMAX FUNC NAME 100 void openModeI(SpdDBId);
?tmam{i山 gc.伽 普 押 日 )
} }
SpdObjIdArray id̲array; SpdObjId funcid; int i, j. func̲num=α char tmp[MAJCFUNC̲NAME];
char func̲list[MAX̲NUMBER][MAJCFUNC̲NAME];
SpdDBId dbId;
FlLE • fp=fopen("./ criti四I̲Func...r..); dbld = spdOpenSDBO;
openModeI(dbld);
while (fscanf(fp,"%s".tmp) != EOF) strcpy(func̲list[func̲num++ ].tmp);
id a町'ay= spdGetObjldArray("identifier". NULL. NULL);
for (i = 0; i
<
id3rray.size; i++) {//クラスidentifi町 、 属 性 曲 此 が 関 数 UD
一
FUNCfπION)であるオプジエクト取得 i尚f町伶【(s凶pdG伽ωe剖t凶At朗士抗伽t廿r川Va'alIn此川t凶凶((id̲a町rr阻町aay.i叫dd引仰刷[口i]釦am陀c
一
̲id司=id一
a町'a匹μy.id[i];forU =匂j< func̲num‑1; j++) { /ソ危険な関数のリストと比較
if(strcmp(func̲list[j].spdGetName(func̲id))== 0) prinぜ("**%5** are used¥n".func̲list[j]);
spdFreeObjldArray(id̲array); retum (ロIT̲SUCC回S);
96 Sapidによるソフトウェア解析技法 実行結果(一部)
Function *. orint
* ・
are used. Function *. Scanf** are used.E バ ッ フ ァ オ ー バ ー フ ロ ー
バッファオーバーフロー(ラン)による攻撃は、メモリ操作関数の脆弱性を 突く。バッファをオーバーフローさせ、あふれた領域に
/ b i n / s h
といったルートを乗っ取るプログラムを組み込み、ルート権限で実行させる。
バッファオーバーフローの可能性を調べるために、メモリ操作関数と標準入 力や環境変数からのデータ入力の組み合わせを調べる。リスク分析プログラム は、危険な関数を呼び出す式中の変数に注目し、値の代入や実行の制御関係な どについて検査する。
2.
1
オーバーランの原因となる変数の出現を求める危険な関数を呼び出す式の中で、バッファオーバーランをもたらす変数を対 象としてトレースを行う。ここでは関数strcpyのみを対象とする。関数strcpy
の第一引数arg1がターゲットであり、引数arg2のサイズがarg1のサイズを超 えるとバッファオーバーフローとなる。
│
strcpy(arg1,arg2):文字列'arg2を 変 数arg1に 代 入 (,文字型のポインタ)サンプルプログラム6‑
2
‑ターゲットとなる変数の出現を求めるアルゴリズム
1. 関数
s t
問pyの出現を求める2 求めた各式中で、第2引数の識別子オブジヱクトを取得
3 .
式の情報を出力する#inc1ude <stdio.h>
#inc1ude <Sapid/Sapid.h>
SpdObjld get̲N̲arg(SpdObjld, int); void openModel(SpdDBld); int spd‑‑1leUine(SpdObjld);
?tmmht叩 ,char毎岬[)) SpdObjldArray id̲array;
SpdObjld eicpUd, func̲id, ar.ιid; SpdCursor
・
idenC由民irit i, argNum;
SpdDBld dbld;
第6章 ソフトウェアのリスク分析 97
出 品 2 1 2 1 Z S ? B ( ) ,
id̲array = spdGetObjldArray("identifier", NULL, NULL);
for (i = 0; i
< :
id̲array.size; i++) {if(spdGetAttI・'Vallnt(id̲array.id[i],"回rt")==ID]UNCTION) / / 1.関動廿cpy()の出現を求める
if(strcmp(spdGetName(id̲aπay.id[i])
、 ,
trcpy")== o){ident 田r=s凶pdGetR恥.elωO切bIjl加r羽刷n1吋耐巾i江t(i叫dd
一
̲arπrr匂ay匹μ
.id[凶,]l日"i白de凶nt一
̲refγ
a叩n)ダf
川,"乃');,
while ((funcid= spdGetRelObj(idenccsr)) !=SAPID̲NON̲ID){expr̲id = spdGetARelObj(func̲id,百xpr̲exprγc‑id");
/ / 2 .
求 め た 各 式 中 で 、 引 数 の 情 報 を 取 得 島 町pyの場合は第2
引 船 argNum = 2;arg.jd = geCN̲arg(expr̲id, argNum);
/ / 3 .
it叫育事置を出力するprintf("¥n%dth‑Arg of¥'%s¥' at #%dーー>",argNum,
spdGetObjText(expr_id),s凶~eCline(expr_id));
printf(" %s¥n" , s凶GetObjText(arιid));
VJ
笥Fa
引
c
h ‑間m m
仙川 一
鍵
e (間 百
釘加
p
e
︑ ︐
JgoiFA
}
spdFreeCursor(ident田r); }
関数 get̲N̲argv ‑N 番目の関数引数を求める(N =O の場合は関数名)
SpdObjld geCN̲arg(SpdObjld expr̲id, int n){SpdRelArray rel̲array; Int i;
rel̲array=spdGetRelSortedArray(expr̲id・',expr̲expr","p‑id",NULL,spdCompareRelOffset); 而r(i=αi< rel aπay.size; i++)
一 一
if(i == n)
return reLarray.rel[i].objectId;
実行結果
2nd Arg of 'strcpy(str, "aaa")'at # 8ー >"aaa"
2nd Arg of 'strcpy(str
,
argv[ 1]);' at # 1ト ー >argv[l]2 . 2 ターゲットとなる変数を含む式の解析
2.1 で求めた式を解析し、第 1 引数の値がどのように決定されるかを解析す
るため、関数 s t r c p y の 第 2 引 数 arg2 の 値 を 調 べ る 。 サ ン プ ル プ ロ グ ラ ム 6 ‑ 1
で求めた式の第 2 引数が変数であれば、その変数の出現する式を解析すること
となる。定数や関数定義のパラメータ変数であれば、その式はデータ依存関係
にある式は持たない。また、第 2 引数が配列や式であるときは、再帰的に解析
し、変数や定数を求める。
98
S a p i d
によるソフトウェア解析技法当該式が
main
関数以外の関数定義文中にあり、その関数定義のパラメータ を第2
引 数 と し て 持 つ 場 合 は 、 関 数 に ま た が る 解 析 が 必 要 と な る 。 ま たmain
関数のパラメータであれば、標準入力へのデータ依存となり、脆弱性の原因と なる。関 数
t r a c e V a r
の第2
引数argn
は、解析する関数中のデータ依存パラメータ の位置を示す。S t r c p y
の場合の位置は2
である。s p r i n t f
といった複数のデー タ依存パラメータを持つ関数についての解析は省略する。解析対象プログラムら
2
int main(int argc, char
・
a培v[]){chars仕[20],s甘1[20],str2;"aaa"; if(argc < 2)
strcpy(str 1, "bbb"); eJ.se
S廿cpy(str1, a曙v[1]);
坑rcpy(s甘,str1); pririぜ'("%5¥n",str); strcpy(str, s廿2); printf("%s¥n"
,
str); return:サンプルプログラムか
3 main
関数(変更部分のみ)prinぜ【"¥n%dth‑Argof ¥'%s¥' at #%dー>",argNuffi,
spdGetObjText(expr̲id)
,
spd‑lleCline(expr̲id)); printf(" %s¥n" , spdGetObjT,剖(arg.Jd));}設
a e e
V:貯(expr̲id,定2); spdF陀eCu同町r(idenC回r);関数
t r a c e V a r
ターゲットとなる変数を含む式の解析(関数ごとの前処理)SpdObjld t回 目Yar(SpdObjldexpUd, int i){ SpdObjld expr U d
,
fnamcid;expr1ー.id;get̲N̲arg(expr̲id,Q);
fname‑=Jd ;~ sp
ぬほ.e
lObj(e, q ,
rU d, "idenUefブ 問 な/ / st民py関数の呼び出しなら、ターゲット変数の位置は2番目、他の関数は省略 百'(s廿cmp(s凶GetName仔namcid),"strcpy");;Q)
甘a回,YarCheck(expr̲id, 2);
/ 1
ユーザ定議関数制乎び出しについては、ターゲyト変数の位置をパラメータ1として持つ elsetra
, 田
YarCheck(expr̲id,
i);第6章 ソフトウェアのリスク分析 99
関数 t r a c e V a
rCh e c k ‑ターゲットとなる変数を含む式の解析(依存関係を求める)
#define CONST 0 / /定数
#define EXPR 0 / /式
#define UNKNOWN / /不明
SpdObjld 岡 田Varcheck(SpdObjldexpr ̲id. int argn){
}
SpdObjld expr Lid. expr 2̲Jd. exprArg ..
. . i
d. expr 3̲id. idenCid. var̲id. const̲id; exprL i
d = spdGetARelObj(expr̲id. "expr̲expr".予id");expr 2̲id =geCN̲arg(expr̲id.argn); / /定数の場合
if((consCid =spdGetARelObj(expr2̲id. "consCref
、 , ・
ny"))!= SAPID̲NON̲ID){printf("¥tConstant Value = %s¥n". spdGetAttrValString(∞nsCid."value")); return 0;
}
/ (変数の場合 唱
else if((v町 国=spdGetARelObj(expr2̲id. "idenCrefγany")) != SAPID̲NON̲ID){
printf("¥tVariable = %s¥n". spdGetName(var̲id)); return var ~id;
} /(~拐の場合
else if((exprArg...id =geCN̲arg(expr2ーid.0)) != SAPID̲NON̲ID){
if((idenUd ,;spdGe凶RelOoj(exjlrArg...id."idenCre
、 久
‑id"))!=SAPID̲NON̲ID) if(s凶GetA枕rValInt(ident̲id."sort")==ID̲VARIABLE){}
printfC'¥tVariable = %s明".spdGetObjText(expr z.....id)); return idenCid;
else{
printf("¥tExpression = %s¥n". spdGetObjText(exprLid)); return EXPR;
} return UNKNOWN;
}
実行結果(部分)
2th‑Arg of 'strcpy(str, str2)' at # 13一回>str2 Vanable=slr2
2th‑Arg of 'strcpy(str, s廿J)'at引 lー >str!
Variable=str 1
2th‑Arg of 'strcpy(str 1. argv[ 1])' at # 9ー >argv[ 1] Va巾ble=argv[1]
2th‑Arg of
・
strcP}o(strJ,"bbb")' at # 7‑‑‑> "bbb:' Constant="bbb")2 . 3 ターゲットとなる変数を含む式の出現を順番に求める
第 2 引数が変数の場合は、直接、データ依存関係にある式を求める。当該変 数の出現する式を選択し、制御構造とともに実行順を調べる。文字型へのポイ ンタを引数とする関数呼び出し式を含む式を調べることとなるが、 s t r c p y のみ を扱うものとする。
制御フロー中で、前に出現する s t r c p y 式 を 取 得 し 、 当 該 変 数 が 第 1 引数とし
100 Sapidによるソフトウエア解析技法
て現れるものを求める。どの文に依存して実行/不実行が決まるかといった 点も調べる。
strcPy(x2,x3)
~ data̲depend 制御の流れ
i ¥
strcpy(xl,x2)
関数getExpressionList ターゲットとなる変数を含む式の出現を求める
intgetExp問ssionList(SpdObjldvaUd, SpdObjld expr̲id, SpdObjld骨obLarray){ SpdO均ldArrayexpr ̲array;
SpdOcc idenCo町;
SpdCursor骨idenC田r int i,j=O;
}
~xp~~町~y= spdGetObjI~r~y(':e~ression", NULL
,
NULL);for (i = ~ i < expr̲array 割 問 ;i++) {
if(spdGetAReIObj(expr̲array,id[i],"b肱 expr"、,ny")!=SAPID̲NON̲ID){
ide泊t田r= sp(\beÍIncludédOëcÍnit(匂r~array.1dîi],"identffier", o);
whiIe ((idenCocc = spdGetIncludedOcc(idenccsr)‑lrelationld !=SAPID̲NON̲ID) if (idencocc.objectld==var̲id && expr̲aπay.id[i]!=expUd){
obLarray[j++] = expr̲array.id[i]; }
spdFreeObjldArray(expr̲array); returnj;
皿 バ ッ フ ァ オ ー バ ー フ ロ ー (2)
strcpy関数を用いる式中の変数をトレースし、最終的に値を決定している式
の中で、標準入力から値を得ているかどうか調べる。 Sprintfといった他のメ モリ操作関数についても同様な方法でトレースが実現できる。
Format String Attackの脆弱性については、 printf中の変数に、標準入力等 の入力で、
%x
といった書式指定子の文字列が入力されたかどうかを判断する。リモートスタックオーバーフローは、ネットワークアクセス関数で入力される データ列のサイズチェックの有無を調べる。
解析対象プログラムか3 void func(char *arg){ char str[ 1 0];
strcpy(str ,arg); priritf("%s¥nιstr); int main(int argc, char骨a唱v[]){
chars仕[20],S廿1[20]; if(argc < 2) strcpy(str, "bbb");
else strcpy(str, argv[ 1]); strcpy(str 1, s甘);
func(s廿1); return; }
3.1 関数strcpy中の変数のトレース