๐Ÿ’ณ Equifax ๋Œ€์œ ์ถœ (2017) โ€” 'ํŒจ์น˜ ํ•œ ์ค„'์ด 1์–ต 4,700๋งŒ ๋ช…์˜ ์‹ ์šฉ์ •๋ณด๋ฅผ ๋„˜๊ฒผ๋‹ค

2026-10-06 ยท Malware Deep-Dive (Attack Chain / CVE-2017-5638 / Web Exploit โ†’ Webshell โ†’ Lateral Movement)
๊ธฐ์ˆ  ์ •๋ณด
  • ์ฒด์ธ๋ช…: 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) ํฌํ„ธ์€ ์ด ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๋งŒ๋“ค์–ด์ ธ ์žˆ์—ˆ๊ณ  โ€” ํŒจ์น˜๋Š” ์ ์šฉ๋˜์ง€ ์•Š์•˜๋‹ค.

2. ์นจํ•ดยท์ „ํŒŒ ๊ฒฝ๋กœ โ€” 4๋‹จ๊ณ„ ๊ณต๊ฒฉ ์ฒด์ธ

Equifax ์‚ฌ๊ณ ๋Š” '์ทจ์•ฝ์  ํ•˜๋‚˜'๊ฐ€ ์•„๋‹ˆ๋ผ ๊ณต๊ฒฉ ์ฒด์ธ(attack chain)์ด๋‹ค. ๊ฐ ๋‹จ๊ณ„๋Š” ์ด์ „ ๋‹จ๊ณ„ ์—†์ด๋Š” ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์•˜๊ณ , ์–ด๋А ํ•œ ๊ณ ๋ฆฌ์—์„œ๋„ ๋ง‰์„ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

ํ•ต์‹ฌ ๊ตํ›ˆ์€ '์›น์„œ๋ฒ„ = ์‹ ๋ขฐ ๊ฒฝ๊ณ„'๋ผ๋Š” ์ฐฉ๊ฐ์ด๋‹ค. ๊ณต๊ฒฉ์ž๋Š” ์ธํ„ฐ๋„ท์— ์—ด๋ฆฐ ๋‚ก์€ ์›น์•ฑ์„ ๋ฐœํŒ ์‚ผ์•„ ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊นŒ์ง€ ๊ฑธ์–ด ๋“ค์–ด๊ฐ”๋‹ค. ์ง„์ž…์ ์€ ํ•˜๋‚˜์˜€๊ณ , ๋ฐฉ์–ด์„ ์€ ์—ฌ๋Ÿฌ ๊ฐœ์˜€๋Š”๋ฐ, ์ „๋ถ€ ๋šซ๋ ธ๋‹ค.

3. ๋™์ž‘ ์›๋ฆฌ ์ƒ์„ธ ๋ถ„์„ โ€” CVE-2017-5638, ์™œ 10.0์ธ๊ฐ€

๊ฒฐํ•จ์€ Struts 2์˜ Jakarta Multipart ํŒŒ์„œ(JakartaMultiPartRequest)์— ์žˆ์—ˆ๋‹ค. ์›น ์š”์ฒญ์ด ๋ฉ€ํ‹ฐํŒŒํŠธ(multipart/form-data, ํŒŒ์ผ ์—…๋กœ๋“œ ํ˜•์‹)์ธ๋ฐ ํŒŒ์‹ฑ์— ์‹คํŒจํ•˜๋ฉด, ํŒŒ์„œ๋Š” ์˜ˆ์™ธ ๋ฉ”์‹œ์ง€๋ฅผ ๋งŒ๋“ ๋‹ค. ๋ฌธ์ œ๋Š” ์ด๋•Œ ์—๋Ÿฌ ๋ฉ”์‹œ์ง€์— Content-Type ํ—ค๋” ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋ผ์›Œ ๋„ฃ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ LocalizedTextUtil.findText()๊ฐ€ OGNL๋กœ ํ‰๊ฐ€ํ–ˆ๋‹ค๋Š” ์ ์ด๋‹ค.

ํ•ญ๋ชฉ๋‚ด์šฉ
์ทจ์•ฝ์  ์œ ํ˜•OGNL ์ธ์ ์…˜ โ†’ ์›๊ฒฉ ์ฝ”๋“œ ์‹คํ–‰(RCE)
๊ณต๊ฒฉ ๋ฒกํ„ฐHTTP Content-Type ํ—ค๋” (๋ฉ€ํ‹ฐํŒŒํŠธ ํŒŒ์‹ฑ ์‹คํŒจ ๊ฒฝ๋กœ)
์ธ์ฆ ํ•„์š”?๋ถˆํ•„์š” (๋ฏธ์ธ์ฆ ์›๊ฒฉ)
๊ณต๊ฐœ โ†’ ์•…์šฉ๊ณต๊ฐœ(3/6) ์งํ›„ ์ˆ˜ ์‹œ๊ฐ„ ๋‚ด ์•…์šฉ ๊ด€์ธก, ์ „ ์„ธ๊ณ„ ์Šค์บ๋‹ ํญ์ฆ
ํŒจ์น˜Struts 2.3.32 / 2.5.10.1 (2017-03-06)
์—ฐ์‡„ ์ทจ์•ฝ์ ๊ฐ™์€ ํ•ด CVE-2017-9805(REST ํ”Œ๋Ÿฌ๊ทธ์ธ XStream ์—ญ์ง๋ ฌํ™”) ๋“ฑ Struts ๊ฒฐํ•จ ์ž‡๋”ฐ๋ผ ๊ณต๊ฐœ
โš ๏ธ ์•ˆ์ „ ๊ทœ์น™: ์ด ๊ธ€์˜ PoC๋Š” ๋™์ž‘ ์›๋ฆฌ ์žฌํ˜„์šฉ ๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์ทจ์•ฝํ•œ Struts ์„œ๋ฒ„๋ฅผ ๊ฒจ๋ƒฅํ•œ ์ต์Šคํ”Œ๋กœ์ž‡ยท์‹ค์ œ ์›น์…ธยท์‹ค์ œ ์œ ์ถœ ์ ˆ์ฐจ๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹ค์Šต์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฒฉ๋ฆฌ ๋žฉ VM์„ ์ „์ œ๋กœ ํ•˜๋ฉฐ, ํƒ€์ธ ์‹œ์Šคํ…œ์— ๋Œ€ํ•œ ์‹คํ–‰์€ ๋ถˆ๋ฒ•์ž…๋‹ˆ๋‹ค.

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. ๋ฐฉ์–ด ๋ฐฉ๋ฒ•/๊ตํ›ˆ โ€” ๋ฌด์—‡์„ ๋ฐฐ์šฐ๋‚˜

7. ์ถœ์ฒ˜

โ€ป ๋ณธ๋ฌธ์˜ PoC ์ฝ”๋“œ๋Š” ๊ตฌ์กฐ ์ดํ•ด ๋ชฉ์ ์˜ ๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ์ด๋ฉฐ, ์‹ค์ œ ์ทจ์•ฝํ•œ Struts ์„œ๋ฒ„ยท์‹ค์ œ OGNL ํŽ˜์ด๋กœ๋“œยท์‹ค์ œ ์›น์…ธยท์‹ค์ œ ๋ฐ์ดํ„ฐ ์œ ์ถœ ์ ˆ์ฐจ๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹ค์Šต์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฒฉ๋ฆฌ ๋žฉ VM์„ ์ „์ œ๋กœ ํ•˜๋ฉฐ, ์‹ค์ œ ์‹œ์Šคํ…œยทํƒ€์ธ ์ž์‚ฐ์— ๋Œ€ํ•œ ์‹คํ–‰์€ ๋ถˆ๋ฒ•์ž…๋‹ˆ๋‹ค.

ํ•จ๊ป˜ ์ฝ๊ธฐ: ๐ŸŒ Log4Shell (2021) โ€” ์˜คํ”ˆ์†Œ์Šค ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ•œ ์ค„์ด ๊ณต๊ธ‰๋ง์„ ํ”๋“  ์ œ๋กœ๋ฐ์ด ์ฒด์ธ ยท ๐Ÿฆ… SolarWinds SUNBURST (2020) โ€” ๊ณต๊ธ‰๋ง์„ ๋ฌด๊ธฐ๋กœ ์‹ ๋ขฐ๋ฅผ ๋šซ์€ ๋ฐฑ๋„์–ด ยท Malware Deep-Dive: MOVEit / Cl0p (CVE-2023-34362) โ€” ์›น ์ทจ์•ฝ์  ํ•˜๋‚˜๋กœ 2,700๊ฐœ ์กฐ์ง ๋Œ€๋Ÿ‰ ์œ ์ถœ ยท ๐ŸŸฅ Code Red (2001) โ€” '์š”์ฒญ ํ•˜๋‚˜'๊ฐ€ ์„œ๋ฒ„๋ฅผ ์‚ผํ‚จ ๋Œ€๋Ÿ‰ ๊ฐ์—ผ

๋ชฉ๋ก์œผ๋กœ ๋Œ์•„๊ฐ€๊ธฐ