CATCTF-Reverse-CatFly Writeup
Analysis
查看文件类型
~/CTF_Challenges/Reverse/CatFly$ file CatFly
CatFly: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=d05baf98f7d937f503c87ced28e78998e8d3046b, for GNU/Linux 3.2.0, stripped
main函数IDA伪代码如下:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
size_t v3; // rbx
_WORD v5[4]; // [rsp+10h] [rbp-4B0h] BYREF
time_t time1; // [rsp+18h] [rbp-4A8h] BYREF
time_t timer; // [rsp+20h] [rbp-4A0h] BYREF
int longind; // [rsp+2Ch] [rbp-494h] BYREF
_QWORD s[129]; // [rsp+30h] [rbp-490h] BYREF
int v10; // [rsp+43Ch] [rbp-84h]
double v11; // [rsp+440h] [rbp-80h]
char *v12; // [rsp+448h] [rbp-78h]
int v13; // [rsp+450h] [rbp-70h]
unsigned int v14; // [rsp+454h] [rbp-6Ch]
unsigned __int8 v15; // [rsp+45Ah] [rbp-66h]
char v16; // [rsp+45Bh] [rbp-65h]
int n; // [rsp+45Ch] [rbp-64h]
int v18; // [rsp+460h] [rbp-60h]
char v19; // [rsp+467h] [rbp-59h]
int m; // [rsp+468h] [rbp-58h]
int k; // [rsp+46Ch] [rbp-54h]
char v22; // [rsp+473h] [rbp-4Dh]
int v23; // [rsp+474h] [rbp-4Ch]
unsigned __int64 v24; // [rsp+478h] [rbp-48h]
int v25; // [rsp+484h] [rbp-3Ch]
int v26; // [rsp+488h] [rbp-38h]
int v27; // [rsp+48Ch] [rbp-34h]
char v28; // [rsp+490h] [rbp-30h]
bool v29; // [rsp+491h] [rbp-2Fh]
unsigned __int16 v30; // [rsp+492h] [rbp-2Eh]
int v31; // [rsp+494h] [rbp-2Ch]
unsigned int v32; // [rsp+498h] [rbp-28h]
unsigned int i; // [rsp+49Ch] [rbp-24h]
int v34; // [rsp+4A0h] [rbp-20h]
unsigned int j; // [rsp+4A4h] [rbp-1Ch]
char *haystack; // [rsp+4A8h] [rbp-18h]
haystack = 0;
i = 0;
v32 = 0;
v31 = 0;
memset(s, 0, 1024);
v30 = 0;
v29 = 0;
v28 = 0;
v27 = 90;
while ( 1 )
{
v26 = getopt_long(a1, a2, "eshiItnd:f:r:R:c:C:W:H:", &longopts, &longind);
if ( v26 == -1 )
break;
if ( !v26 && !*((_QWORD *)&longopts.flag + 4 * longind) )
v26 = *(&longopts.val + 8 * longind);
switch ( v26 )
{
case 'C':
dword_E1F8 = atoi(optarg);
break;
case 'H':
dword_E1EC = (64 - atoi(optarg)) / 2;
dword_E1F0 = (atoi(optarg) + 64) / 2;
break;
case 'I':
v28 = 1;
break;
case 'R':
dword_E1F0 = atoi(optarg);
break;
case 'W':
dword_E1F4 = (64 - atoi(optarg)) / 2;
dword_E1F8 = (atoi(optarg) + 64) / 2;
break;
case 'c':
dword_E1F4 = atoi(optarg);
break;
case 'd':
if ( atoi(optarg) > 9 && atoi(optarg) <= 1000 )
v27 = atoi(optarg);
break;
case 'e':
dword_E104 = 0;
break;
case 'f':
dword_104C4 = atoi(optarg);
break;
case 'h':
sub_67F0(a2);
exit(0);
case 'i':
v29 = 1;
break;
case 'r':
dword_E1EC = atoi(optarg);
break;
case 's':
dword_E108 = 0;
break;
case 't':
dword_104C0 = 1;
break;
default:
continue;
}
}
if ( dword_104C0 )
{
v29 = v28 == 0;
sub_6669();
for ( i = 0; i <= 0xFF; ++i )
{
if ( *((_BYTE *)&unk_104E0 + i) )
{
sub_66AF(*((unsigned __int8 *)&unk_104E0 + i), i);
fflush(stdout);
}
}
for ( i = 0; i <= 0xFF; ++i )
{
if ( *((_BYTE *)&unk_105E0 + i) )
{
sub_66AF(*((unsigned __int8 *)&unk_105E0 + i), i);
fflush(stdout);
}
}
signal(14, sub_64C0);
if ( !_setjmp(env) )
{
alarm(1u);
while ( !feof(stdin) && v32 <= 1 )
{
v16 = getchar();
v15 = 0;
if ( v16 == -1 )
{
v16 = getchar();
switch ( v16 )
{
case -16:
v31 = 0;
if ( LOBYTE(s[0]) == 24 )
{
alarm(2u);
haystack = strndup((const char *)s + 2, 0x3FEu);
++v32;
}
else if ( LOBYTE(s[0]) == 31 )
{
alarm(2u);
dword_E1FC = (BYTE1(s[0]) << 8) | BYTE2(s[0]);
dword_E200 = (BYTE3(s[0]) << 8) | BYTE4(s[0]);
++v32;
}
break;
case -15:
sub_66AF(241, 0);
fflush(stdout);
break;
case -6:
v31 = 1;
v30 = 0;
memset(s, 0, 0x400u);
break;
case -5:
case -4:
v15 = getchar();
if ( !*((_BYTE *)&unk_105E0 + v15) )
*((_BYTE *)&unk_105E0 + v15) = -4;
sub_66AF(*((unsigned __int8 *)&unk_105E0 + v15), v15);
fflush(stdout);
if ( v16 == -5 && v15 == 24 )
{
printf("%c%c%c%c%c%c", 255, 250, 24, 1, 255, 240);
fflush(stdout);
}
break;
case -3:
case -2:
v15 = getchar();
if ( !*((_BYTE *)&unk_104E0 + v15) )
*((_BYTE *)&unk_104E0 + v15) = -2;
sub_66AF(*((unsigned __int8 *)&unk_104E0 + v15), v15);
fflush(stdout);
break;
case -1:
v32 = 2;
break;
default:
continue;
}
}
else if ( v31 && v30 <= 0x3FEu )
{
*((_BYTE *)s + v30++) = v16;
}
}
}
alarm(0);
}
else
{
haystack = getenv("TERM");
ioctl(0, 0x5413u, v5);
dword_E1FC = v5[1];
dword_E200 = v5[0];
}
v34 = 2;
if ( haystack )
{
for ( j = 0; ; ++j )
{
v3 = j;
if ( v3 >= strlen(haystack) )
break;
haystack[j] = tolower(haystack[j]);
}
if ( strstr(haystack, "xterm") )
{
v34 = 1;
}
else if ( strstr(haystack, "toaru") )
{
v34 = 1;
}
else if ( strstr(haystack, "linux") )
{
v34 = 3;
}
else if ( strstr(haystack, "vtnt") )
{
v34 = 5;
}
else if ( strstr(haystack, "cygwin") )
{
v34 = 5;
}
else if ( strstr(haystack, "vt220") )
{
v34 = 6;
}
else if ( strstr(haystack, "fallback") )
{
v34 = 4;
}
else if ( strstr(haystack, "rxvt-256color") )
{
v34 = 1;
}
else if ( strstr(haystack, "rxvt") )
{
v34 = 3;
}
else if ( strstr(haystack, "vt100") && dword_E1FC == 40 )
{
v34 = 7;
}
else if ( !strncmp(haystack, "st", 2u) )
{
v34 = 1;
}
}
v25 = 0;
signal(2, sub_64A8);
signal(13, sub_64E6);
if ( !dword_104C0 )
signal(28, handler);
switch ( v34 )
{
case 1:
qword_FE20 = (__int64)"\x1B[48;5;17m";
qword_FE30 = (__int64)"\x1B[48;5;231m";
qword_FDF8 = (__int64)"\x1B[48;5;16m";
qword_FEC0 = (__int64)"\x1B[48;5;230m";
qword_FDE0 = (__int64)"\x1B[48;5;175m";
qword_FE28 = (__int64)"\x1B[48;5;162m";
qword_FEB0 = (__int64)"\x1B[48;5;196m";
qword_FDF0 = (__int64)"\x1B[48;5;214m";
qword_FE18 = (__int64)"\x1B[48;5;226m";
qword_FDD8 = (__int64)"\x1B[48;5;118m";
qword_FEA8 = (__int64)"\x1B[48;5;33m";
qword_FE98 = (__int64)"\x1B[48;5;19m";
qword_FE10 = (__int64)"\x1B[48;5;240m";
qword_FDE8 = (__int64)"\x1B[48;5;175m";
break;
case 2:
qword_FE20 = (__int64)"\x1B[104m";
qword_FE30 = (__int64)"\x1B[107m";
qword_FDF8 = (__int64)"\x1B[40m";
qword_FEC0 = (__int64)"\x1B[47m";
qword_FDE0 = (__int64)"\x1B[105m";
qword_FE28 = (__int64)"\x1B[101m";
qword_FEB0 = (__int64)"\x1B[101m";
qword_FDF0 = (__int64)"\x1B[43m";
qword_FE18 = (__int64)"\x1B[103m";
qword_FDD8 = (__int64)"\x1B[102m";
qword_FEA8 = (__int64)"\x1B[104m";
qword_FE98 = (__int64)"\x1B[44m";
qword_FE10 = (__int64)"\x1B[100m";
qword_FDE8 = (__int64)"\x1B[105m";
break;
case 3:
qword_FE20 = (__int64)"\x1B[25;44m";
qword_FE30 = (__int64)"\x1B[5;47m";
qword_FDF8 = (__int64)"\x1B[25;40m";
qword_FEC0 = (__int64)"\x1B[5;47m";
qword_FDE0 = (__int64)"\x1B[5;45m";
qword_FE28 = (__int64)"\x1B[5;41m";
qword_FEB0 = (__int64)"\x1B[5;41m";
qword_FDF0 = (__int64)"\x1B[25;43m";
qword_FE18 = (__int64)"\x1B[5;43m";
qword_FDD8 = (__int64)"\x1B[5;42m";
qword_FEA8 = (__int64)"\x1B[25;44m";
qword_FE98 = (__int64)"\x1B[5;44m";
qword_FE10 = (__int64)"\x1B[5;40m";
qword_FDE8 = (__int64)"\x1B[5;45m";
break;
case 4:
qword_FE20 = (__int64)"\x1B[0;34;44m";
qword_FE30 = (__int64)"\x1B[1;37;47m";
qword_FDF8 = (__int64)"\x1B[0;30;40m";
qword_FEC0 = (__int64)"\x1B[1;37;47m";
qword_FDE0 = (__int64)"\x1B[1;35;45m";
qword_FE28 = (__int64)"\x1B[1;31;41m";
qword_FEB0 = (__int64)"\x1B[1;31;41m";
qword_FDF0 = (__int64)"\x1B[0;33;43m";
qword_FE18 = (__int64)"\x1B[1;33;43m";
qword_FDD8 = (__int64)"\x1B[1;32;42m";
qword_FEA8 = (__int64)"\x1B[1;34;44m";
qword_FE98 = (__int64)"\x1B[0;34;44m";
qword_FE10 = (__int64)"\x1B[1;30;40m";
qword_FDE8 = (__int64)"\x1B[1;35;45m";
off_FA88 = (char *)&unk_BCFF;
break;
case 5:
qword_FE20 = (__int64)"\x1B[0;34;44m";
qword_FE30 = (__int64)"\x1B[1;37;47m";
qword_FDF8 = (__int64)"\x1B[0;30;40m";
qword_FEC0 = (__int64)"\x1B[1;37;47m";
qword_FDE0 = (__int64)"\x1B[1;35;45m";
qword_FE28 = (__int64)"\x1B[1;31;41m";
qword_FEB0 = (__int64)"\x1B[1;31;41m";
qword_FDF0 = (__int64)"\x1B[0;33;43m";
qword_FE18 = (__int64)"\x1B[1;33;43m";
qword_FDD8 = (__int64)"\x1B[1;32;42m";
qword_FEA8 = (__int64)"\x1B[1;34;44m";
qword_FE98 = (__int64)"\x1B[0;34;44m";
qword_FE10 = (__int64)"\x1B[1;30;40m";
qword_FDE8 = (__int64)"\x1B[1;35;45m";
off_FA88 = (char *)&unk_BD06;
break;
case 6:
qword_FE20 = (__int64)&unk_BD09;
qword_FE30 = (__int64)&unk_BD0C;
qword_FDF8 = (__int64)" ";
qword_FEC0 = (__int64)&unk_BD0F;
qword_FDE0 = (__int64)&unk_BD12;
qword_FE28 = (__int64)&unk_BD15;
qword_FEB0 = (__int64)&unk_BD0F;
qword_FDF0 = (__int64)&unk_BD18;
qword_FE18 = (__int64)&unk_BD1B;
qword_FDD8 = (__int64)&unk_BD1E;
qword_FEA8 = (__int64)&unk_BD21;
qword_FE98 = (__int64)&unk_BD24;
qword_FE10 = (__int64)&unk_BD27;
qword_FDE8 = (__int64)&unk_BD2A;
v25 = 1;
break;
case 7:
qword_FE20 = (__int64)&unk_BD2D;
qword_FE30 = (__int64)&unk_BD2F;
qword_FDF8 = (__int64)&unk_BD31;
qword_FEC0 = (__int64)&unk_BD33;
qword_FDE0 = (__int64)&unk_BD35;
qword_FE28 = (__int64)&unk_BD37;
qword_FEB0 = (__int64)&unk_BD33;
qword_FDF0 = (__int64)&unk_BD39;
qword_FE18 = (__int64)&unk_BD3B;
qword_FDD8 = (__int64)&unk_BD3D;
qword_FEA8 = (__int64)&unk_BD3F;
qword_FE98 = (__int64)&unk_BD41;
qword_FE10 = (__int64)&unk_BD43;
qword_FDE8 = (__int64)&unk_BD45;
v25 = 1;
dword_E1FC = 40;
break;
default:
break;
}
if ( dword_E1F4 == dword_E1F8 )
{
dword_E1F4 = (dword_E1FC / -2 + 64) / 2;
dword_E1F8 = (dword_E1FC / 2 + 64) / 2;
byte_104CB = 1;
}
if ( dword_E1EC == dword_E1F0 )
{
dword_E1EC = (65 - dword_E200) / 2;
dword_E1F0 = (dword_E200 + 63) / 2;
byte_104CC = 1;
}
if ( dword_E108 )
{
printf("\x1BkNyanyanyanyanyanyanya...\x1B\\");
printf("\x1B]1;Nyanyanyanyanyanyanya...\a");
printf("\x1B]2;Nyanyanyanyanyanyanya...\a");
}
if ( dword_E104 )
printf("\x1B[H\x1B[2J\x1B[?25l");
else
printf("\x1B[s");
if ( v29 )
{
v14 = 5;
for ( j = 0; j < v14; ++j )
{
sub_65E2(3);
printf(" \x1B[1mNyancat Telnet Server\x1B[0m");
sub_65E2(2);
printf(" written and run by \x1B[1;32mK. Lange\x1B[1;34m @_klange\x1B[0m");
sub_65E2(2);
printf(" If things don't look right, try:");
sub_65E2(1);
printf(" TERM=fallback telnet ...");
sub_65E2(2);
printf(" Or on Windows:");
sub_65E2(1);
printf(" telnet -t vtnt ...");
sub_65E2(2);
printf(" Problems? Check the website:");
sub_65E2(1);
printf(" \x1B[1;34mhttp://nyancat.dakko.us\x1B[0m");
sub_65E2(2);
printf(" This is a telnet server, remember your escape keys!");
sub_65E2(1);
printf(" \x1B[1;31m^]quit\x1B[0m to exit");
sub_65E2(2);
printf(" Starting in %d... \n", v14 - j);
fflush(stdout);
usleep(0x61A80u);
if ( dword_E104 )
printf("\x1B[H");
else
printf("\x1B[u");
}
if ( dword_E104 )
printf("\x1B[H\x1B[2J\x1B[?25l");
}
time(&timer);
v13 = 1;
v24 = 0;
v23 = 0;
v22 = 0;
v12 = off_FA88;
while ( v13 )
{
if ( dword_E104 )
printf("\x1B[H");
else
printf("\x1B[u");
for ( k = dword_E1EC; k < dword_E1F0; ++k )
{
for ( m = dword_E1F4; m < dword_E1F8; ++m )
{
if ( k <= 23 || k > 42 || m >= 0 )
{
if ( m >= 0 && (unsigned int)k < 0x40 && m <= 63 )
{
v19 = off_FA20[v24][k][m];
off_FA88 = (char *)sub_6314((unsigned int)v24, (unsigned int)k, (unsigned int)m, v12);
}
else
{
v19 = 44;
}
}
else
{
v18 = (2 - m) % 16 / 8;
if ( ((v24 >> 1) & 1) != 0 )
v18 = 1 - v18;
s[128] = ",,>>&&&+++###==;;;,,";
v19 = asc_BFE3[v18 - 23 + k];
if ( !v19 )
v19 = 44;
}
if ( v25 )
{
printf("%s", *((const char **)&unk_FCC0 + v19));
}
else if ( v19 == v22 || !*((_QWORD *)&unk_FCC0 + v19) )
{
printf("%s", off_FA88);
}
else
{
v22 = v19;
printf("%s%s", *((const char **)&unk_FCC0 + v19), off_FA88);
}
}
sub_65E2(1);
}
if ( dword_E100 )
{
time(&time1);
v11 = difftime(time1, timer);
v10 = sub_63FF((unsigned int)(int)v11);
for ( n = (dword_E1FC - 29 - v10) / 2; n > 0; --n )
putchar(32);
dword_E1E8 += printf("\x1B[1;37mYou have nyaned for %d times!\x1B[J\x1B[0m", ++dword_108E0);
}
v22 = 0;
++v23;
if ( dword_104C4 && v23 == dword_104C4 )
sub_6471();
if ( !off_FA20[++v24] )
v24 = 0;
usleep(1000 * v27);
}
return 0;
}
注意到这一部分,printf("%s", off_FA88);
这里我们进一步跟进 off_FA88
看到了对它的赋值操作 off_FA88 = sub_6314((unsigned int)v24, k, m, (__int64)v12);
此时我们查看sub_6314
的源代码
if ( m >= 0 && (unsigned int)k < 0x40 && m <= 63 )
{
v19 = off_FA20[v24][k][m];
off_FA88 = (char *)sub_6314((unsigned int)v24, (unsigned int)k, (unsigned int)m, v12);
}
else
{
v19 = 44;
以下是sub_6314
的代码:
char *__fastcall sub_6314(__int64 a1, int a2, int a3, __int64 a4)
{
if ( a2 != 18 )
return (char *)a4;
if ( a3 <= 4 || a3 > 54 )
return (char *)a4;
byte_104C9 = 32;
dword_E120[a3 - 5] ^= sub_62B5();
if ( (unsigned __int8)sub_62E3(dword_E120[a3 - 5]) )
byte_104C8 = dword_E120[a3 - 5] & 0x7F;
else
byte_104C8 = 32;
return &byte_104C8;
}
sub_62B5
__int64 sub_62B5()
{
dword_E1E8 = 1103515245 * dword_E1E8 + 12345;
return (dword_E1E8 >> 10) & 0x7FFF;
}
sub_62E3
_BOOL8 __fastcall sub_62E3(char a1)
{
return (a1 & 0x7Fu) <= 0x7E && (a1 & 0x7Fu) > 0x20;
}
dword_E120
.data:000000000000E120 dword_E120 dd 27FBh, 27A4h, 464Eh, 0E36h, 7B70h, 5E7Ah, 1A4Ah, 45C1h
.data:000000000000E120 ; DATA XREF: sub_6314+5C↑o
.data:000000000000E120 ; sub_6314+7A↑o ...
.data:000000000000E140 dd 2BDFh, 23BDh, 3A15h, 5B83h, 1E15h, 5367h, 50B8h, 20CAh
.data:000000000000E160 dd 41F5h, 57D1h, 7750h, 2ADFh, 11F8h, 9BBh, 5724h, 7374h
.data:000000000000E180 dd 3CE6h, 646Eh, 10Ch, 6E10h, 64F4h, 3263h, 3137h, 0B8h
.data:000000000000E1A0 dd 229Ch, 7BCDh, 73BDh, 480Ch, 14DBh, 68B9h, 5C8Ah, 1B61h
.data:000000000000E1C0 dd 6C59h, 5707h, 9E6h, 1FB9h, 2AD3h, 76D4h, 3113h, 7C7Eh
.data:000000000000E1E0 dd 11E0h, 6C70h
结合上述思路,那么可以发现这个程序应该逆向的顺序为:
off_FA88->dword_108E0->dword_E1E8
Exploit
dword_E1E8 = 0x1106
dword_E120 = [
0x27fb, 0x27a4, 0x464e, 0x0e36, 0x7b70, 0x5e7a, 0x1a4a, 0x45c1,
0x2bdf, 0x23bd, 0x3a15, 0x5b83, 0x1e15, 0x5367, 0x50b8, 0x20ca,
0x41f5, 0x57d1, 0x7750, 0x2adf, 0x11f8, 0x09bb, 0x5724, 0x7374,
0x3ce6, 0x646e, 0x010c, 0x6e10, 0x64f4, 0x3263, 0x3137, 0x00b8,
0x229c, 0x7bcd, 0x73bd, 0x480c, 0x14db, 0x68b9, 0x5c8a, 0x1b61,
0x6c59, 0x5707, 0x09e6, 0x1fb9, 0x2ad3, 0x76d4, 0x3113, 0x7c7e,
0x11e0, 0x6c70
]
def sub_62B5():
global dword_E1E8
dword_E1E8 = (1103515245 * dword_E1E8 + 12345) & 0xFFFFFFFF
return (dword_E1E8 >> 10) & 0x7FFF
def llog(n):
a = 0
while n >= 10:
n //= 10
a += 1
return a
def sub_62E3(a1):
return 0x7E >= (a1 & 0x7F) > 0x20
count = 0
while True:
for i in range(50):
dword_E120[i] ^= sub_62B5()
count += 1
dword_E1E8 += 42 + llog(count)
if count % 1000000 == 0:
print(f"Count: {count}")
flag = bytearray(51)
for i in range(50):
if not sub_62E3(dword_E120[i]):
break
flag[i] = dword_E120[i] & 0xFF
else:
continue
if flag[:6] == b'CatCTF':
print(flag.decode())
print(f"Count: {count}")
break
~/Git-Projects/CTF_Challenges/Reverse/CatFly$ python3 exp.py
Count: 1000000
Count: 2000000
Count: 3000000
Count: 4000000
Count: 5000000
Count: 6000000
Count: 7000000
Count: 8000000
Count: 9000000
Count: 10000000
Count: 11000000
Count: 12000000
Count: 13000000
Count: 14000000
Count: 15000000
Count: 16000000
Count: 17000000
Count: 18000000
Count: 19000000
Count: 20000000
Count: 21000000
Count: 22000000
Count: 23000000
Count: 24000000
Count: 25000000
Count: 26000000
Count: 27000000
Count: 28000000
Count: 29000000
Count: 30000000
Count: 31000000
Count: 32000000
Count: 33000000
Count: 34000000
Count: 35000000
Count: 36000000
Count: 37000000
Count: 38000000
Count: 39000000
Count: 40000000
Count: 41000000
Count: 42000000
Count: 43000000
Count: 44000000
Count: 45000000
Count: 46000000
Count: 47000000
Count: 48000000
Count: 49000000
Count: 50000000
Count: 51000000
Count: 52000000
Count: 53000000
Count: 54000000
Count: 55000000
Count: 56000000
Count: 57000000
Count: 58000000
Count: 59000000
Count: 60000000
Count: 61000000
Count: 62000000
Count: 63000000
Count: 64000000
Count: 65000000
Count: 66000000
Count: 67000000
Count: 68000000
Count: 69000000
Count: 70000000
Count: 71000000
Count: 72000000
Count: 73000000
Count: 74000000
Count: 75000000
Count: 76000000
Count: 77000000
Count: 78000000
Count: 79000000
Count: 80000000
Count: 81000000
Count: 82000000
Count: 83000000
Count: 84000000
Count: 85000000
Count: 86000000
Count: 87000000
Count: 88000000
Count: 89000000
Count: 90000000
Count: 91000000
Count: 92000000
Count: 93000000
Count: 94000000
Count: 95000000
Count: 96000000
Count: 97000000
Count: 98000000
Count: 99000000
Count: 100000000
CatCTF{Fly1NG_NyAnC4t_Cha5eS_the_FL4G_in_The_Sky}
Count: 100001958
CATCTF-Reverse-CatFly Writeup
https://zer0ptr.github.io/2025/08/26/CATCTF-RE-CatFly/