pomoの掃きだめ

色々な感情をとりあえず文字に起こす場所. Twitter→@mhskmq

InterKosenCTF Writeup

f:id:pomo_mhskmq:20190813110847p:plainInterKosenCTFにチームterpomoとして参加しました.一人チームです. 結果は36位でした. ぼちぼちですね

Kurukuru Shuffle

フラグをクルクル~っと混ぜたものと,まぜまぜするためのコードが渡されます.

from secret import flag
from random import randrange


def is_prime(N):
    if N % 2 == 0:
        return False
    i = 3
    while i * i < N:
        if N % i == 0:
            return False
        i += 2
    return True


L = len(flag)
assert is_prime(L)

encrypted = list(flag)
k = randrange(1, L)
while True:
    a = randrange(0, L)
    b = randrange(0, L)

    if a != b:
        break

i = k
for _ in range(L):
    s = (i + a) % L
    t = (i + b) % L
    encrypted[s], encrypted[t] = encrypted[t], encrypted[s]
    i = (i + k) % L

encrypted = "".join(encrypted)
print(encrypted)

暗号化のための鍵として3つのランダムな値が使われていますが,大きな数字ではないので愚直に全探索して,フラグっぽい文字列を探してあげます.

KosenCTF{us4m1m1_m4sk_s3np41_1s_r34lly_cut3_38769915}

Hugtto!

フラグを埋め込んだ画像ファイル(steg_emiru.png)と埋め込みに使ったコードが渡されます.

from PIL import Image
from secret import flag
from datetime import datetime
import tarfile
import sys

import random

random.seed(int(datetime.now().timestamp()))

bin_flag = []
for c in flag:
    for i in range(8):
        bin_flag.append((ord(c) >> i) & 1)

img = Image.open("./emiru.png")
new_img = Image.new("RGB", img.size)

w, h = img.size

i = 0
for x in range(w):
    for y in range(h):
        r, g, b = img.getpixel((x, y))
        rnd = random.randint(0, 2)
        if rnd == 0:
            r = (r & 0xFE) | bin_flag[i % len(bin_flag)]
            new_img.putpixel((x, y), (r, g, b))
        elif rnd == 1:
            g = (g & 0xFE) | bin_flag[i % len(bin_flag)]
            new_img.putpixel((x, y), (r, g, b))
        elif rnd == 2:
            b = (b & 0xFE) | bin_flag[i % len(bin_flag)]
            new_img.putpixel((x, y), (r, g, b))
        i += 1

new_img.save("./steg_emiru.png")
with tarfile.open("stegano.tar.gz", "w:gz") as tar:
    tar.add("./steg_emiru.png")
    tar.add(sys.argv[0])

どうやらフラグを1bitずつ画像のr,g,bの最終1bitのどれかに入れているらしいです. フラグの形式が分かっているのでフラグ長を調べて~と思いましたが,この問題のジャンルはforensicsなのです!(なのです!) フラグの1bitがrgbのどれに入るかは乱数によって決定されており,乱数のseedにはdatetime.now()が使われています. ということで,exiftoolでsteg_emiru.pngの最終編集時間を調べます.

File Modification Date/Time     : 2019:08:06 11:44:18+09:00

この時刻をseedに使えば解けると思いましたが解けなかったので結局この付近の時刻を全探索して解きました. 探すべき時間がこれではないのか,そもそも想定解ではないのかはルールーに聞いてほしいのです

import sys
from PIL import Image
import datetime
import random

bin_flag =0
bin_r =""
bin_g =""
bin_b =""

img = Image.open("./steg_emiru.png")
new_img = Image.new("RGB", img.size)

w, h = img.size


i=0
bin_flag=0
rev_flag=0
b_flag=0
st=[]
for d in range (1,7):
    for ho in range (24):
        for m in range (60):
            for s in range (60):
                print(d,ho,m,s)
                bin_r=""
                i=0
                bin_flag=0
                rev_flag=0
                b_flag=0
                dt_jst = datetime.datetime(2019,8, d,ho, m, s, 0,tzinfo=datetime.timezone(datetime.timedelta(hours=9)))
                random.seed(int(dt_jst.timestamp()))
                for x in range(w):
                    for y in range(h):
                        rnd = random.randint(0, 2)
                        r, g, b = img.getpixel((x, y))
                        rb=(r & 1)
                        gb=(g & 1)
                        bb=(b & 1)
                        if (rnd==0):
                            bin_flag=bin_flag<<1 | rb
                        if (rnd==1):
                            bin_flag=bin_flag<<1 | gb
                        if (rnd==2):
                            bin_flag=bin_flag<<1 | bb
                        if (i%8==7 and i!=0):
                            for z in range (8):
                                rev_flag=((rev_flag<<1) |((bin_flag>>z) & 1))
                            bin_r+=(chr(rev_flag))
                            bin_flag=0
                            rev_flag=0
                            if (i==7 and bin_r[0]!="K"):
                                b_flag=1
                                break
                            if (i == 15 and bin_r[1]!="o"):
                                b_flag=1
                                break
                            if (i == 23 and bin_r[2]!="s"):
                                b_flag=1
                                break
                            elif i==23:
                                print ("Perfect!!!!!!!!!!!!!!!!!!")
                        i += 1
                        if (i==800):
                            b_flag=1
                            break
                    if (b_flag==1):
                        b_flag=0
                        break

                if (bin_r[0]=="K" and bin_r[1]=="o" and bin_r[2]=="s" and bin_r[3]=="e"):
                    st.append(bin_r)
                    a1,a2,a3,a4=d,ho,m,s

print(st)
print(d,ho,m,s)

KosenCTF{Her_name_is_EMIRU_AISAKI_who_is_appeared_in_Hugtto!PreCure}

ちなみに私のアイコンもえみるちゃんです.愛らしいですね. 私はキュアマジカル/十六夜リコが好きです(イケメンなので)

basic crackme

ELFが渡されます. 引数に入っている文字列がフラグと前方一致しているとYES!と返してくれます(途中で途切れているフラグでも可). ちゃんとassemblyを読めと声が聞こえた気がしたのでIDAを使って読んでみます. まとめると,引数の文字のn文字目を左4bitシフトしたものと右4bitシフトしたものをorして(n-1)を足してあげます. これがマジックナンバーと一致すればOKということみたいです. 挙動をpythonで再現し,全探索してあげたものがこちらになります.

import sys

li    =[0xb4,0xf7,0x39,0x59,0xea,0x39,0x4B,0x6B,0xbf,0x80,0x3d,0xd1,0x42,0x10,0xe4,0x42,0x105,0x58,0x15,0x108,0xab,0x18,0xe8,0xcd,0x1b,0xeb,0x51,0x1e,0x111,0x44,0x51,0x86,0x53,0x48,0x59,0x36,0x10A,0x9b,0xfd]
flag=[0xb4,0xf7,0x39,0x59,0xea,0x39,0x4B,0x6B,0xbf,0x80,0x3d,0xd1,0x42,0x10,0xe4,0x42,0x105,0x58,0x15,0x108,0xab,0x18,0xe8,0xcd,0x1b,0xeb,0x51,0x1e,0x111,0x44,0x51,0x86,0x53,0x48,0x59,0x36,0x10A,0x9b,0xfd]

for i in range(len(li)):
    for j in range(3,125):
        k=(j<<4)&0xFF
        l=(j>>4)
        m=k|l
        m+=i
        if (m==(li[i])):
            flag[i]=(chr(j))
            break
            
print(flag)

KosenCTF{w3lc0m3_t0_y0-k0-s0_r3v3rs1ng}

lost world

Can you restore my root user? I don't remember the password. Try running dmesg | grep KosenCTF after logging in.

パスワードを忘れたらしいので再設定します. うっかり者の先人たちの知恵を借りながらやりました.

起動したらSHIFTキー連打でRecovery modeに入ります. eキーを押してシングルユーザーモードにしてなんやかんやでパスワードを再設定できればOKです. rootでログインできたら問題文にあるとおりdmesg | grep KosenCTFを実行するとフラグが現われます. 何を参考にして解いたのか記憶がないのでざっくりになってしまいました. 反省します.

KosenCTF{u_c4n_r3s3t_r00t_p4ssw0rd_1n_VM}

感想

初心者向けと聞かされてほいほいと参加してしまいましたが,普通に難しい問題が多くて苦戦しました. 勉強になる問題がたくさんあったので復習させていただきたいと思います. 最後に,運営の皆様お疲れさまでした.

ISITDTU CTF 2019 Quals Writeup

f:id:pomo_mhskmq:20190701002155p:plain

6月29日から6月30にかけて行われたISTDTU CTF 2019 Qualsにチームzer0ptsとして参加しました. チームは7655点を獲得して10位でした. 私は2つの問題を解いて404点入れました.

Programing

balls

There are 12 balls, all of equal size, but only 11 are of equal weight, one fake ball is either lighter or heavier. Can you find the fake ball by using a balance scale only 3 times?
nc 34.68.81.63 6666

12個あるボールの中から1つだけ重さの違うボールを天秤を3回使って見つけてねという問題 似た問題がないかグーグル先生に聞いてみると見つかりました.

www.moxbit.com

これをそのまま実装すればよさそうなのでコードを書きます

# -*- coding: utf-8 -*-

from pwn import *



host, port = "34.68.81.63", 6666

context.clear()

context.log_level='error'

cn= remote(host,port)

for i in range (50):

    cn.clean()

    print cn.recvuntil("Weighting 1:")

    cn.sendline ("1,2,3,4 5,6,7,8")

    ans=cn.recvline()

    print ans

    if (ans.find("Both are equally heavy")!=-1):

        cn.recvuntil("Weighting 2:")

        cn.sendline("1,2 9,10")

        ans=cn.recvline()

        print ans

        if (ans.find("Both are equally heavy")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("1 11")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("12")

                print("12")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("11")

                print("11")

                continue

        else:

            cn.recvuntil("Weighting 3:")

            cn.sendline("1 9")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("10")

                print("10")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("9")

                print("9")

                continue

    if (ans.find("The left is heavier than the right")!=-1):

        cn.recvuntil("Weighting 2:")

        cn.sendline("1,2,5 3,4,6")

        ans=cn.recvline()

        print ans

        if (ans.find("Both are equally heavy")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("1 7")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("8")

                print("8")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("7")

                print("7")

                continue

        if(ans.find("The left is heavier than the right")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("1 2")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("6")

                print("6")

                continue

            if (ans.find("The left is heavier than the right")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("1")

                print("1")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("2")

                print("2")

                continue

        if (ans.find("The left is lighter than the right")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("3 4")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("5")

                print("5")

                continue  

            if (ans.find("The left is heavier than the right")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("3")

                print("3")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("4")

                print("4")

                continue

        
    if (ans.find("The left is lighter than the right")!=-1):

        cn.recvuntil("Weighting 2:")

        cn.sendline("1,2,5 3,4,6")

        ans=cn.recvline()

        print ans

        if (ans.find("Both are equally heavy")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("10 7")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("8")

                print("8")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("7")

                print("7")

                continue

        
        if(ans.find("The left is lighter than the right")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("1 2")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("6")

                print("6")

                continue

            if (ans.find("The left is lighter than the right")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("1")

                print("1")

                continue

            else:

                cn.recvuntil("The fake ball is :")

                cn.sendline("2")

                print("2")

                print cn.recvline()

                print cn.recvline()

                continue
        

        if (ans.find("The left is heavier than the right")!=-1):

            cn.recvuntil("Weighting 3:")

            cn.sendline("3 4")

            ans=cn.recvline()

            print ans

            if (ans.find("Both are equally heavy")!=-1):

                cn.recvuntil("The fake ball is :")

                cn.sendline("5")

                print("5")

                continue

            else:

                if (ans.find("The left is lighter than the right")!=-1):

                    cn.recvuntil("The fake ball is :")

                    cn.sendline("3")

                    print("3")

                    print cn.recvline()

                    print cn.recvline()

                    continue

                else:

                    cn.recvuntil("The fake ball is :")

                    cn.sendline("4")

                    print("4")

                    continue


print cn.recvrepeat()

cn.close()

ISITDTU{y0u_hav3_200iq!!!!}

コードの汚さは点数に反映されないので無視します

Cryptography

Chaos

Could you help me solve this case? I have a tool but do not understand how it works. nc 104.154.120.223 8085

とりあえずサーバーにつないでみます

$ nc 104.154.120.223 8085
Your cipher key: Here is your cipher: 99/yy/II/LL 66/mm/%%/oo 22/cc/%%/pp 33/qq/TT/!!/## 11/zz/VV/PP 88/ll/&&/rr 33/nn/**/ww 44/rr/@@/gg 33/ll/??/mm 55/cc/PP/))/&& 33/ee/RR/VV 77/vv/VV/FF 77/dd/GG/__/,, 55/00/qq 99/ll/**/ee 33/qq/||/aa 22/ll/BB/$$/&& 11/ee/II/VV 88/hh/BB/,,/== 22/xx/EE/UU 99/qq/YY/**/,, 88/mm/&&/ww 33/ii/LL/$$/&& 99/xx/++/hh 11/11/zz 44/ee/##/bb 99/mm/YY/~~/>> 44/uu/KK/==/__ 99/hh/VV/II 11/zz/KK/>>/^^ 77/vv/TT/@@/++ 00/tt/SS/--/@@ 88/ii/LL/<</`` 33/ff/VV/VV 77/vv/``/yy 55/mm/VV/UU 44/uu/~~/oo 55/33/ee 88/gg/))/dd 99/ee/GG/EE 55/oo/JJ/**/?? 22/nn/__/gg 66/ff/,,/ii 22/ll/PP/YY 88/bb/QQ/TT 55/kk/VV/../++ 22/dd/KK/))/)) 44/gg/++/qq 00/bb/``/ww 33/zz/VV/++/~~ 44/ll/!!/ii 11/aa/VV/YY 77/88/qq 11/66/ll 77/oo/,,/qq 55/77/vv 88/ss/LL/UU 99/tt/HH/~~/%% 44/oo/../rr 99/vv/DD/--/!! 22/ss/--/bb 00/77/cc 11/yy/YY/||/== 11/kk/``/ff
WELCOME TO CHAOS TOOL: 
Description: This is a tool which helps you hide the content of the message
Notes:
- Message cannot contain whitespace characters
- Message can use all characters including punctuation marks and number
- Decrypt the above key to get the flag, len(key) = 64
- All punctuation marks use in plain key: ~`!@#$%^&*()_-+=<,>.?|
- Key is not a meaningful sentence
- Find the rule in this tool
**FEATURES**
<1> Encrypt message 
<2> Get the flag 
Your choice:

1を選ぶと任意の文字列をEncryptしたものを返してくれるので,いろいろ試していると半角スペースで1文字が区切られていることがわかりました さらに, 数字は 00/::/:: (0の場合) 小文字のアルファベットは ::/aa/??/:: (aの場合,??は記号列) 大文字のアルファベットは ::/xx/AA/:: (Aの場合,xxは小文字のアルファベット) その他の記号は ::/::/::/::/?? (?の場合)

のように1文字ずつ暗号化されているようです.

となれば,これを復号化するような処理を書いてあげれば終わりです

from pwn import *
host,port="104.154.120.223", 8085
context.clear()
context.log_level='error'
cn=remote(host,port)
key1=cn.recvuntil("WELCOME TO CHAOS TOOL:")
key=key1[17:key1.find("WELCOME TO CHAOS TOOL:")].split(" ")
text=""
for i in range(len(key)):
    key[i]=key[i].strip()
    print key[i]
    if (len(key[i])==14):
        text+=key[i][12]
        continue  
    if (len(key[i])==11):
        if (ord(key[i][6])>=65 and ord(key[i][6])<=90):
            text+=key[i][6]
            continue
        else:
            text+=key[i][4]
            continue
    if (len(key[i])==8):
        text+=key[i][1]
        continue
    
    print ("???")

print cn.recvuntil("Your choice:")
cn.sendline("2")
print cn.recvuntil("Please enter the key to get flag:")
cn.sendline(text)
print text
print cn.recvrepeat()
cn.close()

ISITDTU{Hav3_y0u_had_a_h3adach3??_Forgive_me!^^}

感想

CTF初参加と言っても過言ではないような状態で,初心者向けだからと唆されて参加しましたが,解ける問題もあってよかったとおもいます. 後半はmatrixとずっと戦っていたのですが最後まで何がなんやらわからずに終わってしまったので悔しいです. あと,これは経験がない故の愚痴なので聞き流してもらいたいのですが,すごくスコアサーバーが不安定で大変でした CTFはこんなものなんですか知りませんが

易しい問題を作ってくださった運営の方々に感謝ですありがとうございました

感謝

チームのみなさんに問題を残しておいてと泣きついたためになんとか2つのフラグを通すことができました. 完全に足を引っ張ってしまいましたが,CTFを初めて数か月の超ビギナーな私を快く迎え入れてくれたzer0ptsのみなさんありがとうございました.

この文章は寝ながら書いたので無茶苦茶です.気が向いたら修正します