ひよこになりたい

Programming Server Network Security and so on

TrendMicro CTF(TMCTF) 2015 Online Qualifier 解けた問題などWriteup

Trendmicro CTF 2015のOnline Qualifier(TMCTF)をやってみました。
大学の友人がCTFにやる気を出していたので、今回はいつものチームではなく大学の友人3人でチーム"sagume"として出ました。

結果としては700点ほどしかとれませんでしたが、大学の友人に教えたり教えられたりとワイワイやりながらやるCTFは楽しかったです。

以下Writeup

Prog100

Click on the different color.
http://ctfquest.trendmicro.co.jp:43210/click_on_the_different_color

一つだけ違う色のタイルが表示され、それをクリックすると次に飛ぶといった問題。
進んで行くにつれてタイル数が増えていく。

f:id:zipsan:20150927180003p:plain:w150

イメージはこんな感じ。 http://game.ioxapp.com/eye-test/game.html

<html>
<head>
<title>Choose the different color</title>
<script type=text/javascript src="/js/jquery.min.js"></script>
<script type=text/javascript>
<!--
function clicked(e)
{
  var x=e.layerX-e.target.x;
  var y=e.layerY-e.target.y;
  window.location.href='/5cfe6d025d390ffa640c8cd3ac83b0e560705a001826?x='+x+'&y='+y;
}
// -->
</script>
</head>
<body>
  <img src="/img/5cfe6d025d390ffa640c8cd3ac83b0e560705a001826.png" onClick="clicked(event)">
</body>
</html>

解法

htmlを取得してBeautifulSoupで解析、画像を取得して、順番に色のデータのリストを作ってその中で一つしかないものを抽出してリクエストを送信。

最初は目grepでやってたけど、16x16からつらくなったのでスクリプト化した。 適当に分割して中心付近を適当に取得すれば行けるやろって感じで進めていくと、64くらい?から2x2px, 1x1pxになって悲しみを背負った。

# -*- coding: utf-8 -*-
from PIL import Image
from bs4 import BeautifulSoup

import requests
import sys

HEADERS = {
    'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A403 Safari/8536.25',
    'Accept-Language': 'ja,en-us;q=0.7,en;q=0.3',
    'Accept': 'text/html, application/xml;q=0.9, application/xhtml+xml, image/png, image/webp, image/jpeg, image/gif, image/x-xbitmap, */*;q=0.1'
}

def calc(num, imgpath):
  img = Image.open(imgpath)
  width, height = img.size
  box = (5, 5, width, height)
  img = img.crop(box)
  colors = []
  width, height = img.size
  ds = (width + 1) / num
  for i in range(num):
      wmin = int(ds * i)
      wmax = int(ds * (i + 1))
      for j in range(num):
          k = num * i + j
          hmin = int(ds * j)
          hmax = int(ds * (j + 1))
          box = (wmin, hmin, wmax, hmax)
          c = img.crop(box)
          choice_pixel = (wmin, hmin)
          pixelcolor = img.getpixel(choice_pixel)
          colors.append((pixelcolor, (i + 1, j + 1), choice_pixel))
  only_color_list = [c for c, _, _ in colors]
  for c, p, choicepix in colors:
    if only_color_list.count(c) == 1:
      print("FOUND!", choicepix)
      print(p)
      return choicepix
  else:
    return None
base_url = "http://ctfquest.trendmicro.co.jp:43210"

i = 2
r = requests.get(base_url + '/click_on_the_different_color', headers=HEADERS)
post_ans_url  = base_url + '/click_on_the_different_color'
while True:
  print(i)
  html = BeautifulSoup(r.text)
  img_url = base_url + html.body.img["src"]
  filename = img_url[:-4]
  print(filename)

  r = requests.get(img_url, headers=HEADERS)
  with open("./image-tmp.png", "wb") as fp:
    fp.write(r.content)


  p = calc(i, "./image-tmp.png")
  if p is None:
    with open("result.png", "wb") as fp:
      print("CONTINUE")
      fp.write(r.content)
      r = requests.get(post_ans_url, headers=HEADERS)
      continue

  print(filename)
  filename = img_url[42:-4]
  post_ans_url = base_url + filename + "?x={0}&y={1}".format(p[0] + 5, p[1] + 5)
  print(post_ans_url)
  r = requests.get(post_ans_url, headers=HEADERS)

  if r.text == "Too late":
    print("TOOLATE!! EXIT")
    quit()

  i += 1
TMCTF{U must have R0807 3Y3s!}

汚いコードですがご容赦ください

Prog200

Calculate it.
nc ctfquest.trendmicro.co.jp 51740

計算問題。nc で接続すると式が降ってくるので計算して返す。

[1] 1 - 9 = -8
[2] 8 + 5 - 67 = -54
[3] 63 + 861 * 48 - 3 = 41388
[4] 273 + 9533 * 7888 + 162 * 627 = 75298151
[5] 75 * 95410 - 3 + 8284 * 92 - 5137 = 7912738
[6] 4879 * 80 + 4671 - 3 * 3961 - 69872 * 7063 =

途中からローマ数字や英単語数字が出てくる。

[35] ( CLXXXIX - MMCCLXIII ) * ( VII + 5288 ) * DLXXVIII + CMXLVI =
[46] four thousand, two hundred forty * ( 309,912 + four hundred twenty seven ) * ( six hundred twenty one thousand, eight hundred thirty one - twenty thousand, four hundred sixty seven ) * eight hundred forty + eighty eight thousand, nine hundred twenty three =

解法

式を受け取り、eval()する。途中のローマ数字や単語数字はググッてパース出来る関数を探してきた。
ただし、連なっている単語数字の判定が必要だったので、その部分を書いた。
具体的には、1単語で変換可能なものは次の単語も見て、それも合わせて再変換する。その後、また次の単語も見て再変換...を繰り返して、Exceptionが吐かれた=変換不能となったら終了とした。

solve.py

# -*- coding: utf-8 -*-
import telnetlib
import prog200libs

tn = telnetlib.Telnet()

tn.open("ctfquest.trendmicro.co.jp", 51740)

i = 1
while True:
    msg = tn.read_until(b" = ").decode()
    print("[{0}] {1}".format(i, msg), end="")
    raw = msg[:-3].replace(',', '')

    # roman
    def roman_check(raw):
        for r in ('M', 'D', 'C', 'L', 'X', 'V', 'I'):
            if r in raw:
                return True
        return False

    transformed = []
    for a in raw.split():
        if roman_check(a) and a not in ('-', '+', '*', '/'):
            t = str(prog200libs.roman_to_num(a))
            transformed.append(t)
        else:
            transformed.append(a)
    else:
        raw = ' '.join(transformed)

    # text to int
    transformed = []
    split_raw = raw.split()
    index = 0
    while index < len(split_raw):
        try:
            s = split_raw[index]
            res = str(prog200libs.text2int(split_raw[index]))
            while index < len(split_raw):
                try:
                    s += " " + split_raw[index + 1]
                    res = str(prog200libs.text2int(s))
                except:
                    break
                index += 1
            transformed.append(res)
        except:
            transformed.append(split_raw[index])
        index += 1
    else:
        raw = ''.join(transformed)

    ans = eval(raw)
    print(ans)
    tn.write(str(ans).encode() + b'\n')

    i += 1

prog200libs.py

def roman_to_num(roman):
    num = 0
    r_n = {
        'M': 1000,
        'D': 500,
        'C': 100,
        'L': 50,
        'X': 10,
        'V': 5,
        'I': 1
    }
    pre = 0
    for i in range(len(roman) - 1, -1, -1):
        c = roman[i]
        if not c in r_n:
            raise Exception('有効なローマ数字になっていない!: {}'.format(c))
        n = r_n[c]
        if n >= pre:
            num += n
            pre = n
        else:
            num -= n
    return num


def text2int(textnum, numwords={}):
    if not numwords:
      units = [
        "zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
        "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
        "sixteen", "seventeen", "eighteen", "nineteen",
      ]

      tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

      scales = ["hundred", "thousand", "million", "billion", "trillion"]

      numwords["and"] = (1, 0)
      for idx, word in enumerate(units):    numwords[word] = (1, idx)
      for idx, word in enumerate(tens):     numwords[word] = (1, idx * 10)
      for idx, word in enumerate(scales):   numwords[word] = (10 ** (idx * 3 or 2), 0)

    current = result = 0
    for word in textnum.split():
        if word not in numwords:
          raise Exception("Illegal word: " + word)

        scale, increment = numwords[word]
        current = current * scale + increment
        if scale > 100:
            result += current
            current = 0

    return result + current

結果

[100] 4364133 - 953,422 + one hundred ninety one million, four hundred ninety seven thousand, three hundred eighty five * ( three hundred fifty two + four million, forty eight thousand sixty nine ) * ( 3 + 75 ) * ( twelve + 33660154 ) * ( 83664191 + five hundred fifty eight thousand, three hundred fifty nine ) = 171430368741839902987854582089711
[101] Congratulations!
The flag is TMCTF{U D1D 17!}

Analysis-defensive 300

This is a REAL backdoor network traffic!
Tracing hacker's footprint to find the key!

If you can get "the password", please submit "TMCTF{}" as your answer.

Hint:
Poison Ivy / admin
attach: zipfile(pcap)

pcapファイルが渡される。中身はPoizon Ivyのトラフィックログのようだ。

開いてみてもただ暗号化されたパケットが流れているだけでとくに得られる情報はない。 f:id:zipsan:20150927181946p:plain:w150

Poizon Ivyは標的型攻撃などで使用される最も有名なRATツール(Remote Administration Tool)の一つ。現在でも様々な亜種が生まれており、最近でも新しい機能が追加され、改良され続けている。*1
C&Cサーバーとの通信やリモートシェルによるコマンドの実行、暗号化など基本的な動作に加え、マルウェアバックドア)のビルド機能、スクリーンショットプラグイン機能などを備えており、目的に合わせたマルウェアを作成可能である。
あまりにも有名なため、セキュリティベンダによる解析が進められており、動作解析レポートも公開されている。

解法

PIVYのトラフィック(pcap)を解析し、標的となっている端末内のデータを取得する。

PIVYは通信の初めに256byteの暗号化キー(平文)を格納しており、それを使用して通信を行う*2ため、解読が可能。Camellia cipherが使用されている。

手作業で一つ一つ暗号化解除するのだるい...と思っていたら、MITREがChopshopというProtocol Analysis/Decoder Frameworkを出しており、これを使用すれば良いとFireeyeのレポート*3に書いてあった。 github.com

早速kali linuxに落としてきて、makeして起動。make時にいろんな依存ライブラリが必要なのでインストールが大変.....

使用方法はchopshop内のDocsを参考に色々試してみた。pluginの中にpoisonivy_23xというモジュールが入っているのでこれを使用する。コマンドは--helpやDocsを参考に頑張って組み立てた。

./chopshop -f /vboxshare/tmctf/net.pcap "poisonivy_23x -p /usr/local/lib/python2.7/dist-packages/camcrypt/camellia.so -f -c -l " -s save

実行すると以下の様なログが出る

>> ~/Documents/ctf/tmctf/chopshop
root@murasa# ./chopshop -f /vboxshare/tmctf/net.pcap "poisonivy_23x -p /usr/local/lib/python2.7/dist-packages/camcrypt/camellia.so -f  -c -l " -s save
Warning Legacy Module poisonivy_23x!
Starting ChopShop (Created by MITRE)
Initializing Modules ...
    Initializing module 'poisonivy_23x'
Running Modules ...
[2015-09-04 17:43:44 JST]  Poison Ivy Version: 2.32
[2015-09-04 17:43:44 JST]  *** Host Information ***
PI profile ID: ctf
IP address: 192.168.0.100
Hostname: ADMIN-PC
Windows User: Administrator
Windows Version: Windows XP
Windows Build: 2600
Service Pack: Service Pack 3
[2015-09-04 17:43:58 JST]  *** Directory Listing Initiated ***
Directory: C:\WINDOWS\
[2015-09-04 17:43:58 JST]  *** Directory Listing Sent ***
PI-directory-listing-1.txt saved..
[2015-09-04 17:44:57 JST]  *** Service Listing Sent ***
PI-service-listing-2.txt saved..
[2015-09-04 17:45:06 JST]  *** Screen Capture Sent ***
PI-extracted-file-3-screenshot.bmp saved..
Shutting Down Modules ...
    Shutting Down poisonivy_23x
Module Shutdown Complete ...
ChopShop Complete

-sで指定したディレクトリに書き出されるので確認する。

>> ~/Documents/ctf/tmctf/chopshop/save
root@murasa# ls -la
total 844
drwxr-xr-x  2 root root   4096 Sep 27 03:31 .
drwxr-xr-x 14 root root   4096 Sep 27 03:27 ..
-rw-r--r--  1 root root   9738 Sep 27 18:34 PI-directory-listing-1.txt
-rw-r--r--  1 root root 810054 Sep 27 18:34 PI-extracted-file-3-screenshot.bmp
-rw-r--r--  1 root root  29544 Sep 27 18:34 PI-service-listing-2.txt

bmpに答えが書いてあった。

f:id:zipsan:20150927185415p:plain

TMCTF{May_Flower}

セキュリティベンダっぽい問題で面白いなと思いました。こういうセキュリティっぽい問題増えて欲しいですね。

Analysis-defensive 100

取り組みだすのが遅すぎて解答を提出できなかったけど、終了後答えが出たので書いておきます。早起きすべきだった。

vonnというx64のELFが渡されるのでリバースエンジニアリングして動作を解析する問題。
そのまま実行すると自分自身を消してしまう。また、VM検知が入っておりマルウェアっぽい挙動をする。

解法

gdbで動かしながら実行する。
mainの最初でVM検知が入っており、それにより分岐が行われるみたいなので、jumpして次の動作へ飛ばした。

すると、...,,,...を/tmp以下に生成するというマルウェアっぽい動きをするので、確認するとコレも実行ファイルだった。そのまま続けると...,,,...に動作を移行し、終了すると自己を消去する。動作を続けると終了へ飛ぶ判定があるので、その部分を飛ばすとフラグprintへ移行した。

TMCTF{ce5d8bb4d5efe86d25098bec300d6954}
提出できなかったないけど多分これ

まとめ

TMCTF、セキュリティ寄りの問題も多く、非常に楽しめました。少しばかりエスパーっぽい問題が多いという声もありますが、おそらく経験で補える範囲だと思うので精進したいです。

あと、24時間という時間の割に問題数がそれなりにあったので、見れてない問題も多かったです。Writeup見ながらやります。