拾い物のコンパス

まともに書いたメモ

Ubuntuで認証画面を無限ループさせる方法と対処

最近DM(デスクトップマネージャー)やWM(ウィンドウマネージャー)をいじっていると、ある時突然認証画面でパスワードを入力してもすぐにまた認証画面が出る無限ループに陥った。割と楽しい現象だったので引き起こし方と対処を書き残す。

確認した環境

無限ループを正常に引き起こせたOSは以下の通り(これしか試していない)
・Ubuntu14.04 LTS 32/64bit
・LightDM・GNOMEを入れたArch Linux 64bit

無限ループの呪文

$ sudo startx && sudo reboot
以上。
パスワードの長い認証を抜けると、認証であった。
30回ほどパスしたが、終わらなかった。

対処

まずはシェルを起動する。
簡単な方法としては以下のものがある。
sudoが使える別ユーザでログイン
・Ctrl+Alt+F1とかを押してCUIでログイン
sudoを使えるユーザでログインすることが重要。そして以下のファイルを削除する。

.ICEauthority  : 複数のXクライアント同士で直接通信するときの認証に使われる。内部にランダムなCookieを保有していて、これが同じXクライアント同士が通信できる。
.Xauthority    : Xサーバの認証に使うファイル。詳しくはxauthで調べる。

.ICEauthority.Xauthorityは同じように見えるが、前者はClient to Client, 後者はClient to Serverであることが大きな違い。

これで再起動すれば正常に動くようになる。消したファイルは再度作成される。

原因

認証ファイルを消したらうまくいったってことは、Xサーバの認証の失敗ってことでいいんだろうか。
GDMとLightDMの.Xauthorityファイルの使い方についてもっと詳しく調べたほうがよさそうだ。
正直全くわかっていない。わかったら追記する。

考察

実はもう一つの対処として、ArchのDMをGDMに変更しするというものもある。
これをした場合、sudo startxが自体が成功しなくなった。そして、認証もループが起こらなくなった(無限ループが起こっている状態で隠しファイルを消さずにDMを変更した。このままLightDMに戻すとまたループ)。
UbuntuはDMにLightDMを用いていることから考えると、今回のこの無限ループはLightDMに起因するものなのだろうか。 もしくは、LightDM内で使われているであろうxauthが原因である可能性が高いのではないかと思う。
どちらにせよ、UbuntuDebianベースのXubuntuやLubunt、Kali Linuxのようなファミリーはすべて今回の現象が起こる可能性がある。実際に試してはいないので何とも言えないが。
もし試し方がいたら環境と結果を教えて頂けると非常にありがたい。
これの根本的な原因を追究できるようになりたい・・・。

参考

Ubuntu Xサーバー その3 - ログインしてもログイン画面に戻される時は - Ubuntu kledgeb

open - what is .ICEAuthority file in opensuse 11.2 - Server Fault

見たことあるCコーディングミス

知り合いが実際に書いて悩んでいたCコードを2種類書き残す。

その1

...
int i=0,
    count[i];
...(snip)...

一見してやばいのはわかるはず。要素数0の配列を宣言していた。その場ではただひどいコードだとだけ思ったが、あとから気になってちょっと実験してみた。

実験コード

/* zero_array.c */
#include <stdio.h>

int main(void)
{
   int  a = 0,
        b = 0,
        c[a];
        
   printf("a: %p\nb: %p\nc: %p\n", &a, &b, c);
}
/* E.O.F. */

実行結果

実行したところ、

a: 0xbf877850
b: 0xbf877854
c: 0xbf877840

となった。変数a, bは正しくスタックに積まれているようだ。cは確保されているのかどうかとても怪しいところ。領域は確保されていないがアクセスできるポインタみたいな印象であるが、詳細は不明。わかったら追記する。
当たり前ではあるが、上のソースでc[4]に任意の値を書くとaを書き換えることができる。ただのセキュリティホールにしか見えない。

2015/10/31追記

turn_upさんから指摘があった。要素数0の配列は構造体で

struct hoge {
   int i;
   int a[0];
}

とするとaiの次のアドレスを指すようになる。上手く使うと便利なようだが、マイナーな上にGCC拡張だから、依存性が強い。C++では配列は1個以上の要素を持たないといけないで要素数0の宣言はできない。

その2 (考察間違い。追記要参照)

これも知り合いが書いてしまったコード。詳しく覚えていないが、こんな雰囲気だった。

while (1) {

if (hoge = 32) break;
else ...(snip)...

if文の条件で代入を行うとTrue扱いになってすぐループを抜けてしまっていた。
気になったのでいじっていたら、代入が失敗する場合を1種類だけ見つけた。
それは以下の場合である。

int/char a;
if (a = 0.11) printf("True\n");
else          printf("False\n");

intcharの変数に小数を代入しようとすると失敗するようだ。

2015/10/31追記

turn_upさんから指摘があって、改めて考えてみた。
冷静に考えてみると、分岐命令の中身は
if (i = 0) -> if (i) -> if (0)
の流れでFalseになっているんじゃないかと思って、アセンブラを見てみたら案の定

mov DWORD PTR [esp+0x1c], 0x0
cmp DWORD PTR [esp+0x1c], 0x0
je 0x80......

となっていた。だから、変数に0を代入した場合はすべてFalseになる。これはint/char/float/doubleの時で確認できた。
細かいことではあるが、先ほどのコードのようなシンプルな分岐では、i0以外を代入したときは実行ファイルにcmpはなかった。自明すぎてコンパイラに消されてしまったようだ。-O0をつけても同じだった。

どこかで聞いたのは、分岐条件の記述は変数を後に書いたら良いとのことだった。
例えばif (NULL == ptr)if (32 == hoge)のようにである。これならば間違えてしまったときはコンパイルエラーが出てすぐに気付くことができる。

参考ページ

Using and Porting GNU CC - C 言語ファミリに対する拡張

"B"-con - Laboratory - Data & Algorithm - アクロバティックなコード (1)

GHCでコンパイルしたバイナリを小さくする方法

概要

HaskellGHCコンパイルしてバイナリを生成することができる。しかし、Cの時と比べて意味不明なレベルでバイナリが大きくなる。原因と対策を書いておく。

環境

  • OS
$ uname -a
Linux ubuntu 3.13.0-63-generic #103-Ubuntu i686
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.6.3
$ gcc --version
gcc (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4
...

ソース

-- hello.hs

main = putStrLn "Hello, World!"

-- E.O.F.
/* hello.c */
#include <stdio.h>

int main(void)
{
   printf("Hello, World!\n");
   return 0;
}
/* E.O.F. */

どちらも "Hello, World!"と出力するだけの簡単なプログラム。

コンパイル

  • GHC
    $ ghc -o h_hello hello.hs -O2

  • GCC
    $ gcc -o c_hello hello.c -O2

サイズ

$ ll ?_hello
7332     c_hello
839047   h_hello

HaskellはCの100倍以上の大きさを持っている。
最初見たときは叫んだ。

原因

GHCはデフォルトでHaskell runtime(libHSrts.a)を静的リンクするようだ。 こいつが丸々バイナリに組み込まれてしまうのが問題みたいだった。
GCCは最初から動的リンクするイケメン。

対策

動的リンクに切り替えてやればよい。
UbuntuHaskell platformには動的リンクが含まれていない。
だから最初に ghc-dynamic をインストールする。
$ apt-get install ghc-dynamic
それから以下を実行。

$ ghc -dynamic -o h_hello hello.hs`  
$ ll ?_hello
7332     c_hello
16397    h_hello

2倍程度まで小さくなった。 デバッグ情報を消去するともう少し小さくなる。参考までにデバッグ情報をなくしたCバイナリ(sc_hello)も載せておく。

$ strip -p --strip-unneeded --remove-section=.comment -o sh_hello h_hello
$ ll *_hello
7332     c_hello
16397    h_hello
5448     sc_hello
9688     sh_hello

c_helloのほぼ同程度のサイズまで落とせた。

結論

Haskellのバイナリはリンクさえどうにかすればサイズは2倍程度にできることがわかった。

おまけ

このままCバイナリに負けたままでは悔しかったからもう少し頑張ってみた。
実行形式ファイルを実行可能な圧縮ファイルに変換するgzexeというコマンドがある。これは実行ファイルをgzipで圧縮し、圧縮したファイルの先頭に解凍するスクリプトを埋め込む。容量の小さいディスクにおいて有効なコマンドである。

$ gzexe h_hello
h_hello:   74.2%

$ ll *_hello
7332     c_hello
5061     h_hello         <- 圧縮後
16397    h_hello~        <- 圧縮前

・・・ついにHaskellバイナリはCバイナリを越えた!

$ gzexe c_hello
c_hello    68.7%

$ ll *_hello
3132    c_hello         <- 圧縮後
7332    c_hello~

5061    h_hello         <- 圧縮後
16397   h_hello~

という夢を見た。おしまい。

メモ

最初にPreludeの関数をputStrLn以外をhidingしてみたが、サイズ面では全く変化がなかった。
関数のロード関連はサイズに関係しないフラグなどがあるのだろうか。

参考リンク

haskell - Could not find module Prelude... dyn libraries for package base? - Stack Overflow

linker - Making small haskell executables? - Stack Overflow

xinetdが動作しないときの対処

概要

xinetdでサービスを動かしたら接続は上手くいくが、プログラムからの出力が返ってこなかった。この時に試したことを書き残す。
最終的にはxinetdの公式レポジトリからソースを引っ張ってきてインストールしたら正常に動作した。
他にはonly_fromの記述に注意すれば上手く行くことがわかった.
尚、この現象は32ビット版でのみ起こった. 64ビット版では今回の問題は起こらなかった.

環境

Ubuntu 14.04 LTS 32bit Arch Linux 64bit

設定ファイル

# xinetd.conf
service unlisted
{
    type        = UNLISTED
    socket_type = stream
    protocol    = tcp
    wait        = no
    only_from   = localhost 192.168.0.0/16
    port        = 20020
    server      = /bin/echo
    server_args = hogehogehogehoge                                
}

サーバを立てた状態で $ nc localhost 20020 を実行すれば hogehogehogehoge と表示される。

実行コマンド

$ sudo xinetd -f xinetd.conf -d

試したこと

  • 再インストール
    apt-get remove xinetd && apt-get install xinetd
  • デーモンのリスタート
    $ service xinetd restart
  • 再起動
    $ reboot
  • アップデート・アップグレード
    $ apt-get update; apt-get upgrade どれも効果なし。

対処

その1

ソースを取ってきてインストールする。

$ git clone https://github.com/xinetd-org/xinetd.git
$ cd xinetd
$ ./configure
$ make
$ make install

/usr/local/sbin/にインストールされる。
これでちゃんと動いた。

その2

$ ip address show
で自分のIPを調べる.xintd.confonly_fromで接続を192.168.0.0/24に制限しているから,自分のIPが外れていたら適宜修正すること.
$ nc <your_IP> 20020

その3

以下を編集する.

[-] only_from   = localhost 192.168.0.0/16
[+] only_from   = 127.0.0.1 192.168.0.0/16

only_fromの記述にlocalhostは使わないほうがいいようだ.
これならば nc localhost/127.0.0.1/192.168.?.? 20020 どのIPでも上手く行った.
ちなみに,xnetd実行時にsudoを忘れるとどのIPでも出力が返ってこなかった.

まとめ

  • xinetd実行時にはsudoをつけよう
  • xinetd.confファイルのonly_fromではlocalhostは使わない

以上.

セキュリティキャンプ九州 Writeup

概要

8/28-8/30にかけてセキュリティ・キャンプ実施協議会主催(実はIPA主催じゃないらしい)のセキュリティキャンプ九州に参加した。感想や反省点をここに書き残す。

キャンプ内容

応募

セキュリティキャンプには前々から興味があったので、技術力に自信はなかったが当たって砕けたら縁がなかったと諦める覚悟で応募した。 質問には正直に答えた。質問3のネットワーク・サーバの経験はなかったので、夢見ていることをつらつら書いた覚えがある。
質問4のアピールは他人と比較してではなく、とりあえず自分ができることを書いた。何か書かないと顔も知らない相手には何も伝わらない。
恐らく、一番重要であろう学んだ知識の使い道は今までいろんな技術を公開してくれた人への恩返しがしたいとかいう内容を書いたと思う。
もし応募しようか悩んでいたら、まずカレンダーを見ること。そして暇だったら応募することをお勧めする。キャンプ中は生活習慣が矯正される副作用が魅力。

当日まで

演習で利用する環境の構築やシンプルなパケットの解析、XSSの練習をやった。
講師おすすめのWebサイトや本は知識不足で読んでも理解できず、碌に読まないままキャンプに突入した。この選択が二日目にとても効いた。

一日目

まず会場についてやったことは設営の準備だった。この過程で参加者同士の緊張がいい感じにほぐれて会話がちらほら聞こえるようになった。
参加者の一人が背中に "GitHub" とプリントされたTシャツを着ているのを見て、ここはセキュリティキャンプなんだと強く感じた。
午前中は悪質なアプリの仕組みやサイバー犯罪についての講演があった。スマホのキャッシュ機能を利用して再起動時にも悪質なサイトへアクセスする仕組みには思わず唸った。
サイバーセキュリティはいくら投資しても儲からないから力を入れる経営者は少ないという話は理解はできるが納得はできなかった。危ない橋を渡り続ければいつかは落ちてしまうと思う。

午後は待ちに待った実習。
サイバー大学の園田教授が講師で強いパスワードの作り方を議論し、パケット解析をした。
複雑なパスワードの作り方を提唱するのは簡単だが、高齢者に使えるかどうかと聞かれるとNoだった。
良心的で記憶力の高い孫に頑張ってもらうか、サイバー高齢者を育成するのが早そうだ。サイバーじーちゃんって響きがいいな。
パケット解析では本物のウイルスが入ったパケットを配られ、対策ソフトが検知・削除する現象が流行。初めて対策ソフトの検知方法について考えた。対策ソフトの反応を見る限りだと検知された理由がシグネチャマッチによるものみたいだったから、例外登録以外にも圧縮によるコピーも成功した。
どうやら有名なバスターさんは解凍したときに生成されたファイルをすべて自動スキャンして削除するようだ。潜在的な持込み自体は成功してしまうことになる。ウイルスを作るウイルスはここに付け込んでいるんだろうな。
時間が足りず、パケットを全部解析し切れなかった。

夜は講師の部屋に行って講師やチューターの方と話した。講師の一人から情報を発信する・人との輪を広げていくことの重要性を聞き、ブログを開設しようと決めた。

二日目

午前中はスライドを見ながらFuelPHPで簡単な掲示板を作成した。普段はバイナリ関係を主にいじっているから、やっていることがハイレベルなのは感じたが、内容が理解できなくて申し訳なかった。VimPHPのインデントが正常に機能せず、編集に苦戦した。インデントのパターンマッチは要改良。

午後は午前に作ったサイトで脆弱性探しの練習をした後、講師の作成したサイトで本番を行った。 おかしな挙動を見つけ、その悪用できるのかまでを考えるのが脆弱性解析なんだと実感した。この感覚は磨いていきたい。
チューターの人はツールを使いこなし、バリバリ見つけていて格好良かった。

午後5時を過ぎ、頭がSQLiとPHPでコアダンプしていたところに模擬裁判があった。
判決対象はどちらも致命的なミスがあり、つい両成敗したくなる事例だった。現実の事件もこのように白黒で裁けるものではないのだろう。
私は裁判官のグループだったため、「異議あり」と叫べなかった。とても残念。

晩飯はキャンプ史上初らしい焼肉に行った。他のキャンプ参加者から非難が殺到していたとは後で聞いた。
たまたま講師3人に囲まれた席に座り、前々から気になっていたことを質問したら丁寧に答えてくれた。有難い。
その場で講師の一人からセキュリティ業界には独身が多いという都市伝説を聞いた。セキュリティ人材の10大脅威があったらランクインしてしまいそうだから早くパッチを充てたほうがいいと思う。

三日目

午前中はRaspiを使ってARMのアセンブラを書いた。指定した番号のシステムコールを呼び出すコードを書いたが、シェルコードまで昇華することができなかった。RaspiではKali Linuxも動くらしいので、Linuxの実験用に一つ買いたいと思う。
午後はフレッドボードを使った工作。LEDを光らせたり、スイッチを有効にしたりした。LED端子を逆に刺していたことに気付かず困った。このような小さなデバイスではやはりCが強いなと思う。POSIXではPythonPerlを指定しているから、これらでもいいのだろう。
講座後は卒業証の授与があってからキャンプは終わった。

まとめ

演習会場はネット環境が用意されておらず、Google封じをくらった。やはり必要な知識は一度調べたら頭に叩き込むのが正解だと思う。ネットが無いとなにもできない技術者にならないためにも。

全体を通して講座の内容は知識を掘り下げるというよりはすべて入門という立ち位置だったと感じた。キャンプは知識と人との出会いを提供する場であり、そこから自分でどれほど掘り下げることができるのかが今後の成長につながると思う。精進したい。

おまけ

キャンプでは首や腰など、体に脆弱性を抱えると非常につらいということも学んだ。セグフォを起こさないようセキュアな生活と体作りを心掛けたい。
まずは重い荷物を持つときはちゃんと膝をついて持ち上げるところから始めたいと思う。