๐Ÿฆ Bangladesh Bank SWIFT Heist โ€” '์ •์ƒ ์Šน์ธ ๊ฑฐ๋ž˜'๋กœ ์œ„์žฅํ•ด 8,100๋งŒ ๋‹ฌ๋Ÿฌ๋ฅผ ๋นผ๋‚ธ APT38์˜ ์€ํ–‰ ๊ฐ•ํƒˆ (2016)

2026-09-15 ยท Malware Deep-Dive

1. ๋ฐฐ๊ฒฝ/์—ญ์‚ฌ โ€” ์–ธ์ œ, ๋ˆ„๊ฐ€, ์™œ ์œ ๋ช…ํ•œ๊ฐ€

2016๋…„ 2์›” 4์ผ ๋ฐค๋ถ€ํ„ฐ 5์ผ ์‚ฌ์ด, ๋ฐฉ๊ธ€๋ผ๋ฐ์‹œ ์ค‘์•™์€ํ–‰(Bangladesh Bank)์ด ๋‰ด์š• ์—ฐ๋ฐฉ์ค€๋น„์€ํ–‰(FRB New York)์— ๋‘๊ณ  ์žˆ๋˜ ๊ณ„์ขŒ์—์„œ SWIFT ๊ตญ์ œ๊ฒฐ์ œ๋ง์„ ํ†ตํ•ด 35๊ฑด์˜ ์†ก๊ธˆ ์ง€์‹œ๊ฐ€ ์Ÿ์•„์กŒ๋‹ค. ๋ฐœ์‹ ์ž๋Š” ๋ฐฉ๊ธ€๋ผ๋ฐ์‹œ ์ค‘์•™์€ํ–‰์˜ ์ •์ƒ SWIFT ํ„ฐ๋ฏธ๋„๊ณผ ์ •์ƒ ์ž๊ฒฉ์ฆ๋ช…์ด์—ˆ๊ณ , ์ „๋ฌธ(๋ฉ”์‹œ์ง€) ํ˜•์‹๋„ ์ •์ƒ์ด์—ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ ๋ฐœ์‹ ์ž๋Š” ์€ํ–‰ ๋‚ด๋ถ€์— ์นจํˆฌํ•ด ์žˆ๋˜ ๊ณต๊ฒฉ์ž์˜€๋‹ค. ๋‚˜ํ˜ ๋’ค์ธ 2์›” 8์ผ(์ค‘๊ตญ ์„ค๋‚  ์—ฐํœด ์ฒซ๋‚ ) ์€ํ–‰์ด ์ด์ƒ์„ ์ธ์ง€ํ–ˆ์„ ๋•Œ, ์ด๋ฏธ 8,100๋งŒ ๋‹ฌ๋Ÿฌ๋Š” ํ•„๋ฆฌํ•€์œผ๋กœ ๋น ์ ธ๋‚˜๊ฐ„ ๋’ค์˜€๋‹ค.

  • ์‚ฌ๊ฑด๋ช…: Bangladesh Bank SWIFT Heist (๋ฐฉ๊ธ€๋ผ๋ฐ์‹œ ์ค‘์•™์€ํ–‰ ์‚ฌ์ด๋ฒ„ ๊ฐ•ํƒˆ) / ๊ด€๋ จ ์•…์„ฑ์ฝ”๋“œ DYEPACK(๋ณ„์นญ BanSwift), evtdiag.exe
  • ์‹œ๊ธฐ: ์•…์„ฑ์ฝ”๋“œ ์„ค์น˜ 2016๋…„ 1์›” โ†’ ์†ก๊ธˆ ์ง€์‹œ 2016-02-04 ~ 02-05(๋ฐฉ๊ธ€๋ผ๋ฐ์‹œ ์ฃผ๋ง) โ†’ ์€ํ–‰ ์ธ์ง€ 02-08 โ†’ ํฌ๋ Œ์‹ยท๊ตญ์ œ ๊ณต์กฐ
  • ๊ณต๊ฒฉ์ž: APT38(= Lazarus ๊ทธ๋ฃน ๊ณ„์—ด / ๋ณ„์นญ BeagleBoyz, Bluenoroff, Sapphire Sleet, NICKEL GLADSTONE). ๋ฏธ ์ •๋ถ€๋Š” ๋ถํ•œ ์ •์ฐฐ์ด๊ตญ(RGB) ์†Œํ–‰์œผ๋กœ ๊ท€์†
  • ํ•ต์‹ฌ: ์ทจ์•ฝ์  ์ต์Šคํ”Œ๋กœ์ž‡์ด ์•„๋‹ˆ๋ผ ์€ํ–‰ ๋‚ด๋ถ€ ์นจํˆฌ + ์ž๊ฒฉ์ฆ๋ช… ํƒˆ์ทจ + ๊ฒฐ์ œ ๋ฐ์ดํ„ฐ ์ง์ ‘ ์กฐ์ž‘. ํ”„๋ฆฐํ„ฐ ์ถœ๋ ฅยทPDF ํ‘œ์‹œ๊นŒ์ง€ ์œ„์กฐ
  • ๊ทœ๋ชจ: ์‹œ๋„ $951M(35๊ฑด) ์ค‘ ์Šน์ธ $101M, ์‹ค์ œ ์œ ์ถœ $81M(ํ•„๋ฆฌํ•€) + $20M(์Šค๋ฆฌ๋ž‘์นด, ์ „์•ก ํšŒ์ˆ˜), ๋‚˜๋จธ์ง€ $850M ์ฐจ๋‹จ

์™œ ์œ ๋ช…ํ•œ๊ฐ€. ๊ทธ์ „๊นŒ์ง€ ๋Œ€ํ˜• ์‚ฌ๊ณ ๋Š” "์›œ์ด ํผ์กŒ๋‹ค"(MorrisยทSlammerยทConficker), "๋žœ์„ฌ์›จ์–ด๊ฐ€ ์•”ํ˜ธํ™”ํ–ˆ๋‹ค"(WannaCryยทNotPetya), "์‹œ์„ค์ด ํŒŒ๊ดด๋๋‹ค"(Stuxnet) ๊ฐ™์€ ์„œ์‚ฌ์˜€๋‹ค. ์ด ์‚ฌ๊ฑด์€ ์ฒ˜์Œ์œผ๋กœ "๊ตญ๊ฐ€๊ฐ€ ๊ตญ์ œ ๊ฒฐ์ œ๋ง์— ์ ‘์†๋œ ์€ํ–‰์„ ์ง์ ‘ ํ„ธ์—ˆ๋‹ค"๋Š” ์‚ฌ์‹ค์„ ๋“œ๋Ÿฌ๋ƒˆ๋‹ค. ๊ทธ ์ถฉ๊ฒฉ์€ ์„ธ ๊ฐˆ๋ž˜๋กœ ๋‚จ์•˜๋‹ค.

2. ๊ฐ์—ผยท์ „ํŒŒ ๊ฒฝ๋กœ โ€” '์ „ํŒŒ'๊ฐ€ ์—†๋Š” ๊ณต๊ฒฉ

์ด ์‚ฌ๊ฑด์—๋Š” ์ž๊ธฐ๋ณต์ œ๋„, ์Šค์บ”๋„, ์›Œํฌ ์ „ํŒŒ๋„ ์—†๋‹ค. ๊ทธ๋ž˜์„œ ์•…์„ฑ์ฝ”๋“œ ๋ถ„๋ฅ˜์ƒ '์›œ'์ด ์•„๋‹ˆ๋ผ ํ‘œ์ ํ˜• ์นจํˆฌ(APT) + ๊ฒฐ์ œ ์‚ฌ๊ธฐ๋กœ ๋ณธ๋‹ค. ํ™•์‚ฐ ๋ฐฉ์‹์ด ์•„๋‹ˆ๋ผ ์นจํˆฌ โ†’ ์ •์ฐฐ โ†’ ์‹คํ–‰์ด๋ผ๋Š” ์‹œ๊ฐ„์ถ•์œผ๋กœ ๋ด์•ผ ํ•œ๋‹ค.

์ •๋ฆฌํ•˜๋ฉด ์ด ๊ณต๊ฒฉ์˜ '์ „ํŒŒ ๊ฒฝ๋กœ'๋Š” ๋„คํŠธ์›Œํฌ๊ฐ€ ์•„๋‹ˆ๋ผ "๊ฒฐ์ œ๋ง์— ์ ‘์†๋œ ํ„ฐ๋ฏธ๋„์„ ๋ณด์œ ํ•œ ๊ธฐ๊ด€"์ด๋ผ๋Š” ์ž๊ฒฉ ์กฐ๊ฑด์ด๋‹ค. ๊ทธ๋ž˜์„œ ๋ฐฉ์–ด์˜ ๋‹จ์œ„๋„ ๋ฐฑ์‹ ์ด ์•„๋‹ˆ๋ผ ๊ธˆ์œต ์ธํ”„๋ผ์˜ ํ†ต์ œ(์„ธ๊ทธ๋ฉ˜ํ…Œ์ด์…˜ยท2FAยท๋Œ€์‚ฌ)๊ฐ€ ๋œ๋‹ค.

3. ๋™์ž‘ ์›๋ฆฌ ์ƒ์„ธ ๋ถ„์„ โ€” ์ฝ”๋“œ/๊ตฌ์กฐ ๋ ˆ๋ฒจ

3-1. ์ด ๊ณต๊ฒฉ์˜ ์ „์ œ: ๊ฒฐ์ œ ๋ช…๋ น์€ '์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ๋งŒ๋“œ๋Š” ๋ฐ์ดํ„ฐ'๋‹ค

3-2. BAE Systems๊ฐ€ ๊ณต๊ฐœํ•œ evtdiag.exe์˜ ๋™์ž‘ (์š”์•ฝ)

์‚ฌ๊ฑด ์งํ›„ BAE Systems์˜ ๋ถ„์„(Sergei Shevchenko)์€ ํ‘œ์  ํ™˜๊ฒฝ์„ ์œ„ํ•ด ๋งž์ถค ์ œ์ž‘๋œ ์•…์„ฑ์ฝ”๋“œ๋ฅผ ๊ณต๊ฐœํ–ˆ๋‹ค. ํ•ต์‹ฌ ๋™์ž‘์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

3-3. MITRE ATT&CK ๋งคํ•‘ โ€” "์ €์žฅยท์ „์†กยทํ‘œ์‹œ" 3์ธต ๋™์‹œ ์˜ค์—ผ

์ด 3์ข… ์กฐํ•ฉ์ด ์ด ๊ณต๊ฒฉ์˜ '์„ค๊ณ„ ์˜๋„'๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณด์—ฌ์ค€๋‹ค. ๊ฐ์‚ฌ(audit)๋Š” ๋ณดํ†ต ์„ธ ๊ณ„์ธต์„ ๊ต์ฐจ ํ™•์ธํ•ด์„œ ์‹ ๋ขฐ๋ฅผ ์–ป๋Š”๋‹ค โ€” DB ๋กœ๊ทธ, ์ถœ๋ ฅ ๊ธฐ๋ก, ํ™”๋ฉด/๋ฆฌํฌํŠธ. ๊ณต๊ฒฉ์ž๋Š” ์„ธ ๊ณ„์ธต์„ ๊ฐ™์€ ๋„๊ตฌ๋กœ ๋™์‹œ์— ๋ฌด๋ ฅํ™”ํ–ˆ๊ณ , ๊ทธ๋ž˜์„œ ๋‚ด๋ถ€์—์„œ๋Š” '์ด์ƒ ์—†์Œ'์œผ๋กœ ๋ณด์˜€๋‹ค.

3-4. ๋ฌด์—‡์ด ์‹ค์ œ๋กœ ๋ง‰์•˜๋‚˜ โ€” ์‹œ์Šคํ…œ์ด ์•„๋‹ˆ๋ผ '์‚ฌ๋žŒ์˜ ๋ˆˆ'

4. ํ”ผํ•ด ๊ทœ๋ชจ/์˜ํ–ฅ โ€” ์ˆซ์ž์™€ ์‹ค์ œ ์‚ฌ๋ก€

๊ตฌ๋ถ„๋‚ด์šฉ
์†ก๊ธˆ ์ง€์‹œ35๊ฑด (์•ฝ 3~40๊ฑด์œผ๋กœ ํ‘œ๊ธฐ๋˜๋Š” ์ž๋ฃŒ๋„ ์žˆ์œผ๋ฉฐ, ๋ชจ๋‘ 2016-02-04~05์— ์ง‘์ค‘)
์‹œ๋„ ๊ธˆ์•ก$951M (๋‰ด์š• ์—ฐ์ค€ ๊ณ„์ขŒ ๊ธฐ์ค€)
์Šน์ธยท์„ฑ๊ณต5๊ฑด $101M โ€” ํ•„๋ฆฌํ•€ $81M + ์Šค๋ฆฌ๋ž‘์นด $20M
์ฐจ๋‹จ30๊ฑด $850M (์˜คํƒ€๋กœ ์ธํ•œ ํ™•์ธ ์š”์ฒญ โ†’ ์—ฐ์ค€ ์ฐจ๋‹จ)
ํšŒ์ˆ˜์Šค๋ฆฌ๋ž‘์นด $20M ์ „์•ก ํšŒ์ˆ˜, ํ•„๋ฆฌํ•€ $81M ์ค‘ ์•ฝ $15M๋งŒ ํšŒ์ˆ˜ (๋‚˜๋จธ์ง€ ๋Œ€๋ถ€๋ถ„ ๋ฏธํšŒ์ˆ˜)

5. PoC โ€” ๊ต์œก์šฉ ๋ชจ์˜ ์žฌํ˜„ (๋กœ์ปฌ ๊ฐ€์ƒ ํ™˜๊ฒฝ ์ „์šฉ)

โš ๏ธ ์•ˆ์ „ ๊ทœ์น™: ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋™์ž‘ ์›๋ฆฌ๋ฅผ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•œ ๊ต์œก์šฉ ๋ชจ์˜(mock) ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ์•…์„ฑ์ฝ”๋“œ(evtdiag.exe/DYEPACK)๋ฅผ ๋ณต์‚ฌยท๋ฐฐํฌํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์‹ค์ œ SWIFT ์ „๋ฌธยท์€ํ–‰ ๊ณ„์ขŒยท๊ฒฐ์ œ๋ง์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹คํ–‰์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๋กœ์ปฌ ์‹ค์Šต ๋””๋ ‰ํ„ฐ๋ฆฌ(/tmp/lab_swift_mock)์™€ ๊ฐ€์ƒ์˜ ์›์žฅ ๋ฐ์ดํ„ฐ๋งŒ ๋Œ€์ƒ์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. ๋ชฉ์ ์€ "์™œ ์ด๋Ÿฐ ์กฐ์ž‘์ด ๊ฐ€๋Šฅํ–ˆ๋Š”๊ฐ€"์™€ "์–ด๋–ป๊ฒŒ ํƒ์ง€ํ•˜๋Š”๊ฐ€"๋ฅผ ์žฌํ˜„ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

PoC A โ€” '์ •์ƒ ๋ฐ์ดํ„ฐ'๋ฅผ ์กฐ์ž‘ํ•ด ํ”์ ์„ ์ง€์šฐ๋Š” ์›๋ฆฌ ์žฌํ˜„

๊ฒฐ์ œ ๋ฐ์ดํ„ฐ๊ฐ€ โ‘  ๋‚ด๋ถ€ ์›์žฅ(journal), โ‘ก ๋Œ€์™ธ ํ†ต๋ณด์šฉ ๋ช…์„ธ(statement), โ‘ข ์ถœ๋ ฅ๋ฌผ(print queue)์— ๊ฐ๊ฐ ๋‚จ๋Š”๋‹ค๋Š” ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ƒ SQLite๋กœ ๋ชจ์‚ฌํ•˜๊ณ , ๊ธˆ์•ก ๋ณ€์กฐ โ†’ ๊ธฐ๋ก ์‚ญ์ œ โ†’ ์ถœ๋ ฅ ์œ„์กฐ๊ฐ€ ์–ด๋–ป๊ฒŒ '์ •์ƒ ์—…๋ฌด์ฒ˜๋Ÿผ' ๋ณด์ด๊ฒŒ ๋˜๋Š”์ง€ ์žฌํ˜„ํ•œ๋‹ค.

# poc_a_mock_ledger.py โ€” ๊ต์œก์šฉ ๋ชจ์˜ (์‹ค์ œ ์•…์„ฑ์ฝ”๋“œ ์•„๋‹˜ / ๋กœ์ปฌ ๊ฐ€์ƒ ์›์žฅ ์ „์šฉ)
import os, sqlite3, shutil

LAB = "/tmp/lab_swift_mock"
if os.path.isdir(LAB):
    shutil.rmtree(LAB)                 # ์‹ค์Šต ์ „์šฉ ๋””๋ ‰ํ„ฐ๋ฆฌ๋งŒ ์ดˆ๊ธฐํ™”
os.makedirs(LAB + "/print_queue")

DB = LAB + "/ledger.sqlite"
db = sqlite3.connect(DB); c = db.cursor()
c.execute("CREATE TABLE journal(seq INTEGER PRIMARY KEY, ref TEXT, amount INTEGER, ben TEXT)")
c.execute("CREATE TABLE statement(id INTEGER PRIMARY KEY, ref TEXT, amount INTEGER)")

def send(ref, amount, ben):
    """์ •์ƒ ์†ก๊ธˆ 1๊ฑด: ๋‚ด๋ถ€ ์›์žฅ๊ณผ ๋Œ€์™ธ ๋ช…์„ธ์— ๋™์‹œ ๊ธฐ๋ก + ํ™•์ธ ์ถœ๋ ฅ๋ฌผ ์ƒ์„ฑ"""
    seq = c.execute("SELECT COALESCE(MAX(seq),0)+1 FROM journal").fetchone()[0]
    c.execute("INSERT INTO journal VALUES(?,?,?,?)", (seq, ref, amount, ben))
    c.execute("INSERT INTO statement(ref, amount) VALUES(?,?)", (ref, amount))
    open("%s/print_queue/confirm_%s.txt" % (LAB, ref), "w").write(
        "SWIFT CONFIRMATION\nref=%s amount=USD %d beneficiary=%s\n" % (ref, amount, ben))
    db.commit()

# ์˜์—… ์ค‘ ์ •์ƒ ๊ฑฐ๋ž˜ 3๊ฑด (๊ฐ€์ƒ์˜ ๊ธˆ์•ก/์ˆ˜์ทจ์ธ)
send("REF-0001", 1200000, "Atlas Foods Trading LLC")
send("REF-0002",  330000, "Riverline Logistics Ltd")
send("REM-0300",  810000, "Shalika Foundation")     # ์˜คํƒ€: 'Fandation' ๋กœ ๋ณด๋‚ด์ง„ ์‹ค์ œ ์‚ฌ๊ฑด์˜ ๊ทธ ์†ก๊ธˆ

print("[์ •์ƒ] ์›์žฅ  :", c.execute("SELECT seq, ref, amount FROM journal").fetchall())
print("[์ •์ƒ] ๋ช…์„ธ  :", c.execute("SELECT ref, amount FROM statement").fetchall())

# --- ์—ฌ๊ธฐ์„œ๋ถ€ํ„ฐ '๊ณต๊ฒฉ์ž ๋„๊ตฌ' ํ‰๋‚ด (evtdiag.exe / DYEPACK ์˜ ๋™์ž‘ ๋ชจ์‚ฌ) ---
def attacker_tool(target_ref, new_amount=None, delete=True, fake_print=None):
    """์‹ค์ œ ์•…์„ฑ์ฝ”๋“œ๊ฐ€ ํ•œ ์ผ์„ '๊ฐ€์ƒ ๋กœ์ปฌ ์›์žฅ'์—์„œ ๊ทธ๋Œ€๋กœ ํ‰๋‚ด๋‚ธ๋‹ค."""
    if new_amount is not None:      # 1) ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ (T1565.001)
        c.execute("UPDATE journal SET amount=? WHERE ref=?", (new_amount, target_ref))
    if delete:                      # 2) ์ž๊ธฐ ๊ฑฐ๋ž˜์˜ ๊ธฐ๋ก ์‚ญ์ œ (T1565.001)
        c.execute("DELETE FROM statement WHERE ref=?", (target_ref,))
    if fake_print:                  # 3) ํ”„๋ฆฐํ„ฐ๋กœ ๊ฐ€๋Š” ํ™•์ธ ๋ฉ”์‹œ์ง€ ์œ„์กฐ (T1565.002)
        open("%s/print_queue/confirm_%s.txt" % (LAB, target_ref), "w").write(fake_print)
    db.commit()

attacker_tool("REM-0300",
              new_amount=99000,                                  # ๊ธˆ์•ก์„ 'ํ‰๋ฒ”ํ•œ' ๊ฐ’์œผ๋กœ ๋ฐ”๊ฟ” ๋ˆˆ์— ๋„์ง€ ์•Š๊ฒŒ
              delete=True,                                       # ๋Œ€์™ธ ๋ช…์„ธ์—์„œ๋Š” ๊ฑฐ๋ž˜ ์ž์ฒด๋ฅผ ์‚ญ์ œ
              fake_print="SWIFT CONFIRMATION\nref=REM-0300 status=REJECTED\n")  # "์‹คํŒจํ–ˆ๋‹ค"๋Š” ๊ฐ€์งœ ํ™•์ธ์ฆ

print("[์กฐ์ž‘] ์›์žฅ  :", c.execute("SELECT seq, ref, amount FROM journal").fetchall())
print("[์กฐ์ž‘] ๋ช…์„ธ  :", c.execute("SELECT ref, amount FROM statement").fetchall())
print("[์กฐ์ž‘] ์ถœ๋ ฅ๋ฌผ:", open(LAB + "/print_queue/confirm_REM-0300.txt").read().strip())
print("\nโ†’ ์›์žฅ์—๋Š” ๊ฑฐ๋ž˜๊ฐ€ ๋‚จ์•„ ์žˆ์ง€๋งŒ, ๊ธˆ์•ก์ด ๋‹ค๋ฅด๊ณ  ๋Œ€์™ธ ๋ช…์„ธ์—์„œ๋Š” ์‚ฌ๋ผ์กŒ์œผ๋ฉฐ")
print("  ํ”„๋ฆฐํ„ฐ์—๋Š” '๊ฑฐ์ ˆ๋จ(REJECTED)'์ด๋ผ๋Š” ๊ฐ€์งœ ํ™•์ธ์ฆ๋งŒ ๋‚จ์•˜๋‹ค.")
print("  ๋‚ด๋ถ€ ๋‹ด๋‹น์ž๊ฐ€ ์ถœ๋ ฅ๋ฌผ๋งŒ ๋ณธ๋‹ค๋ฉด '์‹คํŒจํ•œ ๊ฑฐ๋ž˜'๋กœ ๋ณด์ธ๋‹ค. ์ด๊ฒƒ์ด ์ด ๊ณต๊ฒฉ์˜ ํ•ต์‹ฌ์ด๋‹ค.")

PoC B โ€” ๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ์žก๋Š”๊ฐ€: '๋Œ€์‚ฌ(Reconciliation)' ๊ธฐ๋ฐ˜ ํƒ์ง€

์ด ๊ณต๊ฒฉ์˜ ๋ณธ์งˆ์€ ๋ฐ์ดํ„ฐ ๋ฌด๊ฒฐ์„ฑ ํ›ผ์†์ด๋‹ค. ๋”ฐ๋ผ์„œ ํƒ์ง€๋„ ์‹œ๊ทธ๋‹ˆ์ฒ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์„œ๋กœ ๋‹ค๋ฅธ ๊ณ„์ธต์˜ ๊ธฐ๋ก์„ ๊ต์ฐจ ๊ฒ€์ฆํ•˜๊ณ , ๊ธฐ๋ก์ด ์‚ฌ๋ผ์ง„ ๊ฒƒ(์‚ญ์ œ) ์ž์ฒด๋ฅผ ์ด์ƒ ์‹ ํ˜ธ๋กœ ์‚ผ์•„์•ผ ํ•œ๋‹ค. ์•„๋ž˜๋Š” ์‹ค์ œ๋กœ ์žกํžˆ๋Š” ์„ธ ๊ฐ€์ง€ ๋‹จ์„œ๋‹ค.

# poc_b_detect.py โ€” ๊ต์œก์šฉ ๋ชจ์˜ ํƒ์ง€ (๋™์ผํ•œ ๊ฐ€์ƒ ์›์žฅ ๋Œ€์ƒ)
import sqlite3, hashlib

LAB = "/tmp/lab_swift_mock"
db = sqlite3.connect(LAB + "/ledger.sqlite"); c = db.cursor()

# [๋‹จ์„œ 1] ๋‚ด๋ถ€ ์›์žฅ vs ๋Œ€์™ธ ๋ช…์„ธ ๋ถˆ์ผ์น˜ (๊ฑฐ๋ž˜๊ฐ€ '์‚ฌ๋ผ์ง„' ํ”์ )
j = set(r[0] for r in c.execute("SELECT ref FROM journal"))
s = set(r[0] for r in c.execute("SELECT ref FROM statement"))
print("[ํƒ์ง€1] ์›์žฅ์—๋งŒ ์žˆ๊ณ  ๋ช…์„ธ์— ์—†๋Š” ์ฐธ์กฐ๋ฒˆํ˜ธ:", sorted(j - s), "โ†’ ์‚ญ์ œ ์˜์‹ฌ")

# [๋‹จ์„œ 2] ์ฐธ์กฐ๋ฒˆํ˜ธ '๋ฒˆํ˜ธ ๋Œ€์—ญ'์˜ ๊ตฌ๋ฉ(์‹œํ€€์Šค ๊ฐญ)
#   REM-03xx ๋Œ€์—ญ์€ ์ผ๋ จ๋ฒˆํ˜ธ ์ฒด๊ณ„๋‹ค. 0300 ์ด์™ธ๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด '์—†๋Š” ๊ฒƒ'์ด ์•„๋‹ˆ๋ผ '์ง€์›Œ์ง„ ๊ฒƒ'์ผ ์ˆ˜ ์žˆ๋‹ค.
seen = sorted(r[0] for r in c.execute("SELECT ref FROM journal WHERE ref LIKE 'REM-%'"))
print("[ํƒ์ง€2] ์ผ๋ จ๋ฒˆํ˜ธ ์—ฐ์†์„ฑ:", seen, "โ†’ ๊ฐญ ์กด์žฌ ์—ฌ๋ถ€๋ฅผ ์ƒ์‹œ ์ ๊ฒ€")

# [๋‹จ์„œ 3] ๊ธˆ์•ก ์ด์ƒ: ๊ฐ™์€ ์ˆ˜์ทจ์ธ์—๊ฒŒ ์ด์ „์— ์—†๋˜ '์ •๋ฆฌ๋œ' ๊ธˆ์•ก ๋ณ€๋™
rows = c.execute("SELECT ref, amount FROM journal").fetchall()
print("[ํƒ์ง€3] ๊ธˆ์•ก ์Šค๋ƒ…์ƒท:", rows, "โ†’ '๋ผ์šด๋“œ ๋‹ค์šด'๋œ ๋ณ€์กฐ ํ”์ ์€ ์ด๋ ฅ ํ…Œ์ด๋ธ”๊ณผ ๋Œ€์กฐ")

# [๊ถŒ์žฅ] append-only + ํ•ด์‹œ ์ฒด์ธ: ๊ฐ ๊ฑฐ๋ž˜์— ์ด์ „ ๊ฑฐ๋ž˜ ํ•ด์‹œ๋ฅผ ๋ฌถ์–ด ์ €์žฅํ•˜๋ฉด
#        ๋ˆ„๊ตฐ๊ฐ€ DELETE/UPDATE ํ•˜๋Š” ์ˆœ๊ฐ„ ์ฒด์ธ์ด ๋Š์–ด์ ธ '์‚ญ์ œ ์‚ฌ์‹ค'์ด ๋“œ๋Ÿฌ๋‚œ๋‹ค.
chain = b""
for ref, amount in rows:
    chain = hashlib.sha256(chain + ("%s|%s" % (ref, amount)).encode()).digest()
print("[ํƒ์ง€4] ํ•ด์‹œ ์ฒด์ธ ํ—ค๋“œ:", chain.hex()[:16], "โ†’ ์™ธ๋ถ€(์ƒ๋Œ€์€ํ–‰ยท๊ฐ์‚ฌ๊ธฐ๊ด€)์— ํ†ต๋ณดํ•œ ๊ฐ’๊ณผ ๋Œ€์กฐ")

print("\nโ†’ ๊ตํ›ˆ: ์ด ์‚ฌ๊ฑด์—์„œ ์ž๊ธˆ์„ ๋ง‰์€ ๊ฒƒ์€ ๋‚ด๋ถ€ ์‹œ์Šคํ…œ์ด ์•„๋‹ˆ๋ผ")
print("  ์ค‘๊ณ„์€ํ–‰ ์ง์›์˜ ์œก์•ˆ(์˜คํƒ€ ํ™•์ธ)์ด์—ˆ๋‹ค. ๋Œ€์‚ฌ ์ž๋™ํ™”๋Š” '์žˆ์œผ๋ฉด ์ข‹์€ ๊ฒƒ'์ด ์•„๋‹ˆ๋ผ")
print("  '์ด ๊ณต๊ฒฉ์„ ์žก๋Š” ์œ ์ผํ•œ ๊ณ„์ธต'์ด๋‹ค.")

6. ๋ฐฉ์–ด ๋ฐฉ๋ฒ•/๊ตํ›ˆ โ€” ์–ด๋–ป๊ฒŒ ๋ง‰์•˜๊ณ , ๋ฌด์—‡์„ ๋ฐฐ์šฐ๋‚˜

์ถœ์ฒ˜

ํ•จ๊ป˜ ์ฝ๊ธฐ: ๐Ÿฆ… SolarWinds SUNBURST (2020) โ€” '์ •ํ’ˆ ์„œ๋ช…'์„ ์•…์šฉํ•œ ๊ณต๊ธ‰๋ง ๋ฐฑ๋„์–ด ยท โ˜ ๏ธ NotPetya (2017) โ€” M.E.Doc ๊ณต๊ธ‰๋ง์—์„œ ์‹œ์ž‘๋œ ํŒŒ๊ดดํ˜• ์™€์ดํผ ยท โš›๏ธ Stuxnet (2010) โ€” ๋ฌผ๋ฆฌ ์„ธ๊ณ„๋ฅผ ๊ฒจ๋ƒฅํ•œ ์ตœ์ดˆ์˜ ์‚ฌ์ด๋ฒ„ ๋ฌด๊ธฐ

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