第五届红明谷 - MISC - WriteUp


碎碎念

第一次参加这个赛,因为一些原因,组好队了才知道只有web misc 和crypto方向的题,队伍里只有我一个对得上的,最后只能独自奋战了。不过比较好的是红明谷的题目难度还可以,不算难。misc这边全都是脚本题,有对应思路搞个脚本就做出来了(甚至直接gpt)
最后拿了个44名,还不错,下次数字中国积分赛争取能跟队友们冲个决赛qwq

异常行为溯源

知识点省流

本题考查流量取证

WP

用tshark把data.data导出,然后写脚本解码,最后统计哪个ip最多

tshark导出(有很多奇怪的协议导不出来,但其实不影响)

tshark -r network_traffic.pcapng -T fields -e data.data > data.txt

用脚本处理数据

import base64
import json

def hex_to_ascii(file_path, output_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as infile, open(output_path, 'w', encoding='utf-8') as outfile:
            for line in infile:
                line = line.strip()  # 去除换行符和空格
                try:
                    ascii_text = bytes.fromhex(line).decode('ascii')  # hex解码
                    base64_decoded_text = base64.b64decode(ascii_text).decode('utf-8')  # Base64 解码
                    json_data = json.loads(base64_decoded_text)  # 解析JSON数据
                    if 'msg' in json_data:
                        base64_encoded_msg = json_data['msg']  # 提取msg字段
                        final_decoded_msg = base64.b64decode(base64_encoded_msg).decode('utf-8')  # 再次Base64解码
                        outfile.write(final_decoded_msg + '\n')
                except (ValueError, base64.binascii.Error, UnicodeDecodeError, json.JSONDecodeError):
                    print(f"无法解码的行: {line}")
    except FileNotFoundError:
        print("文件未找到,请检查路径是否正确!")

# 示例用法
input_file = "data.txt"  # 你的hex编码数据文件
output_file = "decoded_output.txt"  # 解码后的输出文件
hex_to_ascii(input_file, output_file)
print("解码完成,结果已保存到", output_file)

导入linux,统计ip出现次数,最多那个就是flag

cat decoded_output.txt | awk '{print$1}' | sort | uniq -c | sort -nr | more

数据校验

知识点省流

本题考查写脚本

WP

1742653854156

要保证每一条都是合规,要注意ip也得检验(要符合xxx.xxx.xxx.xxx,xxx不能大于255)

import csv
import hashlib
import re
import ecdsa
import base64
import os


def md5_hash(value):
    return hashlib.md5(value.encode('utf-8')).hexdigest()


def verify_signature(serial_number, username, signature):
    try:
        # Ensure the public key file exists based on the serial_number
        public_key_file = os.path.join('ecdsa-key', f"{serial_number}.pem")

        if not os.path.isfile(public_key_file):
            raise FileNotFoundError(f"公钥文件未找到: {public_key_file}")

        # Read the public key from the file
        with open(public_key_file, 'r') as key_file:
            public_key = key_file.read()

        verifier = ecdsa.VerifyingKey.from_pem(public_key)
        signature_bytes = base64.b64decode(signature)
        verifier.verify(signature_bytes, username.encode('utf-8'))
        return True
    except (ecdsa.BadSignatureError, ValueError, FileNotFoundError) as e:
        print(f"验证签名失败: {e}")
        return False


def validate_row(index, row):
    errors = []

    # 校验 UserName 格式
    if not re.fullmatch(r'User-\w+', row[1]):
        errors.append("UserName 格式错误")

    # 校验 UserName_Check
    if row[2] != md5_hash(row[1]):
        errors.append("UserName_Check 错误")

    # 校验 Password 格式
    if not re.fullmatch(r'[A-Za-z0-9]+', row[3]):
        errors.append("Password 格式错误")

    # 校验 Password_Check
    if row[4] != md5_hash(row[3]):
        errors.append("Password_Check 错误")

    # 校验 Signature
    if not verify_signature(row[0],row[1], row[6]):  # Use Serial_Number for public key lookup
        errors.append("Signature 校验失败")

    # 校验 IP 格式
    ip = row[5]
    ip_pattern = r"^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
    if not re.match(ip_pattern, ip):
        errors.append("IP 格式错误")

    if errors:
        print(f"第 {index + 1} 行数据不合规: {', '.join(errors)}")


def read_csv(file_path):
    with open(file_path, mode='r', encoding='utf-8') as file:
        reader = csv.reader(file)
        headers = next(reader)  # 读取并跳过表头

        for index, row in enumerate(reader):
            validate_row(index, row)


# 示例调用
csv_file_path = "data.csv"  # 请将此路径更改为你的 CSV 文件路径
read_csv(csv_file_path)

Strange_Database

知识点省流

本题考查脚本小子,rc4加密,rsa加密 pem私钥解密

WP

500个db文件里的数据都进行了RSA加密,要写脚本解出来

同时每个pem文件都用openssl加密过,也要逐一解密

看了一下key里面的私钥,都是加密过的,数量太多只能用脚本解了

#!/usr/bin/env python3
import glob
import os
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend


def decrypt_pem_file(filename):
    # 读取加密的 PEM 文件
    with open(filename, "rb") as f:
        encrypted_key = f.read()

    # 从文件名中提取密码:取最后一个 '-' 后面的部分,去除扩展名
    base_name = os.path.basename(filename)
    password_with_ext = base_name.split('-')[-1]
    password = password_with_ext.rsplit('.', 1)[0]

    try:
        # 加载并解密 PEM 文件,password 需要为字节串
        private_key = serialization.load_pem_private_key(
            encrypted_key,
            password=password.encode(),
            backend=default_backend()
        )
    except Exception as e:
        print(f"文件 {filename} 解密失败:{e}")
        return None

    # 将解密后的私钥转换为不加密的 PEM 格式
    try:
        decrypted_key = private_key.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.TraditionalOpenSSL,
            encryption_algorithm=serialization.NoEncryption()
        )
    except Exception as e:
        print(f"序列化私钥时出错 {filename}: {e}")
        return None

    return decrypted_key


def batch_decrypt():
    output_dir = "decrypted"
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    # 遍历当前目录下所有符合 OAEP-*.pem 格式的文件
    pem_files = glob.glob("key/OAEP-*.pem")
    if not pem_files:
        print("未找到符合格式的 PEM 文件。")
        return

    counter = 0  # Initialize the counter

    for pem_file in pem_files:
        print(f"正在解密文件 {pem_file} ...")
        decrypted_key = decrypt_pem_file(pem_file)
        if decrypted_key:
            output_file = os.path.join(output_dir, f"OAEP-{counter}.pem")  # Use the counter for the output file name
            with open(output_file, "wb") as f:
                f.write(decrypted_key)
            print(f"文件 {pem_file} 解密成功,输出文件为 {output_file}\n")
            counter += 1  # Increment the counter for the next file
        else:
            print(f"文件 {pem_file} 解密失败。\n")


if __name__ == "__main__":
    batch_decrypt()

解完再读取db文件再去解密db里的数据

import glob
import os
import sqlite3
from time import sleep

from Crypto.PublicKey import RSA
from Crypto.Cipher import PKCS1_OAEP
import base64


def read_db_data(db_file):
    # 连接到 SQLite 数据库
    conn = sqlite3.connect(db_file)

    # 创建一个游标对象,用于执行 SQL 查询
    cursor = conn.cursor()

    try:
        # 执行查询,获取所有表的名字
        cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
        tables = cursor.fetchall()

        if tables:
            table_name = tables[0][0]  # 假设我们选择第一个表

            # 执行查询,读取表中的数据
            cursor.execute(f"SELECT * FROM {table_name};")
            rows = cursor.fetchall()

            decoded_data = []  # 用于存储所有解码的数据
            # 假设数据在第四列(索引为3)
            for row in rows:
                # 将解码后的数据加入列表
                decoded_data.append(base64.b64decode(row[0]))
            # print(decoded_data)
            return decoded_data  # 返回所有解码后的数据

    except sqlite3.Error as e:
        print(f"Error reading the database: {e}")
    finally:
        # 关闭连接
        conn.close()


def rsa_decrypt(private_key_path, ciphertext):
    # 从文件读取私钥
    with open(private_key_path, 'rb') as f:
        private_key_pem = f.read()

    private_key = RSA.import_key(private_key_pem)
    cipher = PKCS1_OAEP.new(private_key)

    # 解密
    decrypted_data = cipher.decrypt(ciphertext)
    return decrypted_data.decode()


def process_db_and_pem_files(db_directory, pem_directory, output_file):
    """处理所有的DB和PEM文件并将解密结果保存到一个txt文件中"""
    with open(output_file, 'wb') as out_file:
        for i in range(500):
            # DB文件路径
            db_file_path = os.path.join(db_directory, f'database-{i}.db')

            # 查找匹配的PEM文件,格式为 'OAEP-i-xxxxxx-decrypted'
            pem_file_pattern = os.path.join(pem_directory, f'OAEP-{i}-*.pem')
            pem_files = glob.glob(pem_file_pattern)

            if not pem_files:
                print(f"没有找到匹配的PEM文件: {pem_file_pattern}")
                continue

            # 选择找到的第一个PEM文件
            pem_file_path = pem_files[0]

            # 确保DB文件和PEM文件都存在
            if not os.path.exists(db_file_path) or not os.path.exists(pem_file_path):
                print(f"文件不存在: {db_file_path} 或 {pem_file_path}")
                continue

            # 读取DB文件的加密数据
            encrypted_data = read_db_data(db_file_path)

            # 解密数据
            try:
                for t in range(20):
                    decrypted_data = rsa_decrypt(pem_file_path, encrypted_data[t])
                    # print(decrypted_data)
                    # 将解密后的数据追加到输出文件
                    out_file.write(decrypted_data.encode())  # 将字符串转换为字节写入文件
                    out_file.write(b'\n')  # 可以加一个换行符分隔每个文件的内容


            except Exception as e:
                print(f"解密文件 {db_file_path} 时发生错误: {e}")


if __name__ == '__main__':
    db_directory = 'database'  # DB文件所在的目录
    pem_directory = 'decrypted'  # PEM文件所在的目录
    output_file = 'output/decrypted_data3.txt'  # 总的解密结果保存文件

    process_db_and_pem_files(db_directory, pem_directory, output_file)

将其中一db文件用在线db查看网站查看一下可以看到有如下字段

解密完所有数据后导出Type一列,再简单处理一下会发现里面有Enc和Key两个比较特别的用户,不难看出一个对应密文一个对应密钥

0184cfc6-ca3e-4241-a060-6e686c886686

猜测是某种带Key的加密,分别将他们其对应行号的Remark值拼接,最后用RC4解密即可

1742653947356

qwq