ふるつき

v(*'='*)v かに

サイバーコロッセオに参加した

Musashiというチームでサイバーコロッセオに参加していました。

MusashiとHarekazeの人々が集まっている中、誰の顔も補足できなかった僕は一人で会場入りしそうになり、泣きそうでした。

サイコロは King of the Hill 形式でしたが、今回も、SECCON本選に続いて、 jeopardy してました。

僕が解いたのは二問くらいで、 cat keyword1.txt すると 100pt が入る問題と、 nano keyword2.txt すると 100pt が入る問題です。

強いチームはガンガン点数を入れていて、まじかこれが力の差か……となりました。もっと解けるようになりたいし、 defense keyword が意味をなす程度には強くなりたいなと思いました。

WhiteHat WARGAME Writeup

Harekaze で WhiteHat WARGAME に出てました。 Challenge 01 と書いてあったけど本当に 01 かどうかはちょっとわからない。

Harekazeは 140pt で五位。日本勢では1番でした。

私は Crypto001 と Re001 を解いて、45ptを入れました。得点の殆どを st98 さんが入れててすごいなと思いました。

Re001

Faster.jar というファイルが渡されます。実行すると、ランダムな値がコンボボックスに追加されていくアプリケーションでした。次の値を予測して入力し、合ってたら何かしらがあるような感じでした。 jar なのでデコンパイルしたら、FLAGを生成する処理が見つかったのでそこだけ抜き出したらちゃんとフラグになりました。

import java.util.Stack;
class Main {
        Character[] ListChar;
        int[] ListPos;
        Stack<Double> ListRandomNumber;

        public Main() {
        this.ListChar = new Character[] { 'a', '_', 'y', 'l', '_', 'l', 'a', 'T', '_', 'T', '_', 'T', 'e', '_', 'y', 'e', 'r', '_', 'S', '_', '_', 'l', 'r', 'T', 'F', '_', 'Y', '_', 'l', 'e', 'T', 'T', 'T', 'a', 'r', 'T', 'u', 'A', 'o' };
        this.ListPos = new int[] { 11, 7, 14, 13, 26, 22, 4, 34, 15, 37, 3, 31, 19, 27, 23, 6, 18, 25, 30, 24, 17, 12, 9, 38, 28, 8, 0, 16, 21, 10, 32, 36, 33, 20, 5, 35, 2, 29, 1 };
        this.ListRandomNumber = new Stack<Double>();
        }

    public static void main(String[] args) {
        Main a = new Main();
        System.out.println(a.generateFlag());
    }

    public String generateFlag() {
        String flag = "";
        final Character[] tmpListChar = { '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0', '\0' };
        final int lenFlag = this.ListChar.length;
        for (int i = 0; i < lenFlag; ++i) {
            tmpListChar[this.ListPos[i]] = this.ListChar[i];
        }
        for (int i = 0; i < lenFlag; ++i) {
            flag = String.valueOf(flag) + tmpListChar[i];
        }
        return flag;
    }
}

Crypto001

ワンタイムパッドだけど何回も暗号化しちゃったという問題でした。渡されるのは↓

0e4f0c054e4a0c034d430c031454034f0106010019071149040b5941466c0d1141100c000859540a0a440f09124b
0b060901000b031540590a4f1a46000a071d44591b1d45414b161c4d4f220154430e080007455a482c54f41f4104
0e094f1d4f1f4f114d451c1f554f084f190702455409094c4b1c165b003b0c184c46010f12455409174546081302
0b0a0e164e4a090d534d59161053120a070a05595a4829491d0059484f3e45004f0208174a003c071545460a0e15
141b0e1d00021a0c465200435553120e0c4e024f1b040c5303360d4f596c0d014e011b174800071c0459460a0e08
13070a444d0b060c015411061b47461b1d0f10000d07100003040f4b00380a545203040b0942111a454f084c150f
21030e0300031c58016f170a2a740f021031344110370c53340b165a7f3c000646030a1a3b521d0f0d54590a0d06

解くのには↓のようなスクリプトを使いました。

import sys

a = open('cipher.txt').read().splitlines()
a = map(lambda x: x.decode('hex'), a)
l = len(a)

k = ""
pk = ""
while True:
    k = pk + str(raw_input(pk))

    for i in range(len(a)):
        r = map(lambda x: ord(x[0])^ ord(x[1]), zip(a[i%l], k))
        r = map(chr, r)
        print("".join(r))
    r = str(raw_input("OK?")).strip()
    if len(r) == 0:
        pk = k

適当に flag と打ってみたら、最後の行が Good になってたのであたりっぽかったので単語を予測していくと鍵は Good job! you found the key. Let find the flag ぽくて、 出てきた平文が

I can calculate the motion of heavenly bodies,
Life always offers you a second chance. It�s c
If you sleep on life all you will have are dre
Learn form yesterday. Live for today. Hope for
Stay hungry, stay foolishStay hungry, stay foo
The main thing that you have to remember on th
flag is: One_Time_Pad_is_not_perfect_right?fla

でした。


Re002 とか解きたかったんですが重たいバイナリを読むのが苦手で負けてしまいました……

部内でプロコンを開いた

 部内でプロコンを開いたので振り返りも兼ねてまとめておく。

開いたのは、 JMC (Joken Marathonc Contest) で、86時間のマラソンマッチ。開こうと思った経緯から準備・開催中までを書く。

できたもの

要するに n2-1パズルとかスライドパズルと言われるようなパズルで、魔方陣を作ろうぜ、という感じのパズル。各行各列と対角線でsumをとって、その分散を0に近づけたい(魔方陣なら0になるはず)、というもの。

 これを組み上げて、とりあえずテストということでkosen14sに投げた。CSRFについて指摘されたので対策しました。

 それで、まあ、Jokenでもアナウンスして、レジだけならそこそこの人数(10人くらい)になりました。

開催

 ptr-yudaiともう一人が最初に遊んでくれました。僕はSlackの通知のためにURLを設定するのを忘れていて、慌ててURLを追加したりしました。

 事前に問題文だけ公開していたのでptr-yudaiはソルバを書いてたらしく、いくらか提出をしてきました。なんか師匠の試算したスコアとサーバの弾き出すスコアが違ったっぽいのですが、とりあえずぼくが間違えていたわけじゃなかったのでそれは安心。

 今回の問題では、最小は 10 * 10 のパズルで、100*100 とか 1000*1000 でやったんですが、 100 * 100 でも十二分に大きくなってしまったのでまあ 1000 * 1000 はいらなかったかなという感じでした。

 その後、ptr-yudaiがいろいろ頑張って値を改善する時間が続きました。四年生が、手で挑戦したりしてくれました。残念なことに、それ以外の人々がまったくといってよいほど参加してくれませんでした。その晩、kyumina が取り組もうとしてましたが、結局挫折したみたいでした。    前日の反省を受けて、翌日は、スコア計算プログラム(C++版)を Slack に流したり、10*10より小さいケースを幾つか追加したりしました。これはそれなりに人気で、最小のケースでptr-yudaiがおよそ最適解と思われる解も叩き出してくれて一時だけ盛り上がりました。

 その日は、お昼過ぎにptr-yudaiを超える提出がありました。僕も楽しくなってきて自分のソルバを試しましたが、それとどっこいくらいでした。その晩は、 kyumina 向けに、「乱択アルゴリズムを使うと楽ですよ」とかきました(僕の実装が乱択やきなましもどきだったので)。僕もビームサーチもどきに初挑戦しましたが、全然だめだったりしました。

 それでだいたいおわり。結果はこんな感じでした。

f:id:Furutsuki:20170222112826p:plain

感想 反省

 レジるだけで参加してくれないひとが多かったので残念だなーと思いました。ハイスコアを狙うなら別ですが、ちょっと自分でソルバを書くだけならそんなに難しい問題ではないとおもったので(何しろ選択肢が殆ど無いので)、頑張ってほしかった。

 こちらとしても、もっとヒントをだしてもよかったな(例えば私のソルバを投げるなど)とか、もっと小さい入力でもよかったなとか、86時間は長いみたいだし24時間とかでもよかったなとか、いろいろ考えさせられました。いい経験担ったと思います。