📜 ⬆️ ⬇️

Security and problems with it in MODx Revolution

This topic is dedicated to the security of MODx Revolution as a whole, as well as connectors and contexts separately (Revolution 2.1.0 release).

Background: there was a question to create a serious resource on the MODx Revolution engine. We did not see any technical problems, but decided to pay more attention to the issues of engine security.
Honestly, I have always considered the security mechanisms in the MODx Revolution very flexible and reliable, but here I received quite a few surprises ... We will try to make them out as much as possible and in more detail.

Whoever loves to read the most interesting things right away, start reading with the words “Now let's summarize what the connector needs to work ........”, since at first we considered not the problem, but the task.
')
UDP: in version 2.1.1 fixed. But knowing how much> 2.1.0 is still raw, I am sure that 99% of Revo in progress are earlier releases.


First, we take into account the most common myth with open-source products: "It's easy to download engines, dig it and find holes." Let's not prove the opposite, but just do one trick: change the file structure. We rename the main folders of MODx (assets /, connectors /, manager /), and the core / are generally transferred outside the root directory of the site so that there is no undermining through the address bar at all.
The truth is there are a few points with this:
  1. There are subtleties of transfer manager / folder. I do not want to repeat, read here.
  2. Get ready for some plugins to fly. This is especially true of the directresize plugin. In it, firstly, assets / are strictly written, and secondly, the paths to the pictures in the file system are not absolute, but relative, which, in turn, also leads to errors.
  3. Never write hard paths in your code, be sure to use the system variables base_path, core_path, etc.

Thus, it will protect you at least a little from targeted file system probing (for example, from neighboring accounts on a leaky virtual hosting).

Secondly, we decided to make an associated service for privileged users, but not on the basis of the admin panel (that is, not so that they entered the admin panel, there all the security settings were hidden from them, except for the new module for them), but based on the additional context (i.e., on a new subdomain, etc.). In our case, MODx Revolution is a multi-site platform))).
Here all the fun begins ...

So, in order ...
To begin with, we create a new context, create several documents we need in it, prescribe all the necessary settings, bind a new subdomain to the site, in the index file we prescribe the required context for this domain ... We check and work. Now the most important thing is to deny access to this context to everyone except those who are allowed ...

Before continuing, a little about the principles of security:

In MODx Revolution, the rule applies to everything: “If rights are assigned to something, then the principle“ everything that is not allowed, then it is forbidden ”. And if nothing is assigned to anyone, then everything is possible for everyone. ”
I explain: If there is a group of resources to which access is assigned to someone, then all others who are not allowed to go through the forest. And if you simply created a group of resources for grouping, then everyone has access to them.
It is the same with contests: if access to the context is assigned access for some Security Policy Roles, then all the others go through the forest. Everything is logical.

So, we could create a group of resources to which we would assign access levels, and into which our closed documents would be combined. But we have refused this simple method, because, firstly, the human factor could take place (for example, someone created a new resource and forgot to add it to the group), and secondly, we wanted to block access to everything context: whether to log in, or go further.

So we decided to close access to the context. I climb, it means, to edit our new context in order to deny access to it ... And then I entered a small stupor ... And how is it, to close access to it? The context is set to access for the role of a super-user, no one else. However, everyone has access.
After thinking, I understand that it is logical that they have access to it, this is a public context ... But then why in general there is a mechanism for accessing contexts?
Having gotten better, everything turned out to be simple: it turns out that access to public contexts is not checked for access at all, access is checked only for the context of mgr, because the request handlers are generally different. This is also a separate article, so I am sending to what has already been written: about access to contexts MODx Rvolution

Good. We create a new handler for our context that intercepts access to the context. By the way, here MODx shows itself in all its glory. It takes literally 30 minutes (taking into account the development of new technologies and 10 lines of code). If not authorized, display the authorization page. If it is authorized, but does not have access to the download panel (A custom security policy setting was added specifically for this), then it displays a message about the lack of access. It turned out just fine: even super-users, if they are not simultaneously in the right group of users and access to the context with a specific Role, do not have access to the panel. Super)))

Everything seems to be cool, but I got to write the necessary connectors. I create a connector according to the rules, prescribe a code, create a processor. I fulfill the request ... Empty answer. This is due to the execution of the file connectors / index.php
There is such code there:
if (defined( 'MODX_REQP' ) && MODX_REQP === false ) {
} else if (!$modx->context->checkPolicy( 'load' )) {

@session_write_close();
die();
}




That is, you have 2 options:
  1. Register in the define connector ('MODX_REQP', false); In this case, the check for the right to execute the script is disabled in general. Stupidly all possible.
  2. Assign rights to this context to the right load. This is more preferable if you want to restrict access to the connector. There is only one problem in this case: if in the first case, in the event of an error in executing the code, the answer will be given in JSON format, then in case, there will be no answer at all.

We went the second way. But still still not beeping. A little more. As a result, we came to the conclusion that it is necessary to have such a line here: $ _SERVER ['HTTP_MODAUTH'] = $ modx-> site_id; Actually, it is the $ modx-> site_id variable (global $ site_id) that is the reason for writing this article. But more on that later…

In general, we prescribe it in the connector. Still not working. It turns out that you need to set another variable $ _REQUEST ['ctx'] = 'context_id'; And this is a source of headache, because if it is not hard to prescribe, you can send ctx even in GET, at least in a POST request. This is actually the context key.

Now let's summarize what the connector needs and what the global problem is:
For work it is necessary:
  1. $ _REQUEST ['ctx'] - context key passed in any way;
  2. $ site_id is the key of the site, registered in the MODx config, it is also $ _SERVER ['HTTP_MODAUTH']


So, the focus: let's say we use the security policy settings when the connector is executed. We transfer to it ctx of our context. All biting. To whom it is possible to perform, to that it is possible, and to whom it is impossible, that is impossible. And here we take these, and prescribe a fan of ctx = sdfsdfsdfsdfsdf. What is happening at the moment? The sdfsdfsdfsdfsdfsd context is initialized (interesting to know, do you have one?)))). Here I have no such context. And what do you think everything is broken? Nifiga !!! MODx does not check for contexts. Moreover, it generally does not check! He stupidly applies the sdfsdfsdfsdfsdfsd context settings and the sdfsdfsdfsdfsdf context security policies! And since there are no security policies for the sdfsdfsdfsdfsdf context, then (we recall the rule above), if nothing is forbidden, then everything is possible !!! That is, the connector is running.
Moreover. It turns out that in order to gain access to everything and everyone (including in the admin panel, that is, in the context of mgr), you do not need to tyirt a bunch of logins / passwords and other Labuda. The main thing is to steal the mischievous $ site_id (which is also global!) That is, it can be intercepted in even the most deadly piece of code missed through $ modx or if you have access to config.inc.php).
What do you get by getting $ site_id? Full access to admin requests to MODx. Try it yourself on your sites. If you are not authorized, send a simple GET request connectors / resource / index.php? & HTTP_MODAUTH = {value $ site_id} & ctx = fgdfgdfg & action = getToolbar. In this case, we substitute the value of the $ site_id variable as the HTTP_MODAUTH variable (but this is not enough, as the mgr context is still initialized), and the ctx lamp variable we force MODx to initialize a non-existent context, that is, to apply the absence of security settings. And of course, do not forget about the action variable (you can easily debug all these requests using the same firebug by playing around in the admin area of ​​your site, or by downloading the release from the MODx off site). Everything! Accessed! Of course, you will have to rummage to write some interesting requests, but as a result we can easily access the file system through the MODx embedded file manager…

So guys, protect your sites, who have something important :-)

By the way, how to do it?
Very simple. In the file connectors / index.php rewrite the code
$ctx = isset($_REQUEST[ 'ctx' ]) && !empty($_REQUEST[ 'ctx' ]) ? $_REQUEST[ 'ctx' ] : 'mgr' ;
$modx->initialize($ctx);



on
/* initialize the proper context */
$ctx = 'mgr' ;
$modx->initialize($ctx);



that is, initialize the mgr context tightly. This is an example. Further already look at tasks.

Source: https://habr.com/ru/post/121264/


All Articles