Bugku-encrypt

昨天做了bugku上的一道关于加密解密的题,与我寒假时做到另一道题很像(当时没做出来),主要就是分析代码,利用已知条件去推未知条件,然后用逆向思维去写反解脚本。写一下思路

PHP_encrypt_1(ISCCCTF)

fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

下载下来一个php文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function encrypt($data,$key)
{
$key = md5('ISCC');
$x = 0;
$len = strlen($data);
$klen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
for ($i=0; $i < $len; $i++) {
$str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
}
return base64_encode($str);
}
?>

根据代码,首先可以判断出$data这个传进函数中的变量就是flag,再看看已知的:

$key = md5('ISCC'); # 729623334f0aa2784a1599fd374c120d
$x = 0;
$klen = strlen($key);  # 32
base64_decode($str)

其中,将最后得到的字符串进行base64解码得到了一堆乱符,那就转换为ascii符:

1
2
3
4
5
6
7
8
9
10
<?php
$str = 'fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=';
$decodes = base64_decode($str);

for ($i=0; $i<strlen($decodes); $i++)
{
echo ord($decodes[$i]);
echo " ";
}
?>

得到:125 30 26 29 108 46 20 38 24 87 39 69 71 19 42 30 24 66 40 27 30 31 87 91 23 40 43 71 18 22 39 85 24 22 26 45 22 48

函数中有两个for循环,分析第一个for循环:

1
2
3
4
5
6
7
8
for ($i=0; $i < $len; $i++) { 
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}

$key=md5('ISCC')$klen=32$char .= $key[$x]即为$char = $char.$key[$x],$char在函数里面是没有定义的,运行的时候php会抛出Notice,但还是会输出结果,就是把$char当成空字符串处理:

如,运行这行代码:

echo $char.'hello';

会输出:

Notice: Undefined variable: char in D:\phpStudy\PHPTutorial\WWW\bugkus.php on line 24
hello

如果$len<=32,即flag的长度不超过32位,那么最后得到的$char即为md5(‘ISCC’)的前$len位;如果$len>32,那么得到的$char前32位就是md5(‘ISCC’),32位之后的为md5(‘ISCC’)从头开始的数据

下面分析第二个循环:

1
2
3
for ($i=0; $i < $len; $i++) {
$str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
}

刚才得到了$str对应的ascii码,而且$char处于半知状态(知道其内容,但不知道其长度);但是细心一点会发现,得到的$str的长度就等于$len的值,即

strlen('fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=')

那么也就知道了$char的值,则可以求出$data:

根据$str .= chr((ord($data[$i]) + ord($char[$i])) % 128),$char是已知的,$str是已知的,只有一个位置的$data,化简后式子为:

ord($str[$i]) = (ord($data[$i]) + ord($char[$i])) % 128

这个式子用简单数学的方法想一下:

(5 + 7) % 10 = 2
5 = 10 + 2 - 7

由于ascii码范为0-127,情况正好符合上面的10以内的运算,则:

ord($data[$i]) = 128 + ord($str[$i]) - ord($char[$i])

写脚本跑出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$result = [125, 30, 26, 29, 108, 46, 20, 38, 24, 87, 39, 69, 71, 19, 42, 30, 24, 66, 40, 27, 30, 31, 87, 91, 23, 40, 43, 71, 18, 22, 39, 85, 24, 22, 26, 45, 22, 48];

$flag = [];
$char = '';
$key = md5('ISCC'); //729623334f0aa2784a1599fd374c120d
$x = 0;
$len = 38;
$klen = strlen($key); //32

for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}

$char .= $key[$x];
$x+=1;
}

for ($i=0; $i < $len; $i++) {
$flag[$i] = $result[$i] + 128 - ord($char[$i]);
echo chr($flag[$i]);
}
echo "<br>";

得到:

Ælagº{asdqwdfasfdawfefqwdqwdadwqadawd}