Summary
Back in March, I determined that the Textpattern blogging software
contained a number of very serious security vulnerabilities, including a
remote code execution vulnerability that affected every single version
of the software ever released (since September 2004). In response
to my report, the Textpattern developers released a new version of the
software, 4.4.0, which contained fixes for almost all of the
vulnerabilities. One outstanding vulnerability has been patched in SVN
and should be a part of the next release.
What were the vulnerabilities?
I reported a total of 12 issues to the Textpattern developers:
1. Lack of CSRF Protection
Textpattern does not contain any mechanism for protecting against
cross-site request forgery (CSRF) attacks. This vulnerability allows
for everything from privilege elevation (via a CSRF attack on the “Add
User” page) to arbitrary code execution (via use of the <txp:php>
tag, a Textpattern template tag that allows for code execution).
This issue was not addressed in version 4.4.0, but the developers have
subsequently added support for CSRF tokens in SVN (see r3528-r3557).
2. Arbitrary code execution for unauthenticated users via <txp:php>
and form previews
textpattern/index.php
contained the following code, which was meant to
provide a preview of an updated Textpattern form, near the very top of
the file:
| <?php
if (isset($_POST['form_preview'])) {
include txpath.'/publish.php';
textpattern();
exit;
}
|
That code was executed prior to authentication checks being run. In
addition, forms are allowed (by default) to use a special template tag,
<txp:php>
, which executes PHP code. As a result, an unauthenticated
attacker could execute arbitrary code on a server running Textpattern by
submitting a modified POST request containing <txp:php>
tags.
Although support for <txp:php>
tags can be disabled on a per-site
basis, other vulnerabilities (#3 / #4 on this list) allowed that
protection to be bypassed.
This vulnerability was patched for version 4.4.0. In researching
this vulnerability, I discovered that it had existed since the first
version of Textpattern was released.
3. Textpattern tag system allows for code execution
Due to a design flaw in Textpattern’s tag system, it was possible to
build a tag that would execute arbitrary PHP without using
<txp:php>
. The bad code was in the processTags
function in
textpattern/publish.php
:
| <?php
if (function_exists($tag))
{
$out = $tag(splat($atts), $thing);
}
|
In other words, a tag like
<txp:some_function a="b" c="d">some text</txp:some_function>
would try
to execute the PHP function some_function
, passing in the attributes
($atts
) as a PHP array and the inner content as a string. Textpattern
template tags were just regular PHP functions that accepted at most two parameters.
But guess what? Other functions also follow this pattern:
array_filter, array_walk, usort, uksort, and
uasort all accept an array and a callback function as their parameters.
So, placing the following tag on a page would result in shell commands
being run and the results being displayed in the response:
| <txp:array_filter 0="whoami" 1="ls -al">passthru</txp:array_filter>
|
This tag would create a page, test.php, that calls eval on user input,
allowing for arbitrary code execution (eval can’t be called directly
since it’s a language construct, not a function).
| <txp:array_filter 0='echo "<?php eval($_GET1); ?>" > test.php'>passthru</txp:array>
|
A partial fix for this vulnerability was released as part of version
4.4.0. The code now uses get_defined_functions to avoid calling
the internal functions mentioned above. However, there is still no
whitelist of valid functions for the Textpattern parser to call: all
user-defined PHP functions are considered fair game. If the codebase
contained a function that looked like the evil
function defined below,
for instance, sites would again be vulnerable.
| <?php
function evil($array, $callback)
{
return array_filter($array, $callback);
}
|
This vulnerability, just like #2, has existed since Textpattern was
first released.
4. Attacker can bypass <txp:php>
settings
Remember how I just said that any user-defined PHP function could be
called as a tag, provided it accepts the right number of parameters (<=
2)? Well, a function inside of Textpattern made it possible to bypass
configuration settings and reliably execute code via the <txp:php>
tag.
The function was named fileDownloadFormatTime
and it looked like this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14 | <?php
function fileDownloadFormatTime($params)
{
global $prefs;
extract($params);
if (!empty($ftime))
{
return !empty($format) ?
safe_strftime($format, $ftime) : safe_strftime($prefs['archive_dateformat'], $ftime);
}
return '';
}
|
What’s so special about this function?
- It calls extract on
$params
, which is provided by the user as
input. extract, by default, will overwrite (clobber) the values of
variables in the current scope. Inside a function, that’s not a huge
deal: however, $prefs
is marked as a global variable here, which
means it can be overwritten and the new value will be persisted.
$prefs['allow_page_php_scripting']
and
$prefs['allow_article_php_scripting']
are supposed to be boolean
values that indicate whether or not <txp:php>
tags are allowed
in a particular context.
So, by calling fileDownloadFormatTime
with an attribute named prefs
, it
was possible to overwrite the $prefs
variable. If the variable was
overwritten with the correct value (eg: 1),
$prefs['allow_page_php_scripting']
and
$prefs['allow_article_php_scripting']
would be enabled, allowing PHP
execution in subsequent tags. An example is below:
| <txp:fileDownloadFormatTime prefs="1"></txp:fileDownloadFormatTime>
<txp:php>phpinfo();</txp:php>
|
This issue was fixed for version 4.4.0 over the course of several
different SVN revisions.
- r3506 changed the call to extract so it could not be used to
overwrite
$prefs
- r3495 and r3496 added calls in strategic locations to
explicitly verify that
$prefs
is an array, as it should be.
5. File Uploader allows for arbitrary file uploads
The file uploader, which is accessible to all authenticated users except
for freelancers, does not perform filtering on the extensions of
uploaded files. By default, the files are publicly accessible in the
files/ directory of the Textpattern install. That means a malicious user
can, depending on the extension of the file they upload:
- Execute arbitrary PHP
- Execute arbitrary Javascript by uploading an HTML file
- Perform CSRF / XSS by uploading an SWF file
Unfortunately, this vulnerability was not fully addressed in the 4.4.0
release. r3484 added a .htaccess file to the files/ directory that
would have blocked direct access. However, after a small outcry in the
Textpattern forums, r3501, r3502, and r3503 renamed the
.htaccess file to .htaccess-dist and added a note to the README file
suggesting that people should use it. That means installs are vulnerable
to this kind of attack by default. The only mitigating factor is that
file uploads are restricted to non-freelance level users (they are
still CSRF-able though!).
6. Image Uploader allows for SWF uploads
The image uploader, which is accessible to all authenticated users
except for freelancers, does perform filtering on the extensions of
uploaded files. However, it allows SWF files, which can be used to
perform CSRF and XSS attacks. This issue is otherwise identical to #5.
7. Persistent XSS via Articles
HTML in articles is not subject to any sort of filters. As a result,
it’s possible for a freelancer to add a malicious Javascript payload to
his/her article: when another user looks at the article preview, that
Javascript will be executed. Due to the lack of CSRF protection, an
attacker could trick an authenticated user into creating such an article.
This issue has not been addressed in 4.4.0. The development team is
currently investigating possible HTML filtering libraries (ie:
htmLawed).
8. Reflected XSS in textpattern/include/txp_page.php
The following fragment of code was found in
textpattern/include/txp_page.php
. $name
was derived from user input
and was not properly sanitized.
| <?php
$buttons = '<div
class="edit-title">'.gTxt('you_are_editing_page').sp.strong($name).'</div>';
|
This vulnerability was resolved for version 4.4.0.
9. Directory Traversal via Uploaded Files
When editing uploaded files (possible for everyone except freelancers),
it was possible to manually edit the ‘filename’ parameter. This
parameter was vulnerable to a directory traversal attack: by using ../
to change the relative path, it was possible to access any file on the
filesystem which the webserver could read. The file_download URL
handler would then serve up that file without question, even to
unauthenticated users. This attack made it possible to grab the contents
of the Textpattern config file, /etc/passwd, etc. Due to the lack of
CSRF protection, an attacker could trick an authenticated user into
creating such a malicious download.
This vulnerability was resolved for version 4.4.0.
10. Passwords stored insecurely
Account passwords were stored in lower-case and hashed using the
PASSWORD function for MySQL. Passwords should always be case sensitive
and the MySQL PASSWORD function should not be used for a web application
(see
http://dev.mysql.com/doc/refman/5.1/en/encryption-functions.html#function_password).
This vulnerability was resolved for version 4.4.0 through the use
of phpass.
11. $txp_user
not consistently escaped in SQL
Textpattern does not make use of stored procedures and prepared
statements due to the age of its codebase; instead, it uses string
concatenation combined with manual escaping. There were several places
in the code where $txp_user
, the username of the currently logged in
user, was not properly escaped.
This vulnerability was resolved for version 4.4.0.
12. GET requests modify application state
There were several locations in the code where actions were taken in the
application based on GET requests (ie: banning/unbanning users, updating
plugin code, etc). Ideally, GET requests should be idempotent to prevent
unintended submissions that alter the application. This issue is closely
tied to #1, since the lack of cross-site request forgery makes all
requests, including GETs, impossible to validate.
Conclusion
Anyone who is running a Textpattern installation should immediately
upgrade to 4.4.0 if they haven’t already. They should also be aware of
the vulnerabilities that still exist in that version (cross-site request
forgery, file uploads, persistent XSS from article previews).
I would like to thank the Textpattern development team, especially
Robert Wetzlmayr, for their responses to my report.