※プログラムの解析に類する内容を扱っております。
※ハッキングと称していますが、犯罪に類する行為は一切しておりません。
※混乱を避けるため当該キャンペーンの終了を待って、この記事を公開させていただきました。
プロモーション周りの分析も書いているのでこちらもぜひご一読ください
https://note.mu/aiharayuki/n/n87909fdd40c7
まず実際に抽選してみましょう
この「くじを引く」ボタンを押すことで抽選がされるわけだ。
ハズレの画面はこんな感じ
どういった仕組みでくじ引きが動いているんだろう
phpかなんかでサーバーサイドで制御してるのかなー。
https://www.oyolife.co.jp/tokyo-cp/
どうやらjsで抽選をしているみたいだぞ?
でもまず基本のscript.jsをみてみよう
クッキー消して何回かくじ引きしよう
https://www.oyolife.co.jp/tokyo-cp/js/script.js?ver_2
どうやらくじ抽選はクッキーで管理しているらしい。
クッキーは開発ツールで削除できるので、その値をいじれば何回でもくじ引きができる・・・??
当方、Chromeを普段使用しております。
⌘+option+iで開発コンソールを開きましょう。(便利だから覚えて欲しい)
はいはい、"kuji"がありましたね。これを"true"以外の値にすることでくじ引き制限は回避できそうです。
おーできるようになった。
よっしゃくじ引き無限にできるで!!!!!!!!!!
ああああああああああああああああああああああ!!!!!!!!!!!!!何回やってもはずれる!!!!!!!!!俺の夢の1人暮らし生活は実現せんのんか!!!!!!!!!!!?????????
ええやろう。そういえばわいはプログラマーや。もうちょいコード見てみるで。うん。落ち着こう。改めてjsを見てみよう
さっきのscirpt.jsをもう一回見てみる
lottery.loadLottery()
こいつが抽選してるんやろ。さっき気になっていたlottery.jsを見てみよう。
https://www.oyolife.co.jp/tokyo-cp/js/lottery.js
ようわからんのう。
まあでもコードってものは必ず読み解けるものだっていうのはこれまでのエンジニア経験からしてわかっている。
初めて関数につぐ関数のソースを読み解いて行った時
プラグインの中身を読み解いた時
英語のリファレンスを読み始めた時などetc...
あの複雑そうな見た目に対して「とりあえず読み進める」ということを実行するのがどんなに難しいことか。しかしその壁を乗り越えられるとエンジニアとして一歩成長したなと思うのです。
(一番伝えたいことは実はこれ)
なんかわかりづらいがポイントを整理してみよう
気になるコードを見てみましょう。結構個人的な感覚になりますが。
(i=this).requestURL="/tokyo-cp/data/roomData.json?data=",
i.rejectURL="/tokyo-cp/reject.html",
おう?なんだ/tokyo-cp/data/roomData.json?data=
って
"/tokyo-cp/reject.html"
はなんか排除してるのか?
あ、
これや。
https://www.oyolife.co.jp/tokyo-cp/reject.html
ハズレのページのURLや。なるほど。
じゃあ/tokyo-cp/data/roomData.json?data=
は・・・?
roomData.jsonを見てみよう
もうちょい気になるところをみてみよう
return x.prototype.loadLottery=function(){
$.ajax({url:i.requestURL+Date.now(),type:"GET",cache:!1}).done(
function(t){
i.data=t,i.firstLottery()
}).fail(
function(){
i.lostLottery()
})
},
loadLotteryはscript.jsで使われていましたね。
なんかajaxでデータを取得しているのがわかります。
ああ、さっきのroomData.jsonはここの値として参照されてるわけだな。
Date.now()も使ってるけどなんか違いあるのかな・・・(これまだ未検証)
x.prototype.firstLottery=
function(){
var t=100,n=i.data.percent<t?i.data.percent:t,o=[],r=i.getPosition(n);if(0<r){
var e=Math.pow(10,r);n*=e,t*=e
}
for(var a=0;a<t;a++){
var c=a<n?1:0;o.push(c)
}
1===i.selectData(o,t)?i.secondLottery():i.lostLottery()}
これはあれやね。パーセントの計算しながらとりあえず抽選を色々してるね。
x.prototype.lostLottery=function(){location.href=i.rejectURL}
まあ名前からしてハズレの関数だよねと
x.prototype.secondLottery=
function(){
for(var t=i.data.roomData,n=t.length,o=[],r=0;r<n;r++){
var e=t[r].rooms;
if(0<e)for(var a=0;a<e;a++)o.push(u(t[r].id))
}
if(0===o.length)i.lostLottery();else{
var c=i.selectData(o,o.length);
location.href="/tokyo-cp/detail/"+c+".html"
}
}
特殊な書き方だな・・・当然ですが解析されないよう色々分かり辛く書かれてありますね。
さて、見てみると、なんかroomdataを使ってoの中にu関数を使って配列にpushしてますね、t[r].idを。どうせroomのidでしょう。
それでですね、気になるURLを見つけました。
location.href="/tokyo-cp/detail/"+c+".html"
なんやろうこのURL。cってなんやろ。
var c=i.selectData(o,o.length);
cはこれや。何か選ばれた結果が格納されているはずや。
直接書いてるので貼ってみよう
さっき気になってたようわからん文字列があったな。
roomData.jsonの中にあったidを打ってみよ。
https://www.oyolife.co.jp/tokyo-cp/detail/6816c5e4.html
あっっったっった!!!!!!!!!!!!!!あったっっっっっっっった!!!!!!!あああああああ!!!!!!!!
やった!これ正解のURLやんけ!
ギリギリCTOでよかった!!!!!!!!!!!!!!!
翌日対応される(3/15)
対応されてました。jsondataが生じゃない。
ああ、今後先に出てくる物件全て自分のものにしようと思ったのに・・・
なんでやねん!
だがしかし食らいつく
まあidが数字になっただけで、実際最後の文字列が取得できればいいんだけどなあ。
ということで気になったのが(自分の第六感が冴えてた)
if(0<e)for(var a=0;a<e;a++)o.push(u(t[r].id))}
これ読み解けばわかるんですがt[r]にはroomに関するjsondataが入ってます。
どうやらここの関数で何かしているみたいだ。u()
ここでu()は何をしているかがポイントです。
var u=function(t){
for(var n=a("0xf")+t,o=n.length,r="",e=0;e<o;e++)
r+=n.charCodeAt(e);
return Rusha[a("0x10")]()[a("0x11")](r)[a("0x12")](a("0x13"))[a("0x14")](0,8)
};
さてさて、よくわからない文字列が出てきましたよ。
0x10とか、0xfとか、これって文字コードのASCIIコードとかで見かけるに似てるな?
何進数とかそういう類の奴だな?
えーと、Rushaってなんだろう。
とにかくなんらかの関数を通して最終的に文字列を生成しているみたいだなと。
まずはa()を解読
var r,e,a=function(t,n){
return o[t-=0]
},
うーん。これだけではわからない。
でもa()で何か最終的な文字列を出しているのはなんとなくわかる。
Rushaを解読
Rushaってどこかで見覚えあります?
はいそうです、jsで読み込んでましたね
<script type="text/javascript" src="./index_files/rusha.min.js"></script>
この中見るとまあ分かり辛いんすよ。
https://github.com/srijs/rusha
A high-performance pure-javascript SHA1 implementation suitable for large binary data.
あー、暗号化してるんですね。やっぱり。
えーっと、この暗号化のための関数を使えるようになればええんよな。
てことで
./index_files/rusha.min.js
を開いてコピペして、開発コンソールのjs書けるところにどん!!!!!!!!
そしてlottery.jsの中身もうまくコピペして開発コンソールのjs書けるところにどん!
そうするとですね、a()が使えるようになってるはずなんですよ。
(色々検証するためにちょっと自分で編集してる部分あります)
こんな風にですね、a()が使えるようになりましたわけですよ。
やっぱa()で文字列をデコードできるみたいですね。
そこでね、つまりはu()も使えるようになってるはずなんですよ。
はい、使ってみましょう。
(まあ全部手元の環境で作って検証してもいいのですが)
はい、かなりそれっぽい文字列が出てきました。
こうやって文字列生成しているわけですが、これをまた使ってみましょう。
https://www.oyolife.co.jp/tokyo-cp/detail/322ef092.html
あっっっっったっっっっっったンゴおおおおおおおおおおおおお!!!!!!!!!
3/15に行われたOYO様側での対応があったが故に導き出すことができました。
jsで全て管理することの難しさ
ご覧のように、jsってフロントサイドから簡単にいじれるわけですよね。
今回の件から鑑みるに、開発者にとっては諸刃の剣ともいうべきでしょうか・・・
やはりこういった制限が必要である機能に関してはバックエンドでの開発も必須ということでしょうか。。。あとは最終的な文字列を出力する関数はフロント側から見えるところに置かないようにするべきですね。
スタートアップにおけるセキュリティ対応の難しさ
実は私もバリバリのどベンチャーで働いております。
約4年前に知り合いとweb制作の会社を立ち上げ、現在もそこで働いております。
これまでにいくつかサービスを立ち上げてきたのですが
やはりリリース優先・機能開発優先になってしまい、
どうしてもインフラやバリデーション、セキュリティ面に関しては後々の対応になってしまいます。。
これはスタートアップという状況においては解決し辛い問題で、ユーザーの直接のメリットに繋がる機能を優先して開発しつつも、サービスが攻撃されても一発で死んでしまわないよう最低限のセキュリティの構築をする必要があるわけです。
そのバランスが難しいわけですよね。難c。
最後に
実は、ギリギリさんも不動産賃貸業界の未来を明るく照らすプロダクトをつくっています。なんやねんそれって興味をお持ちの方はぜひご連絡ください。一緒に働くエンジニアも、フルタイム・副業問わずに絶賛募集中です。
https://twitter.com/aaaaanochira
またスタートアップの経営者やマーケティング担当者の方は、
こちらもぜひご一読ください
https://note.mu/aiharayuki/n/n87909fdd40c7