前回は0x200のプログラミング基礎だったので、今回は0x300のバッファオーバーフローを進めていきます。
前章で扱ったプログラムに攻撃を仕掛けに行く展開で面白い。shellcodeはデコードしてもよく分からなかったので次へ。
次はstrcpy()を利用したバッファオーバーフローでした。コマンドラインから文字列を受け取って、特定の文字列と一致していれば認証、みたいなプログラムです。
check_authentication()はauth_flagを戻り値にする関数であり、main関数からは、
if(check_authentication(argv[1])){printf("ok");}
という形で呼び出されて利用されます。
この時、ifは「0でなければ」を判断しているため、何らかの方法で戻り値になる初期値0のauth_flagが、0以外に書き変わっていれば通ってしまう形になります。
check_authentication()内部ではstrcpy()を利用して文字列をバッファ用の配列にコピーし、strcmp()を使って比較します。
実行結果ですが、入力文字列が29文字以上の場合どのような文字列を入力しても認証成功となり、40文字以上だとif-else文それぞれに入っていたprintfが実行されなくなりました。
29文字以上で認証成功については、authentication()内で宣言したint型変数auth_flagとchar型配列password_buffer[16]のメモリの位置が関係しているようです。
int auth_flag=0;
char password_buffer[16];
の順で宣言した結果、ポインタはそれぞれ
&auth_flag 0xffffcb5c
&password_buffer 0xffffcb40;
となり、メモリの状態は
(gdb) x/20wx 0xffffcb40
0xffffcb40: 0x8021bf56 0x00000001 0x8018949a 0x00000001
0xffffcb50: 0x0003a1a0 0x00000006 0xffffcbe0 0x00000000
(省略)
でした。
(gdb) x/20wx 0xffffcb40
0xffffcb40: 0x30303030 0x30303030 0x30303030 0x30303030
0xffffcb50: 0x30303030 0x30303030 0x30303030 0x30303030
0x30はアスキーコードで0であることを踏まえると、auth_flagは29文字の0に埋め立てられてしまい、0以外になってしまっているようです。想定よりも長い文字列でのstrcpyによって、書き換えられた模様。
(宣言した順とは逆にメモリに配置されているようです)
29文字以上で書き換えという点については、二つの変数の差を確認すると
(gdb) print 0xffffcb5c-0xffffcb40
&1=28
という結果になるようなので、きちんと理由があることが確認できました。
今度は40文字以上の"0"を実行すると何も出なくなる現象について確認。
実行するとreturn auth_flagでアクセス違反が発生していました。指定アドレスが0x3030303030303040という謎の値に。
このタイミングで他の変数も確認しましたが、
(gdb) p &auth_flag
$2 = (int *) 0x303030303030302c
(gdb) p &password_buffer
$3 = (char (*)[16]) 0x3030303030303010
とポインタそのものが書き変わっている感じになっていました。
Segmentation fault前後のレジスタ状況とメモリを確認するとベースポインタが書き換えられていることを確認。
Segmentation fault前
(gdb) i r
rax 0x17 23
rbx 0xffffcbe0 4294953952
rcx 0x600018040 25769902144
rdx 0x0 0
rsi 0x60003a140 25770041664
rdi 0x8000 32768
rbp 0xffffcb60 0xffffcb60
rsp 0xffffcb20 0xffffcb20
r8 0xffffc85c 4294953052
(gdb) x/20wx 0xffffcb20
0xffffcb20: 0xffffcc65 0x00000000 0xffffcb40 0x00000000
0xffffcb30: 0xffffc85c 0x00000000 0x8013e970 0x00000001
0xffffcb40: 0x8021bf56 0x00000001 0x8018949a 0x00000001
0xffffcb50: 0x0003a1a0 0x00000006 0xffffcbe0 0x00000000
0xffffcb60: 0xffffcb90 0x00000000 0x004011e1 0x00000001
Segmentation fault後
(gdb) i r
rax 0x303030b0 808464560
rbx 0xffffcbe0 4294953952
rcx 0x600018040 25769902144
rdx 0x0 0
rsi 0x60003a140 25770041664
rdi 0x8000 32768
rbp 0x3030303030303030 0x3030303030303030
rsp 0xffffcb70 0xffffcb70
r8 0xffffc85c 4294953052
(gdb) x/20wx 0xffffcb20
0xffffcb20: 0xffffcc65 0x00000000 0xffffcb40 0x00000000
0xffffcb30: 0xffffc85c 0x00000000 0x8013e970 0x00000001
0xffffcb40: 0x30303030 0x30303030 0x30303030 0x30303030
0xffffcb50: 0x30303030 0x30303030 0x30303030 0x30303030
0xffffcb60: 0x30303030 0x30303030 0x00401100 0x00000001
スタック周りにダメージが来ているようでした。rbpはbreak mainして確認した時から本来変わらない値であるはずなのに、変化してしまっていることがプログラムが落ちる原因だったようです。
(p $rbp-$rspはmain関数時点では32,check_authentication関数に入ると64でした)
なお、auth_flagとpassword_bufferの位置を逆にしたところ、auth_flag上書きによるif分強制trueはありませんでした(password_bufferよりauth_flagの方が先にあるので上書きが発生しない)。
ただし、 24文字以上でrbpが上書きされ、アクセス違反になりました。
書き換えが発生しない代わりに、スタックまでが近いので微妙…。
とりあえずこんな感じかなと予想は立てたので、引き続き進めながらエラーの詳細を追っていこうと思います。
0 件のコメント:
コメントを投稿