proc m4ZQu {password LZQjH} {
set ::maui::util::H6VeX ""
set ::maui::util::EL0BW [string repeat \0 32]
package require tcltwofish
package require sha256
if {[string length $LZQjH] == 0} {
error "Unable to initialize encryption - missing essential data"
}
//1.展开payload 获取相关参数
if {[binary scan $LZQjH Ia16a32a64a32a* times iv passwordKey encryptedKey payloadIVsHash encryptedPayloadIVs] < 6} {
error "Unable to initialize encryption - header missing or corrupt"
}
//2.使用输入的密码计算sha256,然后根据迭代次数times加密passwordkey,最后计算结果的sha256
set iHcWR [a64bL $password $passwordKey $iv $times]
//3.取上一步中的后32字节
set BJvoG [string range [tcltwofish::decrypt $iHcWR $encryptedKey] 32 63]
//4.使用上一步中的结果作为key 循环times/64次 解密encryptedPayloadIVs
//这一步没有指定iv 实际过程为16个字节的0x00
for {set i 0} {$i < $times} {incr i 64} {
set encryptedPayloadIVs [tcltwofish::decrypt $BJvoG $encryptedPayloadIVs]
}
//5.获取32字节后的数据
set ZQpQw [string range $encryptedPayloadIVs 32 end]
//6.计算上一步结果的sha256,和预置的hash比对,一致则密码正确
if {[sha2::sha256 -bin $ZQpQw] != $payloadIVsHash} {
maui::d_RUj "Invalid payload password"
}
//7.使用第五步中的结果作为后续选取iv的数据
set ::maui::util::H6VeX $ZQpQw
//8.使用第三步中的结果作为密钥
set ::maui::util::EL0BW $BJvoG
}
然而,根据下面的帖子总结,ida的密码选取字符为58个,密码长度为14个字符,这个测试结果是非常大的。
https://devco.re/blog/2019/06/21/operation-crack-hacking-IDA-Pro-installer-PRNG-from-an-unusual-way/
https://bbs.kanxue.com/thread-277712-1.htm
//根据输入密码生成payload
proc yOZ7q {password {times 16000}} {
set ::maui::util::H6VeX ""
set ::maui::util::EL0BW [string repeat \0 32]
package require tcltwofish
package require sha256
set BJvoG [qA4kM 32]
set passwordKey [qA4kM 32]
set ZQpQw [qA4kM [expr {16 * 256}]]
set payloadIVsHash [sha2::sha256 -bin $ZQpQw]
set encryptedPayloadIVs "[qA4kM 32]$ZQpQw"
for {set i 0} {$i < $times} {incr i 64} {
set encryptedPayloadIVs [tcltwofish::encrypt $BJvoG $encryptedPayloadIVs]
}
set iv [qA4kM 16]
set iHcWR [a64bL $password $passwordKey $iv $times]
set encryptedKey [tcltwofish::encrypt $iHcWR "[qA4kM 32]$BJvoG"]
set LZQjH [binary format Ia16a32a64a32a* $times $iv $passwordKey $encryptedKey $payloadIVsHash $encryptedPayloadIVs]
set ::maui::util::H6VeX $ZQpQw
set ::maui::util::EL0BW $BJvoG
return $LZQjH
}
//生成随机数据
proc qA4kM {length} {
set YKL8w ""
if {[RLPut unix]} {
foreach xML4B {/dev/urandom /dev/random} {
if {[catch {
set WyLEv [open $xML4B r]
fconfigure $WyLEv -translation binary
set YKL8w [read $WyLEv $length]
close $WyLEv
}]} {
catch {close $WyLEv}
} else {
break
}
}
} else {
}
if {[string length $YKL8w] != $length} {
incr length -[string length $YKL8w]
while {$length >= 2} {
append YKL8w [binary format S [expr {int([maui::util::rand] * 0x10000)}]]
incr length -2
}
if {$length > 0} {
append YKL8w [binary format c [expr {int([maui::util::rand] * 0x100)}]]
}
}
return $YKL8w
}
proc rand {} {
if {[maui::util::RLPut "windows"] && [info commands ::bitrock::secure::rand_s] != ""} {
if {[catch {
set r [::bitrock::secure::rand_s]
} V77Ls]} {
maui::util::debug "failed to call rand_s: $V77Ls"
set r [expr {rand()}]
}
} else {
set r [expr {rand()}]
}
return $r
}
BJvoG
也是随机生成的,如果这部分随机数生成流程能复现那也是可以破解的。仔细看随机数的生成,已经排除了以前的LCG随机数生成算法,换成了更安全的,前后状态无影响的方式,linux或unix下用/dev/random或者/dev/urandom,windows下调用rand_s。https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/rand-s?view=msvc-170
proc MmyBM {compressionAlgorithm command data} {
if {[catch {
//1.压缩数据
if {($compressionAlgorithm == "lzma") || ($compressionAlgorithm == "lzma-ultra") || ($compressionAlgorithm == "lzham") || ($compressionAlgorithm == "lzham-ultra")} {
set data [eval [concat $command [list $data]]]
} else {
set data [vfs::zip -mode compress -nowrap 1 $data]
}
//2.1 前面添加四个字节的长度,然后末尾补全32的倍数
//2.2 前面添加32字节的随机数据
set data [qA4kM 32][UOijU $data 32]
//3.在H6VeX中截取16字节的iv
set PG2JA [expr {int([maui::util::rand] * 0x100)}]
set iv [string range $::maui::util::H6VeX [expr {$PG2JA * 16}] [expr {$PG2JA * 16 + 15}]]
//4.计算crc32
set hbA8n [format 0x%08x [expr {[zlib crc32 $data] & 0xffffffff}]]
//5.使用EL0BW中的数据作为key 加密
set data [tcltwofish::encrypt $::maui::util::EL0BW $data iv]
//6. 前面填充四个字节的crc32以及选取iv的位置,共加了五个字节
set YKL8w [binary format Ica* $hbA8n $PG2JA $data]
} V77Ls]} {
set ykaki $::errorCode
set qXoNi $::errorInfo
maui::util::debug "encryptPayload: error encrypting: $V77Ls"
error $V77Ls $qXoNi $ykaki
}
return $YKL8w
}
proc UOijU {data DeSRF} {
//1. 将四字节的长度添加在数据前
set data [binary format Ia* [string length $data] $data]
//2. 计算末尾添加padding的长度
set add [expr {($DeSRF - ([string length $data] % $DeSRF)) % $DeSRF}]
//3. 生成随机padding并附加在末尾
append data [qA4kM $add]
return $data
}
32字节随机数据+4字节数据+$data+最后补全32字节的padding
然后使用twofish进行加密。而twofish是分组加密算法,所以加密过程就是下面这样的。通过在前面添加32字节的随机数据,猜测是为了避免明文攻击。因为很多文件前面都有固定字节的数据。
比如sqlite3数据库的前16字节固定为 SQLite format 3x00
,在ECB模式下,相同密钥加密任何sqlite3数据库的前16个字节都是一样的。所以衍生出了CBC模式,在每个分组加密之前用iv或者上一个密文分组异或明文以产生不一样的数据。
然而,这个例子中前面32个字节的随机数的使用却颇有点画蛇添足的味道。
首先,它不能掩盖明文数据或者阻止明文攻击(稍后提到),其次因为这32字节的加密数据,我们在后面进行密码分析的时候可以忽略了原本的初始iv,初始iv来自哪里,来自输入密码后进行的16000次twofish加密和256次twofish解密后生成的0x1000字节的encryptedPayloadIVs,而现在却可以跳过了,从而暴露了它的阿克琉斯之踵。根据密码算法,现在解密每一段文件数据时只需要从第三个分组的数据开始解密即可,输入iv来源于上一个分组的的密文(即第二个分组),输入的key对所有文件都是一样的(也就是本次要分析出来的目标)。
00 0x xx xx
。如果能将每段加密数据对应到具体的文件,可能还会有更多的明文信息。第一种,根据输入密码爆破,工作量太大,PC上几乎不可能成功。
第二种,随机数攻击。新版本的安装程序摒弃了LCG算法的使用,使用了更加安全的随机数生成方式。
第三种,根据部分明文信息和1212个样本数据爆破32字节密钥,爆破的过程中每次验证只需要一次twofish解密16个字节即可。再配合密码差分分析,可以减少一定的尝试次数。有熟悉angr的大佬也可以尝试一下形式化验证的方式去破解。
看雪ID:龙卷风呼呼呼
https://bbs.kanxue.com/user-home-980588.htm
# 往期推荐
球分享
球点赞
球在看
点击阅读原文查看更多
原文始发于微信公众号(看雪学苑):ida81输入密码验证算法分析以及破解思路