st98 の日記帳


[ctf][seccon] SECCON 2014 オンライン予選 (英語) の write-up

チーム sobachka として参加しました。最終的に獲得できたポイントは 1901 点でチーム順位は 73 位 (登録 1068 チーム、参加 804 チーム中) でした。

の 12 問を解きました。

時間内に解けた問題

Welcome to SECCON (Start 100)

そのままです。

flag: SECCON{20141206}

Easy Cipher (Crypto 100)

0 から始まるものは 8 進数、0 と 1 のみで 4 桁以上あるものは 2 進数、a-f が含まれるものは 16 進数、残りは 10 進数としてパースします。
あとは String.fromCharCode に投げるとフラッグが出てきます。

var s = '87 101 108 1100011 0157 6d 0145 040 116 0157 100000 0164 104 1100101 32 0123 69 67 0103 1001111 1001110 040 062 060 49 064 100000 0157 110 6c 0151 1101110 101 040 0103 1010100 70 101110 0124 1101000 101 100000 1010011 1000101 67 0103 4f 4e 100000 105 1110011 040 116 1101000 0145 040 1100010 0151 103 103 0145 1110011 0164 100000 1101000 0141 99 6b 1100101 0162 32 0143 111 1101110 1110100 101 0163 0164 040 0151 0156 040 74 0141 1110000 1100001 0156 056 4f 0157 0160 115 44 040 0171 1101111 117 100000 1110111 0141 0156 1110100 32 0164 6f 32 6b 1101110 1101111 1110111 100000 0164 1101000 0145 040 0146 6c 97 1100111 2c 100000 0144 111 110 100111 116 100000 1111001 6f 117 63 0110 1100101 0162 0145 100000 1111001 111 117 100000 97 114 0145 46 1010011 0105 0103 67 79 1001110 123 87 110011 110001 67 110000 1001101 32 55 060 100000 110111 0110 110011 32 53 51 0103 0103 060 0116 040 5a 0117 73 0101 7d 1001000 0141 1110110 1100101 100000 102 0165 0156 33';

String.fromCharCode.apply(null, s.split(' ').map(function(e){
  if(/^0[0-7]+$/.test(e)) {
    return parseInt(e, 8)
  } else if (/^[01]{4,}$/.test(e)) {
    return parseInt(e, 2);
  } else if (/[a-f]/.test(e)) {
    return parseInt(e, 16);
  } else {
    return parseInt(e, 10);
  }
}));
flag: SECCON{W31C0M 70 7H3 53CC0N ZOIA}

Shuffle (Binary 100)

objdump -M intel -d shuffle すると mov eax,0x53; mov BYTE PTR [esp+0x25],al みたいな部分が。
gdb で 0x80486b3 にブレークポイントを置いてスタックを見るとフラッグが分かります。

$ gdb shuffle
(gdb) b *0x80486b3
(gdb) r
…
Breakpoint 1, 0x080486b3 in main ()
(gdb) x/s $esp + 0x24
0xbffff714: "SECCON{Welcome to the SECCON 2014 CTF!}"
flag: SECCON{Welcome to the SECCON 2014 CTF!}

Reverse it (Binary 100)

うちのチームが最初に正解したので 1 点のボーナスが入っていました。

ファイルの末尾の 8d ff から反転されており、更に上位 4 ビットと下位 4 ビットが入れ替わっている JPEG であることが分かります。
プログラムで直すとフラッグが書かれた画像が出てきます。

import io
from PIL import Image

a = open('Reverseit', 'rb').read()[::-1]
Image.open(io.BytesIO(bytes(((c & 0xf0) >> 4) | ((c & 0xf) << 4) for c in a))).show()
flag: SECCON{6in_tex7}

jspuzzle (Web 100)

といった JavaScript の仕様を頭に入れて埋めていくとアラートできます。

"use strict";

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

REA-JUU WATCH (Web 200)

ポイントが表示されるページのソースを見ると $.getJSON("/users/chk/14333", …); という部分が。
/users/chk/1 を見ると 99999 ポイントを獲得したユーザのユーザ名とパスワードが書かれています。
手に入れた情報を使ってログインし、ポイントが表示されるページまで進めるとフラッグが表示されます。

flag: SECCON{REA_JUU_Ji8A_NYAN}

Bleeding “Heartbleed” Test Web (Web 300)

まず Heartbleed が通るサーバであると騙すために Heartbleed のハニーポット (http://packetstormsecurity.com/files/126068/Heartbleed-Honeypot-Script.html) を渡したコマンドライン引数を返すようにいじりました。

このハニーポットを使って ' を返すと DATABASE ERROR!!! near "Server": syntax error と表示されました。
恐らく SQLite なのでデータベースの構造を確認するために ' union select group_concat(sql, '|') from sqlite_master;-- と返すとソースに <!-- DEBUG: INSERT OK. TIME=CREATE TABLE results ( time, host, result )|CREATE TABLE ssFLGss ( flag )|CREATE TABLE ttDMYtt ( dummy ) --> と出力されていました。
得られた情報を利用して ' union select flag from ssFLGss;-- と返すと <!-- DEBUG: INSERT OK. TIME=SECCON{IknewIt!SQLiteAgain!!!} --> とソースに出力されていました。

flag: SECCON{IknewIt!SQLiteAgain!!!}

SECCON Wars: The Flag Awakens (QR 300)

動画の加工をするために ffmpeg を使って 55 ~ 61 秒辺りを切り取り、さらにフレームごとに画像を切り出します。
あとは Python で QR コードが出ている部分を切り取って結合して、携帯か何かを使って読み込むとフラッグが出てきます。

$ ffmpeg -i secconwars.mp4 -ss 55 -t 6 out.mp4
$ ffmpeg -i out.mp4 -f image2 images/%02d.png
$ python secconwars.py
# secconwars.py
from PIL import Image

d = 4
start = 1
end = 83

w, h = 400, d * ((end - start) // d)
out = Image.new('RGB', (w, h))
for x in range(start, end + 1, d):
  im = Image.open(r'images/\{}.png'.format(str(x).zfill(2)))
  out.paste(im.crop((35, 236, 280, 240)), (0, (x // d) * d - start))

out2 = Image.new('RGB', (w, h))
for y in range(h):
  out2.paste(out.crop((0, y, w, y + 1)), (y, y))
out2.save('result.png')

(書いたプログラムが適当なので出てきた画像がアレですが、縦に引き延ばして色を反転すると読めるかなと)

flag: SECCON{M4Y 7H3 F0RC3 83 W17H U}

Get the key.txt (Forensics 100)

バイナリエディタで開いて key.txt を検索すると 1f 8b ... から始まる gzip で圧縮されたファイルが引っ掛かります。
次の 1f 8b ... が来るまでコピーして Python で展開するとフラッグが出てきます。

import gzip
print(gzip.open('a.gz', 'rb').read()) # b'SECCON{@]NL7n+-s75FrET]vU=7Z}\n'
flag: SECCON{@]NL7n+-s75FrET]vU=7Z}

Choose the number (Programming 100)

与えられたいくつかの数値の最大値か最小値を答える問題です。
Python でプログラムを書いてしばらく解かせておくとフラッグが出てきます。

import re
import socket
import sys

def main(host='number.quals.seccon.jp', port=31337, *_):
  s = socket.create_connection((host, port), 3)
  s.settimeout(3)

  num = re.compile(r'(-?[0-9]+)')

  while True:
    r = s.recv(1024)
    print('[*]', r)

    if b'?' not in r:
      while True:
        r += s.recv(1024)
        print('[*]', r)
        if b'?' in r:
          break
        if b'SECCON' in r:
          s.close()
          return 0

    n = [int(x) for x in num.findall(r.decode('ascii'))]

    if b'minimum' in r:
      n = min(n)
    else:
      n = max(n)

    s.send(str(n).encode() + b'\n')
    print('[*]', n)

  s.close()
  return 0

if __name__ == '__main__':
  sys.exit(main(*sys.argv[1:]))
flag: The flag is SECCON{Programming is so fun!}

Get the key (Network 100)

渡された nw100.pcap を読むと、http://133.242.224.21:6809/nw100/ を見ようとして一度認証が必要だと返されている様子が。
認証に成功したパケットを見ると Authorization: Basic c2VjY29uMjAxNDpZb3VyQmF0dGxlRmllbGQ= とあるので c2VjY29uMjAxNDpZb3VyQmF0dGxlRmllbGQ= を Base64 でデコードします。
Basic 認証なので、出てきた seccon2014:YourBattleFieldseccon2014YourBattleField がそれぞれログインに利用するユーザ名とパスワードになります。
手に入れたユーザ情報を利用して /nw100/key.html にアクセスするとフラッグが表示されました。

flag: SECCON{Basic_NW_Challenge_Done!}

Get from curious “FTP” server (Network 300)

与えられた URL に telnetanonymous として入って list コマンドを送ると LIST not implemented. と返されました。
なら help コマンドはどうだと送ると You should see the RFC959. と返ってきます。
telnet はさすがに使いにくいので Python で適当なクライアントを作ってから、言われたとおりに RFC を見て書かれているコマンドを片っ端から送っていると、acct を送ったときにディレクトリの情報が返ってきました。
retr コマンドを使ってディレクトリにあるテキストファイルを読み込むとフラッグが出てきました。

> telnet ftpsv.quals.seccon.jp 21
220 (vsFTPd 2.3.5(SECCON Custom))
user anonymous
331 Please specify the password.
pass
230 Login successful.
list
502 LIST not implemented.
help
214-You should see the RFC959.
214 Help OK.
> python ftp.py
[*] 230 Login successful.
> acct
-rw-r--r--    1 0        0              38 Nov 29 04:43 key_is_in_this_file_afjoirefjort94dv7u.txt
[!] 500 Unknown command.
> retr key_is_in_this_file_afjoirefjort94dv7u.txt
SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}
[*] 226 Transfer complete.
>
# ftp.py
import ftplib
import sys

def main(host='ftpsv.quals.seccon.jp', *args):
  with ftplib.FTP(host, timeout=5) as ftp:
    print('[*]', ftp.login())
    while True:
      i = input('> ')

      while i == '':
        i = input('> ')

      if i == 'q' or i == 'quit':
        break

      try:
        print('[*]', ftp.retrlines(i))
      except Exception as e:
        print('[!]', str(e))
        if str(e) == 'timed out':
          break
    ftp.quit()

if __name__ == '__main__':
  main(*sys.argv[1:])
flag: SECCON{S0m3+im3_Pr0t0c0l_t411_4_1i3.}
このエントリーをはてなブックマークに追加
st98.github.io / st98 の日記帳