追記

野良犬日記 2.1

RDF/RSS
この日記の検索

最近のツッコミ:


2019-08-14 (Wed) [長年日記]

_ [sec]https://googleprojectzero.blogspot.com/2019/08/down-rabbit-hole.html

これは夏の怪談ですね。Windows 10 最新版のサンドボックスから管理者権限を取れちゃう件。見出しごとに軽く紹介。

It all seemed clear. の部分は、NtUserPostMessage によるウィンドウ間コミュニケーションが、特権付きウィンドウなら UIPI (User Interface Priviledge Isolation) によって阻まれるはず………なのに、なぜか 0xCNNN あたりの範囲で許可されているという話。

Tracking down the culprit... は、その許可の原因を探したところ、新規ウィンドウ作成にフックされた MSCTF というライブラリだったという話。CTF が何の略かは不明。

The hidden depths reveal... は CTF が TSF の一部だという話。TSF は IME とかの関係なので日本人なら目にすることのある語。

Understanding the CTF Protocol... は CTF の複雑さの説明。デスクトップセッションごとに CTF モニタサービスが起動し、クライアントからのメッセージを処理する。あらゆるウィンドウが CTF クライアントになる。CTF は Windows 98 時代の Office XP から存在。以来ほとんど変更されていない。

Is there any attack surface? これだけ古ければ、さもありなん。アクセス制御はゼロ。サンドボックスからでさえ CTF に参加できてしまううえに、スレッド ID やプロセス ID やウィンドウハンドラも自己申告。なんなら CTF サービス側になりすますこともできる。ここでさらにやる気になって、ctftool というものを作成。

Exploring CTF with ctftool... 図に示されているとおり、wait notepad.exe と打って待つと、メモ帳の方から接続してきてくれる。そこにデータを注入できる。

Looking for bugs... で、「こんなに複雑怪奇なレガシープロトコルならメモリ破壊もいっぱいあるだろうなぁ、境界チェックや桁あふれチェックもほとんどないだろうなぁ」と言いながら脆弱性を探す。すぐに、メソッド番号を受けとるコマンドを発見。任意の数を受けとるので、どこにでもジャンプさせてくれそう。(しかも例外を CTF ライブラリが吸収してくれるから、クラッシュしないで好きなだけ試行できちゃう)

Let's find something to call... そうは言っても CFG (Control Flow Guard) のホワイトリストにあるところにしか飛べないのだけど、ひたすらメソッド番号をしらみつぶしに探して、良さげなメソッドを発見。

A note about ASLR... やっぱり ASLR はブートごとじゃ弱いみたい。さらに、スタックはプロセスごとにランダマイズされるものの、CTF の仕様から、モニタがスタックの場所を教えてくれちゃうので意味ない。

Browsing what gadgets are available... というわけでサーバのスタックは分かった。クライアントは、任意書き込みガジェットを探したけど、dec しかなかったので、任意の値になるまで回す必要がある。まあ面倒だけど、これで、あちこちガジェットを行ったり来たりしてレジスタ調整すれば、あらゆる CTF クライアントを compromise できちゃう。(メモ帳からでさえ cmd.exe を実行できる)

Making it matter... では影響の大きさを説明。アクセス制御が皆無だから Administrator を含む他のユーザのあらゆるアプリケーションを乗っとれる。さらには LockWorkStation を使えば、既に SYSTEM 権限で動いている Winlogon さえ OK。そう、Winlogon も CTF クライアントなのだ!

これだけでも SYSTEM 権限への上昇という成果だが、

The "TIP" of the iceberg... ここまでのは Windows デフォルトの話。TIP (Text Input Processors) を使うと、もっとできる。IME がインストールされていれば (有効化されていなくても)、あらゆる CTF クライアントがあらゆる (他ユーザセッションの) ウィンドウのテキストを読み書きできてしまう。メモ帳にテキストを書けるだけじゃない。Administrator のコンソールにコマンドを送りつけられる。他ユーザのログイン時にパスワードを読み取れる。サンドボックス内 AppContainer プロセスからもできる。UAC 同意ダイアログも SYSTEM 権限だ。だれでも runas で呼べて、すぐ SYSTEM になれる。

So what does it all mean? バグがなくても CTF は仕様からしてアプリケーション間の内容を読み書きし合う。そこに加えてバグがある。今後マイクロソフトがプロトコル自体を現代的にするかどうかに注目。

Conclusion 大変だったけど、20年近くだれにも気づかれなかった弱点だと分かった。

おーさむ!

本日のツッコミ(全5件) [ツッコミを入れる]

_ tamo [これっすね https://portal.msrc.microsoft.com/en-US/security-gui..]

_ tamo [ここに顛末が一部書いてある。発見者も当惑するネーミングって…… https://bugs.chromium.org/..]

_ tamo [hindsightだけど、クラッシュ連発というのは、このfixの途中だったからなのかもね https://news...]

_ tamo [https://github.com/taviso/ctftool#ctftool 今月のパッチを当てれば ctf..]

_ tamo [https://news.mynavi.jp/article/20190601-834516/ これ誤解してるなあ..]


2019-07-20 (Sat) [長年日記]

_ NSIS (アン)インストーラ

NSIS でインストーラを作っていて、インストール前に既存バージョンを消したいとき、.onInit に何をどう書けばいいか悩んでいましたが、結局こうしました。

; GetTime のために FileFunc を使う
!Include "FileFunc.nsh"
Function .onInit
  ; これは普通どおりレジストリを見て既インストールか確認してるだけ
  ReadRegStr $R0 ${PRODUCT_UNINST_ROOT_KEY} "${PRODUCT_UNINST_KEY}" "UninstallString"
  StrCmp $R0 "" done

  ; これも別に要らないけど、まあ動作を知らせている
  MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "既存の ${PRODUCT_NAME} を先にアンインストールしてください。$\r$\n$\r$\nOK を押すと続行します。その後で、改めてインストール画面を出します。" IDOK uninst
  Abort

uninst:
  ClearErrors
  ${GetTime} "" "L" $0 $1 $2 $3 $4 $5 $6
  ; セキュリティ的にはもっと推測や衝突を考えた方がいいだろうが
  StrCpy $R1 "$TEMP\uninstall-$2-$1-$0-$4-$5-$6.exe"
  CopyFiles "$R0" "$R1"
  ExecWait '"$R1" _?=$INSTDIR' $R2
  Delete "$R1"
  ; アンインストーラが正常終了したときだけ続行するなら以下のようにしておく
  StrCmp $R2 0 done
  Abort

done:

FunctionEnd

アンインストーラは自分を消す必要があるので、_? が指定されていなければ一時フォルダに自分をコピーしてからそっちを呼びます。だから ExecWait (子を待つが孫は待たない)が効かないぞ、というのは FAQ みたいです。

で、どうするのかというと、その動作を止め(つつ消去先を指定す)るオプション _? をただ呼べばいいのではありません。それだけでは「自分が起動中なので消せない」というエラーになったり、エラーを消そうとして REBOOTOK にしたら再起動後に(新しい)アンインストーラが消えちゃってアンインストールできなくなったり、という落とし穴があるみたいです。

なのでアンインストーラを ExecWait したいなら自分で TEMP にコピーするというのが一番簡単みたいです。

ちなみに、上記のようにするまでは、アンインストーラが新規インストーラの上に来るように MessageBox を遅らせたりしていました。

Function un.onInit
  Sleep 800
  MessageBox MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2 "$(^Name) を削除していいですか?" IDYES +2
  Abort
FunctionEnd

2019-07-15 (Mon) [長年日記]

_ [ahk]ホイールとか

Windows で何かを押しながらマウス移動でホイールをシミュレートするやつ2019版。その前に普通のホイールを加速するやつも付いてるけど、もちろんこっちは消してもいい。

CoordMode, Mouse, Screen

; あとで使う透明マウスポインタ
VarSetCapacity(AFF, 32 * 4, 0xFF)
VarSetCapacity(X00, 32 * 4, 0x00)

; cf. https://www6.atwiki.jp/eamat/pages/32.html
WheelUp::
WheelDown::
 MouseGetPos, x, y, hwnd, ctrl, 1
 ; UWP?
 If (ctrl == "")
  WinGetClass, ctrl, ahk_id %hwnd%
 If (ctrl ~= "^Windows\.UI\.Core\.CoreWindow")
 {
  ; 適当に加速 (間隔700ms未満から加速し、400ms未満はもっと加速)
  v := 1
  If ((A_PriorHotkey == A_ThisHotkey)
      and (A_TimeSincePriorHotkey < 700))
   v += Floor((700 - A_TimeSincePriorHotkey) / 300) ** 3
  SendInput, {Blind}{%A_ThisHotkey% %v%}
  Return
 }
 v := (A_ThisHotkey == "WheelUp") ? 1 : -1
 If ((A_PriorHotkey == A_ThisHotkey)
     and (A_TimeSincePriorHotkey < 700))
  v *= 1 + ((700 - A_TimeSincePriorHotkey) / 300) ** 3
 ; 適当に進む (120は、マウスの設定で1行にしているとき用って感じ)
 PostMessage, 0x20A, Floor(120 * v) << 16
                   , (y << 16) | x
                   , %ctrl%, ahk_id %hwnd%
 Return

; 中央ボタンを押しながらマウス移動でホイール動作のふり
*MButton::
 MouseGetPos, sx, sy, hwnd, ctrl, 1
 ; UWP?
 If (ctrl == "")
  WinGetClass, ctrl, ahk_id %hwnd%
 uwp := (ctrl ~= "^Windows\.UI\.Core\.CoreWindow")
 ; カーソル隠しとこう
 bc := DllCall("CreateCursor", "UInt", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "UInt", &AFF, "UInt", &X00)
 DllCall("SetSystemCursor", "UInt", bc, "Int", 32512) ; ノーマル
 bc := DllCall("CreateCursor", "UInt", 0, "Int", 0, "Int", 0, "Int", 32, "Int", 32, "UInt", &AFF, "UInt", &X00)
 DllCall("SetSystemCursor", "UInt", bc, "Int", 32513) ; 文字Iビーム
 moved := false
 While GetKeyState("MButton", "P")
 {
  MouseGetPos, x, y
  If ((delta := sy - y) = 0)
   Continue
  Else
   moved := true
  If (!uwp)
  {
   PostMessage, 0x20A, ((delta*Abs(delta)) << 16) | mods()
                     , (sy << 16) | sx
                     , %ctrl% , ahk_id %hwnd%
  }
  Else
  {
   wud := (delta > 0) ? "WheelUp" : "WheelDown"
   SendInput, {Blind}{%wud%}
  }
  ; マウスカーソルが動いてしまっているので、元に戻す
  MouseMove, sx, sy, 0
  Continue
 }
 ; カーソル表示
 DllCall("SystemParametersInfo", "UInt", 0x57, "UInt", 0, "UInt", 0, "UInt", 0)
 ; ここでモディファイアが効いてるかどうかは知らん
 If (!moved)
  MouseClick, Middle
 Return

mods()
{
 Return (1 << 0) * GetKeyState("LButton")
      | (1 << 1) * GetKeyState("RButton")
      | (1 << 2) * GetKeyState("Shift")
      | (1 << 3) * GetKeyState("Ctrl")
      | (1 << 4) * GetKeyState("MButton")
      | (1 << 5) * GetKeyState("XButton1")
      | (1 << 6) * GetKeyState("XButton2")
}

2019-07-02 (Tue) [長年日記]

_ IP ID によるカーネルアドレスのリーク (翻訳)

tedu flak の "random ip id comments" を勝手に翻訳してみました。リンク先の論文は一切見ていませんし、記事内容もすべて理解しているとは言えませんが、興味深い内容だったので。

以前の記事 "KARL"に関連しています。

ランダムな IP ID について一言

最近「IP ID をデバイスの ID、そして KASLR 回避に」という論文があり、気に入った。この論文が位置するのはネットワークと、古くても現役な標準規格と、乱数、セキュリティ、そして実装依存の挙動のすべてが交わった場所だ。どうか、何とかして原文を読んでほしい。ただ、本当に短くまとめると、著者たちは次の二つのことを成し遂げたと言える。彼らは Windows と Linux のネットワーク トラフィックから、ホスト固有の乱数シードをリバース エンジニア解析することにより、フィンガープリンティング (非匿名化) を可能にした。そして、さらに驚くことに、これを Linux の KASLR 突破にまで発展させた。ワイルドだろぉ?

ムカシ

IP ID は IP パケット内のフィールドの一つで、フラグメンテーション (断片化) を扱う。パケットが断片化されていると、受け手側ホストは、どの断片がどのパケットに属するかを知る必要がある。パケットを識別するときに使う IP アドレス (と、おそらくポートも) みたいなものだ。とはいえ、IP ID は 16 ビットしかないフィールドだから、衝突を避ける工夫は大切だ。さもないと、断片を正しく組み直せない。昔の、いちばん単純な方法では、単に断片化のたびにカウンタをインクリメントしていた。想定としては、カウンタがゼロに戻る頃には古い断片がすべて相手に届いているだろうということだ。

これは単純すぎたため、いくつかのワザを許してしまった。初期の論文に「NAT 下のホスト数を知るテクニック」があり、これはグローバルでリニアに増える IP ID を使ってホストを数えている。それより後の論文「TCP/IP フィールドの挙動を観察することによる NAT 下ホストの計数」は、これにもっと多くのフィールドを含めることで改良を加えた。最後に「現場での IP-ID 動作に対する詳細な観察」がある。これは各種の IP ID 生成技術を駆使してホスト数を測定した。

OpenBSD は乱数生成技術に後述のヒネリを加えて使っている。他のシステムではカウンタの亜種を使っているが、前述の検出テクニックその他の残念な結果にならないよう、単純なグローバル カウンタではなく、その接続に由来するオフセットを計算している。

アルゴ

論文において、IP ID 生成のアルゴリズムは三つ説明されている。グローバル カウンタ、ローカル オフセット付きのグローバル カウンタ、そしてルックバック付きの乱数だ。

最後のテクニックは Apple が使っているもので、最近使われた ID のキューを保持しつつ、新しい乱数を生成し、それが重複していないかチェックする。これにより問題のいくつかが解決される。元情報につながるグローバル カウンタやオフセットがなくなる。純粋な乱数アルゴリズムと異なり、重複の確率は減る。

OpenBSD は、Dragonfly を起源とするアルゴリズムを使って、この第三のアルゴリズムの亜種を実行している。存在しうる 64K 個の ID すべてを入れた配列があり、これはシャッフルされている。隣のアイテムを選んだら、それを 32K ほどオフセットしたあたりに混ぜ戻しておく。(訳注: 訳者が誤解しているかもしれないので知りたい人は https://github.com/openbsd/src/blob/master/sys/netinet/ip_id.c 参照) こうして、それほど早く再使用されないようにしているのだ。

デレズ

Klein と Pinkas は論文で、様々な接続から IP ID を取り出して、なんかすごい数学で元のグローバル カウンタを算出している。そうしたらまたあの、1999年みたいなパーティの始まりだ。(訳注: de-rez というのは映画 TRON の用語で de-resolve のこと、つまり分解消滅みたいな意味らしい)

ここでさらにまた興味深いのは、Linux で使っている計算の一部にはグローバル変数のアドレスが含まれており、それゆえにグローバル シードを計算すればカーネルのロード アドレスも分かってしまうということだ。

KASLR

ASLR には粗挽き vs 細挽き、それぞれの利点に関する議論がある。粗挽きなら、プログラム (やカーネル) のロード アドレスだけをランダマイズする。細挽きならプログラムを複数に分割し、それらをすべてランダマイズする。そうすれば攻撃するために当てる必要のあるアドレスが増えるからだ。しかし、ひとつリーク源があれば好きなだけ繰り返してアドレスを知ることができるのだから複雑にしても無意味だという意見もある。

今回のバグは、そういった意見に対して興味深い反論を提供する。ここでは一つのアドレスしかリークしないのだ。攻撃者がさらにパケットを送っても、さらに多くのアドレスを知ることはできない。

つまり、Android デバイスの種類が分かればカーネル レイアウトが分かるし、今回の IP ID derezzing テクを使えば、ロード アドレスまで分かってしまうということだが、一方で、同じカーネルでもデバイスごとに異なるリンク方法を使って、シンボルを異なるレイアウトに配置していたら、一つカーネル アドレスがリークしたところで大して役に立たないはずだった。

16bits

ちょっとした脚注として言っておくと、DNS も 16 bit ID で要求を識別している。

Posted 01 Jul 2019 01:23 by tedu Updated: 01 Jul 2019 01:23
本日のツッコミ(全1件) [ツッコミを入れる]

_ tamo [random は IP ID じゃなくて comments にかかってるのかもしれないな。comments が複数形..]


2018-12-12 (Wed) [長年日記]

_ Windows でショートカット(.lnk)ファイルに対する「ファイルの場所を開く」がエラーになっていた

よく覚えていないけど「関連付けがない」みたいなエラーだった。

結局レジストリがおかしくなっていたみたいで、 HKEY_CLASSES_ROOT\Folder\shellの中身を(既定も)削除 すると直った。

問題の原因

よくわからないけど、自作インストーラが変なキーを入れていたのかもしれない。既定の値が自作ツールっぽい名前になっていた。

解決に至るまで

セーフモードで sfc /scannow してもダメ、CCleaner でレジストリをチェックしてもダメ、MS の diag ツールも意味なしだった。

その MS 純正ツールが勝手にゴミ箱を空にしてくれたので、あとで戻そうと思っていたファイルが永遠に失われた。


2002|06|07|08|09|10|11|12|
2003|01|02|03|04|05|06|08|09|10|11|12|
2004|01|02|03|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|10|11|12|
2011|02|04|05|06|07|08|09|10|11|12|
2012|01|03|04|05|06|09|11|12|
2013|02|03|09|10|11|
2014|02|03|04|06|09|
2015|11|
2016|01|04|09|10|
2017|04|05|07|
2018|04|08|12|
2019|07|08|