CTF
CTF をやり始めたので、解いた問題の記録を書いていく
ding-dong-ting-ping (CakeCTF 2023)
AlpacaHack で解いた。
問題
サーバーに対して次の操作ができる
- register:
{PREFIX}|user={username}|{timestamp}
を暗号化したものを教えてくれる。username
は base64 で渡すので、任意のバイト列を渡すことができる。PREFIX
は不明。username
にroot
が入っていたらだめ - login: register で作ったものを渡すと、username を返してくれる。これが
root
ならフラグがもらえる。解いてるときは気にしていなかったが、decode()
しているので UTF-8 じゃないバイト列になってしまうとエラーらしい
暗号化は 16 byte block の AES-CBC みたいなものだが、通常の CBC では前の block の暗号文と現在の block の平文の XOR をとるのに対し、この問題では前の block の暗号文 の MD5 と現在の block の平文の XOR をとる。
解法
{PREFIX}|user=root|(なにか)
を暗号化したものが得られれば勝ち。
block 境界がわからないと何もできないので、まず PREFIX
の長さを求めることを考える。
username
に aaaabaaaaaaa...
みたいなものを b
の位置を変えて渡して、どのブロックまで暗号文が一致しているかを観察することで、{PREFIX}|user=
部分の長さがわかる。今回の場合これが 23 であるとわかる。特に、PREFIX
の長さは 17 である。
block 境界がわかったので、username
に適当に長いバイト列を渡して、適当な block を見ると、その block の暗号文は AES(md5(その前の block の暗号文) xor (その block の平文))
となる。なので、その block の平文を細工することで、任意の 16 byte block に対して AES(block)
を直接求めることができる。
{PREFIX}|user=root|(なにか)
を暗号化することを考える。これは次のようにブロックに分割できる:
-
PREFIX
の先頭 16 文字 -
?
をPREFIX
の末尾の文字として、?|user=root|...
- (なにか) の後ろのほう
1 を暗号化した結果はわかっているので、2 の ?
が分かれば先ほどの手続きにより全体を暗号化した結果も求められることになる。?
は 1 バイトで、あり得るものは高々 256 通りしかないので、適当に長い username
に対して ?
を全通り試して先ほどの手続きで正しい暗号化結果になるものを選べばよい。
janken vs yoshiking 2 (CakeCTF 2023)
AlpacaHack で解いた。
問題
素数
解法
mod p での
今回、
ただ、この解法だと
AlpacaHack Round 3 (Crypto)
14 位。
qrime
問題
解法
素数定理より、平均的に
Rainbow Sweet Alchemist
問題
以下の手順で 1024 ビットの素数を作る。
- 64 ビットの素数を 16 個作る。
- その積の 2 倍 + 1 が素数かを判定する。素数ならばそれを返し終了する。
- 素数でないならば、今持っている 16 個の素数のうち 1 個ランダムに選んで、それを新たに作った 64 ビット素数で置き換え、2 に戻る。
ただし、64 ビット素数を作る過程は deterministic である(
この過程を 2 回繰り返して素数
解法
実験すると、↑の過程で
A Dance of Add and Mul
問題
暗号文をビット列で
楕円曲線
このとき、
思考過程
- 楕円曲線わからん。なんかアーベル群になることだけ知ってる
-
なら、o_1 \neq o_2 でo_i \cdot c_i 成分消してG_1 がわかって、隣り合うもので比較すると 01 や 10 の並びが特定できるんじゃね?どっちにもならなかったら前のビットと同じということで(o_i x_i) G_2 - とりあえず SageMath をインストールしないと話にならん→インストール
- 動かしてみたところ
が判明。困るo_1 = o_2 - factor により、
が判明するo_1 = 3 \cdot 11 \cdot 10177 \cdot 859267 \cdot 52437899 \cdot 52435875175126190479447740508185965837690552500527637822603658699938581184513 -
、o_1, o_2 の倍数っぽいし、3 の形で書いたときにa G_1 + b G_2 がa, b の倍数になっているかは3 倍することで判定できない?\frac{o_1}{3} - できなかった。↑は
がE の直和で書けることを前提としているが、そうではないため\langle G_1 \rangle , \langle G_2 \rangle - でも隣り合うものの差が
ないしG_1 の整数倍かどうかって使いたいよな~G_2 -
かどうかの判定ができればよいが、一般にそんなことができるとは思えない\langle c_i - c_{i+1} \rangle \subseteq \langle G_1 \rangle - (ライブラリのドキュメントを見て、そのような関数が存在しないかを探す)
-
っていうちょうどいい素因数があるし、10177 倍してから生成される群を見ればいいんじゃね?→正解\frac{o_1}{10177}
解法
まず
同様に、
これ以外のときは、
↑の手順により、隣り合うビットが同じか異なるかどうか、また異なるときは 0→1 か 1→0 のいずれか、というのもわかるため、全体のビットを決定可能である。
Hat Trick
問題
mod がランダムな 64 ビット素数、base が乱数なローリングハッシュが 3 つある。与えられた乱数 3 つ組に対して、ローリングハッシュ 3 個の値がそれになるような文字列を 1 個求めよ。文字列は英小文字 54 文字以下でなければならない。
ローリングハッシュが与えられてから 3 分以内に 3 組の乱数に対して解答する必要がある。
思考過程
- ローリングハッシュじゃん
- 競プロ的な構築か?
- 与えられる乱数が 192 bit くらいのエントロピーを持っていて、文字列のエントロピーが 253.8 bit くらいなので、そんなに余裕がない
- (ちょっと考える)→1 つのローリングハッシュの都合を合わせると他のローリングハッシュの値が壊れるので、直接構築を与えるのは難しそう
- CTF 典型?と思って調べる
- こんな記事が出てきた https://qiita.com/kusano_k/items/5509bff6e426e5043591
- LLL というアルゴリズムがあって、ナップサックを解くのに使えるらしい
- SageMath なら簡単に LLL を使える
- いろいろ試したら解けた
解法
文字列の長さを
この上で、↓のような行列を考える。
これに LLL を適用すると、52 行目 (1-based) の左のほうに求める
LLL について
最初、得られる行列の各行が「元の行列の行ないし列の線形結合の係数」みたいなものを表しているのかと思って混乱した。実際には、行列に対して「行の整数倍の和として書けるベクトルからなるベクトル空間」を考えたとき、LLL はこのベクトル空間が一致するような行列であって、係数が小さいものを求めているのである。
特に、LLL の出力で得られる行列の各行は、元の行列の行の整数倍の和として書くことができる。
LLL で得られた行列の各行の左側