๐Ÿ—‚๏ธ MOVEit/Cl0p (2023) โ€” 'ํŒŒ์ผ์ „์†ก ์–ดํ”Œ๋ผ์ด์–ธ์Šค' ํ•˜๋‚˜๋กœ 2,700๊ฐœ ์กฐ์ง์ด ๋šซ๋ฆฐ ๋Œ€๋Ÿ‰ ์œ ์ถœ ์ฒด์ธ

2026-09-26 ยท Malware Deep-Dive (Attack Chain)

1. ๋ฐฐ๊ฒฝ/์—ญ์‚ฌ โ€” ๋ฉ”๋ชจ๋ฆฌ์–ผ๋ฐ์ด ์ฃผ๋ง์— ์‹œ์ž‘๋œ 'ํ˜‘๋ฐ•ํ˜• ๋Œ€๋Ÿ‰ ์œ ์ถœ'

MOVEit Transfer๋Š” ๊ธฐ์—…ยท์ •๋ถ€ยท๊ธˆ์œต๊ธฐ๊ด€์ด ๋Œ€์šฉ๋Ÿ‰ ๋ฏผ๊ฐ ํŒŒ์ผ์„ ์ฃผ๊ณ ๋ฐ›๋Š” ๊ด€๋ฆฌํ˜• ํŒŒ์ผ์ „์†ก(MFT, Managed File Transfer) ์ œํ’ˆ์ด๋‹ค. 2002๋…„ Standard Networks๊ฐ€ ์ถœ์‹œํ–ˆ๊ณ  2008๋…„ Ipswitch๊ฐ€ ์ธ์ˆ˜, ์ง€๊ธˆ์€ Progress Software๊ฐ€ ์œ ์ง€๋ณด์ˆ˜ํ•œ๋‹ค. ํŠน์ง•์€ ํ™”๋ คํ•จ์ด ์•„๋‹ˆ๋ผ ์œ„์น˜๋‹ค โ€” ๊ธ‰์—ฌ๋ช…์„ธยท์˜๋ฃŒ๊ธฐ๋กยท์„ธ๊ธˆ์ž๋ฃŒยทDMV ๋ฐ์ดํ„ฐ์ฒ˜๋Ÿผ "์กฐ์ง์— ๋“ค์–ด์˜จ ๋ฏผ๊ฐ ์ •๋ณด๊ฐ€ ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ชจ์—ฌ ๋‚˜๊ฐ€๋Š” ์ง€์ "์— ๋†“์ธ๋‹ค. ๊ทธ๋ž˜์„œ MFT ํ•œ ๋Œ€๊ฐ€ ๋šซ๋ฆฌ๋ฉด ๊ทธ ๋’ค์— ์—ฐ๊ฒฐ๋œ ์ˆ˜๋ฐฑ ๊ฐœ ๊ณ ๊ฐ ์กฐ์ง์ด ํ•จ๊ป˜ ๋šซ๋ฆฐ๋‹ค.

2023๋…„ 5์›” 27์ผ, ๋ฏธ๊ตญ ๋ฉ”๋ชจ๋ฆฌ์–ผ๋ฐ์ด ์—ฐํœด์˜ ์ฒซ ์ฃผ๋ง์— ๋Ÿฌ์‹œ์•„์–ด๊ถŒ ๋ฒ”์ฃ„์กฐ์ง Cl0p(TA505 / FIN11)์ด MOVEit Transfer์˜ ๋ฌด์ œ๋กœ๋ฐ์ด SQL ์ธ์ ์…˜(CVE-2023-34362)์„ ํ‘œ์ ํ˜•์ด ์•„๋‹ˆ๋ผ ์Šค์บ”ํ˜•์œผ๋กœ ๋Œ€๋Ÿ‰ ์ต์Šคํ”Œ๋กœ์ž‡ํ•˜๊ธฐ ์‹œ์ž‘ํ–ˆ๋‹ค. 5์›” 28์ผ ํ•œ ๊ณ ๊ฐ์ด ์ด์ƒ ์ง•ํ›„๋ฅผ ์‹ ๊ณ ํ•ด ์กฐ์‚ฌ๊ฐ€ ์‹œ์ž‘๋๊ณ , Progress๋Š” 5์›” 31์ผ ํŒจ์น˜๋ฅผ ๋ฐฐํฌํ–ˆ์œผ๋ฉฐ, CISA๋Š” 6์›” 2์ผ ์ด ์ทจ์•ฝ์ ์„ KEV(์‹ค์ œ ์•…์šฉ๋œ ์ทจ์•ฝ์ ) ์นดํƒˆ๋กœ๊ทธ์— ์˜ฌ๋ ธ๋‹ค. 6์›” 7์ผ์—๋Š” FBIยทCISA๊ฐ€ ๊ณต๋™ ๊ถŒ๊ณ  AA23-158A๋ฅผ ๋ฐœํ‘œํ•˜๋ฉฐ "์ด๋ฏธ ๋‹ค์ˆ˜ ์กฐ์ง์ด ์นจํ•ด๋‹นํ–ˆ๋‹ค"๊ณ  ๊ฒฝ๊ณ ํ–ˆ๋‹ค.

์ด ์‚ฌ๊ฑด์ด ๋ณด์•ˆ์‚ฌ์˜ ๊ธฐ์–ต์— ๋‚จ๋Š” ์ด์œ ๋Š” ์„ธ ๊ฐ€์ง€๋‹ค.

  • ์‚ฌ๊ฑด๋ช…: MOVEit Transfer ๋Œ€๋Ÿ‰ ์ต์Šคํ”Œ๋กœ์ž‡ / Cl0p ๋ฐ์ดํ„ฐ ๊ฐˆ์ทจ ์บ ํŽ˜์ธ (2023)
  • ์ทจ์•ฝ์ : CVE-2023-34362 โ€” MOVEit Transfer ๋ฌด์ธ์ฆ SQL ์ธ์ ์…˜ (์น˜๋ช…์ , CVSS 9.8) ยท ํ›„์† CVE-2023-35036(6์›” 9์ผ), CVE-2023-35708(6์›” ์ค‘์ˆœ) ์—ฐ์ด์–ด ์ˆ˜์ •
  • ์•…์„ฑ์ฝ”๋“œ: ์›น์…ธ LEMURLOOT (human2.aspx โ€” ์ •์ƒ human.aspx ์œ„์žฅ, C# ASP.NET)
  • ๊ณต๊ฒฉ์ž: Cl0p / Clop โ€” TA505ยทFIN11๋กœ ์ถ”์ ๋˜๋Š” ๋Ÿฌ์‹œ์•„์–ด๊ถŒ ๋ฒ”์ฃ„์กฐ์ง, CISA/FBI ํ™•์ธ
  • ๊ธฐ๊ฐ„: ์ต์Šคํ”Œ๋กœ์ž‡ ์‹œ์ž‘ 2023-05-27 (Mandiant ์ถ”์ •) ยท ํŒจ์น˜ 05-31 ยท KEV ๋“ฑ์žฌ 06-02 ยท ๊ณต๋™ ๊ถŒ๊ณ  06-07
  • ํ”ผํ•ด: 2,500๊ฐœ+ ์กฐ์ง(2023-10 Emsisoft ์ง‘๊ณ„) โ†’ ์ดํ›„ 2,700๊ฐœ+ ยท ์•ฝ 9,300๋งŒ ๋ช… (์ง‘๊ณ„ ๋ˆ„์ ) ยท ๋ฏธ๊ตญ ์†Œ์žฌ ์กฐ์ง ๋น„์ค‘ 80% ์ด์ƒ
  • ์ˆ˜์ต ์ถ”์ •: Cl0p๊ฐ€ MOVEit ์บ ํŽ˜์ธ์œผ๋กœ 7,500๋งŒ~1์–ต ๋‹ฌ๋Ÿฌ (2023-07 ์ถ”์ •)
  • ์˜ํ–ฅ ๋ฒ„์ „: MOVEit Transfer 2023.0.x / 2022.1.x / 2022.0.x / 2021.1.x / 2021.0.x / 2020.1.x / 2020.0.x (๊ณต๊ฐœ ๋‹น์‹œ ์ „ ๋ธŒ๋žœ์น˜)
  • ๋Œ€์‘: Progress ๊ธด๊ธ‰ ํŒจ์น˜(05-31) ยท CISA KEV ๊ฐ•์ œ ์กฐ์น˜ ยท CISA/FBI/NSA ๊ณต๋™ ๊ถŒ๊ณ  ยท ์—…๊ณ„๋Š” ์‹ ์†ํ•œ ๋ฒค๋” ์†Œํ†ต์„ ๊ธ์ • ํ‰๊ฐ€

2. ๊ฐ์—ผยท์ „ํŒŒ ๊ฒฝ๋กœ โ€” ํ”ผ์‹ฑ์ด ์•„๋‹ˆ๋ผ '๋…ธ์ถœ๋ฉด ์Šค์บ”'

EmotetยทQakbot๋ฅ˜ ๋กœ๋”๋Š” ๋ฉ”์ผ๋กœ ์‚ฌ๋žŒ์„ ๋‚š๋Š”๋‹ค. MOVEit ์บ ํŽ˜์ธ์€ ์ •๋ฐ˜๋Œ€๋‹ค. ์‚ฌ๋žŒ์ด ๊ฐœ์ž…ํ•  ์ง€์ ์ด ์ฒ˜์Œ๋ถ€ํ„ฐ ์—†์—ˆ๋‹ค.

์ฃผ๋ชฉํ•  ์ ์€ ํƒ€์ด๋ฐ์ด๋‹ค. Accellion FTA๋Š” 2020๋…„ 12์›” 23์ผ(์—ฐ๋ง), MOVEit์€ ๋ฉ”๋ชจ๋ฆฌ์–ผ๋ฐ์ด ์—ฐํœด โ€” Cl0p๋Š” ๋‹ด๋‹น์ž๊ฐ€ ํœด๊ฐ€ ์ค‘์ธ ๊ธฐ๊ฐ„์„ ๋…ธ๋ฆฌ๋Š” ์Šต๊ด€์ด ์žˆ๋‹ค. ํŒจ์น˜๊ฐ€ ๋‚˜์™€๋„ ์‹ค์ œ ์ ์šฉ๊นŒ์ง€์˜ ์ธ๊ฐ„ ์‹œ๊ฐ„์ด ๊ณง ๊ณต๊ฒฉ ์ฐฝ(Window)์ด์—ˆ๋‹ค.

3. ๋™์ž‘ ์›๋ฆฌ ์ƒ์„ธ โ€” '์ •์ƒ ๊ธฐ๋Šฅ'๋งŒ์œผ๋กœ RCE๊นŒ์ง€ ๊ฐ€๋Š” 8๋‹จ๊ณ„

๋ณด์•ˆ์—…์ฒด Horizon3.ai๊ฐ€ ํŒจ์น˜ ์ „ํ›„ ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๋น„๊ตํ•ด ๊ณต๊ฐœํ•œ ์žฌํ˜„ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ€์žฅ ์ดํ•ด๊ฐ€ ๋น ๋ฅด๋‹ค. ์ธ์ƒ์ ์ธ ๊ฑด ์ƒˆ๋กœ์šด ๋ฌด๊ธฐ๊ฐ€ ๊ฑฐ์˜ ์—†๋‹ค๋Š” ์ ์ด๋‹ค โ€” ์ทจ์•ฝํ•œ ์ฝ”๋“œ ํ•œ ๊ณณ + ์ œํ’ˆ์ด ์›๋ž˜ ์ œ๊ณตํ•˜๋˜ ์ •์ƒ ๊ธฐ๋Šฅ๋“ค์„ ์ด์–ด ๋ถ™์—ฌ RCE๊นŒ์ง€ ๋„๋‹ฌํ•œ๋‹ค.

๋‹จ๊ณ„์•…์šฉํ•œ ์ง€์ ์„ฑ๊ฒฉ
1moveitisapi.dll?action=m2 โ†’ machine2.aspx ์ค‘๊ณ„ํ—ค๋” ์ ‘๋ฏธ์‚ฌ ๋งค์นญ ๋ฒ„๊ทธ๋กœ ๊ฒŒ์ดํŠธ ์šฐํšŒ
2session_setvars ๊ฑฐ๋ž˜ / X-siLock-SessVar*์„ธ์…˜ ๋ณ€์ˆ˜ ์ž„์˜ ์„ค์ •(์ •์ƒ ๊ธฐ๋Šฅ)
3guestaccess.aspx (secmsgpost)๋น„๋กœ๊ทธ์ธ ๊ฒฝ๋กœ๋กœ SQLi ๋„๋‹ฌ
4MOVEit DB ์ฝ๊ธฐยท์“ฐ๊ธฐ๊ด€๋ฆฌ์ž ๊ณ„์ • ์ƒ์„ฑ, IP ์ œํ•œ ํ•ด์ œ, ์—ฐํ•ฉ์ธ์ฆ ์„ค์ • ๋ณ€๊ฒฝ
5/api/v1/auth/token + session_grant=external_token๊ณต๊ฒฉ์ž ์ธ์ฆ์„œ๋กœ ์„œ๋ช…ํ•œ JWT๋กœ sysadmin ํ† ํฐ ํš๋“
6/api/v1/folders/<id>/files?uploadType=resumable์ •์ƒ ํŒŒ์ผ ์—…๋กœ๋“œ API๋กœ ์›น์…ธ ๋ฐฐ์น˜
7์—…๋กœ๋“œ ์žฌ๊ฐœ ์ƒํƒœ(uploadState) โ†’ BinaryFormatter.Deserialize().NET ์—ญ์ง๋ ฌํ™” RCE
8LEMURLOOT(human2.aspx) ์ƒ์ฃผDB์— ๋‚จ๋Š” ๊ณต๊ฒฉ์ž ๊ณ„์ • = ์ง€์†์„ฑ

4. ํ”ผํ•ด ๊ทœ๋ชจ/์˜ํ–ฅ โ€” ์ˆซ์ž๋กœ ๋ณด๋Š” 2023

5. PoC โ€” ๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ 4์ข… (๋กœ์ปฌ ์‹ค์Šต ์ „์ œ)

โš ๏ธ ์•ˆ์ „ ๊ทœ์น™: ์•„๋ž˜ ์ฝ”๋“œ๋Š” ์‹ค์ œ MOVEit Transfer ์ฝ”๋“œยท์ต์Šคํ”Œ๋กœ์ž‡ยท์›น์…ธ์ด ์•„๋‹ˆ๋‹ค. ์›๋ฆฌ๋งŒ ์žฌํ˜„ํ•œ ํ•ฉ์„ฑ(synthetic) ๋ชจ์˜ ์ฝ”๋“œ์ด๋ฉฐ, ์ „๋ถ€ ๋กœ์ปฌ ๋ฉ”๋ชจ๋ฆฌ DB(sqlite :memory:)์™€ ํ•ฉ์„ฑ ๋กœ๊ทธ/๋ฌธ์ž์—ด ์œ„์—์„œ๋งŒ ๋™์ž‘ํ•œ๋‹ค. ๋„คํŠธ์›Œํฌ ํ†ต์‹ ยท์‹ค์ œ DB ์ ‘์†ยทํŒŒ์ผ ์—…๋กœ๋“œยท๋ช…๋ น ์‹คํ–‰ ๊ธฐ๋Šฅ์€ ์—†๊ณ , ๊ณต๊ฒฉ ๋Œ€์ƒ์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฐ€์ƒ์˜ ๋กœ์ปฌ ์‹ค์Šต ๋žฉ์„ ์ „์ œ๋กœ ํ•œ๋‹ค. ์‹ค์ œ ์ œํ’ˆ์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ ์ทจ์•ฝ์  ํŠธ๋ฆฌ๊ฑฐ๋Š” ์‹œํ—˜ํ•˜์ง€ ์•Š๋Š”๋‹ค.

PoC โ‘  โ€” ๋ฌด์ธ์ฆ ์ž…๋ ฅ์ด ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ์œผ๋กœ ๋“ค์–ด๊ฐˆ ๋•Œ

3์žฅ์˜ AND Email='<EmailAddress>' ๊ฒฐํ•ฉ ์ง€์ ์„ ๋กœ์ปฌ ๋žฉ์œผ๋กœ ์˜ฎ๊ธด ๊ฒƒ์ด๋‹ค. ๊ฐ™์€ ์ž…๋ ฅ์„ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฐ”์ธ๋”ฉ์œผ๋กœ ๋„˜๊ธฐ๋ฉด ๊ฒฝ๊ณ„๊ฐ€ ์œ ์ง€๋˜๋Š” ๊ฒƒ๊นŒ์ง€ ๋Œ€์กฐํ•œ๋‹ค.

# poc1_moveit_lab.py โ€” ๋ฌดํ•ดํ•œ ๋กœ์ปฌ ์‹ค์Šต์šฉ(:memory: DB). ์‹ค์ œ MOVEit ์ฝ”๋“œ ์•„๋‹˜
def vulnerable_query(email):
    # ์ทจ์•ฝ: ๋ฏธ์ธ์ฆ ์ž…๋ ฅ์ด ์ •๋ฆฌ(XHTMLClean ์ƒ๋‹น) ์—†์ด ๋ฌธ์ž์—ด ๊ฒฐํ•ฉ์œผ๋กœ ๋“ค์–ด๊ฐ„๋‹ค
    return ("SELECT Username, Permission FROM users WHERE InstID=9389 AND Deleted=0 "
            "AND (Email='%s' OR Email LIKE ('%s'))"
            % (email, escape_like_for_sql(email)))

def harden_query():                      # ์ˆ˜์ •: ํ”Œ๋ ˆ์ด์Šคํ™€๋” + ๋ฐ”์ธ๋”ฉ
    return ("SELECT Username, Permission FROM users WHERE InstID=? AND Deleted=0 "
            "AND (Email=? OR Email LIKE ?)")

payload = "x') OR 1=1 --"                # ์ฝค๋งˆ ์—†์ด, ๋’ค์ชฝ ์ ˆ์€ ์ฃผ์„์œผ๋กœ ์ž˜๋ผ๋‚ธ๋‹ค
print(db.execute(vulnerable_query(payload) + "\n").fetchall())
print(db.execute(harden_query(), (9389, payload, escape_like_for_sql(payload))).fetchall())

์‹คํ–‰ ๊ฒฐ๊ณผ(์ด๋ฒˆ ์‹คํ–‰ ์‹ค์ œ ์ถœ๋ ฅ):

[์ทจ์•ฝ ์ฝ”๋“œ]  ์‹คํ–‰๋˜๋Š” SQL (์‚ฝ์ž… ์ง€์  ๋’ค๋กœ๋Š” `--` ์ฃผ์„):
    SELECT Username, Permission FROM users WHERE InstID=9389 AND Deleted=0 AND (Email='x') OR 1=1 --' OR Email LIKE ('x') OR 1=1 --'))
    โ†’ ์‹ค์ œ๋กœ ์„œ๋ฒ„๊ฐ€ ์‹คํ–‰ํ•˜๋Š” ๋ฌธ์žฅ: SELECT Username, Permission FROM users WHERE InstID=9389 AND Deleted=0 AND (Email='x') OR 1=1   /* ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ๋Š” ์ „๋ถ€ ์ฃผ์„ */
   ๋ฐ˜ํ™˜ ํ–‰ ์ˆ˜: 3 โ†’ [('svc_backup', 30), ('sysadmin', 30), ('guest_secmsg', 10)]
   โ€ป ํ‘œ์  ์ธ์Šคํ„ด์Šค(9389)๋กœ ํ•œ์ •๋˜์–ด์•ผ ํ•  ์กฐํšŒ๊ฐ€ ์ „์ฒด ๊ณ„์ •์„ ๋ฐ˜ํ™˜ํ–ˆ๋‹ค.

[์ˆ˜์ • ์ฝ”๋“œ]  ์‹คํ–‰๋˜๋Š” SQL: SELECT Username, Permission FROM users WHERE InstID=? AND Deleted=0 AND (Email=? OR Email LIKE ?)
   ๋ฐ”์ธ๋”ฉ ๊ฐ’: (9389, "x') OR 1=1 --") โ†’ ๋ฐ˜ํ™˜ ํ–‰ ์ˆ˜: 0 โ†’ []
   โ€ป ๊ฐ™์€ ์ž…๋ ฅ์ด '๋ฌธ์ž์—ด'๋กœ๋งŒ ์ทจ๊ธ‰๋˜์–ด 0๊ฑด โ€” ๊ถŒํ•œ ๊ฒฝ๊ณ„๊ฐ€ ์œ ์ง€๋œ๋‹ค.

PoC โ‘ก โ€” ์œ ์ถœ๋œ ์„œ๋ช… ํ‚ค๊ฐ€ '์ธ์ฆ'์„ ๋ฌด๋ ฅํ™”ํ•œ๋‹ค

5์žฅ โ‘ค๋‹จ๊ณ„(sysadmin ํ† ํฐ ํš๋“)์™€ โ‘ฆ๋‹จ๊ณ„(์—ญ์ง๋ ฌํ™”)์˜ ๊ณตํ†ต ์ „์ œ๋Š” "์„œ๋ช… ํ‚ค๋ฅผ ์•ˆ๋‹ค"๋Š” ๊ฒƒ์ด๋‹ค. ์•„๋ž˜๋Š” ASP.NET ์ธ์ฆ ์ฟ ํ‚ค ์„œ๋ช…(HMAC)์„ ๋ชจ์˜๋กœ ์žฌํ˜„ํ•ด, ํ‚ค๋ฅผ ๊ต์ฒดํ•˜๋ฉด ์œ„์กฐ ์„ธ์…˜์ด ์ฆ‰์‹œ ์ „๋ถ€ ๋ฌดํšจ๊ฐ€ ๋˜๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ค€๋‹ค.

# poc2_session_forgery.py โ€” ๊ต์œก์šฉ ๋ชจ์˜(๋กœ์ปฌ). ์‹ค์ œ ์„ธ์…˜ ์ฟ ํ‚คยทmachineKey ์•„๋‹˜
LAB_MACHINE_KEY = hashlib.sha256(b"mock-lab-machine-key-0926").digest()   # ๋ชจ์˜ ํ‚ค

def sign_session(payload, key):          # ์ฟ ํ‚ค ๋ณธ๋ฌธ + HMAC ์„œ๋ช…
    body = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode()
    return (body + b"|" + hmac.new(key, body, hashlib.sha256).digest()).hex()

def verify_session(blob, key):           # ๊ฒ€์ฆ: ์„œ๋ช…์ด ๋งž์œผ๋ฉด ์„ธ์…˜ '์ธ์ •'
    raw = bytes.fromhex(blob)
    body, _, mac = raw.rpartition(b"|")
    if hmac.compare_digest(hmac.new(key, body, hashlib.sha256).digest(), mac):
        return json.loads(body)
    return None

forged = sign_session({"user": "sysadmin", "perm": 30, "ip": "*"}, LAB_MACHINE_KEY)
print(verify_session(forged, LAB_MACHINE_KEY))                    # โ‘  ์›๋ž˜ ํ‚ค
print(verify_session(forged, hashlib.sha256(b"rotated").digest()))  # โ‘ก ํ‚ค ๊ต์ฒด ํ›„

์‹คํ–‰ ๊ฒฐ๊ณผ(์ด๋ฒˆ ์‹คํ–‰ ์‹ค์ œ ์ถœ๋ ฅ):

์œ„์กฐ ์ฟ ํ‚ค(๋ชจ์˜): 7b22657870223a313739303433313431342c226970223a222a222c227065726d223a3330 ...
โ‘  ์›๋ž˜ ํ‚ค๋กœ ๊ฒ€์ฆ : {'exp': 1790431414, 'ip': '*', 'perm': 30, 'user': 'sysadmin'}
โ‘ก ํ‚ค ๊ต์ฒด ํ›„ ๊ฒ€์ฆ: None
   โ€ป ์„œ๋ช… ๊ฒ€์ฆ์€ 'ํ‚ค๋ฅผ ์•„๋Š”๊ฐ€'๋งŒ ๋ฌป๋Š”๋‹ค. ํ‚ค๊ฐ€ DB(SQLi)๋กœ ์ƒˆ๋ฉด ์„œ๋ช…์€ ๋ฌด์˜๋ฏธํ•ด์ง€๊ณ ,
     ๋ฐ˜๋Œ€๋กœ ํ‚ค๋ฅผ ๊ต์ฒดํ•˜๋ฉด ์ด๋ฏธ ๋ฟŒ๋ ค์ง„ ์œ„์กฐ ์„ธ์…˜ยท๋ทฐ์Šคํ…Œ์ดํŠธ๊ฐ€ ํ•œ ๋ฒˆ์— ๋ฌดํšจํ™”๋œ๋‹ค.

PoC โ‘ข โ€” 404๋กœ ์œ„์žฅํ•˜๋Š” ์›น์…ธ์˜ 'ํ˜•ํƒœ'์™€ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ํ—ŒํŒ…

LEMURLOOT์˜ ํ†ต์‹  ๊ทœ์น™(ํ•˜๋“œ์ฝ”๋”ฉ ๋น„๋ฐ€๋ฒˆํ˜ธ + ์ „์šฉ ํ—ค๋” + gzip ์‘๋‹ต + 404 ์œ„์žฅ)์„ ๋กœ์ปฌ ํ•จ์ˆ˜๋กœ ์žฌํ˜„ํ•˜๊ณ , ์ด์–ด์„œ CISA ํ—ŒํŒ… ์‹œ๊ทธ๋‹ˆ์ฒ˜์˜ ์ถ•(๋งˆ์ปค + ํŒŒ์ผ ํฌ๊ธฐ๋Œ€)์ด ์ •์ƒ ํŒŒ์ผ๊ณผ ์–ด๋–ป๊ฒŒ ๊ตฌ๋ถ„๋˜๋Š”์ง€ ๋Œ€์กฐํ•œ๋‹ค. ์›น์…ธ payload ์ž์ฒด๋Š” ์ž‘์„ฑํ•˜์ง€ ์•Š๊ณ , '๋งˆ์ปค๊ฐ€ ์žˆ๋Š” ํ•ฉ์„ฑ ๋ฌธ์ž์—ด'๋งŒ ๊ฒ€์‚ฌํ•œ๋‹ค.

# poc3_webshell_hunt.py โ€” ์›น์…ธ์„ ๋งŒ๋“ค์ง€ ์•Š๊ณ  'ํ˜•ํƒœ'๋งŒ ์žฌํ˜„ยทํƒ์ง€
MOCK_SECRET = "mock-lab-password"      # ์‹ค์Šต์šฉ ๋ชจ์˜ ๊ฐ’ (์‹ค์ œ ์›น์…ธ ๋น„๋ฐ€๋ฒˆํ˜ธ ์•„๋‹˜)

def mock_request(headers):             # ํ†ต์‹  ๊ทœ์น™ ์žฌํ˜„ (๋„คํŠธ์›Œํฌ ์—†์Œ)
    if headers.get("X-siLock-Comment") != MOCK_SECRET:
        return 404, b"Not Found"       # ์ •์ƒ ๋ฐฉ๋ฌธ์žยท์Šค์บ๋„ˆ์—๊ฒŒ๋Š” ๊ทธ๋ƒฅ 404
    if headers.get("X-siLock-Step1") == "list":
        return 200, gzip.compress(b"[mock] /Human/Inbox/mock-dump.csv")
    return 404, b"Not Found"

SIGNATURES = ["X-siLock-Comment", "X-siLock-Step", "Health Check Service",
              "Create_ASP_human2_aspx"]          # CISA ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ถ•์•ฝ(๊ต์œก์šฉ)

def scan_aspx(name, blob):             # ์ด๋ฆ„ + ํฌ๊ธฐ ์œˆ๋„์šฐ + ๋งˆ์ปค ๊ฐœ์ˆ˜
    hits = [s for s in SIGNATURES if s.encode() in blob]
    return (name.endswith(".aspx") and 5000 <= len(blob) <= 10000 and len(hits) >= 2), len(blob), hits

for name, blob in (("human2.aspx", LEMU_SHAPE), ("human.aspx", BENIGN_ASPX)):
    print(name, scan_aspx(name, blob))

์‹คํ–‰ ๊ฒฐ๊ณผ(์ด๋ฒˆ ์‹คํ–‰ ์‹ค์ œ ์ถœ๋ ฅ):

์ •์ƒ ์š”์ฒญ            โ†’ (404, b'Not Found')
๋น„๋ฐ€๋ฒˆํ˜ธ ํ‹€๋ฆฐ ์š”์ฒญ   โ†’ (404, b'Not Found')
๋น„๋ฐ€๋ฒˆํ˜ธ ๋งž๋Š” ์š”์ฒญ   โ†’ 200 | gzip ํ•ด์ œ: b'[mock] /Human/Inbox/mock-dump.csv'

human2.aspx   size=7182  markers=4 ['X-siLock-Comment', 'X-siLock-Step', 'Health Check Ser โ†’ โš ๏ธ ์•…์„ฑ ์˜์‹ฌ
human.aspx    size=2453  markers=0 []                                                      โ†’ ์ •์ƒ

PoC โ‘ฃ โ€” ์‚ฌํ›„ ํ—ŒํŒ…: ๋‚จ์€ ๊ณ„์ • ๊ฐ์‚ฌ + ๋กœ๊ทธ ์ƒ๊ด€

์›น์…ธ ํŒŒ์ผ์„ ์ง€์›Œ๋„ DB์— ๋‚จ์€ ๊ณต๊ฒฉ์ž ๊ณ„์ •์ด ์ง€์†์„ฑ์ด ๋œ๋‹ค๋Š” CISA์˜ ์ง€์ ์„ ๊ทธ๋Œ€๋กœ ์˜ฎ๊ฒผ๋‹ค. ๋”๋ถˆ์–ด, 404๋ฅผ ๋Œ๋ ค์ฃผ๋Š” ์š”์ฒญ์„ ๋กœ๊ทธ์—์„œ ์Šน๊ฒฉ ๊ฒฝ๋ณด๋กœ ๋งŒ๋“œ๋Š” ๊ทœ์น™(์ •์ƒ ์‚ฌ์šฉ์ž๋Š” 404 ํŽ˜์ด์ง€๋ฅผ 3๋ฒˆ ์—ฐ์† POSTํ•˜์ง€ ์•Š๋Š”๋‹ค)์„ ํ•ฉ์„ฑ ๋กœ๊ทธ๋กœ ๊ฒ€์ฆํ•œ๋‹ค.

# poc4_hunt.py โ€” ๋‚จ์€ ๊ณต๊ฒฉ์ž ๊ณ„์ • ๊ฐ์‚ฌ + '404 ์›น์…ธ' ๋กœ๊ทธ ์ƒ๊ด€ (ํ•ฉ์„ฑ ๋ฐ์ดํ„ฐ)
AUDIT_SQL = ("SELECT Username, LoginName FROM users "
             "WHERE Permission=30 AND Status='active' AND Deleted='0'")

for u in db.execute(AUDIT_SQL):          # RealName='Health Check Service' ๊ณ„์ •์ด ํƒ์ง€ ๋Œ€์ƒ
    print("๊ด€๋ฆฌ์ž ๊ถŒํ•œ ๊ณ„์ •:", u)

for (src, path, code), ts in seen.items():        # ๊ฐ™์€ ์ถœ์ฒ˜๊ฐ€ 404 .aspx ๋ฅผ ๋ฐ˜๋ณต POST
    if code == "404" and len(ts) >= 3:
        print("[webshell_probe] src=%s path=%s 404๋ฐ˜๋ณต=%dํšŒ" % (src, path, len(ts)))

์‹คํ–‰ ๊ฒฐ๊ณผ(์ด๋ฒˆ ์‹คํ–‰ ์‹ค์ œ ์ถœ๋ ฅ):

[๊ฐ์‚ฌ ์ฟผ๋ฆฌ] SELECT Username, LoginName FROM users WHERE Permission=30 AND Status='active' AND Deleted='0'
   ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ๊ณ„์ •: ('svc_backup', 'svc_backup')
   ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ๊ณ„์ •: ('hcs_a71f', 'a71f9c22')
   โ€ป RealName='Health Check Service' ๊ณ„์ •์€ CISA ๊ถŒ๊ณ ์ƒ ์‚ญ์ œ ๋Œ€์ƒ(์›น์„œ๋ฒ„๋ฅผ ์žฌ๊ตฌ์ถ•ํ•ด๋„
     DB๊ฐ€ ๋‚จ์•„ ์žˆ์œผ๋ฉด ๊ณต๊ฒฉ์ž ๊ณ„์ •์€ ๊ทธ๋Œ€๋กœ ์‚ด์•„ ์žˆ๋‹ค).

[๋กœ๊ทธ ์ƒ๊ด€] 404 ์‘๋‹ต์ด๋ผ๋„ '๊ฐ™์€ ์ถœ์ฒ˜๊ฐ€ ๋ฐ˜๋ณต POST' ํ•˜๋Š” .aspx ๋ฅผ ์Šน๊ฒฉ ๊ฒฝ๋ณด๋กœ ์ฒ˜๋ฆฌ
   โš ๏ธ [webshell_probe] src=203.0.113.7 path=/human2.aspx 404๋ฐ˜๋ณต=3ํšŒ since=2026-09-25T23:41:05Z

๋„ค ์กฐ๊ฐ์„ ํ•ฉ์นœ ๊ฒฐ๋ก ์€ ์ด๋ ‡๋‹ค โ€” โ‘ ๋ฌด์ธ์ฆ ๊ฒฝ๋กœ โ‘ก์„ธ์…˜ ๋ณ€์ˆ˜ ์ฃผ์ž… โ‘ข์„œ๋ช… ํ‚ค ํƒˆ์ทจ โ‘ฃํŒŒ์ผ ์—…๋กœ๋“œยท์—ญ์ง๋ ฌํ™”๊ฐ€ ๊ฐ๊ฐ์€ '์ •์ƒ ๊ธฐ๋Šฅ'์ธ๋ฐ ์ง๋ ฌ๋กœ ์ด์–ด ๋ถ™์ž RCE๊นŒ์ง€ ๋„๋‹ฌํ–ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฐฉ์–ด๋„ ๊ฐ ์ด์Œ์ƒˆ๋งˆ๋‹ค ํ•„์š”ํ•˜๋‹ค.

6. ๋ฐฉ์–ด ๋ฐฉ๋ฒ•/๊ตํ›ˆ

7. ์ถœ์ฒ˜

โ€ป ๋ณธ๋ฌธ์˜ PoC ์ฝ”๋“œ๋Š” ๊ตฌ์กฐ ์ดํ•ด ๋ชฉ์ ์˜ ๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ์ด๋ฉฐ, ์‹ค์ œ CVE-2023-34362 ํŠธ๋ฆฌ๊ฑฐยท์‹ค์ œ ์ต์Šคํ”Œ๋กœ์ž‡ ์š”์ฒญยท์‹ค์ œ ์›น์…ธ(LEMURLOOT)ยท์œ ์ถœ ๋ฐ์ดํ„ฐยทC2/IOC๋Š” ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹ค์Šต์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฒฉ๋ฆฌ ๋žฉ VM์„ ์ „์ œ๋กœ ํ•ฉ๋‹ˆ๋‹ค.

ํ•จ๊ป˜ ์ฝ๊ธฐ: โ˜ ๏ธ NotPetya (2017) โ€” ์‹ ๋ขฐ๋ฐ›๋Š” ์—…๋ฐ์ดํŠธ ์ฑ„๋„์„ ๋ฌด๊ธฐ๋กœ ์“ด ์™€์ดํผ ยท ๐Ÿฆ… SolarWinds SUNBURST (2020) โ€” ๊ณต๊ธ‰๋งยท์„œ๋ช… ์‹ ๋ขฐ์˜ ๋ถ•๊ดด ยท ๐Ÿ’ฅ Emotet (2014โ€“2021) โ€” ๋žœ์„ฌ์›จ์–ด์˜ 1์ฐจ ์นจํˆฌ ํ”Œ๋žซํผ ยท ๐Ÿชฑ Conficker (2008) โ€” ์ž๊ฐ€ ๋ฐฉ์–ดํ˜• ์›œ ยท ๐Ÿฆ Bangladesh Bank SWIFT Heist (2016) โ€” '์ •์ƒ ์Šน์ธ ๊ฑฐ๋ž˜'๋กœ ์œ„์žฅํ•œ ์€ํ–‰ ๊ฐ•ํƒˆ

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