0x00 源码
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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Login Form</title> <link href="static/css/style.css" rel="stylesheet" type="text/css" /> <script type="text/javascript" src="static/js/jquery.min.js"></script> <script type="text/javascript"> $(document).ready(function() { $(".username").focus(function() { $(".user-icon").css("left","-48px"); }); $(".username").blur(function() { $(".user-icon").css("left","0px"); });
$(".password").focus(function() { $(".pass-icon").css("left","-48px"); }); $(".password").blur(function() { $(".pass-icon").css("left","0px"); }); }); </script> </head>
<?php define("SECRET_KEY", '9999999999999999'); 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 '<p>Hello admin</p>'; echo '<p>Flag is ctf{123cbcchange}</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> <div class="footer"> <input type="submit" name="submit" value="Login" class="button" /> </div> </form> </div> </body>'; } } ?> </html>
|
0x01 AES-CBC 加密、解密过程
加密过程
Plaintext:明文数据
IV:初始向量
Key:分组加密使用的密钥
Ciphertext:密文数据
明文都是先与混淆数据(第一组是与IV,之后都是与前一组的密文)进行异或,再执行分组加密的。
1 2 3 4 5 6 7 8
| 1、首先将明文分组(常见的以16字节为一组),位数不足的使用特殊字符填充。 2、生成一个随机的初始化向量(IV)和一个密钥。 3、将IV和第一组明文异或。 4、用密钥对3中xor后产生的密文加密。 5、用4中产生的密文对第二组明文进行xor操作。 6、用密钥对5中产生的密文加密。 7、重复4-7,到最后一组明文。 8、将IV和加密后的密文拼接在一起,得到最终的密文。
|
解密过程:
每组解密时,先进行分组加密算法的解密,然后与前一组的密文进行异或才是最初的明文。
对于第一组则是与IV进行异或。
1 2 3 4
| 1、从密文中提取出IV,然后将密文分组。 2、使用密钥对第一组的密文解密,然后和IV进行xor得到明文。 3、使用密钥对第二组密文解密,然后和2中的密文xor得到明文。 4、重复2-3,直到最后一组密文。
|
0x02 攻击
异或的特性:
异或是不进位加法
1 2 3
| 1⊕1=0 //这里1+1=10 进位了,不需要管他 1⊕0=1 0⊕1=1
|
连续异或两次同一个内容,就相当于没有异或
比如:
1100110 ⊕1010100⊕1010100 = 1100110
而异或还有交换律,可以随意打乱顺序
1010100 ⊕ 1100110 ⊕1010100 = 1100110
1.对于解密时:
设明文为X,密文为Y,解密函数为k。
X[i] = k(Y[i]) Xor Y[i-1]
2.字节翻转
要改变Cipher1[0]
的值为x
则 'a' ^ x = decrypt(cipher2[0])
而又因为:Plain2[0] ^ Cipher1[0]=decrypt(cipher2[0])
把decrypt(cipher2[0])
代入第一个式子得
'a' ^ x = Plain2[0] ^ Cipher1[0]
解得x= Plain2[0] ^ Cipher1[0] ^ 'a'
x是我们要赋值的,也就是Cipher1[0]
,所以最后写出的语句为:
Cipher1[0]=Plain2[0] ^ Cipher1[0] ^ 'a'
Plain2[0]
是原明文组2的第一个字节(已知),Cipher1[0]
是第一组密文的第一个字节(已知)
此时再把Cipher1和Cipher2连接起来生成Ciphertext,把C,就会成功变成我们想要的明文。
3.修复IV:
当我们破坏掉密文的第一组时,同样明文的第一组在解密的时候就并不是原来的明文了,这个时候我们需要修复初始向量IV,给它一个新的值,
使NewIv ^ NewCipher1 = Plain1(原)
推导过程:
Plain1(损坏) ^ iv = decrypt(newCipher1)
Newiv=Plain1(原) ^ decrypt(newCipher1)
(目的是要给Newiv赋值)
把第一个式子代入到第二个式子中得到:(此时Plain(原) 已知,Plain1(损坏)已知)
Newiv=Plain1(原) ^ Plain1(损坏) ^ iv
有了Newiv和翻转好的Ciphertext,这时网站后端解密后的明文就是我们构造好的明文了。
0x03 例题
只有admin才能看flag,而登陆的时候过滤了admin。后端检测的时候使用的是cookie
里面的cipher
以及iv
解密后得到的字符进行反序列化
原文:
a:2:{s:8:"username";s:5:"admix";s:8:"password";s:5:"admix";}
cipher:
540LbV57RP5K18c1jJeAOk7HOjcMLHthWSvcLRPMmOTUdyvcS8NkZsx%2BjR7cIgNOqIURlke333VwZ8Tv0Ajpkg==
iv:
Ne%2BoeXNBcE%2BuI%2BCrlXqjqw%3D%3D
这里cookie可控。
明文加密时分组为:
1 2 3 4
| a:2:{s:8:"userna me";s:5:"admix"; s:8:"password";s :2:"123";}
|
这里只需要把admix
的x
反转成n
就行了
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
| import base64 import urllib.parse
ciphertext = 'w1uvgfzxxuYHA%2Bo08ZL%2BCefhwr2jHuwglOIBAh8cP1w5TCiCmY0Yy%2BQxelAl9%2B%2FiZeRvLD7UjzlF58bTGFZ%2BWQ%3D%3D'
cipher = base64.b64decode(urllib.parse.unquote(ciphertext)) array_cipher = bytearray(cipher) array_cipher[13] = array_cipher[13]^ ord('N') ^ ord('n')
print('newCipher:',urllib.parse.quote(base64.b64encode(array_cipher)))
decode_plain = base64.b64decode('sxvY7wUlyPgC+/iV7InfjG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7fQ==')
iv = base64.b64decode(urllib.parse.unquote('1HxERxo2%2FTuymbrPoVDB%2Bw%3D%3D'))
plain = 'a:2:{s:8:"userna'
newiv = list(iv)
for i in range(16): newiv[i] = (ord(plain[i].encode('utf-8')) ^ iv[i] ^ decode_plain[i])
newiv = bytes(newiv)
print('newiv:',urllib.parse.quote(base64.b64encode(newiv)))
|
小饼干一换就行了。
0x04 Refence
CBC字节翻转攻击解析
[CTF]AES-CBC字节翻转攻击