好奇心の足跡

飽きっぽくすぐ他のことをしてしまうので、忘れないため・形にして頭に残すための備忘録。

ångstromCTF 2020 Misc の writeup

2020年 3/14(土)9:00 - 3/19(木)9:00 JST で開催された、ångstromCTFMisc分野のwriteupです。CTF Timesはこちら
他の分野のwriteup, 戦績はこちら。

kusuwada.hatenablog.com

[Misc] Sanity Check

Join our Discord! https://discord.gg/Dduuscw

discordに参加したらflagがもらえる...わけではなさそう。

最初に見えるチャネルはこれだけ。

f:id:kusuwada:20200319090825p:plain

このroleチャネルにヒントが書いてある。

React with:triangular_flag_on_post: to gain access to the rest of the channels, and react with :bell: to get notifications about announcements.

ユーザーリストを観察するに、おそらく competitator の role になる必要がありそう。

...でもどうやって?

だいぶ悩んだ末、書いてあるとおりにフラグとベルの絵文字でリアクションすると、roleが割り当てられて色んなchannelが見れるようになりました👍

generalチャネルのtopicにflagが置いてありました。

f:id:kusuwada:20200319090859p:plain

Sanity Checkにしては時間かかった…。

[Misc] ws1

Find my password from this recording (:

pcapファイル recording.pcapng が配布されます。wiresharkで開く前に strings コマンドしたらflagが出てきました。

$ strings recording.pcapng | grep actf
flagz,actf{wireshark_isn't_so_bad_huh-a9d8g99ikdf})

[Misc] clam clam clam

clam clam clam clam clam clam clam clam clam nc misc.2020.chall.actf.co 20204 clam clam clam clam clam clam

Hints

U+000D

ヒントのU+000Dは、

Unicode Character 'CARRIAGE RETURN (CR)' (U+000D)

キャリッジリターンのこと。

最初人見たときは、指定されたホストにアクセスしても何も返ってこなかったけど、ちょっと後に見たらちゃんと返ってきてた。

$ nc misc.2020.chall.actf.co 20204
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}

これがだーーーーーっと続く。

途中でflagが流れてくるのかな?と思って

$ nc misc.2020.chall.actf.co 20204 | grep actf

したんだけど、出てこないまま接続切れてしまう。
とりあえず通信のログを取ろうと思って

$ nc misc.2020.chall.actf.co 20204 > log.log

した後だーっと見ると

type "clamclam" for salvation^Mmalc{malc_malc_malc_malc_malc}

という行がところどころにいるのを発見。
再度ncコマンドで接続して clamclam を打つとflagが出てきました。

malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
malc{malc_malc_malc_malc_malc}
clam{clam_clam_clam_clam_clam}
actf{cl4m_is_my_f4v0rite_ctfer_in_th3_w0rld}

[Misc] ws2

No ascii, not problem :)

recording.pcapng

ws1の続き。今回はasciiじゃないらしい。また pcap ファイルが配布されます。

今度はちゃんとwiresharkで開いてみると、こんな通信が。

68   10.104713   127.0.0.1   127.0.0.1   HTTP    497 HTTP/1.0 200 OK  (text/html)
(中身)
<strong>Success:</strong>File '/Users/toaster/Desktop/personal/projects/angstromctf/2020/misc/ws2/flag.jpg' upload success!<br><a href="http://localhost/">back</a><hr><small>Powerd By: bones7456, check new version at <a href="http://li2z.cn/?s=SimpleHTTPServerWithUpload">here</a>.</small></body>
                      ></body>.

どうやらjpegがやり取りされたようです。

上にさかのぼって探すと、ありました。

54   10.100649   127.0.0.1   127.0.0.1   HTTP    102 POST / HTTP/1.1  (JPEG JFIF image)

この通信を、File > Export Objects > HTTP... で取り出してみると、一つが

------WebKitFormBoundaryRkbG5NuQ7ardXnel
Content-Disposition: form-data; name="file"; filename="flag.jpg"
Content-Type: image/jpeg

で始まっているデータなので、この中にjpegがありそう。このファイルをまるっとforemostにかけて

$ formost extract 
-bash: formost: command not found
kusuwada:ws2 natsumi$ foremost -i extract 
foremost: /usr/local/etc/foremost.conf: No such file or directory
Processing: extract
|*|

無事、jpegを抽出してくれました。

f:id:kusuwada:20200319091017j:plain

ワンパンマン!!!!!!!!!

[Misc] Inputter

Clam really likes challenging himself. When he learned about all these weird unprintable ASCII characters he just HAD to put it in a challenge. Can you satisfy his knack for strange and hard-to-input characters? Source.

Find it on the shell server at /problems/2020/inputter/.

Hint

There are ways to run programs without using the shell.

実行ファイルinputterと、下記のソースコードが配布されます。

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#define FLAGSIZE 128

void print_flag() {
    gid_t gid = getegid();
    setresgid(gid, gid, gid);
    FILE *file = fopen("flag.txt", "r");
    char flag[FLAGSIZE];
    if (file == NULL) {
        printf("Cannot read flag file.\n");
        exit(1);
    }
    fgets(flag, FLAGSIZE, file);
    printf("%s", flag);
}

int main(int argc, char* argv[]) {
    setvbuf(stdout, NULL, _IONBF, 0);
    if (argc != 2) {
        puts("Your argument count isn't right.");
        return 1;
    }
    if (strcmp(argv[1], " \n'\"\x07")) {
        puts("Your argument isn't right.");
        return 1;
    }
    char buf[128];
    fgets(buf, 128, stdin);
    if (strcmp(buf, "\x00\x01\x02\x03\n")) {
        puts("Your input isn't right.");
        return 1;
    }
    puts("You seem to know what you're doing.");
    print_flag();
}

プログラムを読む限り、実行時の引数に\n'\"\x07, その後のinputで\x00\x01\x02\x03\nを入力する必要があります。

Hintにはshellを使わずに実行したらいいよ、とのことなので、pythonのsubprocessなんかでやってみます。

import subprocess
from subprocess import Popen, PIPE

p = subprocess.Popen(["./inputter", " \n'\"\x07"], stdin = PIPE)
p.stdin.write("\x00\x01\x02\x03\n")

このコードを、shell server の ~/solve.py なんかに配置して、指定されたinputterのあるディレクトリに移動して実行します。

$ cd /problems/2020/inputter/
$ vi ~/solve.py
(上記のコードを貼り付け)
$ python ~/solve.py
You seem to know what you're doing.
actf{impr4ctic4l_pr0blems_c4ll_f0r_impr4ctic4l_s0lutions}

[Misc] msd

You thought Angstrom would have a stereotypical LSB challenge... You were wrong! To spice it up, we're now using the Most Significant Digit. Can you still power through it?

Here's the encoded image, and here's the original image, for the... well, you'll see.

Important: Redownload the image if you have one from before 11:30 AM 3/14/20. Important: Don't use Python 3.8, use an older version of Python 3!

Hint

Look at the difference between the original and what I created!

Also, which way does LSB work?

画像2枚breathe.jpg,output.pngと、下記のコードが配布されます。

from PIL import Image

im = Image.open('breathe.jpg')
im2 = Image.open("breathe.jpg")

width, height = im.size

flag = "REDACT"
flag = ''.join([str(ord(i)) for i in flag])


def encode(i, d):
    i = list(str(i))
    i[0] = d

    return int(''.join(i))
    

c = 0

for j in range(height):
    for i in range(width):
        data = []
        for a in im.getpixel((i,j)):
            data.append(encode(a, flag[c % len(flag)]))

            c+=1

        im.putpixel((i,j), tuple(data))
        
im.save("output.png")
pixels = im.load()

コードをよく見ると、元の画像のそれぞれのピクセル・輝度に対して、cをどんどん足していき、輝度の先頭桁をflag[c % len(flag)]に置換して新しい画像output.pngに入れていることがわかります。
例えば、元画像 breathe.jpg のとあるピクセルの値が (25, 143, 193) で、 flag[c % len(flag)]4, 0, 5 だった場合は、(45, 43, 593)となります。
きっとこれがLSBが〜とヒントでいってたやつのことですね。
で、最後の593は画像に変換する際に255に丸められてしまうので、実際output.png側の輝度255というのは信用できません。ただ、flagが何回分か出てくるくらいの長さがあるはずなので、flagの頭出しがわかれば、断片をつなぎ合わせるとちゃんとしたflagが出てきそう。

ということで試行錯誤の結果、わかる部分だけ記したflagの数字列を出力しました。

from PIL import Image

im = Image.open("breathe.jpg")
im2 = Image.open('output.png')

width, height = im.size

def decode(ordpixel, newpixel):
    o_i = list(str(ordpixel))
    n_i = list(str(newpixel))
    if str(newpixel) == '255':
        return "*"
    if len(o_i) == len(n_i):
        return n_i[0]
    else:
        return "0"
    
c = 0
flag_arry=[]

for j in range(height):
    for i in range(width):
        data = []
        ord_pixel = im.getpixel((i,j))
        new_pixel = im2.getpixel((i,j))
        for n in range(3):  # len(im.getpixel((i,j)))) = 3
            #print(ord_pixel[n], new_pixel[n])
            flag_arry.append(decode(ord_pixel[n], new_pixel[n]))
            c+=1

with open('flag.txt', 'w') as f:
    f.write(''.join(flag_arry))

flag.txtはこんな感じ。

76111114101109321051121151171093210011110811111432115105116329710910111644329911111011510199116101116117114329710010511210511599105110103321011081051164432115101100321001113210110511711510911110032116101109112111112101109910510010510011110111111210*971111110131011121001111011111410121099101102**10101111211210111010102100**1010110101021110111010102111110*11211011111111111100210112010111101111101111102111010101112101111110115211010511*10211111010111110*1121011202101211110101111001112**1111101110111111121110112111110121011111110121001111011111**210110211101112111011010111010010111101121011021111110111121111101211101101011**210111111012101010111021001111011111101210111**2102111010112110111010211211101111711426912099101112116101117114321151051101163211199999710199971163299117112105100971169711632110111110321121141111051001011101164432115117110116321051103299117108112973211311710532111102102105991059732100101111011111....

最初はこれ何語か全然わからなかった。google自動翻訳ではラテン語と出たけど。で、かなりflagは長くて、途中でflag formatが現れると予想。actf{を数字列に直した9799116102123で検索すると、こんな感じで引っかかりました。本当はもっとたくさんあった。

9799116102123105110104971081019510112010497108101951011221121224549505148579810*10*103121104****1*112*
979911610212310511010***10*101*510112010***10*101**101122112122454950514857*810*10*10*12110*****1211**
9799116102123105110104971081019*10112010497108101**1011*2112122*****0*1******10*10*10*12110*****12112*
979911610212310511010497108101951011201049710810195101122112122454950514857981051031031211049798121125
...

最後の行が運良く、不明な数字がないので、これを文字列に変換したらflagが出てきました!

flag_i = "979911610212310511010497108101951011201049710810195101122112122454950514857981051031031211049798121125"

flag = ''
ord_c = ''
for f in flag_i:
    ord_c += f
    if (40 <= int(ord_c)) and (int(ord_c) <= 125) or (int(ord_c) == 32):
        flag += chr(int(ord_c))
        ord_c = ''
    else:
        continue
print(flag)

実行結果

$ python test.py 
actf{inhale_exhale_ezpz-12309biggyhaby}

[Misc] Shifter

What a strange challenge...

It'll be no problem for you, of course!

nc misc.2020.chall.actf.co 20300

Hint

Do you really need to calculate all those numbers?

指定のホストにつないでみます。

$ nc misc.2020.chall.actf.co 20300
Solve 50 of these epic problems in a row to prove you are a master crypto man like Aplet123!
You'll be given a number n and also a plaintext p.
Caesar shift `p` with the nth Fibonacci number.
n < 50, p is completely uppercase and alphabetic, len(p) < 50
You have 60 seconds!
--------------------
Shift RZRTFLXAMJKZDPMRHKXVZEKKLMLMW by n=17
: 

フィボナッチ数列の n 番目の数ぶん、与えられた文字列をshiftしなさいとのこと。
n < 50 の条件があるので、予めフィボナッチ数列の配列を用意しておくと良さそう。
60秒あるので、別プログラムで計算してから答えをコピペしても間に合いそう。

…間に合わなかった。よく問題見たら50問もあるわ…。それは無理だ…。

ということで、自動化プログラムを書きました。フィボナッチ数列は、そのへんのプログラムを拝借してn=51まで出力しておきました。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

import string
from pwn import *

def shift(c, num):
        return alphabet[(alphabet.index(c) + num) % 26]

fib_arr = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049]

alphabet = list(string.ascii_uppercase)

host = 'misc.2020.chall.actf.co'
port = 20300
r = remote(host, port)
r.recvuntil(b'--------------------')

c = 0
while c < 50:
    q_line = (r.recvuntil(b': '))
    plain = q_line.split(b' ')[1].decode()
    n = int(q_line.split(b' ')[3].split(b'=')[1][:-2])
    print(plain, n)
    
    ans = ''
    for p in plain:
        ans += shift(p, fib_arr[n])
    print(ans)
    r.sendline(ans.encode())
    c += 1
print(r.recv())

実行結果

$ python solve.py 
[+] Opening connection to misc.2020.chall.actf.co on port 20300: Done
PYEVC 22
UDJAH
TBTLHXRDDVIEACEABPBKQ 9
BJBTPFZLLDQMIKMIJXJSY
...
(中略)
...
KUNLVUZTJJSPZZZAXWAYSXAAGX 33
CMFDNMRLBBKHRRRSPOSQKPSSYP
b'actf{h0p3_y0u_us3d_th3_f0rmu14-1985098}\n'
[*] Closed connection to misc.2020.chall.actf.co port 20300

[Misc] ws3

What the... record.pcapng

Hint

Did I send something? Or...

record.pcapngが配布されます。
wiresharkで開いてみると、gitに関する通信があるようです。

とりあえず、ws2の要領で、HTTP通信の部分をExportObjectsで抽出してみます。これらのファイルが得られました。

$ ls
git-receive-pack            record.pcapng
git-receive-pack(1)         refs%3fservice=git-receive-pack
git-receive-pack(2)         refs%3fservice=git-receive-pack(1)
git-receive-pack(3)         refs%3fservice=git-receive-pack(2)
git-upload-pack             refs%3fservice=git-receive-pack(3)
git-upload-pack(1)          refs%3fservice=git-upload-pack

これを直接読もうと思っても無理なので、復元の仕方を調べてみると、下記のwriteupが!

CTF日本語サイト : ASIS CTF Quals 2016

まんまこれじゃん!!!!!

ということで、git-receive-packgit-receive-pack(2)を、上記の記事に倣ってPACKの部分から切り出し、pack0, pack2というファイルに抽出しました。

これを

$ git init newrepo

で新しく作った空のgirリポジトリに、

$ git unpack-objects < pack0

のように、unpackしながら取り込んでいきます。
この状態で、gitの.git/objectsに5つコミット記録が復元されたので、各記録を下記のように見ていきます。

$ git cat-file -p 0f2f9c092a89bb896f573506300393327aeb8dd2
tree a1ec825e60819302795b5e33e92be08bfcd1885e
parent 34b1647544bdcf0e896e080ec84bb8b57cccc8d0
author josh <josh@josh.josh> 1584054469 -0400
committer josh <josh@josh.josh> 1584054469 -0400

whoops

おっと。この調子。

$ git cat-file -p 34b1647544bdcf0e896e080ec84bb8b57cccc8d0
tree 87872f28963e229e8271e0fab6a557a1e5fb5131
parent 129b99f3e90fe8faa5ed9b4e18bfb6c0cb5ce340
author JoshDaBosh <jwanggt@gmail.com> 1584054396 -0400
committer JoshDaBosh <jwanggt@gmail.com> 1584054396 -0400

suiper secret stuff

$ git cat-file -p 87872f28963e229e8271e0fab6a557a1e5fb5131
100644 blob c5fabdc7acdd48a2b49427d8536595028454973e    README.md
100644 blob fe3f47cbcb3ad8e946d0aad59259bdb1bc9e63f2    flag.jpg

$ git cat-file -p a1ec825e60819302795b5e33e92be08bfcd1885e
100644 blob c5fabdc7acdd48a2b49427d8536595028454973e    README.md

$ git cat-file -p fe3f47cbcb3ad8e946d0aad59259bdb1bc9e63f2
????JFIFHH??C  
...

最後のファイル確認時に、バイナリっぽい文字列がだーーーーーっと出た&JFIFが見えるので、これがflag.jpgっぽい。

$ git cat-file -p fe3f47cbcb3ad8e946d0aad59259bdb1bc9e63f2 > flag.jpg

すると、flag.jpgが復元されました!

f:id:kusuwada:20200319091104j:plain

[Misc] Noisy

My furrier friend tried to send me a morse code message about some new furs, but he was using a noisy connection. I think he repeated it a few times but I still can't tell what he said, could you figure out what he was trying to tell me? Here's the code he used.

(the flag is not in the actf{} format, it's all lowercase, 1 repetition only)

Hint

The code that was used to generate the transmission is included

受け取った雑音入りのモールス信号,4ea.txtと、Noisey.pyが配布されます。

0.6974801438468565
-0.8039053786072994
1.0650310069718993
2.0219860732919095
1.9217414701519497
3.103170460886686
0.8197932669788626
0.001776550205617955
-0.10800584702075877
-0.6065378974904347
...

594kbもある!巨大な信号だ…。そしてこれは、モールス信号…?ソースはこちら。

import numpy as np
from random import gauss
morse = REDACTED
repeats = REDACTED
pointed = []
for c in morse:
    if c == ".":
        pointed.extend([1 for x in range(10)])
    if c == "-":
        pointed.extend([1 for x in range(20)])
    if c == " ":
        pointed.extend([0 for x in range(20)])
    pointed.extend([0 for x in range(10)])

with open("points.txt", "w") as f:
    for _ in range(repeats):
        signal = pointed
        output = []
        for x, bit in enumerate(signal):
            output.append(bit + gauss(0,2))

        signal = list(np.array(output) - .5)
        f.write('\n'.join([str(x) for x in signal])+"\n")
f.close()

モールス信号をpointedに格納する際、.の場合は1を10回、-の場合は1を20回、の場合は0を20回のあと、0を10回、となっています。なので、ノイズを乗せる前に-.- ..という信号だと、

11111111110000000000
111111111111111111110000000000
11111111110000000000
000000000000000000000000000000
111111111111111111110000000000
111111111111111111110000000000

の並びになっているはず。その後、gauss(0,2)でノイズを付加して全体を0.5マイナスしている。何周か繰り返してやっているので、怪しいのは統計的に処理したらきれいになりそう。ただし繰り返しの回数・長さは不明。

gauss(0,2)は、平均0, 標準偏差2 なので、意外と振れ幅が大きい。結構繰り返し数が多くないと、ノイズ除去するのは難しそう。信号の数は28800。割り切れる長さのflagのはず。

まず、フラグの長さ(厳密には変換されたフラグの長さ)を予想するために、40くらいから2880くらいの中には収まると予想して、このレンジで28800で割り切れる長さを全部試してみます。
繰り返し長があっている場合は、平均を取るとノイズが軽減されて0,1の信号に近くなるはずなので、どれだけ0,1に近づいたかを簡単に評価します(evaluate関数)。
この評価値を出力し、0に近いフラグ長が正解と考えます。

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

def evaluate(signals):
    total_diff = 0
    for i in signals:
        if i >= 0.5:
            diff = abs(i-1)
        else:
            diff = abs(i-0)
        total_diff += diff
    return total_diff / len(signals)

with open('4ea.txt', 'r') as f:
    signals = f.readlines()

print(len(signals))  # 28800

for i in range(len(signals)):
    signals[i] = float(signals[i]) + 0.5

for flag_len in range(40, 2880):
    if len(signals) % flag_len != 0:
        continue
    repeat = len(signals) // flag_len    
    flag_candidate = [0] * flag_len
    for r in range(repeat):
        for i in range(flag_len):
            flag_candidate[i] += signals[r*flag_len + i]
    for i in range(flag_len):
        flag_candidate[i] /= repeat
    ave_diff = evaluate(flag_candidate)
    print(flag_len, ave_diff)

※出題時に0.5引いてあるのは、singal > 0で評価するのに丁度よく、わざわざ元に戻す必要はないとわかっていつつも、頭の中と視覚的に0,1で考えたかったので、最初に信号全部に対して0.5プラスしています…。ご容赦ください…。

実行結果

$ python solve.py 
28800
40 0.4104876286206416
45 0.4134565307191044
48 0.3916532564536454
50 0.41313195925321944
60 0.4078641854026849
64 0.3829775055047512
72 0.3969147506917181
75 0.410977419423746
80 0.40085149427370104
90 0.41028466927053825
96 0.34603583979001296
100 0.4104073125542726
120 0.35323868611443815
128 0.3789130205669667
144 0.3827563971234046
150 0.4002134986892289
160 0.3510136535892008
180 0.39956576557955725
192 0.2784617845816631
200 0.39614715991444105
225 0.39423669617421286
240 0.30898961919188656
288 0.33673376631518825
300 0.3834139208944319
320 0.2786253987990396
360 0.34107497244503987
384 0.27426653847545074
400 0.3714041300537022
450 0.37285946333054615
480 0.2531210192057826
576 0.26925955366683235
600 0.3251662470094454
640 0.2768570561624624
720 0.2924544085831742
800 0.3166453600358657
900 0.3414780433047847
960 0.14464269887759018
1152 0.2628264393062986
1200 0.2821841222980208
1440 0.2567624222455556
1600 0.2611858303228836
1800 0.2867816956422254
1920 0.2004293839366065
2400 0.2575786423653436

ここで、フラグ長が長いほど、評価値が低くなる傾向があることに注意です。この中で、いきなりがくんと評価値が下がっているのが960です。フラグ長を960と仮定して、信号を復元してみます。

# ※上のスクリプトの続き
flag_len = 960
repeat = len(signals) // flag_len    
flag_candidate = [0] * flag_len
for r in range(repeat):
    for i in range(flag_len):
        flag_candidate[i] += signals[r*flag_len + i]
for i in range(flag_len):
    flag_candidate[i] /= repeat

bin_arr = []
for i in flag_candidate:
    if i >= 0.5:
        bin_arr.append('1')
    else:
        bin_arr.append('0')
print(''.join(bin_arr))

実行結果

111111111100000000001111111111111111111100000000000000000000000000000000000000001111111111111111111100000000001111111111000000000000000000000000000000000000000011111111111111111111000000000011111111111111111111000000000011111111111111111111000000000000000000000000000000000000000011111111110000000000111111111100000000000000000000000000000000000000001111111111000000000011111111110000000000111101111100000000000000000000000000000000000000001111011111111111111100000000001111111111000000000011111111111111111111000000000011111111111111111111000000000000000000000000000000000000000011111111111111111111000000000011111111110000000000000000000000000000000000000000111111111111111111110000000000111111111111111111110000000000111111111111111111110000000000000000000000000000000000000000111111111100000000001111111111000000000000000000000000000000000000000011111111110000000000111111111100000000001111111111000000000000000000000000000000000000000011111111110000000000

おっ!これは正解の予感!誤りがいくつかありそうですが、想定される並びに近いです(1,0が10個単位で並ぶ)

#!/usr/bin/env python3
# -*- coding:utf-8 -*-

bin_signal = """
11111111110000000000
111111111111111111110000000000
000000000000000000000000000000
111111111111111111110000000000
11111111110000000000
000000000000000000000000000000
111111111111111111110000000000
111111111111111111110000000000
111111111111111111110000000000
000000000000000000000000000000
11111111110000000000
11111111110000000000
000000000000000000000000000000
11111111110000000000
11111111110000000000
11111111110000000000
000000000000000000000000000000
111111111111111111110000000000
11111111110000000000
111111111111111111110000000000
111111111111111111110000000000
000000000000000000000000000000
111111111111111111110000000000
11111111110000000000
000000000000000000000000000000
111111111111111111110000000000
111111111111111111110000000000
111111111111111111110000000000
000000000000000000000000000000
11111111110000000000
11111111110000000000
000000000000000000000000000000
11111111110000000000
11111111110000000000
11111111110000000000
000000000000000000000000000000
11111111110000000000
"""

bin_arr = bin_signal.split('\n')

morse = ''
for b in bin_arr:
    if b == '11111111110000000000':
        morse += '.'
    elif b == '111111111111111111110000000000':
        morse += '-'
    elif b == '000000000000000000000000000000':
        morse += ' '
print(morse)

実行結果

$ python solve.py 
.- -. --- .. ... -.-- -. --- .. ... .

これをmorse codeで復号すると、anoisynoise、これがflagでした ٩(๑❛ᴗ❛๑)尸

[Misc] Survey

Thanks for playing this CTF! Please fill out our survey so that we can improve ångstromCTF next year. This challenge does not affect time-based tiebreakers.

用意されたアンケートフォームに飛び、アンケートにすべて答えるとflagがもらえます。お決まりのやつ。

f:id:kusuwada:20200319091133p:plain