Rating:

Writeups

TEAMTaruTaru
RANK116
CTF TIME URLhttps://ctftime.org/event/1567
CONTEST URLhttps://spaceheroes.ctfd.io/

abstract

sanity_check

Discord

Discord

shctf{4ut0b0ts_r013_0u7}

k?

MEE6に/helpを実行すると/help commands が存在することがわかり,/help commandsを実行すると!kコマンドが存在することがわかります.

よって#mee6チャンネルで「!k」と入力して送信するとDMに「k? shctf{WhY_iS_K_BaNnEd} ?」と送信されます.

shctf{WhY_iS_K_BaNnEd}

web

R2D2

http://173.230.138.139/robots.txt

shctf{th1s-aster0id-1$-n0t-3ntir3ly-stable}

Space Traveler

スクリプトにこのような箇所があります.

[
  "\x47\x75\x65\x73\x73\x20\x54\x68\x65\x20\x46\x6C\x61\x67",
  "\x73\x68\x63\x74\x66\x7B\x66\x6C\x61\x67\x7D",
  "\x59\x6F\x75\x20\x67\x75\x65\x73\x73\x65\x64\x20\x72\x69\x67\x68\x74\x2E",
  "\x73\x68\x63\x74\x66\x7B\x65\x69\x67\x68\x74\x79\x5F\x73\x65\x76\x65\x6E\x5F\x74\x68\x6F\x75\x73\x61\x6E\x64\x5F\x6D\x69\x6C\x6C\x69\x6F\x6E\x5F\x73\x75\x6E\x73\x7D",
  "\x59\x6F\x75\x20\x67\x75\x65\x73\x73\x65\x64\x20\x77\x72\x6F\x6E\x67\x2E",
  "\x69\x6E\x6E\x65\x72\x48\x54\x4D\x4C",
  "\x64\x65\x6D\x6F",
  "\x67\x65\x74\x45\x6C\x65\x6D\x65\x6E\x74\x42\x79\x49\x64",
];

ブラウザのコンソール機能で実行します

['Guess The Flag', 'shctf{flag}', 'You guessed right.', 'shctf{eighty_seven_thousand_million_suns}', 'You guessed wrong.', 'innerHTML', 'demo', 'getElementById']
0: "Guess The Flag"
1: "shctf{flag}"
2: "You guessed right."
3: "shctf{eighty_seven_thousand_million_suns}"
4: "You guessed wrong."
5: "innerHTML"
6: "demo"
7: "getElementById"

shctf{eighty_seven_thousand_million_suns}

Flag in Space

http://172.105.154.14/?flag=の後ろにフラグを入力し,合っていた数だけフラグが表示される(=私が予測したフラグと正しいフラグを比較し,合っている文字数を教えてくれる)ようです.Pythonを用いて全探索します.

import requests

baseurl = "http://172.105.154.14/?flag=shctf{2_"
chars = "0123456789_}abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
size = 8

for _ in range(30):
    for c in chars:
        url = baseurl + c
        print(url)
        html = requests.get(url).text[799:]
        flag = [
            c.replace("<div>", "")
            .replace("</div>", "")
            .replace("</html>", "")
            .replace("\n", "")
            for c in html.split("</div>\n<div>")
        ]
        flag = [c for c in flag if c != ""]
        if len(flag) > size:
            print("I got!! -> " + "".join(flag))
            baseurl += c
            size+=1
            break

shctf{2_explor3_fronti3r}

Mysterious Broadcast

アクセスするたびに1文字ずつ~,0,1のいずれかが表示されます.何度もアクセスしてみます.

import requests

url = "http://173.230.134.127/"

while True:
    res = requests.get(url)
    print(res.text, end="")
    url = res.url
~1100011011001011010001101010110010010001111011010011011110100011011000100111011010101100001101011111011001001010110001101100001000101011001110100011101101110110001100001010101011001110100101101000110001011011011010010110100011000111101101101001001110011000011110011101111010111101~1100011011001011010001101010110010010001111011010011011110100011011000100111011010101100001101011111011001001010110001101100001000101011001110100011101101110110001100001010101011001110100101101000110001011011011010010110100011000111101101101001001110011000011110011101111010111101~1100011011001011010001101010110010010001111011010011011110100011011000100111011010101100001101011111011001001010110001101100001000101011001110100011101101110110001100001010101011001110100101101000110001011011011010010110100011000111101101101001001110011000011110011101111010111101~1100011011001

~を区切り文字して1100011011001011010001101010110010010001111011010011011110100011011000100111011010101100001101011111011001001010110001101100001000101011001110100011101101110110001100001010101011001110100101101000110001011011011010010110100011000111101101101001001110011000011110011101111010111101が何度も繰り返されています.

これを7ビットごとに区切ってSSCIIに変換します

cybershef

shctf{AsciiIsA7BitStandard}

Space Buds

Space Budsで検索すると「space buddies」という画像の映画が存在することがわかります.問題文よりその映画の犬の一人がWebサーバに入ったと推測します.「space buddies」の登場人物はWebサイトに書いてあり,B-Dawg,Budderball,Buddha,Captain Canine,Mudbud,Rosebudの六匹です.順にinputタグとcookieに入れます.

試してみると,CookieはMudbudをdata-rawは犬の名前のいずれかを入れるとフラグが得られます.

curl 'http://45.79.204.27/getcookie' -H 'Cookie: userID=Mudbud' --data-raw 'nm=Rosebud'

cookies

shctf{tastes_like_raspberries}

OSINT

Launched

まずはexif情報を見ます.

exiftool -c "%.6f launch.jpg

ここで重要そうな情報が2つ書かれていました.

GPS Position                    : 28.586000 N, 80.650689 W
File Modification Date/Time     : 2022:04:03 05:15:21+09:00

このGPSの場所はジョン F ケネディ・スペース・センター図書館のあたりで,NASAの施設です. そこで,「NASA rocket 2019/04/11」と検索します.するとこのような記事が出てきます.Falcon Heavy, SpaceX’s Giant Rocket, Launches Into Orbit, and Sticks Its Landings

ロケットの名前は「Falcon Heavy」です.そこで,「Falcon Heavy」のWikipediaを見ました.するとPayloadは「Arabsat-6A」ということがわかります.よってフラグは以下の通りです.

shctf{falcon_heavy_Arabsat-6A}

Curious?

Google Lensを使用するとNASAのサイトが出てきます.

nasa

しかし,このサイトからフラグの情報が得られません.私はかなり多くのサイトから画像を検索しましたが,類似画像は見つからなかったので,動画から検索することにしました.

Youtubeで「mars curiosity SOL dune」と検索するとそれらしき動画がありました.

この動画の引用元が下記のサイトです(こちらの方が見やすい).

https://www.360cities.net/image/mars-panorama-curiosity-solar-day-530

mars

赤枠の箇所が問題の写真と一致しているように見えます.答えはshctf{SOL_530}だと思い,提出しましたがincorrectでした. SOLの1単位は(わかりませんが)あまり長い距離ではありません.

これがSOL530 mars530-2

これがSOL534 SOL534

このように,あまり移動していないことがわかります.そこで,私はSOL_527から順に入力しました.答えは533でした.

shctf{SOL_533}

Programming

FRK War

このような入力が1200個与えられます [1267.56, 1130.04, 2588.27, 3338.14, 236.17, 11320.41, 2363.41, 531.25, 1136.72, 1690.02] Romulan Light Fighter

最終的にこのような数がRomulanなのかKlingonsなのかStarfleetなのか当てます.Starfleetの時は船を攻撃してはいけないのでNを,それ以外の時はYを出力します.

[1250.23, 2817.63, 1820.81, 60.23, 492.89, 44.86, 2013.35, 48.02, 765.08, 6591.08]

私はまずintelligence reportsからgrepして数列を探索しましたが,そのようなものは存在しませんでした.しかし,intelligence reportsをよく見ると規則性がありそうです.例えば,この辺りを見てみます(一部編集しています)

[35.05,1941.88,4287.8,2902.9,184.9,2917.93,338.47,2367.21,732.21,4272.74] Klingon
[35.57,2209.72,3959.27,3023.05,134.17,3388.35,880.13,2647.26,460.87,2831.3] Klingon
[35.02,2417.65,1156.22,3055.21,146.49,3348.8,520.05,3973.01,913.65,2746.14] Klingon
[35.13,1575.59,772.11,2883.36,136.16,3894.96,762.43,5266.27,157.02,4019.84] Klingon
[35.32,1770.4,1833.5,4252.28,141.68,2294.74,967.64,2061.43,119.84,5691.19] Klingon
[35.01,1556.79,817.6,2872.76,145.95,3523.56,512.55,4362.45,822.15,5423.58] Klingon
[35.21,1457.47,1882.9,3831.39,154.28,3150.99,646.55,2969.09,276.35,3330.41] Klingon
[35.45,1471.34,4342.47,4768.3,138.08,3532.35,765.62,2374.26,993.36,4643.89] Klingon
[35.18,1790.57,3906.92,4013.17,177.81,2794.02,297.51,2653.02,338.13,4948.36] Klingon
[35.32,1239.93,741.28,3029.71,135.18,2471.76,621.28,3402.43,861.54,5827.88] Klingon

Klingonの船の先頭は35が非常に多く見えます.そこで,この問題は数列から特徴量を抽出し,どの船か判別する機械学習の問題だと判断しました.私はSVMを使用しました.

from pwn import *
import numpy as np
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC

clf = make_pipeline(StandardScaler(), SVC(gamma="auto"))

io = remote("0.cloud.chals.io", 29511)

X = []
y = []

while True:
    line = str(io.recvline())[2:-3]
    if line == "=" * 112:
        break
    if line[0] == "=":
        continue
    print(line)
    line = line.split("]")
    ships = line[0]
    country = line[1][1:].split(" ")[0]
    ships = eval(ships + "]")
    X.append(ships)
    if country == "Romulan":
        y.append(0)
    elif country == "Klingon" or country == "Klington":
        y.append(1)
    elif country == "Starfleet":
        y.append(2)

X = np.array(X)
y = np.array(y)

clf.fit(X, y)

while True:
    line = str(io.recvline())[2:-3]
    print(line)
    if line[:9] == "Congrats!":
        continue
    if line[:20] != "A ship approaches:  ":
        continue
    line = eval(line[20:])
    _ = str(io.recv())[2:-1]
    payload = "N"
    res = clf.predict([line])
    if res[0] != 2:
        payload = "Y"
    print("fire ->", payload)
    io.send(payload + "\n")

実行してから数秒後,フラグが得られました.

Congrats, the war is over: shctf{F3Ar-1s-t4E-true-eN3my.-Th3-0nly-en3my}

shctf{F3Ar-1s-t4E-true-eN3my.-Th3-0nly-en3my}

Crypto

Mobile Infantry

パスワードを入力するとフラグが得られる仕組みです.

Welcome to Ricos Roughnecks. We use 1-time-pads to keep all our secrets safe from the Arachnids.
Here in the mobile infantry, we also implement some stronger roughneck checks.
Enter pad > 

パスワードは以下の形式である必要があります.

  1. 全部で38文字
  2. 先頭20文字は大文字
  3. 後半18文字は小文字
  4. 前半19文字は,次の文字が文字コードで1多くする(ABC...)
  5. 20文字目以降は,次の文字が文字コードで1少なくする(zyx...)

まず私はABCDEFGHIJKLMNOPQRSTzyxwvutsrqponmlkjiを入力しました.すると[+] Welcome the mobile infantry, keep fighting.とだけ出力され,フラグえられませんでした. そこでDiscordを見てみるとこのようなヒントが出ていました.

pad = input("Enter pad > ")
result = otp(secret, pad)
if (result == flag):
    print("[+] The fight is over, here is your flag: %s" % result)
else:
    print("[+] Welcome the mobile infantry, keep fighting.")

今回正しいpadとなる文字は(len(upper_chers)-20+1)(len(lower_chers)-18+1)=79=63しかありません.例えばこのようなものです.

ABCDEFGHIJKLMNOPQRSTzyxwvutsrqponmlkji
BCDEFGHIJKLMNOPQRSTUzyxwvutsrqponmlkji
CDEFGHIJKLMNOPQRSTUVzyxwvutsrqponmlkji
...
GHIJKLMNOPQRSTUVWXYZtsrqponmlkjihgfedc
GHIJKLMNOPQRSTUVWXYZsrqponmlkjihgfedcb
GHIJKLMNOPQRSTUVWXYZrqponmlkjihgfedcba

よってこれらを全探索します.

from pwn import *


up = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
low = "abcdefghijklmnopqrstuvwxyz"

for i in range(7):
    for j in range(9):
        up_sub = up[i : i + 20]
        low_sub = "".join(list(reversed(low[j : j + 18])))
        pad = up_sub + low_sub
        io = remote("0.cloud.chals.io", 27602)

        for _ in range(100):
            line = str(io.recvline())[2:-3]
            print(line)
            if (
                line
                == "Here in the mobile infantry, we also implement some stronger roughneck checks."
            ):
                break
        line = str(io.recvline())
        line = str(io.recv())[2:-1]
        print(line)

        io.send(pad + "\n")
        for _ in range(7):
            line = str(io.recvline())[2:-3]
            print(line)
            if "flag" in line:
                exit()
            if "[+] Welcome" in line:
                break

最終的にフラグが出力されました.

[+] The fight is over, here is your flag: shctf{Th3-On1Y-G00d-BUg-I$-A-deAd-BuG}

shctf{Th3-On1Y-G00d-BUg-I$-A-deAd-BuG}

Khaaaaaan!

暗号文を見ると4つの難解なフォントでフラグが書かれているように思えます.後半3つはGoogle Lensなどを用いるとすぐ出てきますが,最初がわかりません.

https://de.ffonts.net/Covenant.font.download https://ja.m.wikipedia.org/wiki/%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB:KLI_pIqaD.svg https://www.pinterest.jp/pin/460704236877423332/

ここまでの暗号文を一度復号します.

language

of choice there is no creativity

Google で「of choice there is no creativity」と検索します.

Without freedom of choice, there is no creativity

が大量に出てきます.換字式暗号なので,同じ記号は同じ文字を意味します.「Without freedom」はあっていそうです.

language

これをもとに最初の5文字も当てはめてみます.

language

おそらく最初の5文字はshctfでしょう.これを_で区切ってフラグの形式で提出するとフラグが得られます.

shctf{without_freedom_of_choice_there_is_no_creativity}

Forensics

Space Captain Garfield

画像をGoogle Lensで検索するとこのようなサイトが出てきます.

https://king-harkinian.fandom.com/wiki/Garfield?file=Trekfield.jpg

space

これを問題の画像に当てはめます.

SpaceCaptainGarfield

ただの換字式暗号です.するとフラグはshctf{LASAGNALOVER}となります.

shctf{LASAGNALOVER}

Rev

Cape Kennedy

与えられるプログラムより,パスワードは8文字で合計すると713,??ABBACC 形式,特殊文字は使用しないようです.正規表現にするとr'([A-z0-9][A-z0-9]([A-z0-9])([A-z0-9])\3\2([A-z0-9])\4)'のようになります.

私はまずこのようなプログラムを書きましたが数十万個のパスワード候補が出てしまいました.

if __name__ == "__main__":
    alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

    for c1 in tqdm(alphabet):
        for c2 in alphabet:
            for c3 in alphabet:
                for c4 in alphabet:
                    for c5 in alphabet:
                        password = f"{c1}{c2}{c3}{c4}{c4}{c3}{c5}{c5}"
                        if check(password):
                            print(password)

regex word search siteにもそのような単語はなく,かなり悩んだ末,問題文とDiscordをよくみました.

remember this is a themed ctf, the answer is NOT random Its a famous thing from the US Space Program(discord)

「US Famous Space Program」で検索するとList of space programs of the United Statesが出てきます.しかしこのページのテキストには上記の正規表現に一致する単語はありません. そこで,私はwikipediaをクロールすることにしました.プログラムはこれです.

import requests
import re
from tqdm import tqdm
url = "https://en.wikipedia.org/wiki/List_of_space_programs_of_the_United_States"


html = requests.get(url).text

href = re.findall(r'<a href="(.*?)"', html)
urls = []
for h in href:
    if h[0]=='/':
        urls.append("https://en.wikipedia.org"+h)
    elif h[0]=="#":
        continue
    else:
        urls.append(h)

text = ""

for u in tqdm(urls):
    html = requests.get(u).text
    text += html

passwd = re.findall(r'([A-z0-9][A-z0-9]([A-z0-9])([A-z0-9])\3\2([A-z0-9])\4)', text)
print(passwd)

List of space programs of the United Statesから1回のリンクを踏んだ先にあるコンテンツから正規表現で単語を探しています. 最終的に出力から「Apollo11」が出てきました.

shctf{Apollo11}

Original writeup (https://github.com/xryuseix/CTF_Writeups/blob/master/SpaceHeroes2022/Writeups.md#discord).