cancel
Showing results for 
Search instead for 
Did you mean: 

incredible security hole with scheduler

vincent-kali
Star Contributor
Star Contributor
When running script actions launched by alfresco scheduler, I got the following behaviour (5.0.d or 5.1.e):
- scheduler run as 'System' or 'admin' (same results), executes js script stored in repo folder 'Scheduled Actions'
- this script create/move/delete documents
- documents created or moved have arbitrary 'owner' or 'modifiedBy' properties
- documents deleted appear in arbitrary user's trash ( -> user gains access to content he had no permission on initially)

Consequences:
- any users may become owner-deletors of any content created/moved/deleted by this script, executed by admin/system.
- Users ask me: why this document is owned by 'john', he has no access to this site…….

It seems that this 'arbitrary user' is the last logged in user…..
This is very easy to reproduce, you just have write a js script that:
- create log file in any site
- update it each times it runs
you'll see that the cm:modifier becomes any arbitrary user……

one question : How is it possible ??? did somebody face the same kind of issue ?

Scheduled-action-service-context.xml:

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>

<beans>
   
    <!–
    Define the model factory used to generate object models suitable for use with freemarker templates.
    –>
    <bean id="templateActionModelFactory" class="org.alfresco.repo.action.scheduled.FreeMarkerWithLuceneExtensionsModelFactory">
        <property name="serviceRegistry">
            <ref bean="ServiceRegistry"/>
        </property>
    </bean>
   
   <bean id="runScriptActionTestJScript" class="org.alfresco.repo.action.scheduled.SimpleTemplateActionDefinition">
      <property name="actionName">
         <value>script</value>
      </property>
      <property name="parameterTemplates">
         <map>
            <entry>
               <key>
                  <value>script-ref</value>
               </key>
               <value>\$\{selectSingleNode('workspace://SpacesStore', 'fts-alfresco', 'PATH:"/app:company_home/app:dictionary/cm:Scheduled_x0020_Actions/cm:myTestScript.js"' )\}</value>
            </entry>
         </map>
      </property>
      <property name="templateActionModelFactory">
         <ref bean="templateActionModelFactory"/>
      </property>
      <property name="dictionaryService">
         <ref bean="DictionaryService"/>
      </property>
      <property name="actionService">
         <ref bean="ActionService"/>
      </property>
      <property name="templateService">
         <ref bean="TemplateService"/>
      </property>
   </bean>
   
   
   
    <bean id="runtestScheduleEveryOneMinutes" class="org.alfresco.repo.action.scheduled.CronScheduledQueryBasedTemplateActionDefinition">
        <property name="transactionMode">
            <value>UNTIL_FIRST_FAILURE</value>
         <!–value>ISOLATED_TRANSACTIONS</value–>
        </property>
        <property name="compensatingActionMode">
            <value>IGNORE</value>
        </property>
        <property name="searchService">
            <ref bean="SearchService"/>
        </property>
        <property name="templateService">
            <ref bean="TemplateService"/>
        </property>
        <property name="queryLanguage">
            <value>fts-alfresco</value>
        </property>
        <property name="stores">
            <list>
                <value>workspace://SpacesStore</value>
            </list>
        </property>
        <!– Find all nodes that do not have the aspect –>
        <property name="queryTemplate">
            <value>PATH:"/app:company_home/st:sites/cm:mySite/cm:documentLibrary/cm:_Inbox"</value>
        </property>
        <property name="cronExpression">
            <value>0 0/15 * * * ?</value>
        </property>
        <property name="jobName">
            <value>TestJScriptJobName</value>
        </property>
        <property name="jobGroup">
            <value>TestJScriptJobGroup</value>
        </property>
        <property name="triggerName">
            <value>TestJScriptTriggerName</value>
        </property>
        <property name="triggerGroup">
            <value>TestJScriptTriggerGroup</value>
        </property>
        <property name="scheduler">
            <ref bean="schedulerFactory"/>
        </property>
        <property name="actionService">
            <ref bean="ActionService"/>
        </property>
        <property name="templateActionModelFactory">
            <ref bean="templateActionModelFactory"/>
        </property>
        <property name="templateActionDefinition">
            <ref bean="runScriptActionTestJScript"/>
        </property>
        <property name="transactionService">
            <ref bean="TransactionService"/>
        </property>
        <property name="runAsUser">
            <value>System</value>
            <!–value>admin</value–>
        </property>
    </bean>
   
</beans>

4 REPLIES 4

steven_okennedy
Star Contributor
Star Contributor
Hi,

Out of interest, can you include the content of the script you're running (/app:company_home/app:dictionary/cmSmiley Frustratedcheduled_x0020_Actions/cm:myTestScript.js) ?

Can you also outline how many files exist beneath /app:company_home/st:sites/cm:mySite/cm:documentLibrary/cm:_Inbox and whether the arbitrary cm:modifier property values you're seeing correspond to the modifiers/owners of these files?  The queryTemplate you've specified will run your script for each of the nodes that exist underneath the cm:_Inbox path, so it would be good to see if this has a bearing here

Thanks

Steven

vincent-kali
Star Contributor
Star Contributor
Hi steven,
The whole process is aimed to publish invoices to a Customer portal.
All Customer invoices from ERP are pushed into 'Inbox'. The scheduler run the script which is parsing the folder content, and takes actions for each child document (transform, classify, extract content, add aspect, manage permissions, move files….). I can't put all the script here (very big script), but below is the main logic.
Script is run as either System or admin user by the scheduler, and I get the following:
- for moved invoices: cm:modifier is replaced by an arbitrary user (any user currently authenticated against alfresco, I didn't understand any rule, could be the last logged in user). cm:creator is preserved, and no cmSmiley Surprisedwner defined (ownable aspect no set).
- for deleted temporary files (mainly transformed document), following properties are set to an arbitrary user :
cmSmiley Surprisedwner, cm:modifier, cm:creator
The arbitrary users ARE NOT MEMBERS of the site.
- for script log file: cm:modifier altered

I think it's possible to reproduce this just by running a simple script that create/open a log file, and write into it each time the scheduler execute the jobs.
The cm:modifier will be altered to any authenticated user. (environment 5.x community, with some users working on the platform)

structure of my script:

logger.system.out(" -> running script, " + new Date().toString());

try{


   if ((debug) || (log))
   {
      // get or create log file
      var configFolder = companyhome.childByNamePath(configRootPath);
      var logFile = configFolder.childByNamePath(logFileName);
      // if no log file, create a new one
      if ( logFile == null)
      {
         logFile = configFolder.createFile(logFileName);
      }
      // else rotate log file if necessary
      else if (parseInt(logFile.size/1024) > logMaxSize){
         var logFileBackup = configFolder.childByNamePath(logFileNameBackup);
         // remove previous log backup
         if (logFileBackup != null){
            configFolder.removeNode(logFileBackup);
         }
         // rename existing log
         logFile.name = logFileNameBackup;
         logFile.save();
         // create new log file
         logFile = configFolder.createFile(logFileName);
      }
      if ( logFile == null) throw "Unable to open/create log file";
      logFile.content += "\n\n________________________________________________________________\n";       
      logFile.content += new Date().toString() + "Run execution \n";       
   }
   // manage locks (one process at a time).
   var configFolder = companyhome.childByNamePath(configRootPath);
   var lockFile = companyhome.childByNamePath(configRootPath + "/" + lockFileName);
   if (!lockFile){  // lock file does not exists
      // create lock file if not exists
      if (!lockFile) lockFile = configFolder.createFile(lockFileName);
      if (!lockFile) throw "Unable to create lock file";
      
      // get document array to process
      var childsFiles = new Array();
      if (document.type == "{http://www.alfresco.org/model/content/1.0}folder"){
         if (log) logFile.content += "\nCurrent node is a folder, processing childs\n";
         childsFiles = document.childFileFolders(true, false);
      }
      else if (document.type == "{http://www.alfresco.org/model/content/1.0}content"){
         if (log) logFile.content += "\nCurrent node is a document, processing current node only\n";
         childsFiles[0] = document;
      }
      else {
         throw "Unknow document type";
      }
      // process documents
      for (ii_main in childsFiles){
         if (log) logFile.content += " -> Processing file: " + childsFiles[ii_main].properties.name + "\n";   
         if (debug) logger.system.out(" -> Processing file: " + childsFiles[ii_main].properties.name);
         
         //
         // put all business logic here ()
         //
         
      }
      // release lock (hide)
      var lockFile = companyhome.childByNamePath(configRootPath + "/" + lockFileName);
      if (lockFile){
         configFolder.removeNode(lockFile);
      }
   }
   else{
      // Process already running
      if (log) logFile.content += "Process already running\n";
      if (debug) logger.system.out("Process already running");
   }
}
catch (err){
   logger.system.out("! Aborting, error message: " + err);
   if (log) logFile.content += "\n ! Aborting, error message: " + err + "\n";
   // release lock
   var configFolder = companyhome.childByNamePath(configRootPath);
   var lockFile = companyhome.childByNamePath(configRootPath + "/" + lockFileName);
   if (lockFile){
      configFolder.removeNode(lockFile);
   }
}

vincent-kali
Star Contributor
Star Contributor
I reproduce this behaviour with any 5.x version (tested on 5.0.d, 5.1.e, 5.1.g).
To summarize:
- Scheduler execute script action as 'admin' user
- Script is just openning a log file and writing current user name into it
- Users are accessing alfresco using CIFS protocol (Kerberso authentication)
- User context of the script becomes any authenticated user, log files properties cm:modifier is altered with any arbitrary user (breaking security model, this user has no access to current site…).

This is a major security breach.
I'll open a case in jira.

Does anybody already faced this ? Any suggestion ?




vincent-kali
Star Contributor
Star Contributor