Dangers of importing non-trusted .fob files due to Event Subscriptions

Mark_SmartMark_Smart Member Posts: 16
The source code at the end of this post is perhaps a light hearted take on a serious issue, albeit one that is probably not well known. After some thought I feel that the wider community being aware of this is better than not letting the cat out of the bag, and any malicious hackers having a zero-day environment to play with.

Event triggers are a handy way to build solutions, especially for vertical products that you wish to import seamlessly without modifying any objects outside of your product range. This raises a security concern though, in that when you import a compiled object, the event subscriptions come into effect immediately. In theory you could even use a codeunit setup to run itself once to update data during implementation, and then remove itself when done.

So how is this an issue? Well, you could easily make an object that opens a http connection and sends the customer list from all companies in the database, or inserts a BACS payment. This code would run in the background without any user intervention.The object might even delete itself when done, or copy itself into an old and unused (but universally licensed) object number.

In the past whenever I have downloaded a handy tool in object form, I would import it and imediately scour the code, usually exporting it as text to be 100% sure nothing is hiding in there. With the ability to rewrite the source code separately from the compiled code in the Object Metadata table, malicious code could be hidden entirely.

I would suggest that before importing any .fob files, the following steps should occur:
  1. Import the fob into a quarantined database with no sensitive data, not connected to any network
  2. Design the object and save it, thus overwriting the compiled code with the visible source code.
  3. Check the object as usual
  4. Export the object for use in your real databases

Another defense might be to have a codeunit of your own which subscribes to the insert trigger of the Event Subscription table and throws an error whenever anything tries to add to it, thereby catching it during import.

Below is a basic proof of concept, somewhat inspired by the Useless Machine (a box with a switch that promptly switches itself off when switched on). Using numerous event subscriptions, the only purpose of this object is to remove itself. It is triggered by logging in, logging out, and any global insert/modify/delete actions. There is no indication that this has happened.
OBJECT Codeunit 50026 Useless Object
{
  OBJECT-PROPERTIES
  {
    Date=15.03.18;
    Time=11:40:54;
    Modified=Yes;
    Version List=USELESS OBJECT;
  }
  PROPERTIES
  {
    Permissions=TableData 2000000001=rimd,
                TableData 2000000071=rimd,
                TableData 2000000140=rimd;
    OnRun=BEGIN
            RunAway(TRUE);
          END;

  }
  CODE
  {
    VAR
      EventSubsciption@1100168001 : Record 2000000140;
      Object@1100168000 : Record 2000000001;
      ObjectMetadata@1100168003 : Record 2000000071;

    [EventSubscriber(Codeunit,1,OnAfterOnGlobalModify,"",Skip,Skip)]
    LOCAL PROCEDURE KingArthur@1100168000(RecRef@1100168000 : RecordRef;xRecRef@1100168001 : RecordRef);
    BEGIN
      RunAway(TRUE);
    END;

    [EventSubscriber(Codeunit,1,OnAfterOnGlobalDelete,"",Skip,Skip)]
    LOCAL PROCEDURE SirBedevere@1100168005(RecRef@1100168000 : RecordRef);
    BEGIN
      RunAway(TRUE);
    END;

    [EventSubscriber(Codeunit,1,OnAfterOnGlobalInsert,"",Skip,Skip)]
    LOCAL PROCEDURE SirGalahad@1100168004(RecRef@1100168000 : RecordRef);
    BEGIN
      RunAway(TRUE);
    END;

    [EventSubscriber(Codeunit,1,OnAfterCompanyOpen,"",Skip,Skip)]
    LOCAL PROCEDURE SirLancelot@1100168001();
    BEGIN
      RunAway(TRUE);
    END;

    [EventSubscriber(Codeunit,1,OnAfterCompanyClose,"",Skip,Skip)]
    LOCAL PROCEDURE SirRobin@1100168003();
    BEGIN
      RunAway(TRUE);
    END;

    LOCAL PROCEDURE RunAway@1100168002(SomethingHappened@1100168000 : Boolean);
    BEGIN
      IF SomethingHappened THEN BEGIN
        IF EventSubsciption.GET(50026,1100168000) THEN
          EventSubsciption.DELETE;

        IF Object.GET(Object.Type::Codeunit,'',50026) THEN
          Object.DELETE;

        IF ObjectMetadata.GET(ObjectMetadata."Object Type"::Codeunit,50026) THEN
          ObjectMetadata.DELETE;
      END;
    END;

    BEGIN
    END.
  }
}

Sign In or Register to comment.