• 検索結果がありません。

ソフトウェアのリスク分析

ドキュメント内 Sapidによるソフトウェア解析技法 (ページ 96-104)

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 

av[]){ char str[ 20]; 

if(argc < 2)  retum;  strcpy(str. "aaa"); 

prin("%s¥n"s廿); strcpy(str. av[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 

#deneMAX 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("./ critiI̲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)であるオプジエクト取得 if町伶【(spdGωetAtt廿rVa'alInt((id̲arr阻町aay.idd[i]

amc

̲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̲SUCCS);

96  Sapidによるソフトウェア解析技法 実行結果(一部)

Function *. orint

* ・

are used.  Function *. Scanf** are used. 

E  バ ッ フ ァ オ ー バ ー フ ロ ー

バッファオーバーフロー(ラン)による攻撃は、メモリ操作関数の脆弱性を 突く。バッファをオーバーフローさせ、あふれた領域に

/ b i n / s h

といったルー

トを乗っ取るプログラムを組み込み、ルート権限で実行させる。

バッファオーバーフローの可能性を調べるために、メモリ操作関数と標準入 力や環境変数からのデータ入力の組み合わせを調べる。リスク分析プログラム は、危険な関数を呼び出す式中の変数に注目し、値の代入や実行の制御関係な どについて検査する。

2.

オーバーランの原因となる変数の出現を求める

危険な関数を呼び出す式の中で、バッファオーバーランをもたらす変数を対 象としてトレースを行う。ここでは関数strcpyのみを対象とする。関数strcpy

の第一引数arg1がターゲットであり、引数arg2のサイズがarg1のサイズを超 えるとバッファオーバーフローとなる。

│ 

strcpy(arg1arg2):文字列'arg2を 変 数arg1に 代 入 (,文字型のポインタ)

サンプルプログラム6‑

2

‑ターゲットとなる変数の出現を求める

アルゴリズム

1.  関数

s t

pyの出現を求める

求めた各式中で、第2引数の識別子オブジヱクトを取得

3 .  

式の情報を出力する

#inc1ude <stdio.h> 

#inc1ude <Sapid/Sapid.h> 

SpdObjld get̲N̲arg(SpdObjld, int);  void openModel(SpdDBld);  int spd1leUine(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=spdGetR.elωObIjlrn1it(idd

̲arπrray

μ

.id[]l"ident

̲ref

γ 

an)

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(identr);

関数 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""pid"NULLspdCompareRelOffset);  r(i=αirel  aπay.size; i++)

一 一

if(i == n) 

return reLarray.rel[i].objectId; 

実行結果

2nArg of 'strcpy(str, "aaa")'at # 8ー >"aaa" 

2nArg 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

av[]){

chars[20]s1[20]str2;"aaa";  if(argc 2) 

strcpy(str 1, "bbb");  eJ.se 

S廿cpy(str1, av[1]); 

rcpy(sstr1);  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)

spdlleCline(expr̲id));  printf(" %s¥n" , spdGetObjT,剖(arg.Jd));

}設

a e e

V:(expr̲id2); spdFeCu同町r(idenCr);

関数

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̲idQ); 

fname‑=Jd ;~ sp

ぬほ.e

lObj(e

, q ,

rU d, "idenUefブ 問 な

/ / stpy関数の呼び出しなら、ターゲット変数の位置は2番目、他の関数は省略 '(s廿cmp(s凶GetNamenamcid)"strcpy");;Q)

aYarCheck(expr̲id, 2); 

/ 1

ユーザ定議関数制乎び出しについては、ターゲyト変数の位置をパラメータ1として持つ else 

tra

, 田

YarCheck(expr̲id

i); 

6章 ソフトウェアのリスク分析 99 

関数 t r a c e V a

rC

h 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;  expr 

L 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(expr2id.0)) != SAPID̲NON̲ID){ 

if((idenUd  ;spdGeRelOoj(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 = %sn". 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]  Vable=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 ターゲットとなる変数を含む式の出現を求める

intgetExpssionList(SpdObjldvaUd, SpdObjld expr̲id, SpdObjldobLarray){ SpdOldArrayexpr ̲array; 

SpdOcc idenCo町;

SpdCursoridenCr int  ij=O; 

~xp~~町~yspdGetObjI~r~y(':e~ression", NULL

, 

NULL); 

for (i ~ expr̲array 割 問 ;i++) { 

if(spdGetAReIObj(expr̲array,id[i],"b expr"、,ny")!=SAPID̲NON̲ID){

idetr= sp(\beÍIncludédOëcÍnit(匂r~array.1dîi],"identffier", o); 

whiI((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, charav[]){

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中の変数のトレース

ドキュメント内 Sapidによるソフトウェア解析技法 (ページ 96-104)