CBC字节翻转攻击

CBC字节翻转攻击

参考链接: https://rhythmmark.github.io//bugku-web-login4-WP/ 这个贼详细

https://www.cnblogs.com/s1ye/p/9021202.html

题目代码

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
<?php
include 'sqlwaf.php';
define("SECRET_KEY", "................");
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){
$iv='';
for($i=0;$i<16;$i++){
$iv.=chr(rand(1,255));
}
return $iv;
// 随机生成从1到255中的一个数字并且将它进行ascii编码,重复16次,将它们合在一起为$vi,返回$vi
}
function login($info){
$iv=get_random_iv(); #初始向量
$plain = serialize($info); #info序列化后得到明文
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, OPENSSL_RAW_DATA, $iv); #CBC模式下AES的加密明文
$_SESSION['username'] = $info['username'];
setcookie("iv", base64_encode($iv));
setcookie("cipher", base64_encode($cipher)); // 初始向量加密,aes后的明文再加密(base64)
}
function show_homepage(){
if ($_SESSION["username"]==='admin'){ ## 得到flag条件是我们Session里面的username为admin
echo '<p>Hello admin</p>';
echo '<p>Flag is *************</p>';
}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>';
die();
}
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!"); ## 这里全部解密,反序列化,来检查login
}
}
}

if (isset($_POST['username'])&&isset($_POST['password'])) {
$username=waf((string)$_POST['username']);
$password=waf((string)$_POST['password']);
if($username === 'admin'){
#如果username是admin的话,就会显示下面这句话然后退出
exit('<p>You are not real admin!</p>');
}else{
$info = array('username'=>$username,'password'=>$password);
login($info);
show_homepage();
}
}
else{
if(isset($_SESSION["username"])){
check_login();
show_homepage();
}
}

这里我加了一些注释方便理解代码

  • define(“METHOD”, “aes-128-cbc”) 说明采用的是aes-128-cbc模式加密
  • 在show_homepage()函数中,只要$_SESSION[“username”]===’admin’就可以得到flag
  • 然后观察哪里会用到show_homepage()这个函数

image

  • 如图可见有两个地方,这两个分别有login和check_login函数,其实就是互逆关系,下面给出login大概步骤图

image

  • login()函数中
    1
    $_SESSION['username'] = $info['username'];

但后面说传入username的若为admin,则exit,显然不行的

1
2
3
if($username === 'admin'){
#如果username是admin的话,就会显示下面这句话然后退出
exit('<p>You are not real admin!</p>');

  • 另外一个check_login其实就是login逆过来了

在正常情况下,如果我们正正经经地post一个账户名和口令字,这个网页就会把你post的东西经过login那一系列的变化,用cookie保存下来,然后下次我们不需要再post东西了,凭借cookie,网站通过check_login得到我们post过得账户名和口令字,就能知道我们的身份。

这样经过check_login函数之后,$_SESSION[‘username’]就还是我们最开始post的那个username。

但我们是正经人吗?我们是要搞事get flag的人,所以我们就要想如何把$_SESSION[‘username’]变成admin,

即使我们最开始post的可能是admi2,

但是它经过了AES加密和解密

这里就要用到CBC字节翻转攻击

攻击的point就在check_login()的解密过程里。

总的来说就是只有admin用户才能读取flag,但是admin用户又不允许登录。虽然相互矛盾,由于题目用到了aes的cbc模式加密,所以我们可以利用cbc字节翻转攻击来得到我们想要的明文。

CBC字节翻转攻击原理

image

CBC,就是每个明文块先与前一个密文块进行异或后,再进行加密

(下面复制一个例子参考)

【复制粘贴开始】

比方说,我们有这样的明文序列:

a:2:{s:4:”name”;s:6:”sdsdsd”;s:8:”greeting”;s:20:”echo ‘Hello sdsdsd!’”;}

我们的目标是将“s:6”当中的数字6转换成数字“7”。我们需要做的第一件事就是把明文分成16个字节的块:

• Block 1:a:2:{s:4:”name”;

• Block 2:s:6:”sdsdsd”;s:8

• Block 3::”greeting”;s:20

• Block 4::”echo ‘Hello sd

• Block 5:sdsd!’”;}

因此,我们的目标字符位于块2,这意味着我们需要改变块1的密文来改变第二块的明文。

有一条经验法则是(注:结合上面的说明图可以得到),你在密文中改变的字节,只会影响到在下一明文当中,具有相同偏移量的字节。所以我们目标的偏移量是2:

• [0] = s

• 1 = :

• 2 =6

因此我们要改变在第一个密文块当中,偏移量是2的字节。正如你在下面的代码当中看到的,在第2行我们得到了整个数据的密文,然后在第3行中,我们改变块1中偏移量为2的字节,最后我们再调用解密函数。

$v = “a:2:{s:4:”name”;s:6:”sdsdsd”;s:8:”greeting”;s:20:”echo ‘Hello sdsdsd!’”;}”;

$enc = @encrypt($v);

$enc[2] = chr(ord($enc[2]) ^ ord(“6”) ^ ord (“7”));

$b = @decrypt($enc);

运行这段代码后,我们可以将数字6变为7:

但是我们在第3行中,是如何改变字节成为我们想要的值呢?

基于上述的解密过程,我们知道有,A = Decrypt(Ciphertext)与B = Ciphertext-N-1异或后最终得到C = 6。等价于:

C = A XOR B

所以,我们唯一不知道的值就是A(注:对于B,C来说)(block cipher decryption);借由XOR,我们可以很轻易地得到A的值:

A = B XOR C

最后,A XOR B XOR C等于0。有了这个公式,我们可以在XOR运算的末尾处设置我们自己的值,就像这样:

A XOR B XOR C XOR “7”会在块2的明文当中,偏移量为2的字节处得到7。

【复制粘贴结束】

题目进行CBC字节翻转攻击

这里我们尝试admi2,首先在本地phpstudy生成明文序列

1
2
3
4
<?php
$info = array('username'=>'admi2','password'=>'Password');
echo serialize($info);
?>

1
a:2:{s:8:"username";s:5:"admi2";s:8:"password";s:8:"Password";}

同样地分成16个字节的块

1
2
3
4
a:2:{s:8:"userna
me";s:5:"admi2";
s:8:"password";s
:8:"Password";}

要修改第二行的第13个2 -> n ,按照CBC字节翻转攻击的套路,我们要修改的是上一个分组对应位置的密文, 即修改第一行的第13个r

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# -*- coding:utf-8 -*-
import base64
from urllib import unquote
from urllib import quote_plus
bs = 'PFTchi6G9vZxttcQECnYNOQYYfo9qENaEX80uR45Wulnekb0WfZMTH7QKYTm3Lqk4WYwKIYmPv8GahlmDsQYCw%3D%3D'
#密文cipher,从Chrome的插件EditThisCookie可以很轻松get到/抓包也可以
bs = unquote(bs)
#url解码
bs_de = base64.b64decode(bs)
#base64解码

ch = chr(ord(bs_de[13]) ^ ord('2') ^ ord('n'))
bs_de=bs_de[0:13]+ch+bs_de[14::]
#其实就是bs_de[13]=chr(ord(bs_de[13]) ^ ord('2') ^ ord('n'))
#因为python里面字符串不可变,即你直接bs_de = 'a'这样会报错

rs = base64.b64encode(bs_de)
#print rs
print quote_plus(rs)

cipher应该是经过cbc模式下aes加密后最后的密文(挺不好理解的多看看几遍就行了)

首先第一行填admi2,第二行碰都不要碰(它会自动帮你填充Password,如果点击了一下Password就gg为空了)然后抓包(重新按回车)

image

然后上刚刚的脚本生成密文替换抓包里面的密文,然后运行一下看看

image

报错是因为我们换成了新密文,不是刚才的密文,反序列化失败是正常的,把里面的base64码进行编码如下:

image

可以看到有admin,我们刚开始输入的是amin2,说明转换成功,所以下一步我们就要用相同的套路,来复原第一个密文分组,也就是初始向量。接下来改变一下初始向量vi,上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# -*- coding:utf-8 -*-

import base64
from urllib import unquote
from urllib import quote_plus

mingwen_de='nj7W9vwn0gxyOdX3xUN/lG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjg6IlBhc3N3b3JkIjt9'
#base64_decode('这里面的') can't unserialize
mingwen = base64.b64decode(mingwen_de)
print mingwen

iv = 'cFDYuzPtN1Q5ETd1E7s1Zw%3D%3D'
#此时cookie里的iv
iv = unquote(iv)
iv_de = base64.b64decode(iv)
new = 'a:2:{s:8:"userna'
for i in range(16):
iv_de = iv_de[:i] + chr(ord(iv_de[i]) ^ ord(mingwen[i]) ^ ord(new[i])) + iv_de[i+1:]
#iv_de[i]=chr(ord(iv_de[i]) ^ ord(mingwen[i]) ^ ord(new[i]))

print(base64.b64encode(iv_de))
#用这个结果把原来的iv换掉

之后就容易多了,这其中,iv_de就是IV,mingwen就是当前它解密出来的明文,也就是我们要改的,new是正确的明文,就是我们想要改变的.

修改后运行得到flag

文章目录
  1. 1. CBC字节翻转攻击
  2. 2. 题目代码
    1. 2.1. 总的来说就是只有admin用户才能读取flag,但是admin用户又不允许登录。虽然相互矛盾,由于题目用到了aes的cbc模式加密,所以我们可以利用cbc字节翻转攻击来得到我们想要的明文。
  3. 3. CBC字节翻转攻击原理
  4. 4. 题目进行CBC字节翻转攻击
,