jtwp470’s blog

日記とかプヨグヤミングとか

VolgaCTF 2016 Tic-Tac-Toe Writeup

お久しぶりです.たんごです.週末はVolgaCTFとpwn2win CTFが被っていてあびゃーって感じでした.結局あまり集中して解くことが出来ずVolga CTFに出題されていたTic Tac Toeの問題だけ解くことが出来たのでそれのWriteupとしてメモ書きを残しておきます.

Tic-Tac-Toe

問題文:

Tic-Tac-Toe

An important step towards the strong AI is the ability of an artificial agent to solve a well-defined problem. A project by the name 'tic-tac-toe' was one of such test problems. It's still up...

nc tic-tac-toe.2016.volgactf.ru 45679

$ nc tic-tac-toe.2016.volgactf.ru 45679
Welcome to Noughts & Crosses World Championship!
Please, name yourself.
jtwp470
The match is played over 500 rounds, the winner of each round gets 1.0 points, the looser gets 0.0 points, and in case of a draw each player gets 0.5 points.
To make your move choose the empty cell and send it's index followed by '\n', e.g. '4\n'.The indices map:
 0 | 1 | 2
---+---+---
 3 | 4 | 5
---+---+---
 6 | 7 | 8

Round number 1.
Server vs. jtwp470. Current score: 0.0 - 0.0
   |   |
---+---+---
   | X |
---+---+---
   |   |

0
 O |   |
---+---+---
 X | X |
---+---+---
   |   |

5
 O |   | X
---+---+---
 X | X | O
---+---+---
   |   |

6
 O |   | X
---+---+---
 X | X | O
---+---+---
 O | X |

1
 O | O | X
---+---+---
 X | X | O
---+---+---
 O | X | X

Round number 2.
Server vs. jtwp470. Current score: 0.5 - 0.5

こんな感じ.Alpha Goみたいにめっちゃ強いやつ出てきたらどうしようと思っていたけど流石にそんなことはなかったw

Tic-Tac-Toe とは

ぐぐるWikipediaの記事3目並べ とかが見つかる. これ授業でやったなあという感じ.

このゲームでは、先手・後手ともに最善を尽くすと必ず引き分けとなる。

と書いてあるので最善手を打ち続ければ少なくとも負けることはないことがわかる.

またいろいろ調べているとミニマックス法とかアルファベータ法と呼ばれるアルゴリズムが出てくるが結局のところ以下の様な方針で実装すればいいらしい.

  1. 3つ並べて勝てる場合はそこにおいて勝つ.
  2. 相手が3つ並べて勝てる場合は妨害する
  3. 中央が空いていればそこに置く.
  4. 四隅が空いていればそこに置く.
  5. 置ける場所に適当に置く.

というわけで盤面を読み込み上の方法を使ってこまを置く場所を決定しゲームを進めるスクリプトを書いた.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import random
import re
import socket
import itertools


def print_board(b):
    print("{0:^3}|{1:^3}|{2:^3}".format(b[0], b[1], b[2]))
    print("---+---+---")
    print("{0:^3}|{1:^3}|{2:^3}".format(b[3], b[4], b[5]))
    print("---+---+---")
    print("{0:^3}|{1:^3}|{2:^3}".format(b[6], b[7], b[8]))
    print("")

# ['X', '', 'O', 'O', 'X', '', 'X', 'O', 'X']
# ['', 'X', '', '', 'O', '', '', '', '']

"""
一番強い(というか最善手の) AIアルゴリズム

1. 置けば勝てるなら置く
2. 敵が勝ちそうであれば妨害する
3. 優先順位的に真ん中 -> 端 におく
4. それも空いていない場合は 諦めて 適当な場所に置く.
"""

lines = (
    (0, 1, 2), (3, 4, 5), (6, 7, 8),
    (0, 3, 6), (1, 4, 7), (2, 5, 8),
    (2, 4, 6), (0, 4, 8))


# my_hand で勝てるかどうか? 勝てるのであれば置くべき場所を返す
def is_win(board, my_hand):
    for pos in lines:
        for ps in itertools.permutations(pos):
            p = board[ps[0]]
            if p == "" and board[ps[1]] == my_hand and board[ps[2]] == my_hand:
                return ps[0]
    return -1  # わからんw


def is_draw(board):
    rest_board = [x for x, y in enumerate(board) if y == ""]
    if len(rest_board) <= 2:
        if is_win(board, "O") == -1 and is_win(board, "X") == -1:
            return True
    return False


# 今の状態から次にどこに置くかを決定する
def decide_pos(board, my_hand):
    enemy_hand = ""
    if my_hand == "X":
        enemy_hand = "O"
    else:
        enemy_hand = "X"

    # もし勝てるのであれば勝つ
    p = is_win(board, my_hand)
    if p >= 0:
        return p

    # もし負けるのであれば妨害を入れる
    p = is_win(board, enemy_hand)
    if p >= 0:
        return p

    # もし中央が空いていればそこに置く.
    if board[4] == "":
        return 4

    # もし隅が空いていれば隅の適当な場所に置く.
    corner = (0, 2, 6, 8)
    for c in corner:
        if board[c] == "":
            return c

    # それ以外の場合は適当に置く.
    for c in range(9):
        if board[c] == "":
            return c
    return None


def read_board(sc_board):
    """
    t is following input.
     X |   |
    ---+---+---
       |   |
    ---+---+---
       |   |
    """
    board = []
    sc = sc_board.replace("---+---+---", "").split("\n")
    for s in sc:
        if s != "":
            s = map(lambda x: x.strip(), s.split("|"))
            for k in s:
                board.append(k)
    return board


def sock(reip, report):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((reip, report))
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    return s, s.makefile('rw')


def read_until(f, delim='\n'):
    data = ""
    while not data.endswith(delim):
        data += f.read(1)
    return data


def main():
    # HOST = "tic-tac-toe.2016.volgactf.ru"
    # HOST = "95.213.237.93"
    HOST = "95.213.237.91"
    PORT = 45679
    s, f = sock(HOST, PORT)
    print(read_until(f), end="")
    print(read_until(f), end="")
    s.sendall(b'jtwp470\n')
    print(read_until(f, "\n\n"))

    print(read_until(f), end="")  # Round number X.
    print(read_until(f), end="")  # Server vs. k. Current score: 0.5 - 0.5

    # 最初に2行読み込む.
    # もし Round が入っていたら 一度リセット
    my_hand = "O"  # start は 自分が O
    while True:
        inp = ""
        inp += read_until(f)
        inp += read_until(f)
        if "Volga" in inp:
            print("Congrats!")
            print(inp)
            break

        if "Round" in inp:
            m = re.match(r"Round number (\d+).", inp.split("\n")[0])
            round_number = int(m.group(1))

            print("Round : %d " % round_number)
            print(inp)
            # 偶数の時は自分は X
            if round_number % 2 == 0:
                my_hand = "X"
            else:
                my_hand = "O"

            print("my_hand is : " + my_hand)

            inp = ""
            inp += read_until(f)
            inp += read_until(f)

        inp += read_until(f)
        inp += read_until(f)
        inp += read_until(f)
        inp += read_until(f)
        board = read_board(inp)

        x = decide_pos(board, my_hand)
        if x is not None:
            s.sendall((str(x) + "\n").encode())
            print("Send: % d" % x)
        print_board(board)

    s.close()
    f.close()


def test():
    ## test_code
    board = ['X', 'X', '',
             '', '', '',
             '', '', '']
    assert decide_pos(board, "X") == 2
    board = ['X', '', 'O',
             'O', 'O', '',
             '', '', '']

    # 妨害を入れる
    assert decide_pos(board, "X") == 5
    board = ['X', 'O', '',
             'O', '', '',
             '', '', '']
    assert decide_pos(board, "X") == 4

    board = ['X', 'O', 'X',
             'O', 'O', 'X',
             'X', 'X', 'O']

    print(decide_pos(board, "X"))

if __name__ == "__main__":
    main()


    """
    ...
    Round : 500
    Round number 500.
    Server vs. jtwp470. Current score: 73.0 - 426.0

    my_hand is : X
    Send:  4
       |   |
    ---+---+---
       |   |
    ---+---+---
       |   |

    Send:  0
       |   |
    ---+---+---
     O | X |
    ---+---+---
       |   |

    Send:  8
     X |   |
    ---+---+---
     O | X |
    ---+---+---
       | O |

    Congrats!:
    Server vs. jtwp470. Final score: 73.0 - 427.0
    Congratulations! Your flag is: VolgaCTF{tic_t@c_t0e_is_the_e@rly_step_t0wards_AI}
    """

最終スコアは73 - 427 でスクリプトの勝ち.全試合の記録はしていないがあまり負けることはなく大抵アイコになって相手に点数取られるみたいなことが多かった.

辛かった点としては自宅のネットワークからロシアにあるであろうサーバーの間でパケットが途中で喪失してしまうのかConnection refusedに苦しまされ続けた.Wiresharkでパケットを追ったりしつづけ結局動かないので大学のサーバーにアクセスしそこでこのスクリプトを動かしたらうまいこと動いてくれたのでなんとか点数を入れることが出来た.

最後に

最近「トライセイルのトライアングルハーモニー」を聴き始めて色んな意味で癒やされています.ハイ 面白いのでぜひ聞いてみては

あとエクストリームCTF1問目からわからずつらい思いをしているのでなんとかしたいです

Internetwache CTF 2016 Writeup

こんにちは.たんごです.授業も終わり就活も佳境になっている今日この頃.やるべきことをすべて放り出しCTFに取り組んでいました. Internetwache CTFは比較的簡単なCTFで私のような初心者にも非常に簡単に答えを出すことが出来たのでとっつきやすいCTFという感じでしたがrevやexpがちょっとrevらしく, exploitらしくない問題が出題されていたりとちょっと首を傾げそうな感じです.

最終順位は180位 970点であともうちょっとで1000点超えというような位置でした. 兎にも角にも珍しく問題を大量に解いたり特定ジャンルを全完したりと楽しいCTFでしたので解けた問題のWriteupを載せておこうかなと思います.

Misc

Misc 50

Description: My friend really can't remember passwords. So he uses some kind of obfuscation. Can you restore the plaintext?

ファイルを解凍するとみた感じ8進数でダンプされたファイルが出てきます.適当に数字部分をPythonに投げつけて文字列に変換するとどうやらbase64っぽい文字列が出てきたのでデコードします.

$ echo "V2VsbCBkb25lIQoKRmxhZzogSVd7TjBfMG5lX2Nhbl9zdDBwX3kwdX0K" | base64 -D
 Well done!

 Flag: IW{N0_0ne_can_st0p_y0u}

Flag: IW{N0_0ne_can_st0p_y0u}

Misc 60 Quick Run

Description: Someone sent me a file with white and black rectangles. I don't know how to read it. Can you help me?

全然わかりませんでした.しかしふとこの中身のファイルはbase64なんじゃないかと思いデコードしてみたところ何かQRっぽいことが発覚しました.

f:id:jtwp470:20160222014559p:plain

ですのでスマホでスキャンしてFlagを得ました.

出た文字列: FIagis:IW{QR_C0DES_RUL3}

Flag: IW{QR_C0DES_RUL3}

Misc 70 Rock with the wired shark!

Description: Sniffing traffic is fun. I saw a wired shark. Isn't that strange?

ファイルを展開するとパケットキャプチャしたファイルが.Wiresharkで開いてパケットを見ているとBASIC認証のページからzipファイルをダウンロードしているようです.ですのでExport Objectsを使ってzipファイルを展開します. 出てきたzipファイルを展開しようとするとパスワードを求めてきます. パスワードなんだよっと思いましたがBasic認証で使っているパスワードを入力すると展開できました.

Flag: IW{HTTP_BASIC_AUTH_IS_EASY}

Misc 80 404 Flag not found

解けないでいたらチームメンバーが解いてくれました.多分彼がWriteupを書いてくれることでしょう.

Misc 90 BarParty

ファイルを展開すると切断されたバーコードが! 仕方ないのでコンビニで写真を印刷しカッターで切りパズルのように合わせました.(辛かった)

f:id:jtwp470:20160222015310j:plain

あとは頑張って読み取りました.

Flag: IW{Bar_B4r_C0d3s}

Web

Web 80 0ldsk00lBlog

WebページにアクセスするとGitで管理したみたいな事が書いてあります.そこで /.git にアクセスすると403が返ってきます. しかし /.git/HEAD とかすると中身が表示されます.なのでGitリポジトリがそのまま公開されているようですね.

ここで片っ端からGitオブジェクトを手動でダウンロードしました.

ダウンロードしたリポジトリのファイルツリーは以下の様な感じでした.

.git
├── COMMIT_EDITMSG
├── HEAD
├── ORIG_HEAD
├── config
├── index
├── logs
│   ├── HEAD
│   └── refs
│       └── heads
│           └── master
├── objects
│   ├── 14
│   │   └── d58c53d0e70c92a3a0a5d22c6a1c06c4a2d296
│   ├── 19
│   │   └── 49446afea12e0937044fdabe8cc101c87f7c54
│   ├── 25
│   │   └── a3f35784188ac1c9bf48a94e5a9c815bcb598c
│   ├── 26
│   │   └── 858023dc18a164af9b9f847cbfb23919489ab2
│   ├── 33
│   │   └── a5c0876603d7a6f9729637f36030bbabb2afa3
│   ├── 3b
│   │   └── e70be50c04bab8cd5d115da10c3a9c784d6bae
│   ├── 4b
│   │   └── 825dc642cb6eb9a060e54bf8d69288fbee4904
│   ├── 55
│   │   └── 08adb31bf48ae5fe437bdeba60f83982356934
│   ├── 75
│   │   └── 03402e4d48be951cddda34aae6e01905bb5c98
│   ├── 8c
│   │   └── 46583a968da7955c13559693b3b8c5e5d5f510
│   ├── 91
│   │   └── f09a7948e02d891d3a39c058a634a8752aba20
│   ├── 95
│   │   └── a5396e62ca5c9577f761ebe969f52d3b6a9235
│   ├── b1
│   │   └── 5ec824e8f45356b9d829586e9950eb3e6c621a
│   └── db
│       └── a52097aba3af2b30ccbc589912ae67dcf5d77b
└── refs
    └── heads
        └── master

20 directories, 22 files

/.git/objects/ 以下はハッシュ化されているのでダウンロードするのは難しいと思いますがコミットログの各ハッシュでまずダウンロードし次にgit log --statでFatal Error と出てくるハッシュを同様にダウンロードすることで何とか全部ダウンロードすることが出来ました.

git diffの結果.

diff --git a/index.html b/index.html
deleted file mode 100644
index 5508adb..0000000
--- a/index.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<html>
-<head>
-       <title>0ldsk00l</title>
-</head>
-<body>
-       <h2>2000</h2>
-       <p>
-               Oh, did I say that I like kittens? I like flags, too: IW{G1T_1S_4W3SOME}
-       </p>
-
-       <h2>1990-2015</h2>
-       <p>
-               Hmm, looks like totally forgot about this page. I should start blogging more often.
-       </p>
-
-       <h2>1990</h2>
-       <p>
-               I proudly present to you the very first browser for the World Wide Web. Feel free to use it to view my awesome blog.
-       </p>
-
-       <h2>1989</h2>
-       <p>
-               So, yeah, I decided to invent the World Wide Web and now I'm sitting here and writing this.
-       </p>
-</body>
-</html>
\ No newline at end of file

Flag: IW{G1T_1S_4W3SOME}

.git/ 配下にどのようなファイルがあるのか調べるきっかけになったので非常に勉強になる問題でした.

Web 90 TexMaker

LaTeXのコードを入力するとPDFファイルを生成するWebサービス.問題をみた瞬間にOSコマンドインジェクションかディレクトリトラバーサルもどきで頑張ってPHPファイルとかを読むみたいな問題だろうなと予測しました.

はじめにOSコマンドインジェクションを試してみました.どうやら\input{} コマンドで普通にコマンドが利用できるようでしたがBLACKLISTに登録されているみたいで実行できず諦めました.

次にLaTeX内で何とかファイルを読めないかといろいろ調べてみるとTeX Hackとかいう論文を見つけました. これ通りに/etc/passwdを読んでみるときちんと読めることが確認できました.

そこで ./../index.php を読もうとして指定してもエラーになってしまいます.先ほどの論文の注釈を参考にちょっと変えたコードを書くとindex.phpも読むことが出来ました. どうやらcatcodeというコマンドでLaTeX特殊文字を飛ばしたりすることができるみたいですね?.多分#LaTeX特殊文字でエラーになってしまうのを回避させようとしているのだと思います.

このようなノリで調べているとどうやら index.phpはconfig.phpを読み, config.phpはflag.phpを読んでいます. 最後にflag.phpを読むコードを書きます.

\openin5=./../flag.php
\def\readfile{%
\catcode`\$=1
\read5 to\curline
\ifeof5 \let\next=\relax
\else \curline˜\\
\let\next=\readfile
\fi
\next}%
\ifeof5 Couldn't read the file!%
\else \readfile \closein5
\fi

エラーメッセージのところにFlagが書かれています.

f:id:jtwp470:20160222021149p:plain

Flag: IW{L4T3x_IS_Tur1ng_c0mpl3te}

Rev

Rev 50 SPIM

Description: My friend keeps telling me, that real hackers speak assembly fluently. Are you a real hacker? Decode this string: "IVyN5U3X)ZUMYCs"

どうみてもMIPSのコードです.本当にありがとうございました. というわけで簡単にC言語に変換して実行しました.

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

int main(void) {
    char *flag = "IVyN5U3X)ZUMYCs";

    if (strlen(flag) > 15) {
        return 1;
    }
    int t1;
    for (t1 = 0; t1 < 15; t1++) {
        int a0 = flag[t1];
        a0 = a0 ^ t1;
        putchar(a0);
    }
    printf("\n");
    return EXIT_SUCCESS;
}

後は実行.

Flag: IW{M1P5_!S_FUN}

Rev 70 ServerfARM

ARMのバイナリ.適当にディスアセンブルしてコードを読むとどうやらprintfしまくっているみたい. なのでその文字を片っ端から吐き出して繋げればFlagが得られました.

Flag: IW{S.E.R.V.E.R>=F:A:R:M}

Rev 90 The Cube

ルービックキューブの問題.チームメンバーが解いてくれましたので関わってはいないのですがFlagの形式もわかっているので120通りのFlagを提出すれば当たるやろwって思っていました.(ゲス顔)

Code

Code 50 A numbers game

プログラミング問題 その1

問題例としては

x - 17 = 10

みたいなのが与えられるのでxの値は何か?を求める問題.つまり1次方程式を解くプログラムを書けばいいということになる. さてPythonには便利なことにSymPyと呼ばれる記号計算ライブラリがあります.これを使えば簡単に実装できます.

import socket
from sympy import *
from sympy.parsing.sympy_parser import parse_expr


HOST = "188.166.133.53"
PORT = 11027


def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    return s, s.makefile('rw')


def read_until(f, delim='\n'):
    data = ""
    while not data.endswith(delim):
        data += f.read(1)
    return data


s, f = sock(HOST, PORT)

var("x")  # x を変数として扱う
print(read_until(f))

while True:
    exp = read_until(f)
    print("<< : " + exp)
    # exp = "Level 1.: x - 17 = 13"
    exp = exp.split(".: ")[1].split(" = ")
    ans = solve(Eq(parse_expr(exp[0]), int(exp[1])))
    print(">> : " + str(ans))
    s.send(str(ans[0]).encode())
    print(read_until(f))

100回解くとフラグが得られました.

Flag: IW{M4TH_1S_34SY}

Code 60 It's Prime Time!

表示されている数字の次の素数を求めよというような問題.例えば3なら5を返してあげる. いちいち与えられた数の次の素数を求めるというようにすると時間がかかると思ったので最初に素数表を作成しておき辞書引きするような感じで書いた.

import socket
import re


HOST = "188.166.133.53"
PORT = 11059


def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    return s, s.makefile('rw')


def read_until(f, delim='\n'):
    data = ""
    while not data.endswith(delim):
        data += f.read(1)
    return data


def prime_table(n):
    lis = [True for _ in range(n+1)]
    i = 2
    while i * i <= n:
        if lis[i]:
            j = i + i
            while j <= n:
                lis[j] = False
                j += i
        i += 1

    table = [i for i in range(n + 1) if lis[i] and i >= 2]
    return table


prime_table = prime_table(100000)  # 100000でのテーブルを予め作成しておく

s, f = sock(HOST, PORT)
m = re.compile(r"Level (\d+).: Find the next prime number after (\d+):")

print(read_until(f))
while True:
    exp = read_until(f)
    print("<< : " + exp)
    exp = m.match(exp)
    y = int(exp.group(2))
    n = list(filter(lambda x: x > y, prime_table))[0]
    print(">> : " + str(n))
    s.send(str(n).encode())
    print(read_until(f))

実行してわかりましたが100くらいまでの素数表で足りる問題でした.

Flag: IW{Pr1m3s_4r3_!mp0rt4nt}

Code 70 A numbers game 2

今度は符号化したコードを与えられるのでそれをデコードしてみました.するとCode 50で解いたような1次方程式が出てきたのでCode 50のコードをパクる感じで書きました.なおずっと返す値もエンコードして返さないといけないということを忘れていて1時間溶かしましたorz

import socket
import string
from sympy import *
from sympy.parsing.sympy_parser import parse_expr

HOST = "188.166.133.53"
PORT = 11071


def xor(x, y):
    return x ^ y


def encode(eq):
    out = []
    for c in eq:
        q = bin(xor(ord(c), (2 << 4))).lstrip("0b")
        q = "0" * ((2 << 2)-len(q)) + q
        out.append(q)
    b = ''.join(out)
    pr = []
    for x in range(0, len(b), 2):
        c = chr(int(b[x:x+2], 2)+51)
        pr.append(c)
    s = '.'.join(pr)
    return s


def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    return s, s.makefile('rw')


def read_until(f, delim='\n'):
    data = ""
    while not data.endswith(delim):
        data += f.read(1)
    return data


maps = {encode(x): x for x in string.printable}


def check(exp):
    ret = ""
    for i in range(0, len(exp), 4):
        t = ".".join(exp[i:i+4])
        m = maps.get(t)
        if m:
            ret += m
        else:
            print(t)
            break
    return ret


def main():
    s, f = sock(HOST, PORT)

    print(read_until(f))
    while True:
        exp = read_until(f)
        print("<< : " + exp)
        exp = exp.split(".: ")[1].replace("\n", "").split(".")
        exp = check(exp)  # x - 12 = 10 みたいな形式になる
        print(" == " + exp)
        exp = exp.split(" = ")
        ans = solve(Eq(parse_expr(exp[0]), int(exp[1])))
        print(">> : " + str(ans[0]))
        s.send(encode(str(ans[0])).encode())
        print(read_until(f))


if __name__ == "__main__":
    # print(check(["3", "4", "4", "4"]))
    main()

Flag: IW{Crypt0_c0d3}

Crypto

暗号は1つしか解けなかったです.精進が必要.

Crypto 60 Oh Bob!

与えられた公開鍵をダンプして中の合成数を表示してみました.

$ openssl rsa -pubin -in bob.pub -text -noout
Modulus (228 bit):
    0d:56:4b:97:8f:9d:23:35:04:95:8e:ed:8b:74:43:
    73:28:1e:d1:41:8b:29:f1:ec:fa:80:93:d8:cf
Exponent: 65537 (0x10001)

すると非常に弱い鍵であることがわかりました.この程度であれば一般的なコンピューターのCPUでも数分で素因数分解できるはずですので適当なマシンを拝借してぶん回しました.

あとは得られた素数2つを使って秘密鍵を生成しそれを用いて暗号文を解読しました.

Flag: IW{WEAK_RSA_K3YS_4R3_SO_BAD!}

まとめ

解けたのはこんな感じでした.あと主催チームがさり気なく問題を全部公開しているので見ると勉強になっていいかなと思います.

github.com

あとチームのSlackにBOTを導入したところ結構便利になったのでこれからもう少し機能を追加してやりたいなという気持ちですw

f:id:jtwp470:20160222234245p:plain

今月号のきららMAXのごちうさも可愛くて最高だったのでみんな買いましょう!

Sharif CTF 2016 Writeup

こんにちは.たんごです. もう大学も3年が終わりますがまだ実験のレポートが進んでいませんw とりあえずやらないといけないことが多いなか, Sharif CTFが行われていたのでチームg0tiu5aのメンバーとして参加しました.250ptで248位でした(糞雑魚)

以下は解けた問題のWriteupと解けなかったけど他人のWriteup読んで実際にやってみた記録です.

解けた問題

Rail Fence Cipher (Crypto)

名前でググるRail Fence Cipherにあるように既知の暗号方式であることがわかりました. そこで適当にPythonのソルバーなどを検索して出てきたコードに暗号文を投下して適当に回しました.

key = 21でした.

Kick Tort Teen (Forensics)

Excel形式のファイルが出てきます.WindowsExcelなどで開くとExcel VBAマクロがあることがわかるので実行してみるが特に変化はなし.そこでコードを読むとどうやらfileXYZ.dataとかいうファイルを生成しているみたい.

$ file fileXYZ.data
fileXYZ.data: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, stripped

あとはこれを実行してみるとフラグが得られます.

Dumped (Forensics)

やるだけ問.strings RunMe.DMP | grep Sharif

dMd (Reverse)

何故か電車の中でRevした問題.フラグの元となる値はバイナリに直書きされています. GDBで動かしながらアセンブラコードを読んでいると以下の様な動作であることが分かりました.

  1. ユーザーから入力を受け取る
  2. ユーザーの入力値をMD5に変換する
  3. そのまま書かれているMD5値とユーザーの入力値をMD5化したものを比較して正しければThe key is valid! みたいな感じでおしまい.

中に書かれているMD5値は780438d5b6e29db0898bc4f0225935c0でした.これに対応する値はMD5(MD5(grape))ということを検索して知りましたのでその値がフラグでした.

解けなかったけど他人のWriteup読んで理解した問題

Sec-Coding 1 (Misc)

C++で書かれたコードにある脆弱性とかを潰せみたいな問題.提出サーバーにアクセス集中していたのか全然アクセスできなかったのが非常に辛かったです. ずっとvector charの大きさが0のときが未定義動作らしいのでその脆弱性を潰したかったのですがどうやっても潰れずに諦めました.

競技終了後, 別にvector char使う必要なくね?と思いStringにして提出したところ通ったのが辛いです.

We lost the Fashion Flag! (Forensics)

解凍するとtar.gzとfashion.modelというファイルが得られました.tar.gzを展開してもわけわからんファイルが10000くらいでてきて謎いです.結局どうすれば解けるのかわからず終わってしまいました.

他の人のWriteupを読みながら進めているとどうやらfashion.modelはfzipというもので圧縮されているらしいですね.

 $ hexdump -C fashion.model| head -2
00000000  08 00 00 00 46 65 6d 74  6f 5a 69 70 00 00 00 00  |....FemtoZip....|
00000010  00 00 01 00 34 7d 27 2c  20 27 63 74 66 27 3a 20  |....4}', 'ctf': |

なんじゃそりゃ~って感じですが適当にググります. https://github.com/gtoubassi/femtozipっぽいので使ってみる.

あとは10000くらいあるファイルがテキスト化されるのでgrepして出てきたフラグを提出すれば良いらしい.

 $  grep "forensic" out/* | grep 2016 | grep "'points': 100"
11180:{'category': 'forensic', 'author': 'staff_3', 'challenge': 'Fashion', 'flag': 'SharifCTF{2b9cb0a67a536ff9f455de0bd729cf57}', 'ctf': 'Shairf CTF', 'points': 100, 'year': 2016}
11223:{'category': 'forensic', 'author': 'staff_5', 'challenge': 'Fashion', 'flag': 'SharifCTF{41160e78ad2413765021729165991b54}', 'ctf': 'Shairf CTF', 'points': 100, 'year': 2016}
11908:{'category': 'forensic', 'author': 'staff_2', 'challenge': 'Fashion', 'flag': 'SharifCTF{8725330d5ffde9a7f452662365a042be}', 'ctf': 'Shairf CTF', 'points': 100, 'year': 2016}
273:{'category': 'forensic', 'author': 'staff_3', 'challenge': 'Fashion', 'flag': 'SharifCTF{1bc898076c940784eb329d9cd1082a6d}', 'ctf': 'Shairf CTF', 'points': 100, 'year': 2016}
9401:{'category': 'forensic', 'author': 'staff_6', 'challenge': 'Fashion', 'flag': 'SharifCTF{c19285fd5d56c13b169857d863a1b437}', 'ctf': 'Shairf CTF', 'points': 100, 'year': 2016}

Flag: 2b9cb0a67a536ff9f455de0bd729cf57

ググる力が大切だなと思う問題でした.

uagent (Forensics)

pcapファイルをWireSharkで開くとZIPファイルを大量に分割してダウンロードしていることがわかりました. 最初はIPアドレスが実際に存在するようなのでアクセスしてダウンロードを試みましたが結局できず,次にpcapに含まれるZIPの分割ファイル郡をWiresharkの機能を用いて取り出し, 適当にシェルスクリプトを書いてファイルをくっつける作業をしましたが出来たファイルが壊れていて解凍できなかったりそのファイルを直して解凍できるようにしても今度はそのZIPファイルのパスワードがわからず終了してしまいました.

競技終了後Writeupを読んでみると SharifCTF 2016 Uagent (Forensics 100) Writeup · xil.se のような解き方があったようです.Scapyは便利なようですね.私も実際にWriteupを見ながらコードを書きました.

雰囲気から察するにHTTP 206でダウンロードされるのがZIPファイル, User-Agentにあるsctf-app/[base64]/の部分がそのZIPファイルの鍵となっていたようです. 次からネットワーク問を解くときにはWiresharkだけでなくScapyも使ってみることが重要だなと学ぶことが出来た問題でした.

Network Forensics (Forensics)

パケットをざっと見てWifiのパスワードを何とかしてゲットしてデコードしてうまいことやるのかな?と思っていましたがパスワードをどうやって取るのかわかりませんでした.

rom-0というファイルは得ていましたがそれをどう使うのかも謎でWriteupを読んで実際にWPAのパスワードを得るとこれこそハッキングだーという感じがしました.

良いWriteup: opcode.ninja

以下はTODO. 書くよ

PhotoBlog (Web)

hackme (Web)

Asian Cheetah (Misc)

Android App (Reverse)

Serial (Reverse)

SRM (Reverse)

感想

個人的には2016年が始まって参加したOnline CTFのうち前2つのCTFがフラグ形式が不明でRecon問題すぎたのが辛かったので今回のCTFは非常に解きやすかったという印象が強かったです.しかしスコアサーバーが雑魚いのかよくアクセスできなくなったり文字入力にいちいちCAPTCHAを入れてくるのはうざかったかなあ. チーム目標として100 team以上解けている問題は解けるようにしたいですし, Pwn問題に1問も手を出せなかったのでPwn修行したいですし, きちんと時間をかけて取り組みたいなぁという気持ちです. まだまだ雑魚雑魚ですがこれからも暇を見ながらいろんなCTFに参加したいです.

最後に

このCTFのやっている間僕は大学の友人と大洗に行ってアンコウ鍋をおいしくいただき更にはガルパン聖地巡礼的なことをしていました.ガルパンはいいぞ!

f:id:jtwp470:20160207205741j:plain

アンコウ鍋 美味しかった.

f:id:jtwp470:20160207205749j:plain

〆の雑炊.天国にも登るようなうまさ

f:id:jtwp470:20160207205752j:plain

大洗磯前神社.痛絵馬がめっちゃいっぱいありました.海が綺麗

f:id:jtwp470:20160207205758j:plain

大洗マリンタワー.

Trend Micro CTF 2015 Quals Writeup

今月末にあったトレンドマイクロCTFに参戦してました.

本当はISUCONの予選に参加したかったのですが友人にISUCONに出るマンがいないため,何もできないなと思って今回は参加をやめました. あと,CODEFESTIVALの予選もあったのですがその時間帯にすっかり忘れていて参加できなかったですw

私はご注文はうさぎですか?の大ファンなのでチーム名がg0tiu5aとごちうさをもじったチーム名なんですがとりあえず500ポイントを入れて181位でした. なんとも微妙な順位ですねw

実はインターンが終わってからモチベーションをあげようということでCTFtimeに載っているオンラインCTFに片っ端から参加していて,CSAW CTFとMMA CTFにも参加していたのですが特筆することもないので書きませんでした. その2回のCTFは1人で寂しくCTFをしていたのですが今回は友人3人と共にSlackでわいわいやっていたので結構楽しかったです.

とりあえず解けた問題のWriteupを載せておきます.

Analysis-defensive 100

実はこいつなぜ解けたのかわかっていない謎問題.VM上のUbuntuにダウンロードして実行したら一発目になぜかFLAGがでてきた.意味不明. 最初はダミーかなと思っていたのですがとりあえずスコアサーバーに提出したらFLAGが通った.

f:id:jtwp470:20150929182614p:plain

Cryptography 100

256ビットのRSA問題.この問題にすごい時間をかけてしまった. 問題の概要としては256ビットのRSA公開鍵を渡すんだけど中身1ビット壊れてるから直して素因数分解してみ?というような問題であった. とりあえずOpenSSLのコマンドで公開鍵の中身を見てみると以下のような表示になる.

 $ openssl rsa -pubin -in PublicKey.pem -text -noout
Modulus (256 bit):
    00:b6:2d:ce:9f:25:81:63:57:23:db:6b:18:8f:12:
    f0:46:9c:be:e0:cb:c5:da:cb:36:c3:6e:0c:96:b6:
    ea:7b:fc
Exponent: 65537 (0x10001)

とりあえず出てきた合成数をPari/GPに投げて素因数分解してみるとこんな感じになる.

? factor(82401872610398250859431855480217685317486932934710222647212042489320711027708)
%1 =
[                                                    2 2]

[                                                    3 2]

[                                                   11 1]

[                                                   19 1]

[                                                  307 1]

[                                            180728237 1]

[                                           2478211847 1]

[79649944603613274174761346722176223218141026933909079 1]

ふむふむ.こりゃないなと.

とりあえず2進数でビットを1つずつ反転して素因数分解させてみればいいかなということに気づいた.どうやってそれをやろうかと思っていたのだが目的のビットの場所だけ1とXORすればいいじゃんということに気づいたのでそれを使って1つずつやってみる.ついでに偶数は結果に出力させないようにした.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# from sympy.ntheory import primefactors
import random
source = "".join("b6:2d:ce:9f:25:81:63:57:23:db:6b:18:8f:12:f0:46:9c:be:e0:cb:c5:da:cb:36:c3:6e:0c:96:b6:ea:7b:fc".split(":"))

def solver():
    vals = int(source, 16)
    # vals = 0b0001
    k = vals
    print("F: " + hex(k))
    start = 1
    for i in range(0, 257):
        k = vals ^ start
        start = start << 1
        if k % 2 != 0:
            print(str(i) + ": " + str(k))


if __name__ == "__main__":
    solver()
    print("END")

こうすると1つだけ奇数が含まれていることがわかった.

こいつ:82401872610398250859431855480217685317486932934710222647212042489320711027709

さて他の方のWriteupを読んでいるとPari/GPで素因数分解できたらしいのだが我がMacBook Airでは全く終わる気配がなかったので適当に素因数分解できそうなライブラリを探しているとmsieveという強そうなソフトウェアを見つけた.ただこれを用いてもMBA上では全然素因数分解できないので強い計算機が必要だなということで大学で使える強そうな計算機サーバーくんを少し拝借して実行した.(今更だけどさくらのクラウドのクーポン券あるしクラウドにでかいインスタンス立てて実行すればよかったかも)

 $  ./msieve -v -e -n -q 82401872610398250859431855480217685317486932934710222647212042489320711027709

82401872610398250859431855480217685317486932934710222647212042489320711027709
prp39: 279125332373073513017147096164124452877
prp39: 295214597363242917440342570226980714417

これで得られた2つの素数を元に秘密鍵を作成する.ここでrsatoolの存在を思い出したのでそれを用いて秘密鍵を作成する.

$ python rsatool.py -f PEM -o key.pem -p 279125332373073513017147096164124452877 -q 295214597363242917440342570226980714417

こんなかんじで得られた秘密鍵を使って復号する.最初復号しようとしても

 $ openssl rsautl -decrypt -in encrypted.txt -out decrypt -inkey key.pem
RSA operation error
7047:error:0406506C:rsa routines:RSA_EAY_PRIVATE_DECRYPT:data greater than mod len:/SourceCache/OpenSSL098/OpenSSL098-52.40.1/src/crypto/rsa/rsa_eay.c:521:

こんな感じのエラーに悩まされてキレていたのですがもしかしてbase64でデコードしたやつを復号するんじゃね?とふと思いやってみたらそのとおりでしたw

$ cat encrypted.txt | base64 -D | openssl rsautl -inkey key.pem -decrypt
TMCTF{$@!zbo4+qt9=5}

わーい.

実はFactor DBなるサイトが合ってそこに投げるとすぐに得られたらしい. Factor DB

Programming 200

実は後述するクロスワード問題を解いていたのですが途中で飽きたのと英語読解するのが辛くなって深夜のテンションで解いた問題です.

サーバーに接続すると式がでてきて計算した結果を返せばいいようです.最初は普通に手でやっていたのですが辛くなったので自動化しました.最終的に以下のような形式が出てきました.

  • 数字のみの計算 (1 + 1, 100 * 200 - (300 / 2) みたいなかんじ)
  • 数字のカンマ表記 (100,000 + 2)
  • ローマ数字 (IV + 3)
  • 英語表記 (sixty handred one + 30)

ローマ数字の部分はPythonで書かれたライブラリのコードを適当に拝借し,英語表記の読解もStack OverflowにあったIs there a way to Convert Number words to Integers? Pythonを拝借して深夜の謎テンションで実装をキメました.(個人的には漢数字とかも出てくるのかなと思ってビクビクしてましたが(アジア圏なので)そんなことはなかったです)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import socket
import re

URL = "ctfquest.trendmicro.co.jp"
PORT = 51740


def text2int(textnum, numwords={}):
        if not numwords:
            units = [
                "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
                "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
                "sixteen", "seventeen", "eighteen", "nineteen",
            ]

            tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

            scales = ["hundred", "thousand", "million", "billion", "trillion"]

            numwords["and"] = (1, 0)
            for idx, word in enumerate(units):    numwords[word] = (1, idx)
            for idx, word in enumerate(tens):     numwords[word] = (1, idx * 10)
            for idx, word in enumerate(scales):   numwords[word] = (10 ** (idx * 3 or 2), 0)

        current = result = 0
        for word in textnum.split():
            if word not in numwords:
                raise Exception("Illegal word: " + word)
            scale, increment = numwords[word]
            current = current * scale + increment
            if scale > 100:
                result += current
                current = 0

        return result + current


# Define digit mapping
romanNumeralMap = (('M',  1000),
                   ('CM', 900),
                   ('D',  500),
                   ('CD', 400),
                   ('C',  100),
                   ('XC', 90),
                   ('L',  50),
                   ('XL', 40),
                   ('X',  10),
                   ('IX', 9),
                   ('V',  5),
                   ('IV', 4),
                   ('I',  1))


# Define pattern to detect valid Roman numerals
romanNumeralPattern = re.compile("""
    ^                   # beginning of string
    M{0,4}              # thousands - 0 to 4 M's
    (CM|CD|D?C{0,3})    # hundreds - 900 (CM), 400 (CD), 0-300 (0 to 3 C's),
                        #            or 500-800 (D, followed by 0 to 3 C's)
    (XC|XL|L?X{0,3})    # tens - 90 (XC), 40 (XL), 0-30 (0 to 3 X's),
                        #        or 50-80 (L, followed by 0 to 3 X's)
    (IX|IV|V?I{0,3})    # ones - 9 (IX), 4 (IV), 0-3 (0 to 3 I's),
                        #        or 5-8 (V, followed by 0 to 3 I's)
    $                   # end of string
    """ ,re.VERBOSE)


def roman2int(s):
    """convert Roman numeral to integer"""
    if not s:
        # raise InvalidRomanNumeralError('Input can not be blank')
        raise ValueError()
    if not romanNumeralPattern.search(s):
        return text2int(s)
        #raise InvalidRomanNumeralError('Invalid Roman numeral: %s' % s)

    result = 0
    index = 0
    for numeral, integer in romanNumeralMap:
        while s[index:index+len(numeral)] == numeral:
            result += integer
            index += len(numeral)
    return result



def parser(d):
    data = d.replace(",", "").replace(" = ", "")

    op = []
    for k in data:
        if k in ["+", "-", "*", "/", "(", ")"]:
            op.append(k)

    data = re.sub("[\+\-*/()]", " <> ", data).split(" <> ")
    print(data)
    for i in range(len(data)):
        data[i] = data[i].strip()
        if data[i] not in ["+", "-", "*", "/", "(", ")", "", "<>"] \
           and data[i].isdecimal() is False:
            data[i] = str(roman2int(data[i]))


    data = " ".join(data)
    cnt = 0
    res = ""
    for k in data:
        if k == " ":
            res += op[cnt]
            cnt += 1
        else:
            res += k
    return res


def main():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((URL, PORT))

    while True:
        data = s.recv(1024).decode()
        if data ==  "Congratulations!\n":
            print(s.recv(1024).decode())
            print(s.recv(1024).decode())
            break
        print(data)

        res = str(eval(parser(data)))
        print(res)
        s.send(res.encode())


if __name__ == "__main__":
    main()
    #print(parser("1 + 2 = "))

f:id:jtwp470:20150929190053p:plain

TMCTF{U D1D 17!}

Miscellaneous 100

友人と調べながら解いた問題.実は1問だけわからなかったのですがブルートフォースで沈めた.

頭文字を並べるとSVIEDTRVSCSECDHBBDITMASTCとなったのでこれをMD5エンコードして提出.

TMCTF{88f5505a45c9e176e36898095f505187}

解けそうで解けなかった問題

Web 100

ログインするとIDがだらだら出てくる問題.途中でID1がログインしているのが特徴的だったのでもしかすると同時に接続すればいいのかなと思ったのですがうまくいかず断念.方向性はあっていた模様.

Fix me PDF

壊れたPDFが与えられる.いろいろ直してみたがよくわからない.どうやらPDFファイルをバイナリで覗くとBase64エンコードされた画像がありそれがFLAGらしい. OSXでは普通に開けてしまっていたため何が壊れているのかさっぱりだった.

Programming 100

色を識別しろみたいな問題.PythonでやっていたのだがImage系のライブラリを触ったことがなかったため途中で挫折した.

Miscellaneous 300

200回連続でCAPTCHAを通過しろみたいな問題.時間制限は特に無いので最初は手でやっていたが間違えるとすぐ0になるのと数の多さに圧倒されてやっぱり自動化しなきゃなとおもった.

Pythonでやろうとしたのだが2値化してライブラリに投げてもうまく読み取ってくれず諦めた.@chibieggさんのブログに詳細コードが載っていたのであとで見てやり方を学びたい.

最後に

3回のCTFに参加してわかったこととしてはWeb力, Pwn力のなさがあるということ.Webはある意味Reconみたいなものと思っているがいろいろな脆弱性や攻撃手法を学んでいきたいなと思う.あとPwnというかReversingの力もほしいのでとりあえずx86アセンブラを読めるようになりたいなと言った感じ.

とりあえずこれからも暇があればCTFの問題にたくさんアタックしていきたい.

TDUCTF 2014 Satellite in ConoHa 参加記 & Writeup

去る3月29日にTDUCTF 2014 Satellite in ConoHaに行ってきました.

大学に入学当初はCTFに全く興味を持っていませんでしたがSECCON CTF 2014 online 予選をやってみて結構面白かったのと大学の授業で秋からアセンブラをやったのもありせっかくだし初心者向けということなので突撃です.

以下に解けた問題を列挙していきますが問題名を忘れているwww

Trip Picture

開くとラブライバーらしい画像が出てきます.見ても特にFLAGらしきものはない.なので何も考えずにhexdump -Cすると以下のような感じでFLAGが得られる. f:id:jtwp470:20150331182012p:plain

もちろんstringsコマンドでもよい.

読めない文字1

最初まじで読めなかった.見た感じbase64だけど1回デコードしても駄目だしなんだよ~と思ってIPython上でバチバチキーボード叩いてたらFLAGが! 単純に2回base64エンコードされているだけだった.

ASCII ART

flag.zipが渡される.fileコマンドに通すときちんとzipファイルと出てくる割に展開できない.これは死んだかと思ったがTrip Pictureのときのようにとりあえずバイナリ読むかと思ったらアスキーアートでFLAGが書かれていた.頭を横にしてFLAGを読んでいたのは秘密な. f:id:jtwp470:20150331182630p:plain

Unknown File (でしたっけ?)

タイトル忘れました.糞長い名前のファイル3B14EA88FCDB58239928D829DD5182E23D866255がありました.とりあえずfileコマンドで見てみるとASCII Textとか出てきたので普段はEmacs使いですがこういうときはなぜかVimを使って開いたらFLAGが得られます.ちなみにEmacsではちょっと頑張らないと見られない.兎にも角にもファイルを開けば何とかなる問題でした.

Broken data

UEsDBBQAAAAIAG6feUZPi12UJwAAACUAAAAIAAAAZmxhZy50eHQLcQmtNjczcDY2dbQwcDE3tXAzMDZyMTBydjNxcTI0cjF2NTWtBQBQSwECHwAUAAEACABun3lGT4tdlCcAAAAlAAAACAAkAAAAAAAAACAAAAAAAAAAZmxhZy50eHQKACAAAAAAAAEAGADHOOjD6mbQAasYVU7pZtABqxhVTulm0AFQSwUGAAAAAAEAAQBaAAAATQAAAAAA

こんな文字列が与えられる.そして1ビット欠けているらしい.見た感じbase64かなともおもったけどとりあえず最初の5文字位をググる. するとbase64化されたZIPファイルらしいことがわかる.更に調べてみると末尾に=が付いているのでこれが欠けた1ビットかなと思いとりあえず付けてbase64デコードしてファイルに書き出す.とりあえずPythonで書いた.

#!/usr/bin/env python3
import codecs

data = "UEsDBBQAAAAIAG6feUZPi12UJwAAACUAAAAIAAAAZmxhZy50eHQLcQmtNjczcDY2dbQwcDE3tXAzMDZyMTBydjNxcTI0cjF2NTWtBQBQSwECHwAUAAEACABun3lGT4tdlCcAAAAlAAAACAAkAAAAAAAAACAAAAAAAAAAZmxhZy50eHQKACAAAAAAAAEAGADHOOjD6mbQAasYVU7pZtABqxhVTulm0AFQSwUGAAAAAAEAAQBaAAAATQAAAAAA"

data += "="

b = codecs.decode(data.encode(), "base64")

with open("broken_data.zip", "wb") as f:
    f.write(b)

生み出されたzipデータは普通にunzipすると鍵がかかっていて辛い感じになっている.

しかしOSX標準の展開ソフトだとすんなり展開できてしまい,flag.txtを読めば良い.

競技終了後,まさか私が指されてしまい解説することになりましたがこんな感じで解きました.あと俺はOSX持ってないから鍵かかったzip展開できないやーんって言う人にはzip -FF broken.zip --out fix.zipのようにzipコマンドのFFオプションでうまいこと直してもらえば展開できるようにまりますよ.

あと作問者曰くバイナリ内にZIPにパスワードがかかっているかどうかのフラグがあるらしく,それが破損していたらしいのだがよくわからなかったので精進する必要がまだまだありそうです.

Question

名前こんなのでしたっけ.とりあえずはZIPファイルでどっかのおっさん(Steve_ballmer_2007_outdoors2.jpg)とflag.txtが入っている問題があった.この瞬間にこりゃpkcrack使う問題だろjkとなり入っているおっさんをぐぐり写真をWikipediaから持ってきてpkcrackにかければ終わり.朝飯前だった.

柚子胡椒 vol.1

pcapファイルなのでとりあえずWiresharkで開く.するとどうもBluetooth通信を見ているらしい.初心者向けの割にいきなりBluetoothはいかがなものかともおもったけどPINを見つけろとのことなので追っていけばPINのやりとりが見つかりFLAGも見つかる.

string_compare

IDAにつっこめば分かる.読んでいると入力文字と何かをstrcmpで比較し合っていればFLAGを間違っているとInvalid keyと吐くもののようだ.しかし適当に読んでいるとMinamiKotoriの文字が.これとTDU{%s_is_very_cutie}をつなげたものを吐くのかなと思いFLAGとしてみたら合ってた.

我が同胞に告ぐ

最初はまったくわからなかった.フォレンジックと言われてもと言う感じだったが途中で運営側から「宗派が別れますねー」みたいな発言が... その瞬間こりゃVimか!と思い,Vimを立ち上げやってみればやっぱりそうだった.過去にもこのような問題が出たらしいが私は知らなかったので非常に面白い問題でした.

ちなみに私はEmacerなので終わった瞬間にVimをそっと落としました.

Mini Quiz #1, #2

ノリと感

解けなかった問題

type.log.gzの問題

strings使えばなんとかなったんですね

SQLi

SQLインジェクション能力を磨きたい.そもそもSQLインジェクション知ったのがpreだったのが全ての元凶

Evidence

Macフォレンジックツールであるautopsy入れていたのにインストールするのめんどいしとか言って忌避してたら時間が終わったw

autopsy使うとすぐ解けるね.

ゆゆ式

つらい

総評

なんだかんだといって順位は1330点で8位でしたが怖い人達はみんなマイナスのFLAGを入れていたみたいなので10位くらい下がるのかな.

少なくともSQLとかPHPとかWEB問題が1問も解けず泣きを見ることになったのでそこら辺の知識をもっと入れていきたいなと思いましたまる

もともとWindowsを用意してねって書いてあってLinux, Macユーザーである私からすると非常に辛いものがあり競技開始30分くらいまではWindowsを使っていたんですが何も整備していないWindowsが糞すぎて何もできず死にました.そこでMacを起動したら本当に捗りました.やっぱりなれた環境でやるのが一番です(MBAメモリがないのでWindowsは仮想環境ではなくBootcampで起動していた)

あと途中でごちうさ流れたときはめっちゃテンションあがりました.本当に有難うございます.

元気の源

f:id:jtwp470:20150331225400j:plain