
.NET 7 포크 버전의 seal-security-nuget-demo: 동일한 CVE-2024-21907 악용 스토리이지만, .NET SDK 7에 고정된 고객을 위해 재조정되었습니다.
이것은 표준 seal-security-nuget-demo(net9.0을 대상으로 함)의 대상을 변경한 포크입니다. 익스플로잇 스토리, 컨트롤러, 그리고 봉인된 패키지 목록은 동일합니다 — TargetFramework만 다릅니다.
이 포크가 존재하는 이유는 대부분의 엔터프라이즈 고객이 요청 시 최신 .NET SDK로 전환할 수 없기 때문입니다. .NET 7은 2024년 5월 14일에 지원이 종료되었지만, 실제 프로덕션 환경에서는 호환성, 인증, 또는 운영상의 이유로 여전히 이를 사용하고 있습니다. 이것이 바로 Seal Security가 설계된 정확한 사례입니다: 고객이 메이저 버전 업그레이드를 할 수 없거나(또는 하지 않으려 할 때), Seal은 취약한 의존성을 동일한 버전으로 제자리에서 패치하며, 공개 API 변경이나 고객 앱의 코드 수정이 없습니다. 이 데모는 고객에게 net8/net9을 먼저 설치하도록 요청하는 대신, 고객의 실제 스택에서 이 대화가 이루어지도록 합니다.
이 포크는 global.json을 통해 SDK를 7.0.x로 고정하여 데모 중에 실수로 업그레이드가 발생하지 않도록 합니다.
이 데모 애플리케이션은 Newtonsoft.Json 12.0.2을 사용하여 사용자 입력을 구성 객체로 파싱하는 간단한 ASP.NET Core 환영 페이지입니다. 앱에는 이름 필드가 있으며, 이름을 입력하고 Go를 클릭하면 가 표시됩니다. 내부적으로는 입력을 Newtonsoft.Json의 에 전달합니다. 그게 전부입니다 — 인기 있는 JSON 라이브러리의 완전히 표준적인 사용법입니다.
JsonConvert.DeserializeObject<NestedConfig>()문제는 Newtonsoft.Json 12.0.2(및 13.0.1 이전 버전)에 CVE-2024-21907 — CVSS 점수 7.5 (HIGH) 의 높은 심각도 서비스 거부(DoS) 취약점이 있다는 것입니다. 이 데모는 Seal Security가 메이저 버전 업그레이드 없이 취약점을 제자리에서 패치하는 방법을 보여줍니다.
Newtonsoft.Json의 JsonConvert.DeserializeObject<T>() 메서드는 깊게 중첩된 JSON 페이로드를 조작하여 악용될 수 있습니다. 타입이 지정된 객체(POCO)로 역직렬화할 때, 라이브러리의 JsonSerializerInternalReader는 진정한 재귀 호출(CreateValueInternal → CreateObject → PopulateObject → SetPropertyValue → CreateValueInternal)을 수행하여 스택 오버플로우를 일으키고, 이로 인해 애플리케이션이 충돌합니다(서비스 거부).
앱은 사용자 입력을 받아 Newtonsoft.Json을 통해 파싱합니다. 입력이 URL인 경우, 앱은 먼저 콘텐츠를 가져옵니다 — 구성 로더, API 테스터, 웹훅 수신기에 사용되는 현실적인 패턴입니다:
public class NestedConfig
{
[JsonProperty("n")]
public NestedConfig? N { get; set; }
}
var config = JsonConvert.DeserializeObject<NestedConfig>(name);
정상 입력: alice 입력 → "Welcome, alice!" 표시
익스플로잇: 이름 필드에 이 URL을 붙여넣고 Go를 클릭합니다:
https://raw.githubusercontent.com/seal-sec-demo-2/json-payload/main/payload.json
앱은 URL임을 감지하고 json-payload(깊게 중첩된 JSON {"n":{"n":{...}}})를 가져와 Newtonsoft.Json을 통해 재귀적인 NestedConfig 클래스로 역직렬화하여 스택 오버플로우를 트리거합니다.
JsonSerializerInternalReader는 모든 중첩 수준에 대해 CreateValueInternal → CreateObject → PopulateObject → SetPropertyValue를 재귀합니다. 약 5,000 수준 깊이에서 스레드 스택이 소진되고 애플리케이션이 StackOverflowException으로 충돌합니다 — 프로세스가 즉시 종료됩니다(우아한 오류 처리가 불가능).
이 취약점으로 공격자는 다음을 수행할 수 있습니다:
공개적으로 제공되는 수정 사항은 버전 13.0.1로 업그레이드해야 합니다. 그러나 메이저 버전 업그레이드는 종종 다음을 도입합니다:
이로 인해 "그냥 업그레이드" 수정은 개발자 시간 수 주가 소요될 수 있는 프로젝트가 되며, 그동안 취약점이 노출된 상태로 남게 됩니다.
Seal의 패치 버전(12.0.2-sp1)은 공개 API를 변경하지 않고 재귀 깊이 보호를 추가합니다. 패치는:
MaxDepth 제한 추가이것은 Newtonsoft.Json 13.0.1에 적용된 것과 동일한 완화 전략으로, 드롭인 교체로 12.0.2에 백포트된 것입니다.
이 데모에는 Seal Security가 패치할 수 있는 다른 취약한 NuGet 패키지도 포함되어 있습니다:
log4net의 XML 구성 파싱의 XML 외부 엔티티(XXE) 취약점. log4net 구성 파일을 제어할 수 있는 공격자는 다음을 수행할 수 있습니다:
System.Net.Http에 대한 참고: 표준 net9 데모에는 CVE-2017-0249에 대한 취약한System.Net.Http 4.3.0참조도 포함되어 있습니다. .NET 7에서System.Net.Http는 BCL의 일부이고 독립 실행형 패키지 참조는 잔존 메타 패키지이므로 이 net7 포크에서는 이를 생략했습니다 —dotnet add package --source <local-nupkg>(Seal CLI가 봉인된 버전을 적용하는 정확한 방식)에 알려진 엣지 케이스가 있습니다. 이를 제거하면 데모의 스토리를 변경하지 않고도seal fix단계가 안정적으로 작동합니다(HttpClient는 여전히 정상 작동하며 런타임이 제공합니다).
Windows CLI 바이너리는 v0.3.238 이후로 중단되었습니다. v0.3.238은 Windows에서 NuGet 수정에 완전히 기능합니다.
자세한 Windows Server 설치 및 실행 가이드는 README-WINDOWS-SERVER.md에 있습니다. 아래의 빠른 시작은 동일한 단계를 간결한 형태로 다룹니다.
PowerShell:
$env:SEAL_TOKEN = "your-seal-token-here"
$env:SEAL_PROJECT = "nuget-demo-net7"
cd seal-security-nuget-demo-net7
# 복원 (nuget.org 및 Seal 피드에서 가져옴 — nuget.config 참조)
dotnet restore
# 빌드 및 실행
dotnet build
dotnet run
http://localhost:5000을 엽니다 — 앱이 취약한 의존성으로 실행 중입니다.
이름 필드에 alice를 입력하고 Go를 클릭합니다. "Welcome, alice!" 가 표시되어야 합니다.
이름 필드에 다음 URL을 붙여넣고 Go를 클릭합니다:
https://raw.githubusercontent.com/seal-sec-demo-2/json-payload/main/payload.json
패치 전 결과: 브라우저에 오류 / 연결 재설정이 표시됩니다 — 앱이 JsonSerializerInternalReader.CreateValueInternal에서 StackOverflowException으로 충돌했습니다. 프로세스가 종료되었습니다.
# (선택 사항 — 위에서 이미 수행) 먼저 의존성 복원
dotnet restore
# 취약점 수정을 위해 Seal CLI 실행
seal fix . --mode remote -v
# 봉인된 버전을 가져오기 위해 다시 복원
dotnet restore
# 패치된 앱 빌드 및 실행
dotnet build
dotnet run
이제 앱은 패치된 버전(Newtonsoft.Json 12.0.2-sp1, log4net 2.0.5-sp1)을 사용합니다.
패치 후 결과: 동일한 익스플로잇 URL을 붙여넣으면 페이지에 "Blocked by Seal patch: MaxDepth of 64 has been exceeded." 가 표시됩니다 — Newtonsoft.Json의 패치된 재귀 제한이 깊은 페이로드를 거부했습니다. 서버는 정상적으로 계속 실행됩니다.
dotnet list package
Seal Security 패치를 나타내는 -sp1 접미사가 있는 패키지가 표시되어야 합니다.
CLI 단계는 의존성이 설치된 직후, 그러나 최종 빌드 이전에 추가되어야 합니다.
# 1. 의존성 복원
dotnet restore
# 2. <--- 여기서 Seal CLI 실행
$env:SEAL_TOKEN = "your-seal-token-here"
$env:SEAL_PROJECT = "nuget-demo-net7"
seal fix . --mode remote -v
# 3. 다시 복원 (봉인된 버전을 가져오기 위해)
dotnet restore
# 4. 빌드
dotnet build
| 모드 | 설명 |
|---|---|
all | 사용 가능한 모든 수정 사항을 자동으로 적용 |
remote | Seal UI에서 승인된 수정 사항만 적용 |
local | .seal-actions.yml에 정의된 수정 사항만 적용 |
이 저장소의 .seal-actions.yml은 이미 local 모드에 대한 세 가지 봉인된 재정의를 나열하므로 seal fix . --mode local -v는 오프라인에서 작동합니다(아티팩트 서버에는 여전히 토큰 필요).
nuget.config는 환경 변수와 함께 Seal Security를 사용하도록 사전 구성되어 있습니다:
<configuration>
<packageSources>
<add key="Seal" value="https://nuget.sealsecurity.io/v3/index.json" />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
</packageSources>
<packageSourceCredentials>
<Seal>
<add key="Username" value="%SEAL_PROJECT%" />
<add key="ClearTextPassword" value="%SEAL_TOKEN%" />
</Seal>
</packageSourceCredentials>
</configuration>
| 변수 | 설명 |
|---|---|
SEAL_TOKEN | Seal Security 액세스 토큰 |
SEAL_PROJECT | 프로젝트 ID (예: nuget-demo-net7) |
Seal CLI는 다음에 대한 아웃바운드 HTTPS(TCP 443)가 필요합니다:
cli.sealsecurity.io — 스캔 / 수정 구성authorization.sealsecurity.io — 토큰 검증nuget.sealsecurity.io — 봉인된 .nupkg 다운로드d2zko6i8myndc4.cloudfront.net — 실제 봉인된 아티팩트를 제공하는 CDN (.sealsecurity.io 호스트 이름이 여기로 리디렉션됨)api.nuget.org / 표준 nuget.org 엔드포인트 — 봉인되지 않은 의존성용방화벽이 열린 후 PowerShell에서 확인:
Test-NetConnection cli.sealsecurity.io -Port 443
Test-NetConnection authorization.sealsecurity.io -Port 443
Test-NetConnection nuget.sealsecurity.io -Port 443
Test-NetConnection d2zko6i8myndc4.cloudfront.net -Port 443
모두 TcpTestSucceeded: True를 보고해야 합니다.
| 항목 | 중요한 이유 |
|---|---|
global.json이 SDK를 rollForward: latestFeature로 7.0.x에 고정 | net8/net9이 병렬 설치된 머신이 데모 중에 SDK를 조용히 전환하는 것을 방지합니다. |
System.Configuration.ConfigurationManager가 7.0.0으로 고정 | 표준 net9 데모는 8.0.0을 사용하는데, 이는 net8만 대상으로 하며 net7에서 복원에 실패합니다. 7.0.0은 log4net이 필요로 하는 동일한 API 표면을 가지고 있습니다. |
csproj에서 NU1701 억제 | log4net 2.0.5는 .NET 7이 런타임에서 수용하지만 복원 시 경고하는 레거시 net4x TFM을 광고합니다. 경고는 표면적인 것이며, 억제는 데모 중 빌드 출력을 깨끗하게 유지합니다. |
| Windows x64용 Seal CLI v0.3.238 | Windows 바이너리가 있는 마지막 릴리스; NuGet 수정에 완전히 기능합니다. Windows 바이너리는 이 버전 이후로 중단되었으므로 최신 릴리스를 제안하지 마십시오. |
12.0.2-sp1은 12.0.2에 대한 바이너리 호환 드롭인 교체입니다.이 데모에서 사용되는 패키지:
| 패키지 | 취약한 버전 | 봉인된 버전 | CVE | CVSS |
|---|---|---|---|---|
| Newtonsoft.Json | 12.0.2 | 12.0.2-sp1 | CVE-2024-21907 | 7.5 HIGH |
| log4net | 2.0.5 | 2.0.5-sp1 | CVE-2018-1285 | 9.8 CRITICAL |
Seal 피드에서 사용 가능한 기타 봉인된 NuGet 패키지(이 데모에는 없음, 참고용으로 나열):
| 패키지 | 취약한 버전 | 봉인된 버전 | CVE | CVSS |
|---|---|---|---|---|
| log4net | 2.0.0 | 2.0.0-sp1 | CVE-2018-1285 | 9.8 CRITICAL |
| System.Net.Http | 4.3.0 | 4.3.0-sp1 | CVE-2017-0249 | 7.3 HIGH |
| Snappier | 1.1.0 | 1.1.0-sp1 | CVE-2023-28638 | 7.0 HIGH |
| jQuery.Validation | 1.17.0 | 1.17.0-sp1 | CVE-2021-21252 | 7.5 HIGH |
MIT 라이선스 — 자세한 내용은 LICENSE 파일을 참조하십시오.