D~DIDI~DIDIDI!!!!

0%

CBC字节翻转攻击

0x01 CBC加密

1

首先贴上一张原理图,其中:

  1. Plaintext:待加密的数据块
  2. Initialization Vector(IV):用于随机化加密的比特块(初始化向量)
  3. key:随机生成的密钥
  4. Ciphertext:加密后的数据

加密过程:

  1. 将需要加密的明文分组(常见的为16字节一组),位数不足使用特殊字符进行填充,该原理图中就分为了3组
  2. 生成一个初始的随机向量(IV)和一个密钥(key)
  3. 将IV与第一组明文分组进行XOR(异或)运算
  4. 使用key对xor后产生的密文进行加密,产生一个Ciphertext
  5. 将上面产生的Ciphertext作为新的IV与第二组的明文进行xor运算,得到一个密文
  6. 使用key对5中产生的密文进行加密,再次得到一个Ciphertext
  7. 重复5和6两个步骤,得到第三个分组产生的Ciphertext
  8. 将最开始产生的IV和机密后的几个Ciphertext拼接到一起,得到最终的密文

这种加密模式的最重要的特点就是:使用前一块的密文来产生后一块的密文

0x02 CBC解密

2

原理图如上,解密过程:

  1. 从密文中提取出IV,然后将密文进行分组
  2. 使用key对第一组Ciphertext进行解密,然后与IV进行xor得到一个明文
  3. 使用key对第二组Ciphertext进行解密,再与2中Ciphertext进行xor得到一个明文
  4. 重复上诉步骤,得到第三组明文
  5. 将三组明文拼接,得到待求明文

0x03 CBC翻转攻击

3

我们已经知道前一块的Ciphertext会用来产生下一块明文,如果我们改变前一块Ciphertext中的一个字符,再去和下一个区块得到的密文进行xor,就可以得到一个不同的明文,而这个明文我们是可以控制的,我们就可以利用这一点对服务端进行欺骗或者绕过过滤器。

0x04 实例分析

打开页面后

4

源码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}

function login($info){
$iv = get_random_iv();
$plain = serialize($info);
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv);
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher));
}

function check_login(){
if(isset($_COOKIE['cipher']) && isset($_COOKIE['iv'])){
$cipher = base64_decode($_COOKIE['cipher']);
$iv = base64_decode($_COOKIE["iv"]);
if($plain = openssl_decrypt($cipher, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv)){
$info = unserialize($plain) or die("<p>base64_decode('".base64_encode($plain)."') can't unserialize</p>");
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}

function show_homepage(){
if ($_SESSION["username"]==='admin'){
echo $flag;
}else{
echo '<p>hello '.$_SESSION['username'].'</p>';
echo '<p>Only admin can see flag</p>';
}
echo '<p><a href="loginout.php">Log out</a></p>';
}

if(isset($_POST['username']) && isset($_POST['password'])){
$username = (string)$_POST['username'];
$password = (string)$_POST['password'];
if($username === 'admin'){
exit('<p>admin are not allowed to login</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
}else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}else{
echo '<body class="login-body">
<div id="wrapper">
<div class="user-icon"></div>
<div class="pass-icon"></div>
<form name="login-form" class="login-form" action="" method="post">
<div class="header">
<h1>Login Form</h1>
<span>Fill out the form below to login to my super awesome imaginary control panel.</span>
</div>
<div class="content">
<input name="username" type="text" class="input username" value="Username" onfocus="this.value=\'\'" />
<input name="password" type="password" class="input password" value="Password" onfocus="this.value=\'\'" />
</div>
<div class="footer">
<input type="submit" name="submit" value="Login" class="button" />
</div>
</form>
</div>
</body>';
}
}
?>

在这个题目中,要求登录名为admin则输出flag,但是题目禁止了admin登陆,所以这里就可以用到CBC字节翻转攻击。

3

这里将用户名和密码存入数组,序列化之后进行AES-CBC模式的加密,其中IV和密文都以cookie方式存储,可以控制,所以就存在攻击的可能。

因为需要翻转的目标是admin,我已我们这里的登录名最好也是5位,我使用

​ 用户名:emmmm

​ 密码:qwert

6

登录后用户名和密码被存入数组然后序列化变成:

a:2:{s:8:"username";s:5:"emmmm";s:8:"password";s:5:"qwert";}

我们需要翻转的目标为:

a:2:{s:8:"username";s:5:"admin";s:8:"password";s:5:"qwert";}

7

处理过程:

  1. 首先对明文进行分组,16字节一组,分为4组

    • a:2:{s:8:”userna
    • me”;s:5:”emmmm”;
    • s:8:”password”;s
    • :5:”qwert”;}
  2. 根据CBC攻击原理,我们只需要修改第一组的Ciphertext对应第二组’skctf’的位置的Ciphertext,就可以实现第二组明文的改变,这里我们需要修改的位置为第一组Ciphertext的10-14位

  3. 利用脚本重新生成Ciphertext

1
2
3
4
5
6
7
8
import base64
cipher = 'rsvYuT0qD7XdLXi8D3DSs6J4xWpA82s6b6ofC/6JKJjxdijo0yzIaRW8D/TFyrXECDQWwUh4dDtXs+tCQq94WQ=='.decode('base64')
old = "me\";s:5:\"emmmm\";"
new = "me\";s:5:\"admin\";"
for i in xrange(16):
cipher = cipher[:i] + chr(ord(cipher[i]) ^ ord(old[i]) ^ ord(new[i])) + cipher[i+1:]
print cipher.encode('base64').strip()

​ 执行脚本得到:rsvYuT0qD7XdKXG8C3PSs6J4xWpA82s6b6ofC/6JKJjxdijo0yzIaRW8D/TFyrXECDQWwUh4dDtXs+tCQq94WQ==

  1. 这样我们修改第一区块的密文就可以打到修改第二块密文的效果,但是这时候也同时破坏了第一块的明文,我们需要对第一块的数据进行恢复,用上面生成的密文修改cookie的cipher(需要将符号url Encode),访问,返回:

8

正好获取到第一次翻转后的明文:RKIHz2N76jZOTfUPD/9/n21lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6InF3ZXJ0Ijt9

  1. 通过修改IV来修改第一块的明文:

9

利用一下脚本重新生成IV:

1
2
3
4
5
6
7
8
9
# -*- coding: utf-8 -*-
import base64
plain = 'RKIHz2N76jZOTfUPD/9/n21lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6InF3ZXJ0Ijt9'.decode('base64')
iv = '9TKkeY91bZat0arrAaKiQw=='.decode('base64')
old = plain[:16]
new = "a:2:{s:8:\"userna";
for i in xrange(16):
iv = iv[:i] + chr(ord(iv[i]) ^ ord(old[i]) ^ ord(new[i])) + iv[i+1:]
print iv.encode('base64').strip()

运行得到:

0KqRjJd9vZjZviqXay+zvQ==

  1. s使用这里得到的VI和上面的cipher替换cookie

10

发包后,响应成功,应为我这里本地只有一个php文件,所以无法返回flag,但其实CBC攻击已经完成,已经作为admin登陆

参考链接:

https://blog.csdn.net/csu_vc/article/details/79619309

https://p0sec.net/index.php/archives/99/

https://mochazz.github.io/2018/05/06/CBC字节翻转攻击/#CBC原理

2019040126