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