ひよこになりたい

Programming Server Network Security and so on

pwniumCTF ROT (Prog 300) Writeup

pwniumCTF に少しだけ手をつけたので1問かいておきます。

順を追って書いていくので、全体だけ欲しい方は最後のプログラムをどうぞ

ROT (Programming 300)

Rot 90, Rot -90, Rot 90, Rot -90... nc 41.231.53.40 9090

Hint : No hints for this task yet 与えられたサーバーに接続すると、base64が降ってきます。

iVBORw0KGgoAAAANSUhEUgA(中略)nuxIpAAAAAElFTkSuQmCC
Answer:

(こちらに置いておくのでどうぞ→pwnium300base64.txt

Answer:で、答えを聞かれるのでどうにかデコードして答えてねーと言う問題。

とりあえず与えられたデータをデコードしてみる。

# Python3
>>> fp = open('pwnium300base64.txt', 'r', encoding='utf-8')
>>> msg = fp.read()
>>> import base64
>>> print(base64.b64decode(msg))
b'x89PNGrnx1anx00x00x00rIHDRx00x00x00xc8x00x00x00(以下略)

PNGっぽいので、保存してみる。

# Python3
>>> fp = open('x.png', 'wb')
>>> decmsg = base64.b64decode(raw.encode('utf-8'))
>>> fp.write(decmsg)
>>> fp.close()

保存したx.pngを開いてみる。

f:id:zipsan:20150214145008p:plain

目が痛くなる。。。

なんか文字が書かれています。(解いた後の画像をあげているので、上でデコードしたものとは違うかも)

パスワードらしきものが加工されています。画像をいじれば文字が読めそうなので、きれいな形にぐりぐりいじってから、画像文字認識(OCR)にかけてみる方針でやってみます。以下のスクリプトはPython2.7で書いています。

(1) 半分にスライス

今回は画像加工に、Pythonの画像加工ライブラリ、Pillowを使用しました。

# pip install pillow

でモジュールを追加することができます。

2つにわかれた文字を合成する必要があるので、2つにスライスをします。

image.clop()に矩形を表すタプルを渡してあげればスライスすることができます。保存はimage.save(filename, format)です。

>>> from PIL import Image
 # ファイルを開く
>>> image = Image.open('x.png', 'r')
# 半分にスライス
>>> boxl = (0, 0, int(image.size[0] / 2), image.size[1])
>>> boxr = (int(image.size[0] / 2), 0, image.size[0], image.size[1])
>>> leftimage = image.crop(boxl)
>>> leftimage.save('left.png', 'PNG')
>>> rightimage = image.crop(boxr)
>>> rightimage.save('right.png', 'PNG')

出力されたleft.pngとright.pngはこちら

f:id:zipsan:20150214145016p:plain f:id:zipsan:20150214145018p:plain

(2) 反転

文字と文字をちょうどよく合わせるために、左半分を対角反転します。

image.transpose()にimage.FLIP_TOP_BOTTOMで上下反転とimage.FLIP_LEFT_RIGHTで左右反転を同時に行っています。

# 左半分を対角反転
>>> leftRevimage = leftimage.transpose(Image.FLIP_TOP_BOTTOM).transpose(Image.FLIP_LEFT_RIGHT)
>>> leftRevimage.save('leftRev.png', 'PNG')

出力されたものがこちら

f:id:zipsan:20150214145432p:plain

右半分と合わせればちょうどいい感じになりそうです。

(3) 黒抽出

目が痛くなるようなカラフルフレームを外したいので、黒色だけ抽出します。

黒色を抽出するために、元画像からimage.getpixel()でピクセルの色を取得し、その色が(100, 100, 100)以下ならimage.putpixel()でキャンバスに書き込んでいます。

閾値を設定しているのは、ジャギーを出来るだけ減らしたかったためです。image.getpixel() == (0, 0, 0)とすると、文字が欠けてしまったのでこのような処理にしています。

# 黒抽出
>>> def checkblack(I, V):
... if I[0] < V[0] and I[1] < V[1] and I[2] < V[2]:
...    return True
...  return False
>>> leftBlackimage = Image.new("RGB", leftRevimage.size, (255, 255, 255))
>>> for x in range(leftRevimage.size[0]):
...   for y in range(leftRevimage.size[1]):
...    if checkblack(leftRevimage.getpixel((x, y)), (100, 100, 100)):
...      leftBlackimage.putpixel((x, y), (0, 0, 0))
>>> leftBlackimage.save('leftblack.png', 'PNG')

>>> rightBlackimage = Image.new("RGB", rightimage.size, (255, 255, 255))
>>> for x in range(rightimage.size[0]):
...   for y in range(rightimage.size[1]):
...     if checkblack(rightimage.getpixel((x, y)), (100, 100, 100)):
...       rightBlackimage.putpixel((x, y), (0, 0, 0))
>>> rightBlackimage.save('rightblack.png', 'PNG')

出力されたものがこちら。

f:id:zipsan:20150214145438p:plain f:id:zipsan:20150214145441p:plain

(4) 合成・回転

左右の画像が出来上がったので合成します。見た感じ単純合成で良さそうなので、左右両方のデータを読んでいき、黒ならキャンバスに書き込んでいます。

>>> blendimage = Image.new("RGB", rightBlackimage.size, (255, 255, 255))
>>> for x in range(rightBlackimage.size[0]):
...   for y in range(rightBlackimage.size[1]):
...     if rightBlackimage.getpixel((x, y)) != (255, 255, 255):
...       blendimage.putpixel((x, y), rightBlackimage.getpixel((x, y)))
...    if leftBlackimage.getpixel((x, y)) != (255, 255, 255):
...      blendimage.putpixel((x, y), leftBlackimage.getpixel((x, y)))
>>> blendimage.save('blend.png', 'PNG')

出力結果はこちら

f:id:zipsan:20150214145724p:plain

これなら読めそう。横のままだと読みづらいので回転します

>>> lastimage = blendimage.transpose(Image.ROTATE_270)
>>> lastimage.save('last.png', 'PNG')

f:id:zipsan:20150214145728p:plain

これならOCRにかけれそうです。

(5) OCRにかける

他アプリケーションを使って手動でOCRにかけるのはさすがにきついので、PythonOCRモジュールを使用します。

今回はpytesseractというOCRモジュールを使用しました。(python3では使えません)

余談:上のスクリプトはPython2.7になっていますが、最初に解いた時はPython3.4で書いていました。pytesseractはPython2.7用のモジュールでそのままでは使えないので、Python3→Python2へ変換する必要があります。今回は解く途中で2to3コマンドで一括で変換してpytesseractを使用しています。べんり。

(誰かPython3用のOCRモジュール知ってる人いませんか?(切実))

インストールはpipから行えます。

# pip2.7 install pytesseract

上手く行けばそのまま使用可能です。

ということでOCRモジュールを使用してみます。

# OCR
>>> import pytesseract
>>> message =  pytesseract.image_to_string(lastimage)
>>> print message

すごい!これだけでOCRにかけることができちゃうんですね。(Python3でもつかえたらいいなぁ)

これでN24P2QV64Qという文字が取得出来ました。この手順を自動化すればよさそうです。

(6) 接続からAnswer提出まで

接続はTelnetlibを使用しました。接続関係のPythonスクリプトはsocketとか使用する人が多いみたいですが、Telnetlibで簡単に記述することができます。(そのうちまとめたい)

import telnetlib

tn = telnetlib.Telnet("41.231.53.40", "9090")
raw = tn.read_until("\n")

~解析コード~

tn.read_until("Answer:")
tn.write(message + '\n')
print tn.read_all()

このスクリプトを走らせるとフラグが降ってきます。

Flag: Pwnium{b1a371c90da6a1d2deba2f6ebcfe3fc0}

以上です。

こんなに簡単に300ptもらえていいのだろうか・・・・

ソースコード投げておきます。(Link(text):pwnium-ROT-solve

3Dマルバツゲームの残骸

DEFCON 22 CTF Qualifications で出題された3dtttを解いてたのですが、惜しくもなく解けなかったので、途中まで組んで出来上がった残骸を放り投げておきます。

3dtttは3D Tic Tac Toe(3Dマルバツゲーム)を指定された条件で解いていく問題でした。WriteUpは __math氏のWriteUpが参考になります。

3dttt(@__math氏) - https://gist.github.com/math314/34ff1da0b4e169b1ed33

プログラム(Python3)

3Dの盤面をランダムに生成して指定した個数だけ○と×が揃っている盤面を抽出する。何かに使えるかもしれない(?)

# -*- coding: utf-8 -*-
import random

ox = ('o', 'x')

# generate 3d tic tac
def makeline():
  return [random.choice(ox), random.choice(ox), random.choice(ox)]
def makeaspect():
  return [makeline(), makeline(), makeline()]
def maketic():
  return [makeaspect(), makeaspect(), makeaspect()]

# print 3d tic tac
def printtic(tic):
  for aspect in tic:
    for line in aspect:
      print(line)
    print('')

# print aspect
#def printasp(tic):
# for aspect in tic:
#   print(aspect)

# 横チェック
def check_c(aspect):
  win = [0, 0]
  for i, c in enumerate(ox):
    win[i] += sum([1 if line.count(c) == 3 else 0 for line in aspect])
    
  return tuple(win)

# 縦チェック
def check_r(aspect):
  win = [0, 0]
  for i, c in enumerate(ox):
    win[i] += sum([1 if line.count(c) == 3 else 0 for line in list(map(list, zip(*aspect)))])
  return tuple(win)

# 斜めチェック
def check_d(aspect):
  win = [0, 0]
  for i, c in enumerate(ox):
    win[i] += 1 if [aspect[j][j]     for j in range(3)].count(c) == 3 else 0
    win[i] += 1 if [aspect[j][2 - j] for j in range(3)].count(c) == 3 else 0
  return tuple(win)

# 3Dの盤面の斜めチェック
def check_tic_d(tic):
  win = [0, 0]
  for i, c in enumerate(ox):
    if tic[0][0][0] == tic[1][1][1] == tic[2][2][2] == c:
      win[i] += 1
    if tic[2][0][0] == tic[1][1][1] == tic[0][2][2] == c:
      win[i] += 1
    if tic[0][2][0] == tic[1][1][1] == tic[2][0][2] == c:
      win[i] += 1
    if tic[2][2][0] == tic[1][1][1] == tic[0][0][2] == c:
      win[i] += 1
  return tuple(win)

if __name__ == '__main__':
  o = 4
  x = 2

  count = 0
  while True:
    if count % 10000 == 0:
      print(count)
    count += 1

    # 3D空間にランダムプロット
    tic = maketic()

    # ticのスライス1(面を変える)
    tictrans1 = []
    asptrans = []
    for i in range(3):
      for aspect in tic:
        asptrans.append(aspect[i])
      tictrans1.append(asptrans)
      asptrans = []

    # ticのスライス2(面を変える)
    tictrans2 = []
    asptrans = []
    for i in range(3):
      for aspect in tic:
        aspect = list(map(list, zip(*aspect)))
        asptrans.append(aspect[i])
      tictrans2.append(asptrans)
      asptrans = []

    # 縦横チェック (通常状態 8x3=24通り)
    chk = []
    for aspect in tic:
      chk.append(check_r(aspect))
      chk.append(check_c(aspect))
      chk.append(check_d(aspect))

    # 別の面からの判定1(縦方向への向き3x3=9通り + 斜め2x3=6通り)
    for aspect in tictrans1:
      chk.append(check_r(aspect))
      chk.append(check_d(aspect))

    # 別の面からの判定2(斜め2x3=6通り)
    for aspect in tictrans2:
      chk.append(check_d(aspect))

    # 斜め(3Dの対角線 4通り)
    chk.append(check_tic_d(tic))

    # 個数
    sumO = sum([X for X, Y in chk])
    sumX = sum([Y for X, Y in chk])

    if sumO == o and sumX == x:
      print("Found!: ({0}, {1})".format(o, x))
      printtic(tic)

出力

$ python 3dtictac.py
0
Found!: (4, 2)
['x', 'o', 'x']
['o', 'o', 'x']
['o', 'o', 'o']

['o', 'o', 'x']
['x', 'o', 'o']
['o', 'x', 'x']

['x', 'x', 'x']
['o', 'o', 'x']
['o', 'x', 'o']
.....(略)

多分あってると思う(たぶん)

競技プログラミング勢は簡単に解いていたみたい。つよい。

crypto200-1 Write up

まともに解いたのこれくらいだった。。

cryptの基礎が詰まってて結構面白かったのでWrite upしておきます。

crypto 200-1

H4x0R got this weird code while coming back from school. can you get a 32char code flag that can make him happy ?

[bash]

//一行

1f8b08089c452c530003737465703900edd85b6ec3300c44d1ffae86dc

ffe61ac7e1437403e42b1a171745d02a946c890713383537f3c7cb3c7e

8e37ec7c99d7fb39cbe3afaa5bac89f1735e5c3597bf263e571ef52ab5

49d6d759bb5eed2de6d7c56bf76d3f6de47d76ac8e9567c9bdefb0d7db

e16c3d67ded997bbb71eae3d8d7db8d796d6fed7fda317f808fb5ceaf8

48f9b48ee1a3e833ebf868f9901f6d9f59c747cb87fca8faacfbc1079f

bbf8ccfa469f1ce323999f4b1d9f6b7d8f4f1e161ff2732b1ff2a3eda3

911f7cb47d78bed6f6213fef7c667da3cfa58e0ff9c1071f7cf0f986cf

ace3830f3e1f7fffb9d4f1c147dd67d6f7f8f43ee0d37d34f29387c547

d6a7c6f828faf48ee1e335191f611ff283cf673e35c647d1a7770c1f2d

9fb33ff8a8fa909f3bf8cc3a3e5a3ee447d9a7c6f8a8f9f8da157c14ff

7f80cf1f3e1af9895ee0a3e973cec147d5270f8b8fa40fcf07da3ed10b

7ca6cfacefcc8f8d3a3e2af9e1f3ed9d8f467ef2b0f8e8e6071f599fe8

053e9a3ee4e7063e39c647d287fce0830f3effd327f6808fa6cfd2317c

b47c7a1ff0d1f3213fda3ee447db87fc68fbb4fbe123e8e36b57f0499f

59df989fd6317ce4f2137bc067f1d1c8cfd99f65ddebe0a976fcfef905

9996b432616b0000

[/bash]

16進数っぽいので、とりあえずASCIIコードに変換・・・

してみるも、全く読めず。元データの長さが944だったので、16で割り切れるな?と思い、16文字のハッシュ探しても見つからなかった。8文字はあったけど、うーん。

ひと通り試してだめだったので、ASCIIコードに変換した時に見つけたstep9という文字から、もしかしたら何かのファイルなのかなということで、バイナリエディタで直接貼り付けて、ファイル化してfileコマンドを実行してみた。

[bash]

$ file raw.bin

raw.bin: gzip compressed data, was "step9", from Unix, last modified: Fri Mar 21 22:58:52 2014

[/bash]

お、gzipぽい。早速gzipを解凍。

[bash]

// raw2(一行)

010011000101010101100100010000100・・・(中略)・・・1000011011001110011110100111101

[/bash]

2進数でてきた。

読みづらいので16進数に変換してみる。

[python]$ python

>>> raw2 = open("raw2.txt", "r")

>>> raw2tohex = open("raw2tohex.txt", "w")

>>> i = raw2.readline()

>>> raw2.close()

>>> h = hex(int(i, 2))

>>> raw2tohex.write(str(h))

>>> raw2tohex.close()

[/python]

開くと

[bash]

0x4C55644255316C42524651714A・・・(中略)・・・56436F6D4C544D7843673D3DL

[/bash]

0xとLを取ってASCIIコードに変換すると

[bash]

LUdBU1lBRFQqJi0yNS1H・・・(中略)・・・MzYtR0FTWUFEVComLTMxCg==

[/bash]

お、base64。デコードしてみる。

[bash]

-GASYADT*&-25-GASYADT*&-33-GASYADT*&-37-GASYADT*&-25-・・・(中略)・・・GASYADT*&-25-GASYADT*&-36-GASYADT*&-

[/bash]

GASYADTってなんぞ?とりあえずGASYADTを取り除いて数値だけ取り出す。

[bash]

//一行

253337253335253336253337253336253337253336253333253

333253631253332253636253332253636253336253333253336

253635253336253336253336253337253337253332253332253

635253336253338253336253636253336253338253336253331

253336253337253336253338253332253635253337253330253

336253332253337253631253332253636253333253337253333

253331253333253333253333253330253333253335253333253

335253333253334253332253636253330253631

[/bash]

だいぶ短くなってきた。

25が多いな?確か25は%だったから、URLエンコードかな?

というわけで、二文字おきに%でつないでURLデコード。

[python]

#coding: utf-8

def splitStr2(str, num):

l = []

for i in range(num):

l.append(str[i::num])

l = ["".join(i) for i in zip(*l)]

rem = len(str) % num  # zip で捨てられた余り

if rem:

l.append(str[-rem:])

return l

s = "2533372533352533362533372533362533372533362533332533332

536312533322536362533322536362533362533332533362536352533362

533362533362533372533372533322533322536352533362533382533362

536362533362533382533362533312533362533372533362533382533322

536352533372533302533362533322533372536312533322536362533332

533372533332533312533332533332533332533302533332533352533332

53335253333253334253332253636253330253631"

s2 = splitStr2(s, 2)

ans = ""

for v in s2:

ans += "%" + v

print(ans)

[/python]

出てきたものをURLデコード。

[bash]

%37%35%36%37%36%37%36%33%33%61%32%66%32%66%36%33%36%65%36%36%36%37%37%32%32%65%36%38%36%66%36%38%36%31%36%37%36%38%32%65%37%30%36%32%37%61%32%66%33%37%33%31%33%33%33%30%33%35%33%35%33%34%32%66%30%61

[/bash]

もういっちょ!

[bash]

756767633a2f2f636e6667722e686f686167682e70627a2f373133303535342f0a

[/bash]

ASCIIコードに直してみる。

[bash]

uggc://cnfgr.hohagh.pbz/7130554/

[/bash]

むむむっ これは換字式暗号。rot13をとりあえず試してみる。

[bash]

http://paste.ubuntu.com/7130554/

[/bash]

URLゲット!

アクセスすると

[bash]

5d3144233c46404dba4afc766601b997

[/bash]

32文字のMD5をゲットしました。これがフラグでした。

長かったけど楽しかった。

backdoorCTFは割と簡単な問題が多かったっぽい感じがするので、復習しておかないとな。。

CentOS でプロンプトのユーザー名が「私は名前がありません!」になった件について

VMで作ったテストCentOS鯖で遊んでいると・・・プロンプトのユーザー名が"私は名前がありません!"になってしまいました。

 

初めて見る謎のエラー文?で少しばかり手こずったのでメモ。

 

useradd usernameでユーザーを作成して、`su username` で変身。おもむろに画面を見ると・・・

 

```

[私は名前がありません!@server001 ~]$

```

 

なんだこの名前・・・ぐぐってもあまりいい情報が出ず。

 

とりあえず何か手がかりはないか模索してみる。すると、

 

```

[root@server001 ~]# su username

id: cannot find name for user ID 502

# -------------

[私は名前がありません!@server001 ~]$ id

uid=502 gid=502(username) 所属グループ=502(username) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

```

 

uidの関連付けあたりでエラー出てるっぽい?

 

usernameに変身もできるし、idでユーザー情報の確認ができるけど、uidが見つからない模様。

 

この辺りがおかしいのかな?

 

というわけで、`/etc/shadow` と `/etc/passwd` と `/etc/group` を確認。

 

内容には問題がなかったのですが、`/etc/passwd` パーミッションがオカシイ

 

```

[root@server001 etc]# ls -la passwd

-rw-------. 1 root root 1473  3月 20 00:47 2014 passwd

```

 

なるほど・・・

 

原因はここっぽい。/etc/passwdは644になっているべきなので、変更してあげる。

 

```

[root@server001 etc]# chmod 644 passwd

``

 

再度変身。

 

```

[root@server001 etc]# su server300

[server300@server001 etc]$

```

 

なおった!

 

passwdファイルはroot以外でも読み込むことがあるので644じゃないと行けないみたいですね〜

 

/etc/passwd ファイルを一般ユーザが更新できる理由 - ねもぷらす: http://nemoplus.hateblo.jp/entry/20100704/1278252696

 

なおってよかったー

SECCON2013全国大会に出場しました

SECCON全国大会に出場してきました。

ということで私が解くことができた問題のWriteupをしたいと思います。

今回、私が回答を提出した問題は0問でした。

以下、提出した問題のWriteupです。



・・・という冗談はさておき。

SECCON2013全国大会に出場してきました!

20140301_122919in 東京電機大学。会場も大学自体もすごくキレイで、さすが私大だなぁと・・・羨ましい。駅にも近いし周りは色々なものがあるしいい場所です。

地方大会ではJeopady方式(問題を解いてFLAGを手に入れる)ですが、本戦では攻防戦。自分のチームのFLAGを書き込み続ければその分点数どんどん増えていく感じです。

出場チームは地方大会とOnline予選を通過した強豪ばかり。会場の顔ぶれもすごい。

DSC01166sこんなかんじで挑みました。MacBook Airdynabook(Win8.1)。メインで使用するのはMBAです。

各チームのテーブルにはルーターとコンセント(3つ)があり、そこに接続して問題サーバーに接続するといった感じになってました。(http://2013.seccon.jp/seccon2013finalchallenge.html

周りは手慣れた様子で着々と用意を進めてました。(こわい)

会場モニタにはネットワーク可視化システム、NIRVANA改が動いていました。(写真撮り忘れていた。。。)

図2 NIRVANA改 SECCONカスタム(Towerモード)

イメージはhttp://www.nict.go.jp/info/topics/2014/02/140228-1.htmlより

かっこいい!!パケットの飛びがすごくわかりやすかったです。

nmapでスキャンしたりすると、パケットの点がズラーっと連なって飛んで行く様子が見て取れます。TCP, UDPどちらもわかるようになっているみたいです。私の家にも欲しいなぁ()w

隣のチームへのパケットも表示されるので運営側は不正行為を見つけやすい(?)ので非常に良いシステムだと思います。中の人とも軽くお話させてもらいましたが、このシステムを作るのに非常に苦労したようで・・・。

競技者側からのNIRVANA改への意見・視点を参考にしたいとおっしゃってました。(ただ私は競技に集中していたのでNIRVANA改を見る余裕があまりありませんでした。一度観客側からどんな感じか見たいものです)

問題を解いた感想など

私は参加はしたものの、Key提出までには至らずWriteupを書く人権がないのですが(足引っ張ってしまい非常に申し訳ないです)気づいた点などをメモしておきます。

次回参加できれば今回の全国大会の経験を活かして点数獲得したいな。

問題について

予選と違い、与えられる問題は実際の攻撃に近い形で与えられるので、ノーヒントです。分類やタイトルなども与えられないため、問題サーバーに接続しただけではどのジャンルなのかも分からない状態でした。

また、復号問題が大半で、なかなか取っ掛かりを見つけることが難しかったです。

やったことなど簡単に書きますがWriteupではないので、解法がほしい方は他のサイトを見るといいです。

karin.tower

Web系の問題。クッキーを覗いたり、取得できるディレクトリを探しまわっていましたが、なにも解けませんでした。

フォームに色々入れて、Mailaddressの入力フォームにXSSできることがわかりました。それだけでした。というのも、/image/の中に、captchaSt1.cgiやKey.jpg, Key2.jpg, /nothinghereディレクトリなど意味深なものが大量にあったのでそこの解析を重点的にやってしまったのです。。

Adminページがあることと、CookieCGIのSESSIONIDがあったので、どこかで使うのだろうと考えていたのですが、XSSでAdminのSESSIDを奪うなどという事は考えても見なかったです。終わったあとに考えてみれば、確かに管理人に対してメールを送るフォームであるから、AdminのSESSIDをXSSで奪えるよなぁ。

そこが解けないと次に行くのは無理みたいなので、解けたチームはどんどんKeyを獲得して、逆に解けなかったチームは全然得点を得ることが出来ないということに。悔しい。

2.kaku.tower

通天閣タワー。どんな問題か忘れかけてる程度には手も足も出なかったバイナリ系の問題。

シェルコードをうにゅうにゅする問題みたいですがバイナリ系は全くわからないので投げました。

バイナリを勉強する予定なので、問題来たら解けるようになりたいな。

Pisa.tower

ピサの斜塔。掲示板の問題。hattoriさんがつくったらしい。

掲示板にフラグを書き込むと得点が増えていく模様。とりあえずいろいろ試してみてもあまりわからず。。。

途中からXSSでダイアログでたり、強制リダイレクトされたり。

多分自動化すれば点は入るだろうと思って、思っただけでした。←こういうのダメですね

やっぱりやらないとダメだということを思い知らされました。結局これをやらなかったおかげで得点が伸びず。

captchaの自動化をどうするのだろうか?と思ったのですが、実はCaptchaを見ないとキーが変わらないみたい。気付かなかった。

Druaga.tower

ここから3問は一日目の最後に追加されたもの。

とりあえず落とし始めたのですが、回線は大混乱。80MBのファイルダウンロードに14kb/sとか無理だろ!

ギリギリまで粘りましたが、結局ダウンロードできず。落とせたチームは、二日目のはじめに即提出できたみたい。つよい。

Passwordはすでに与えられていたのでTrueCryptでマウントするとKeyを発見。

その後は、5つのディレクトリの中に100のディレクトリがあり、その中に100のディレクトリがあり、その中にtaka.jpgがそれぞれ入っていて、そのファイルはそれぞれ違うアセンブラ短歌が書かれている。これを詠んで、0609を出力できるものを探せというもの。

OCRを掛けて、ひたすら実行すれば出るだろうと思ったのですが、アセンブラ短歌の実行方法がわからず。

バイナリ勉強せねば・・・

Babel.tower

バベルの塔。2つのpureserverとjamserverという名前のバイナリファイルがそれぞれ別のポートで動いており、さらにそのファイルをもらえる。

jamserverの方は、メンバーがブラウザからそのポートにアクセスするとキーが出ることを発見。

pureserverの方は、フォーマットストリング攻撃だということはすぐにわかったのですが、やっぱりバイナリは(´・ω・`)

han01.tower

ログインフォームとハノイの塔のGifアニメがあり、更にid.txtやpass.txtへのリンクが有る。

id.txtの方をクリックすると、daemonとbackupと書かれたファイルが。URLをみると

[bash]

http://han01.tower/readfile.php?filename=id.txt&accesscode=1bc29b36f623ba82aaf6724fd3b16718

[/bash]

となっていた。accesscodeはMD5でハッシュされてるっぽい。何のハッシュなんだろう?と思ってるとメンバーの方がfilenameのハッシュだよ!と教えてくれた。やってみるとたしかにそうだ。

pass.txtも見れるかな?と思ってやってみたら見れた。daemonとbackupの後にsha~(1なのか512日は忘れた)のハッシュがあるだけ。結局わからず。

readfileではなんでも読み込めそうだったので、readfile.phpを読ませると読めた。さらにindex.htmlも読めて、ここにはkeyがありました。(すでに提出済み)

一日目はここで終了。後の話し合いの時に、メンバーさんが「ハノイの画像にIPアドレスみたいなのがあるよ」と。

二日目に試しにDNSに問い合わせてみました。がわからず。終わり際に別のサーバーがあるということが判明したので、2つめのハノイの塔のサーバーにアクセスをして見ると同じようなWebサイトが。同じようにreadfileに読ませてみると同じような感じに。

ただ今回は少し違って、menuというファイルがあることがreadfile.phpからわかった。index.htmlを読むとパスワードは/bin/menuのmd5だよ!って書いてあったので、readfile.phpにmenuを読ませるとbase64エンコードされた文字があった。コピペして、Pythonbase64をデコードして、出てきたバイナリを保存。

fileコマンドで見てみるとSysExファイルとの表示が。ググって調べるとどうやらMIDI関連のものらしい?アプリケーションで開いて探ってみましたが全くわからず、タイムアップ。よくわからないまま終わってしまった。

得点について

全国大会では地方予選と違い攻防戦なので、Keyを提出した点数と、自チームのFLAGを書き込み、守ることで得られる点数の2つがあります。

そのため、その両方を念頭に置いて挑まなければならないのですが、本戦はFLAGを守ることで得られる点数が非常に高かった模様。

私のチームはそのことを後回しにしていたせいで、後半の周りの伸びについていけず、大きく点数を落としてしまいました。

例えばPisa.towerの掲示板にFLAGを書き込む問題、最初から自動投稿するようにスクリプト化をしていたチームはそれだけでKey提出数回分の得点を得ていたみたいです。

点数配分は一応書いていたみたいなので、そこを読み飛ばしたのが悪いのですが。

一日目が終わっても競技は続く・・・

基本中の基本ですが一日目が終わってもファイル解析や落としたバイナリの穴探しは続けることができます。

Web系の問題は競技ネットワークが切られているので無理ですが、それでも話し合い位はできるので私のチームは居酒屋で酒を飲みながらやっていました(w

ホテルに到着してからもファイルの中を覗いたり色々とやっていましたが・・・うーん、難しかったw

全体的に

バイナリ系がなにもできなかったことが辛かったです。Web系の問題なども解けなかったので、メンバーさんに申し訳ない限り・・。

本戦形式の問題は、手がかりを探すことからはじめなければならないので、それに慣れていない私には厳しい大会でした。この辺りは経験が必要なのかな。

問題自体もスラスラと解けるようなものではなかったので、苦戦しました。

しかし、本戦出場は非常にいい経験になりました。チームメンバーには感謝してもしきれません。

来年のCTFで戦えるようにWriteupを見てバイナリとWebを中心に勉強しよう。

DSC01187