Eyes, JAPAN Blog > Advent Calendar CTF 2014 Write Up [Rotate]

Advent Calendar CTF 2014 Write Up [Rotate]

beko

この記事は1年以上前に書かれたもので、内容が古い可能性がありますのでご注意ください。

今週担当の杉山です。今回はつい最近まで行われていた、Advent Calendar CTF 2014のRotateというCrypt問題についてのwrite upを書いていきたいと思います。

 

まず始めにこの問題はどういう問題なのか

rotate.zipファイルが渡され、それを解凍すると、暗号化されたjpegらしきファイルflag.jpg.encと暗号化に用いられたであろうrotate.pyというファイルが渡され、どうにかして、flag.jpg.encを復号化するという問題です。

rotate.pyはどういうファイルなのか。

このファイルはpython rotate.py 暗号化したいFILENAME 任意の数字という感じで実行すると、暗号化したいFILENAME.encという暗号化されたファイルを生成します。またKeyの値はKey = math.radians(int(任意の数字))という式で生成されており、暗号化には(x * math.cos(key) - y * math.sin(key)) + p(x * math.sin(key) + y * math.cos(key))) という式が使われています。これの中央の部分の+は数値の足し算ではなく、文字列の連結を意味していることがわかりました。また暗号化前のバイナリ1byteに対して4byteのバイナリを生成するということがコードを読むとわかり、一度に2byteのバイナリを読み込み、それぞれ1byteずつ暗号化し、それぞれの暗号化後の4byteのバイナリと4byteのバイナリを文字列連結して8byteのバイナリをファイルに書き出していることがわかりました。

どうやって復号するか。

問題の題名的にRot暗号かなと思ったのですが、解く方法が思いつかなかったのでゴリ押し(頭の悪い解き方)で解くことにしました。
流れとしては、Keyの値を求める->Keyの値と暗号化されたバイナリを用いてx*cos(key)-y*sin(key)x*sin(key)+y*cos(key)連立方程式(この部分がたぶんダメ)を求め復号していくような形になります。

実際に解いてみる。

まず暗号化されたファイルがjpgファイルということで、暗号化前のjpgファイルの先頭の2byteは\xff\d8ということが確定しています。暗号化されたflag.jpg.encをバイナリエディタに突っ込んでみると、先頭の8byteがA8 5D 08 42 3C 93 A7 41 となっており、暗号化前の\ff\a8\x5d\x08\x42に対応、\d8\x3c\x93\xa7\x41に対応しており、これによりxとyの値が固定されKeyの値を求めることができる。

次にkeyが求められたので、Keyの値と暗号化されたファイルのバイナリを8byteずつ使って連立方程式を解く。そうすると暗号化される前のxとyの値が求められ、さらにxとyの数値をpackしてバイナリにしてあげ、ファイルに書き込むと復号化ができる。

20141227180325

import sys
import math
import struct
from numpy import *
from numpy.linalg import *
p = lambda x :struct.pack('f',x)
p_2 = lambda x:struct.pack('b',x)
u = lambda x :struct.unpack('b',x)[0]
u_2 = lambda x:struct.unpack('f',x)
a = '\xa8\x5d\x08\x42'
b = '\x3c\x93\xa7\x41'
def get_key():
x,y = u('\xff'),u('\xd8')
for i in range(0,256):
key=math.radians(i)
c = p(x * math.cos(key) - y * math.sin(key))
d = p(x * math.sin(key) + y * math.cos(key))
if a == c and b == d:
print "key is %f\n" % key
return key
def decrypt(key):
filename = sys.argv[1]
all_list =[]
cos_key = math.cos(key)
sin_key = math.sin(key)
enc = open(filename,'rb').read()
dec = open(filename.replace('.enc',''),'wb')
for i in range(0,len(enc),4):
binary_4bytes = enc[i]+enc[i+1]+enc[i+2]+enc[i+3]
z = u_2(binary_4bytes)
all_list+=list(z)
for j in range(0,len(enc),2):
equation_1 = array([[cos_key,-sin_key],[sin_key,cos_key]])
equation_2 = array([all_list[j],all_list[j+1]])
sol = solve(equation_1,equation_2)
s_1 = int(round(sol[0],3))
s_2 = int(round(sol[1],3) )
dec.write(p_2(s_1))
dec.write(p_2(s_2))
def main():
if len(sys.argv) != 2:
sys.exit(1)
key=get_key()
try:
decrypt(key)
except IndexError:
print "Success Make flag.jpg file"
else:
print "Failed Make flag.jpg file"
sys.exit(1)
if __name__ == '__main__':
main()

今回は普段あまり挑戦しない、Crypt問題に挑戦してみました。解き慣れていないこともあって、問題の解き方がごり押しな感じになってしまいました(笑)。他の方のWrite Upを見たら、やはり効率的かつ数学的理論に基づいた解き方があるようで、数学も勉強しないとなって気持ちになりました!

ではノシ

Comments are closed.