読者です 読者をやめる 読者になる 読者になる

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

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できる環境を作ってみる

android

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!

無事に接続できた。

社会...

ぽえむ

給料日







次の日

催促したら入金はされた。





これが社会か

某イベントで某社に声をかけられてapkの脆弱性診断のバイトをやっていたが社会はきびしい。

やっぱ、名刺交換している人全員にスカウトメール送るような会社はやばかった。エンジニアが優秀そうでも経理とか事務とか人事がダメだとつらい。

これで消耗してから、お酒おいしすぎてもうダメ🙅

goのバイナリを簡単に読みたい

go reversing

先日の goのバイナリをちょっとまじめに読んでみるstrippedなgoのバイナリを読み解くでは気合いでgoのバイナリを読んでいった。goのバイナリの解析の難しいところは、strippedだった場合に静的リンクされたライブラリの1000個以上の関数がどの関数なのか分かりづらいとか、メインの処理がどこからなのか分かりづらいとかそういうところである。

idaapiから生成した静的リンクされたライブラリのFLIRT(Fast Library Acquisition for Identification and Recognition) signatureとバイナリを照らし合わせれば、楽にシンボルを特定できそうだと考えたが、FireEyeがすでにgoバイナリを想定した似たようなツールを作っていた。

FLARE IDA Pro Script Series: Generating FLAIR function patterns using IDAPython « Threat Research Blog | FireEye Inc

これを試そうと思ったが、研究室のIDAがstarterライセンスであることに気づき完全にやる気をなくし1ヶ月経った。

また、Labeling Library Functions in Stripped Binariesでは関数ごとにCFG(Control Flow Graph)を作ることで、FLIRTよりも高精度でシンボルを回復できると論じているが、正直そこまでやる気がでない。

誰か知見を教えてほしい。

gccでlibc抜きでコンパイルを通す

c言語 asm

Hello from a libc-free world! (Part 1) (Ksplice Blog) がおもしろかったのでやっていく。

libcありのとき

コード

よくあるhello worldのコード

$ cat hello_with_libc.c
# include <stdio.h>

int main()
{
  printf("Hello World\n");
  return 0;
}

コンパイル

$ gcc hello_with_libc.c -o hello_with_libc
$ ./hello_with_libc 
Hello World
$ wc -c ./hello_with_libc 
8561 ./hello_with_libc

もちろん問題なくコンパイルできる

libcなしのとき

コード

libcを使わないコードを書いた。

$ cat hello_no_libc.c
int main()
{
  char *str = "Hello World";
  return 0;
}

コンパイル

$ gcc -nostdlib hello_no_libc.c -o hello_no_libc
/usr/bin/ld: 警告: エントリシンボル _start が見つかりません。デフォルトとして 0000000000400144 を使用します
$ ./hello_no_libc
Segmentation fault (コアダンプ)

-nostdlibをつけてlibcを含まずに、コンパイルするとエントリポイントが見つからないと怒られる。 ELF形式のエントリポイントはcrt1.oで定義されているのでリンクしてみる。

$ gcc -Os -c hello_no_libc.c
$ ld /usr/lib/x86_64-linux-gnu/crt1.o -o hello_no_libc hello_no_libc.o
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 0 が無効なシンボル索引 11 を持っています
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 1 が無効なシンボル索引 12 を持っています
...
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 18 が無効なシンボル索引 13 を持っています
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_info): 再配置 19 が無効なシンボル索引 21 を持っています
ld: /usr/lib/debug/usr/lib/x86_64-linux-gnu/crt1.o(.debug_line): 再配置 0 が無効なシンボル索引 2 を持っています
/usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内:
(.text+0x12): `__libc_csu_fini' に対する定義されていない参照です
/usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内:
(.text+0x19): `__libc_csu_init' に対する定義されていない参照です
/usr/lib/x86_64-linux-gnu/crt1.o: 関数 `_start' 内:
(.text+0x25): `__libc_start_main' に対する定義されていない参照です

libc_csu_fini、libc_csu_init、libc_start_mainがないと怒られた。これらはmain関数が呼ばれる前に呼ばれる関数でlibcのセットアップを行う。crt1.oに頼らず、main関数を呼ぶエントリポイントを書く必要がある。

$ cat stubstart.s 
.globl _start

_start:
    call main
    mov  $60, %eax # exit
    xor  %rdi, %rdi   
    syscall

stubstart.sはmain関数を呼んだ後、exitのシステムコールを呼び出し終了する_start関数を書いたものである。 これがエントリポイントとなる。

$ gcc -nostdlib stubstart.s hello_no_libc.c -o hello_no_libc
$ ./hello_no_libc
$ wc -c ./hello_no_libc 
1613 ./hello_no_libc

無事にコンパイルが通り実行できるようになった。冒頭でコンパイルしたHello worldの8561byteに比べ、libcがない分、1613byteとサイズが小さくなったことも確認できる。

Unbreakable Enterprise Product Activationをz3pyを使って解いてみる

CTF reversing

はじめに

z3pyにkatagaitai勉強会#6 - 関西|easyで入門したので、練習がてらGoogle CTF 2016のUnbreakable Enterprise Product Activationを解いた。 この問題を人力で解くのは難しく、競技期間中は解けなかったが、z3pyを使えば楽に解くことができた。

実行してみる

$ ./unbreakable-enterprise-product-activation 
unbreakable-enterprise-product-activation: ./unbreakable_enterprise_product_activation product-key
$ ./unbreakable-enterprise-product-activation aaaa
Product activation failure 255

正しいproduct-keyがflagになりそうと分かる。

静的解析

0x4005aaから0x4005b8までの処理では入力したproduct-keyをコピーしている。0x4005bfから0x40071bまでつづくcallによって、0x4027f0から0x403440にproduct-keyをチェックする処理を呼んでいる。

4005aa         mov        rsi, qword [ds:rsi+8]             ; src address
4005ae         mov        edx, 0x43                         ; size
4005b3         mov        edi, 0x6042c0                     ; dist address
4005b8         call       j_strncpy
4005bd         xor        eax, eax
4005bf         call       0x4027f0
4005c4         xor        eax, eax
4005c6         call       0x402830
4005cb         xor        eax, eax
4005cd         call       0x402870
4005d2         xor        eax, eax
4005d4         call       0x4028c0

以下のようなproduct-keyをチェックする処理が50個ほどあり、人力で解くのは難しいと分かる。

4027f0         movzx      edx, byte [ds:0x6042e6]                     ; XREF=sub_400590+47
4027f7         movzx      eax, byte [ds:0x6042de]
4027fe         movzx      ecx, byte [ds:0x6042c6]
402805         movzx      esi, byte [ds:0x6042c8]
40280c         movzx      edi, byte [ds:0x6042c0]
402813         xor        eax, edx
402815         sub        eax, esi
402817         add        eax, ecx
402819         cmp        dil, al

また、strcpyのコピー先でXREFがついているアドレスは0x6042c0から0x6042f2だったので、flagの長さは51だと分かった。

6042c0         db  0x00 ; '.'                               ; XREF=sub_400590+35, sub_400870+9, sub_400870+30, sub_400870+2932, sub_400870+4029, sub_400870+4644, sub_400870+4934, sub_400870+6356, sub_400870+7477, sub_400870+8092, sub_400870+9189, …
6042c1         db  0x00 ; '.'                               ; XREF=sub_400870+47, sub_400870+65, sub_400870+2954, sub_400870+3889, sub_400870+4666, sub_400870+5244, sub_400870+6378, sub_400870+6980, sub_400870+8156, sub_400870+8528, sub_400870+9902, …
6042c2         db  0x00 ; '.'                               ; XREF=sub_400870+83, sub_400870+103, sub_400870+2977, sub_400870+4007, sub_400870+4651, sub_400870+4689, sub_400870+6401, sub_400870+6527, sub_400870+8227, sub_400870+9088
6042c3         db  0x00 ; '.'                                ; XREF=sub_400870+122, sub_400870+141, sub_400870+3001, sub_400870+3675, sub_400870+4713, sub_400870+5824, sub_400870+6425, sub_400870+7099, sub_400870+8213, sub_400870+8307, sub_400870+9182, …
6042c4         db  0x00 ; '.'                               ; XREF=sub_400870+161, sub_400870+179, sub_400870+3026, sub_400870+3034, sub_400870+4738, sub_400870+5100, sub_400870+6450, sub_400870+6598, sub_400870+8366, sub_400870+8414, sub_400870+8421, …
6042c5         db  0x00 ; '.'                               ; XREF=sub_400870+197, sub_400870+215, sub_400870+3050, sub_400870+4074, sub_400870+4762, sub_400870+5078, sub_400870+6474, sub_400870+6956, sub_400870+8428, sub_400870+8592, sub_400870+8599, …
...

z3pyを使う

コード

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

from z3 import *

FLAG_LENGTH = 0x6042f2 - 0x6042c0 + 1

s = Solver()
text = []

for i in xrange(FLAG_LENGTH):
    text.append(BitVec(i, 8))
    s.add(And(text[i] >= 0x20, text[i] < 0x7f)) # in printable ascii

s.add(text[0] == text[6] + (text[38] ^ text[30]) - text[8])
s.add(text[1] == (text[42] ^ (text[38] ^ text[20] ^ text[19])))
s.add(text[2] == text[35] + text[36] - text[19] - text[3] - text[44])
s.add(text[3] == text[19] + (text[17] ^ (text[41] - text[10] - text[10])))
s.add(text[4] == text[33] - text[21])
s.add(text[5] == (text[4] ^ (text[4] ^ text[8] ^ text[39])))
s.add(text[6] == (text[14] ^ (text[10] + text[25] - text[39])))
s.add(text[7] == text[32] + (text[15] ^ text[1]))
s.add(text[8] == text[8])
s.add(text[9] == (text[24] ^ text[7]))
s.add(text[10] == text[32] + (text[49] ^ text[17]) - text[4])
s.add(text[11] == (text[42] ^ text[38]) - text[17] - text[8])
s.add(text[12] == text[14] + text[8])
s.add(text[13] == text[45] + text[20])
s.add(text[14] == text[9] + (text[20] ^ (text[25] - text[48])))
s.add(text[15] == text[18] - text[31])
s.add(text[16] == (text[24] ^ text[46]))
s.add(text[17] == ((text[13] + text[2] + text[47]) ^ (text[14] ^ text[50])))
s.add(text[18] == text[0] + text[36] + text[44] - text[3])
s.add(text[19] == (text[41] ^ text[30]) - text[25] - text[28])
s.add(text[20] == (text[25] ^ text[44]))
s.add(text[21] == text[25] + ((text[28] + text[22]) ^ (text[39] ^ text[21])))
s.add(text[22] == (text[31] ^ (text[44] - text[4] - text[12])) - text[30])
s.add(text[23] == (text[39] ^ (text[32] - text[14])))
s.add(text[24] == (text[21] ^ (text[0] ^ text[18] ^ text[21])))
s.add(text[25] == text[18] + text[4] + (text[12] ^ text[17]) - text[11])
s.add(text[26] == (text[32] ^ text[46]) + text[49] + text[20])
s.add(text[27] == text[36] + text[25] + text[39] - text[48])
s.add(text[28] == (text[14] ^ text[15]))
s.add(text[29] == text[1] + text[35] - text[42])
s.add(text[30] == text[8] - text[31] - text[30] - text[24])
s.add(text[31] == (text[42] ^ (text[15] + text[18] - text[29])))
s.add(text[32] == text[14] + text[5] + text[15] - text[44])
s.add(text[33] == (text[20] ^ (text[45] - text[15])) - text[32])
s.add(text[34] == (text[3] ^ text[33]) - text[20] - text[10])
s.add(text[35] == (text[44] ^ (text[6] - text[43])) + text[1] - text[44])
s.add(text[36] == (text[49] ^ (text[31] + text[25] - text[28])))
s.add(text[37] == text[11] + (text[34] ^ text[31]) - text[34])
s.add(text[38] == text[42] + (text[27] ^ text[36]) - text[5])
s.add(text[39] == (text[37] ^ text[8]))
s.add(text[40] == (text[44] ^ (text[7] + text[28])) - text[10])
s.add(text[41] == (text[20] ^ (text[7] ^ text[17] ^ text[26])))
s.add(text[42] == text[50] + text[1] - text[28])
s.add(text[43] == text[46] + text[33] - text[15])
s.add(text[44] == ((text[24] + text[42] + text[16]) ^ (text[45] ^ text[21])))
s.add(text[45] == text[22] - text[40])
s.add(text[46] == text[12] - text[46] - text[7] - text[35])
s.add(text[47] == (text[39] ^ (text[15] + text[26])) - text[12])
s.add(text[48] == (text[11] ^ (text[15] - text[8])))
s.add(text[49] == (text[27] ^ text[37]))
s.add(text[50] == ((text[13] + text[8] + text[17]) ^ (text[24] ^ text[15])))

if s.check() == sat:
    m = s.model()
    flag = ''
    for i in xrange(FLAG_LENGTH):
        flag += chr(int(str(m[text[i]])))

    print('RESULT : {0}'.format(flag))

結果

$ python unbrekable.py 
RESULT : CTF{0The1Quick2Brown3Fox4Jumped5Over6The7Lazy8Fox9}
$ ./unbreakable-enterprise-product-activation CTF{0The1Quick2Brown3Fox4Jumped5Over6The7Lazy8Fox9}
Thank you - product activated!

参考記事

IoTセキュリティハッキングコンテスト神戸で大洗女子学園は廃校になりました

イベント CTF

IoTセキュリティハッキングコンテスト神戸2016 | Kobe Digital Labo 神戸デジタル・ラボ に大洗女子学園を代表して参加してきました。IoTセキュリティハッキングコンテストというのは、要はIoTに関係がある問題が出るCTFですね。

様子





結果

高校の存続をかけて大洗女子学園代表として参加しましたが4位で惜しくも入賞できず、大洗女子学園は廃校になりました。今後は継続高校で精進していこうと思います。