New script to load main.inc.php on third-party modules, avoiding [Include of main fails]

Hello, I’m a module developer for Dolibarr with about 6 years of experience, and I have 3 modules on Dolistore (Purchases, Stocktransfers, TOTp 2FA) and two themes.

The issue I’d like to discuss is that in recent weeks, I’ve been contacted by a couple of customers (people who purchased my modules) who were having trouble getting them to work and were seeing the “Include of main fails” message in the browser on a blank screen. If you’re a module developer, you probably know what this means: the module script couldn’t locate the main.inc.php file from the Dolibarr core.

With one of these customers facing this problem, the root cause was that their Dolibarr installation had been done using an automated script by their hosting provider (Bitnami on AWS), and as a result, the Dolibarr core was installed in an ABSOLUTE path DIFFERENT from that of the /custom subdirectory. Therefore, the recommended “reverse search” system doesn’t work:

   // Try main.inc.php using relative path
   if (!$res && file_exists("../main.inc.php")) $res = @include "../main.inc.php";
   if (!$res && file_exists("../../main.inc.php")) $res = @include "../../main.inc.php";
   if (!$res && file_exists("../../../main.inc.php")) $res = @include "../../../main.inc.php";
   if (!$res) die("Include of main fails");

So, for that customer, I simply added an @include($absolute_path) with the absolute path of their installation:

/opt/bitnami/dolibarr/htdocs/main.inc.php

I realized that this path wasn’t dependent on their username or domain name, so it might be useful for other Dolibarr users using Bitnami.

That’s when I thought the IDEAL solution would be to develop a SINGLE PHP script to be included at the root of all my modules. This script would search for the main.inc.php file in the usual locations but also in locations like the one used by Bitnami.

But that’s not all! The script I’ve created, instead of displaying a static message like “Include of main fails,” presents the user with a form in the browser, explaining the issue better and asking them to enter the absolute path of the main.inc.php file in a text box. Then, my script checks if it can actually load it, and if successful, it saves that path in a plain text file called “load_main_inc_php,” stored in the root of /custom. This way, it becomes the first thing to be read the next time the module is called and tries to load the main.inc.php file, eliminating the need for repeated recursive searches.

I’ve published this script on Github along with the explanations I’m giving here, so that together, we can make this script more powerful and “smart.”

https://github.com/caos30/dolibarr_load_main_inc_php

Furthermore, I suggest to the Dolibarr core team that they consider incorporating this into the “module builder” by default.

What do you think? Am I doing something reckless or am I on the right track? Do you have any suggestions?

Best regards,
Sergi

1 Like

Hello,

Thanks for sharing!
May be this can goes in a Feature Request in dolibarr Github?

Hello @ksar , i was just waiting the approval HERE from people like you who deeply knows how does run Dolibarr in the core (better than me) to create a Feature Request in Github, as you say. Yes.

Also, i was waiting for an answer from Philippe (Dolistore) regarding the same question (i wrote to him on friday), and he has suggested to me to test also (on the Bitnami installation) to use the server variables:

  • $_SERVER['CONTEXT_DOCUMENT_ROOT']
  • $_SERVER['DOCUMENT_ROOT']
  • even maybe $_SERVER['SCRIPT_FILENAME']

So, i wrote to my customer using Bitnami to ask him to share with me an output of these variables from a PHP script to put in /custom directory… to know at least if it would run for Bitnami installation.

But, yes, i suppose that anyway, independently of the results on Bitnami or the addition of this CONTEXT_DOCUMENT_ROOT path exploration, continue being useful to have a unique script like load_main.inc.php to be included always by any third module, to properly and smartly loading of the main.inc.php file.

Note: i want to remark that in any case, i need the support of Dolibarr core team to proceed with this kind of loading of main.inc.php file, because -at leas by now- Dolistore is not letting me to upload modules using this alternative loading system. You probably know that Dolistore run an automated script to check certain rules on the modules uploaded to Dolistore. One of those rules is to check that you include main.inc.php file using the classical “reverse back” allocating (../ , ../../ , ../../../ ) and if you don’t include it ALWAYS in this way, then the module is immediately rejected.

Ups, in my initial version of this script i forgot to add the use of the $_SERVER variables CONTEXT_DOCUMENT_ROOT and SCRIPT_FILENAME added from Dolibarr 6 in the module builder! :sweat_smile:

@eldy suggested it to me. Thanks Laurent!

These variables are not always defined depending also on the server settings, but usually should help a lot to likely find the location of the Dolibarr core files, like main.inc.php

So i’ve updated now my script on Github.

1 Like

So we have to update the method
private function validateZipFile
into
https://github.com/Dolibarr/dolibarr-foundation/blob/develop/dolistore/modules/blockmysales/blockmysales.php
where is the prestashop module to manage dolistore, to accept a module with only the
load_main.inc.php

After a quick view of this code, can you try to rename your file load_main.inc.php into main_module.inc.php for example ? I think it should be enough to bypass the validation of your module.

Also note that the dynamic solution you used to write the file when file is not found looks very dangerous. It introduces serious vulnerability for a hacker to make its remote execution code.

1 Like

Thank you @eldy, indeed I have been able to upload the update of my module to Dolistore using main_module.inc.php instead of load_main.inc.php.

I’ve updated the Github respository whith this change.

It’s unlikely that someone other than the administrator will see that form, but I have modified the form to disallow the use of remote URLs like https:// or sftp:// etc. I believe it is quite robust now. But more suggestions are gladly accepted.

// 6. last try, with the PATH passed from the <FORM> in this script to let the user indicate the path

	if ($path=='' && !empty($_POST['main_inc_php_path']) 
				&& file_exists($_POST['main_inc_php_path'])){
		
		// SSRF (Server Side Request Forgery) prevention
		$regexp1 = "/:\/\//i";
		$regexp2 = "/^(http|https|ftp|ftps|sftp|ldap|ldaps|gopher|telnet|ssh|file|dict|news|nntp|nntps):/i";

		if (!preg_match($regexp1, $url) && !preg_match($regexp2, $url)) {

			define('NOCSRFCHECK',1); // this disable for this unique call the check of the CSRF security token!
			if (@include($_POST['main_inc_php_path'])) $path = $_POST['main_inc_php_path'];
			
		}
	}

Oops… @eldy rereading my previous comment, I realize that the file_exists() function will return FALSE when the provided PATH is an external “url”! So, that check I added with preg_match($regexp,$url) isn’t necessary :sweat_smile:

Hello,

Don’t know if you have seen that : NEW better way to manage main.inc.php inclusion in modules generated … by altairis-noe · Pull Request #26151 · Dolibarr/dolibarr · GitHub

2 Likes

Thanks @ksar, i’ve respond on that thread of Github.

Surely not enough.
The include is from a user data
include($_POST[‘main_inc_php_path’])
so a user that succeed it creating a file on disk (for example by uploading a file) can execute any executable code of its choice on the server. I seriously discourage of doing this in your module.

1 Like

Yes @eldy it’s true that the risk exists, BUT it’s very minimal because:

  1. The script main_module.inc.php only displays on the web browser the form to write the file path of main.inc.php when this file cannot be found in the usual locations. So this affects only a very low percentage of users.
  2. This webform appears ONLY ONCE for a Dolibarr instance: when installing a third-party module. So again, it’s a very low percentage of cases where a hacker is able to know the exact day and hour of this installation by the admin user of the Dolibarr instance.

Anyway, I’ve added these 2 extra checks:

  1. The filename must be main.inc.php (this is obvious, but it wasn’t being checked).

  2. The parent directory containing this file must also contain one of the core directories of the Dolibarr core and its sub-directories (fourn/facture/tpl). So for the hack to be successful, the hacker needs to be able also to create directories and subdirectories on the server… which is also very unlikely if that user does not have access to FTP/SSH.

Although it could still be possible. So please, suggest new defences :slight_smile: