読者です 読者をやめる 読者になる 読者になる

ASIS CTF Finals 2013に参加した

CTF

久しぶりに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