江湖魔头
题目:
学会如来神掌应该就能打败他了吧
解题:
一打开题目,看到这熟悉的画面,仿佛回到了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,

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

下一步将我们的序列化内容分组,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发送重新编码后的结果,报错了:

报错显示无法序列化,这是因为我们修改了第一组密文,那第一组密文解密后也是错的,不能正确序列化。通过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:

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