scripts

scripts:
    <script_name>: <base64_encoded_script>

Scripts are distinct atomic functionalities of an ATLAS rule, like functions of the functional programming languages.

Note

Currently Python and Powershell are supported. Javascript support is in the backlog.

Key-Value

A key can be an arbitrary keyword or the function name of the entry point in the script. First, it tries to call the key. Then, if it fails, it calls the run.

Note

The value must be base64 encoded.

scripts:
    # def printer(*args) -> bool:

    #     print(" ".join(args))
    #     return True
    s1: "ZGVmIHByaW50ZXIoKmFyZ3MpIC0+IGJvb2w6CgogICAgcHJpbnQoIiAiLmpvaW4oYXJncykpCiAgICByZXR1cm4gVHJ1ZQ=="
import base64

with open('script.py', 'rb') as file:
    data = file.read()

encoded_script = base64.b64encode(data)
# It is important to give -Encoding as UTF8 instead of directly getting Byte.
# The byte option appends byte order marker at the beginning thus ruins everthing
$content = Get-Content .\script.ps1 -Raw -Encoding UTF8
[System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($content))

Python’s execution

python_executor function inside the core library is used. The function gets the script as base64 encoded, the script’s name and arguments to pass to its entry point.

  • It does in-memory import for the script.

  • Calls the entry point.

  • Gets the return data.

Null Bytes

If the script contains null bytes, they must be encoded in the script:

Problematic script example
def run(data: bytes) -> bool:
    try:
        keyIL = re.search(b'(?<=((\x72)[\x00-\xff]{4}(\x80)[\x00-\xff]{4}(\x72)))[\x00-\xff]{4}', data).group()
        su = re.search(b'[\x00-\xff]{4}(?=([\x00-\xff]{4}(\x23\x55\x53\x00)))', data).group()
    except:
        return False

    return True

When ATLAS tries to execute a rule that contains the above function as a script, the base64 module will raise an exception due to null bytes.

Null byte solution
def run(data: bytes) -> bool:
    try:
        keyIL_encoded = b'KD88PSgocilbAC3/XXs0fSiAKVsALf9dezR9KHIpKSlbAC3/XXs0fQ=='
        su_encoded = b'WwAt/117NH0oPz0oWwAt/117NH0oI1VTACkpKQ=='
        keyIL = re.search(b64decode(keyIL_encoded), data).group()
        su = re.search(b64decode(su_encoded), data).group()
    except:
        return False

Powershell’s Execution

powershell_executor function inside the core library is used. The function gets the script as base64 encoded, the script’s name and arguments to pass to its entry point.

  • Creates a Powershell process.

  • Inside this process;
    • Decodes the script and creates a ScriptBlock.

    • Prepares the arguments.

    • Calls the entry point.

    • Base64 encodes the return data.

    • Creates TemporaryFile, prints the path, and writes encoded data to the file.

    • Stores the encoded data to the file.

  • In Python, it reads the file and decodes it.

Note

Right now str and bytes type arguments are supported for powershell execution.