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

妄想まとめ

研究とかWebセキュとか時事ネタとか。 @kazu1130_h

SECCONオンライン予選 Write-up

SECCONオンライン予選にちょこっとだけ参加して遊んできました。
解いたのはWeb400,Bin100,その他200です。

Web400を一番乗りで解いたし、Web500の裏道も見つけたしで、名古屋大会の時よりかは人権があったかなと思います。(ただし未だにやぎはしゅ/10な感じ)
あと何よりボウリングで笑いましたww



さて、では本題。


Web・ネットワーク 400 SECCON競馬

八百長疑惑のあるSECCON競馬ですが、オールスターレースの全着順を予想し、 馬番を1着からカンマ区切りで答えなさい。
===========解答例ここから===========
16,17,12,8,2,15,1,7,9,14,3,5,13,10,11,6,4
===========解答例ここまで===========

既にtyageさんが書いてますが、まぁ冗長なWrite-upでも書こうかなと。


とりあえず何やってるかを見るためにアカウント登録+Burpを挟んで観察。
……ってあれ、何も起こらない。んー???とか思ってたら、Websocketベースのアプリでした。
で、それに対応したProxyツールが無かったので諦めてJSを解析。
client.jsが全てを動かしてるっぽかったので、とりあえず狙いをこのコードに絞る。

と同時に、問題文的にロジック系かSQLiかなー、みたいなアタリもつけとく。終わったレースの結果は表示されているので、時間をいじるなり終わった事にするなり、SQLiするなり、みたいな。

さらっと読んでみるとコメントが入ってるわ難読化されてないわで読みやすい。親切設計。
でも丸々読む気力は無かったので、Chrome(諸事情によりIronだけど)のデベロッパーツールでJS中の適当な所にブレークポイントを入れて、レース結果処理を追っかける。

すると以下のコードが。

	get_race_info: function(message) {
		var socket = this;
		if(message.status!=='OK'){
			return;
		}
		$('h2#race_name').text(message.data.course + (message.data.seq>0?'第'+message.data.seq:'') + 'レース')

		$('div#tabs').css('display', 'block').attr('race_id', message.data.id);

		$('#win_rate').text(message.data.win_rate*10+'G');
		$('#box_rate').text(message.data.box_rate*10+'G');
		
		if(message.data.done){
			socket.emit('get_result', {id: message.data.id});
			$('.result_contents').css('display', 'block');
			$('.vote_contents').css('display', 'none');
			$('.odds_contents').css('display', 'block');
			$('#tabs a[href^="#panel"]:eq(0)').trigger('click');
		} else {
			$('.result_contents').css('display', 'none');
			if(loginuser){
				$('.vote_contents').css('display', 'block');
				$('#tabs a[href^="#panel"]:eq(1)').trigger('click');
			} else {
				$('.vote_contents').css('display', 'none');
				$('#tabs a[href^="#panel"]:eq(2)').trigger('click');
			}
		}
	},

終わっている奴はmessage.data.doneがtrueになってて、その場合はsocket.emit('get_result', {id: message.data.id});を実行している、と。

というわけで

  if(message.data.done){
    (略)
  }

  if(true){
    (略)
  }

に書き換えて実行。


しかし何も起こらず。うーん……そこはチェックされてるみたい。
で、コードを漁っても面白いものが無かったので、今度は視点を変えてSQLiを狙う。しかし前述の通りwebsocketなので、Burpとかは使えない。てわけで、使ったのはこのコード。

		$('a.race').click(function(){
			$('div#tabs').css('display', 'none');
			$('div#odds').css('display', 'none');
			$('div#result').css('display', 'none');
			$('tbody#win_odds').empty();
			$('tbody#box_odds').empty();
			$('tbody#result').empty();
			$('tbody#win_vote').empty();
			$('tbody#box_vote').empty();
			
			var race_id = $(this).attr('id').substr(5);
			// レースIDを指定して、レース情報を取得する
			
			socket.emit('get_race_info', {id: race_id});
			socket.emit('get_entries', {id: race_id});
		});
		if($('#tabs').attr('race_id') && parseInt(message.race_id)==$('#tabs').attr('race_id')){
			socket.emit('get_result', {id: message.race_id});
			$('.vote_contents').fadeOut();
			$('li.result_contents').fadeIn();
		}
	},

これはclient.js中にあったコード。見ての通りクリックされた時に起きるし、Socketの結果に依存せずに実行される。オマケにこれが実行される時はSession周りの処理も終わってるし、何も考えなくても自然な通信が行えるので使いやすい。一方で、SQLエラーでWebsocketに変なメッセージが来たら読み取れない可能性があったので念のためwiresharkで読めるようにしとく。

とりあえず

  socket.emit('get_race_info', {id: "0+1"});
  socket.emit('get_entries', {id: "0+1"});

を実行。そしたらちゃんとid:1のデータを取ってきた。

  socket.emit('get_race_info', {id: "0 OR 1 = 1 #"});
  socket.emit('get_entries', {id: "0 OR 1 = 1 #"});

は何故かエラー。でもSQL syntax errorって返って来た。うん、確定。


後はまぁいつも通りの流れ。要素数探ってく時にget_race_infoは多くてget_entriesは少ないっぽかったから、get_entriesをターゲットにしたくらいしか工夫点無いです。
詳しくはSQL 攻撃方法 とかでググると何か出てきます。凄い時代になったなぁ……。



その他 200 Encode me.

パスワードを答えよ。

encode_me_91

一日目はさらっと流してたけど、二日目の終了1,2時間前によし、やるか!!ってなった。
とりあえずencode 91でググるかー → base91なんてのがあるのか。とりあえず落として試すか → 

PASSWORD/IS/WHICH+ENCODING+DO+YOU+LIKE

取り掛かってから10分もしないうちに解けた。なんという……


バイナリ 100

このゲームをクリアしたらパスワードが得られる。
you will get the password if you get a goal of the dungeon.

game.zip

一日目は見向きもしなかったけど二日目やることが無くなったのでやった問題。

とりあえず解凍してIDA Proに投げ込む。64bit。うん、俺には無理だな。

……いやまてよ、SECCONって確かシューティングゲームとか出てたよな。あん時は画像を差し替えるだけだったな……。
というわけで、チートしてゲームクリアって方針を立てる。

とりあえずRead Meを読む。

DX library
http://homepage2.nifty.com/natupaji/DxLib/

sample code
http://homepage2.nifty.com/natupaji/DxLib/program/dxprogram_3Dmeiro.html

成程、このライブラリとこんな感じのコードで動いてるのか。

起動してみる。やっぱただの迷路。しかも不思議なダンジョンみたいにランダムに変わるのではなく、固定迷路。

で、dataフォルダを漁ると壁の画像とPrintPasswordって書かれた画像と、それぞれのメタセコイア用3Dイメージが。
とりあえず壁を透明にしてみる。

f:id:kazu1130_h:20140126164236j:plain


スタート地点の横にゴールがっ。
でも流石に判定は消えてないので、そのまま進めるなんて事は無い。


透明にした関係で、黒いマスは進める事もあれば進めない事もあるマスに、緑のマスは絶対に進めるマスになった。

あとはコレをがんばって辿ってみるだけ。そんなにMAP広くないので、初見でも2分くらいでゴール出来ると思います。



以上、めっちゃ雑なwrite-upでした。


全体の感想としては、ゴミ箱さんのソロチームに普通に負けたとかkatagaitaiとteam enu強すぎワロタとかですかね。まぁでもバイナリアン無しでここまで取れたので、それなりに良かったかな、みたいな。
あと箱庭XSS、クソワロタです