Attack All Around

今やCTFと競プロばかりだが、トビタテ生のアメリカ留学やTOEFL奮闘記

Tamil CTF 2021 crypto writeup

平日開催だったのですが、思わずやってしまいました。前回のCTFのwriteup見たいなとCTFtimeを開いたのが敗因です…。おかげで平日にやるべきことをどんどん後回しにしています。

今回のCTF、最後に時間を延長することになってそのおかげで追加で1問解けたのですが、その後FLAGをtwitterに載せる人がいて強制終了となりました。本当に良くない。





Result



f:id:partender810:20210929124240p:plain
良かったと思う


f:id:partender810:20210929204320p:plain
あと2問は難しすぎる



Writeup


Triple Dimple Easy Squish 200pt


The Author is addicted to reels , find out what he is doing!!
ファイル:chall

challを見ると、ciphertext, key, ivがあります。keyは24バイトでivは8バイトでした。慣れてる方は8バイトのivでピンと来るかもしれません。ピンと来なかった私は、とりあえずAESだろう、keyが読める文字だな、筆者はリールが好きとのことだからivを繰り返して16バイトにするのかな?と色々考えていました。しかし、どのAES暗号のモードを使ってもダメ…。


問題名より、Triple DES に気付けるかどうかの問題でした。DESはIVを8バイトにしがちなのか…。

from Crypto.Cipher import DES3
from Crypto.Util.number import *
f = open("chall")
a = f.readlines()
ct = a[0].split()[-1]
key = a[1].split()[-1]
iv = a[2].split()[-1]
ct = long_to_bytes(int(ct,16))
key = long_to_bytes(int(key,16))
iv = long_to_bytes(int(iv,16))
cipher = DES3.new(key, DES3.MODE_CBC,iv)
print(cipher.decrypt(ct))
print()
cipher = DES3.new(key, DES3.MODE_CFB,iv)
print(cipher.decrypt(ct))
print()
cipher = DES3.new(key, DES3.MODE_OFB,iv)
print(cipher.decrypt(ct))
print()

OFBモードでうまくいきました。


TamilCTF{Triplee_DES_iss_quitee_samee_as_DES_isnt_it???}



PJ-JP 238pt


You know something Pj and Jp are friends and CTF contributers... go ahead
ファイル:pj_jp.txt

いつもvimでファイルを見ているのですが、今回catで見たのでファイルの下の方の文字にすぐ気付けました。

最後にある、"jp", "pj"を2進数に変換すればOKです。8文字毎に空白が2つあることや、8文字毎の先頭が毎回"jp"であることから推測できると思います。"jp"を0, "pj"を1としてASCIIコードとすればOKです。

f = open("pj_jp.txt")
a = f.readlines()
enc = a[-3].split()

for i in range(0,len(enc),8):
    temp = enc[i:i+8]
    val = 0
    val = ""
    for j in range(len(temp)):
        if temp[j] == "pj": val += "1"
        else: val += "0"
    print(chr(int(val,2)),end="")
print()


TamilCTF{wh4t_th3_b!4ry}



AEXOR 293pt


The title itself enough ig?
ファイル:data.txt, enc.py

正直この問題はあまり納得いっていないです。

Triple Dimple Easy Squishと同様、data.txtを見るとciphertext, key, ivがあります。ivが16バイトであることや問題名からAES暗号でしょう。enc.pyでAESであることは分かりますがモードは分かりません。


問題は、encです。encはgetkey関数とgetsubkey関数で取得したバイト列を1バイト毎XORしています。getkey関数で得られたバイト列はkeyとなり、暗号化時に使われています。getsubkeyはそのkeyの部分文字列であろう、だからrep_keyという変数名(repeat)なのだろうと推測。どのバイト列が部分文字列ならうまくいくかすべて検証しましたが、どれもうまくいかず…。


結局、data.txtの最後にあった"okay"がgetsubkey関数で得られた文字列とのこと。Discordで質問したらよくdata.txtを見ろと言われました。元から"okay"には気付いていましたが、まさかこれがという感じです。しかも、別にkeyの部分文字列ではないです。うーん…

from Crypto.Util.number import *
from Crypto.Cipher import AES
ct = "68e934aa25be2c5f1674e101b31c25672400d69f9cf910a9f64071cea79f2de01d01bcf140105e5f7a3db66fffe64694"
enc = "030e150a0b0415110618111c001b0d1c"
iv = "1cb7942bf4ae14947150f9f196f92b2c"
ct = long_to_bytes(int(ct,16))
iv = long_to_bytes(int(iv,16))
ok = "okay"
key = b""
for i in range(0,len(enc),2):
    v = int(enc[i:i+2],16)^ord(ok[(i//2)%4])
    key += long_to_bytes(v)

cipher = AES.new(key,AES.MODE_CBC,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("CBC",flag)
cipher = AES.new(key,AES.MODE_CFB,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("CFB",flag)
cipher = AES.new(key,AES.MODE_OFB,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("OFB",flag)
cipher = AES.new(key,AES.MODE_OPENPGP,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("OPENPGP",flag)
cipher = AES.new(key,AES.MODE_EAX,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("EAX",flag)
cipher = AES.new(key,AES.MODE_GCM,iv)
flag = cipher.decrypt(ct)
if b"Tamil" in flag: print("GCM",flag)

CBCモードでした。


TamilCTF{AESS+XORR_issss_W3irdd_Combinationn???}



Secret Sharing 294pt


Jo , Pj and Shamir communicating with each other in group, jo ask for a flag, Shamir send's junk of data, did you decrypt this data?
ファイル:file

問題名をググると、「Shamirの秘密分散法」がヒットします。以下のサイトが分かりやすかったです。

Shamir's Secret Sharing Step-By-Step - Qvault


つまり、f(x) = a_n × xn + a_(n-1) × xn-1 + ... + a_1 × x + a_0 mod p という式があり、a_0が秘匿情報、つまりFLAGとなります。f(1) ~ f(5)の値が与えられています。また、required_sharesが2であることから、上記の式のnが2ではないかとエスパーします。


f(x) = a1*x+a0 mod p  (a0 = flag)

f(1) = a1+a0 = shares[0] mod p
f(2) = 2*a1+a0 = shares[1] mod p

よって
a1 = f(2) - f(1) = shares[1] - shares[0] mod p
flag = a0 = f(1) - a1 mod p
import base64
from Crypto.Util.number import *

p = b"AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEp"
p = bytes_to_long(base64.b64decode(p))

rs = 2

shares = [b"MEwx7cz+C01rL8H0Hhz2EIgHjWYXVcL81uITmRha674=",b"YJhj22+ntS1s80CT9b6Y7ayc52baTFGNRpPUyLxtaf8=",b"kOSVyRJRXw1utr8zzWA7ytEyQWedQuAdtkWV+GB/6EA=",b"wTDHtrT7CO1wej3TpQHep/XHm2hgOW6uJfdXKASSZoE=",b"8Xz5pFekss1yPbxzfKOBhRpc9WkjL/0+lakYV6ik5MI="]

#f(1) = a1+a0 mod p
#f(2) = 2*a1+a0    mod p
f1 = bytes_to_long(base64.b64decode(shares[0]))
f2 = bytes_to_long(base64.b64decode(shares[1]))
a1 = (f2-f1)%p
flag = (f1-a1)%p
print(long_to_bytes(flag))


TamilCTF{S3cr3eT_4lg0RitHm}



Break The RSA 296pt


Megan Foxx sent a mssg regarding her age..Did you decrypt message only using public keys?
Note: the flag is seprated by two parts
Name+age enc Second one Rsa
ファイル:message.enc, message.pub

この問題もあまり好みではありませんでした…。


1. 1つ目の暗号

公開鍵が、「BEGING PUBLIC KEY」と少し変です。ちゃんとしたものではないのでしょう。問題文より、どうやらRSA暗号ではないようです。


作問者に聞くと、Meganの年齢が重要だと言っていました。Megan Foxxという人が実在しているとは思わず放置していました。調べてみると35歳でした。なんと、megan35 という符号化処理があるんですね。調べてみると、base64でencodeされた文字列を別の文字に置き換える処理がmegan35のようです。


weird_encodings · GitHub

このソースコードを参考にして自作megan32 decodeプログラムを作りました。decode時にpaddingが合わないと出てきたので最後の文字だけのけると、URLエンコードされた文字列となりASCII文字列にするとこのような文章が得られました。

Congrats You Got it !
n = 667
d = 1027
e = 3
decrypt it


message.encの1行目の整数たちをこの値で復号すると、flagの前半部分が得られました。

import base64
from Crypto.Util.number import *
import urllib.parse

megan35 = b"3GHIJKLMNOPQRSTUb=cdefghijklmnopWXYZ/12+406789VaqrstuvwxyzABCDEF5"
b = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
dec_dict = dict(zip(megan35, b))

# megan 35 decode
enc = b"bwDVjxOXnMR1RZGjlxe1RZGMlxb1RZG0nHesRHesRceqbgy1RZ31Rub1RZ3wSZm1RJK/OdNqOdSJOdNqRd3sSseqbge1RZ31Rub1RZ3tOdGGjLfZm+1qnHesRL1u="
dec = b""
for e in enc:
    v = dec_dict[e]
    dec += long_to_bytes(v)
msg = base64.b64decode(dec[:-1])
print(urllib.parse.unquote(msg.decode()))


# decode RSA

n = 667
d = 1027
e = 3

ct = [408,217,382,380,416,613,408,162,604,9,537,146,280]
for c in ct:
    m = pow(c,d,n)
    print(chr(m),end="")
print()


TamilCTF{y0u_


2. 2つ目の暗号

DownUnder CTFでもあったPKCS#1方式のRSAです。モジュール使って公開鍵を見てみると、nの値がそこまで大きくなかったのでfactorDBに入れると素因数分解できました。復号したのですが、なんか変な文字列に…。

b'\x02\x90\xa9\x14\x93l\xe2\x9f\x8a?-\xa1\xf4\x01b\xbbD\xa8\x00br34k3d}\n'

後ろの方にギリギリ読めそうな文字列がありました。いつかのCTFで復号したらこれと似た文字列が出てきたことがありました。その時は、m1, m2が得られてm2をlong_to_bytesするとこのように後ろの方に読める文字列があり、答えはlong_to_bytes(m1+m2)でした。

今回もこんな感じになるのだろうと思っていたのですが、1つ目の暗号は綺麗にもう出ています。これからどうするんだと悩んでいたら、作問者からそのまま読めるとこ引っ付ければいいよと言われます。


公式writeupではopensslでやると綺麗になるそうですが、なんかもやっとしています。

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
import base64
b2 = b"MDgwDQYJKoZIhvcNAQEBBQADJwAwJAIdDVZLl4+dIzUElY7ti3RDcyge0UGLKfHs+oCT2M8CAwEAAQ=="
v2 = base64.b64decode(b2)
cipher = RSA.importKey(v2)
print(cipher)
# RsaKey(n=359567260516027240236814314071842368703501656647819140843316303878351, e=65537)

# nを素因数分解すると、p,q= 17963604736595708916714953362445519,20016431322579245244930631426505729
p,q= 17963604736595708916714953362445519,20016431322579245244930631426505729
n = 359567260516027240236814314071842368703501656647819140843316303878351
assert n == p*q
e = 65537
ct2 = b"C1qKLBtrUwLkebPf+JKX6ie1bKEdUGmzkYwBJWQ="
ct2 = bytes_to_long(base64.b64decode(ct2))
phi = (p-1)*(q-1)
d = inverse(e,phi)
m = pow(ct2,d,n)
print(long_to_bytes(m))


TamilCTF{y0u_br34k3d}



Code Book of Bases 482pt


Blocks and Blocks of data in Cipher Books
ファイル:cipher, key

keyに書いてあるものが読みにくかったので、fileコマンド叩くと見慣れないものが出てきました。

$ file key
key: ISO-8859 text


これでutf-8に変換できました。

不正なマルチバイト文字ですと表示された時の対処法 - Qiita

$ iconv -f sjis -t utf8 key
モ]tラMuモ]tモ]5モ]uラM5モ]uモ]4モ]tモM5モ]tラ]5モ]tラM5モ]tラ]4モ]tモMuモ]uモ]4モ]tモ]tモMuモMtモMuモM4モMuモMtモMuモM5モMtモM5


バイナリデータとして読み込んでみると、なんとなく周期がありそうで、5バイト毎のようです。

b'\xef\xbe\x93]t'
b'\xef\xbe\x97Mu'
b'\xef\xbe\x93]t'
b'\xef\xbe\x93]5'
b'\xef\xbe\x93]u'
(以下略)


2進数で見てみましょう。

1110111110111110100100110101110101110100
1110111110111110100101110100110101110101
1110111110111110100100110101110101110100
1110111110111110100100110101110100110101
1110111110111110100100110101110101110101
(以下略)


21, 27, 33, 37bit目以外は毎回同じです。ここの部分だけ抜き取ってみると、ASCIIコードのようです。ここのエスパー要素ちょっと強め?

b'keytamilctf2021!'


どうやらkeyが得られたようです。問題名よりブロック暗号のようなので、IVの要らないAESのECBモードでcipherにあった暗号文らしきbase64encodeを復号してみたら、FLAGになりました。


TamilCTF{bL0ckS_ar3_Br34kabL3!!}



Bases 485pt


Its going to complex as usual, but if you know random bases very well it wouldn't be. Play with them
ファイル:cipher.txt

時間延長のおかげで解けた問題です。作問者にめっちゃDM送りました。

どうやら65536がヒントだぞと教えてもらったのですが、問題名より65536進数にするのかと思いやってみるがうまくいかず…。

結局、base65536 → base91 → base62 → base32 → base85とdecodeすればFLAGになりました。base65536は初耳です。情弱には難しいです…。


復号ツールはこちらです。
base65536 : Base65536 Decoding Tool Online Free
base91 : Base91 Encoding - Base 91 Online Decoder, Encoder, Translator
残り : CyberChef


TamilCTF{B4s3_C1ph3r_4r3_re4lly_c00l!!}



Vai Raja Vai 984pt


Karthick(you) and Panda are friends.. You have a unique power to see the future and panda knows the gambling world . Panda is in debt of 10 lakhs to Rande(a local gangster) as one of his gambling incident went wrong. Use your ability to help panda. As his fate depends on your ability and time.
nc 3.99.48.161 8002
ファイル:vai_raja_rai_client

crypto要素はそれほどなく、reversing問のような気がします。reversingにしてもそれほど難しいわけではなく、コスパがいい問題でした。


Ghidraに与えられたファイルを突っ込んでみると、乱数を生成しておりシードはそのプログラムが実行された時のUNIXTIMEです。

順序としては、mod 7の乱数を6回生成し、その合計値を配列に格納すること50回行います。その配列の値を50個すべて当てればFLAGがもらえます。

そのアルゴリズムをそのまんま自分でも作ります。ここでシードは現在のUNIXTIMEから10足した値にします。自分で実行してから10秒後にncすると、シードが同じ値になるので、実行したプログラムでの配列の値をそのまま送ればFLAGがもらえます。

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

int main(void) {
    unsigned int nowtime = (unsigned int) time( NULL );
    srand(nowtime+10);
    int a,b,x;
    a = 0;
    b = 1;
    int val[53];
    while(b < 301) {
        if(b%6 == 0) {
            val[b/6-1] = a;
            a = 0;
            x = rand();
            a += x%7;
        } else {
            x = rand();
            a += x%7;
        }
        b++;
    }
    for(int i=0;i<53;i++) {
        printf("%d %d\n",i,val[i]);
    }
    printf("%u\n",nowtime+10);
}


TamilCTF{Af73r_m4nY_1nt3r3st1ng_tuRn_0f_3v3nts_R4nd3_tri3s_t0_k1ll_k4rth1ck_ but_k4rth1ck_g0t_s4v3d_by_KumAru_uh_K0kki_kumAru_uh}



Problems&solver

GitHub - ksbowler/TamilCTF_2021