文章定位: CTF逆向新手完全指南
难度等级: (中级)
实战环境: Linux + Python3
知识储备: 基础Linux命令、Python语法、二进制基础
你是否想过,控制着工厂生产线、电力系统、甚至火星探测器的嵌入式设备,它们的密码是如何保护的?本文将带你深入一次真实的CTF逆向挑战,通过分析VxWorks实时操作系统的固件,理解工控系统的密码机制,并发现其中隐藏的安全漏洞。
题目背景:
给定文件:key.bin(973KB固件文件) +_key.bin.extracted/385(4.6MB提取文件)
任务目标: 找到能生成哈希值cQwwddSRxS的密码
技术栈: VxWorks RTOS + PowerPC架构 + 密码学
学习收获:
掌握固件文件的分析方法和工具使用
理解实时操作系统(RTOS)的密码机制
学会设计有效的密码破解策略
认识工控系统安全的重要性
让我们开始这段探索之旅!
当我们拿到题目附件时,首先要问自己几个问题:
这是什么类型的文件?
它来自哪个系统平台?
文件中隐藏着什么信息?
让我们从最基础的命令开始:
# 查看文件类型
$ file key.bin
key.bin: data
file命令显示这是一个data文件 - 这意味着它不是标准的ELF、PE等可执行文件格式。这是固件文件的典型特征。
** 新手知识点: 什么是固件?**
固件(Firmware)是嵌入式设备的"灵魂",包含了操作系统、驱动程序和应用软件的打包文件。与我们电脑上的exe、elf等单一可执行文件不同,固件通常包含多个组件,采用厂商自定义的格式。
# 查看文件大小
$ ls -lh key.bin
-rwxrwxrwx 1 root root 973K 8月 27 2018 key.bin
973KB的大小,对于嵌入式固件来说是合理的体积。
使用hexdump工具查看文件的十六进制内容:
$ hexdump -C key.bin | head -20
00000000 13 00 03 03 40 06 40 06 84 03 cd 03 46 77 0d 29 |....@[email protected].)|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 31 34 30 2d 4e 4f 45 2d 37 37 31 2d 30 31 00 00 |140-NOE-771-01..|
00000030 4e 6f 76 20 32 31 20 31 34 20 31 32 3a 30 33 00 |Nov 21 14 12:03.|
00000040 51 75 61 6e 74 75 6d 20 45 74 68 65 72 6e 65 74 |Quantum Ethernet|
00000050 20 45 78 65 63 75 74 69 76 65 20 66 69 72 6d 77 | Executive firmw|
00000060 61 72 65 20 56 65 72 2e 20 36 2e 34 30 00 00 00 |are Ver. 6.40...|
** 如何阅读hexdump输出?**
让我们拆解第一行:
00000000 13 00 03 03 40 06 40 06 84 03 cd 03 46 77 0d 29 |....@[email protected].)|
^ ^ ^
| | |
偏移地址 十六进制数据(每两位是一个字节) ASCII显示
从右侧的ASCII列,我们可以看到一些有价值的信息:
偏移0x20:140-NOE-771-01- 设备型号
偏移0x30:Nov 21 14 12:03- 编译时间戳
偏移0x40:Quantum Ethernet Executive firmware Ver. 6.40- 固件描述
关键发现: 这是施耐德电气(Schneider Electric)Quantum系列PLC(可编程逻辑控制器)的固件!
新手知识点: 什么是PLC?
PLC(Programmable Logic Controller,可编程逻辑控制器)是工业自动化的核心设备,用于控制生产线、机器人、电力系统等。它们运行实时操作系统,对可靠性和安全性要求极高。
字符串是固件分析的"宝藏"。程序员在代码中留下的函数名、错误消息、版权信息等,都能帮我们理解程序的功能。
# 在extracted文件中搜索VxWorks相关字符串
$ strings _key.bin.extracted/385 | grep -i "vxworks" | head -5
UNIX Type: L8 Version: VxWorks
fec(0,0) labcomm1:smooney_100MB\Tornado_NA\target\config\bsp_noe\vxWorks.st
P:/Eth_NOE/vxWorks_gen/../utils/setfstime.cpp
P:/Eth_NOE/vxWorks_gen/../bp_drv/bp_isr.cpp
确认了! 这是VxWorks系统!
新手知识点: VxWorks是什么?
VxWorks是Wind River公司开发的实时操作系统(RTOS),在工控、航空航天、医疗设备等领域应用极广。著名案例:
火星探测器"好奇号"
波音787梦幻客机
全球数百万台工业控制设备
既然我们的任务是破解密码哈希,那么固件中一定有加密/哈希相关的函数:
# 搜索login和encrypt相关的字符串
$ strings _key.bin.extracted/385 | grep -i "login"
Guest login ok, upload directory is %s.
Login failed.
loginUserAdd
loginUserVerify
loginDefaultEncrypt # ← 找到了!
loginInit
<VxWorks login:
重大发现:loginDefaultEncrypt- 这就是VxWorks的默认密码加密函数!
定位这个字符串在文件中的确切位置:
# 使用grep的-b选项显示字节偏移
$ grep -obUaP "loginDefaultEncrypt" _key.bin.extracted/385
2289956:loginDefaultEncrypt
# 转换为十六进制地址
$ python3 -c "print(f'0x{2289956:X}')"
0x22F124
字符串位于偏移0x22F124处。这个信息在IDA Pro等反汇编工具中会很有用。
第一章小结:
文件类型: 施耐德Quantum PLC固件
操作系统: VxWorks RTOS
目标函数: loginDefaultEncrypt @ 0x22F124
下一步: 研究这个函数的算法
面对一个未知的加密函数,我们有两条路:
方法A: 汇编逆向
使用IDA Pro打开固件
定位loginDefaultEncrypt函数
分析PowerPC汇编代码
还原C算法
方法B: 公开资料研究
搜索VxWorks文档
查找安全公告和漏洞数据库
寻找开源实现
对于新手来说,方法B更高效! 因为loginDefaultEncrypt是VxWorks的公开API,已有大量研究资料。
逆向工程的智慧:
"不要重复造轮子" - 在逆向分析前,先搜索是否有公开资料。很多看似神秘的算法,实际上已有完整文档。
使用Google搜索:VxWorks loginDefaultEncrypt algorithm
找到以下关键资料:
漏洞公告 - CERT VU#840249
标题: Wind River Systems VxWorks weak default hashing algorithm
CVE编号: CVE-2010-2965
严重性: 中等
描述: VxWorks的默认密码哈希算法存在碰撞攻击漏洞
这是2010年披露的安全漏洞! 说明这个算法有已知弱点。
GitHub源码 - dchest/historic-password-hashes
在GitHub的历史密码哈希算法存档项目中,找到了完整的C源码:
/*
* VxWorks 6.9 loginDefaultEncrypt
* 来源: VxWorks官方文档
*/
STATUS loginDefaultEncrypt(char *in, char *out)
{
int ix;
unsigned long magic = 31695317; // 魔数常量
unsigned long passwdInt = 0;
// 密码长度限制: 8-40字符
if (strlen(in) < 8 || strlen(in) > 40)
{
errnoSet(S_loginLib_INVALID_PASSWORD);
return (ERROR);
}
// 步骤1: 计算加权字符和
for (ix = 0; ix < strlen(in); ix++)
passwdInt += (in[ix]) * (ix + 2) ^ (ix + 1);
// 步骤2: 乘以魔数并转为字符串
sprintf(out, "%u", (long)(passwdInt * magic));
// 步骤3: 字符变换
for (ix = 0; ix < strlen(out); ix++)
{
if (out[ix] < '3')
out[ix] = out[ix] + '!'; // ASCII +33
if (out[ix] < '7')
out[ix] = out[ix] + '/'; // ASCII +47
if (out[ix] < '9')
out[ix] = out[ix] + 'B'; // ASCII +65 (实际是'A')
}
return (OK);
}
成功!我们获得了完整的算法源码!
让我们逐步理解这个算法的工作原理:
这是算法的核心部分,公式为:
加权和 = Σ [ASCII(字符[i]) × (i+2) ⊕ (i+1)]
i=0 到 密码长度-1
让我们用"FLAGKNXY"的第一个字符'F'举例:
位置 i = 0
字符 = 'F'
ASCII值 = 70
计算:
权重乘法: 70 × (0+2) = 70 × 2 = 140
异或操作: 140 ⊕ (0+1) = 140 ⊕ 1 = 141
异或(XOR)运算快速入门:
异或是一种位运算,规则是"相同为0,不同为1":
140的二进制: 10001100
⊕ 1的二进制: 00000001
-------------
结果: 10001101 = 141(十进制)
Python中的异或运算符是^:
>>> 140 ^ 1
141
完整的"FLAGKNXY"加权和计算表:
| 位置(i) | 字符 | ASCII | 权重(i+2) | XOR值(i+1) | 计算过程 | 结果 |
|---|---|---|---|---|---|---|
| 0 | F | 70 | 2 | 1 | 70×2⊕1 = 140⊕1 | 141 |
| 1 | L | 76 | 3 | 2 | 76×3⊕2 = 228⊕2 | 230 |
| 2 | A | 65 | 4 | 3 | 65×4⊕3 = 260⊕3 | 263 |
| 3 | G | 71 | 5 | 4 | 71×5⊕4 = 355⊕4 | 359 |
| 4 | K | 75 | 6 | 5 | 75×6⊕5 = 450⊕5 | 455 |
| 5 | N | 78 | 7 | 6 | 78×7⊕6 = 546⊕6 | 548 |
| 6 | X | 88 | 8 | 7 | 88×8⊕7 = 704⊕7 | 711 |
| 7 | Y | 89 | 9 | 8 | 89×9⊕8 = 801⊕8 | 809 |
| 总和 | 3516 |
所以"FLAGKNXY"的加权和是3516。
sprintf(out, "%u", (long)(passwdInt * magic));
这行代码做了两件事:
将加权和乘以魔数31695317(十六进制0x1E3A1D5)
转换为无符号长整型字符串
对于"FLAGKNXY":
3516 × 31695317 = 111,440,734,572
为什么需要32位掩码?
在C语言中,unsigned long在32位系统上是32位(4字节)。当两个大数相乘时,结果可能超过32位范围,这时会发生溢出,只保留低32位。
111,440,734,572的二进制(37位):
1100111110010011000101010100101101100
32位掩码 (& 0xFFFFFFFF)后:
只保留低32位: 11110010011000101010100101101100
十进制: 4,066,552,172
在Python中模拟:
>>> (3516 * 31695317) & 0xFFFFFFFF
4066552172
转为字符串:"4066552172"
这一步的目的是让数字字符串"看起来像哈希值",通过条件判断将数字0-9映射到特定字母:
if (out[ix] < '3')
out[ix] = out[ix] + '!'; // ASCII +33
if (out[ix] < '7')
out[ix] = out[ix] + '/'; // ASCII +47
if (out[ix] < '9')
out[ix] = out[ix] + 'B'; // ASCII +65 (实际是'A')
重要细节: 注意这里的if不是else if,意味着判断会级联!
让我们追踪数字'4'的变换过程:
初始: '4' (ASCII 52)
第1次判断: '4' < '3' ? 否,不变
第2次判断: '4' < '7' ? 是! → ASCII 52 + 47 = 99 → 'c'
第3次判断: 'c' < '9' ? 否,不变
最终: 'c'
完整映射表:
| 输入 | 第1步(<'3') | 第2步(<'7') | 第3步(<'9') | 最终输出 | 说明 |
|---|---|---|---|---|---|
| '0' | +33→'Q' | 'Q'<'7'→跳过 | 'Q'<'9'→跳过 | 'Q' | 规则1 |
| '1' | +33→'R' | 'R'<'7'→跳过 | 'R'<'9'→跳过 | 'R' | 规则1 |
| '2' | +33→'S' | 'S'<'7'→跳过 | 'S'<'9'→跳过 | 'S' | 规则1 |
| '3' | 不变 | +47→'b' | 'b'<'9'→跳过 | 'b' | 规则2 |
| '4' | 不变 | +47→'c' | 'c'<'9'→跳过 | 'c' | 规则2 |
| '5' | 不变 | +47→'d' | 'd'<'9'→跳过 | 'd' | 规则2 |
| '6' | 不变 | 不变 | +65→'w' | 'w' | 规则3 |
| '7' | 不变 | 不变 | +65→'x' | 'x' | 规则3 |
| '8' | 不变 | 不变 | +65→'y' | 'y' | 规则3 |
| '9' | 不变 | 不变 | 不变 | '9' | 保持 |
应用到"4066552172":
| 位置 | 输入 | 输出 |
|---|---|---|
| 0 | 4 | c |
| 1 | 0 | Q |
| 2 | 6 | w |
| 3 | 6 | w |
| 4 | 5 | d |
| 5 | 5 | d |
| 6 | 2 | S |
| 7 | 1 | R |
| 8 | 7 | x |
| 9 | 2 | S |
最终哈希: cQwwddSRxS
算法特征总结:
有限字符集: 哈希只包含10个字符{Q, R, S, b, c, d, w, x, y, 9}
确定性: 相同密码总产生相同哈希(无盐值)
简单运算: 仅用加法、乘法、异或,无复杂变换
短哈希: 最多10位长度
这些特征预示着算法的安全性较弱!
理解了算法原理后,让我们用Python实现它:
def vx_hash(password):
"""
VxWorks loginDefaultEncrypt 算法的Python实现
Args:
password: 密码字符串 (必须8-40字符)
Returns:
哈希字符串,如果密码长度无效则返回None
"""
# 参数验证
if len(password) < 8 or len(password) > 40:
return None
magic = 0x1E3A1D5 # 31695317
password_int = 0
# 步骤1: 计算加权字符和
for i in range(len(password)):
password_int += int(ord(password[i]) * (i + 2) ^ (i + 1))
# 步骤2: 乘以魔数并掩码到32位
temp = str((int(password_int * magic) & 0xffffffff))
# 步骤3: 字符变换
output = ''
for c in temp:
if c < '3':
output += chr(ord(c) + ord('!')) # +33
elif c < '6':
output += chr(ord(c) + ord('/')) # +47
elif c < '9':
output += chr(ord(c) + ord('A')) # +65
else:
output += c # '9'保持不变
return output
Python关键函数说明:
ord(字符): 获取字符的ASCII值
>>> ord('F')
70
chr(数值): 将ASCII值转换为字符
>>> chr(70)
'F'
& 0xffffffff: 32位掩码,相当于模2^32
在破解前,必须验证我们的实现是正确的。使用已知的VxWorks默认密码测试:
# 测试脚本
test_cases = [
("fdrusers", "ycwxQxSS9"), # VxWorks默认用户
("targettarget", "Sxddcd9cSQ"), # 常见测试密码
("FLAGKNXY", "cQwwddSRxS"), # 题目目标(稍后解释如何知道)
]
print("VxWorks Hash 算法验证:")
print("=" * 70)
print(f"{'密码':<20} {'预期哈希':<15} {'计算结果':<15} {'状态':<5}")
print("-" * 70)
for pwd, expected in test_cases:
result = vx_hash(pwd)
status = "" if result == expected else ""
print(f"{pwd:<20} {expected:<15} {result:<15} {status:<5}")
运行结果:
VxWorks Hash 算法验证:
======================================================================
密码 预期哈希 计算结果 状态
----------------------------------------------------------------------
fdrusers ycwxQxSS9 ycwxQxSS9
targettarget Sxddcd9cSQ Sxddcd9cSQ
FLAGKNXY cQwwddSRxS cQwwddSRxS
所有测试通过!算法实现正确,可以进入破解阶段。
新手提示: 在逆向工程中,验证算法正确性是关键步骤。使用已知输入输出对进行测试,可以确保理解无误。
现在我们面临一个经典的密码破解问题:
已知:
目标哈希:cQwwddSRxS
哈希算法:vx_hash()函数
密码长度: 8-40字符
未知:
密码内容: ?
问题: 如何高效找到密码?
让我们设计一个渐进式破解策略:
策略1: 字典攻击 (最快)
↓ 失败
策略2: 智能暴力破解 (针对性)
↓ 失败
策略3: 全量暴力破解 (保底)
字典攻击利用"人性" - 人们倾向于使用容易记忆的密码。
构建字典:
wordlist = [
# VxWorks常见默认密码
"password", "vxworks", "targetvx", "windrive",
"admin123", "12345678", "adminadmin",
# 工控系统常见密码
"scadadmin", "sysadmin", "rootroot",
"targettarget", "operator", "engineer",
# CTF常见密码模式
"ctfctfctf", "flagflag", "testtest", "hackhack",
"password123", "qwertyui", "asdfghjk",
]
执行攻击:
target_hash = "cQwwddSRxS"
print("策略1: 字典攻击")
print("-" * 70)
print(f"目标哈希: {target_hash}")
print(f"字典大小: {len(wordlist)} 个密码\n")
found = False
for idx, pwd in enumerate(wordlist, 1):
# 跳过太短的密码
if len(pwd) < 8:
continue
result = vx_hash(pwd)
# 检查是否匹配
if result == target_hash:
print(f"\n 字典攻击成功!")
print(f" 密码: {pwd}")
found = True
break
else:
# 显示部分尝试过程
print(f" [{idx:2d}] {pwd:<20} → {result}")
if not found:
print(f"\n 字典攻击失败 - 密码不在字典中")
运行结果:
策略1: 字典攻击
----------------------------------------------------------------------
目标哈希: cQwwddSRxS
字典大小: 21 个密码
[ 1] password → cSdcSQdwc9
[ 3] targetvx → RyS9SwSQdw
[ 4] windrive → bSb9ywbcR
[ 5] admin123 → RyQQdyyyc9
[10] rootroot → bx9cbxRxRQ
[15] ctfctfctf → RSyy9bQwRS
[21] asdfghjk → RxycdS9ycy
字典攻击失败 - 密码不在字典中
看来这次的密码不是常见密码。
观察题目特征:
这是CTF比赛题目
CTF密码通常以FLAG开头
文件名是key.bin,暗示密钥/密码主题
智能假设: 密码可能是FLAG****格式 (FLAG + 4个字符)
搜索空间分析:
字符集: A-Z大写字母 + 0-9数字 = 36个字符
组合数: 36^4 = 1,679,616 种
预估时间: 1,679,616 / 400,000(次/秒) ≈ 4秒
实现暴力破解:
import itertools
import string
import time
print("\n策略2: 智能暴力破解")
print("-" * 70)
print("假设: 密码格式为 FLAG + 4个字符")
print("字符集: A-Z + 0-9")
charset = string.ascii_uppercase + string.digits # ABCD...Z0123...9
total = len(charset) ** 4
print(f"搜索空间: {total:,} 种组合")
print(f"开始时间: {time.strftime('%H:%M:%S')}\n")
start_time = time.time()
tested = 0
for combo in itertools.product(charset, repeat=4):
pwd = "FLAG" + ''.join(combo)
result = vx_hash(pwd)
tested += 1
# 进度显示(每5万次)
if tested % 50000 == 0:
elapsed = time.time() - start_time
speed = tested / elapsed if elapsed > 0 else 0
eta = (total - tested) / speed if speed > 0 else 0
print(f" 进度: {tested:>8,}/{total:,} ({tested/total*100:5.2f}%) "
f"速度: {speed:>8,.0f}/s 预计: {eta:>5.1f}s", end='\r')
# 检查是否匹配
if result == target_hash:
elapsed = time.time() - start_time
print(f"\n\n 密码破解成功!")
print(f" 密码: {pwd}")
print(f" 哈希: {result}")
print(f" 尝试次数: {tested:,}")
print(f" 耗时: {elapsed:.2f}秒")
print(f" 平均速度: {tested/elapsed:,.0f} 次/秒")
found = True
break
运行结果:
策略2: 智能暴力破解
----------------------------------------------------------------------
假设: 密码格式为 FLAG + 4个字符
字符集: A-Z + 0-9
搜索空间: 1,679,616 种组合
开始时间: 20:15:38
密码破解成功!
密码: FLAGAWYZ
哈希: cQwwddSRxS
尝试次数: 29,402
耗时: 0.07秒
平均速度: 403,259 次/秒
破解成功!密码是FLAGAWYZ!
性能分析:
仅测试了约3万次(总空间的1.75%)就找到答案
Python纯计算速度达到40万次/秒
无需C/C++优化或GPU加速
智能破解的优势:
通过分析题目特征,将搜索空间从天文数字(36^8 = 2.8万亿)缩小到167万,提升效率1600万倍! 这就是"智能"的力量。
让我们验证找到的密码:
password = "FLAGAWYZ"
result = vx_hash(password)
print("\n" + "=" * 70)
print("破解结果验证")
print("=" * 70)
print(f"密码: {password}")
print(f"哈希: {result}")
print(f"目标: cQwwddSRxS")
print(f"匹配: {' 完全匹配!' if result == target_hash else ' 不匹配'}")
输出:
======================================================================
破解结果验证
======================================================================
密码: FLAGAWYZ
哈希: cQwwddSRxS
目标: cQwwddSRxS
匹配: 完全匹配!
任务完成!我们成功找到了能够生成目标哈希的密码。
为了完全理解算法,让我们手工验证一遍"FLAGKNXY"(我们很快会解释为什么选这个密码)的完整计算过程。
密码: FLAGKNXY
详细计算过程:
位置 字符 ASCII 权重 XOR值 计算公式 乘法结果 异或结果
---- ---- ----- ----- ------ ------------------- --------- --------
0 F 70 2 1 70 × 2 ⊕ 1 140 141
1 L 76 3 2 76 × 3 ⊕ 2 228 230
2 A 65 4 3 65 × 4 ⊕ 3 260 263
3 G 71 5 4 71 × 5 ⊕ 4 355 359
4 K 75 6 5 75 × 6 ⊕ 5 450 455
5 N 78 7 6 78 × 7 ⊕ 6 546 548
6 X 88 8 7 88 × 8 ⊕ 7 704 711
7 Y 89 9 8 89 × 9 ⊕ 8 801 809
总和: 3516
异或计算详解(以位置0为例):
140的二进制表示:
128 64 32 16 8 4 2 1
1 0 0 0 1 1 0 0 = 140
1的二进制表示:
128 64 32 16 8 4 2 1
0 0 0 0 0 0 0 1 = 1
异或运算(相同为0,不同为1):
128 64 32 16 8 4 2 1
1 0 0 0 1 1 0 1 = 141
加权和总计: 141 + 230 + 263 + 359 + 455 + 548 + 711 + 809 = 3516
加权和 × 魔数:
3516 × 31695317 = 111,440,734,572
64位完整结果:
十进制: 111,440,734,572
二进制: 0b1100111110010011000101010100101101100 (37位)
十六进制: 0x19F2618AAC
应用32位掩码:
111,440,734,572 & 0xFFFFFFFF
二进制:
原始(37位): 1 1001 1111 0010 0110 0001 1000 1010 1010 1100
掩码(32位): 1111 1111 1111 1111 1111 1111 1111 1111
结果(32位): 1111 0010 0110 0001 1000 1010 1010 1100
十进制: 4,066,552,172
十六进制: 0xF2618AAC
转为字符串:"4066552172"
对字符串"4066552172"的每个数字字符应用变换规则:
位置 输入 判断1(<'3') 判断2(<'7') 判断3(<'9') 输出
---- ---- ----------- ----------- ----------- ----
0 4 否(不变) 是(+47=99) 否(跳过) c
1 0 是(+33=81) 否(跳过) 否(跳过) Q
2 6 否(不变) 否(不变) 是(+65=119) w
3 6 否(不变) 否(不变) 是(+65=119) w
4 5 否(不变) 是(+47=100) 否(跳过) d
5 5 否(不变) 是(+47=100) 否(跳过) d
6 2 是(+33=83) 否(跳过) 否(跳过) S
7 1 是(+33=82) 否(跳过) 否(跳过) R
8 7 否(不变) 否(不变) 是(+65=120) x
9 2 是(+33=83) 否(跳过) 否(跳过) S
最终哈希: cQwwddSRxS
验证: 与目标哈希完全匹配!
在破解过程中,我们发现密码FLAGAWYZ。但如果继续搜索,会有更多发现:
# 搜索所有产生相同哈希的FLAG****密码
import itertools
import string
target_hash = "cQwwddSRxS"
charset = string.ascii_uppercase # 仅大写字母
collisions = []
print("搜索所有FLAG****碰撞密码...")
for combo in itertools.product(charset, repeat=4):
pwd = "FLAG" + ''.join(combo)
if vx_hash(pwd) == target_hash:
collisions.append(pwd)
print(f"\n发现 {len(collisions)} 个碰撞密码!")
运行结果:
搜索所有FLAG****碰撞密码...
发现 434 个碰撞密码!
震惊!仅FLAG****模式就有434个不同的密码产生相同的哈希!
让我们看看前20个碰撞密码:
print("\n前20个碰撞密码:")
print("-" * 70)
for idx, pwd in enumerate(collisions[:20], 1):
print(f" {idx:3d}. {pwd}")
输出:
前20个碰撞密码:
----------------------------------------------------------------------
1. FLAGAWYZ
2. FLAGAZWY
3. FLAGBYXZ
4. FLAGCVWY
5. FLAGCWYV
6. FLAGCXYW
7. FLAGDUXZ
8. FLAGDWXX
9. FLAGDXTY
10. FLAGDYXV
11. FLAGDZXW
12. FLAGEWVZ
13. FLAGEYXX
14. FLAGEZTY
15. FLAGFTXY
16. FLAGFUZV
17. FLAGFVZW
18. FLAGFWZT
19. FLAGFXZU
20. FLAGFYUZ
...
55. FLAGKNXY ← 这也是一个有效答案!
...
434. FLAGZZTK
关键发现:FLAGKNXY也在碰撞列表中! 这解释了为什么我们在验证算法时使用了这个密码 - 它是碰撞密码之一。
为什么会有如此多的碰撞? 让我们分析碰撞密码的加权和:
print("\n碰撞密码的加权和分析:")
print("-" * 80)
print(f"{'密码':<15} {'加权和':<10} {'魔数乘积':<20} {'32位掩码':<15}")
print("-" * 80)
for pwd in collisions[:10]:
# 计算加权和
password_int = 0
for i in range(len(pwd)):
password_int += int(ord(pwd[i]) * (i + 2) ^ (i + 1))
product = password_int * 0x1E3A1D5
masked = product & 0xFFFFFFFF
print(f"{pwd:<15} {password_int:<10} {product:<20} {masked:<15}")
输出:
碰撞密码的加权和分析:
--------------------------------------------------------------------------------
密码 加权和 魔数乘积 32位掩码
--------------------------------------------------------------------------------
FLAGAWYZ 3516 111440734572 4066552172
FLAGAZWY 3516 111440734572 4066552172
FLAGBYXZ 3516 111440734572 4066552172
FLAGCVWY 3516 111440734572 4066552172
FLAGCWYV 3516 111440734572 4066552172
FLAGCXYW 3516 111440734572 4066552172
FLAGDUXZ 3516 111440734572 4066552172
FLAGDWXX 3516 111440734572 4066552172
FLAGDXTY 3516 111440734572 4066552172
FLAGDYXV 3516 111440734572 4066552172
关键发现: 所有碰撞密码的加权和都是3516!
原因1: 加权和碰撞
不同的字符组合可以产生相同的加权和。例如:
FLAGAWYZ: W(87)×8⊕7 + Y(89)×9⊕8 + Z(90)×10⊕9
FLAGAZWY: Z(90)×8⊕7 + W(87)×9⊕8 + Y(89)×10⊕9
虽然字符顺序不同,但通过不同的权重值,可以凑出相同的总和3516。
这是一个整数分割问题- 有很多种方式组合出和为3516的加权值。
原因2: 32位掩码信息丢失
原始乘积: 111,440,734,572 (37位)
32位掩码: 只保留低32位
丢失的信息: 高5位 (11001)
即使有不同的加权和,乘以魔数后,高位信息被丢弃,可能导致掩码后的值相同。
原因3: 确定性字符变换
步骤3的变换是完全确定的:
"4066552172" → "cQwwddSRxS"
只要32位掩码值相同,最终哈希必然相同。
在真实VxWorks设备上:
管理员设置密码为FLAGKNXY
哈希存储为cQwwddSRxS
攻击者用FLAGAWYZ,FLAGAZWY等434个密码中任何一个都能登录!
由于算法无盐值(salt),攻击者可以预先计算彩虹表:
密码哈希数据库:
password → cSdcSQdwc9
admin123 → RyQQdyyyc9
...
FLAGAWYZ → cQwwddSRxS
FLAGAZWY → cQwwddSRxS
...
获取哈希后,直接查表即可破解,无需实时计算。
攻击者不需要找到真实密码:
真实密码可能是40字符的强密码
但攻击者只需找到任意8字符的碰撞即可登录
** 现代密码学的防御措施**:
| 防御措施 | VxWorks算法 | 现代算法(bcrypt) |
|---|---|---|
| 盐值(Salt) | 无 | 有(随机,每个密码不同) |
| 迭代次数 | 1次 | 可配置(2^n次,如2^12=4096次) |
| 输出空间 | 10^10 | 2^184 |
| 抗碰撞 | 弱 | 强 |
| 抗彩虹表 | 弱 | 强(有盐值) |
| 计算成本 | 0.0025ms | 可调(如100ms) |
通过本次实战,我们学到了:
使用file、hexdump、strings等基础工具
从文件头部提取设备型号、编译时间等元数据
通过字符串定位关键函数
识别VxWorks、PowerPC等嵌入式技术
利用公开资料(CVE、GitHub、文档)研究算法
理解加权求和、魔数乘法、字符变换等技术
将C算法翻译为Python实现
使用已知测试用例验证算法正确性
字典攻击 - 利用人性弱点,测试常见密码
智能暴力破解 - 根据题目特征缩小搜索空间
性能优化 - Python实现达到40万次/秒
碰撞分析 - 发现算法的深层安全弱点
识别哈希碰撞漏洞
量化安全影响(434个碰撞)
对比现代密码学最佳实践
提出安全加固建议
技巧1: 先搜索,后逆向
很多看似神秘的算法,实际上是公开标准或已有文档。善用Google、GitHub可以节省大量时间。
技巧2: 从字符串入手
字符串是固件分析的突破口,能快速定位系统类型、关键函数。
技巧3: 识别题目特征
CTF题目通常有提示:FLAG开头、8字符长度、文件名暗示等。识别这些特征能大幅缩小搜索空间。
技巧4: 验证每一步
用已知数据验证算法实现,确保理解正确,避免在错误方向上浪费时间。
技巧5: 由简到繁
破解策略从简单到复杂:字典→智能暴力→全量暴力,逐步升级。
系统分析工具:
file # 文件类型识别
hexdump # 十六进制查看
strings # 字符串提取
grep # 文本搜索和模式匹配
高级分析工具(本题未用但值得学习):
binwalk # 固件自动分析和提取
IDA Pro # 专业反汇编工具(支持PowerPC)
Ghidra # NSA开源逆向工程平台
radare2 # 开源逆向框架
编程工具:
Python3
- itertools # 组合生成
- string # 字符集定义
- time # 性能测试
- struct # 二进制数据处理
想要继续深入? 这里有一些建议:
基础加强:
二进制基础: 学习十六进制、ASCII、字节序(大小端)
密码学入门: 理解哈希、对称加密、非对称加密
Python进阶: 掌握位运算、字节处理、性能优化
工具进阶:
IDA Pro教程: 学习PowerPC汇编,分析固件代码
binwalk实战: 自动提取路由器、摄像头固件
Ghidra使用: 开源替代IDA,功能强大
实战提升:
CTF平台: pwnable.kr、root-me.org、hackthebox
固件分析: 分析家用路由器、IoT设备固件
漏洞研究: 研究CVE公告,复现历史漏洞
工控安全:
工控协议: Modbus、OPC UA、DNP3
SCADA系统: 监控和数据采集系统安全
关键基础设施: 电力、水厂、化工等系统防护
Q1: 为什么要用32位掩码?
A: 因为VxWorks运行在32位系统上,C语言的unsigned long是32位。大数相乘会溢出,只保留低32位。& 0xFFFFFFFF模拟了这种硬件行为。
Q2: 异或运算在密码学中的作用?
A: 异或(XOR)有三个重要性质:
可逆性:A ⊕ B ⊕ B = A
均匀性: 输出0和1的概率各50%
敏感性: 输入1位变化,输出1位变化
在强密码算法中,异或是核心运算之一。但在VxWorks这个简单算法中,作用有限。
Q3: Python够快吗? 需要用C吗?
A: 对于本题,Python已足够(40万次/秒)。但如果搜索空间更大(如10亿级),可以考虑:
Cython: Python代码编译为C
Numba: JIT编译加速
C/C++重写核心循环
GPU加速(CUDA)
Q4: 如何防御这类弱哈希攻击?
A: 现代密码存储最佳实践:
使用bcrypt、scrypt或Argon2
添加随机盐值(每个密码不同)
多次迭代(增加计算成本,如10,000次)
实施密码策略(强制复杂密码)
启用多因素认证(2FA)
Q5: VxWorks现在还用这个算法吗?
A: VxWorks 6.9之后改用了SHA-256+盐值,但:
仅单次迭代,仍不够安全
大量老设备仍使用旧算法
升级固件成本高,很多设备未更新
恭喜你完成了这次工控固件逆向之旅!
我们的旅程:
固件文件 (key.bin)
↓
字符串分析 → 发现loginDefaultEncrypt
↓
公开资料 → CVE-2010-2965漏洞
↓
算法实现 → Python vx_hash()
↓
智能破解 → FLAGAWYZ (0.07秒)
↓
深度分析 → 434个碰撞密码
↓
安全评估 → 算法存在严重弱点
核心数据:
目标哈希:cQwwddSRxS
破解密码:FLAGAWYZ
破解速度: 403,259 次/秒
破解时间: 0.07秒
发现碰撞: 434个(仅FLAG****模式)
碰撞原因: 加权和相同(3516)
这不仅仅是一道CTF题目,而是一个真实存在的工控系统漏洞(CVE-2010-2965)。
现实影响:
全球数百万台VxWorks设备在使用
电力系统、水处理厂、化工厂等关键基础设施
弱密码算法可能被攻击者利用
很多老设备因成本问题未更新固件
我们的责任:
作为安全研究者,我们不仅要发现漏洞,更要:
负责任地披露漏洞
推动使用现代安全算法
提高工控系统的安全意识
守护关键基础设施的安全
这只是安全研究的开始,更多挑战等待着你:
下一步建议:
参加CTF比赛,提升实战能力
分析更多固件(路由器、摄像头、智能家居)
深入学习密码学、操作系统、网络协议
关注工控安全,参与漏洞研究
加入安全社区,与同行交流
推荐资源:
CTF平台: CTFtime.org、pwnable.kr
学习网站: CryptoHack、OverTheWire
会议: DEF CON、Black Hat、S4x
论坛: r/ReverseEngineering、看雪论坛
算法实现 (vx_hash.py):
#!/usr/bin/env python3
"""VxWorks loginDefaultEncrypt 算法实现"""
def vx_hash(password):
"""
VxWorks loginDefaultEncrypt 哈希函数
参数:
password: 密码字符串(8-40字符)
返回:
哈希字符串,如果密码长度无效则返回None
"""
if len(password) < 8 or len(password) > 40:
return None
magic = 0x1E3A1D5 # 31695317
password_int = 0
# 步骤1: 加权和
for i in range(len(password)):
password_int += int(ord(password[i]) * (i + 2) ^ (i + 1))
# 步骤2: 魔数乘法与掩码
temp = str((int(password_int * magic) & 0xffffffff))
# 步骤3: 字符变换
output = ''
for c in temp:
if c < '3':
output += chr(ord(c) + ord('!'))
elif c < '6':
output += chr(ord(c) + ord('/'))
elif c < '9':
output += chr(ord(c) + ord('A'))
else:
output += c
return output
if __name__ == "__main__":
# 验证
assert vx_hash("FLAGAWYZ") == "cQwwddSRxS"
print(" 算法验证通过")
破解脚本 (crack.py):
#!/usr/bin/env python3
"""智能密码破解"""
import itertools
import string
from vx_hash import vx_hash
target = "cQwwddSRxS"
charset = string.ascii_uppercase + string.digits
print(f"目标哈希: {target}")
print("搜索FLAG****模式...")
for combo in itertools.product(charset, repeat=4):
pwd = "FLAG" + ''.join(combo)
if vx_hash(pwd) == target:
print(f" 找到密码: {pwd}")
break
完整的434个碰撞密码已保存在collision_passwords.txt文件中。
样本展示(前10个):
1. FLAGAWYZ
2. FLAGAZWY
3. FLAGBYXZ
4. FLAGCVWY
5. FLAGCWYV
6. FLAGCXYW
7. FLAGDUXZ
8. FLAGDWXX
9. FLAGDXTY
10. FLAGDYXV
...
55. FLAGKNXY
...
434. FLAGZZTK
官方文档:
Wind River VxWorks Documentation
VxWorks Programmer's Guide - loginLib
安全公告:
CVE-2010-2965: VxWorks weak password hashing
CERT VU#840249: Wind River Systems vulnerability
CISA ICS-ALERT-10-214-01
开源项目:
GitHub: dchest/historic-password-hashes
GitHub: Fluepke/VX-Works-Password-Hash-Cracker
学习资源:
CTF-Wiki: 固件分析入门
看雪论坛: 嵌入式安全板块
Industrial Cyber: 工控安全新闻
文章信息:
标签:VxWorks固件逆向密码破解工控安全CTF
题目: CTF Reverse Challenge - VxWorks Password Hash
难度: (中级)
版权声明:
本文仅供安全研究和教育目的使用。对VxWorks设备的安全测试应获得合法授权。未经授权访问计算机系统是违法行为。
"安全研究不是为了破坏,而是为了更好地守护"