# 冀信杯 - 日志分析

# -*- coding: utf-8 -*-
# @Author  : 1cePeak
import re
import urllib.parse
f = open("newlog-2024.log","r")
lines = f.readlines()
datas = []
for line in lines:
    t = urllib.parse.unquote(line)
    if 'LIMIT' in t and 'security' in t and '722' in t and 'Nqtp' in t:
        datas.append(t)
flag_ascii = {}
for data in datas:
    matchOjb = re.search( r'0,1\),(\d+),.+>(\d+)', data)
    if matchOjb:
        # print(matchOjb.group(1),chr(int(matchOjb.group(2))+1))
        print("key",end=':')
        key = int(matchOjb.group(1))
        print(key)
        print("value",end=':')
        value = int(matchOjb.group(2))
        print(value)
        flag_ascii[key] = value
        print(flag_ascii)
flag = ""
for value in flag_ascii.values():
    flag += chr(value)
print(flag)
# 8ea72d948d9cceb0a12c34b1c06952606547fdd25d4f6ffad12ab42a858d37fc

二分查找的 SQL 盲注,返回长度 706 为 true ,722 为 false
找每一位最后出现的 722 长度的记录就是正确值

日志中查了几个表

  • security.flag : aBvW ,压缩包
  • security.aes : Nqtp 密钥, dnIk 偏移量
  • security.passwd : ybwv ,AES 加密后的压缩包密码

AES-CBC
key: 0133456789abbdaf0123446689abadbf
iv: 00000000000000000000000000000000

解压后再次解密 flag

Ly_Flag{oqv_%2d~ivw2855qdis5eo6_^b9orh}

# 冀信杯 - 数据窃取

冰蝎 shell 流量

找关键数据包,740 号,读取了 flag


对应的响应包解 base64

Ly_Flag{82874087f332fe4dffe3a4702632ac621841d2ce0176e30f91e45bb44e768609}

# Beyond

冰蝎 shell,工具直接跑出

flag{404c601a8ca9d44d586f7b93ba236b12}

# 冀信杯 - 流量分析

SQL 注入流量,工具直接跑出

flag{import3nt_use2s_inf0}

# Input Mice

工具直接跑出,然后上下翻转图片


XNUCA{USBPCAPGETEVERYTHING}

# Input Keyboard

2025/05/23 19:59:52

执行的类型: usbhid

执行成功。结果如下:

9<CAP>gr<CAP>gn<CAP>a<DEL>cu2c<CAP>c<CAP>e<CAP>ht<CAP>f<CAP>hj4712<DEL><DEL><CAP>hm<CAP>l<CAP>es<CAP>b<DEL>cc<CAP>x<CAP>h<CAP>r5<CAP>t<DEL><DEL>tg9<CAP>ovii<CAP>pot<DEL><DEL><DEL>7h<CAP>t<CAP>t<CAP>k<CAP>n<CAP>hjrxlk<CAP>v5<DEL><DEL><CAP>c<DEL>4<DEL>/x/zcks<CAP>o<CAP>e<CAP>hbv<DEL><DEL><DEL>tvg<CAP>e<CAP>b8<CAP>a<CAP>c

处理后的结果如下:

9GRgnCU2CcEhtFhj47HMlESccXhRtg9OVII7hTtKnHJRXLK/X/ZCKSoEtvgEb8Ac

删除的字符如下:

A12b5tpotv5C4hbv

AES-ECB,输入的是密文,删除的是密钥

# Be-AES-Waver

N1CTF 2023 Misc - zysgmzb's BLOG

分析流量包,UDP 传输的数据是很多个 WAV 文件

提取所有 UDP 数据

tshark -r challenge.pcapng -T fields -Y "udp" -e udp.payload | sed '/^\s*$/d' > 1.txt

用 foremost 分离

foremost -i 1.txt -o output

一共 131 个 wav 文件,随便打开一个

容易看出来是摩斯密码,7 位一组

用二进制打开,分析出长短音的持续时间有两种


第一种,长音 0x120 字节,短音和位间隔 0x60 字节,每 7 位间隔 0x180 字节


第二种,长音 0xc0 字节,短音和位间隔 0x40 字节,每 7 位间隔 0x100 字节

import wave
import os
def parse_morse_wav(filename, long_ucnt, short_ucnt, bit_sep, block_sep):
    # 打开 WAV 文件
    with wave.open(filename, 'rb') as wav_file:
        n_frames = wav_file.getnframes()
      
        # 读取所有帧数据
        frames = wav_file.readframes(n_frames)
      
        # 定义基本单位模式 (00 00 32 33 00 00 CE CC)
        base_unit = b'\x00\x00\x32\x33\x00\x00\xCE\xCC'
        base_unit_len = len(base_unit)
        bit_separator = b'\x00' * bit_sep
        block_separator = b'\x00' * block_sep
        pos = 0
        bits = []
        data = []
        while pos < len(frames):
            if frames[pos : pos + block_sep] == block_separator:
                if bits:
                    data.append(int(''.join(bits), 2))
                bits = []
                pos += block_sep
            elif frames[pos : pos + base_unit_len * long_ucnt] == base_unit * long_ucnt:
                bits.append('1')
                pos += base_unit_len * long_ucnt
            elif frames[pos : pos + base_unit_len * short_ucnt] == base_unit * short_ucnt:
                bits.append('0')
                pos += base_unit_len * short_ucnt
            elif frames[pos : pos + bit_sep] == bit_separator:
                pos += bit_sep
            else:
                break
        return data
long_ucnt1 = 36
short_ucnt1 = 12
bit_sep1 = 96
block_sep1 = 384
long_ucnt2 = 24
short_ucnt2 = 8
bit_sep2 = 64
block_sep2 = 256
if __name__ == "__main__":
  
    filenames = os.listdir('./wav')
    data = []
    try:
        for filename in filenames:
            decoded_data = parse_morse_wav(f'./wav/{filename}', long_ucnt1, short_ucnt1, bit_sep1, block_sep1)
            if not decoded_data:
                decoded_data = parse_morse_wav(f'./wav/{filename}', long_ucnt2, short_ucnt2, bit_sep2, block_sep2)
            data.extend(decoded_data)
        print(f"解码: {filename}")
    except Exception:
        print(f"解码错误: {filename}")
    print(data)

将得到的 ASCII 数组转换成文本,得到一个文章,中间插入了一些花括号括起来的字符

搜索出来一共有 65 处,根据题目名字猜测是 AES 密文,先提取出来

ZJoXMMxKEIxeX5dAEw8wBPbwF47X8P033iIWziLI9kkZ6RKKXBhKlrsqH+i3Bqv3T

现在还缺少密钥,回到 tshark 提取的 UDP 数据,发现每个 wav 文件头前都还有一些数据

有一些是 4 个字节,有一些是 5 个字节
经过验证,只有 5 个字节的情况下的第一个字节是有效的,全部提取出来

import string
f = open('1.txt', 'rb').read()
for i in range(len(f)-4):
    if(f[i:i+4] == b'RIFF'):
        if(chr(f[i-6]) in string.printable):
            print(chr(f[i-6]),end='')

得到密钥

P@$Sw0rd1!Y0uGot

直接解密会发现解不了,回到有花括号的文章中,发现第二行后每行开头都会有 Roger! ,但第 43 行没有

这里猜测 Roger! 是对上一行发送的字符进行确认,而 43 行没有对 42 行的 k 进行确认,因此密文对应删掉一个 k

ZJoXMMxKEIxeX5dAEw8wBPbwF47X8P033iIWziLI9kZ6RKKXBhKlrsqH+i3Bqv3T
P@$Sw0rd1!Y0uGot

配置好 AES 参数后成功解密

n1ctf{4c04c98007c5229f86cd2b70643f38d8}

# TxTime

拿到 38 个 txt,内容一样,都是 1124789000 ,发现文件的修改时间比较奇怪

文件修改时间戳减去文件内容即可

import time
import os
import re
T = 1124789000
filenames = [filename for filename in os.listdir('.') if filename.endswith('.txt')]
sorted_filenames = sorted(filenames, key=lambda x: int(re.search(r'change(\d+)\.txt', x).group(1)))
print(sorted_filenames)
data = []
for filename in sorted_filenames:
    meta_info = os.stat(filename)
    mtime = time.localtime(meta_info.st_mtime)
    timestamp = time.mktime(mtime)
    data.append(int(timestamp) - T)
print(data)

# puzzle picture

2023 第三届 “香山杯” 网络安全大赛|pintu Writeup - 1cePeak

4703 个 png,长度都是 65,只有宽度不相同

import os
import base64
from PIL import Image
def get_Image_Pixel(image_path):
    with Image.open(image_path) as img:
        pixel_data = img.getpixel((0,0))[0]
        return pixel_data
def get_image_dimensions(image_path):
    with open(image_path, 'rb') as img:
        img_bytes = img.read()
        # print(img_bytes[:18])
        return int(img_bytes[0x13]), int(img_bytes[0x17])
def height_Steg():
    heights = []
    current_directory = os.getcwd()
    for i in range(1, 4704):
        image_name = f"pintu/{i}.png"
        image_path = os.path.join(current_directory, image_name)
        if os.path.exists(image_path):
            width, height = get_image_dimensions(image_path)
            heights.append(height)
            # print(f"{image_name}: Width = {width}, Height = {height}")
        else:
            print(f"{image_name} does not exist.")
    res = ''
    data = ''
    for item in heights:
        decimal_heights = int(str(item),8)
        res+=chr(decimal_heights)
    #print(res)
    res = res.split(' ')
    for item in res:
        data +=chr(int(item))
    #print(data)
    data = base64.b32decode(data)
    print(data)
    return data
def translate_pixel():
    res = ''
    output = []
    current_directory = os.getcwd()
    for i in range(1, 4704):
        image_name = f"pintu/{i}.png"
        image_path = os.path.join(current_directory, image_name)
        if os.path.exists(image_path):
            pixel_data = get_Image_Pixel(image_path)
            if pixel_data == 255:
                res += '1'
            else:
                res += '0'
    if len(res) % 8!=0:
        # 这里一定要注意是在前面补零还是在后面补零,一般都是在前面补零对齐
        # res+='0'*(8-len(res)%8)
        res = '0'*(8-len(res)%8) + res
    for i in range(0,len(res),8):
        binary_segment = res[i:i+8]
        int_value = int(binary_segment,2)
        char_value = chr(int_value)
        output += [char_value]
    # print(output)
    print(''.join([str(x) for x in output]))
b64data = height_Steg()
translate_pixel()
b64t = str.maketrans('sUvcu5rgSeAmJQCfdXtEMKIB91Lj3niOo4hyV0b/2azpx8HqZP6wk7GNlTFYDR+W', "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")
dec = b64data.decode('ascii').translate(b64t)
print(dec)

上传到 npiet 解密
https://www.bertnase.de/npiet/npiet-execute.php

flag{4b6c1737-27e5-41c4-95e3-f70ad196063e}

# are you ok

一些蓝牙流量分析 - g0at's Blog
数字中国 2023|数字网络安全人才挑战赛出题思路 - 1cePeak

tshark -r 20230314_101903.log  -T fields -Y "btatt.opcode==0x52" -e btatt.value > 1.txt
with open('1.txt', 'r') as f:
    data = f.readlines()
trans = {
    '00': '.',
    '01': '-',
    '02': '/'
}
print(''.join([trans[b.strip()] for b in data]))

电量不知道为什么是这个数据包

Handle: 0x7e

0x48 即 72

flag

# hard web

服务器开放了哪些端口,请按照端口大小顺序提交答案,并以英文逗号隔开 (如服务器开放了 80 81 82 83 端口,则答案为 80,81,82,83)

服务器中根目录下的 flag 值是多少?

该 webshell 的连接密码是多少?

筛选源 ip 为服务器的 tcp 流量,查看端口扫描过程中返回了 RST 的即为开放端口

20,21,22,80,81,888,3306,8001,8888

748007e861908c03

flag{9236b29d-5488-41e6-a04b-53b0d8276542}

# 飞驰人生

安装好 ICSim 环境,启动 canplayer 重放后发现过程中转向灯、速度仪表有异常

用 SavvyCAN 分析


188#01000000 # 左转
188#02000000 # 右转
188#03000000 # 双闪

19B#00000E000000 # 开车门
19B#00000F000000 # 关车门


244#000000xx # 第三个字节为速度值
244#000000A60000



关车门及油门为异常操作

flag{19B#00000F000000_244#000000A60000}

# Bubble Fish

2023 年新疆 “天山固网杯” 网络安全技能竞赛|Misc Writeup - 1cePeak

import os
import base64
import shutil
def base32_decode_filename(filename):
    # 移除.png 后缀
    if filename.endswith('.png'):
        filename = filename[:-4]
  
    # Base32 解码
    try:
        decoded_bytes = base64.b32decode(filename)
        return decoded_bytes.decode('utf-8')
    except base64.binascii.Error as e:
        # Base32 末尾填充 =
        if "Incorrect padding" in str(e):
            padded_input = filename + '=' * (8 - len(filename) % 8)
            return base64.b32decode(padded_input).decode('utf-8')
def decode_png_filenames_in_tmp():
    b32decode_results = {}
    tmp_dir = 'tmp'
    for filename in os.listdir(tmp_dir):
        if filename.lower().endswith('.png'):
            decoded_name = base32_decode_filename(filename)
            if decoded_name:
                print(f"原始文件名: {filename} -> 解码后: {decoded_name}")
                b32decode_results[filename] = decoded_name
    return b32decode_results
def decode_filename_number(b32decode_results):
    cn2number = {
        '零': '0',
        '一': '1',
        '二': '2',
        '三': '3',
        '四': '4',
        '五': '5',
        '六': '6',
        '七': '7',
        '八': '8',
        '九': '9',
        '壹': '1',
        '贰': '2',
        '叁': '3',
        '肆': '4',
        '伍': '5',
        '陆': '6',
        '柒': '7',
        '捌': '8',
        '玖': '9',
        '零': '0',
        '0': '0',
        '1': '1',
        '2': '2',
        '3': '3',
        '4': '4',
        '5': '5',
        '6': '6',
        '7': '7',
        '8': '8',
        '9': '9',
    }
    numberdecode_results = {}
    for item in b32decode_results.items():
        number_str = ''.join([cn2number[char] for char in item[1] if char in cn2number])
        numberdecode_results[item[0]] = number_str
        print(f"原始文件名: {item[0]} -> 解码后: {number_str}")
  
    os.makedirs('renamed_png', exist_ok=True)
    for item in numberdecode_results.items():
        shutil.copy2(f'tmp/{item[0]}', f'renamed_png/{item[1]}.png')
if __name__ == "__main__":
    dec1 = decode_png_filenames_in_tmp()
    decode_filename_number(dec1)

原始文件名先 base32 解码,得到中文数字和字母混合的文件名

混合文件名中提取数字,重新对图片进行排序

数图片中气泡的数量(只算大的或小的),得到一个八进制数据

104101123103124106173041041041146061163150137142165142142154063163137142165142142154063163137146061163150137150141150141150141150141041041041175

# 冀信杯 - 账号泄露

小明发现自己的账号泄露了,他检查了近期的电脑日志找到了黑客创建的登录账号,听说密码是 6 位的,flag 是创建的登录账号和密码拼接的字符串例如:登录账号为 lyflag,密码为 123456,则提交的 flag 值为 lyflag123456

创建用户的事件 ID 为 4720

创建的用户名为

2Ha0c2K34ruiop$

用这个用户名解压 sam.zip ,得到 SAM, SECURITY, SYSTEM 三个注册表文件

可用 securitydump.py 或者 mimikatz 获得密码

python securitydump.py LOCAL -sam sam.save -system system.save -security security.save


mimikatz # lsadump::sam /SAM:sam.save /SYSTEM:system.save

78ea10b3d3d0ae290580778800b4de5d

但是解密不了
附件有问题,Hash 应为 b4a13264d379f697fbc6d0b1a5c1fcf0

根据题目提示 6 位密码

hashcat -m 1000 -a 3 b4a13264d379f697fbc6d0b1a5c1fcf0 ?a?a?a?a?a?a --show

爆破得到密码 asdf,.

flag{2Ha0c2K34ruiop$asdf,.}

# NTLM

流量包筛选 smb2 协议

在上传 6.png 的时候写入的内容是 7z 压缩包

压缩包提取出来后解压,其中有一个密码字典文件

用工具直接提取 NTLMv2 Hash

hashcat 字典爆破

hashcat -m 5600 administrator:::1166026a7745c2d0:971356880954f778c48937ad52ccae51:01010000000000005a308cd161f7d8016092111fede1fd960000000002001e004400450053004b0054004f0050002d004a0052005500510045003900360001001e004400450053004b0054004f0050002d004a0052005500510045003900360004001e004400450053004b0054004f0050002d004a0052005500510045003900360003001e004400450053004b0054004f0050002d004a00520055005100450039003600070008005a308cd161f7d80106000400020000000800300030000000000000000100000000200000a2fc8de06e3dff62c01429115953f2f8ea32e069bc69837a7b03ee0112ca689a0a0010000000000000000000000000000000000009001c0063006900660073002f003100320037002e0030002e0030002e0031000000000000000000 ./passwords.txt --show

(此处必须把设备名称 DESKTOP-JRUQE96 删除

得到密码为 @Aa123456789,题目需要的是 NTLM Hash 值

flag{D65AFD465A2E3006EF582F02179502EE}

# hacklog

二进制打开图片,文件尾后还存在 zip 数据

提取出的 zip 有解压密码

用 archpr 尝试爆破,结果是弱口令 123456

解压出的流量包直接打开报错

二进制打开,发现文件最开始的 16 个字节全是 0

根据文档还原头部,长度要根据末尾的长度填

wireshark 可以打开了,上工具,解析出一个压缩包,里面有 CS 密钥

工具直接跑出

flag{402887a87fdac13c01808eabe2c111cd}

# CSGO

文件中有个 .cobaltstrike.beacon_keys 私钥

工具直接出结果。。

# webshell

SQL 盲注流量

GET /sqli-labs-master/Less-5/?id=13' and ord(mid((select username from security.users limit 0,1),16,1))=48--  HTTP/1.1\r\n

工具可以直接跑出盲注结果

09020dd54a897ba0

将结果作为冰蝎密钥,直接跑出

# linux 日志分析

分析 Linux 服务器日志,找到 ssh 登录失败次数最多的 IP 和次数,以及日志期间添加的有执行命令权限的用户名
例如:192.168.1.1 登录失败次数最多为 876 次,日志期间创建了用户 user1 和 user2,则提交的 flag 格式为 192.168.1.1_876_user1_user2

有 4 个 auth.log,合并
cat auth.log auth.log.1 auth.log.2 auth.log.3 auth.log.4 > auth.logs

grep "Failed password" /var/log/auth.logs | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | head -n1

121.36.89.67_2028

登录失败只统计 auth.log

123.193.145.57_120

grep -E "useradd.*new user|new user.*name=" auth.logs

有执行命令权限的用户为 student 和 guest

# Aeri 的秘密

流量分析,筛选 ftp,发现有两段数据传输

关注第二次数据传输,传了一个 exe 文件

通过工具 dump 下来

ida 打开分析

最终目的是输入的字符串经过 customEncrypt 后结果为 5mMLP4sX7l39dbYLnebEdebXIlPMIMZiCMAoPHeinmqXUhfp

查看该函数

基本上可以判断就是换表的 base64

直接解码

Ly_Flag{U_R_SMB_Master_6vQ3Dm6Pp!!!}

# 密码喷洒

流量包分析,筛选 smb2.cmd==1 关注密码喷洒过程中连接建立情况

最后一个用户名为 fileserver 的连接建立成功,分析此时的 ntlmv2 hash

格式: username::domain:challenge:NTProofStr:blob

  • ServerChallengeNTLMSSP_CHALLENGE 包中
  • NTProofStrNTLMSSP_AUTH 包中
  • NTLMv2 ResponseNTLMSSP_AUTH 包中,但复制后要去掉前面的 NTProofStr


fileserver::fbi.gov:1f1bdaf788d2c102:15c26d67db4c24e8a67fea9b447c05f8:0101000000000000a8e7eb30b851db0168765147424970490000000002000600460042004900010004004400430004000e006600620069002e0067006f00760003001400640063002e006600620069002e0067006f00760005000e006600620069002e0067006f00760007000800a8e7eb30b851db0109000e0063006900660073002f00440043000000000000000000
hashcat -m 5600 fileserver::fbi.gov:1f1bdaf788d2c102:15c26d67db4c24e8a67fea9b447c05f8:0101000000000000a8e7eb30b851db0168765147424970490000000002000600460042004900010004004400430004000e006600620069002e0067006f00760003001400640063002e006600620069002e0067006f00760005000e006600620069002e0067006f00760007000800a8e7eb30b851db0109000e0063006900660073002f00440043000000000000000000

flag{fileserver_QWEasd1234}

(kali 自带的 rockyou.txt 本身没有这个密码,手动加进去

# 可移动磁盘

用 FTK Imager 打开镜像

绿色通道 plane3 有数据

中间有提示 1^0=1 ,是像素画形式,应该不是有效数据
注意到两侧分别有两列数据

再根据中间的提示,将这两个数据异或

print(hex(0x2de0d7dc833099fcdcd7d452bf24d98e8281e1b72f384a57b71683520e893386894b998ed71888503de031f1f2d0c9f7ad9247d618679aed ^ 0x778dafb4d903eac99183bf2bf14990bfcfc2d0dc626f0363fb42d22a43bb62f2c60ffde59961b93867a7679cbd9798c6f7d516af4130d4d4))

解一层 hex 和一层 base64

flag{91926b50-d1b8-413d-87d7-adef8d5dd2ac}

# Be-A-11-Packet

附件是流量包,打开发现其中有 FTP 传输文件的流量,上工具全部提取

提取出 68 个 zip 文件

打开查看发现,除了 11.zip ,其他每个 zip 中都是一个 4 字节的 txt,可以 CRC 碰撞

import binascii
import itertools
import string
import zipfile
import os
def crack_crc32(target_crc, length=4, charset=None):
    """
    通过CRC32碰撞找到匹配的字符串
    :param target_crc: 目标CRC32值(整数或十六进制字符串)
    :param length: 原文长度(默认为4)
    :param charset: 使用的字符集(默认所有可打印ASCII字符)
    :return: 匹配的字符串或None
    """
    # 处理目标 CRC32 值
    if isinstance(target_crc, str):
        target_crc = int(target_crc, 16)
    target_crc = target_crc & 0xffffffff  # 确保是 32 位无符号整数
  
    # 设置字符集
    if charset is None:
        charset = string.printable  # 默认使用所有可打印 ASCII 字符
    elif isinstance(charset, str):
        charset = list(charset)
  
    print(f"开始碰撞 {length} 字节CRC32: 0x{target_crc:08x}")
    print(f"使用的字符集: {charset[:20]}... (共{len(charset)}个字符)")
  
    # 遍历所有可能的组合
    for candidate in itertools.product(charset, repeat=length):
        text = ''.join(candidate)
        crc = binascii.crc32(text.encode()) & 0xffffffff
        if crc == target_crc:
            return text
  
    return None  # 未找到匹配
if __name__ == "__main__":
    ext = ''
    filelist = [f'ext/{i}.zip' for i in range(1, 69)]
    for filename in filelist:
        with zipfile.ZipFile(filename) as zf:
            file_info = zf.infolist()[0]
            target_crc = file_info.CRC
  
        result = crack_crc32(target_crc, length=4)
        ext += result
        if result:
            print(f"找到匹配: '{result}' (CRC32: 0x{binascii.crc32(result.encode()) & 0xffffffff:08x})")
        else:
            print("未找到匹配")
    print(ext)

UEsDBBQACQAIAHoGa1UEgkKgNgAAACgAAAAIAAAAZy50eHQnPL9D20a0sgfK5QEzCtDJWrot+ljdYIPzWkybokUz57zIc8u0DCUTyQHa02X8M5IAviVj/fZQSwECFAAUAAkACAB6BmtVBIJCoDYAAAAoAAAACAAkAAAAAAAAACAAAAAAAAAAZmxhZy50eHQKACAAAAAAAAEAGABZOca6JPXYAdHi/Qsl9dgBILcWtyT12AFQSwUGAAAAAAEAAQBaAAAAXAAAAAAA

这串直接 base64 解码后,得到的压缩包无法打开

仔细看可以发现这个地方少了 fla ,可以先手动补上 Zmxh 字符

下载压缩包,可以打开,但是需要密码


回到 11.zip ,有 14 个字节,无法爆破,尝试爆破密码

弱口令,解压后拿到 flag 压缩包的密码

DASCTF{69a7c3f8eae457779f53be70a7e14a87}

# Be-A-QRer

拿到 531 个 png 格式二维码图片,解码后的内容全都一样,只有图片长宽不同

注意到图片的长宽只有几种固定的数值,写一个简单的脚本统计

from PIL import Image
filenames = [f'qrsea/{i}.png' for i in range(531)]
data = {}
for file in filenames:
    with Image.open(file) as img:
        n = img.width
        if n in data.keys():
            data[n] += 1
        else:
            data[n] = 1
print(data)

只有 10 种长宽,猜测按大小排列对应数字 0-9
直接转换后,似乎不是一个有效数据

二维码内容 tupper,指 Tupper 公式

抄一段代码画图

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
filenames = [f'qrsea/{i}.png' for i in range(531)]
data = {}
for file in filenames:
    with Image.open(file) as img:
        n = img.width
        if n in data.keys():
            data[n] += 1
        else:
            data[n] = 1
data = sorted(data.keys())
data = {data[i]: i for i in range(10)}
result = ''
for file in filenames:
    with Image.open(file) as img:
        n = img.width
        result += str(data[n])
def Tupper_self_referential_formula(k):
    aa = np.zeros((17, 106))
    def f(x, y):
        y += k
        a1 = 2 ** -(-17 * x - y % 17)
        a2 = (y // 17) // a1
        return 1 if a2 % 2 > 0.5 else 0
    for y in range(17):
        for x in range(106):
            aa[y, x] = f(x, y)
    return aa[:, ::-1]
k = int(result)
aa = Tupper_self_referential_formula(k)
plt.figure(figsize=(15, 10))
plt.imshow(aa, origin='lower')
plt.gca().invert_xaxis()
plt.show()

flag{miku}

# 冀信杯 - 数据库恢复

附件有两个文件夹 mysqldatadata 下有 2 个 mysqlbinlog 文件

读取这两个文件,转为 sql

./mysql/bin/mysqlbinlog data/mysql-bin.000009 > sql1.sql
./mysql/bin/mysqlbinlog data/mysql-bin.000010 > sql2.sql

sql1.sql 中可以看到恢复操作,创建 jx_db 数据库, lyflag 表,添加记录


删除的时间戳是 1734484497

flag{jx_db_1734484497_U_f1nd_5!}

# Be-A-surfer

打开附件的 index.html,是网页游戏
查看源码

surf.bundle.js

ROT13 + base64

提交发现是假的 flag

找到分数判断逻辑

修改为 1 分,随便玩一局后弹出一个图片

提示为 lsb_img ,下载查看隐写

无法直接提取数据,猜测前面获取的可能是隐写的 key,上工具解密
选 cloacked-pixel

DASCTF{Edge_Surf_Cheated_By_Ctferrr!!}

# Be-A-rnolder

分析流量包,有 FTP 传输数据的流量,用工具全部提取

有 100 张 400*4 的 png,和一个 key.txt

直接 stegsolve 无法提取出有用的数据
题目名字提示可能是 Arnold 变换算法,且 key.txt 正好提供了参数 a b ,需要把 100 张图片拼接

from PIL import Image
import os
def stitch_images(output_path):
    # 获取所有图片文件(按文件名排序)
    image_files = [f'ftpdump/{i}.png' for i in range(1, 101)]
  
    # 创建空白画布
    result = Image.new('RGB', (400, 400))
  
    # 当前垂直位置
    y_offset = 0
  
    # 逐个拼接图片
    for img_file in image_files:
        img_path = os.path.join(img_file)
        with Image.open(img_path) as img:
            # 确保图片尺寸是 400x4
            if img.size != (400, 4):
                raise ValueError(f"图片{img_file}尺寸不是400x4,而是{img.size}")
          
            # 将当前图片粘贴到结果中
            result.paste(img, (0, y_offset))
            y_offset += 4  # 每次下移 4 像素
  
    # 保存结果
    result.save(output_path)
    print(f"图片已成功拼接并保存到: {output_path}")
stitch_images("output.jpg")

抄一个 arnold 变换的代码

# -*- coding: utf-8 -*-
# @Author  : 1cePeak
import matplotlib.pyplot as plt
import cv2
import numpy as np
from PIL import Image
img = cv2.imread('output.png')
def arnold_encode(image, shuffle_times, a, b):
    """ Arnold shuffle for rgb image
    Args:
        image: input original rgb image
        shuffle_times: how many times to shuffle
    Returns:
        Arnold encode image
    """
    # 1: 创建新图像
    arnold_image = np.zeros(shape=image.shape)
  
    # 2:计算 N
    h, w = image.shape[0], image.shape[1]
    N = h   # 或 N=w
  
    # 3:遍历像素坐标变换
    for time in range(shuffle_times):
        for ori_x in range(h):
            for ori_y in range(w):
                # 按照公式坐标变换
                new_x = (1*ori_x + b*ori_y)% N
                new_y = (a*ori_x + (a*b+1)*ori_y) % N
              
                # 像素赋值
                print(image[ori_x, ori_y, :])
                print(arnold_image[new_x, new_y, :])
                arnold_image[new_x, new_y, :] = image[ori_x, ori_y, :]
    cv2.imwrite('flag_arnold_encode.png', arnold_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
    return arnold_image
def arnold_decode(image, shuffle_times, a, b):
    """ decode for rgb image that encoded by Arnold
    Args:
        image: rgb image encoded by Arnold
        shuffle_times: how many times to shuffle
    Returns:
        decode image
    """
    # 1: 创建新图像
    decode_image = np.zeros(shape=image.shape)
    # 2:计算 N
    h, w = image.shape[0], image.shape[1]
    N = h  # 或 N=w
    # 3:遍历像素坐标变换
    for time in range(shuffle_times):
        for ori_x in range(h):
            for ori_y in range(w):
                # 按照公式坐标变换
                new_x = ((a * b + 1) * ori_x + (-b) * ori_y) % N
                new_y = ((-a) * ori_x + ori_y) % N
                decode_image[new_x, new_y, :] = image[ori_x, ori_y, :]
    cv2.imwrite('flag.png', decode_image, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])
    return decode_image
# arnold_encode(img, 1, 2, 3)
arnold_decode(img, 1, 0x726e, 0x6f6c64)

(注意题给的 a 和 b 和代码里参数 a 和 b 的位置是反的

变换后得到二维码,解码 + base64

flag{2d8aa42a0347c2d66cc86a0138dc9664}

# 冀信杯 - 勒索加密

某主机重要文件遭到了勒索加密,恶意程序执行完自我删除只留下了加密后的文件,好在保留了流量记录,能够从中提取到加密程序样本,请还原出重要文件

wireshark 查看 HTTP 对象,有一个 lesuojiami.exe

转存后是一个 python 图标的 exe,用 pyinstxtractor 先解包

![[../blog_post/attachments/blog_post/0523-misc/IMG-20251226153056-5.png]]

然后安装 pycdc,反编译 lesuojiami.pyc

![[../blog_post/attachments/blog_post/0523-misc/IMG-20251226153056-6.png]]

打开源码查看,是一个 AES 加密,并且是加密两次

然而附件给的是未加密的文件。。可能是题目给错
但是这个压缩包无法正常打开
010Editor 打开查看后发现文件尾缺少了几个字节,根据文档补全

是一个正常加密的压缩包,尝试破解密码

弱口令 654321,解压 flag.txt

ly_flag{2c8aaca9-6ada-41b0-9803-2ded0e5f8d7a}

2024 年中央企业网络安全大赛|Writeup - 1cePeak
SUCTF2025 WriteUp - Z3n1th Blog

1.pcapng 提取 NTLMv2 Hash,爆破

hashcat -m 5600 sk::sk.com:77534d575de5f632:83889cdf4d1336bd3cc92f23c94f1f6d:010100000000000080a6da2b0464db012ad8e6c2d43a869c000000000200040053004b00010004004400430004000c0073006b002e0063006f006d0003001200440043002e0073006b002e0063006f006d0005000c0073006b002e0063006f006d00070008009dafb62b0464db01090036006c006400610070002f003100390032002e003100360038002e003100370039002e00310033003100400073006b002e0063006f006d000000000000000000 /usr/share/wordlists/rockyou.txt

sk.com\sk
!@#123QWEqwe

Wireshark 配置密码,解密数据

28 号包中有修改 Administrator 密码的操作,密码修改为 02)78M5CcE=+

打开 2.pcapng ,有 Kerberos 认证以及加密的 SMB3 流量
已知 Administrator 的密码,利用 Create-KeyTab 制作 key table

导入 KRB5 keytab,可以解密 Kerberos 流量

下一步解密 SMB3 流量,需要寻找 Session ID 和对应的 Session Key
SMB 包中可以查看 ID,解密后的 TGS-REP 包中可以找到 Key


SessionID 需要按右边的大端序填写
按这个方法找出所有的 pair

Session IDSession Key
510000080018000038da7bd9fdba2dd52ce2d670e59d4984
5500000800180000<br>78e3e3f1559813f7286374a816b99862
6500000800180000b97c4cc93e3a424aacccd4dafba3ceca
6900000800180000b40ccbe618c34a3c54716ac5ba14408d

解密后导出 SMB 对象,有 flag.zip

压缩包有密码,继续看 3.pcapng

只有一组 id:key
1100000c00180000 : 3c4276a33a529832163bb2b7d7e3db87

解密,找到 90 号数据包写入了一个 xml 文件

复制为 ASCII Text

\NGNhFjsY<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <Triggers>
    <CalendarTrigger>
      <StartBoundary>2015-07-15T20:35:13.2757294</StartBoundary>
      <Enabled>true</Enabled>
      <ScheduleByDay>
        <DaysInterval>1</DaysInterval>
      </ScheduleByDay>
    </CalendarTrigger>
  </Triggers>
  <Principals>
    <Principal id="LocalSystem">
      <UserId>S-1-5-18</UserId>
      <RunLevel>HighestAvailable</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
    <AllowHardTerminate>true</AllowHardTerminate>
    <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
    <IdleSettings>
      <StopOnIdleEnd>true</StopOnIdleEnd>
      <RestartOnIdle>false</RestartOnIdle>
    </IdleSettings>
    <AllowStartOnDemand>true</AllowStartOnDemand>
    <Enabled>true</Enabled>
    <Hidden>true</Hidden>
    <RunOnlyIfIdle>false</RunOnlyIfIdle>
    <WakeToRun>false</WakeToRun>
    <ExecutionTimeLimit>P3D</ExecutionTimeLimit>
    <Priority>7</Priority>
  </Settings>
  <Actions Context="LocalSystem">
    <Exec>
      <Command>cmd.exe</Command>
      <Arguments>/C C:\Windows\7z.exe x -pwvnNDOLkjyXZ925aJ32x822dEe C:\Windows\flag.zip -y > %windir%\Temp\NGNhFjsY.tmp 2>&1</Arguments>
    </Exec>
  </Actions>
</Task>

得到压缩包密码 wvnNDOLkjyXZ925aJ32x822dEe

flag{c018fb75888c8a7044789370db05981d}

# SQLKing