Writer:b1uef0x / Webページ建造途中
2問解いた。Webが解けなかった。
Welcome to SekaiCTF! Flags will be in format SEKAI\{[\x20-\x7e]+\}.
Join our Discord for admin support and challenge updates. Flag is in #announcement channel.
SEKAI{w31c0m3_t0_th3_w0r1d!}
I have encrypted a secret message with this super secure algorithm and put it into a Time Capsule. Maybe nobody can reveal my secret without a time machine...
Author: sahuang
暗号化を行うchall.pyと暗号データflag.encが配布される。
import time
import os
import random
from SECRET import flag
def encrypt_stage_one(message, key):
u = [s for s in sorted(zip(key, range(len(key))))]
res = ''
for i in u:
for j in range(i[1], len(message), len(key)):
res += message[j]
return res
def encrypt_stage_two(message):
now = str(time.time()).encode('utf-8')
now = now + "".join("0" for _ in range(len(now), 18)).encode('utf-8')
random.seed(now)
key = [random.randrange(256) for _ in message]
return [m ^ k for (m,k) in zip(message + now, key + [0x42]*len(now))]
# I am generating many random numbers here to make my message secure
rand_nums = []
while len(rand_nums) != 8:
tmp = int.from_bytes(os.urandom(1), "big")
if tmp not in rand_nums:
rand_nums.append(tmp)
for _ in range(42):
# Answer to the Ultimate Question of Life, the Universe, and Everything...
flag = encrypt_stage_one(flag, rand_nums)
# print(flag)
# Another layer of randomness based on time. Unbreakable.
res = encrypt_stage_two(flag.encode('utf-8'))
with open("flag.enc", "wb") as f:
f.write(bytes(res))
f.close()
暗号化は二段階で行われており、逆算するにあたってまずencrypt_stage_twoから見ていく。
def encrypt_stage_two(message):
now = str(time.time()).encode('utf-8')
now = now + "".join("0" for _ in range(len(now), 18)).encode('utf-8')
random.seed(now)
key = [random.randrange(256) for _ in message]
return [m ^ k for (m,k) in zip(message + now, key + [0x42]*len(now))]
encrypt_stage_twoでは現在時刻をseedにrandom関数を初期化して、messageの長さ分の乱数を生成してkeyとしてmessageとXORをとっている。
よく見るとmessage+現在時刻nowと足したデータにXORをかけており、また現在時刻nowにかかっているXOR演算はすべて0x42となっていることから、現在時刻を復元することができる。
flag.encの末尾はstvupvsusrlqzvtuwr
で、各文字に0x42とXORすれば1647241710.3846750
が復元される。この値でrandom関数を初期化してencrypt_stage_twoを逆算できる。
続いてencrypt_stage_oneを見る。
def encrypt_stage_one(message, key):
u = [s for s in sorted(zip(key, range(len(key))))]
res = ''
for i in u:
for j in range(i[1], len(message), len(key)):
res += message[j]
return res
入力されるkeyは1byteの異なる乱数が8個入った配列になっており、乱数はわからない。しかしやっていることは8文字の組ごとに順序を並び替えているだけである。総当りで解くことができる。
各ステージの逆関数を書いて、encrypt_stage_oneを総当りするsolverを書いた。SEKAIから始まる文字列が見つける。
import time
import os
import random
import itertools
def encrypt_stage_one_rev(message, key):
u = [s for s in sorted(zip(key, range(len(key))))]
resl = [0] * len(message)
k = 0
for i in u:
for j in range(i[1], len(message), len(key)):
resl[j] = message[k]
k += 1
return "".join(resl)
def encrypt_stage_two_rev(message0,now):
message = message0[:len(message0)-len(now)]
random.seed(now)
key = [random.randrange(256) for _ in message]
return [m ^ k for (m,k) in zip(message0, key + [0x42]*len(now))]
with open("flag.enc", "rb") as f:
enc = f.read()
f.close()
now0 = b'1647241710.3846750'
res = encrypt_stage_two_rev(bytes(enc),now0)
res = res[:len(res)-len(now0)]
l = [0,1,2,3,4,5,6,7]
for v in itertools.permutations(l):
flag = bytes(res).decode('utf-8')
for _ in range(42):
flag = encrypt_stage_one_rev(flag, v)
if flag[:5] == "SEKAI" :
print(flag)
SEKAI{T1m3_15_pr3C10u5_s0_Enj0y_ur_L1F5!!!}
Welcome to the first lab of Course ML10001 from Sekai University! The Lab 1 assignment should be pretty easy...
Author: sahuang
Matrix_Lab_1.classが配布される。jadを使って逆コンパイルをしてみたところ、Parsing Matrix_Lab_1.class...JavaClassFileParseException: Invalid tag value 0x12
と失敗してしまった。
というわけでGhidraで逆コンパイルしてみた。
main関数void main_java.lang.String[]_void(void)
{
PrintStream objectRef;
String objectRef_00;
int iVar1;
String objectRef_01;
int iVar2;
String objectRef_02;
boolean bVar3;
Scanner objectRef_03;
AssertionError objectRef_04;
objectRef_03 = new Scanner(System.in);
objectRef = System.out;
objectRef.print("Enter the flag: ");
objectRef_00 = objectRef_03.next();
iVar1 = objectRef_00.length();
if (iVar1 != 0x2b) {
objectRef = System.out;
objectRef.println("Oops, wrong flag!");
return;
}
objectRef_01 = objectRef_00.substring(0,Sekai.length);
iVar1 = Sekai.length;
objectRef_02 = objectRef_00;
iVar2 = objectRef_00.length();
objectRef_02 = objectRef_02.substring(iVar1,iVar2 + -1);
iVar1 = objectRef_00.length();
objectRef_00 = objectRef_00.substring(iVar1 + -1);
bVar3 = objectRef_01.equals("SEKAI{");
if ((bVar3 != false) && (bVar3 = objectRef_00.equals("}"), bVar3 != false)) {
if ((Sekai.$assertionsDisabled == false) &&
(iVar1 = objectRef_02.length(), iVar1 != Sekai.length * Sekai.length)) {
objectRef_04 = new AssertionError();
/* WARNING: Do nothing block with infinite loop */
athrowOp(objectRef_04);
do {
} while( true );
}
bVar3 = Sekai.solve(objectRef_02);
if (bVar3 != false) {
objectRef = System.out;
objectRef.println("Congratulations, you got the flag!");
return;
}
objectRef = System.out;
objectRef.println("Oops, wrong flag!");
return;
}
objectRef = System.out;
objectRef.println("Oops, wrong flag!");
return;
}
solve関数int solve_java.lang.String_boolean(void param1)
{
undefined2 uVar1;
char[] pcVar2;
char[] pcVar3;
String pSVar4;
String pSVar5;
String pSVar6;
boolean bVar7;
int iVar8;
int iVar9;
String objectRef;
pcVar2 = param1.toCharArray();
pcVar2 = Sekai.transform(pcVar2,Sekai.length);
iVar8 = 0;
while( true ) {
if (Sekai.length / 2 < iVar8) break;
iVar9 = 0;
while( true ) {
if (Sekai.length + iVar8 * -2 + -1 <= iVar9) break;
uVar1 = (undefined2)(((int[])pcVar2[iVar8 * 2])[0] + (iVar8 + iVar9) * 2);
(undefined2)(((int[])pcVar2[iVar8 * 2])[0] + (iVar8 + iVar9) * 2) =
(undefined2)(((int[])pcVar2[(((Sekai.length + -1) - iVar8) - iVar9) * 2])[0] + iVar8 * 2)
;
(undefined2)(((int[])pcVar2[(((Sekai.length + -1) - iVar8) - iVar9) * 2])[0] + iVar8 * 2) =
(undefined2)
(((int[])pcVar2[((Sekai.length + -1) - iVar8) * 2])[0] +
(((Sekai.length + -1) - iVar8) - iVar9) * 2);
(undefined2)
(((int[])pcVar2[((Sekai.length + -1) - iVar8) * 2])[0] +
(((Sekai.length + -1) - iVar8) - iVar9) * 2) =
(undefined2)(((int[])pcVar2[(iVar8 + iVar9) * 2])[0] + ((Sekai.length + -1) - iVar8) * 2)
;
(undefined2)(((int[])pcVar2[(iVar8 + iVar9) * 2])[0] + ((Sekai.length + -1) - iVar8) * 2) =
uVar1;
iVar9 = iVar9 + 1;
}
iVar8 = iVar8 + 1;
}
objectRef = "oz]{R]3l]]B#50es6O4tL23Etr3c10_F4TD2";
pcVar3 = Sekai.getArray(pcVar2,0,5);
pSVar4 = Sekai.encrypt(pcVar3,2);
pcVar3 = Sekai.getArray(pcVar2,1,4);
pSVar5 = Sekai.encrypt(pcVar3,1);
pcVar2 = Sekai.getArray(pcVar2,2,3);
pSVar6 = Sekai.encrypt(pcVar2,0);
pSVar4 = makeConcatWithConstants(pSVar4,pSVar5,pSVar6);
bVar7 = objectRef.equals(pSVar4);
return (int)bVar7;
}
transform関数char[] transform_char[]_int_char[][](int param1,int param2)
{
char[] pcVar1;
int iVar2;
pcVar1 = multianewarrayOp(char[][],param2,param2);
iVar2 = 0;
while( true ) {
if (param2 * param2 <= iVar2) break;
(undefined2)(((int[])pcVar1[(iVar2 / param2) * 2])[0] + (iVar2 % param2) * 2) =
(undefined2)(param1 + iVar2 * 2);
iVar2 = iVar2 + 1;
}
return pcVar1;
}
getArray関数char[] getArray_char[][]_int_int_char[](int param1,int param2,int param3)
{
char[] pcVar1;
int iVar2;
int iVar3;
pcVar1 = new char[Sekai.length * 2];
iVar2 = 0;
iVar3 = 0;
while( true ) {
if (Sekai.length <= iVar3) break;
pcVar1[iVar2] = ((char[])(((int[])(param1 + param2 * 4))[0] + iVar3 * 2))[0];
iVar2 = iVar2 + 1;
iVar3 = iVar3 + 1;
}
iVar3 = 0;
while( true ) {
if (Sekai.length <= iVar3) break;
pcVar1[iVar2] =
((char[])(((int[])(param1 + param3 * 4))[0] + ((Sekai.length + -1) - iVar3) * 2))[0];
iVar2 = iVar2 + 1;
iVar3 = iVar3 + 1;
}
return pcVar1;
}
encryptString encrypt_char[]_int_java.lang.String(int param1,short param_2)
{
char[] pcVar1;
String pSVar2;
int iVar3;
int iVar4;
int iVar5;
pcVar1 = new char[Sekai.length * 2];
iVar3 = Sekai.length;
iVar5 = 0;
iVar4 = Sekai.length;
while( true ) {
iVar3 = iVar3 + -1;
if (Sekai.length * 2 <= iVar5) break;
pcVar1[iVar5] = ((char[])(param1 + iVar3 * 2))[0];
pcVar1[iVar5 + 1] = ((char[])(param1 + iVar4 * 2))[0];
iVar5 = iVar5 + 2;
iVar4 = iVar4 + 1;
}
iVar5 = 0;
while( true ) {
if (Sekai.length * 2 <= iVar5) break;
pcVar1[iVar5] = pcVar1[iVar5] ^ param_2;
iVar5 = iVar5 + 1;
}
pSVar2 = String.valueOf(pcVar1);
return pSVar2;
}
処理を簡単に述べると、main関数は入力されたflagのSEKAI{....}の{}の中身を取り出してsolve関数に渡す。
solve関数では文字列をtransformで2次元配列(6*6)に変換して、適当にシャッフルする。
getArray関数で2次元配列(6*6)からの1次元配列(12)を3つ取り出し、それぞれにencrypt関数を使って文字列に変換する。
変換された文字列3つを1つにつなげ、oz]{R]3l]]B#50es6O4tL23Etr3c10_F4TD2
と一致するかを判定する。
したがってやることは、各関数を丁寧に読んで逆関数を作ってoz]{R]3l]]B#50es6O4tL23Etr3c10_F4TD2
からflag文字列を復元することになる。
頑張ってJavaScriptで書いた。
Sekai = {}
Sekai.length = 6
Sekai.antitransform = function(pcVar1,param2)
{
var output = "";
var iVar2 = 0;
while( true ) {
if (param2 * param2 <= iVar2) break;
output += String.fromCharCode(pcVar1[Math.floor(iVar2 / param2)][iVar2 % param2]);
iVar2 = iVar2 + 1;
}
return output;
}
Sekai.setArray = function(param1,param2,param3,pcVar1){
var iVar2;
var iVar3;
iVar2 = 0;
iVar3 = 0;
while( true ) {
if(Sekai.length <= iVar3) break;
param1[param2][iVar3] = pcVar1[iVar2];
iVar2 = iVar2 + 1;
iVar3 = iVar3 + 1;
}
iVar3 = 0;
while( true ) {
if (Sekai.length <= iVar3) break;
param1[param3][Sekai.length -1 - iVar3] = pcVar1[iVar2];
iVar2 = iVar2 + 1;
iVar3 = iVar3 + 1;
}
return param1;
}
Sekai.decrypt = function(Sparam1,param_2){
var param1 = [];
for(var _i=0; _i<Sekai.length * 2;_i++)param1[_i] = Sparam1.charCodeAt(_i)
var iVar5 = 0;
while( true ) {
if (Sekai.length * 2 <= iVar5) break;
param1[iVar5] = param1[iVar5] ^ param_2;
iVar5 = iVar5 + 1;
}
var pcVar1 = [];
for(var _i=0; _i<Sekai.length * 2;_i++)pcVar1[_i] = 0
iVar3 = Sekai.length;
iVar5 = 0;
iVar4 = Sekai.length;
while( true ) {
iVar3 = iVar3 + -1;
if (Sekai.length * 2 <= iVar5) break;
pcVar1[iVar3] = param1[iVar5];
pcVar1[iVar4] = param1[iVar5+1];
iVar5 = iVar5 + 2;
iVar4 = iVar4 + 1;
}
return pcVar1;
}
Sekai.desolve = function(param1)
{
pcVar2 = [];
for(var _i=0; _i<Sekai.length;_i++) {
pcVar2[_i] = []
for(var _j=0; _j<Sekai.length;_j++)pcVar2[_i][_j] = 0;
}
var dec;
var objectRef = ["oz]{R]3l]]B#","50es6O4tL23E","tr3c10_F4TD2"];
dec = Sekai.decrypt(objectRef[2],0);
pcVar2 = Sekai.setArray(pcVar2,2,3,dec);
dec = Sekai.decrypt(objectRef[1],1);
pcVar2 = Sekai.setArray(pcVar2,1,4,dec);
dec = Sekai.decrypt(objectRef[0],2);
pcVar2 = Sekai.setArray(pcVar2,0,5,dec);
var iVar8 = Sekai.length / 2;
var iVar9,uVar1;
while( true ) {
if (iVar8<0) break;
iVar9 = Sekai.length + iVar8 * -2 -2;
while( true ) {
if (iVar9<0) break;
uVar1 = pcVar2[iVar8 + iVar9][Sekai.length -1 - iVar8];
pcVar2[iVar8 + iVar9][Sekai.length -1 - iVar8] = pcVar2[Sekai.length -1 - iVar8][Sekai.length -1 - iVar8 - iVar9];
pcVar2[Sekai.length -1 - iVar8][Sekai.length -1 - iVar8 - iVar9] = pcVar2[Sekai.length -1 - iVar8 - iVar9][iVar8]
pcVar2[Sekai.length -1 - iVar8 - iVar9][iVar8] = pcVar2[iVar8][iVar8 + iVar9];
pcVar2[iVar8][iVar8 + iVar9] = uVar1;
iVar9 = iVar9 - 1;
}
iVar8 = iVar8 - 1;
}
return Sekai.antitransform(pcVar2,Sekai.length);
}
document.writeln("Sekai{"+Sekai.desolve(0)+"}");
Sekai{m4tr1x_d3cryP710N_15_Fun_M4T3_@2D2D!}