- ์ฒด์ธ๋ช : Equifax ์นจํด (2017) โ Apache Struts 2 OGNL ์ธ์ ์ โ ์น์ ธ โ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋๋ ์ ์ถ
- ํต์ฌ ์ทจ์ฝ์ : CVE-2017-5638 (Apache Struts Jakarta Multipart ํ์ OGNL ์ธ์ ์ , CVSS 3.0 10.0)
- ์ํฅ ๋ฒ์ : Struts 2.3.x(โค2.3.31) / 2.5.x(โค2.5.10) ยท ์์ : 2.3.32 ยท 2.5.10.1 (2017-03-06 ๊ณต๊ฐ)
- ๊ณต๊ฒฉ์: ์ค๊ตญ ์ธ๋ฏผํด๋ฐฉ๊ตฐ(PLA) ์ฐ๊ณ APT๋ก ์ง๋ชฉ โ 2020๋ ๋ฏธ ๋ฒ๋ฌด๋ถ๊ฐ 54์ฐ๊ตฌ์ ์์ 4๋ช ๊ธฐ์
- ์นจํด ๊ธฐ๊ฐ: 2017-05-13๊ฒฝ ~ 2017-07-29 (์ฝ 76์ผ) ยท ๊ณต๊ฐ: 2017-09-07
- ํผํด ๊ท๋ชจ: ๋ฏธ๊ตญ ์ฑ์ธ 1์ต 4,790๋ง ๋ช ์ ์ ์ฉ์ ๋ณด(์ด๋ฆยทSSNยท์๋ ์์ผยท์ฃผ์), ์นด๋๋ฒํธ 20๋ง 9์ฒ ๊ฑด, ๋ถ์๋ฌธ์ 18๋ง 2์ฒ ๊ฑด + ์๊ตญ 1,520๋งยท์บ๋๋ค 1.9๋ง
- ์ด ๋น์ฉ: FTCยทCFPBยท50๊ฐ์ฃผ ํฉ์๊ธ 5์ต 7,500๋ง ๋ฌ๋ฌ(์ต๋ 7์ต ๋ฌ๋ฌ), ์ ์ฒด ๋น์ฉ 14์ต ๋ฌ๋ฌ ์ด์์ผ๋ก ์ถ์
1. ๋ฐฐ๊ฒฝ/์ญ์ฌ โ '์ ์ฉ์ ์ฌํ์'๊ฐ ๋ฌด๋์ง๋ค
Equifax(์ํดํฉ์ค)๋ ๋ฏธ๊ตญ 3๋ ์ ์ฉํ๊ฐ์ฌ(CB) ์ค ํ๋๋ก, ์์ต ๋ช ์ ์ ์ฉ์ ์ยท๋์ถยท์นด๋ ์ด๋ ฅ์ ๋ค๊ณ ์๋ ์ฌ์ค์์ ๋ฐ์ดํฐ ๋ ์ ๊ธฐ์ ์ด์๋ค. ์ด๋ฆยท์ฃผ๋ฏผ๋ฒํธ(SSN)ยท์๋ ์์ผยท์ฃผ์๋ ๋ฌผ๋ก , ์๋น์๊ฐ ์ ๊ธฐํ ์ ์ฉ ๋ถ์ ๋ฌธ์๊น์ง ํ ๊ณณ์ ๋ชจ์ฌ ์์๋ค. ์ด ํ์ฌ๋ฅผ ๋ซ๋๋ค๋ ๊ฑด '์ ์ฉ์ ์ฌํ์'๋ฅผ ํต์งธ๋ก ๋ณต์ ํ๋ ๊ฒ๊ณผ ๊ฐ์๋ค.
๋ฌด๋๋ 2017๋ 3์์ด์๋ค. ์คํ์์ค ์น ํ๋ ์์ํฌ Apache Struts 2์์ ์น๋ช ์ ์ ๋ก๋ฐ์ด๊ฐ ๊ณต๊ฐ๋๋ค(CVE-2017-5638). Struts๋ ์๋ง์ ๋๊ธฐ์ ยท์ ๋ถ ์น ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ผ๋์๊ณ , ํจ์น๋ 2017๋ 3์ 6์ผ ๊ณต๊ฐ๋๋ค. ๊ทธ๋ฐ๋ฐ Equifax์ ์จ๋ผ์ธ ๋ถ์(dispute) ํฌํธ์ ์ด ํ๋ ์์ํฌ๋ก ๋ง๋ค์ด์ ธ ์์๊ณ โ ํจ์น๋ ์ ์ฉ๋์ง ์์๋ค.
- ์ ์ ๋ช ํ๊ฐ: ๋จ์ผ ์ทจ์ฝ์ (CVE ํ ๊ฑด) ํ๋๋ก 1์ต ๋ช ๋ ๊ฐ์ธ์ ๋ณด๊ฐ ์ ์ถ๋, ์ญ์ฌ์ ๊ฐ์ฅ ๋น์ฉ์ด ํฐ ์นจํด ์ฌ๊ณ ์ค ํ๋๋ค. '๊ธฐ์ ๊ฒฐํจ'๋ณด๋ค ํจ์น ๊ด๋ฆฌ ์คํจ๊ฐ ์ฐธ์ฌ์ ๋ณธ์ง์ด๋ผ๋ ์ ์์ ๋ชจ๋ ๋ณด์ ๊ต์ก์ ๊ต๊ณผ์์ ์ฌ๋ก๊ฐ ๋๋ค.
- ์์ด๋ฌ๋: Equifax๋ ์ ๋ณด๋ณดํธ ๊ธฐ์ ์ ๋ชจํ์ฌ์ ๋ํ ์ ๋ฃ ๋ชจ๋ํฐ๋ง ์๋น์ค๋ฅผ ํ๋ ํ์ฌ์๋ค. ์ฌ๊ณ ํ ๊ทธ ์๋น์ค์ ์ฝ๊ด์ด '์์ก ์ ๋ฐฐ์ ์ฒญ๊ตฌ๋ฅผ ํฌ๊ธฐํ๋ค'๋ ์กฐํญ์ ํฌํจํด ์ฌ๋ก ์ ์ญํ์ ๋ง์๋ค(์ดํ ์กฐํญ ์ญ์ ).
- ๊ตฌ์กฐ์ ๋ฌธ์ : ์ํยทGAO ์กฐ์ฌ๋ ๋ง๋ฃ๋ ์ค์บ๋ ์ธ์ฆ์, ๋ฐฉ์น๋ ์์ฐ ์ธ๋ฒคํ ๋ฆฌ, ๋ฏธ์ ์ฉ ํจ์น๊ฐ ๊ฒน์น '๊ด๋ฆฌ ์คํจ์ ํจ์'์์์ ๋๋ฌ๋๋ค. ์ด๋ค ๋๊ตฌ๋ฅผ ์๋๋๊ฐ ์๋๋ผ ์ด์์ด ๋๊ณ ์์๋๋๊ฐ ๋ฌธ์ ์๋ค.
- ๋ฒยท์ ๋ ํ์ฅ: ๋ฏธ๊ตญ ์ต์ด๋ก ๊ฐ์ธ์ ๋ณด ์ ์ถ์ ์ํยท๋ด๋ถ์ ๊ฑฐ๋๊น์ง ๋คํฐ ์ฌ๊ฑด์ด ๋๋ค. ์ต๊ณ ์ ๋ณด์ฑ ์์(CIO)๊ฐ ์ฌ๊ณ ๊ณต๊ฐ ์ ์ฃผ์์ ํ์ SEC๊ฐ ๋ด๋ถ์ ๊ฑฐ๋๋ก ๊ธฐ์ํ๊ณ , FTCยทCFPBยท50๊ฐ ์ฃผ๊ฐ ๊ณต๋์ผ๋ก 5์ต 7,500๋ง ๋ฌ๋ฌ ํฉ์๋ฅผ ์ด๋์ด๋๋ค.
2. ์นจํดยท์ ํ ๊ฒฝ๋ก โ 4๋จ๊ณ ๊ณต๊ฒฉ ์ฒด์ธ
Equifax ์ฌ๊ณ ๋ '์ทจ์ฝ์ ํ๋'๊ฐ ์๋๋ผ ๊ณต๊ฒฉ ์ฒด์ธ(attack chain)์ด๋ค. ๊ฐ ๋จ๊ณ๋ ์ด์ ๋จ๊ณ ์์ด๋ ์ฑ๋ฆฝํ์ง ์์๊ณ , ์ด๋ ํ ๊ณ ๋ฆฌ์์๋ ๋ง์ ์ ์์๋ค.
- โ ์ง์
(Exploit): ๊ณต๊ฒฉ์๊ฐ ์จ๋ผ์ธ ๋ถ์ ํฌํธ์ ์กฐ์๋
Content-Typeํค๋๋ฅผ ๋ณด๋ธ๋ค. Struts๊ฐ ์์ฒญ ํ์ฑ์ ์คํจํ๋ฉด์ ๊ทธ ํค๋ ๊ฐ์ด OGNL ํํ์์ผ๋ก ํ๊ฐ๋์ด ๊ฒฐ๊ตญ ์๊ฒฉ ์ฝ๋ ์คํ(RCE)์ผ๋ก ์ด์ด์ง๋ค. ๋ก๊ทธ์ธยท๋น๋ฐ๋ฒํธ๊ฐ ํ์ ์๋ค โ ๊ทธ๋ฅ HTTP ์์ฒญ ํ ๋ฒ์ด๋ฉด ๋๋ค. - โก ๋ฐํ(Webshell): ์คํ ๊ถํ์ ์ป์ ๊ณต๊ฒฉ์๋ ์น์๋ฒ์ ๊ฐ๊ฒฐํ JSP ์น์ ธ(์ด๋ฅธ๋ฐ 'China Chopper' ๊ณ์ด)์ ์ฌ๋๋ค. ์ดํ๋ถํฐ๋ ์ ์ ์น ์์ฒญ์ ๋ช ๋ น์ ์ค์ด ์กฐ์ฉํ ์ ์ดํ๋ค โ ๋ฐฉํ๋ฒฝ ๊ด์ ์์๋ '์น ํธ๋ํฝ'์ผ๋ก ๋ณด์ธ๋ค.
- โข ํก์ ์ด๋(Lateral Movement): ์น์๋ฒ๋ ์ธํฐ๋ท ๊ฒฝ๊ณ์ ์์ง๋ง ๋ด๋ถ DB์ ์ฐ๊ฒฐ๋ผ ์์๋ค. ๊ณต๊ฒฉ์๋ ์น์ ธ โ ๋ด๋ถ ์๊ฒฉ์ฆ๋ช โ 51๊ฐ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ก ์ด๋ํ๋ฉฐ ์ ๊ทผ ๋ฒ์๋ฅผ ๋ํ๋ค. ํ ๋์ ์น์๋ฒ๊ฐ ๋ด๋ถ๋ง์ '์ด๋ฆฐ ๋ฌธ'์ด ๋ ์ ์ด๋ค.
- โฃ ์ ์ถ(Exfiltration): ์ฝ 76์ผ ๋์ ๋ฐ์ดํฐ๋ฅผ ๋๋ ๋นผ๋๋ค. ๋๋ ๋จ์ผ ์ ์ก์ด ์๋๋ผ ์ ์ ์กฐํ ํธ๋ํฝ์ ์์ด ๋๋ฆฌ๊ฒ ์ ์ถํ๊ธฐ ๋๋ฌธ์ ๋น์ DLPยท์ด์ํ์ง๊ฐ ๋๋ด ์ก์ง ๋ชปํ๋ค.
ํต์ฌ ๊ตํ์ '์น์๋ฒ = ์ ๋ขฐ ๊ฒฝ๊ณ'๋ผ๋ ์ฐฉ๊ฐ์ด๋ค. ๊ณต๊ฒฉ์๋ ์ธํฐ๋ท์ ์ด๋ฆฐ ๋ก์ ์น์ฑ์ ๋ฐํ ์ผ์ ๋ด๋ถ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊น์ง ๊ฑธ์ด ๋ค์ด๊ฐ๋ค. ์ง์ ์ ์ ํ๋์๊ณ , ๋ฐฉ์ด์ ์ ์ฌ๋ฌ ๊ฐ์๋๋ฐ, ์ ๋ถ ๋ซ๋ ธ๋ค.
3. ๋์ ์๋ฆฌ ์์ธ ๋ถ์ โ CVE-2017-5638, ์ 10.0์ธ๊ฐ
๊ฒฐํจ์ Struts 2์ Jakarta Multipart ํ์(JakartaMultiPartRequest)์
์์๋ค. ์น ์์ฒญ์ด ๋ฉํฐํํธ(multipart/form-data, ํ์ผ ์
๋ก๋ ํ์)์ธ๋ฐ ํ์ฑ์ ์คํจํ๋ฉด,
ํ์๋ ์์ธ ๋ฉ์์ง๋ฅผ ๋ง๋ ๋ค. ๋ฌธ์ ๋ ์ด๋ ์๋ฌ ๋ฉ์์ง์
Content-Type ํค๋ ๊ฐ์ ๊ทธ๋๋ก ๋ผ์ ๋ฃ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ
LocalizedTextUtil.findText()๊ฐ OGNL๋ก ํ๊ฐํ๋ค๋ ์ ์ด๋ค.
- OGNL(Object-Graph Navigation Language)์ด๋: ์๋ฐ ๊ฐ์ฒด ๊ทธ๋ํ๋ฅผ ํ์ยทํธ์ถํ๋ ํํ์ ์ธ์ด๋ค. Struts๋ ํ๋ฉด ๊ฐ์ ๊ฐ์ฒด์ ๋ฐ์ธ๋ฉํ๋ ค๊ณ OGNL์ ํญ๋๊ฒ ์ด๋ค. ์ฆ ์ฌ์ฉ์๊ฐ ๋ฃ์ ๋ฌธ์์ด์ด 'ํ๊ฐ ๋์'์ด ๋๋ ํ๋ฉด์ด ์๋๋ถํฐ ๋์๋ค.
- ์น๋ช
์ ๊ฒฐํฉ: 'ํค๋ ๊ฐ โ ์์ธ ๋ฉ์์ง ๋ฌธ์์ด โ OGNL ํ๊ฐ'๋ผ๋ ๊ฒฝ๋ก๊ฐ ์ด์ด์ง๋ฉด์,
๊ณต๊ฒฉ์๋
Content-Type์์ OGNL ์์ ๋ฃ์ ์ ์๊ฒ ๋๋ค. OGNL์ ๋จ์ ์ฐ์ ์ ๋์ด ์์ Java ๋ฉ์๋ ํธ์ถ์ด ๊ฐ๋ฅํ๋ฏ๋ก, ์ ํ๋๊ฐ ๊ณง ์๊ฒฉ ์ฝ๋ ์คํ์ด ๋๋ค. ์ด๊ฒ์ด CVSS 10.0(์ต๊ณ ์ํ)์ธ ์ด์ ๋ค โ ์ธ์ฆ ๋ถํ์ ยท ์๊ฒฉ ยท ์ฆ์ ์ฝ๋ ์คํ. - ํจ์น ๋ฐฉ์: ์์ ํ(2.3.32ยท2.5.10.1)์ ์์ธ ๋ฉ์์ง์ ์ฌ์ฉ์ ์ ๋ ฅ์ ๋ฌธ์์ด๋ก๋ง ๋ค๋ฃจ๊ณ ํ๊ฐํ์ง ์๋๋ก ๋ฐ๊ฟจ๋ค. '๋ฐ์ดํฐ'์ '์ฝ๋'๋ฅผ ๋ถ๋ฆฌํ๋ ๊ฐ์ฅ ๊ณ ์ ์ ์ธ ์ฒ๋ฐฉ์ด๋ค.
- ์ ํ์ ์ต์คํ๋ก์ ํํ: ๊ณต๊ฒฉ์๋
Content-Type๊ฐ์%{...}๋ก ๊ฐ์ผ OGNL ์์ ๋ฃ๋๋ค. ์ ์์์๋ ๊ธฐ๋ณธ ๋ฉค๋ฒ ์ ๊ทผ ์ ํ์ ํด์ ํ๊ณ (#dm=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS), ํ๋ก์ธ์ค ์คํ ๊ฐ์ฒด๋ฅผ ์ป์ด ๋ช ๋ น์ ์คํํ๋ค. ์ฌ๊ธฐ์๋ ๊ตฌ์กฐ ์ดํด๋ฅผ ์ํด ์์ ์ถ์ฝํด ์ค๋ช ํ๋ฉฐ, ๊ตฌ์ฒด์ ์คํ ํ์ด๋ก๋๋ ๋ค๋ฃจ์ง ์๋๋ค.
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ์ทจ์ฝ์ ์ ํ | OGNL ์ธ์ ์ โ ์๊ฒฉ ์ฝ๋ ์คํ(RCE) |
| ๊ณต๊ฒฉ ๋ฒกํฐ | HTTP Content-Type ํค๋ (๋ฉํฐํํธ ํ์ฑ ์คํจ ๊ฒฝ๋ก) |
| ์ธ์ฆ ํ์? | ๋ถํ์ (๋ฏธ์ธ์ฆ ์๊ฒฉ) |
| ๊ณต๊ฐ โ ์ ์ฉ | ๊ณต๊ฐ(3/6) ์งํ ์ ์๊ฐ ๋ด ์ ์ฉ ๊ด์ธก, ์ ์ธ๊ณ ์ค์บ๋ ํญ์ฆ |
| ํจ์น | Struts 2.3.32 / 2.5.10.1 (2017-03-06) |
| ์ฐ์ ์ทจ์ฝ์ | ๊ฐ์ ํด CVE-2017-9805(REST ํ๋ฌ๊ทธ์ธ XStream ์ญ์ง๋ ฌํ) ๋ฑ Struts ๊ฒฐํจ ์๋ฐ๋ผ ๊ณต๊ฐ |
4. ํผํด ๊ท๋ชจ/์ํฅ โ ์ซ์๋ก ๋ณด๋ ์ฐธ์ฌ
- ๊ฐ์ธ์ ๋ณด: ๋ฏธ๊ตญ ์๋น์ 1์ต 4,790๋ง ๋ช โ ์ฌ์ค์ ๋ฏธ๊ตญ ์ฑ์ธ ์ธ๊ตฌ์ ์ ๋ฐ ์ด์. ์ด๋ฆยท์ฃผ๋ฏผ๋ฒํธยท์๋ ์์ผยท์ฃผ์๊ฐ ์ ์ถ๋๋ค. ์ด๊ธฐ ๋ฐํ๋ 1์ต 4,300๋ง ๋ช ์ด์๊ณ ์ดํ ์ํฅ๋๋ค.
- ๊ธ์ต์ ๋ณด: ์ ์ฉ์นด๋๋ฒํธ 20๋ง 9์ฒ ๊ฑด, ์ ์ฉ ๋ถ์ ๊ด๋ จ ๋ฌธ์ 18๋ง 2์ฒ ๊ฑด. ์๊ตญ ์ฝ 1,520๋ง ๋ช , ์บ๋๋ค ์ฝ 1๋ง 9์ฒ ๋ช ๋ ์ํฅ๊ถ์ ๋ค์๋ค.
- ๊ธฐ๊ฐ: ์นจํด๋ 2017๋ 5์ ์ค์๋ถํฐ ์ฝ 76์ผ ์ด์ด์ก๊ณ , 7์ 29์ผ์์ผ ๋ฐ๊ฒฌ๋๋ค. ๋ฐ๊ฒฌ ๋ค์ ๋ ์ธ 7์ 30์ผ Equifax๋ ๊ทธ์ ์ผ ํจ์น๋ฅผ ์ ์ฉํ๋ค โ ์ด๋ฏธ 4๊ฐ์ ์ด์ ๋ฆ์ ์์ ์ด์๋ค.
- ๊ฒฝ์ ์ ๋น์ฉ: 2019๋ FTCยทCFPBยท50๊ฐ ์ฃผ ํฉ์๋ก 5์ต 7,500๋ง ๋ฌ๋ฌ (์กฐ๊ฑด ์ถฉ์กฑ ์ ์ต๋ 7์ต ๋ฌ๋ฌ)๋ฅผ ์ง๊ธํ๊ธฐ๋ก ํ๋ค. ๋ณด์ยท์์กยทํํ ์์ค์ ํฉ์น ์ ์ฒด ๋น์ฉ์ 14์ต ๋ฌ๋ฌ ์ด์์ผ๋ก ์ถ์ ๋๋ค.
- ์ธ์ ยท์กฐ์ง ์ํฅ: CEO๊ฐ ์ํ์ ์ถ์ํด ์ฌ๊ณผํ๊ณ , CIOยทCSO๊ฐ ๋ฌผ๋ฌ๋ฌ๋ค. ์ฌ๊ณ ๊ณต๊ฐ ์ ์ฃผ์์ ๋งค๋ํ ์์์ ๋ด๋ถ์ ๊ฑฐ๋๋ก ๊ธฐ์๋๋ค. 2020๋ ๋ฏธ ๋ฒ๋ฌด๋ถ๋ ์ค๊ตญ ์ธ๋ฏผํด๋ฐฉ๊ตฐ 54์ฐ๊ตฌ์ ์์ 4๋ช ์ ์ด ์นจํด ๋ฑ์ ์ฐ๋ฃจ๋ผ ๊ธฐ์ํ๋ค.
- ์ฌํ์ ์ ๋ขฐ: '์ ์ฉ ์ ์๋ฅผ ๋งค๊ธฐ๋ ํ์ฌ'๊ฐ ์ ์ฉ์ ๋ณด๋ฅผ ์งํค์ง ๋ชปํ๋ค๋ ์ฌ์ค์ ๊ฐ์ธ์ ๋ณด ๋ณดํธ ๊ท์ (๊ฐ ์ฃผ ํ๋ผ์ด๋ฒ์๋ฒ, ์ดํ ์ฐ๋ฐฉ ๋ ผ์)๋ฅผ ์๋น๊ธฐ๋ ์ด๋งค๊ฐ ๋๋ค.
5. PoC (๊ฐ๋ ์ฆ๋ช ) โ ๊ต์ก์ฉ ๋ชจ์ ์ฌํ 4์ข
์๋ ์ฝ๋๋ ์ค์ ๊ณต๊ฒฉ ์ฝ๋๊ฐ ์๋๋ผ, ์์์ ์ค๋ช ํ ๋ฉ์ปค๋์ฆ์ ์์ ํ ๋ชจ์ ๊ฐ์ฒด๋ก ์ฌํํ ๊ฒ์ ๋๋ค. ์ค์ Struts ์ทจ์ฝ ์๋ฒยท์ค์ OGNL ์คํยท์ค์ ์น์ ธ์ ํฌํจํ์ง ์์ต๋๋ค. ๊ฒฉ๋ฆฌ๋ ๋ฉ ํ๊ฒฝ(์ธํฐ๋ท ์ฐจ๋จ VM)์์๋ง ์คํํ์ธ์.
PoC โ ํค๋ ๋ฌธ์์ด์ด 'ํ๊ฐ'๋๋ ์๊ฐ (OGNL ์ธ์ ์ ์ ํต์ฌ)
import re
# ์ค์ OGNL์ด ์๋๋ค. '๋ฌธ์์ด์ด ํ๊ฐ ๋์์ด ๋๋' ๊ตฌ์กฐ๋ง ๋ชจ์ํ๋ค.
class MockOgnl:
ALLOWED = re.compile(r'^[0-9+\-*/(). ]+$') # ์์ ํ ์ฐ์ ์๋ง ํ์ฉ
def getValue(self, expr):
if self.ALLOWED.match(expr):
return eval(expr, {'__builtins__': {}}, {}) # ๊ต์ก์ฉ ๋ชจ์, ์ค์ ์๋๋ฐ์ค ์๋
return "์ฐจ๋จ: ๋ฆฌํฐ๋ด(์ฐ์ ์) ์๋"
def build_error_message(content_type): # ์ทจ์ฝ ํจํด(๋ชจ์): ํค๋ ๊ฐ์ ์์ธ ๋ฉ์์ง์ '๊ทธ๋๋ก' ๋ถ์
return "the request was rejected because no multipart boundary was found (%s)" % content_type
def vulnerable_parse(content_type):
m = re.search(r'%\{(.+?)\}', content_type) # ํค๋์ %{...} ๊ฐ ์์ผ๋ฉด 'ํ๊ฐ' ์๋
return MockOgnl().getValue(m.group(1)) if m else None
print(vulnerable_parse("multipart/form-data; boundary=----abc")) # ์ ์ ์์ฒญ
print(vulnerable_parse("multipart/form-data boundary=%{7*6}")) # ํค๋์ ์์ด ๋ค์ด์จ ๊ฒฝ์ฐ
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
None
42
ํค๋ ๊ฐ์ด ๋ฌธ์์ด๋ก๋ง ์ฒ๋ฆฌ๋๋ฉด None์ด์ง๋ง, ํ๊ฐ๋๋ฉด 42๊ฐ
๋์จ๋ค. ์ค์ OGNL์ ์ฐ์ ์ ๋์ด ์์ ๋ฉ์๋ ํธ์ถ์ด ๊ฐ๋ฅํ๋ฏ๋ก, ์ด 'ํ๊ฐ'๊ฐ ๊ณง
์๊ฒฉ ์ฝ๋ ์คํ์ผ๋ก ์ด์ด์ง๋ค.
PoC โก ์ทจ์ฝ vs ํจ์น โ ํ์ ๋์ ๋น๊ต
VULN_MAX = "2.5.10" # 2.5.10 ์ดํ ์ทจ์ฝ, 2.5.10.1 ๋ถํฐ ํจ์น
def parse_multipart(content_type, version):
if "boundary=" not in content_type: # ํ์ฑ ์คํจ ๊ฒฝ๋ก
if version <= VULN_MAX:
return "์ทจ์ฝ: Content-Type ๊ฐ์ด ์์ธ ๋ฉ์์ง๋ก 'ํ๊ฐ'๋จ"
return "์์ : ์์ธ ๋ฉ์์ง ์ด์ค์ผ์ดํ(ํ๊ฐ ์ ํจ)"
return "์ ์ ํ์ฑ"
for v in ("2.3.31", "2.5.10", "2.5.10.1", "2.5.13"):
print(v, "โ", parse_multipart("multipart/form-data", v))
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
2.3.31 โ ์ทจ์ฝ: Content-Type ๊ฐ์ด ์์ธ ๋ฉ์์ง๋ก 'ํ๊ฐ'๋จ
2.5.10 โ ์ทจ์ฝ: Content-Type ๊ฐ์ด ์์ธ ๋ฉ์์ง๋ก 'ํ๊ฐ'๋จ
2.5.10.1 โ ์์ : ์์ธ ๋ฉ์์ง ์ด์ค์ผ์ดํ(ํ๊ฐ ์ ํจ)
2.5.13 โ ์์ : ์์ธ ๋ฉ์์ง ์ด์ค์ผ์ดํ(ํ๊ฐ ์ ํจ)
์์ ์ ๋ณธ์ง์ ๋จ ํ ์ค โ ์ฌ์ฉ์ ์ ๋ ฅ์ 'ํ๊ฐ'ํ์ง ์๊ณ '์ด์ค์ผ์ดํ'ํ๋ค. ๋ฐ์ดํฐ์ ์ฝ๋๋ฅผ ๋ถ๋ฆฌํ๋ ๊ฒ, ์ด๊ฒ์ด ์ธ์ ์ ๊ณ์ด ์ทจ์ฝ์ ์ ๋ณดํธ์ ์ฒ๋ฐฉ์ด๋ค.
PoC โข ์น์ ธ ์ ๋ก๋ ์ ์ ํ์ง (๋ฐฉ์ด ๊ด์ )
JSP_SHELL_SIGNS = [b"Runtime.getRuntime().exec", b"ProcessBuilder",
b"request.getParameter(", b"Class.forName"]
def scan_jsp(buf):
hits = [s.decode() for s in JSP_SHELL_SIGNS if s in buf]
return ("โ ์น์
ธ ์์ฌ: " + ",".join(hits)) if hits else "์ ์"
files = {
"invoice.jsp": b"<html>hello</html>",
"logo.png.jsp": b'<%@ page import="java.util.*" %>' + b'<% Runtime.getRuntime().exec(request.getParameter("c")); %>',
}
for n, b in files.items():
print(n, "โ", scan_jsp(b))
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
invoice.jsp โ ์ ์
logo.png.jsp โ โ ์น์
ธ ์์ฌ: Runtime.getRuntime().exec,request.getParameter(
์ฃผ๋ชฉํ ์ ํธ๋ ๋ ๊ฐ์ง๋ค. (1) ์ด์ค ํ์ฅ์ logo.png.jsp โ '์ด๋ฏธ์ง์ธ ์ฒ
ํ๋ ์คํฌ๋ฆฝํธ'. (2) ํ๋ก์ธ์ค ์คํยท์์ฒญ ํ๋ผ๋ฏธํฐ API์ ๋ฑ์ฅ. ์
๋ก๋ ๋๋ ํฐ๋ฆฌ์
์คํ ๊ฐ๋ฅ ํ์ฅ์๊ฐ ๋จ์ด์ง์ง ์๊ฒ ํ๊ณ , ์
๋ก๋ ํ์ผ์ ์ ์ ์ผ๋ก ๊ฒ์ฌํ๋ ๊ฒ์ด 2์ฐจ ๋ฐฉ์ด์ ์ด๋ค.
PoC โฃ ๋ก๊ทธ์์ OGNL ํ์ด๋ก๋ ํ์ง (ํ์ง ๋ฃฐ ๋ชจ์)
def log_anomaly(content_type):
bad = any(t in content_type for t in ("%{", "#dm=", "ognl.", "@java.lang"))
return "๐จ OGNL ํ์ด๋ก๋ ์์ฌ" if bad else "์ ์"
for ct in ["multipart/form-data; boundary=----x",
"multipart/form-data; boundary=x%{(#_='multipart/form-data')}"]:
print(log_anomaly(ct), "|", ct[:48])
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
์ ์ | multipart/form-data; boundary=----x
๐จ OGNL ํ์ด๋ก๋ ์์ฌ | multipart/form-data; boundary=x%{(#_='multipart/
์ ์ ์์ฒญ์ Content-Type์๋ %{ยทognl. ๊ฐ์ ํ ํฐ์ด ์๋ค.
WAFยท์น์๋ฒ ๋ก๊ทธ์ ์ด๋ฐ ํจํด์ด ๋ณด์ด๋ฉด '๋ฌด์ธ๊ฐ๋ฅผ ํ๊ฐํ๋ ค๋ ์๋'๋ค. ๊ท์น ํ๋๋ก๋
๋๋ ์ค์บ๋์ ๋ฌผ๊ฒฐ์ 1์ฐจ๋ก ๊ฑธ๋ฌ๋ผ ์ ์๋ค โ Equifax๊ฐ ๋์น ๊ฒ์ ์ด ๋จ์ํ ์ด์ ์ ํธ์๋ค.
6. ๋ฐฉ์ด ๋ฐฉ๋ฒ/๊ตํ โ ๋ฌด์์ ๋ฐฐ์ฐ๋
- ํจ์น๋ '๊ฒฐ์ '์ด ์๋๋ผ '์๋ฃ'๋ก ๊ด๋ฆฌํ๋ผ(์ต์ฐ์ ): Equifax๋ ํจ์น๋ฅผ ์ง์ํ๋ ๋ด๋ถ ๋ฉ์ผ๊น์ง ์ฃผ๊ณ ๋ฐ์์ง๋ง, ์ค์ ์ ์ฉ์ ์นจํด๋ฅผ ๋ฐ๊ฒฌํ ๋ค์ ๋ (7์ 30์ผ)์ด์๋ค. ํจ์น SLA ยท ์์ฐ ์ธ๋ฒคํ ๋ฆฌ ยท ์์ธ ์น์ธ ์ ์ฐจ๊ฐ ์์ผ๋ฉด '์ง์'๋ '์๋ฃ'๊ฐ ๋์ง ์๋๋ค.
- ์ธํฐ๋ท ๋ ธ์ถ ์์ฐ์ ์ต์ฐ์ ์ผ๋ก ํจ์นํ๋ผ: ๊ฒฝ๊ณ์ ์ด๋ฆฐ ์น์ฑ์ ๊ณต๊ฒฉ์์๊ฒ ์ ๋ฉด๋ฌธ์ด๋ค. ์ธ๋ถ ๋ ธ์ถ ์์ฐ์ ์ทจ์ฝ์ ์ ๋ด๋ถ ์์ฐ๋ณด๋ค ํจ์ฌ ์งง์ SLA๋ก ์ฒ๋ฆฌํ๋ค.
- ํ์ง ์ธํ๋ผ๋ฅผ '์ด์ ์๊ฒ' ์ ์งํ๋ผ: ๋ง๋ฃ๋ ์ค์บ๋ ์ธ์ฆ์, ๋ฐฉ์น๋ ์์ฐ ๋ชฉ๋ก, ๊บผ์ง ๊ฒฝ๋ณด๋ ์๋ ๊ฒ๊ณผ ๊ฐ๋ค. ๋ณด์ ๋๊ตฌ๋ ์ฌ๋ ๊ฒ์ด ์๋๋ผ ์ด์ํ๋ ๊ฒ์ด๋ค.
- ์น์๋ฒ๋ฅผ ์ ๋ขฐ ๊ฒฝ๊ณ๋ก ๋์ง ๋ง๋ผ(๋คํธ์ํฌ ๋ถ๋ฆฌ): DMZ์ ์น์๋ฒ๊ฐ ๋ด๋ถ DB์ 51๊ฐ์ ์ง์ ๋ฟ์ผ๋ฉด, ํ ๋๊ฐ ๋ซ๋ฆด ๋ ๋ด๋ถ ์ ์ฒด๊ฐ ๋ฌด๋์ง๋ค. ์ต์ ๊ถํ ยท ์ธ๊ทธ๋ฉํ ์ด์ ์ผ๋ก ํก์ ์ด๋์ ๋ง๋๋ค.
- ๋๋ ์ ์ถ์ '๋๋ฆฐ ์ ์ถ'๋ก ํ์งํ๋ผ: 76์ผ๊ฐ ์ ์ ์กฐํ์ ์์ฌ ๋๊ฐ ๋ฐ์ดํฐ๋ ์ ์ ๋ฃฐ๋ก ์ ์กํ๋ค. ์ฌ์ฉ์ยท์๊ฐ๋ยท๋ณผ๋ฅจ ๊ธฐ๋ฐ ์ด์ํ์ง(UEBA)๊ฐ ํ์ํ๋ค.
- ์ ๋ก๋ = ์คํ ํ๋ฉด์ผ๋ก ์ทจ๊ธํ๋ผ: ์น์ ธ์ '์ ๋ก๋ ์ทจ์ฝ์ '์ด ์๋๋ผ '์ค๊ณ ๋ฌธ์ '๋ค. ์ ๋ก๋ ๋๋ ํฐ๋ฆฌ์์ ์คํฌ๋ฆฝํธ ์คํ์ ๊ธ์งํ๊ณ , ํ์ผ์ ์ ์ ์ผ๋ก ๊ฒ์ฌํ๋ค.
- ์คํ์์ค ์์กด์ฑ์ '๋ด ์ฝ๋'์ฒ๋ผ ์ถ์ ํ๋ผ(SBOM): Struts๋ ์ง์ ์ง ์ฝ๋๊ฐ ์๋์ง๋ง, ์ทจ์ฝ์ ์ด ๋์ค๋ฉด ์ฑ ์์ ๋ด ์๋น์ค์ ๋จ๋๋ค. ์ปดํฌ๋ํธ ๋ชฉ๋ก(SBOM)๊ณผ ์ทจ์ฝ์ ๋ชจ๋ํฐ๋ง์ด ํ์๋ค.
- ์ฌ๊ณ ๋์ยทํฌ๋ช ์ฑ๋ ํต์ ๋์์ด๋ค: ๊ณต๊ฐ ์ ๋ด๋ถ์ ๊ฑฐ๋, ๋์ฅ ๊ณต์ง, ์์ก ํฌ๊ธฐ ์ฝ๊ด์ ๊ธฐ์ ์ ํผํด๋ณด๋ค ํฐ ์ ๋ขฐ์ ๋ถ๊ดด๋ฅผ ๋ณ์๋ค. ๋์ ๊ณํ์๋ ๋ฒ๋ฌดยทํ๋ณดยท์ค๋ฆฌ๊ฐ ํฌํจ๋ผ์ผ ํ๋ค.
7. ์ถ์ฒ
- U.S. House Committee on Oversight and Government Reform โ The Equifax Data Breach (2018-12, Majority Staff Report; ๋ฏธ์ ์ฉ ํจ์นยท๋ง๋ฃ ์ธ์ฆ์ยท76์ผ ์นจํด ํ์๋ผ์ธ) โ oversight.house.gov (Equifax Report, PDF)
- Federal Trade Commission โ Equifax to Pay $575 Million as Part of Settlement (2019-07-22 ๋ณด๋์๋ฃ; FTCยทCFPBยท50๊ฐ์ฃผ ํฉ์) โ ftc.gov
- U.S. Department of Justice โ Chinese Military Personnel Charged with Computer Fraud, Economic Espionage and Wire Fraud (2020-02-10; PLA 54์ฐ๊ตฌ์ 4๋ช ๊ธฐ์) โ justice.gov
- Apache Software Foundation โ S2-045: RCE when using Jakarta Multipart parser (CVE-2017-5638) (2017-03-06 ๋ณด์ ๊ณต์งยท์ํฅ ๋ฒ์ ยท์์ ํ) โ cwiki.apache.org (S2-045)
- NIST NVD โ CVE-2017-5638 (CVSS 3.0 10.0, ๊ณต๊ฒฉ ๋ฒกํฐ ๊ธฐ์ ) โ nvd.nist.gov/vuln/detail/CVE-2017-5638
- SEC โ SEC Charges Former Equifax Chief Information Officer With Insider Trading (2020-06; ๋ด๋ถ์ ๊ฑฐ๋ ๊ธฐ์) โ sec.gov
- Wikipedia โ 2017 Equifax data breach (๊ท๋ชจยทํ์๋ผ์ธยทํฉ์ยทํ์ ์กฐ์น ๊ฐ์) โ en.wikipedia.org/wiki/2017_Equifax_data_breach
โป ๋ณธ๋ฌธ์ PoC ์ฝ๋๋ ๊ตฌ์กฐ ์ดํด ๋ชฉ์ ์ ๊ต์ก์ฉ ๋ชจ์ ์ฝ๋์ด๋ฉฐ, ์ค์ ์ทจ์ฝํ Struts ์๋ฒยท์ค์ OGNL ํ์ด๋ก๋ยท์ค์ ์น์ ธยท์ค์ ๋ฐ์ดํฐ ์ ์ถ ์ ์ฐจ๋ ํฌํจํ์ง ์์ต๋๋ค. ๋ชจ๋ ์ค์ต์ ์ธํฐ๋ท๊ณผ ๋ถ๋ฆฌ๋ ๊ฒฉ๋ฆฌ ๋ฉ VM์ ์ ์ ๋ก ํ๋ฉฐ, ์ค์ ์์คํ ยทํ์ธ ์์ฐ์ ๋ํ ์คํ์ ๋ถ๋ฒ์ ๋๋ค.
ํจ๊ป ์ฝ๊ธฐ: ๐ Log4Shell (2021) โ ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ ์ค์ด ๊ณต๊ธ๋ง์ ํ๋ ์ ๋ก๋ฐ์ด ์ฒด์ธ ยท ๐ฆ SolarWinds SUNBURST (2020) โ ๊ณต๊ธ๋ง์ ๋ฌด๊ธฐ๋ก ์ ๋ขฐ๋ฅผ ๋ซ์ ๋ฐฑ๋์ด ยท Malware Deep-Dive: MOVEit / Cl0p (CVE-2023-34362) โ ์น ์ทจ์ฝ์ ํ๋๋ก 2,700๊ฐ ์กฐ์ง ๋๋ ์ ์ถ ยท ๐ฅ Code Red (2001) โ '์์ฒญ ํ๋'๊ฐ ์๋ฒ๋ฅผ ์ผํจ ๋๋ ๊ฐ์ผ