Sploitus

Exploit for rewrite

kitploit Β· 2026-09-04

Exploit Code

MARKDOWN223 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-MODERNEINC-REWRITE-CVE-2026-22732
# rewrite-cve-2026-22732

CVE-2026-22732에 μ·¨μ•½ν•œ μ½”λ“œλ₯Ό νƒμ§€ν•˜λŠ” OpenRewrite λ ˆμ‹œν”Όμž…λ‹ˆλ‹€. μ΄λŠ” μ„Έ κ°€μ§€ 응닡 λ©”μ„œλ“œ 쀑 ν•˜λ‚˜λ₯Ό 톡해 `Content-Length`λ₯Ό μ„€μ •ν•˜λ©΄ Spring Security의 `OnCommittedResponseWrapper`λ₯Ό μš°νšŒν•˜λŠ” Spring Security κ²°ν•¨μž…λ‹ˆλ‹€. λž˜νΌκ°€ 헀더λ₯Ό μΈμ‹ν•˜μ§€ λͺ»ν•˜λ―€λ‘œ `onResponseCommitted()`κ°€ μ‹€ν–‰λ˜μ§€ μ•Šκ³ , μ§€μ—° μΆ”κ°€λ˜λŠ” λ³΄μ•ˆ 헀더(`X-Frame-Options`, `X-Content-Type-Options`, `Cache-Control` λ“±)κ°€ 쑰용히 λˆ„λ½λ©λ‹ˆλ‹€.

## 탐지 λŒ€μƒ

μ·¨μ•½ν•œ Spring Security 6.4.12 + Spring Boot 3.4.3 / λ‚΄μž₯ Tomcatμ—μ„œ ν™•μΈλœ μ‹€μ œ νŠΈλ¦¬κ±°λŠ” λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

  1. **래퍼λ₯Ό μš°νšŒν•˜λŠ” μ˜€λ²„λ‘œλ“œλ₯Ό ν†΅ν•œ μ„œλΈ”λ¦Ώ`Content-Length`**

root@kitploit:~
         
         response.setHeader("Content-Length", "42");
         response.setIntHeader("Content-Length", 42);
         response.addIntHeader("Content-Length", 42);
         

이 μ„Έ κ°€μ§€ μ˜€λ²„λ‘œλ“œλŠ” `OnCommittedResponseWrapper`μ—μ„œ μž¬μ •μ˜λ˜μ§€ _μ•ŠμŠ΅λ‹ˆλ‹€_. 이후 λ³Έλ¬Έ μ“°κΈ°κ°€ μ„ μ–Έλœ 길이λ₯Ό μ™„μ„±ν•˜κ³  μ»¨ν…Œμ΄λ„ˆλŠ” μ§€μ—° 헀더 μž‘μ„±κΈ°λ₯Ό μ‹€ν–‰ν•˜μ§€ μ•Šκ³  μ»€λ°‹ν•©λ‹ˆλ‹€.

  2. **`HttpHeaders`λ₯Ό ν†΅ν•œ WebFlux `Content-Length`**

root@kitploit:~
         
         serverHttpResponse.getHeaders().set("Content-Length", "42");
         serverHttpResponse.getHeaders().add("Content-Length", "42");
         serverHttpResponse.getHeaders().setContentLength(42L);
         

  3. **WebFlux 무쑰건적 응닡 컀밋**

root@kitploit:~
         
         serverHttpResponse.writeWith(Mono.just(dataBuffer));
         serverHttpResponse.writeAndFlushWith(publisher);
         serverHttpResponse.setComplete();
         




이 λ ˆμ‹œν”ΌλŠ” Spring Security 쑴재 μ—¬λΆ€(즉, `org.springframework.security.*` νƒ€μž…μ„ μ°Έμ‘°ν•˜μ§€ μ•ŠλŠ” νŒŒμΌμ—μ„œλŠ” 아무것도 좜λ ₯ν•˜μ§€ μ•ŠμŒ)와 영ν–₯λ°›λŠ” Spring Security 버전 λ²”μœ„μ— 따라 κ²Œμ΄νŒ…λ©λ‹ˆλ‹€. 2026-03-19에 κ²Œμ‹œλœ Spring κΆŒκ³ μ— λ”°λ₯΄λ©΄ 영ν–₯ λ²”μœ„μ™€ μˆ˜μ • 버전은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€:

ν•΄λ‹Ή μ‹œλ¦¬μ¦ˆμ˜ μˆ˜μ • 버전 이상(λ˜λŠ” 7.0 / 6.5 μ΄ν›„μ˜ ν–₯ν›„ μ‹œλ¦¬μ¦ˆ)의 Spring Security 버전을 ν•΄μ„ν•˜λŠ” ν”„λ‘œμ νŠΈλŠ” 영ν–₯λ°›μ§€ μ•ŠλŠ” κ²ƒμœΌλ‘œ 처리되며 싱크 λ˜λŠ” νŒŒμΌλ³„ 마컀λ₯Ό λ°›μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. 버전을 해석할 수 μ—†λŠ” ν”„λ‘œμ νŠΈλŠ” 일반적인 νŒ¨ν„΄ 기반 νƒμ§€λ‘œ ν΄λ°±λ˜μ–΄ μŠ€μΊλ„ˆκ°€ λ°˜μ¦ν•  수 μ—†λŠ” λ°œκ²¬μ„ λ³΄κ³ ν•˜λŠ” μͺ½μœΌλ‘œ 였λ₯˜λ₯Ό λ²”ν•©λ‹ˆλ‹€. `SpringSecurityVersionByProject` 데이터 ν…Œμ΄λΈ”μ€ μ—¬μ „νžˆ ν•΄μ„λœ 버전을 κΈ°λ‘ν•˜κ³  각 ν”„λ‘œμ νŠΈλ₯Ό 영ν–₯ μ—¬λΆ€λ‘œ ν”Œλž˜κ·Έν•˜λ―€λ‘œ ν•„ν„°λ§λœ λ‚΄μš©μ„ 감사할 수 μžˆμŠ΅λ‹ˆλ‹€.

## μ˜λ„μ μœΌλ‘œ ν”Œλž˜κ·Έν•˜μ§€ μ•ŠλŠ” ν•­λͺ©

λ‹€μŒμ€ μœ„ν—˜ν•΄ λ³΄μ΄μ§€λ§Œ λž˜νΌκ°€ μΆ”μ ν•˜λ―€λ‘œ 응닡이 μ»€λ°‹λ˜κΈ° 전에 λ³΄μ•ˆ 헀더가 μž‘μ„±λ©λ‹ˆλ‹€:

Semgrep 데λͺ¨μ˜ `/vuln/flush` μ—”λ“œν¬μΈνŠΈλŠ” `flushBuffer()`κ°€ 트리거라고 μ£Όμž₯ν•˜μ§€λ§Œ, μ·¨μ•½ν•œ Spring Security 6.4.12μ—μ„œ 응닡은 μ‹€μ œλ‘œ μ—¬μ„― 개의 λ³΄μ•ˆ 헀더λ₯Ό λͺ¨λ‘ λ°˜ν™˜ν•©λ‹ˆλ‹€. 데λͺ¨μ˜ μ‹€μ œ νŠΈλ¦¬κ±°λŠ” `/vuln/stream` 및 `/vuln/content-length`의 `setIntHeader("Content-Length", ...)` ν˜ΈμΆœμž…λ‹ˆλ‹€.

## 탐지

λ‹€μŒμ„ μ‹€ν–‰ν•˜μ„Έμš”:

λ ˆμ‹œν”Ό| μš©λ„  
---|---  
`io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression`| λͺ¨λ“  탐지λ₯Ό μ‹€ν–‰ν•˜κ³  버전 보고 ν…Œμ΄λΈ”μ„ 좜λ ₯ν•©λ‹ˆλ‹€  
  
### ꡬ성 μš”μ†Œ (κ³ κΈ‰)

μœ„μ˜ μ§‘κ³„κΈ°λŠ” 두 개의 더 μž‘μ€ λ ˆμ‹œν”Όλ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€. ν•˜λ‚˜μ˜ νƒμ§€λ§Œ μ›ν•˜λŠ” 경우 κ°œλ³„μ μœΌλ‘œ ν˜ΈμΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.

## μˆ˜μ •

λ‹€μŒμ„ μ‹€ν–‰ν•˜μ„Έμš”:

λ ˆμ‹œν”Ό| μš©λ„  
---|---  
`io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppression`| 각 ν”„λ‘œμ νŠΈκ°€ μ‹€μ œλ‘œ μ·¨ν•  수 μžˆλŠ” κ°€μž₯ μ €λ ΄ν•œ μˆ˜μ •μ„ μ„ νƒν•©λ‹ˆλ‹€  
  
두 단계λ₯Ό μˆœμ„œλŒ€λ‘œ μ‹€ν–‰ν•©λ‹ˆλ‹€.

**1\. ν”„λ‘œμ νŠΈ 자체 μ‹œλ¦¬μ¦ˆμ˜ μˆ˜μ • λ²„μ „μœΌλ‘œ μ—…κ·Έλ ˆμ΄λ“œ.** Spring SecurityλŠ” μˆ˜μ • 버전을 Maven Central에 6.5.9 및 7.0.4둜 κ²Œμ‹œν–ˆμŠ΅λ‹ˆλ‹€. 각 μ—…κ·Έλ ˆμ΄λ“œλŠ” `FindAffectedSpringSecuritySeries` 사전 쑰건에 κ²Œμ΄νŒ…λ©λ‹ˆλ‹€. `UpgradeDependencyVersion`은 λŒ€μƒμ΄ _더 μƒˆλ‘œμš΄_ λ²„μ „μΈμ§€λ§Œ ν™•μΈν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. 7.0.4둜 μ΄λ™ν•˜λΌκ³  μ§€μ‹œν•˜λ©΄ 5.8 ν”„λ‘œμ νŠΈλ₯Ό 두 개의 메이저 버전을 κ°€λ‘œμ§ˆλŸ¬ 끌고 갈 수 μžˆμŠ΅λ‹ˆλ‹€.

**2\. 1λ‹¨κ³„μ—μ„œ μˆ˜μ •ν•  수 μ—†λŠ” ν•­λͺ©μ— λŒ€ν•΄ μ„ μ œμ  헀더 μž‘μ„± ꡬ성을 μΆ”κ°€.** μ΄λŠ” ν”„λ‘œμ νŠΈλ‹Ή ν•˜λ‚˜μ˜ `@Configuration` 클래슀λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€:

root@kitploit:~
    
    
    @Bean
    public static BeanPostProcessor eagerHeaderWriterFilterBeanPostProcessor() {
        return new BeanPostProcessor() {
            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof HeaderWriterFilter) {
                    ((HeaderWriterFilter) bean).setShouldWriteHeadersEagerly(true);
                }
                return bean;
            }
        };
    }
    

헀더λ₯Ό 미리 μž‘μ„±ν•˜λ©΄ λž˜νΌκ°€ 컀밋을 κ΄€μ°°ν•˜λŠ”μ§€ μ—¬λΆ€κ°€ λ¬΄μ˜λ―Έν•΄μ§€λ―€λ‘œ ν”„λ‘œμ νŠΈμ˜ _λͺ¨λ“ _ 싱크가 ν•œ λ²ˆμ— λ‹«νž™λ‹ˆλ‹€. μ—¬κΈ°μ—λŠ” "μ•Œλ €μ§„ μ œν•œ 사항"의 `Map` νλ¦„μ²˜λŸΌ μ˜€μ—Ό 뢄석이 도달할 수 μ—†λŠ” 싱크도 ν¬ν•¨λ©λ‹ˆλ‹€. `BeanPostProcessor`λŠ” `AutowireBeanFactoryObjectPostProcessor`κ°€ 빈 νŒ©ν† λ¦¬λ₯Ό 톡해 ν•„ν„°λ₯Ό μ΄ˆκΈ°ν™”ν•˜λ―€λ‘œ `HttpSecurity` DSL둜 λΉŒλ“œλœ ν•„ν„°λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€. 이 CVE에 λŒ€ν•΄ λ…λ¦½μ μœΌλ‘œ κ²Œμ‹œλœ 두 κ°€μ§€ μˆ˜μ • 사항이 μ •ν™•νžˆ 이 ν˜•νƒœλ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€(hmcts/idam-web-public 및 armory-io의 Spinnaker ν¬ν¬λŠ” λ™λ“±ν•œ `ObjectPostProcessor`λ₯Ό 톡해).

2λ‹¨κ³„λŠ” 1단계가 λ„μšΈ 수 μ—†λŠ” ν”„λ‘œμ νŠΈλ₯Ό λ‹€λ£Ήλ‹ˆλ‹€:

상황| μ—…κ·Έλ ˆμ΄λ“œκ°€ μž‘λ™ν•˜μ§€ μ•ŠλŠ” 이유  
---|---  
5.7, 5.8, 6.3, 6.4| μˆ˜μ • 사항이 Spring Enterprise κ΅¬λ…μžμ—κ²Œλ§Œ 제곡됨 β€” Maven Central에 μ—†μŒ  
6.0 - 6.2| ν•΄λ‹Ή μ‹œλ¦¬μ¦ˆμ—λŠ” μˆ˜μ • 버전이 κ²Œμ‹œλœ 적 μ—†μŒ  
κ°€μ Έμ˜¨ BOM으둜 κ΄€λ¦¬λ˜λŠ” 버전| μ—…κ·Έλ ˆμ΄λ“œκ°€ νŽΈμ§‘ν•  둜컬 선언이 μ—†μŒ  
  
λ§ˆμ§€λ§‰ 행은 κ°€μž₯자리 사둀가 μ•„λ‹™λ‹ˆλ‹€. `nla/bamboo`λŠ” μ˜€ν”ˆμ†ŒμŠ€ μˆ˜μ • 버전이 _μžˆλŠ”_ μ‹œλ¦¬μ¦ˆμΈ 7.0.3을 μ „μ μœΌλ‘œ Spring Boot BOMμ—μ„œ ν•΄μ„ν•˜λ―€λ‘œ 버전 ν™•μΈλ§ŒμœΌλ‘œλŠ” 두 단계 λͺ¨λ‘μ—μ„œ κ±΄λ„ˆλ›°κ³  μ·¨μ•½ν•œ μƒνƒœλ‘œ λ‚¨κ²Œ λ©λ‹ˆλ‹€. λ”°λΌμ„œ `AddEagerHeaderWriterConfiguration`은 ν”„λ‘œμ νŠΈμ— μ˜€ν”ˆμ†ŒμŠ€ μˆ˜μ • 버전이 있고 _자체 버전을 μ„ μ–Έν•œ_ κ²½μš°μ—λ§Œ μ—…κ·Έλ ˆμ΄λ“œμ— μœ„μž„ν•©λ‹ˆλ‹€.

μƒμ„±λœ ν΄λž˜μŠ€λŠ” `@EnableWebSecurity` ν΄λž˜μŠ€κ°€ μžˆλŠ” 경우 κ·Έ μ˜†μ— 배치되고, μ—†μœΌλ©΄ `@SpringBootApplication`, κ·Έ λ‹€μŒμ—λŠ” λͺ¨λ“  `@Configuration`으둜 ν΄λ°±λ˜λ―€λ‘œ 항상 μ»΄ν¬λ„ŒνŠΈ μŠ€μΊ”μ΄ λ„λ‹¬ν•˜λŠ” μœ„μΉ˜μ— λ°°μΉ˜λ©λ‹ˆλ‹€. 이미 헀더λ₯Ό μ„ μ œμ μœΌλ‘œ μž‘μ„±ν•˜λŠ” ν”„λ‘œμ νŠΈλ‚˜ 패치된 `OnCommittedResponseWrapper`λ₯Ό λ²€λ”λ§ν•˜λŠ” ν”„λ‘œμ νŠΈ(jogetworkflow/jw-community처럼)λŠ” κ·ΈλŒ€λ‘œ λ‘‘λ‹ˆλ‹€.

### ꡬ성 μš”μ†Œ (κ³ κΈ‰)

λ ˆμ‹œν”Ό| μš©λ„  
---|---  
`io.moderne.recipe.cve202622732.UpgradeSpringSecurityToPatchedVersion`| μ‹œλ¦¬μ¦ˆ κ²Œμ΄νŒ…λœ 6.5.9 / 7.0.4 μ—…κ·Έλ ˆμ΄λ“œ  
`io.moderne.recipe.cve202622732.AddEagerHeaderWriterConfiguration`| μƒμ„±λœ ꡬ성 자체  
`io.moderne.recipe.cve202622732.FindAffectedSpringSecuritySeries`| 영ν–₯λ°›λŠ” μ‹œλ¦¬μ¦ˆμ˜ ν”„λ‘œμ νŠΈλ₯Ό ν‘œμ‹œν•©λ‹ˆλ‹€. μ—…κ·Έλ ˆμ΄λ“œ 사전 μ‘°κ±΄μž…λ‹ˆλ‹€  
  
### μ‹€ν–‰ 쀑인 μ„œλ²„λ‘œ 검증

μ·¨μ•½ν•œ Spring Security 6.4.12의 `semgrep/cve-2026-22732-demo`에 λ‚΄μž₯ Tomcat을 λŒ€μƒμœΌλ‘œ μ μš©ν–ˆμŠ΅λ‹ˆλ‹€. ν•΄λ‹Ή `HeaderVerificationTest`λŠ” λ³΄μ•ˆ 헀더가 _λˆ„λ½_ λ˜μ—ˆμŒμ„ μ£Όμž₯ν•˜λ―€λ‘œ μž‘λ™ν•˜λŠ” μˆ˜μ • 사항은 이λ₯Ό μ‹€νŒ¨ν•˜κ²Œ λ§Œλ“­λ‹ˆλ‹€:

`X-Frame-Options` 및 `Cache-Control`도 λ™μΌν•œ νŒ¨ν„΄μ„ λ”°λ¦…λ‹ˆλ‹€.

λΉŒλ“œλ˜λŠ” μ½”νΌμŠ€μ˜ 16개 μ €μž₯μ†Œ(μ‹λ³„λœ 29개 쀑)μ—μ„œ μˆ˜μ • 사항은 μ„Έ κ°œμ— λŒ€ν•œ ꡬ성을 μƒμ„±ν•˜κ³  λ‚˜λ¨Έμ§€λŠ” μ˜¬λ°”λ₯΄κ²Œ κ·ΈλŒ€λ‘œ λ‘μ—ˆμŠ΅λ‹ˆλ‹€:

μ„Έ 개의 패치된 μ €μž₯μ†Œμ— μˆ˜μ • 사항을 λ‹€μ‹œ μ‹€ν–‰ν•˜λ©΄ μΆ”κ°€ 생성이 μ—†μœΌλ―€λ‘œ μˆ˜μ •μ€ μ‹€μ œ ν”„λ‘œμ νŠΈμ—μ„œ 자체 좜λ ₯에 λŒ€ν•΄ λ©±λ“±μ μž…λ‹ˆλ‹€.

두 번째 더 넓은 μ½”νΌμŠ€λŠ” μˆ˜μ • 사항이 μ‹€μ œλ‘œ λ‹€λ£¨λŠ” λͺ¨μ§‘단(싱크가 ν•„μš” μ—†μœΌλ―€λ‘œ 영ν–₯λ°›λŠ” λͺ¨λ“  μ„œλΈ”λ¦Ώ Spring Security μ• ν”Œλ¦¬μΌ€μ΄μ…˜)을 λŒ€μƒμœΌλ‘œ ν•©λ‹ˆλ‹€. μ½”λ“œ κ²€μƒ‰μœΌλ‘œ 찾은 64개 ν”„λ‘œμ νŠΈ 쀑 52κ°œκ°€ λΉŒλ“œλ˜μ—ˆκ³ , 48κ°œκ°€ 영ν–₯λ°›λŠ” 버전을 ν•΄μ„ν–ˆμœΌλ©°, **38κ°œκ°€ νŒ¨μΉ˜λ˜μ—ˆκ³  그쀑 35κ°œκ°€ μ»΄νŒŒμΌλ©λ‹ˆλ‹€**(λ‚˜λ¨Έμ§€ 3κ°œλŠ” μƒμ„±λœ 파일 없이도 λ™μΌν•˜κ²Œ μ‹€νŒ¨). Spring Security 5.2 미만의 8개 ν”„λ‘œμ νŠΈλŠ” λͺ¨λ‘ μ˜¬λ°”λ₯΄κ²Œ κ±΄λ„ˆλ›°μ—ˆμŠ΅λ‹ˆλ‹€. `SUSCEPTIBLE-REPOSITORIES.md` μ„Ήμ…˜ 8을 μ°Έμ‘°ν•˜μ„Έμš”.

### μ œν•œ 사항

  * **WebFlux νƒμ§€λŠ” 이 CVEκ°€ μ•„λ‹Œ λ‹€λ₯Έ μœ„ν—˜μž…λ‹ˆλ‹€.** `OnCommittedResponseWrapper extends jakarta.servlet.http.HttpServletResponseWrapper`μ΄λ―€λ‘œ CVE-2026-22732λŠ” μ„œλΈ”λ¦Ώ μ „μš©μ΄λ©° λ¦¬μ•‘ν‹°λΈŒ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ€ 이에 λ…ΈμΆœλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. `FindHttpResponseContentLengthOrFlushBuffer`의 λ°œκ²¬μ€ μœ μ‚¬ν•œ λ¦¬μ•‘ν‹°λΈŒ νŒ¨ν„΄μ„ ν”Œλž˜κ·Έν•˜λ©° μ—¬μ „νžˆ μˆ˜λ™ κ²€ν† κ°€ ν•„μš”ν•˜μ§€λ§Œ μˆ˜μ • 사항은 μ˜λ„μ μœΌλ‘œ 이에 λŒ€ν•΄ μ‘°μΉ˜ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. `AddEagerHeaderWriterConfiguration`은 뒀에 μ„œλΈ”λ¦Ώ API 없이 `HeaderWriterFilter`λ₯Ό λ³Ό 수 μžˆλŠ” λͺ¨λ“  λͺ¨λ“ˆμ„ κ±΄λ„ˆλœλ‹ˆλ‹€. ν•„ν„°λŠ” `OncePerRequestFilter`λ₯Ό ν™•μž₯ν•˜κ³  `spring-security-web`은 μ„œλΈ”λ¦Ώ APIλ₯Ό 비전이적 `provided` μ’…μ†μ„±μœΌλ‘œ ν¬ν•¨ν•˜λ―€λ‘œ κ±°κΈ°μ„œ μƒμ„±ν•˜λ©΄ `cannot access jakarta.servlet.Filter`둜 μ‹€νŒ¨ν•©λ‹ˆλ‹€(`apache/shenyu`μ—μ„œ 관찰됨).
  * **Spring Security 5.2 미만 버전은 νƒμ§€λ˜μ§€λ§Œ μˆ˜μ •λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.** `HeaderWriterFilter.setShouldWriteHeadersEagerly`λŠ” 5.2μ—μ„œ λ„μž…λ©λ‹ˆλ‹€. 4.0.4μ—μ„œ ν•„ν„°μ—λŠ” μƒμ„±μžμ™€ `doFilterInternal`만 μžˆμŠ΅λ‹ˆλ‹€. νƒμ§€λŠ” μ—¬μ „νžˆ EOL 버전을 λ³΄κ³ ν•˜μ§€λ§Œ μ»΄νŒŒμΌν•  수 μ—†λŠ” ν˜ΈμΆœμ„ 좜λ ₯ν•˜λŠ” λŒ€μ‹  μˆ˜μ •μ΄ 보λ₯˜λ©λ‹ˆλ‹€.



## 데이터 ν…Œμ΄λΈ”

ν…Œμ΄λΈ”| ν–‰  
---|---  
`TaintFlowTable` (`rewrite-program-analysis`μ—μ„œ)| Content-Length 헀더 μ˜€μ—Ό 적쀑당 ν•œ ν–‰  
`HttpResponseDirectCommitTable`| WebFlux ꡬ쑰적 적쀑당 ν•œ ν–‰  
`SpringSecurityVersionByProject`| νƒμ§€λœ Spring Security 버전이 μžˆλŠ” ν”„λ‘œμ νŠΈλ‹Ή ν•œ ν–‰  
  
## μ‹€ν–‰

Moderne CLIλ₯Ό 톡해:

root@kitploit:~
    
    
    mod run . --recipe io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
    

`rewrite.yml`을 톡해:

root@kitploit:~
    
    
    ---
    type: specs.openrewrite.org/v1beta/recipe
    name: com.example.DetectSpringSecurityHeaderSuppression
    displayName: Detect CVE-2026-22732
    recipeList:
      - io.moderne.recipe.cve202622732.FindSpringSecurityHeaderSuppression
    

## 평가 μ½”νΌμŠ€ μž¬ν˜„

`repos.csv`λŠ” 이 λ ˆμ‹œν”Όκ°€ 개발되고 μΈ‘μ •λœ 75개의 곡개 μ €μž₯μ†Œλ₯Ό 각각 ν‰κ°€λœ μ •ν™•ν•œ 컀밋에 κ³ μ •ν•˜μ—¬ λ‚˜μ—΄ν•©λ‹ˆλ‹€. μ—¬λŸ¬ μ €μž₯μ†ŒλŠ” ν™œλ°œνžˆ μœ μ§€ κ΄€λ¦¬λ˜λ©° μ—…μŠ€νŠΈλ¦Όμ—μ„œ 패치될 μ˜ˆμ •μ΄λ―€λ‘œ `changeset` 열이 μ•„λž˜ 숫자λ₯Ό λ‹¨μˆœνžˆ κ·ΈλŸ΄λ“―ν•˜κ²Œ λ§Œλ“œλŠ” 것이 μ•„λ‹ˆλΌ μž¬ν˜„ κ°€λŠ₯ν•˜κ²Œ λ§Œλ“­λ‹ˆλ‹€.

root@kitploit:~
    
    
    mod git sync csv ./corpus repos.csv --with-sources
    mod build ./corpus
    mod run ./corpus --recipe=io.moderne.recipe.cve202622732.CveDevCenter
    mod devcenter ./corpus --last-recipe-run
    

λ™κΈ°ν™”λŠ” μ•½ 20μ΄ˆμ™€ 1.4GBκ°€ 걸리고, λΉŒλ“œλŠ” μ•½ 15뢄이 걸리며 μœ μΌν•˜κ²Œ 느린 λ‹¨κ³„μž…λ‹ˆλ‹€. `mod devcenter`λŠ” `corpus/.moderne/run/<runId>/`에 `devcenter.html`을 μž‘μ„±ν•˜κ³  쑰직 ν•˜μœ„ 디렉터리당 ν•˜λ‚˜μ”© μΆ”κ°€λ‘œ μž‘μ„±ν•©λ‹ˆλ‹€.

`org1` 열은 각 μ €μž₯μ†Œκ°€ μ‘΄μž¬ν•˜λŠ” 이유λ₯Ό λ°˜μ˜ν•˜λŠ” 그룹으둜 μ½”νΌμŠ€λ₯Ό λΆ„ν• ν•©λ‹ˆλ‹€. 두 κ°€μ§€ μ·¨μ•½ν•œ 호좜 ν˜•νƒœμ— λŒ€ν•œ `Servlet Sinks` 및 `WebFlux Sinks`, 이미 μ—…μŠ€νŠΈλ¦Όμ—μ„œ μˆ˜μ •λœ μ €μž₯μ†Œμ— λŒ€ν•œ `Patched`, Spring Security 자체 및 기타 λΉ„μ†ŒλΉ„μžμ— λŒ€ν•œ `Reference`, μ‹€ν–‰ 쀑인 μ„œλ²„μ— λŒ€ν•΄ ν™•μΈλœ 사둀에 λŒ€ν•œ `Verified`, λΉŒλ“œ 도ꡬ λ²”μœ„μ— λŒ€ν•œ `Gradle`, λŒ€λŸ‰ μƒ˜ν”Œμ— λŒ€ν•œ `Wide`μž…λ‹ˆλ‹€.

κ³ μ •λœ μ»€λ°‹μ—μ„œ κΈ°λŒ€ν•  수 μžˆλŠ” κ²°κ³Ό:

| κ²°κ³Ό  
---|---  
μ—…κ·Έλ ˆμ΄λ“œ μΉ΄λ“œ| 메이저 39, λ§ˆμ΄λ„ˆ 21, 패치 6, μ™„λ£Œ 4 (μ €μž₯μ†Œ 70개)  
λ³΄μ•ˆ μΉ΄λ“œ| λ…ΈμΆœλœ μ €μž₯μ†Œ 65개  
ν•΄λ‹Ή μ—†μŒ| Spring Security 쒅속성을 ν•΄μ„ν•˜μ§€ μ•ŠλŠ” μ €μž₯μ†Œ 5개  
  
κ·Έ λ‹€μ„― 개 쀑 λ„€ κ°œλŠ” μ‹€μ œλ‘œ Spring Securityλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. `spring-projects/spring-security`λŠ” 라이브러리 자체이고, `JoeyBling/bootplus`λŠ” Apache Shiroλ₯Ό μ‚¬μš©ν•˜λ©°, `jenkinsci/stapler`λŠ” μ„œλΈ”λ¦Ώ APIλ₯Ό 직접 λŒ€μƒμœΌλ‘œ ν•˜κ³ , `infofabrik/reportserver`μ—λŠ” 해석할 Maven λ˜λŠ” Gradle λΉŒλ“œκ°€ μ—†μŠ΅λ‹ˆλ‹€. λ‹€μ„― 번째인 `xtuer/template-app`은 `spring-security-web:5.0.0.RELEASE`λ₯Ό μ„ μ–Έν•˜μ§€λ§Œ ν•΄λ‹Ή Gradle λΉŒλ“œλŠ” `mod build` 쀑에 쒅속성을 μ „ν˜€ ν•΄μ„ν•˜μ§€ μ•ŠμœΌλ―€λ‘œ μ–΄λ–€ λ ˆμ‹œν”Όλ„ 버전을 λ³Ό 수 μ—†μŠ΅λ‹ˆλ‹€. 영ν–₯λ°›μ§€ μ•ŠλŠ” 것이 μ•„λ‹ˆλΌ μΈ‘μ •λ˜μ§€ μ•Šμ€ κ²ƒμœΌλ‘œ μ·¨κΈ‰ν•˜μ„Έμš”.

μˆ˜μ • 사항을 μ μš©ν•˜κ³  κ²°κ³Όλ₯Ό ν™•μΈν•˜λ €λ©΄:

root@kitploit:~
    
    
    mod run ./corpus --recipe=io.moderne.recipe.cve202622732.FixSpringSecurityHeaderSuppression
    mod git apply ./corpus --last-recipe-run
    mod exec ./corpus --last-recipe-run MODERNE_BUILD_TOOL_CHECK
    

`mod git apply`λŠ” 체크아웃에 직접 μž‘μ„±ν•©λ‹ˆλ‹€. μ½”νΌμŠ€ 전체에 λŒ€ν•œ 전체 `check`λŠ” 느리며 이 λ³€κ²½κ³Ό λ¬΄κ΄€ν•œ μ‹€νŒ¨(λˆ„λ½λœ JDK 툴체인, 도달할 수 μ—†λŠ” 쒅속성 μ €μž₯μ†Œ, 이미 λΉ¨κ°„ ν…ŒμŠ€νŠΈ)λ₯Ό ν‘œλ©΄ν™”ν•˜λ―€λ‘œ 의미 μžˆλŠ” μ‹ ν˜ΈλŠ” 적용 전에 λ™μΌν•œ λͺ…령을 μ‹€ν–‰ν•œ κ²ƒκ³Όμ˜ λΈνƒ€μž…λ‹ˆλ‹€.

## λ¦¬ν„°λŸ΄ 데λͺ¨ μ΄μƒμœΌλ‘œ λ‹€λ£¨λŠ” λ‚΄μš©

`rewrite-program-analysis`의 μ˜€μ—Ό 뢄석은 둜컬 데이터 흐름과 λ©”μ„œλ“œλ³„ μš”μ•½μ„ μ²˜λ¦¬ν•˜λ―€λ‘œ λ‹€μŒ νŒ¨ν„΄μ΄ μžλ™μœΌλ‘œ νƒμ§€λ©λ‹ˆλ‹€:

  * **μƒμˆ˜ μ „νŒŒλœ Content-Length 헀더 이름.** `String h = "Content-Length"; response.setIntHeader(h, 42);`κ°€ ν”Œλž˜κ·Έλ©λ‹ˆλ‹€. ν”„λ ˆμž„μ›Œν¬κ°€ 둜컬 할당을 톡해 λ¦¬ν„°λŸ΄ μ˜€μ—Όμ„ μΆ”μ ν•©λ‹ˆλ‹€.
  * **ν˜ΈμΆœμ„ λž˜ν•‘ν•˜λŠ” 헬퍼** β€” λ©”μ„œλ“œ μš”μ•½μ„ 톡해 λ°˜ν™˜ 값을 톡해 μ˜€μ—Όμ΄ νλ¦…λ‹ˆλ‹€.



## μ•Œλ €μ§„ μ œν•œ 사항

  * **μ œλ„€λ¦­ μ»¨ν…Œμ΄λ„ˆ νƒ€μž…μ„ ν†΅ν•œ 흐름**(`Map`, `List`, μ‚¬μš©μž μ •μ˜ μ»¬λ ‰μ…˜). `stash.put("k", "Content-Length")` λ‹€μŒμ— `response.setIntHeader(stash.get("k"), 42)`κ°€ μ˜€λŠ” κ²½μš°λŠ” νƒμ§€λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. put/get의 동일성은 뢄석에 뢈투λͺ…ν•©λ‹ˆλ‹€.



## λΌμ΄μ„ μŠ€

Moderne 독점. 상업 계약 쑰건에 따라 Moderne 고객만 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.