Bugku--login4--CBC翻转攻击

分析

原本网站目录下存在 index.php.swp 但是我做的时候已经不在了。附上源码

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
<?php
define("SECRET_KEY", file_get_contents('/root/key'));
define("METHOD", "aes-128-cbc");
session_start();

function get_random_iv(){ //随机生成16位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); //AES-128-CBC加密
//OPENSSL_RAW_DATA=1
$_SESSION['username'] = $info['username']; //注册SESSION全局变量
//以下两行设置cookie
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>");
//若这里解序列失败,会把明文以base64编码形式传给用户。
$_SESSION['username'] = $info['username'];
}else{
die("ERROR!");
}
}
}

#第二个执行,检测用户名为admin时,打印flag
function show_homepage(){
if ($_SESSION["username"]==='admin'){
echo '<p>Hello admin</p>';
echo '<p>Flag is $flag</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>';
}

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>
'
?>

大概过程:

先由POST正文提交username(不能是admin)和password,进login函数获取iv和cipher到cookie中,然后攻击时重新发送构造好的iv和cipher,此时得把POST正文去掉,否则会又进入获取iv和cipher的过程中。

前导

CBC加密解密过程:

cbcencrypt

cbcdecrypt

加密过程: ①先将明文分组,②生成随机iv,③将随机iv与第一组明文异或运算,④将异或后的结果进行带key值加密运算,⑤将加密密文与下一轮次的明文异或运算。⑥循环。

解密过程: 第一轮: ①将密文进行带key值解密运算,②将解密结果与随机iv异或运算得到第一组明文。第二轮: ①将密文进行带key值解密运算,②将解密结果与第一组密文异或运算得到第二组明文。 其余轮次与第二轮相同。

重点: 不管加密解密,除了第一组过程用的是随机iv进行异或,其他组使用前一组的数据进行加(解)密。

使用异或运算修改iv和cipher()

修改cipher方法:

cipher[n] = chr(ord(cipher[n])^ord('明文要修改前的字符')^ord('明文修改后的字符'))

推导:

* 假设目的是控制第二组明文
  • 假设第二组明文内容为C,第一组密文为A,第二组密文解密运算后为B[还未与前一组密文异或]。

  • 则修改后的第一组密文为 A'=A^C^D

    • 根据解密过程可以得到
    1. C = A^B
    2. 根据1 可得到A^B^C=0
    3. 假设D为修改后的内容,则 A^B^C^D=D
    4. 由于C=A^B 所以将第一组密文A改为A’。A‘=A^C^D
    5. 这样当解第二组明文时 C=A'^B=A^C^D^B=D 这里的异或B在后端CBC解密时进行。

修改iv方法:

iv[i] += chr(ord(iv[i])^ord(right_plaint[i])^ord(error_plaint[i]))

推导:

  • 假设第一组错误明文为A,第一组密文解密运算后为B,随机iv为C,则A=B^C
  • 假设修改后正确的iv是C‘,第一组正确明文为A’,则A'=B^C'
  • 由于B未知,根据上一条可得A'=A^C^C'
  • 所以最后求得 C’=A'^A^C

模拟CBC加解密过程

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
<?php 
define("SECRET_KEY", "12345678");
define("METHOD", "aes-128-cbc");
function get_random_iv(){ //随机生成16位初始化向量
$random_iv='';
for($i=0;$i<16;$i++){
$random_iv.=chr(rand(1,255));
}
return $random_iv;
}
$iv = get_random_iv();
function encrypt($plain,$iv){
#plain未经base64编码
$cipher = openssl_encrypt($plain, METHOD, SECRET_KEY, 1, $iv);
#print (base64_encode($iv));
#print('-------------');
#print (base64_encode($cipher)); #密文base64编码
#print('-------------');
return $cipher;
}
function decrypt($cipher,$iv){
$cipher=base64_decode($cipher);
$iv = base64_decode($iv);
$plaintext=openssl_decrypt($cipher, METHOD, SECRET_KEY, 1, $iv);
var_dump($plaintext);
#print(base64_encode($plaintext));
}
$iv = "qgDEMbvT9ZynXxZYrYBv6Q==";
$cipher = "dZP96fpIaLwFnN+KxixPy+ueECi1UrivQ6BBE3fZPrdvXmHMlHelL0oZvub6//WOnGLO5TOqL14t9XEq1DpU1w==";
decrypt($cipher,$iv);

?>
//可以借助这代码理解异或修改iv和cipher

构造

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
import urllib.parse as parse 
#如果是python3的话,不会自动引入子模块。
#import urllib
#urllib.unquote类似会报错
#AttributeError: module 'urllib' has no attribute 'unquote'
import base64

iv = "o4wsexr24ZVVnZ98g0bdDA%3D%3D" #Cookie中的随机iv
iv = parse.unquote(iv)
#对字符串进行url解码 urllib.parse.unquote 将类似%xx的URL编码转换为字符
cipher = "RgnRVGVfCccaotuAY0nRauKQyGNovFJh0rWr%2FBLOxvp67DVu1fx0nHIFiYmKsi9hIxE3DVR3JZC3sgKbS768SA%3D%3D"
cipher = parse.unquote(cipher)

#明文每16B分为一组:
#a:2:{s:8:"userna
#me";s:5:"Admin";
#s:8:"password";s
#:4:"1234";}
#base64加密:
#rqLTzbVXl+cFW49R0uEjdm1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjQ6IjEyMzQiO30=
#改前一段的密文导致后一段的明文可控。
cipher = base64.b64decode(cipher).decode("latin-1")
#ISO Latin-1定义了256字符,前128个字符与Ascii相同,编码范围0x00-0xFF。根据提示can't decode byte 0xf5 选择latin-1。
cipher = cipher[0:9]+chr(ord(cipher[9])^ord('A')^ord('a'))+cipher[10:]
#cipher = cipher[0:9]+chr(0^ord('a'))+cipher[10:]
cipher = base64.b64encode(cipher.encode("latin-1")) #生成翻转的密文
cipher = parse.quote(cipher)
print(cipher)
#获取翻转后的cipher

iv = base64.b64decode(iv).decode('latin-1')
righ_plaint = 'a:2:{s:8:"userna'
error_plaint = "Oq5ifrS81bit/PpbRnbwyW1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9"
#错误明文从服务器回显取得<p>base64_decode('SWugD+4ZRz/0fWsO86U3+m1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9') can't unserialize</p>
error_plaint =base64.b64decode(error_plaint).decode('latin-1') #明文base64编码形式
new_iv = ''
for i in range(16):
new_iv+=chr(ord(righ_plaint[i])^ord(error_plaint[i])^ord(iv[i]))
new_iv = base64.b64encode(new_iv.encode('latin-1'))
new_iv = parse.quote(new_iv) #以URL编码传给burpsuite 不然会导致CBC解密失败返回ERROR!
print(new_iv)
#获取修改后的iv

总结

CBC翻转攻击可以用来 绕过注入检测、直接提升权限等。。

这一题用到了翻转字节,CBC攻击还有个字节填充。

本文标题:Bugku--login4--CBC翻转攻击

文章作者:Tanker

发布时间:2019年11月21日 - 12:37

最后更新:2019年11月21日 - 17:19

原始链接:https://www5059109.github.io/2019/11/21/Bugku-login4-CBC翻转攻击/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

0%