刘功瑞的博客

有一天你突然惊醒,发现这一切,都只不过是一场梦。

BUGKU CTF WEB Writeup 6

江湖魔头

题目:

http://123.206.31.85:1616/

学会如来神掌应该就能打败他了吧

解题:

一打开题目,看到这熟悉的画面,仿佛回到了2000年,当时网吧里的人都在玩这种江湖网页文字游戏。点开始游戏,随便选个属性确定。

点讨伐,提示:你现在还不能攻击蒙老魔。然后还有一些赚钱,修炼,商店什么的选项,应该就是属性值达到限定值的时候才可以攻击老魔拿到flag。

右键查看源代码,发现有3个js。

		<script type="text/javascript" src="js/script.js"></script>    
		<script type="text/javascript" src="js/md5.js"></script>    
		<script type="text/javascript" src="js/base64.js"></script>
//script.js
eval(function(p,a,c,k,e,r){e=function(c){return(c<62?'':e(parseInt(c/62)))+((c=c%62)>35?String.fromCharCode(c+29):c.toString(36))};if('0'.replace(0,e)==0){while(c--)r[e(c)]=k[c];k=[function(e){return r[e]||e}];e=function(){return'[57-9abd-hj-zAB]'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('7 s(t){5 m=t+"=";5 8=9.cookie.n(\';\');o(5 i=0;i<8.d;i++){5 c=8[i].trim();u(c.v(m)==0)p c.substring(m.d,c.d)}p""}7 w(a){5 x=new Base64();5 q=x.decode(a);5 r="";o(i=0;i<q.d;i++){5 b=q[i].charCodeAt();b=b^i;b=b-((i%10)+2);r+=String.fromCharCode(b)}p r}7 ertqwe(){5 y="user";5 a=s(y);a=decodeURIComponent(a);5 z=w(a);5 8=z.n(\';\');5 e="";o(i=0;i<8.d;i++){u(-1<8[i].v("A")){e=8[i+1].n(":")[2]}}e=e.B(\'"\',"").B(\'"\',"");9.write(\'<img id="f-1" g="h/1-1.k">\');j(7(){9.l("f-1").g="h/1-2.k"},1000);j(7(){9.l("f-1").g="h/1-3.k"},2000);j(7(){9.l("f-1").g="h/1-4.k"},3000);j(7(){9.l("f-1").g="h/6.png"},4000);j(7(){alert("浣犱娇鐢ㄥ鏉ョ鎺屾墦璐ヤ簡钂欒€侀瓟锛屼絾涓嶇煡閬撴槸鐪熻韩杩樻槸鍋囪韩锛屾彁浜よ瘯涓€涓嬪惂!A{"+md5(e)+"}")},5000)}',[],38,'|||||var||function|ca|document|temp|num||length|key|attack|src|image||setTimeout|jpg|getElementById|name|split|for|return|result|result3|getCookie|cname|if|indexOf|decode_create|base|temp_name|mingwen|flag|replace'.split('|'),0,{}))

使用https://www.sojson.com/jsjiemi.html解码后:

function getCookie(cname) {
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for (var i = 0; i < ca.length; i++) {
        var c = ca[i].trim();
        if (c.indexOf(name) == 0) return c.substring(name.length, c.length)
    }
    return ""
}

function decode_create(temp) {
    var base = new Base64();
    var result = base.decode(temp);
    var result3 = "";
    for (i = 0; i < result.length; i++) {
        var num = result[i].charCodeAt();
        num = num ^ i;
        num = num - ((i % 10) + 2);
        result3 += String.fromCharCode(num)
    }
    return result3
}

function ertqwe() {
    var temp_name = "user";
    var temp = getCookie(temp_name);
    temp = decodeURIComponent(temp);
    var mingwen = decode_create(temp);
    var ca = mingwen.split(';');
    var key = "";
    for (i = 0; i < ca.length; i++) {
        if (-1 < ca[i].indexOf("flag")) {
            key = ca[i + 1].split(":")[2]
        }
    }
    key = key.replace('"', "").replace('"', "");
    document.write('<img id="attack-1" src="image/1-1.jpg">');
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-2.jpg"
    }, 1000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-3.jpg"
    }, 2000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/1-4.jpg"
    }, 3000);
    setTimeout(function() {
        document.getElementById("attack-1").src = "image/6.png"
    }, 4000);
    setTimeout(function() {
        alert("浣犱娇鐢ㄥ鏉ョ鎺屾墦璐ヤ簡钂欒€侀瓟锛屼絾涓嶇煡閬撴槸鐪熻韩杩樻槸鍋囪韩锛屾彁浜よ瘯涓€涓嬪惂!flag{" + md5(key) + "}")
    }, 5000)
}

可以看到从cookie中获取user字段,然后使用decode_create解密。我们思路就是解密出cookie为明文,然后修改金钱,从商店里买东西。

使用decode_create()函数解密cookie,得到明文,并修改money为999999。

O:5:"human":10:{s:8:"xueliang";i:952;s:5:"neili";i:801;s:5:"lidao";i:56;s:6:"dingli";i:91;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:999999;s:4:"flag";s:1:"0";}

从js中没有找到加密函数,需要我们根据解密函数自己编写一个加密函数。

代码如下:

	// private property
	_keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
	// public method for encoding
	encode = function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;
	// input = _utf8_encode(input);
		while (i < input.length) {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}
			output = output +
			_keyStr.charAt(enc1) + _keyStr.charAt(enc2) +
			_keyStr.charAt(enc3) + _keyStr.charAt(enc4);
		}
		return output;
	}
 
	// public method for decoding
	decode = function (input) {
		var output = "";
		var chr1, chr2, chr3;
		var enc1, enc2, enc3, enc4;
		var i = 0;
		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
		while (i < input.length) {
			enc1 = _keyStr.indexOf(input.charAt(i++));
			enc2 = _keyStr.indexOf(input.charAt(i++));
			enc3 = _keyStr.indexOf(input.charAt(i++));
			enc4 = _keyStr.indexOf(input.charAt(i++));
			chr1 = (enc1 << 2) | (enc2 >> 4);
			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
			chr3 = ((enc3 & 3) << 6) | enc4;
			output = output + String.fromCharCode(chr1);
			if (enc3 != 64) {
				output = output + String.fromCharCode(chr2);
			}
			if (enc4 != 64) {
				output = output + String.fromCharCode(chr3);
			}
		}
		output = _utf8_decode(output);
		return output;
	}
 
	// private method for UTF-8 encoding
	_utf8_encode = function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
		for (var n = 0; n < string.length; n++) {
			var c = string.charCodeAt(n);
			if (c < 128) {
				utftext += String.fromCharCode(c);
			} else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			} else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
 
		}
		return utftext;
	}
 
	// private method for UTF-8 decoding
	_utf8_decode = function (utftext) {
		var string = "";
		var i = 0;
		var c = c1 = c2 = 0;
		while ( i < utftext.length ) {
			c = utftext.charCodeAt(i);
			if (c < 128) {
				string += String.fromCharCode(c);
				i++;
			} else if((c > 191) && (c < 224)) {
				c2 = utftext.charCodeAt(i+1);
				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
				i += 2;
			} else {
				c2 = utftext.charCodeAt(i+1);
				c3 = utftext.charCodeAt(i+2);
				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
				i += 3;
			}
		}
		return string;
	}
    


function encode_create(temp) {
    var result3 = "";
    for (i = 0; i < temp.length; i++) {
        // 将字符转为Unicode编码
        var num = temp.charCodeAt(i);
        num = num + ((i % 10) + 2);
        num = num ^ i;
        result3 += String.fromCharCode(num);
    }

    var result1 = encode(result3);
    return result1;
}
   var a= encode_create('O:5:"human":10:{s:8:"xueliang";i:952;s:5:"neili";i:801;s:5:"lidao";i:56;s:6:"dingli";i:91;s:7:"waigong";i:0;s:7:"neigong";i:0;s:7:"jingyan";i:0;s:6:"yelian";i:0;s:5:"money";i:999999;s:4:"flag";s:1:"0";}');
   console.log(encodeURIComponent(a));
  //"UTw7PCxqe3FjcC42OThOjWtSUFYwbm99amlzbG0wI3MeHxkaZ1liZxQMWEFDXl8EdUUOCAADd016B34WUlFWWTVoATEABX55P3Z2CmYgPTY5Pj90FSUUFWMfL2ZnYnYhCRMTGRQPQCcHKFIvEShXUlYCGQMbDQ4FXEcXREo%2FBTzBxKbu6fbrB%2BH%2Bps3nsLrP6dCs0LgR8fj1%2F%2B6y3%2B%2FapJ3XnJnkjNPf0NnRjpPD7u%2Fx8%2FH3j4vBiIL4kNTK0dea%2F7mC%2B4bu%2FOr1SQ%3D%3D"

再在浏览器的console中设置cookie

document.cookie="user=UTw7PCxqe3FjcC42OThOjWtSUFYwbm99amlzbG0wI3MeHxkaZ1liZxQMWEFDXl8EdUUOCAADd016B34WUlFWWTVoATEABX55P3Z2CmYgPTY5Pj90FSUUFWMfL2ZnYnYhCRMTGRQPQCcHKFIvEShXUlYCGQMbDQ4FXEcXREo%2FBTzBxKbu6fbrB%2BH%2Bps3nsLrP6dCs0LgR8fj1%2F%2B6y3%2B%2FapJ3XnJnkjNPf0NnRjpPD7u%2Fx8%2FH3j4vBiIL4kNTK0dea%2F7mC%2B4bu%2FOr1SQ%3D%3D"

刷新页面后金钱变为999999,然后去商店中购买秘籍,全部买完后可以讨伐老魔,得到flag。

flag{a13d82fe0daf4730eac8f8e0d4c17e72}


login4

题目:

http://123.206.31.85:49168/
flag格式:SKCTF{xxxxxxxxxxxxxxxx}
hint:CBC字节翻转攻击

解题:

CBC字节反转攻击,没听说过。看其他的大佬writeup解出来的,总结要点:1.有iv和cipher字段;2.有源码泄露。

字节翻转攻击原理参考文章:

http://www.anquan.us/static/drops/tips-7828.html

http://www.freebuf.com/articles/system/163756.html


尝试登录,输入admin,密码随便,提示普通用户无法查看,用户名改为admin,密码任意。

admin又不允许登陆,并且密码是任意输入的,也就是说关键点不是绕过登陆,而是身份认证,既然是身份认证,那有可能是在cookie里进行认证的,用burp抓包,可以看到存在以iv和cipher为关键字的cookie,这是CBC加密中的需要的字段,题目也提示了.

但到目前为止,我也不知道服务器端对我们传入的username和password做了怎样的处理,也就不知道加密的的字符串是什么,无奈扫了一下源码泄露,发现存在.index.php.swp文件,这是index.php文件异常退出时系统自动的备份文件,可以恢复源文件的,下载下来,拿到lunix下,用命令 vim -r index.php.swp 就可以恢复,

源码:

<!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>

可以看出我们传入的用户名和密码是经过序列化的,那我们将用户名admik,密码123序列化后再进行攻击,序列化后:

s:2:{s:8:”username”;s:5:”admik”;s:8:”password”;s:3:”123″;}


现在我们清理一下思路:

服务器将我们传入的数据序列化后,得到上面的字符串,然后用一个key值(未知),和一个iv值(已知,就是cookie中的iv)来加密得到密文,经过base64和url编码后,添加到cookie中(cipher的值),作为当前用户是admik的身份标识。我们再一次发起请求时,如果不再提交username和password的值(POST的内容为空),服务器就不会进行身份更新(不会重复上面的步骤),而是会用我们的cookie中的iv和cipher值进行一次CBC解密,得到上面的字符串,再进行反序列化,得到用户名admik.

现在的目的就是我们修改cookie的值,使服务器解密后得到的用户名为admin,就是把k改为n

不过这里我们只有密文,没有明文,利用的就是CBC解密的特点:前16个密文字符用来解密接下来的一组16个密文,就是说通过改变前16个密文就可以改变下面16个密文解密的结果,这就是字符翻转攻击。具体原理看链接,解释的挺详细。


首先在burp中发送请求,得到response的内容,显示要设置两个cookie,

1.png

burp当然没法自动设置,我们手动设置,再次发送,不过这次不带POST的内容,只带刚才设置的cookie,返回显示为admik,这也就验证了我们的猜想,服务器是通过这两个cookie来进行身份认证的。

2.png

下一步将我们的序列化内容分组,16个一组:

s:2:{s:8:”userna

me”;s:5:”admik”;

s:8:”password”;s

:3:”123″;}

我们想要改的k在第二组的13位(从0开始),那我们要改第一组的第13位。

编写脚本:

<?php
$enc=base64_decode("87BH5McyVuI+NF2rnz3eDeMvXU7WFE8HPCdYBk7hSSVC13UedKdHziRUmilhbCbm1e8ciwMWjerUj081YgG7/w==");
$enc[13] = chr(ord($enc[13]) ^ ord("k") ^ ord ("n"));
$new_enc = base64_encode($enc);
echo urlencode($new_enc);
//result : 87BH5McyVuI%2BNF2rnzjeDeMvXU7WFE8HPCdYBk7hSSVC13UedKdHziRUmilhbCbm1e8ciwMWjerUj081YgG7%2Fw%3D%3D

发送重新编码后的结果,报错了:

3.png

报错显示无法序列化,这是因为我们修改了第一组密文,那第一组密文解密后也是错的,不能正确序列化。通过CBC解密的原理我们知道,第一组密文解密时,用的是iv的值,那么我们构建一个新的iv,使这个iv解密第一组后,得到s:2:{s:8:”userna即可,脚本如下:

<?php
$plain_text=base64_decode("ufZRz4BolswdK0aDDHnxZ21lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7fQ==");//此处的base64字符串为报错返回的字符串。我看了很多writeup这个地方都没有写清楚。
$iv=base64_decode("5hHBrgiekfg06GnWOT1AQA==");
$cleartext = 'a:2:{s:8:"userna';
$newiv = '';
for ($i=0;$i<16;$i++){
    $newiv=$newiv.chr(ord($iv[$i]) ^ ord($plain_text[$i]) ^ ord ($cleartext[$i]));
}
echo urlencode(base64_encode($newiv));
//result : Pt2iW%2FOFPQwT4VomUDbfRg%3D%3D

修改iv,重新发送后,得到flag:

4.png

注意的一点:每次登录请求后iv的值是随机变化的,所以解题时候注意vi和cipher是要配对的。


SKCTF{CBC_wEB_cryptography_6646dfgdg6}

发表评论:

Powered By Z-BlogPHP 1.5.2 Zero

Copyright www.liugongrui.com.All Rights Reserved.