場阿忍愚(バーニング)CTF write-up

はじめに

場阿忍愚(バーニング)CTFに出てた。

1628点とって105位だった。

解いた問題は以下の通り。簡単なのしか解けていない。

芸術と兵法術、諜報術は割愛。

id カテゴリ タイトル ポイント
101 練習 image level 1 10
111 芸術 ワットイズディス? 33
112 芸術 cole nanee? 55
121 二進術 壱萬回 100
131 解読術 image level 5 50
132 解読術 Ninjya Crypto 100
133 解読術 Decrypt RSA 200
152 解析術 情報漏洩 100
161 電網術 ftp is not secure. 50
162 電網術 ベーシック 75
173 諜報術 Akiko-chan 155
181 記述術 search_duplicate_character_string 100
183 記述術 Count Number Of Flag's SubString! 100
201 兵法術 将棋詰め壱 50
202 兵法術 将棋詰め弐 100
203 兵法術 将棋詰め参 150
204 兵法術 将棋詰め四 200

image level 1

問題文

フラグを探して奉り候

101-level001a.png
101-level001b.png
101-level001c.png
101-level001d.png

CTF始めての方は一回四つの画像ファイルをダウンロードして、何かフラグっぽい文字列が分かれば提出してみて下さい。
それでも駄目でしたら、この解き方(チュートリアル)をご覧下さい。

解法

次のような4つのpngファイルが与えられる。

f:id:kira000:20151124023919p:plain f:id:kira000:20151124023951p:plain f:id:kira000:20151124024003p:plain f:id:kira000:20151124024011p:plain

縦に並べれば、"START-YAMATO-SEC!!!"という文字列が読み取れる。

FLAG:START-YAMATO-SEC!!!

壱萬回

問題文

121-calculation

解法

問題のファイルをリンクを踏むなりwgetなりでダウンロードしfileコマンドをかけると、64ビットのELFファイルだとわかる。

仮想マシン上の64bit版のUbuntuでファイルに実行権限を付与して実行してみる。

kira@kira-VirtualBox:~/Downloads$ chmod +x 121-calculation
kira@kira-VirtualBox:~/Downloads$ ./121-calculation
9 % 8 = 1
1 / 10 = 0
3 / 3 = 1
2 + 8 = 10
9 % 9 = 0
7 % 2 = 2

gdbでmainを逆アセンブルすると、main+398のところで、FLAGを表示するであろうshowFlagという関数を呼んでいるのが分かる。

そこに処理を飛ばしてみるとFLAGが表示される。

kira@kira-VirtualBox:~/Downloads$ gdb 121-calculation
GNU gdb (GDB) 7.6.1-ubuntu

(中略)

(gdb) start
Temporary breakpoint 1 at 0x400660
Starting program: /home/kira/Downloads/121-calculation

Temporary breakpoint 1, 0x0000000000400660 in main ()
(gdb) disas
Dump of assembler code for function main:
=> 0x0000000000400660 <+0>:    push   %r15
   0x0000000000400662 <+2>:   push   %r14

(中略)

   0x00000000004007e9 <+393>: jmpq   0x4006c8 <main+104>
   0x00000000004007ee <+398>: callq  0x400920 <showFlag>
   0x00000000004007f3 <+403>: nopl   0x0(%rax,%rax,1)

(中略)

   0x0000000000400818 <+440>: retq   
   0x0000000000400819 <+441>: callq  0x4005e0 <__stack_chk_fail@plt>
End of assembler dump.
(gdb) jump *main+398
Continuing at 0x4007ee.
FLAG_5c33a1b8860e47da864714e042e13f1e
*** stack smashing detected ***: /home/kira/Downloads/121-calculation terminated
(以下略)

FLAG:FLAG_5c33a1b8860e47da864714e042e13f1e

image level 5

問題文

フラッグを探してください

131-mondai.zip

解法

与えらえたzipファイルを解凍すると、次のような9つのpngファイルがみつかる。

f:id:kira000:20160207144727p:plain

ファイル名がmd5ハッシュ値のようなので、Google検索とかしてみると元の値が分かる。

8f14e45fceea167a5a36dedd4bea2543 => 7
45c48cce2e2d7fbdea1afc51c7c6ad26 => 9
1679091c5a880faf6fb5e6087eb1b2dc => 6
a87ff679a2f3e71d9181a67b7542122c => 4
c4ca4238a0b923820dcc509a6f75849b => 1
c9f0f895fb98ab9159f51fd0297e236d => 8
c81e728d9d4c2f636f067f89cc14862c => 2
e4da3b7fbbce2345d7772b0674a318d5 => 5
eccbc87e4b5ce2fe28308fd9f2a7baf3 => 3

元の値の順番でファイルを並べると"KOUBE-GYU"という文字列が読み取れる。

FLAG:KOUBE-GYU

Ninjya Crypto

問題文

敵か仲間か? 証明して!

f:id:kira000:20160207160742j:plain

解法

忍者文字というものらしい。

http://iga-ueno.or.jp/ninjamoji/%E5%BF%8D%E8%80%85%E6%96%87%E5%AD%97%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88/

ここのサイトを見ながら、デコードするとヤマトイエバとなる。

したがってFLAGは川

FLAG:

Decrypt RSA

問題文

133-Decrypt_RSA.zip

解法

与えられたzipファイルを解凍するとflag.txt, public-key.pem, READMEの3つのファイルが得られる。

opensslでn(=p*q)とeの値を取り出す。

$ openssl rsa  -text -pubin < public-key.pem
Public-Key: (640 bit)
Modulus:
    00:ae:5b:b4:f2:66:00:32:59:cf:9a:6f:52:1c:3c:
    03:41:01:76:cf:16:df:53:95:34:76:ea:e3:b2:1e:
    de:6c:3c:7b:03:bd:ca:20:b3:1c:00:67:ff:a7:97:
    e4:e9:10:59:78:73:ee:f1:13:a6:0f:ec:cd:95:de:
    b5:b2:bf:10:06:6b:e2:22:4a:ce:29:d5:32:dc:0b:
    5a:74:d2:d0:06:f1
Exponent: 65537 (0x10001)
writing RSA key
-----BEGIN PUBLIC KEY-----
MGwwDQYJKoZIhvcNAQEBBQADWwAwWAJRAK5btPJmADJZz5pvUhw8A0EBds8W31OV
NHbq47Ie3mw8ewO9yiCzHABn/6eX5OkQWXhz7vETpg/szZXetbK/EAZr4iJKzinV
MtwLWnTS0AbxAgMBAAE=
-----END PUBLIC KEY-----

16進数を10進数に直して

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

s = """
    00:ae:5b:b4:f2:66:00:32:59:cf:9a:6f:52:1c:3c:
    03:41:01:76:cf:16:df:53:95:34:76:ea:e3:b2:1e:
    de:6c:3c:7b:03:bd:ca:20:b3:1c:00:67:ff:a7:97:
    e4:e9:10:59:78:73:ee:f1:13:a6:0f:ec:cd:95:de:
    b5:b2:bf:10:06:6b:e2:22:4a:ce:29:d5:32:dc:0b:
    5a:74:d2:d0:06:f1"""

print int(s.replace(" ", "").replace(":", "").replace("\n", ""), 16)
n = 3107418240490043721350750035888567930037346022842727545720161948823206440518081504556346829671723286782437916272838033415471073108501919548529007337724822783525742386454014691736602477652346609

http://factordb.com/index.php

に投げると素数p, qの値が分かる。

p = 1634733645809253848443133883865090859841783670033092312181110852389333100104508151212118167511579

q = 1900871281664822113126851573935413975471896789968515493666638539088027103802104498957191261465571

あとはここ( http://d.hatena.ne.jp/kusano_k/20140323/1395571265)スクリプトを勝手に使わせてもらって、PRIVATE KEYを生成。

$ cat flag.txt | openssl rsautl -decrypt -inkey  private-key.pem
FLAG_IS_WeAK_rSA

FLAG:FLAG_IS_WeAK_rSA

情報漏洩

問題文

152-Leaked-Information.pcap

解法

与えられたpcapファイルをWiresharkで開くとUSBのパケットをキャプチャしたファイルだと分かる。

データのひときわ大きなパケットと前後二つのパケットを右クリックしてコピーしBinary Editor BZに貼り付ける。

ファイルのマジックナンバーを目印にして先頭の余分なバイト列を削除するとpngファイルが得られる。

f:id:kira000:20160207185318p:plain

FLAG:flag={gambare benesse}

ftp is not secure.

問題文

161-problem.pcap

解法

stringsコマンドで出てきた怪しげな文字列をbase64デコードすればFLAGが得られる。

$ echo RkxBR3tYVEluWDY5bnF2RmFvRXd3TmJ9Cg== | base64 -d
FLAG{XTInX69nqvFaoEwwNb}

FLAG:FLAG{XTInX69nqvFaoEwwNb}

ベーシック

問題文

162-basic.pcap

解法

pcapファイルをWiresharkで開くと何度かgetリクエストを送ってるのが分かる。

認証が成功した(200OKが返された)ときのgetリクエストを送ったパケットを見ると、

ユーザー名:http, パスワード://burning.nsc.gr.jpだと分かる。

ぱっと見た感じURIは見つからなかったけどおそらくhttp://burning.nsc.gr.jpにつなげばいいと推測がつくので、

そこにさっきのユーザー名とパスワードを使いアクセスすればFLAGが分かる。

FLAG:flag={BasicIsNotSecure}

Count Number Of Flag's SubString!

問題文

http://210.146.64.36:30840/count_number_of_flag_substring/

解法

GETリクエストの送り方や基本的な文字列操作ができれば、あとはやるだけ。

#!/usr/bin/env python2
#-*- coding: utf-8 -*-

import urllib, urllib2

FLAG = "flag={"
l = list("abcdefghijklmnopqrstuvwxyz_}")

while True:
  for i in l:
    FLAG += i
    url = "http://210.146.64.36:30840/count_number_of_flag_substring/?str=" + FLAG + "&count=count"
    req = urllib2.urlopen(url)
    s = "member of &quot;" + FLAG + "&quot; are 1"

    if i == '}':
      print "\n" + FLAG
      exit()

    elif s in req.read():
      print i,
      break

    else:
      FLAG = FLAG[:-1] #文字列のラストを削除
  • 実行結果
a f s f d s f d s f s o _ i d a r d k x a _ h g i a h r e i _ n x n k a s j d x _ h f u i d g i r e _ a n r e i a f n _ d s k a f i u d s u r e r f r a n d s k j n x x r
flag={afsfdsfdsfso_idardkxa_hgiahrei_nxnkasjdx_hfuidgire_anreiafn_dskafiudsurerfrandskjnxxr}

FLAG:afsfdsfdsfso_idardkxa_hgiahrei_nxnkasjdx_hfuidgire_anreiafn_dskafiudsurerfrandskjnxxr

search_duplicate_character_string

問題文

次のファイルに書かれている文字列の異なる2つの部分文字列s1, s2を考えた時、s1=s2となるもののうち、最も長いものを答えてください。

ただし、「sがSの部分文字列である」とは、0 <= k <= |S| - |s|であるようなある整数kを用いて、0 <= i < |s|なる全ての整数iについて、S[k+i] = s[i]が成り立つことを言います。

181-search_duplicate_character_string

解法

適当に実装した。

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

with open("in.txt", "r") as file:
  strings = file.read()

strings_list = strings.split("\n") #行ごとに分けたリストを生成

tmp = FLAG = ""

maxnumber = 0

for i in range(len(strings_list)):

  for j in range(len(strings_list[i])):

    tmp += strings_list[i][j]

    if strings.count(tmp) == 1:
      tmp = tmp[1:] #先頭文字を削除

    if len(tmp) > maxnumber:
      maxnumber = len(tmp)
      FLAG = tmp

  tmp = ""

print FLAG

FLAG:f_sz!bp_$gufl=b?za>is#c|!?cxpr!i><

おわりに

受験まであと3週間切ったこの時期にやることじゃなかった。

原付免許をとった

 アルバイトでも始めようかと思ったのがきっかけだった。

バイトをはじめるにあたって良い機会だから、銀行口座を開設しようと思ってゆうちょ銀行の口座を作ろうとしたら、本人確認書類というものが必要らしく困った。

免許もパスポートも持ってないし、保険証もどこにあるのか把握していないとあってどうしたものかと思いGoogle先生を頼ったのだけれど、免許やパスポート、印鑑登録証明書などを新たに取得しようとすると、そのために身分証明書が必要だという。

住基カードは簡単に作れそうだと思ったら、マイナンバー制度による個人番号カードの交付開始に伴い、交付を終了するとのこと。

どうしたものかと思い、ゆうちょ銀行の本人確認書類一覧のページをながめていたら、母子健康手帳(母および子に限る)とあって、おそらくこれであれば家を探せばどこかにあるだろう踏んで家をあさってみたところ見つかったのでその日のうちに近所の郵便局へ行って無事口座開設できた。

ゆうちょ銀行に口座開設する際に必要だったものは次の3点。

これで当初の目的は果たせたのだけれど、身分証明書がなければ何かと不便であると分かったので、この際作ってしまおうと思い比較的容易に取得できそうな原付の免許でもとろうかと考え調べてみると、どうやら住民票の写しと学生証が必要なようだった。

学生証に関しては問題ないとして、住民票の写しはどうやって入手すればいいのか調べたところ、私の住んでる自治体の場合は学生証と預金通帳の2点があれば市役所等で取得可能だとのこと。

住民票の写しを手に入れる際に必要だったものは次の3点。

  • 学生証

  • 預金通帳

  • 手数料(300円)

住民票の写しを手に入れて、後日運転免許試験場に赴き原付免許の試験を受けてだるい講習を経て免許を取得した。

原付の免許を取得するのに必要だったものは次の通り。

  • 住民票の写し
  • 学生証
  • 手数料(合計7,750円)
  • 写真(縦3cm×横2.4cm)
  • 両眼で0.5以上の視力
  • 16歳以上の年齢
  • 学科試験を合格できるだけの知識

学科試験対策はスマホのアプリを主に利用した。下記のアプリがクオリティ高くてよかった。

無料1210問!原付免許問題集! - Google Play の Android アプリ

余談だけど視力検査で2回再検査になって辛かった。なんとか1.0までには回復させたい。

SECCON 2015 オンライン予選 write-up

はじめに

 SECCON 2015 オンライン予選にぼっちチームhoge444として出てた。

競技開催期間は2015年12月05日(土) 15:00:00(JST)から24時間。

実質2問しか解けなかった。

Title Genre Points
Start SECCON CTF Exercises 50
SECCON WARS 2015 Stegano 100
Connect the server Web/Network 100
Last Challenge (Thank you for playing) Exercises 50

Start SECCON CTF

問題文

ex1
Cipher:PXFR}QIVTMSZCNDKUWAGJB{LHYEO
Plain: ABCDEFGHIJKLMNOPQRSTUVWXYZ{}

ex2
Cipher:EV}ZZD{DWZRA}FFDNFGQO
Plain: {HELLOWORLDSECCONCTF}

quiz
Cipher:A}FFDNEVPFSGV}KZPN}GO
Plain: ?????????????????????

解法

置換表がex1で与えられているのでPythonのstring.maketransを使って変換テーブルを作る。

#!/usr/bin/env python2
# -*- conding: utf-8 -*-

import string

cihper = 'A}FFDNEVPFSGV}KZPN}GO'
table = string.maketrans('PXFR}QIVTMSZCNDKUWAGJB{LHYEO', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ{}')

print cihper.translate(table)

trコマンドでも解ける。

$ echo "A}FFDNEVPFSGV}KZPN}GO" | tr PXFR}QIVTMSZCNDKUWAGJB{LHYEO ABCDEFGHIJKLMNOPQRSTUVWXYZ{}
SECCON{HACKTHEPLANET}

FLAG:

SECCON{HACKTHEPLANET}

Connect the server

問題文

login.pwn.seccon.jp:10000

解法

 まずncコマンドで接続を試みる。

f:id:kira000:20151206054213p:plain

WireSharkで通信を監視して適当にFollow TCP StreamするとFLAGっぽいものが見える。

f:id:kira000:20151206054234p:plain

FLAG:

SECCON{Sometimes_what_you_see_is_NOT_what_you_get}

SECCON WARS 2015

問題文

https://youtu.be/8SFsln4VyEk

解法

[追記]

 よくよく考えたらgifをそのままPythonで処理すればよかった。

  • Youtubeからmp4の動画をダウンロードしFree Video to GIF Converterで動画の00:25から01:05を切り取ってhoge.gifに変換(このときフレームレートは2 fpsで幅は600pxぐらいにするとよい)

あとは以下のようなスクリプトQRコードを作成すればいい。

#!/usr/bin/env python
#-*- coding: utf-8 -*-

from PIL import Image

gif = Image.open('hoge.gif')

width, height = gif.size
png = Image.new('RGB', gif.size, 'black')

while True:
    gif.load()
    for pos in ((x, y) for x in range(width) for y in range(height)):
        if gif.getpixel(pos) < 0x80:
            png.putpixel(pos, (0xff, 0xff, 0xff))

    try:
        gif.seek(gif.tell() + 1)
    except:
        break

png.save('flag.png', 'PNG')
  • gifのフレームレートを10 fpsにしたとき生成される画像

f:id:kira000:20151223012840p:plain

[ここまで追記]

 ひどい解き方をした。手順は次の通り。

  • youtubeから、問題の動画をダウンロード

  • Free Video to GIF Converterで動画の00:25から01:05を切り取ってhoge.gifに変換(フレームレートは1fpsにした)

  • convertコマンドで分離

$ convert +adjoin hoge.gif fuga.gif
  • Gimpで一枚一枚白黒の画像に加工(全部で42枚)。(画像>モード>グレースケールを選択し、色>しきい値からしきい値を127に設定)

f:id:kira000:20151206054353p:plain

(最初まっ黒な画像を作って読み込んだgifに白いところがあったら白を描画してる)

wars_solver.py · GitHub

※このコードは汚いだけでなくて実行時間もかなりかかるので色々とひどい。

f:id:kira000:20151206054433p:plain

  • 最後にQRコードを読み取るとFLAGが分かる。

FLAG:

SECCON{TH3F0RC3AVVAK3N53P7}

Last Challenge (Thank you for playing)

問題文

ex1
Cipher:PXFR}QIVTMSZCNDKUWAGJB{LHYEO
Plain: ABCDEFGHIJKLMNOPQRSTUVWXYZ{}

ex2
Cipher:EV}ZZD{DWZRA}FFDNFGQO
Plain: {HELLOWORLDSECCONCTF}

quiz
Cipher:A}FFDNEA}}HDJN}LGH}PWO
Plain: ??????????????????????

解法

 最初の問題と同じ。

$ echo "A}FFDNEA}}HDJN}LGH}PWO" | tr PXFR}QIVTMSZCNDKUWAGJB{LHYEO ABCDEFGHIJKLMNOPQRSTUVWXYZ{}
SECCON{SEEYOUNEXTYEAR}

FLAG:

SECCON{SEEYOUNEXTYEAR}

おわりに

 問題は全然解けなかったけど、時間はたくさん溶けた。とても辛かった。

VirtualBox上のUbuntuの画面を自動リサイズできるようにする方法

ターミナルでコマンド入力すれば簡単にできる。

$ sudo apt-get install virtualbox-guest-dkms virtualbox-guest-utils virtualbox-guest-x11

$ sudo reboot

Ubuntuに限らずDebian系OS全般は全く同じコマンドで多分問題ない。

x86アセンブリ言語に関するメモ

x86系マイクロプロセッサの持つ主なレジスタ

レジスタ 呼び名 主な機能
EAX アキュムレータ 算術演算の結果を格納
EBX ベースレジスタ メモリアドレスを格納
ECX カウントレジスタ ループ回数をカウント
EDX データレジスタ データを格納
ESI ソースインデックス データ転送元のメモリアドレスを格納
EDI ディスティネーションインデックス データ転送先のメモリアドレスを格納
EBP ベースポインタ データの格納領域のメモリアドレスを格納
ESP スタックポインタ スタック領域のメモリアドレスを格納

主な命令

命令 使用例 意味    詳細
MOV MOV EAX,ECX EAX = ECX ECXの値をEAXに格納
MOVZX MOVZX_EAX,ECX EAX = ECX MOVのサイズが違うレジスタにコピーするとき用いる版
ADD ADD EAX,ECX EAX += ECX EAXにECXを加算
SUB SUB EAX,ECX EAX -= ECX EAXからECXを減算
MUL MUL EAX,ECX EAX *= ECX EAXにECXを乗算
DIV DIV EAX,ECX EAX =EAX / ECX EAXをECXで割って,商をEAXに,余りをEDXに格納
EDX = EAX % ECX
INC INC EAX EAX++ EAXに1を加算
DEC DEC EAX EAX-- EAXに1を減算
XOR XOR EAX,ECX EAX = EAX ^ ECX EAXとECXの各ビットごとに排他的論理和を取り,結果をEAXに格納
LEA LEA EAX,[ECX+4] EAX = ECX + 4 ECX+4(アドレス値)をEAXに格納
CMP CMP EAX,ECX if(EAX==ECX)_ZF=1 EAXとECXの値を比較してフラグに反映
else ZF = 0 EAXとECXが等しければZF=1,EAXとECXが等しくなければ、ZF=0
TEST TEST EAX,EAX if(EAX==0) ZF = 1 EAXの値を0と比較してフラグに反映
else ZF = 0 EAXが0と等しければZF=1,EAXが0でなければZF=0
JE(JZ) JE 041001000 if(ZF == 1) ZFが1なら041001000にジャンプ
GOTO 041001000
JNE(JNZ) JNE 041001000 if(ZF == 0) ZFが0なら041001000にジャンプ
GOTO 041001000
JMP JMP 041001000 GOTO 041001000 無条件で041001000にジャンプ
CALL CALL lstrcmpW lstrcmpW() lstrcmpw関数の呼び出し
PUSH PUSH 00000001 スタックへ00000001を格納
POP POP EAX スタックからEAXへ値を取得
NOP NOP 何もしない
SAR SAR EAX EAXを右にシフトする,すなわち2で割る
RET RET 処理を呼び出し元に戻す

豆知識

  • XOR EAX,EAX は MOV EAX,0 と同様の働きをする。

  • dword ptr は、指定されたメモリアドレスから4バイトのデータを読み出すことを示す。

例えば、

MOV DWORD PTR SS:[EBP-8],1

の場合ベースポインタのアドレス-8から4バイトまでのデータに整数値1を格納している。