HowTo: Detection of (malicious) script components

Hello,

over the past couple of years being an active member in this forum, I encountered several attempts to run malicious code hidden inside a uploaded Grasshopper definition.

Downloading a definition in this forum is always a high potential security risk. This is because, by default, a definition gets immediately executed in Grasshopper.

A simple practice I always do for years now, is to disable the automatic computation before loading a file.
solver

And then using the search function to spot script components …

find


So far so bad…

The problem with that is it can A. easily being tricked using user object or cluster and B. its a manual process with a high chance of missing something.

Now, the question is how can you scan a definition for script components without running the solver?

There are two options, creating a plugin or using the Rhinos Python Script Editor. The problem with the first one is its lack of portability. So I decided to create a Python script which gets executed from Rhino (not GH):

import Rhino
import Grasshopper as gh
import Grasshopper.Kernel.Special as ks
import System.Reflection as sr
import clr

def SearchScriptComponents(document):
    
    pyCount = 0
    csCount = 0
    vbCount = 0
    clCount = 0
    
    for docObj in document.Objects:
        
        if type(docObj) is ks.GH_Cluster:    
            clCount += 1
            subDoc = CheckCluster(docObj,ks.GH_Cluster)
            SearchScriptComponents(subDoc)
            continue
        elif type(docObj) is ks.GH_Cluster_OBSOLETE:    
            clCount += 1
            subDoc = CheckCluster(docObj,ks.GH_Cluster_OBSOLETE)
            SearchScriptComponents(subDoc)
            continue         
        
        typeName = docObj.ToString()
    
        if typeName.Contains("ython"):
            pyCount += 1
        elif typeName.Contains("CSNET"):
            csCount += 1
        elif typeName.Contains("VBNET"):
            vbCount += 1    
    
    print ("WARNING - On Document '%s' detected:\n\n\t %d Python, %d C#, %d VB.Net components and %d cluster\n" 
          % (document.DisplayName, pyCount, csCount, vbCount, clCount))
    
def CheckCluster(cluster, clType):
    clType = clr.GetClrType(clType)
    info = clType.GetField("m_internalDocument", 
        sr.BindingFlags.NonPublic | sr.BindingFlags.Instance)
    if (info == None):
        info = clType.GetField("m_doc", 
            sr.BindingFlags.NonPublic | sr.BindingFlags.Instance)
    return info.GetValue(cluster)

document = gh.Instances.ActiveCanvas.Document 
SearchScriptComponents(document)

scriptCheck.py (1.6 KB)
Script Detection Test.gh (8.7 KB)

Have fun, and happy new year!

15 Likes

This has been a huge concern of mine since I started using GH almost seven years ago. NOTHING from McNeel so thank you, I’ll check it out. Of course, detecting embedded code and knowing whether or not it’s malicious are two different things, eh?

2 Likes

The most malicious thing I’ve found was to have the colours of the canvas changed without asking. But yes, it’s a potential risk. So for me personally it is not worthwhile to include this scripting process manually. If I don’t trust the source I block the solver before opening it. Why don’t you upload a .gha version to Github so we can contribute to an automated version? If not, someone who doesn’t know to read a script can’t be really helped by this, I think. In the end an extension is needed, so why not a gha that only runs when an object is added to the document for example?

I don’t know if we can cancel the opening of a document while it’s opening, but we can investigate. At least I think the solver can be automatically blocked if it contains scripts or clusters (as a plugin user option I mean). Several conditions can be included depending on which libraries are referenced (if the script contains System.Reflection or unknown dlls for example) since if it only references native libraries, there is a possibility of being malicious? This can also search for keywords to carry out more concrete analyses. Surely something else can be done to automate it, so it would be good if you make the project open to collaboration somewhere. I don’t know python so I can not contribute more here.

Of course 99% of all script components are not malicious. Detecting odd behaviour for all supported languages is out of my skills. You also need to find and read the code, but at least you can detect script components now. Could be imrpoved a lot, just did this within 60 Minutes. And yes I agree, Grasshopper should build in something like this and refuse to execute if it detects script components.

1 Like

As an optional feature that is turned off by default please. Some of us issue Grasshopper definitions in production every day that are more or less 100% GHPython scripting components :wink:

3 Likes

Another question is whether antivirus/malware software can detect something embedded in a GH definition, even if its after the fact?

It will very likely not check, since it assumes you have written that. E.g., it’s not a malicious act of moving, deleting on encrypting files in first place. If you run as admin you can even do a lot of other fancy stuff. I wonder how many professionals here open definitions while being on a highly secured network. It’s not so uncommon, and as I said I’ve opened 3 files yet with questionable content.

@Dani_Abalde, I can put in on GitHub later. Feel free to modify it. I think the point of this thread is also to make people aware of the fact, that it’s a security risk to download gh definitions.

This is a risk, and we do/did discuss this internally (whether it’s plugins, RhinoScripts, gh files, …), but the problem doesn’t really allow for a decent solution.

Here’s three things we can do:

  1. Nothing. Which is what we’re doing now. Hope that this is not a serious issue. As far as I’m aware, it hasn’t been. So if you run across a file which does something malicious, please do let us know so we can do some damage control.
  2. Block any suspicious content. Which is what @TomTom is suggesting. But this leads simply to the situation where many files have to be manually okayed by the user. This becomes muscle memory after the 50th time no longer providing any protection. Also, how many users are in fact capable of assessing whether a file poses a threat? It really just shifts responsibility onto the user, who in many cases is not qualified to bear this responsibility.
  3. Try and detect malicious intent. This is pretty much a fools errand. It’s easy enough detecting suspicious looking code in script code prior to execution (File.Delete() calls for example), but it’s equally easy to write code which does something bad but doesn’t look like it does. Since we’re already in the zone of malicious intent by programming savvy antagonists, that’s a battle you can only lose.

Ultimately, code doing damage to a computer is a problem that needs to be solved by the OS. It is the only player in this game with the capacity to properly sandbox processes and keep track of changes made to the system.

Until they finally start doing this though, the only thing the user can (and should) do, is make sure they have a proper back-up system in place. This will prevent loss of work for any and all reasons up to and including catastrophic hardware failure, not just naughty C# scripts inside gh files.

1 Like

I used to code a very simple tool to achieve something similar. Maybe I can extend it somehow as it doesn’t parse VB and Python right now.

Also, certain components from third-party components, which operate on file system, may also be attack vector.

A practical issue for me is that Grasshopper doesn’t provide a before FileOpen event so that the scanning needs to be manually done.

1 Like

I wouldn’t say you need to block, but you could persist information on who has created a file and if the current user is not the same of the file (someone else…) and it detects script components, then the user should be informed about a potential security risk, preventing the solution to be executed. A user could then check manually, dismiss or run the solution. It would be helpful if all the code is shown in one page.

Furthermore I don’t think someone can do what antivirus scans are doing, since filesystem operation are not the only attacking vector. You could inject keylogger, c&c server to control other pcs, you could mess up the registry (breaking antivir software), stealing license keys …

Well okay, not a block in the strictly legal sense. But either a warning which will be comprehensively ignored by N% of users —thus not preventing anything bad from happening— or which will be taken at face value by (98-N)% of users, thus functioning as a block.

The remaining 2% will be informed and skilled enough to know what the warning really means and perhaps act on it, but that’s also the 2% who don’t really need the warning to begin with.

So here’s an idea, what if there’d be a way to explore a file without loading and running it, using some sort of browser window which would allow you to inspect all the components, plugins, scripts, expressions and embedded values. Such a feature would probably serve a purpose beyond searching for nasty surprises.

If you feel uncertain about a specific file and want to really see what’s under the hood, examine it first. If, perhaps, maybe, at some point, I add some malicious-intent detection code to Grasshopper, it could pop up a warning and offer to display the file in this browser first.

8 Likes

Yes, that would be nice. There is also no need for a big solution because it is not a big problem, but because of the relevance of this, it should be there. I would like that, when I try to open a file with scripts, the interface asks me if I am sure if I want to open it if I trust the source, or if I want to analyse it with that tool or reject it. But just having an isolated analyzer that the user must include in his workflow (before open any random file) when 99.99% is not needed… I don’t think the user’s tendency is to use it. Just a warning I think is enough (and necessary) for the user to escape from the routine of opening a file without thinking.

Only the first 30 times. After that the warning will no longer register on a conscious level.

Most shared definitions do not contain scripts, so the warning effect will be there. And I keep thinking what to do when the interface asks me if I want to recover the file from the autosave, despite having seen it hundreds of times. It’s just a layer of security, perfect solutions exist for perfect problems.

In Grasshopper2 expressions will be fully C# compliant, so the warning will have to include those as well.

2 Likes

:astonished: What type of malicious code have you seen ?

Achieved by Roslyn?

  • Tcp connection to a unknown endpoint.
  • unknown system libraries calls
  • Deletion of files

But none of them affected my system. All three where hidden in gh files and were executed silently. Not sure if they were attacks, pranks or spying activity…

1 Like

Are you saying files doing these things were posted on this forum?
Did you report these or raise any concerns at the time? Can you give us links to the posts?

The latest was send to me as PM, by an user who only signed in once. The cluster was pw protected and obfuscated. Chinese labels all over the place.

The two other attacks were not posted within this forum. One was in the old forum , the other one from a blog. It was some years ago. None of them were causing issues, since I basically checked definitions from beginning on. This was basically just because I was almost exclusively using Rhino in protected company networks, which made me behave very defensive from beginning on. The latest attack, I notified McNeel.

2 Likes