四时宝库

程序员的知识宝库

CBC字节翻转攻击讲解与示范&SKCTF&bugku——jayus

1, 首先CBC是一种加密方式,下面是CBC加密得原理图

initialization vector是伪随机数,由此可以使同样的明文每次加密得到的结果都不一样.key是密钥.首先将明文分段,一般都是16字节为一段.而后将第一段与IV异或,加密之后生成第一段密文;

将第一段密文作为第二段明文加密使用得IV,以此类推,最终将得到的密文拼接起来就完成了加密操作.

2, 解密过程如图:

解密过程就是反过来,首先将密文按照之前得分段,第一段用解密算法解密,而后与IV再次异或

这里涉及到异或得性质:a^a=0;

b^a^a=b;

因此这里IV再次参与异或就将IV消掉了,得到原来的明文.之后将第一段密文作为第二段得VI,以此类推,将所有密文分段解密,再拼接起来即得到明文.

而CBC字节翻转攻击就利用到CBC加密过程得特点,这里以SKCTF得一道题也即bugku内得一道题来示范CBC字节翻转攻击.

bugku:ctf训练网站 http://ctf.bugku.com

进入之后看到得是一个很常见的网页登陆框

试着登陆一下

发现要求使用admin账号登陆,使用admin登陆却发现:

这里就非常蹊跷,猜测应当是要构造绕过.这里没有头绪时,尤其时对这种特别简介没有任何提示信息得题目,很大可能会设置有源码泄露.

使用sourceleakheaker工具扫描网站(使用方法网上很多,大家可以去搜索)

在搜索结果中发现果然有源码泄露,是index.php.swp文件,swp文件时linux下vi编辑器错误退出产生得.我们在linux环境下处理这个swp文件.执行

vi -r index.php.swp

在vi中打开后,按住esc,而后shift+q

输入

w index.php

将文件写入一个index.php文件中,

输入q退出

得到的index.php文件发现是网站源码.

代码如下:

<!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", 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 '<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>
 <div class="footer">
 <input type="submit" name="submit" value="Login" class="button" />
 </div>
 </form>
 </div>
 </body>';
 }
}
?>
</html>

代码得讲解这里不过多讲述,大佬一看就懂,像我们这样得菜鸟我建议还是多看看弄懂到底是个什么逻辑.

整个得代码思路大概就是,将用户输入得username,password构造成为一个数组而后进行序列化,再用CBC加密,同时随机生成一个随机数,将加密后的结果与随机数均进行base64加密后作为页面得两个cookie存在:

 setcookie("iv", base64_encode($iv));
 setcookie("cipher", base64_encode($cipher));

正常登陆时先判断username是否为admin,是的话就说

admin are not allowed to login

不是的话就说

Only admin can see flag

这显然是一个矛盾得关系.我们再次查看源代码,发现有一个check_login函数.当用户登陆没有输密码但是输入了用户名时会触发.而后进行页面得展示.

在check_login函数中,首先会判断是否有前文得两个cookie,有的话就会对两个cookie进行判别,解析处cookie中含有得username,而后进入该username对应得界面.

这里我们就发现,check_login函数之后没有对username进行检测,如果我们修改了cookie使cookie解析出来的username为admin,那麽我们就绕过了对username得检测,成功进入admin页面取得flag.

这个过程就是CBC字节翻转攻击发挥作用的时候了

首先我们用zdmin作为用户名登陆.而后会获得两个cookie值:

iv:cMIh5SDj890k6v1HGPYHLw%3D%3D

cipher:pkUm0C%2F0ruGbG9vsGrWTu1IwoLNQmRPGZ9%2BX62pQw3Gk9%2BTqN0B%2Fy9gjq%2FfXDLiHrB7GwxuQhq%2Br5UMgBL9QBw%3D%3D

这里cipher就是我们传入的{username:zdmin,password:123}反系列化而后进程CBC加密,再base64加密得到得结果.

iv则是一个随机数进行CBC加密而后base64加密得结果.

我们要做的就是想办法修改cookie,使后面cbc解密分析cookie中含有的username得值变为admin,而不是我们传进去得zdmin,只有以为需要修改.

再次回顾CBC解密得过程:

我们可以发现,一旦我们控制了密文,那麽我们就可以通过异或得方式更改后面一段得明文.我们知道CBC以16字符为一段.

我们传进去的值序列化然后分段得结果为:

#a:2:{s:8:"username";s:5:"zdmin";s:8:"password";s:5:"12345"}

#s:2:{s:8:"userna

#me";s:5:"zdmin";

#s:8:"password";s

#:3:"12345";}

我们想要修改第二段得z这个字母,就应该对应的修改第一段密文中的z对应位置得字母

修改得脚本如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import base64
import requests
import urllib
iv_raw='%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D' #这里填写第一次返回的iv值
cipher_raw='8WdhbPxjZy9xYAgoCeghiOUQu0ri1Y3dv7cX44MbvOfIC6zZxCbR%2FPFpeMatL5qIgT%2BYA66tIdCBpxtWsWxV9Q%3D%3D' #这里填写第一次返回的cipher值
cipher = base64.b64decode(urllib.unquote(cipher_raw))
xor_cipher = cipher[0:9] + chr(ord(cipher[9]) ^ ord('z') ^ ord('a')) + cipher[10:] 
xor_cipher=urllib.quote(base64.b64encode(xor_cipher))
print "反转后的cipher:" + xor_cipherxi

修改后得到的cookie,再次参与cbc解密后,得到的第二段明文中的zdmin就会变为admin

我们执行后结果:

这里判断解密之后反序列化出现错误,我们查看cbc解密得过程可以发现.在我们修改了第一段密文之后,第一段明文整个就变了.而正确的第一段明文我们是知道的.应该为:

s:2:{s:8:"userna

这里我们就可以对iv进行修改,使第一段产生得明文更改为s:2:{s:8:"userna

代码如下:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import base64
import urllib
cipher = 'Bc6oENSSAEPpPdv/rbqRZG1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjU6IjEyMzQ1Ijt9'#填写提交后所得的无法反序列化密文
iv = '%2F8iEm4jh%2BjbgVGwlQ31ycg%3D%3D'#一开始提交的iv
#cipher = urllib.unquote(cipher)
cipher = base64.b64decode(cipher)
iv = base64.b64decode(urllib.unquote(iv))
newIv = ''
right = 'a:2:{s:8:"userna'#被损坏前正确的明文
for i in range(16):
 newIv += chr(ord(right[i])^ord(iv[i])^ord(cipher[i])) #这一步相当于把原来iv中不匹配的部分修改过来
print urllib.quote(base64.b64encode(newIv))

成功取得flag:

以上便是对CBC字节翻转攻击原理得讲解和一道ctf题目中的实际利用.这些只是我自己的一点理解,有不对的地方大家批评指正.

参考链接:

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

https://mochazz.github.io/2018/05/06/CBC%E5%AD%97%E8%8A%82%E7%BF%BB%E8%BD%AC%E6%94%BB%E5%87%BB/

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接