数据分析

server_logs

Challenge

某服务器在2023-06-15 02:00-04:00期间被入侵,请分析日志并回答:

  1. 攻击者使用的SSH用户名和IP
  2. 植入的恶意服务名称
  3. 泄露机密文件时使用的DNS域名

提交形式:flag{SSH用户名_IP_恶意服务名称(不包括后缀)_DNS域名(固定部分)}

Solution

/var/log/auth.log的第201行找到攻击者使用的SSH用户名和IP分别是attacker192.168.42.77

text
Jun 15 02:30:15 server sshd[5678]: Accepted password for attacker from 192.168.42.77 port 1337

/var/log/syslog的第501-502行找到植入的恶意服务名称是hidden_backdoor

text
Jun 15 02:35:15 server systemd[1]: Started hidden_backdoor.serviceJun 15 02:35:15 server hidden_backdoor: listening on [any] 31337 ...

/var/log/dnsmasq.log的第601-604行找到泄露机密文件时使用的DNS域名为data.leak.ev

text
Jun 15 02:40:15 dnsmasq[123]: query[A] CiAgICByb290Oio6MTk0Nzk6MDo5OTk5OTo3Ojo6.data.leak.ev from 192.168.42.77Jun 15 02:40:17 dnsmasq[123]: query[A] CmRhZW1vbjoqOjE5NDc5OjA6OTk5OTk6Nzo6Ogph.data.leak.ev from 192.168.42.77Jun 15 02:40:19 dnsmasq[123]: query[A] dHRhY2tlcjokNiRzZWNyZXQkZW5jcnlwdGVkcGFz.data.leak.ev from 192.168.42.77Jun 15 02:40:21 dnsmasq[123]: query[A] c3dvcmQ6MTk0Nzk6MDo5OTk5OTo3Ojo6CiAgICA.data.leak.ev from 192.168.42.77

拼起来得到flag

text
flag{attacker_192.168.42.77_hidden_backdoor_data.leak.ev}

flowzip2[复现]

Challenge

There are many encrypted zip files.

Solution

从流量包导出200个加密方式为AES的.zip压缩包

在压缩包的备注发现提示\d{3},推测密码是三位数的数字,用ARCHPR爆一下发现还真是

写脚本尝试从000到999的三位数字密码来暴力破解并解压指定目录下名为 000.zip 至 199.zip 的加密压缩包

python
# -*- coding: utf-8 -*- import osimport pyzipperfrom tqdm import tqdmimport time # --- 配置区 ---# 使用原始字符串 (r"...") 来避免路径中的反斜杠问题ZIP_DIRECTORY = r"E:\Desktop\flowzip2_fe9d6d2cf5a6fe83f1d7f0fe3c60fa25"# --- 配置区结束 --- def generate_passwords():    """    生成所有可能的密码 (000, 001, ..., 999)    """    # 使用 f-string 和 {:03d} 格式化来生成三位数的字符串,不足的前面补0    return [f"{i:03d}" for i in range(1000)] def crack_zips():    """    爆破和解压ZIP文件    """    if not os.path.isdir(ZIP_DIRECTORY):        print(f"错误:目录'{ZIP_DIRECTORY}'不存在。请检查路径是否正确。")        return     # 在主函数开始时生成密码列表并打印提示    print("正在生成密码列表 (000-999)...")    passwords = generate_passwords()    print(f"密码列表生成完毕,共 {len(passwords)} 个。")    print(f"开始处理目录'{ZIP_DIRECTORY}'中的ZIP文件...\n")     # 使用 with 语句管理 tqdm 实例,确保进度条在结束或异常时能被正确关闭    # bar_format 自定义了进度条的显示格式,{l_bar} 是左边的描述文本    with tqdm(range(200),              desc="[+] 准备开始...",              bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]") as pbar:        for i in pbar:            file_number_str = f"{i:03d}"            zip_filename = f"{file_number_str}.zip"            zip_filepath = os.path.join(ZIP_DIRECTORY, zip_filename)             if not os.path.exists(zip_filepath):                # 更新描述信息,提示文件不存在                pbar.set_description(f"[-] {zip_filename}不存在,已跳过")                continue                        found_password = False            # 遍历所有可能的密码            for password in passwords:                try:                    # 尝试用当前密码打开和解压ZIP文件                    # 密码需要被编码成bytes                    with pyzipper.AESZipFile(zip_filepath, 'r') as zf:                        zf.extractall(path=ZIP_DIRECTORY, pwd=password.encode('utf-8'))                                        # 密码正确,更新tqdm的描述信息                    # 这会覆盖上一条信息,实现动态更新的效果                    pbar.set_description(f"[+] {zip_filename}已解压,密码是'{password}'")                    found_password = True                    break # 密码已找到,跳出内部的密码循环                 except RuntimeError as e:                    # pyzipper在密码错误时会抛出RuntimeError                    if 'Bad password' in str(e):                        continue # 密码不对,继续尝试下一个密码                    else:                        # 其他运行时错误,更新描述并跳出密码循环                        pbar.set_description(f"[!] 处理{zip_filename}时发生运行时错误")                        break                                except Exception:                    # 捕获其他可能的异常                    pbar.set_description(f"[!] 处理{zip_filename}时发生意外错误")                    break                        if not found_password:                # 如果遍历完所有密码都未成功,更新描述信息                pbar.set_description(f"[-] 未找到{zip_filename}的密码")     # 在所有文件处理完毕后,光标移到新的一行,并打印最终的完成信息    print("\n--- 所有文件处理完毕 ---") if __name__ == "__main__":    crack_zips()

最后在138.zip(解压密码是853)中的dcsxr.txt找到flag

text
flag{5f5491b6-fddf-4be8-ab44-5a18831cc45b}

密码破解

xxtea

Challenge

It’s getting hard to even copy the data this time.

Solution

没啥好说的,照猫画虎就是了From Hexdump, XXTEA Decrypt - CyberChef

text
00000000 9e 45 02 82 ea 25 d2 62 0d 06 e7 b4 5f dc 62 bd |.E...%.b...._.b.|00000010 39 68 47 26 54 50 9e 26 4c 86 06 2e 2b af 3b 8b |9hG&TP.&L...+.;.|00000020 7c e4 99 0d ad 12 e2 7e 46 c9 00 29 a6 ea 1f b0 ||......~F..)....|00000030 ae c1 da 30 f3 98 94 82 33 fd 99 d3 d3 b9 a0 12 |...0....3.......|00000040 d9 de a5 d2 0e 95 f3 a5 bb f2 f9 91 b2 a3 c2 94 |................|00000050 b1 1c b7 eb 87 65 d7 0e 0c 6b d5 65 4d e1 43 1e |.....e...k.eM.C.|00000060 be b4 34 71 b5 53 d4 ea 62 4e b9 80 f3 6f f0 5c |..4q.S..bN...o.\|00000070 59 75 58 52 9e a4 ec e2 35 b7 d8 68 27 ee c0 94 |YuXR....5..h'...|
text
flag{4eb88a16-be48-4de2-ab2a-ed09a09ed386}

fastcoll

Challenge

这是一个MD5碰撞挑战:你能找到两个不同文件,但却拥有相同的MD5值吗?

Solution

题目要求有相同前缀 gamelab,因此先在当前目录下新建内容为 gamelab 的文件 prefix.txt

然后运行得到两个文件

text
fastcoll_v1.0.0.5.exe -p prefix -o msg1.bin msg2.bin

用厨子分别编码一下然后交上去就行

text
Z2FtZWxhYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgJW38EhH3hRbFIaBQ1pJrGjBHm7YrS5TlmOI0uqIkmIeLjwF5kFHpLMMBS9qHZu8CskXE6WmVGDv5KV/BJywYUt9xfoI2x/8k8ldPje4Yyx6N3WzwZgr8tsyDXKmCoxtcr9AOvCI4e+v6BkkqwGwu2OQlPh8TpgdH0Mq0Cic8m
text
Z2FtZWxhYgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgJW38EhH3hRbFIaBQ1pJrGjBFm7YrS5TlmOI0uqIkmIeLjwF5kFHpLMMBS9iHau8CskXE6WmVGDv5K1/BJywYUt9xfoI2x/8k8ldPje4Yyx6N32zwZgr8tsyDXKmCoxtcr9AOvCI4e+v6BkkowGwu2OQlPh8TpgdH0Mi0Cic8m

逆向分析

encodefile[复现]

Challenge

分析一个用于加密明文的可执行程序,通过识别其加密逻辑成功解密生成的密文文件,恢复出原始数据内容。

Solution

这个程序的核心功能是读取 flag.txt,使用RC4加密算法对其内容进行加密,然后将加密后的结果写入一个名为 enc.dat 的文件

加密所使用的密钥是硬编码在程序中的字符串 key2025lqb

找到主函数 sub_40487D 并分析

c
__int64 sub_40487D(){  _QWORD *v1; // rax  unsigned int v2; // ebx  __int64 v3; // rbx  __int64 v4; // rax  _QWORD *v5; // rax  __int64 result; // rax  char v7; // [rsp+Fh] [rbp-481h] BYREF  __int64 v8[2]; // [rsp+10h] [rbp-480h] BYREF  __int64 v9[2]; // [rsp+20h] [rbp-470h] BYREF  _QWORD v10[4]; // [rsp+30h] [rbp-460h] BYREF  _QWORD v11[4]; // [rsp+50h] [rbp-440h] BYREF  _QWORD v12[31]; // [rsp+70h] [rbp-420h] BYREF  __int64 v13; // [rsp+168h] [rbp-328h] BYREF  _QWORD v14[32]; // [rsp+270h] [rbp-220h] BYREF  __int64 v15; // [rsp+370h] [rbp-120h] BYREF  unsigned __int64 ______Stack_Canary_; // [rsp+478h] [rbp-18h]   ______Stack_Canary_ = __readfsqword(0x28u);  ifstream((__int64)v14, (__int64)"flag.txt", 4);// 读取 "flag.txt"  ofstream((__int64)v12, (__int64)"enc.dat", 4);// 写入 "enc.dat"  if ( sub_44FB80((__int64)&v15) || sub_44FB80((__int64)&v13) )// 检查文件是否成功打开  {    v1 = sub_46F140(qword_5DD320, (__int64)&unk_578019);    sub_46DC30((__int64)v1, (__int64 (*)(void))sub_46EA20);// 如果文件打开失败,打印错误信息并退出    v2 = 1;  }  else                                          // 如果文件打开成功,执行核心加密逻辑  {    sub_407080();    sub_404E42((__int64)v9);    sub_404DF8((__int64)v8, v14);               // 从文件流 v14 ("flag.txt")读取数据到 v8    sub_404E6C((__int64)v10, v8[0], v8[1], v9[0], v9[1], (__int64)&v7);// 再将数据从 v8 转移到 v10    sub_4070A0();    sub_407080();    sub_404F50((__int64)v11, (__int64)"key2025lqb", (__int64)v9);// v11 是密钥结构,使用 "key2025lqb" 进行初始化    sub_404605((__int64)v10, (__int64)v11);     // 使用密钥 v11 对数据 v10 进行 RC4 加密    sub_475510(v11);    sub_4070A0();    v3 = sub_404DB4(v10);    v4 = sub_405014(v10);    sub_46E890(v12, v4, v3);                    // 将数据 (v4, v3) 写入文件流 v12 ("enc.dat")    v5 = sub_46F140(qword_5DD440, (__int64)&unk_578040);    sub_46DC30((__int64)v5, (__int64 (*)(void))sub_46EA20);    v2 = 0;    sub_404F08(v10);  }  sub_44C300(v12);  sub_44C440(v14);  result = v2;  if ( ______Stack_Canary_ != __readfsqword(0x28u) )    sub_52FBA0();  return result;}

分析加密函数 sub_404605

c
unsigned __int64 __fastcall sub_404605(_QWORD *a1, __int64 a2){  int v2; // r12d  unsigned __int64 v3; // rax  __int64 v4; // kr00_8  _BYTE *v5; // rax  unsigned __int64 result; // rax  char v7; // [rsp+17h] [rbp-139h]  int i; // [rsp+18h] [rbp-138h]  int v9; // [rsp+1Ch] [rbp-134h]  int v10; // [rsp+1Ch] [rbp-134h]  int j; // [rsp+20h] [rbp-130h]  int v12; // [rsp+24h] [rbp-12Ch]  unsigned __int64 k; // [rsp+28h] [rbp-128h]  _BYTE v14[264]; // [rsp+30h] [rbp-120h] BYREF  unsigned __int64 v15; // [rsp+138h] [rbp-18h]   v15 = __readfsqword(0x28u);  for ( i = 0; i <= 255; ++i )                  // 初始化S盒    v14[i] = i;  v9 = 0;  for ( j = 0; j <= 255; ++j )                  // 打乱S盒  {    v2 = (unsigned __int8)v14[j] + v9;    v3 = sub_475730(a2);    v9 = (v2 + *(char *)sub_475A40(a2, j % v3)) % 256;    sub_404D3D(&v14[j], &v14[v9]);              // swap(S[i], S[j])  }  v12 = 0;                                      // i  v10 = 0;                                      // j  for ( k = 0LL; k < sub_404DB4(a1); ++k )  {    v12 = (v12 + 1) % 256;                      // i = (i + 1) % 256    v4 = (unsigned __int8)v14[v12] + v10;    v10 = (unsigned __int8)(HIBYTE(v4) + v14[v12] + v10) - HIBYTE(HIDWORD(v4));// j = (j + S[i]) % 256    sub_404D3D(&v14[v12], &v14[v10]);           // swap(S[i], S[j])    v7 = v14[(unsigned __int8)(v14[v12] + v14[v10])];// 生成密钥流中的一个字节    v5 = (_BYTE *)sub_404DD8(a1, k);            // 获取第 k 个明文字节的地址    *v5 ^= v7;                                  // XOR  }  result = v15 - __readfsqword(0x28u);  if ( result )    sub_52FBA0();  return result;}

发现这是标准的RC4,密钥是硬编码在主函数中的key2025lqb,用厨子解就行RC4 - CyberChef

text
flag{db6007d2-9b1e-2f98-cef3-6595b63763dd}

rand_pyc

Challenge

对由Python打包生成的exe文件进行逆向处理,提取并还原出其核心源码,以便进一步分析程序逻辑并获得正确的输入。

Solution

解包反编译拿到代码

python
# Source Generated with Decompyle++# File: rand_pyc_obf.pyc (Python 3.8) import sysimport randomimport base64Ii = input('Please input the flag: ').strip()if not Ii.startswith('flag{') and Ii.endswith('}') or len(Ii) == 42:    print('Length incorrect')    sys.exit(-999)oo0O000ooO = base64.b64encode(Ii.encode()).decode() + '_easyctf'ii = []for iiI in oo0O000ooO:    random.seed(ord(iiI))    ii.append(random.randint(1000000, 9999999))iii111 = [    4417023,    5690625,    9639225,    1327718,    4417023,    5085550,    5752075,    9556690,    5240080,    6431679,    3428007,    3189766,    3438336,    5757818,    3189766,    5690625,    4148389,    2254831,    6292433,    2122126,    5240080,    6431679,    9488271,    2464675,    7216908,    5757818,    3189766,    5690625,    3438336,    6431679,    2360475,    6002055,    5240080,    9040261,    8655414,    9347278,    3438336,    2254831,    2122126,    5135281,    2360475,    9347278,    4417023,    1327718,    3438336,    3448715,    9488271,    5501611,    5240080,    5757818,    9488271,    5501611,    5240080,    9347278,    4148389,    1714134,    9923116,    4267438,    4263793,    5752075,    2464675,    7777627,    6002055,    3485900]Iio0 = []for iiI in oo0O000ooO:    random.seed(ord(iiI))    Iio0.append(random.randint(1000000, 9999999))if Iio0 != iii111:    print('Wrong flag')    sys.exit(-1)print('Correct!')

程序将输入的内容base64编码在尾部添加字符串_easyctf,然后分别将这些字符作为伪随机数的种子后生成一个数

由于种子是知道的因此这些随机数也是知道的,用base64的表制作一个对应的伪随机数的表,然后查表还原出密文,最后解码base64即可

python
import randomimport base64 iii111 = [    4417023,    5690625,    9639225,    1327718,    4417023,    5085550,    5752075,    9556690,    5240080,    6431679,    3428007,    3189766,    3438336,    5757818,    3189766,    5690625,    4148389,    2254831,    6292433,    2122126,    5240080,    6431679,    9488271,    2464675,    7216908,    5757818,    3189766,    5690625,    3438336,    6431679,    2360475,    6002055,    5240080,    9040261,    8655414,    9347278,    3438336,    2254831,    2122126,    5135281,    2360475,    9347278,    4417023,    1327718,    3438336,    3448715,    9488271,    5501611,    5240080,    5757818,    9488271,    5501611,    5240080,    9347278,    4148389,    1714134,    9923116,    4267438,    4263793,    5752075,    2464675,    7777627,    6002055,    3485900] list1 = list("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890+/=_") list2 = []for guess_flag in list1:    random.seed(ord(guess_flag))    list2.append(random.randint(1000000, 9999999))print(list2) flag_list = []for i in iii111:    if i in list2:        flag_list.append(list1[list2.index(i)])    else:        print(i)flag = "".join(flag_list[:-8])print(base64.b64decode(flag))
text
flag{30de99f4-50d2-9f8f-2868-dcfa9d81483c}