アオカケスの鳥かご

日々の出来事を綴っていきたい

angstromCTF 2020 writeup

2020年 3月 14日 09:00~2020年 3月 19日 09:00に開催されたangstromCTF 2020に1人で参加しました。

結果は今サーバーにアクセスできないので分かりませんが、最後に見たときは300位以内には入っていたぐらいだったような気がします。


簡単な問題しか解けていませんが、誰かの参考になれば幸いです。

Misc

Sanity Check

discordに書いてあるFlagを入力するだけ...のはずなのにFlagを見つけるのに無駄に苦労しました。

ずっと #general なんて無いじゃん💢とか思っていましたが、ちゃんと書いてありました。
f:id:aokakes:20200314191758p:plain

旗のやつを押さないと #general にアクセス出来ないようになっていました。
f:id:aokakes:20200314191905p:plain


ws1よりも解けた人が少ないのを見ると、結構ここで沼っている人が多かったのではないでしょうか。


Flag:actf{never_gonna_let_you_down}

ws1

pcapファイルなのでとりあえずwiresharkで開いてみます。

普通にHTTP通信をしているだけでした。
f:id:aokakes:20200314155421p:plain

すでにFlagが見えていますが、HTTPのところで 右クリック>追跡>HTTPストリーム で見やすくなります。
f:id:aokakes:20200314155608p:plain

Flag:actf{wireshark_isn't_so_bad_huh-a9d8g99ikdf}

clam clam clam

とりあえず指定された場所に接続してみると、clamとmalcを叫び続けているだけでした。
ヒントにある改行からエンターキーを連打してみてもとくに何もありません。
f:id:aokakes:20200314163507p:plain

埒が明かないので、pythonで取得内容を確認してみると、calmとmalcに紛れて以下のようなものがありました。

type "clamclam" for salvation\r

google先生に翻訳してもらいます。

救いのために「clamclam」と入力します

再度接続してcalmとmalcが流れている中、ダメ元で clamclam と入力してエンターしてみると、Flagが表示されました。
f:id:aokakes:20200314163516p:plain

Flag:actf{cl4m_is_my_f4v0rite_ctfer_in_th3_w0rld}

ws2

またpcapファイルだったのでwiresharkで開いてみると、どうやらflag.jpgをアップロードしていることが分かりました。
f:id:aokakes:20200314171441p:plain

これをエクスポートします。
f:id:aokakes:20200314171531p:plain

しかし、これをそのまま開こうとしても壊れているようで開くことが出来ません。
f:id:aokakes:20200314171817p:plain

こんなときはforemostの出番です。
f:id:aokakes:20200314171700p:plain

ワンパンマンとともにFlagが出てきました。
f:id:aokakes:20200314171725j:plain


Flag:actf{ok_to_b0r0s-4809813}

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();
}

引数と標準入力が一致すればFlagを出してくれるようです。

まずは第一関門。

 \n'\"\x07

普通に手打ちでやると \x07 すなわち警告音を作りだすことが出来ません。

そこでechoで同じものを作ります。

echo -e -n " \n'\x22\x07"

まずはこれで第一関門はクリア。

./inputter "`echo -e -n " \n'\x22\x07"`"


第二関門。

\x00\x01\x02\x03\n

これもechoで作って、今度はパイプで渡します。

echo -e "\x00\x01\x02\x03\n" | ./a.out "`echo -e -n " \n'\x22\x07"`"


当然自分の環境にFlagは無いので、angstromのshellで同じように実行すればFlagが手に入ります。
f:id:aokakes:20200316215239p:plain


Flag:actf{impr4ctic4l_pr0blems_c4ll_f0r_impr4ctic4l_s0lutions}

Shifter

50問解くだけです。

まずは普通にncで接続してどのような問題か確認します。
f:id:aokakes:20200315172329p:plain

50未満の整数nと平文pが与えられ、pをnのフィボナッチ数列で暗号化したものを答えればよいようです。

たまに与えられる平文が0文字のときがあるっぽいので、そこも考慮しなくてはならないようです。
運次第では対策してなくてもFlag取れると思いますけど。
f:id:aokakes:20200315172554p:plain

Flag:actf{h0p3_y0u_us3d_th3_f0rmu14-1985098}

Web

The Magic Word

真ん中にデカデカとgive flagと書かれたページです。
f:id:aokakes:20200314172541p:plain

とりあえずソースを確認してみると、どうやらmagicのところがplease give flagだとFlagがもらえるようです。
f:id:aokakes:20200314172757p:plain

デベロッパーツールでplease give flag に書き換えます。
f:id:aokakes:20200314172924p:plain

Flagが表示されました。
f:id:aokakes:20200314172948p:plain


Flag:actf{1nsp3c7_3l3m3nt_is_y0ur_b3st_fri3nd}

Consolation

リンク先にはボタンと$がありました。
f:id:aokakes:20200316185219p:plain

ボタンを押すたびに$25ずつ増えていきます。よく分からない。

ソースを確認してみると、iftenmillionfireflies.jsというファイルを読み込んでいて、ボタンを押すたびにこの中の関数を呼び出しているようです。

iftenmillionfireflies.jsを覗いてみると難読化されていたので、適当なサイトで難読化解除してみましたが、それでもよく分かりませんでした。
https://beautifier.io/


最初にBase64らしき文字列がたくさん並んでいたので、何かありそうな気がしましたが、デコードしてみてもそれらしい情報は得られません。

全然手掛かりが得られないので半ば諦めていたのですが、冷静に考えると点数や解いたチームの数から察するに、難しいことを要求されているわけではないはずなんですよね。

そこで、Chromeデベロッパーツールを使って処理を1つ1つ追ってみました。


まずは13行目にブレークポイントを設定します。
f:id:aokakes:20200316190006p:plain

この状態で「pay me some money」のボタンをクリックするとデバッグが始まります。
f:id:aokakes:20200316190137p:plain

処理を1つずつ進めていきます。赤丸のところをクリックするか、F11を連打します。
f:id:aokakes:20200316190237p:plain

何も考えずに連打すると大事な情報を見逃してしまうので、なんだか大事そうな青枠のところを確認しながら適度な速さで連打します。
f:id:aokakes:20200316190509p:plain

しかし残念ながらFlagは現れません。


なんだよ~と思いつつ、デバッグする前に何度かクリックしてから再度チャレンジしてみたら、なんと出ました!!
f:id:aokakes:20200316190823p:plain

Flag:actf{you_would_n0t_beli3ve_your_eyes}

Git Good

普通にURLにアクセスしてみるとHello, worldになるだけです。
f:id:aokakes:20200314212252p:plain

ところがURLの末尾にflagと入れてみると変わりました。ここから色々取ってこれそうです。
f:id:aokakes:20200314212331p:plain

問題のタイトルや問題文にもあるようにgitが関連しているようなので、.gitディレクトリがあると推測します。
コミット履歴などが含まれている.git/logs/HEADをダウンロードするために「https://gitgood.2020.chall.actf.co/.git/logs/HEAD」にアクセスしてみると、以下のようなファイルを取ってくることができました。

0000000000000000000000000000000000000000 6b3c94c0b90a897f246f0f32dec3f5fd3e40abb5 aplet123 <jasonqan2004@gmail.com> 1583598444 +0000	commit (initial): haha I lied this is the actual initial commit
6b3c94c0b90a897f246f0f32dec3f5fd3e40abb5 e975d678f209da09fff763cd297a6ed8dd77bb35 aplet123 <jasonqan2004@gmail.com> 1583598464 +0000	commit: Initial commit

e975d678f209da09fff763cd297a6ed8dd77bb35は現行なので、6b3c94c0b90a897f246f0f32dec3f5fd3e40abb5が怪しそうです。

BSides San Francisco CTF の write-up - st98 の日記帳を参考にしつつ、掘り下げていってみます。

https://gitgood.2020.chall.actf.co/.git/objects/6b/3c94c0b90a897f246f0f32dec3f5fd3e40abb5からダウンロード

3c94c0b90a897f246f0f32dec3f5fd3e40abb5をデコンパイル

import zlib
f=open('3c94c0b90a897f246f0f32dec3f5fd3e40abb5',mode='rb')
flag = zlib.decompress(f.read())

print(flag)

commit 210\x00tree b630430d9d393a6b143af2839fd24ac2118dba79\nauthor aplet123 <noneof@your.business> 1583598444 +0000\ncommitter aplet123 <jasonqan2004@gmail.com> 1583598444 +0000\n\nhaha I lied this is the actual initial commit\n

https://gitgood.2020.chall.actf.co/.git/objects/b6/30430d9d393a6b143af2839fd24ac2118dba79

30430d9d393a6b143af2839fd24ac2118dba79をデコンパイル

tree 242\x00100644 .gitignore\x00\xc2e\x8d}\x1b1\x84\x8c;q\x96\x05C\xcb\x03h\xe5l\xd4\xc7100644 index.html\x00c\x88\x87\xa5Is&\\B\x8c\xd5\x1c\xe6\xdf\xd4\x8f\x19m\x91\xc4100644 index.js\x00I\xb3\x19\xc3}\xc6t\xbc\xa6\x82\xca\xb0\xf2Pds\xdd\xa6\xbd\x9a100644 package-lock.json\x00x\x9f\xa5\xca\xf4R\xf5\xf6\xf2[\xfa\x9b\x1c\n\xb1\xd5\x93\xdc\xe1\xb3100644 package.json\x00\x8f\x08\xaf5 ]\x0b\xa8\x0e\x94\xb4\xf40c\x11\x03\x9db\xe18100644 thisistheflag.txt\x00\x0fRY\x80\x06\xf9\xcd\xb2\x1d\xb2\xf4\xc8\xd4MpSV0(\x9b

thisistheflag.txtというファイルがあるようです。

意気揚々とアクセスしてみると、イラッとさせられます。
f:id:aokakes:20200314213425p:plain


ここで方針を変えることにしました。

新たに参考になりそうなページを発見しました。
BSides San Francisco CTF 2017 - Write-ups | Rawsec

gitdumperという便利なツールがあるようです。早速使ってみます。

なんだかいっぱいダウンロードできました!!
f:id:aokakes:20200314213629p:plain


先程のページに従って色々試してみると、急にFlagが現れました。
f:id:aokakes:20200314213806p:plain


仕組みがよく分かりません...

Flag:actf{b3_car3ful_wh4t_y0u_s3rve_wi7h}

Secret Agents

ログインページへのリンクボタンがあります。
f:id:aokakes:20200315034546p:plain

当然ながらloginに飛んでもFlagは表示されません。
f:id:aokakes:20200315034629p:plain

問題文のリンクからソースを確認します。
なんだかSQLインジェクションできそうな部分があります。
f:id:aokakes:20200315034722p:plain

どうやらUser-Agentの値をそのままSQL文に突っ込んでいるだけのようなので、デベロッパーツールで値を変えながら色々試してみます。

a' OR 1 = 1;
f:id:aokakes:20200315034959p:plain

UAには複数の値が存在すると思われるので、それを考慮するとSQLインジェクションは成立しているようです。

それではUAにはどのようなカラムが存在するのか見ていきます。
しかし、普通に information_schema.columns とかをUNIONしても、最後のif文のところで2行以上になると弾かれてしまいます。

なのでLIMITを使って1つずつ地道に特定していきます。
正直手作業でやったほうが早いような気はしましたが、pythonで自動化してみました。

以下のカラムがありました。

AAAA
shrek V
123ujklaguhjnkadsuop98uij2k3l4mlgjaisdopgjk;mlkl;k234lkjhasd89f709iok23jkdf keyboard bash op
neeeeeeeeeeeeeeeeeeeeeeeeum
I WANT THAT KRABBY PATTY
bzzzzzzztaaaa
bzzz&#39;;lhjkdjsjkldfgzzzztaaaa
hiiiii whatis our aaa
nice try but this is harder :p

なんとなく一番複雑な123ujklaguhjnkadsuop98uij2k3l4mlgjaisdopgjk;mlkl;k234lkjhasd89f709iok23jkdf keyboard bash opが怪しそうなので突っ込んでみたらFlagが表示されました。
f:id:aokakes:20200315035755p:plain


Flag:actf{nyoom_1_4m_sp33d}

Crypto

Keysar

暗号化されたFlagと鍵が与えられます。

ANGSTROMCTF
agqr{yue_stdcgciup_padas}

パッと見でシーザー暗号かな~と思ってやってみましたが、いくらずらしてもFlagらしいものは出てきません。

これは色々試した後の話なのですが、シーザー暗号に鍵を使うことが出来たんですね。知りませんでした。


無駄にヴィジュネル暗号じゃないかとか考えて、結局全然出来なくてもはや諦めかけていました。


最終的にはこちらのサイトでいけました。
https://www.geocachingtoolbox.com/index.php?lang=en&page=caesarCipher

Rotateの値を0にしてalphabet keyに先程の鍵を入力したらFlagが出てきます。
f:id:aokakes:20200315191837p:plain


Flag:actf{yum_delicious_salad}

Reasonably Strong Algorithm

名前から察するにRSA暗号でしょうか。
リンク先にはやはりn、e、cの値がありました。

n = 126390312099294739294606157407778835887
e = 65537
c = 13612260682947644362892911986815626931

とりあえずfactordb.comに突っ込んでpとqの値を特定します。

p = 9336949138571181619
q = 13536574980062068373

あとはいい感じにプログラムを書くだけです。


Flag:actf{10minutes}

Wacko Images

暗号化された画像と暗号化用のプログラムが与えられます。
f:id:aokakes:20200315195316p:plain

from numpy import *
from PIL import Image

flag = Image.open(r"flag.png")
img = array(flag)

key = [41, 37, 23]

a, b, c = img.shape

for x in range (0, a):
	for y in range (0, b):
		pixel = img[x, y]
		for i in range(0,3):
			pixel[i] = pixel[i] * key[i] % 251
		img[x][y] = pixel

enc = Image.fromarray(img)
enc.save('enc.png')

大事なところは15行目だけです。

逆のことをすればいいだけですが、私はちょっと除算の逆の処理というものを存じ上げないので、頭悪く総当りすることにします。

総当りといっても最大で500*200*3*255=76,500,000回しかありません。十分すぐ終わる範囲です。

正直あまり自信はありませんでしたが、ちゃんと出来ていました。
f:id:aokakes:20200315195807p:plain


Flag:actf{m0dd1ng_sk1llz}

Binary

No Canary

実行ファイルとソースがもらえます。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void flag() {
	system("/bin/cat flag.txt");
}

int main() {
	setvbuf(stdin, NULL, _IONBF, 0);
	setvbuf(stdout, NULL, _IONBF, 0);
	gid_t gid = getegid();
	setresgid(gid, gid, gid);

	puts("Ahhhh, what a beautiful morning on the farm!\n");
	puts("       _.-^-._    .--.");
	puts("    .-'   _   '-. |__|");
	puts("   /     |_|     \\|  |");
	puts("  /               \\  |");
	puts(" /|     _____     |\\ |");
	puts("  |    |==|==|    |  |");
	puts("  |    |--|--|    |  |");
	puts("  |    |==|==|    |  |");
	puts("^^^^^^^^^^^^^^^^^^^^^^^^\n");
	puts("Wait, what? It's already noon!");
	puts("Why didn't my canary wake me up?");
	puts("Well, sorry if I kept you waiting.");
	printf("What's your name? ");

	char name[20];
	gets(name);

	printf("Nice to meet you, %s!\n", name);
}

とりあえずIDAで開いてflag関数のアドレスを調べます。
f:id:aokakes:20200316233114p:plain

アドレスは0x00401186のようです。

そしてなんかよく分からないですが、適当に埋めるbufferは20+8で0x28とします。


あとはプログラムに落とし込みます。

実行したら見事Flagが取れました。
f:id:aokakes:20200316233548p:plain


Flag:actf{that_gosh_darn_canary_got_me_pwned!}

Rev

Revving Up

とりあえず指定のディレクトリまで移動し、revving_upを実行してみます。
「give flag」を入力しろと言われるので素直に入力してみると怒られました。
f:id:aokakes:20200315011143p:plain

コマンドライン引数に「banana」を入力すればよいみたいです。
f:id:aokakes:20200315011311p:plain

Flag:actf{g3tting_4_h4ng_0f_l1nux_4nd_b4sh}

Windows of Opportunity

とりあえずIDAで開いてみたらなんか普通にFlagが書いてありました。
f:id:aokakes:20200315011658p:plain

Flag:actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}