ASIS CTF Finals 2013に参加した

久しぶりにCTFの参戦記。これまでもいくつか参加していたけど、ブログに書く余裕がなかったのであった。 write upではないので、詳細な解説はないです。

今回のCTFは、イランの ASIS CTF Finals 2013。Finalsだけど参加は誰でもできる。 ジャンルはWeb, Forensic, PPC (Professional Programming and Coding), RE (Reverse Engineering), Crypto, Stegoの6つ。 自分はPPCぐらいしか解けなかった。WebもCryptoも解けなかったのはつらい。 128チーム中45チームが解けていた "[RE 51] Simple Binary" が解けなかったのもつらい。

[PPC 76] Maze

$ nc *** 12433

Hi! Solve these mazes like the sample
1000111111
1101100101
0110111001
1011010111
1101010101
1011010101
1101101101
0100110101
0111010101
1101111101
=>
*000111111
**01100101
0**0111001
10**010***
110*010*0*
101*010*0*
110**01*0*
0100**0*0*
01110*0*0*
11011***0*

go ahead

1011101111
1110111001
0001000011
1111111110
1000010001
1111101101
0100110111
1010010101
1111010101
1001111101

^C

迷路を解く問題。

単純な深さ優先で解けた。微妙。

import sys
import socket
from copy import deepcopy
 
def solve1(stage, r=0, c=0):
    stage[r][c] = ord('*')
    print "[+]\n%s\n" % '\n'.join(map(str, stage))
 
    if r == len(stage)-1 and c == len(stage[0])-1:
        return stage
    for (dr, dc) in [(0,-1), (-1,0), (0,1), (1,0)]:
        (r2, c2) = (r+dr, c+dc)
        if not (0 <= r2 < len(stage) and 0 <= c2 < len(stage[0])):
            continue
        if stage[r2][c2] == ord('1'):
            ans = solve1(deepcopy(stage), r2, c2)
            if ans:
                return ans
 
 
def solve(data):
    stage = map(bytearray, data.split())
    answer = solve1(stage)
    answer = '\n'.join(map(str, answer))
    return answer
 
 
HOST = '***'    # The remote host
PORT = 12433    # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
 
data = s.recv(1024)
print "[+] Received: %r" % data
 
while data:
    data = s.recv(1024)
    print "[+] Received: %r" % data
 
    answer = solve(data)
    print "[+] Answer\n%s\n" % answer
    s.sendall(answer)
    print "[+] Sended: %r" % answer
    data = s.recv(1024)
    print "[+] Received: %r" % data
 
s.close()
[+] Received: '\nCongratulations! The flag is ASIS_1274a1c4f3a6f00ae92d2985f1413759\n'

[Forensic 75] rm-rf

We have received a usb flash backup. Which file the flag is in?

[for-75.img.xz]

$ unxz for-75.img.xz

$ ll -h for-75.img
-rw-r--r-- 1 john None 512M Aug 29 21:44 for-75.img

$ file for-75.img
for-75.img: Linux rev 1.0 ext3 filesystem data, UUID=f2b52915-94b9-418c-963b-b7f6e15b4f25 (large files)

512MBのext3ダンプからフラグを探す問題。

Mazeの答えからflagのフォーマットを推測し、stringsしただけで解けた。微妙。

$ strings for-75.img | grep -i ASIS > grep-asis.log

$ head grep-asis.log
ASIS_logo_200.png
ASIS_b34c5b5b1b78cf9f352099aa35610ced!.D@
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
    portions thereof) either on an unmodified basis, with other
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS,
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
    portions thereof) either on an unmodified basis, with other
COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN ``AS IS'' BASIS,
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
ASIS_b34c5b5b1b78cf9f352099aa35610ced

[PPC 203] Code Code Code

$ nc *** 12435

MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMNNWNXNNWMMMMNXXKKXXNWMMMMMMMMMM
MMMMMNOKOdkXxdK0XWMMMMMMWXOxdlloodkOXWMMMM
MMNOKXxlOWMMMWMMMMNkddkKMMMMMKdollodxdkNMM
MMXol0XOo0WMMMMMO,,:okc'.dWMMMWOllloxoco0W
XxXNkONW0kWMMMMO lkXOdKOl.lMMMMWKXkllllcdN
KlkWWKOXWKWMMMMo'NXWMWkx0:,MMMMWkdolllccoX
Nxo0WNd:xWWMMMMX.cNW0KXkc.OMMMMKoooddlllkW
MWOdNMXx:oKWWMWo:l;:c.';lXMMMWKOlcdd:lo0WM
MMMWNNMKlcON0c..oNMMNNWMMMMXkkOldkkkKKWMMM
MMMMMMMMMXO:  lNMMMMMMMMNO0KOKWXKNMMMMMMMM
MMMMMMMMMMMNkWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
Are you ready for this, man! press any key

What is the message hidden below:
 - - - - - - - + - - - + + + - + + - + + - + - - - - - - -
 - + + + + + - + - + - + + - - - - + + + - + - + + + + + -
 - + - - - + - + + - + + - + - + + + + + - + - + - - - + -
 - + - - - + - + + + - - - + + - - + - - - + - + - - - + -
 - + - - - + - + - - + - + - - - + - - + - + - + - - - + -
 - + + + + + - + - + + + + - - - + + - - - + - + + + + + -
 - - - - - - - + - + - + - + - + - + - + - + - - - - - - -
 + + + + + + + + + - - - - + + + + - - - - + + + + + + + +
 - - - - + - - + + + + - + - + - + - + - + - + - - + + - -
 + + - - - - + + + + - - + - - - + - - - + + - + + - + + +
 + + + - - - - - - + - + + - - + - - - - - - + + + + + + +
 + - + + + + + - + + + + - + - - + + + + - + - - + + + + -
 + - - - - + - + - + + - + - - + - + + - + - - + - + + - +
 - - + + + + + - - - + + - + - - - - + + - + - - - - + + -
 - - + + - + - + + - + - - + - + + - + - - + - + + - + - -
 - + + + - + + - - - + - - + - + - - - - - + - + + - + - -
 + - + - - - - + + + + + + - + + + + - + + - - + + + - + +
 + - + - - - + + - - - + + - + + + - - + - - - - - - - - -
 - + + - - + - + - - + - - - - + - - - - + - + + + - + - -
 + - - + + - + + + - + - - + - - + - + - + - + - - + + + -
 + - - + - - - - - + + - - - + - - + - + - - - - - - - - -
 + + + + + + + + + + - - + + + - - + + - - + + + - - - + -
 - - - - - - - + + - + + - - + + + + - + - + - + - + - + -
 - + + + + + - + - + - + + + + + + - - - - + + + - + - - +
 - + - - - + - + + + - + + - + - - - + - - - - - - - - + +
 - + - - - + - + - + + + - + + - - + + - - + + + - + + - -
 - + - - - + - + - - + + - + + - + + + + - - - + + + - - +
 - + + + + + - + - + - - - + - - - + + + - - - - + + - - +
 - - - - - - - + - + + - - + + + + - + - - + - + + + - + +
Dont sleep!!!

QRコードをデコードする問題。

ZBarというライブラリがPILオブジェクトから直接デコードできて便利そうだったので、これを使って解いた。

import sys
import socket
import zbar
import Image
 
 
def solve(data):
    pixels = []
    for line in data.splitlines():
        pixels.append([0xFF if c == '+' else 0 for c in line.split()])
    im = Image.new('L', (len(pixels), len(pixels[0])))
    width, height = im.size
    for r in range(width):
        for c in range(height):
            im.putpixel((r,c), pixels[r][c])
    width, height = width*10, height*10
    im = im.resize((width, height))
 
    scanner = zbar.ImageScanner()
    scanner.parse_config('enable')
    zbar_img = zbar.Image(width, height, 'Y800', im.tostring())
 
    scanner.scan(zbar_img)
 
    for symbol in zbar_img:
        answer = symbol.data
 
    del(zbar_img)
 
    return answer
 
 
HOST = '***'    # The remote host
PORT = 12435    # The same port as used by the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
 
data = s.recv(1024)
print "[+] Received: %r" % data
 
data = s.recv(1024)
print "[+] Received: %r" % data
 
s.sendall("\n")
print "[+] Sended: %r" % "\n"
 
while data:
    data = s.recv(34)
    print "[+] Received: %r" % data
 
    data = s.recv(2048)
    print "[+] Received: %r" % data
 
    answer = solve(data)
    s.sendall(answer)
    print "[+] Sended: %r" % answer
    data = s.recv(1024)
    print "[+] Received: %r" % data
 
s.close()
[+] Received: 'the flag is: ASIS_ebd814d43f558d1b73d077234f65b71b\n'

[PPC 450] Captcha (時間切れ)

1枚の画像に8つの円が描かれているので、それらの背景色を文字列として答える問題。

Python-OpenCVで円を認識させるところまではできたが、HoughCirclesのパラメータがうまく決まらず、個数が合わない。 パラメータを変えながら何度も試さないといけないんだろうな……と思い当たったあたりで、時間が取れず終了。

# -*- coding: utf-8 -*-
 
import urllib2
import Image
from cStringIO import StringIO
import cv, cv2
import numpy as np
 
def solve(im, cv_im):
    color2char = {'100': 'r', '010': 'g', '001': 'b', '110': 'y', '101': 'm', '011': 'c', '111': 'w'}
 
    cv_im = np.asarray(cv_im[:,:])
    cv_im_gray = cv2.cvtColor(cv_im, cv.CV_BGR2GRAY)
    circles = cv2.HoughCircles(cv_im_gray, cv.CV_HOUGH_GRADIENT, dp=2, minDist=10, circles=None, param1=100, param2=100, minRadius=10, maxRadius=100)
 
    answer = ""
    for (x, y, radius) in circles[0]:
        rgb = im.getpixel((int(x), int(y)))
        color = ''.join([str(x >> 7) for x in rgb])
        answer += color2char[color]
 
    assert len(answer) == 8
    return answer
 
 
if __name__ == '__main__':
    f = urllib2.urlopen('http://***:65437/')
    cookie = f.info()['Set-Cookie'].split(';', 1)[0]
    f.close()
    print "[+] cookie: %r" % cookie
 
    while True:
        opener = urllib2.build_opener()
        opener.addheaders.append(('Cookie', cookie))
        f = opener.open('http://***:65437/captcha.png')
        png = f.read()
        f.close()
 
        im = Image.open(StringIO(png))
        cv_im = cv.CreateImageHeader(im.size, cv.IPL_DEPTH_8U, 3)
        cv.SetData(cv_im, im.tostring())
        assert im.size == cv.GetSize(cv_im)
        assert im.tostring() == cv_im.tostring()
        im.save('test.png')    # for debugging
 
        answer = solve(im, cv_im)
        print "[+] answer: %r" % answer
 
        postdata = "username=&password=&captcha=%s&submit=" % answer
        print "[+] postdata: %r" % postdata
        f = opener.open('http://***:65437/', postdata)
        html = f.read()
        f.close()
 
        print "[+] result:\n%s" % html