โš›๏ธ๐Ÿ’€ TRITON / TRISIS (2017) โ€” '์•ˆ์ „(Safety)' ๊ทธ ์ž์ฒด๋ฅผ ๊ฒจ๋ƒฅํ•œ ์ตœ์ดˆ์˜ ์•…์„ฑ์ฝ”๋“œ: ์Šˆ๋‚˜์ด๋” ํŠธ๋ผ์ด์ฝ˜ SIS์™€ TriStation ํ”„๋กœํ† ์ฝœ

2026-10-15 ยท Malware Deep-Dive (ICS/OT ยท Safety Instrumented System ยท Schneider Electric Triconex ยท TriStation ยท Xenotime/CNIIHM)
๊ธฐ์ˆ  ์ •๋ณด
  • ์•…์„ฑ์ฝ”๋“œ๋ช…: TRITON (๋ณ„์นญ TRISIS ยท HatMan) โ€” FireEye๊ฐ€ TRITON, Dragos๊ฐ€ TRISIS, ๋ฏธ CISA์˜ ์•…์„ฑ์ฝ”๋“œ ๋ถ„์„๋ณด๊ณ ์„œ(MAR)๊ฐ€ HatMan์ด๋ผ๋Š” ์ด๋ฆ„์„ ๋ถ™์˜€๋‹ค
  • ๋“ฑ์žฅ: ์ตœ์ดˆ ํ”์  2017๋…„ 6์›”(๊ณต์žฅ ๋น„์ •์ƒ ์ •์ง€) โ†’ 8์›” ๋‘ ๋ฒˆ์งธ ์ •์ง€ ํ›„ ์กฐ์‚ฌ โ†’ 2017๋…„ 12์›” ๋Œ€์™ธ ๊ณต๊ฐœ(FireEyeยทDragos)
  • ํ‘œ์ : ์Šˆ๋‚˜์ด๋” ์ผ๋ ‰ํŠธ๋ฆญ Triconex ์•ˆ์ „๊ณ„์žฅ์‹œ์Šคํ…œ(SIS)(Tricon ๊ณ„์—ด ์ปจํŠธ๋กค๋Ÿฌ) + ์ด๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์—”์ง€๋‹ˆ์–ด๋ง ์›Œํฌ์Šคํ…Œ์ด์…˜(TriStation 1131 ์†Œํ”„ํŠธ์›จ์–ด๊ฐ€ ์„ค์น˜๋œ Windows PC)
  • ํ”ผํ•ด ํ˜„์žฅ: ์‚ฌ์šฐ๋””์•„๋ผ๋น„์•„์˜ ์„์œ ํ™”ํ•™ ๊ณต์žฅ 1๊ณณ์œผ๋กœ ์ง€๋ชฉ(๊ณต์‹ ํ™•์ธ์€ ์ œํ•œ์ )
  • ๊ณต๊ฒฉ ์ฃผ์ฒด: Xenotime(Dragos ๋ช…๋ช…) โ€” FireEye๋Š” ๋Ÿฌ์‹œ์•„ CNIIHM(์ค‘์•™ํ™”ํ•™๊ธฐ๊ณ„๊ณผํ•™์—ฐ๊ตฌ์†Œ) ์—ฐ๊ณ„ ๊ฐ€๋Šฅ์„ฑ์„ ์ œ๊ธฐํ–ˆ๊ณ , 2020-10-23 ๋ฏธ ์žฌ๋ฌด๋ถ€ OFAC๊ฐ€ CNIIHM์„ ์ œ์žฌ ๋Œ€์ƒ์œผ๋กœ ์ง€์ •ํ•˜๋ฉฐ TRITON ๊ฐœ๋ฐœ์„ ๊ทผ๊ฑฐ๋กœ ๋“ค์—ˆ๋‹ค
  • ์‚ฌ์šฉํ•œ ๊ฒฝ๋กœ: ์—”์ง€๋‹ˆ์–ด๋ง ์›Œํฌ์Šคํ…Œ์ด์…˜ โ†” SIS ์ปจํŠธ๋กค๋Ÿฌ ์‚ฌ์ด์˜ ์—”์ง€๋‹ˆ์–ด๋ง ํ”„๋กœํ† ์ฝœ TriStation(UDP 1502 ๊ณ„์—ด) โ€” IT ๋„คํŠธ์›Œํฌ์—์„œ OT๋กœ ๋„˜์–ด์˜จ ๋’ค '์ •์ƒ ๊ด€๋ฆฌ ํŠธ๋ž˜ํ”ฝ'์œผ๋กœ ์œ„์žฅ
  • ๋ณด๊ณ ๋œ ๋„๊ตฌ ๊ตฌ์„ฑ: ๋Ÿฐ์ฒ˜ trilog.exe + ํŒŒ์ด์ฌ ํ”„๋ ˆ์ž„์›Œํฌ ์••์ถ•๋ณธ(๋‚ด๋ถ€ tslib ๋ชจ๋“ˆ) + TsLow.dll/TsHi.dll(TriStation ํ†ต์‹  ๊ณ„์ธต) + ์ปจํŠธ๋กค๋Ÿฌ์— ๊ธฐ๋กํ•˜๋Š” ํŽ˜์ด๋กœ๋“œ(inject.bin ๊ณ„์—ด)
  • ATT&CK for ICS ๋งคํ•‘: Impair Process Control(T0836), Unauthorized Command Message, Program Download/Modify ๋“ฑ โ€” ์ฆ‰ '๋ฐ์ดํ„ฐ ํƒˆ์ทจ'๊ฐ€ ์•„๋‹ˆ๋ผ ์ œ์–ดยท์•ˆ์ „ ๋กœ์ง ์ž์ฒด์˜ ๋ณ€์กฐ
  • ์˜์˜: ์‚ฐ์—… ์•ˆ์ „ ์‹œ์Šคํ…œ(SIS)์„ ๋ชฉํ‘œ๋กœ ๋งŒ๋“ค์–ด์ง„ ์ตœ์ดˆ์˜ ๊ณต๊ฐœ ์‚ฌ๋ก€. ๋ฐ์ดํ„ฐ๋„ ๋ˆ๋„ ์•„๋‹Œ ์ธ๋ช…ยทํ™˜๊ฒฝ์„ ์ง€ํ‚ค๋Š” ๋งˆ์ง€๋ง‰ ๋ฐฉ์–ด์„ ์„ ๊ณต๊ฒฉ ๋Œ€์ƒ์œผ๋กœ ์‚ผ์•˜๋‹ค๋Š” ์ ์—์„œ ICS ์•…์„ฑ์ฝ”๋“œ ๊ณ„๋ณด์˜ ๋ถ„๊ธฐ์ 

1. ๋ฐฐ๊ฒฝ/์—ญ์‚ฌ โ€” 'ํŒŒ๊ดด'์—์„œ '์•ˆ์ „ ๋ฌด๋ ฅํ™”'๋กœ ๋„˜์–ด๊ฐ„ 2017๋…„

ICS(์‚ฐ์—…์ œ์–ด์‹œ์Šคํ…œ) ์•…์„ฑ์ฝ”๋“œ๋Š” ์„ธ๋Œ€๋งˆ๋‹ค '๋ฌด์—‡์„ ๋…ธ๋ฆฌ๋Š”๊ฐ€'๊ฐ€ ๋‹ฌ๋ผ์กŒ๋‹ค. 2010๋…„ Stuxnet์€ ์›์‹ฌ๋ถ„๋ฆฌ๊ธฐ๋ฅผ ๋ง๊ฐ€๋œจ๋ฆฌ๊ธฐ ์œ„ํ•ด PLC ๋กœ์ง์„ ์กฐ์ž‘ํ–ˆ๋‹ค. 2015~2016๋…„ ์šฐํฌ๋ผ์ด๋‚˜ ์ „๋ ฅ๋ง ๊ณต๊ฒฉ(BlackEnergy ยท Industroyer)์€ ๊ฐœํ๊ธฐ ๋ช…๋ น์„ ์ง์ ‘ ๋งํ•˜๋Š” ํ”„๋กœํ† ์ฝœ ์•…์„ฑ์ฝ”๋“œ์˜€๋‹ค. ๊ทธ๋ฆฌ๊ณ  2017๋…„, ์„ธ ๋ฒˆ์งธ ์„ธ๋Œ€์ธ TRITON์€ ๋ชฉํ‘œ๋ฅผ ๋ฐ”๊ฟจ๋‹ค. ํŒŒ๊ดด๊ฐ€ ์•„๋‹ˆ๋ผ, ํŒŒ๊ดด๋ฅผ ๋ง‰์•„์ฃผ๋Š” ์•ˆ์ „ ์‹œ์Šคํ…œ์„ ๊บผ๋ฒ„๋ฆฌ๋Š” ๊ฒƒ์ด๋‹ค.

๋ฌด๋Œ€๋Š” ์„์œ ํ™”ํ•™ ๊ณต์žฅ์ด์—ˆ๋‹ค. ์ด ๊ณต์žฅ์—๋Š” SIS(Safety Instrumented System, ์•ˆ์ „๊ณ„์žฅ์‹œ์Šคํ…œ)๊ฐ€ ์žˆ์—ˆ๋‹ค. SIS๋Š” ์••๋ ฅยท์˜จ๋„ยท๋ฐธ๋ธŒ ์ƒํƒœ๊ฐ€ ์œ„ํ—˜ ์ž„๊ณ„์น˜๋ฅผ ๋„˜์œผ๋ฉด ์‚ฌ๋žŒ์ด ๊ฐœ์ž…ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ ๊ณต์ •์„ ์ •์ง€์‹œ์ผœ ํญ๋ฐœยทํ™”์žฌยท์ธ๋ช… ํ”ผํ•ด๋ฅผ ๋ง‰๋Š” '๋งˆ์ง€๋ง‰ ์•ˆ์ „์žฅ์น˜'๋‹ค. ์—ฌ๊ธฐ์— ์“ฐ์ธ ์žฅ๋น„๊ฐ€ ์Šˆ๋‚˜์ด๋” ์ผ๋ ‰ํŠธ๋ฆญ์˜ Triconex(Tricon) ๊ณ„์—ด ์ปจํŠธ๋กค๋Ÿฌ์˜€๋‹ค. Tricon์€ TMR(Triple Modular Redundant) ๊ตฌ์กฐ๋กœ, ์„ธ ๊ฐœ์˜ ๋…๋ฆฝ ์ฑ„๋„์ด ํˆฌํ‘œ(voting)๋กœ ๊ฒฐ๊ณผ๋ฅผ ๊ฒฐ์ •ํ•ด ํ•˜๋‚˜๊ฐ€ ๊ณ ์žฅ ๋‚˜๋„ ์•ˆ์ „ ๊ธฐ๋Šฅ์„ ์œ ์ง€ํ•˜๋„๋ก ์„ค๊ณ„๋œ ์žฅ๋น„๋‹ค.

2017๋…„ 6์›”, ๊ณต์žฅ์ด ์˜ˆ๊ธฐ์น˜ ์•Š๊ฒŒ ๋ฉˆ์ท„๋‹ค. ๋‹น์‹œ์—” ์•ˆ์ „ ์‹œ์Šคํ…œ์˜ ๊ธฐ๊ณ„์  ๊ฒฐํ•จ์œผ๋กœ ์—ฌ๊ฒจ์กŒ๋‹ค. 8์›” ๋‘ ๋ฒˆ์งธ ์ •์ง€๊ฐ€ ์ผ์–ด๋‚˜์ž ์กฐ์‚ฌ๊ฐ€ ์‹œ์ž‘๋๊ณ , ์—”์ง€๋‹ˆ์–ด๋ง ์›Œํฌ์Šคํ…Œ์ด์…˜์—์„œ ์ •์ฒด๋ถˆ๋ช…์˜ ์ฝ”๋“œ๊ฐ€ ๋ฐœ๊ฒฌ๋๋‹ค. 12์›” FireEye์™€ Dragos๊ฐ€ ์ด๋ฅผ TRITON/TRISIS๋กœ ๊ณต๊ฐœํ•˜๋ฉด์„œ ์‚ฌ๊ฑด์˜ ์„ฑ๊ฒฉ์ด ๋“œ๋Ÿฌ๋‚ฌ๋‹ค. ๊ณต๊ฒฉ์ž๋Š” ๊ณต์ •์„ ์กฐ์ž‘ํ•˜๋ ค ํ•œ ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, SIS๊ฐ€ ์•ˆ์ „ ์ •์ง€๋ฅผ ๊ฑธ์ง€ ๋ชปํ•˜๋„๋ก ๋งŒ๋“ค๋ ค ํ–ˆ๋‹ค. ์ฆ‰ '์•ˆ์ „'์„ ๋„๊ณ , ๊ณต์ •์„ ์œ„ํ—˜ ์ƒํƒœ๋กœ ๋ชฐ์•„๋„ฃ์œผ๋ ค ํ•œ ๊ฒƒ์ด๋‹ค.

์ด ์‚ฌ๊ฑด์ด ์ง€๊ธˆ๋„ ํšŒ์ž๋˜๋Š” ์ด์œ ๋Š” ๋‹จ์ˆœํ•˜๋‹ค. TRITON์€ ์ธ๋ช… ํ”ผํ•ด๋ฅผ ๋ชฉ์ ์œผ๋กœ ์„ค๊ณ„๋œ ๊ฒƒ์œผ๋กœ ํ‰๊ฐ€๋˜๋Š” ์ตœ์ดˆ์˜ ๊ณต๊ฐœ ์•…์„ฑ์ฝ”๋“œ์ด๋ฉฐ, ๋™์‹œ์— IT ๋ณด์•ˆ์˜ ์‚ฌ๊ณ ๋ฐฉ์‹์ด OT(์šด์˜๊ธฐ์ˆ ) ํ˜„์žฅ์—์„œ ๊ทธ๋Œ€๋กœ ํ†ตํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊ฐ€์žฅ ๋ผˆ์•„ํ”„๊ฒŒ ๋ณด์—ฌ์ค€ ์‚ฌ๋ก€๋‹ค. ์‚ฌ๊ณ ๋Š” ๋ฒค๋”์˜ ํŒจ์น˜, ๊ตญ๊ฐ€ ์ฐจ์›์˜ ์ œ์žฌ, ๊ทœ์ œยทํ‘œ์ค€ ๊ฐœ์ •์œผ๋กœ ์ด์–ด์กŒ๋‹ค.

2. ๊ฐ์—ผยท์ „ํŒŒ ๊ฒฝ๋กœ โ€” ์ž๊ธฐ๋ณต์ œ๊ฐ€ ์•„๋‹ˆ๋ผ '์ •๋ฐ€ ์นจํˆฌ'

TRITON์€ ์›œ์ด ์•„๋‹ˆ๋‹ค. Morris WormยทCode RedยทBlasterยทSQL Slammer์ฒ˜๋Ÿผ ์Šค์Šค๋กœ ํผ์ง€์ง€ ์•Š๋Š”๋‹ค. ๋Œ€์‹  ํ•œ ํ˜„์žฅ์„ ์ •๋ฐ€ํ•˜๊ฒŒ ์žฅ์•…ํ•˜๊ธฐ ์œ„ํ•œ ํ‘œ์ ํ˜•(ํƒ€๊นƒํ˜•) ํ”„๋ ˆ์ž„์›Œํฌ๋‹ค. ๊ทธ๋ž˜์„œ ์ „ํŒŒ ๊ฒฝ๋กœ๋Š” '์ธํ„ฐ๋„ท ์ „์ฒด'๊ฐ€ ์•„๋‹ˆ๋ผ ํ•œ ๊ณต์žฅ์˜ ๋„คํŠธ์›Œํฌ ์•ˆ์—์„œ ์™„์„ฑ๋œ๋‹ค.

์ •๋ฆฌํ•˜๋ฉด TRITON์˜ ๊ฐ์—ผ ๊ฒฝ๋กœ๋Š” '์—์–ด๊ฐญ์˜ ํ™˜์ƒ โ†’ ์œ ์ง€๋ณด์ˆ˜์šฉ ๋‹ค๋ฆฌ โ†’ ์‹ ๋ขฐ๋œ EWS โ†’ ์•ˆ์ „ ์ปจํŠธ๋กค๋Ÿฌ'๋ผ๋Š” 4๋‹จ ์‚ฌ์Šฌ์ด๋‹ค. ํ”ํ•œ ๋žœ์„ฌ์›จ์–ด ์‚ฌ์Šฌ๊ณผ ๊ฒ‰๋ชจ์Šต์€ ๋น„์Šทํ•˜์ง€๋งŒ, ๋งˆ์ง€๋ง‰ ํ™”์‚ดํ‘œ์˜ ๋„์ฐฉ์ง€๊ฐ€ ์‚ฌ๋žŒ์˜ ๋ชฉ์ˆจ์„ ์ง€ํ‚ค๋Š” ์žฅ์น˜๋ผ๋Š” ์ ์ด ๊ทผ๋ณธ์ ์œผ๋กœ ๋‹ค๋ฅด๋‹ค.

3. ๋™์ž‘ ์›๋ฆฌ ์ƒ์„ธ ๋ถ„์„ โ€” ๋‘ ๊ฐœ์˜ ์„ธ๊ณ„, ํ•˜๋‚˜์˜ ํ”„๋ ˆ์ž„์›Œํฌ

TRITON์˜ ๊ตฌ์กฐ๋ฅผ ์ดํ•ดํ•˜๋Š” ์—ด์‡ ๋Š” '์•…์„ฑ์ฝ”๋“œ๊ฐ€ ๋‘ ๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์„ธ๊ณ„์—์„œ ๋™์‹œ์— ์‚ด์•„์•ผ ํ•œ๋‹ค'๋Š” ์ ์ด๋‹ค. ํ•˜๋‚˜๋Š” x86 Windows ์„ธ๊ณ„(EWS), ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ „์šฉ ์‹ค์‹œ๊ฐ„ ์ปจํŠธ๋กค๋Ÿฌ ์„ธ๊ณ„(Tricon)๋‹ค. ๊ทธ๋ž˜์„œ TRITON์€ ํ•˜๋‚˜์˜ ์‹คํ–‰ ํŒŒ์ผ์ด ์•„๋‹ˆ๋ผ ์—ญํ• ์ด ๋‚˜๋‰œ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ๊ตฌ์„ฑ๋๋‹ค๋Š” ๋ถ„์„์ด ์ง€๋ฐฐ์ ์ด๋‹ค.

[ ๊ณต์žฅ IT/OT ๋„คํŠธ์›Œํฌ ]
        โ”‚  (โ‘  ์ดˆ๊ธฐ ์นจํˆฌ ยท ITโ†’OT ํ”ผ๋ฒ—)
        โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ ์—”์ง€๋‹ˆ์–ด๋ง ์›Œํฌ์Šคํ…Œ์ด์…˜ (Windows + TriStation 1131) โ”‚
โ”‚                                              โ”‚
โ”‚  trilog.exe   โ† ๋Ÿฐ์ฒ˜/๋กœ๋”                     โ”‚
โ”‚  library.zip  โ† ํŒŒ์ด์ฌ ํ”„๋ ˆ์ž„์›Œํฌ              โ”‚
โ”‚    โ”” tslib    โ† TriStation ํ”„๋กœํ† ์ฝœ์„ ๊ตฌํ˜„ํ•œ ๊ณ„์ธต โ”‚
โ”‚  TsLow.dll / TsHi.dll โ† ์ €์ˆ˜์ค€ ํ†ต์‹  DLL         โ”‚
โ”‚  inject.bin   โ† ์ปจํŠธ๋กค๋Ÿฌ์— ๋ฐ€์–ด ๋„ฃ์„ ํŽ˜์ด๋กœ๋“œ     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
        โ”‚  (โ‘ก TriStation ํ”„๋กœํ† ์ฝœ, UDP 1502 ๊ณ„์—ด)
        โ”‚     โ†’ ์ •ํ’ˆ ์—”์ง€๋‹ˆ์–ด๋ง ํˆด๊ณผ ๊ตฌ๋ณ„์ด ์–ด๋ ค์›€
        โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Triconex SIS ์ปจํŠธ๋กค๋Ÿฌ (TMR: 3์ค‘ ์ฑ„๋„ + ํˆฌํ‘œ)    โ”‚
โ”‚  ยท ํ”„๋กœ๊ทธ๋žจ/ํŽŒ์›จ์–ด ์ •์ฐฐ                        โ”‚
โ”‚  ยท ๋ฉ”๋ชจ๋ฆฌ ์ฝ๊ธฐ / ์“ฐ๊ธฐ                         โ”‚
โ”‚  ยท ํ‚ค์Šค์œ„์น˜ ์ƒํƒœ ์กฐํšŒยท์กฐ์ž‘                     โ”‚
โ”‚  ยท โ˜… ์•ˆ์ „ ํŠธ๋ฆฝ(trip) ๋กœ์ง์„ ๋ฌด๋ ฅํ™”ํ•˜๋ ค ์‹œ๋„     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
   โœ— ์‹คํŒจ โ†’ ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ์•ˆ์ „ ์ •์ง€/์žฅ์•  ์ƒํƒœ๋กœ ์ง„์ž…
        โ†’ ๊ณต์ • ์ •์ง€(2017-06 / 2017-08) โ†’ ํ”ผํ•ด์ž ์ธก ์กฐ์‚ฌ ์‹œ์ž‘

3-1. TriStation์€ '์—”์ง€๋‹ˆ์–ด๋ง ์–ธ์–ด'๋‹ค. TriStation ํ”„๋กœํ† ์ฝœ์€ ์—”์ง€๋‹ˆ์–ด๊ฐ€ ๋…ธํŠธ๋ถ์—์„œ SIS ์ปจํŠธ๋กค๋Ÿฌ์— ์ ‘์†ํ•ด ํ”„๋กœ๊ทธ๋žจ์„ ์˜ฌ๋ฆฌ๊ณ  ๋‚ด๋ฆฌ๊ณ (๋‹ค์šด๋กœ๋“œ/์—…๋กœ๋“œ), ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ  ์“ฐ๊ณ , ์ƒํƒœ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•œ ์‚ฌ๋‚ด ํ‘œ์ค€ ๋Œ€ํ™”๋ฒ•์ด๋‹ค. ์ฆ‰ ํƒœ์ƒ์ ์œผ๋กœ ๊ฐ•๋ ฅํ•œ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ํ†ต๋กœ๋‹ค. TRITON์€ ์ด ๋Œ€ํ™”๋ฒ•์„ ํŒŒ์ด์ฌ์œผ๋กœ ๋‹ค์‹œ ๊ตฌํ˜„ํ•ด, ์ปจํŠธ๋กค๋Ÿฌ์—๊ฒŒ๋Š” '์ •ํ’ˆ ํˆด์ด ์ ‘์†ํ•œ ๊ฒƒ'์ฒ˜๋Ÿผ ๋ณด์ด๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

3-2. ์™œ '๋ฉ”๋ชจ๋ฆฌ ์“ฐ๊ธฐ + ํŠธ๋ฆฝ ๋ฌด๋ ฅํ™”'์ธ๊ฐ€. SIS๊ฐ€ ์œ„ํ—˜์„ ๊ฐ์ง€ํ•˜๋ฉด ์ปจํŠธ๋กค๋Ÿฌ๋Š” ํŠธ๋ฆฝ(trip)์„ ๊ฑธ์–ด ๊ณต์ •์„ ์•ˆ์ „ ์ •์ง€์‹œํ‚จ๋‹ค. ๊ณต๊ฒฉ์ž๊ฐ€ ๋…ธ๋ฆฐ ๊ฒƒ์€ ํŠธ๋ฆฝ์ด ๊ฑธ๋ฆฌ์ง€ ์•Š๊ฒŒ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด๋ฅผ ์œ„ํ•ด ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”๋ชจ๋ฆฌ(์•ˆ์ „ ๋กœ์ง์ด ์‚ฌ์šฉํ•˜๋Š” ์ƒํƒœยท๋ณ€์ˆ˜ ์˜์—ญ)์— ๊ฐ’์„ ์“ฐ๊ฑฐ๋‚˜ ํŽ˜์ด๋กœ๋“œ๋ฅผ ์ฃผ์ž…ํ•ด, ์œ„ํ—˜ ์ž„๊ณ„์น˜๋ฅผ ๋„˜์–ด๋„ ์ •์ง€์‹œํ‚ค์ง€ ์•Š๋Š” ์ƒํƒœ๋ฅผ ๋งŒ๋“ค๋ ค ํ–ˆ๋‹ค. ์ด๊ฒƒ์ด ATT&CK for ICS์˜ Impair Process Control(T0836) ์ด๋‹ค. ๋ฐ์ดํ„ฐ๋ฅผ ํ›”์น˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ฌผ๋ฆฌ ๊ณต์ •์ด '์œ„ํ—˜ํ•ด๋„ ๋ฉˆ์ถ”์ง€ ์•Š๊ฒŒ' ๋งŒ๋“œ๋Š” ๊ฒƒ์ด๋‹ค.

3-3. ๋ฌผ๋ฆฌ ๊ณ„์ธต๊ณผ์˜ ์ถฉ๋Œ. ์•„๋ฌด๋ฆฌ ํ”„๋กœํ† ์ฝœ์„ ์™„๋ฒฝํžˆ ํ‰๋‚ด ๋‚ด๋„, ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋ฌผ๋ฆฌ ํ‚ค์Šค์œ„์น˜๊ฐ€ PROGRAM์ด ์•„๋‹ˆ๋ฉด ํ”„๋กœ๊ทธ๋žจ ๋ณ€๊ฒฝ์€ ๊ฑฐ๋ถ€๋œ๋‹ค. ๊ทธ๋ž˜์„œ TRITON์—๋Š” ํ‚ค์Šค์œ„์น˜ ์ƒํƒœ๋ฅผ ์กฐํšŒยท์กฐ์ž‘ํ•˜๊ณ , ํ•„์š”ํ•˜๋ฉด ์•ˆ์ „ ๊ธฐ๋Šฅ์„ ์šฐํšŒํ•˜๋ ค๋Š” ๋กœ์ง์ด ํฌํ•จ๋œ ๊ฒƒ์œผ๋กœ ๋ถ„์„๋๋‹ค. ๊ฒฐ๊ตญ ์ด ์‚ฌ๊ณ ์˜ ์„ฑ๋ฆฝ ์กฐ๊ฑด์€ ์†Œํ”„ํŠธ์›จ์–ด ์ทจ์•ฝ์  + ๋ฌผ๋ฆฌ ํ†ต์ œ ์‹คํŒจ์˜ ๊ฒฐํ•ฉ์ด์—ˆ๋‹ค.

3-4. '์‹ค์ˆ˜'๊ฐ€ ๋“œ๋Ÿฌ๋‚ธ ๊ณต๊ฒฉ. ๊ณต๊ฒฉ์ž์˜ ์ฃผ์ž… ์‹œ๋„๋Š” ์ตœ์ข…์ ์œผ๋กœ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์•ˆ์ „ ์ •์ง€/์žฅ์•  ์ƒํƒœ๋กœ ๋ชฐ์•„๋„ฃ์—ˆ๋‹ค. ์˜๋„๋Š” '๋ฉˆ์ถ”์ง€ ์•Š๊ฒŒ' ํ•˜๋Š” ๊ฒƒ์ด์—ˆ์ง€๋งŒ, ๊ฒฐ๊ณผ๋Š” ๊ณต์žฅ์ด ๋ฉˆ์ถ”๋Š” ๊ฒƒ์ด์—ˆ๋‹ค. ์ด ์˜ˆ๊ธฐ์น˜ ๋ชปํ•œ ์ •์ง€๊ฐ€ ์•„๋‹ˆ์—ˆ๋‹ค๋ฉด ๊ณต๊ฒฉ์€ ํ›จ์”ฌ ์˜ค๋ž˜ ์ˆจ์–ด ์žˆ์—ˆ์„ ์ˆ˜ ์žˆ๋‹ค. ์ฆ‰ ์ด ์‚ฌ๊ฑด์„ ์„ธ์ƒ์— ์•Œ๋ฆฐ ๊ฒƒ์€ ๋ฐฉ์–ด ๊ธฐ์ˆ ์ด ์•„๋‹ˆ๋ผ ๊ณต๊ฒฉ์ž์˜ ์‹คํŒจ์˜€๋‹ค๋Š” ์ ์ด ๋ƒ‰์ •ํ•œ ๊ตํ›ˆ์ด๋‹ค.

3-5. ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ '๋ฌด๊ธฐ'๋ผ๋Š” ์„ฑ๊ฒฉ. TRITON์€ ํ•œ ํ˜„์žฅ์—์„œ๋งŒ ์“ฐ์ด๋„๋ก ๋งŒ๋“ค์–ด์ง„ ์ผํšŒ์šฉ ๋„๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ SIS/ICS๋กœ ์ด์‹ ๊ฐ€๋Šฅํ•œ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํ‰๊ฐ€๋๋‹ค. ์‹ค์ œ๋กœ ์ดํ›„ ์•ˆ์ „ ์‹œ์Šคํ…œ์„ ๋…ธ๋ฆฌ๋Š” ๊ณต๊ฒฉ ํˆดํ‚ท(๋ชจ๋“ˆํ˜• ICS ๊ณต๊ฒฉ ํ”„๋ ˆ์ž„์›Œํฌ)์˜ ๋“ฑ์žฅ ์ •ํ™ฉ์ด ๋ณด๊ณ ๋˜๋ฉด์„œ, 'SIS๋ฅผ ๊ฒจ๋ƒฅํ•˜๋Š” ์œ„ํ˜‘'์€ ์ผํšŒ์„ฑ ์‚ฌ๊ฑด์ด ์•„๋‹ˆ๋ผ ํ•˜๋‚˜์˜ ๊ณ„๋ณด๊ฐ€ ๋๋‹ค.

4. ํ”ผํ•ด ๊ทœ๋ชจ/์˜ํ–ฅ โ€” ์ˆซ์ž๋ณด๋‹ค ๋ฌด๊ฑฐ์šด ๊ฒƒ

์ •๋ฆฌํ•˜๋ฉด TRITON์˜ '์ˆซ์ž'๋Š” ์ž‘๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ '์˜๋ฏธ'๋Š” ์•…์„ฑ์ฝ”๋“œ ์—ญ์‚ฌ์—์„œ ๊ฐ€์žฅ ๋ฌด๊ฑฐ์šด ์ถ•์— ๋“ ๋‹ค. ํ”ผํ•ด ๊ทœ๋ชจ๊ฐ€ ์•„๋‹ˆ๋ผ ํ‘œ์ ์˜ ์„ฑ์งˆ์ด ์ด ์‚ฌ๊ฑด์„ ์œ ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค.

5. PoC (๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ 4์ข…)

์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋ชจ๋‘ ๊ต์œก์šฉ ๋ชจ์˜(mock) ์‹ค์Šต์ด๋‹ค. ์‹ค์ œ TriStation ํ”„๋กœํ† ์ฝœ์˜ ๋ช…๋ น ์ฝ”๋“œยท์ •์ˆ˜ยทํŒจํ‚ท ํ˜•์‹์€ ์“ฐ์ง€ ์•Š์•˜๊ณ , ์ปจํŠธ๋กค๋Ÿฌ์— ํ”„๋กœ๊ทธ๋žจ์„ ๋‚ด๋ ค๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ๋„, ์‹ค์ œ ์ทจ์•ฝ์ ์„ ๊ฒจ๋ƒฅํ•˜๋Š” ์ต์Šคํ”Œ๋กœ์ž‡๋„ ์—†๋‹ค. ๋ชฉ์ ์€ "์•ˆ์ „ ํŠธ๋ฆฝ์„ ๋„๋Š” ๋ช…๋ น์ด ๊ตฌ์กฐ์ ์œผ๋กœ ๋ฌด์—‡์„ ํ•˜๋Š”๊ฐ€"๋ฅผ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋ฟ์ด๋‹ค. ๋ชจ๋“  ์‹ค์Šต์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฐ€์ƒ ๋žฉ(/tmp/lab)์—์„œ, ์—ฌ๋Ÿฌ๋ถ„์ด ์ง์ ‘ ๋งŒ๋“  ๋ชจ์˜ ์ปจํŠธ๋กค๋Ÿฌ๋ฅผ ์ƒ๋Œ€๋กœ๋งŒ ์ˆ˜ํ–‰ํ•œ๋‹ค. ์‹ค์ œ ์„ค๋น„ยทํƒ€์ธ์˜ ์‹œ์Šคํ…œ์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ ์‹คํ–‰์€ ๋ถˆ๋ฒ•์ด๋‹ค.

PoC 1 โ€” '๊ฐ€์ƒ SIS ์ปจํŠธ๋กค๋Ÿฌ' ๋งŒ๋“ค๊ธฐ. ์•ˆ์ „ ์ •์ง€(trip)๊ฐ€ ๋ฌด์—‡์„ ํŒ๋‹จํ•˜๋Š”์ง€๋ถ€ํ„ฐ ์žฌํ˜„ํ•œ๋‹ค.

# PoC 1: ๊ฐ€์ƒ SIS ์ปจํŠธ๋กค๋Ÿฌ โ€” ์•ˆ์ „ ํŠธ๋ฆฝ์ด '์™œ' ๊ฑธ๋ฆฌ๋Š”์ง€ ์ดํ•ดํ•˜๋Š” ๋ชจ์˜ ์ฝ”๋“œ
# ์‹ค์ œ Triconex/TriStation๊ณผ ๋ฌด๊ด€ํ•œ ์ˆœ์ˆ˜ ๊ต์œก์šฉ ์‹œ๋ฎฌ๋ ˆ์ดํ„ฐ. ๋กœ์ปฌ ์ถœ๋ ฅ๋งŒ ํ•œ๋‹ค.
TRIP_LIMIT   = 100.0   # ์œ„ํ—˜ ์ž„๊ณ„์น˜ (์ƒ์ง•๊ฐ’)
TRIP_ENABLED = True    # ์•ˆ์ „ ๋กœ์ง ์‚ฌ์šฉ ์—ฌ๋ถ€ (์ •์ƒ ์ƒํƒœ)

def sis_vote(readings):
    """TMR(3์ค‘ ์ฑ„๋„) ํˆฌํ‘œ๋ฅผ ํ‰๋‚ด: ๋‹ค์ˆ˜๊ฒฐ๋กœ ์œ„ํ—˜ ์—ฌ๋ถ€๋ฅผ ๊ฒฐ์ •ํ•œ๋‹ค."""
    over = sum(1 for r in readings if r > TRIP_LIMIT)
    return over >= 2   # 3์ฑ„๋„ ์ค‘ 2์ฑ„๋„ ์ด์ƒ์ด ์ž„๊ณ„ ์ดˆ๊ณผ โ†’ ์œ„ํ—˜

def safety_scan(readings):
    global TRIP_ENABLED
    risk = sis_vote(readings)
    if not TRIP_ENABLED:
        return "[!] ์•ˆ์ „ ์ •์ง€ ๊ธฐ๋Šฅ์ด ๋น„ํ™œ์„ฑ ์ƒํƒœ โ†’ ์œ„ํ—˜ ์‹ ํ˜ธ๋ฅผ ๋ฌด์‹œํ•œ๋‹ค (์ด๊ฒƒ์ด ๊ณต๊ฒฉ์˜ ๋ชฉํ‘œ)"
    return "TRIP: ๊ณต์ • ์•ˆ์ „ ์ •์ง€" if risk else "์ •์ƒ ์šด์ „"

if __name__ == "__main__":
    print("[์ •์ƒ]", safety_scan([80.0, 82.0, 79.0]))
    print("[์œ„ํ—˜]", safety_scan([130.0, 128.0, 131.0]))

PoC 2 โ€” ์—”์ง€๋‹ˆ์–ด๋ง ํ”„๋ ˆ์ž„์˜ ๊ตฌ์กฐ ์ดํ•ด. ์‹ค์ œ ํ”„๋กœํ† ์ฝœ ๋Œ€์‹  ๊ฐ€์ƒ ๋ช…๋ น ์ฝ”๋“œ๋กœ ์š”์ฒญ/์‘๋‹ต์„ ํ‘œํ˜„ํ•ด, EWS๊ฐ€ ์ปจํŠธ๋กค๋Ÿฌ์—๊ฒŒ ๋ณด๋‚ด๋Š” ๋Œ€ํ™”์˜ ๋ผˆ๋Œ€๋ฅผ ๋ณธ๋‹ค.

# PoC 2: ์—”์ง€๋‹ˆ์–ด๋ง ์š”์ฒญ ํ”„๋ ˆ์ž„์˜ '๊ตฌ์กฐ'๋งŒ ์žฌํ˜„ (๊ฐ€์ƒ opcode โ€” ์‹ค์ œ ๊ฐ’๊ณผ ๋‹ค๋ฆ„)
# ์‹ค์ œ ์ต์Šคํ”Œ๋กœ์ž‡ ๋ฐ”์ดํŠธ/์‹ค์ œ TriStation ๋ช…๋ น ์ •์ˆ˜๋Š” ํฌํ•จํ•˜์ง€ ์•Š๋Š”๋‹ค.
FICTIONAL_OPCODES = {
    0x01: "READ_STATUS",      # ์ปจํŠธ๋กค๋Ÿฌ ์ƒํƒœ ์กฐํšŒ
    0x02: "READ_MEMORY",      # ๋ฉ”๋ชจ๋ฆฌ ์ฝ๊ธฐ
    0x03: "WRITE_MEMORY",     # ๋ฉ”๋ชจ๋ฆฌ ์“ฐ๊ธฐ  โ† ๊ณต๊ฒฉ์ด ์•…์šฉํ•˜๋Š” ์ง€์ 
    0x04: "GET_KEYSWITCH",    # ๋ฌผ๋ฆฌ ํ‚ค์Šค์œ„์น˜ ์ƒํƒœ
}

def build_request(op, payload=b""):
    """๊ฐ€์ƒ EWSโ†’SIS ์š”์ฒญ ํ”„๋ ˆ์ž„. op + ๊ธธ์ด + ํŽ˜์ด๋กœ๋“œ ๊ตฌ์กฐ๋งŒ ๋ณด์—ฌ์ค€๋‹ค."""
    if op not in FICTIONAL_OPCODES:
        raise ValueError("unknown op")
    frame = {"op": FICTIONAL_OPCODES[op], "len": len(payload), "data": payload}
    print(f"[EWS โ†’ SIS] {frame}")
    return frame

def controller_side(frame, keyswitch_program=False):
    """๋ชจ์˜ ์ปจํŠธ๋กค๋Ÿฌ: ์“ฐ๊ธฐ ์š”์ฒญ์€ ๋ฌผ๋ฆฌ ํ‚ค์Šค์œ„์น˜๊ฐ€ PROGRAM์ผ ๋•Œ๋งŒ ์ˆ˜์šฉํ•œ๋‹ค."""
    if frame["op"] == "WRITE_MEMORY" and not keyswitch_program:
        print("[SIS] ์“ฐ๊ธฐ ๊ฑฐ๋ถ€: ํ‚ค์Šค์œ„์น˜๊ฐ€ PROGRAM ์œ„์น˜๊ฐ€ ์•„๋‹˜ (๋ฌผ๋ฆฌ์  ์•ˆ์ „์žฅ์น˜)")
        return False
    print("[SIS] ์š”์ฒญ ์ˆ˜์šฉ")
    return True

if __name__ == "__main__":
    f = build_request(0x03, b"<mock-payload>")
    controller_side(f, keyswitch_program=False)   # ๊ธฐ๋ณธ์€ ๊ฑฐ๋ถ€ โ†’ ์ •์ƒ
    controller_side(f, keyswitch_program=True)    # ํ‚ค์Šค์œ„์น˜ ๋ฐฉ์น˜ ์‹œ ์ˆ˜์šฉ๋จ

PoC 3 โ€” 'ํŠธ๋ฆฝ ๋ฌด๋ ฅํ™”'๋ฅผ ๋ชจ์˜ ๋žฉ์—์„œ ์žฌํ˜„. PoC 1์˜ ๊ฐ€์ƒ ์ปจํŠธ๋กค๋Ÿฌ์— PoC 2์˜ ์“ฐ๊ธฐ ์š”์ฒญ์„ ์—ฐ๊ฒฐํ•ด, ์•ˆ์ „ ๋กœ์ง์ด ๊บผ์ง€๋Š” ๊ณผ์ •์„ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•œ๋‹ค. ์‹ค์ œ ์ปจํŠธ๋กค๋Ÿฌ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†ŒยทํŽ˜์ด๋กœ๋“œ๋Š” ์ผ์ ˆ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.

# PoC 3: ๋‘ ๋ชจ์˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์—ฐ๊ฒฐ โ€” '์•ˆ์ „ ํŠธ๋ฆฝ ๋ฌด๋ ฅํ™”'๊ฐ€ ๋ฌด์—‡์„ ๋ฐ”๊พธ๋Š”์ง€ ํ™•์ธ
# ๋Œ€์ƒ์€ ์˜ค์ง ๋กœ์ปฌ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๋ชจ์˜ ์ปจํŠธ๋กค๋Ÿฌ ๊ฐ์ฒด๋‹ค.
import importlib.util, sys

# (๊ต์œก์šฉ) PoC 1์˜ ๋ชจ๋“ˆ์„ sis.py ๋กœ ์ €์žฅํ•ด ๋‘์—ˆ๋‹ค๊ณ  ๊ฐ€์ •
spec = importlib.util.spec_from_file_location("sis", "/tmp/lab/sis.py")
sis = importlib.util.module_from_spec(spec); spec.loader.exec_module(sis)

def inject_disable_trip(target_module, authorized_keyswitch):
    """'์•ˆ์ „ ์ •์ง€ ๋น„ํ™œ์„ฑํ™”'๋ผ๋Š” ํšจ๊ณผ๋งŒ ์žฌํ˜„ํ•œ๋‹ค(์‹ค์ œ ์ธ์ ์…˜ ์•„๋‹˜)."""
    if not authorized_keyswitch:
        print("[!] ํ‚ค์Šค์œ„์น˜๊ฐ€ PROGRAM ์•„๋‹˜ โ†’ ์ฃผ์ž… ์‹คํŒจ (๋ฐฉ์–ด ์„ฑ๊ณต ์‹œ๋‚˜๋ฆฌ์˜ค)")
        return False
    target_module.TRIP_ENABLED = False
    print("[!] ๋ชจ์˜ ์ปจํŠธ๋กค๋Ÿฌ์˜ ์•ˆ์ „ ์ •์ง€ ๊ธฐ๋Šฅ์ด ๊บผ์กŒ๋‹ค โ†’ ์ดํ›„ ์œ„ํ—˜ ์‹ ํ˜ธ๋ฅผ ๋ฌด์‹œํ•œ๋‹ค")
    return True

if __name__ == "__main__":
    print("[์œ„ํ—˜ ์ƒํƒœ ์ง„์ž…]", sis.safety_scan([140.0, 139.0, 141.0]))
    inject_disable_trip(sis, authorized_keyswitch=False)   # 1) ๋ฐฉ์–ด๋˜๋Š” ๊ฒฝ์šฐ
    inject_disable_trip(sis, authorized_keyswitch=True)    # 2) ํ‚ค์Šค์œ„์น˜ ๋ฐฉ์น˜ โ†’ ์„ฑ๊ณต
    print("[๊ฐ™์€ ์œ„ํ—˜ ์ƒํƒœ]", sis.safety_scan([140.0, 139.0, 141.0]))  # ์ •์ง€๋˜์ง€ ์•Š์Œ

PoC 4 โ€” ๋ฐฉ์–ด/ํƒ์ง€ ์—ฐ์Šต. ์ด์ œ ๊ณต๊ฒฉ์ž ๊ด€์ ์ด ์•„๋‹ˆ๋ผ ๋ฐฉ์–ด์ž ๊ด€์ ์œผ๋กœ ๋„˜์–ด๊ฐ„๋‹ค. ์ •์ƒ ์—”์ง€๋‹ˆ์–ด๋ง ์„ธ์…˜ ํ”„๋กœํŒŒ์ผ์—์„œ ๋ฒ—์–ด๋‚œ ์“ฐ๊ธฐ ์‹œ๋„๋ฅผ ์žก์•„๋‚ด๋Š” ๋ชจ์˜ ํƒ์ง€๊ธฐ์™€, ๋ฌผ๋ฆฌ ํ‚ค์Šค์œ„์น˜ ํ†ต์ œ๋ฅผ ์ฝ”๋“œ๋กœ ์ ๊ฒ€ํ•œ๋‹ค.

# PoC 4: ๋ชจ์˜ ํƒ์ง€ ๋กœ์ง โ€” '๋น„์ •์ƒ ์—”์ง€๋‹ˆ์–ด๋ง ์“ฐ๊ธฐ'๋ฅผ ์žก์•„๋‚ธ๋‹ค
NORMAL_WINDOW = ("09:00", "18:00")      # ์ •์ƒ ์—”์ง€๋‹ˆ์–ด๋ง ์ž‘์—… ์‹œ๊ฐ„
ALLOWED_OPS   = {"READ_STATUS", "READ_MEMORY", "GET_KEYSWITCH"}

def is_anomalous(session):
    """session = {time, host, src_ip, op, keyswitch_program, ticket_id}"""
    if session["op"] == "WRITE_MEMORY" and not session["ticket_id"]:
        return "์ž‘์—… ํ‹ฐ์ผ“ ์—†๋Š” ๋ฉ”๋ชจ๋ฆฌ ์“ฐ๊ธฐ (๋ณ€๊ฒฝ๊ด€๋ฆฌ ์œ„๋ฐ˜)"
    if not (NORMAL_WINDOW[0] <= session["time"] <= NORMAL_WINDOW[1]):
        return "์—…๋ฌด์‹œ๊ฐ„ ์™ธ ์—”์ง€๋‹ˆ์–ด๋ง ์„ธ์…˜"
    if session["keyswitch_program"] and session["op"] == "WRITE_MEMORY":
        return "๊ฐ€์žฅ(PROGRAM) ์ƒํƒœ์—์„œ์˜ ์“ฐ๊ธฐ โ€” ๋ฌผ๋ฆฌ ํ†ต์ œ ๋กœ๊ทธ์™€ ๋Œ€์กฐ ํ•„์š”"
    return None

def check_keyswitch_policy(policy):
    """๋ฌผ๋ฆฌ ํ‚ค ๊ด€๋ฆฌ ์ •์ฑ…์˜ ๊ฐ€์žฅ ํ”ํ•œ ์‹คํŒจ๋ฅผ ์ฝ”๋“œ๋กœ ์ ๊ฒ€ํ•œ๋‹ค."""
    bad = [k for k, v in policy.items() if v == "left_in_program"]
    return ("[FAIL] PROGRAM ์œ„์น˜๋กœ ๋ฐฉ์น˜๋œ ํ‚ค: " + ", ".join(bad)) if bad \
           else "[OK] ๋ชจ๋“  ํ‚ค๊ฐ€ ํ†ต์ œ๋œ ์œ„์น˜์— ์žˆ๋‹ค"

if __name__ == "__main__":
    s1 = {"time": "23:40", "host": "EWS-01", "src_ip": "10.10.4.7",
          "op": "WRITE_MEMORY", "keyswitch_program": True, "ticket_id": None}
    print("ํƒ์ง€:", is_anomalous(s1))
    print(check_keyswitch_policy({"SIS-1": "run", "SIS-2": "left_in_program"}))
    print(check_keyswitch_policy({"SIS-1": "run", "SIS-2": "run"}))

๋งˆ์ง€๋ง‰์œผ๋กœ, ์ด๋Ÿฐ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ๋„คํŠธ์›Œํฌ์—์„œ ์ด์ƒ ์ง•ํ›„๋ฅผ ๋ณด๋Š” ๊ทœ์น™ ์•„์ด๋””์–ด(๊ฐœ๋… ์˜ˆ์‹œ)๋ฅผ ์ •๋ฆฌํ•ด ๋‘”๋‹ค. ์‹ค์ œ ๋ฐฐํฌ ์ „์—๋Š” ๋ฐ˜๋“œ์‹œ ์ž๊ธฐ ํ˜„์žฅ ํŠธ๋ž˜ํ”ฝ์œผ๋กœ ์˜คํƒ์„ ์กฐ์ •ํ•ด์•ผ ํ•œ๋‹ค.

# ๊ฐœ๋… ์˜ˆ์‹œ(๋ชจ์˜): '์ •์ƒ ํ”„๋กœํŒŒ์ผ ๋ฐ–' ์—”์ง€๋‹ˆ์–ด๋ง ํŠธ๋ž˜ํ”ฝ ์•Œ๋ฆผ ๊ทœ์น™ ์Šค์ผ€์น˜
# ์‹ค์ œ ์‹œ๊ทธ๋‹ˆ์ฒ˜ ์ •์ˆ˜/๋ฐ”์ดํŠธ๋Š” ์˜๋„์ ์œผ๋กœ ๋„ฃ์ง€ ์•Š๋Š”๋‹ค โ€” ์ž๊ธฐ ๋ง ํŠธ๋ž˜ํ”ฝ์œผ๋กœ ์กฐ์ •ํ•  ๊ฒƒ.
rule:
  name: sis_engineering_out_of_profile
  if:
    - proto: udp
    - dst_port_in: [1502]           # ์—”์ง€๋‹ˆ์–ด๋ง ํ”„๋กœํ† ์ฝœ ํฌํŠธ(๊ฐœ๋…)
    - src_not_in: [ENG_WORKSTATIONS] # ์Šน์ธ๋œ EWS ๋ชฉ๋ก ๋ฐ–์—์„œ ์˜จ ์š”์ฒญ
    - op_in: [WRITE_MEMORY, PROGRAM_DOWNLOAD]
  then:
    - alert: "๋น„์Šน์ธ ํ˜ธ์ŠคํŠธ์˜ SIS ์“ฐ๊ธฐ ์‹œ๋„ โ€” ์ฆ‰์‹œ ๋ฌผ๋ฆฌ์  ํ™•์ธ + ์„ธ์…˜ ๊ฒฉ๋ฆฌ"
    - log_fields: [ts, src_ip, op, keyswitch_state, ticket_id]

6. ๋ฐฉ์–ด ๋ฐฉ๋ฒ•/๊ตํ›ˆ โ€” '๋งˆ์ง€๋ง‰ ์•ˆ์ „์žฅ์น˜'๋ฅผ ์–ด๋–ป๊ฒŒ ์ง€ํ‚ฌ ๊ฒƒ์ธ๊ฐ€

๊ตํ›ˆ ํ•œ ์ค„. ์•ˆ์ „ ์‹œ์Šคํ…œ์„ ๋””์ง€ํ„ธ๋กœ ์—ฐ๊ฒฐํ•˜๋Š” ์ˆœ๊ฐ„, ๊ทธ ์•ˆ์ „ ์‹œ์Šคํ…œ๋„ ๊ณต๊ฒฉ๋ฉด์ด ๋œ๋‹ค. ๋งˆ์ง€๋ง‰ ๋ฐฉ์–ด์„ ์„ ์ง€ํ‚ค๋Š” ์ผ์€ ๋ฐฉํ™”๋ฒฝ ํ•œ ๋Œ€๊ฐ€ ์•„๋‹ˆ๋ผ, ๋ฌผ๋ฆฌ ํ†ต์ œ ยท ๋ณ€๊ฒฝ๊ด€๋ฆฌ ยท ํŠธ๋ž˜ํ”ฝ ํ”„๋กœํŒŒ์ผ๋ง ยท ํ‘œ์ค€ ์ค€์ˆ˜๊ฐ€ ๊ฒน์ณ์ง„ ๋‹ค์ธต ๊ตฌ์กฐ๋กœ๋งŒ ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์ด ์‚ฌ๊ฑด์„ ์„ธ์ƒ์— ์•Œ๋ฆฐ ๊ฒƒ์€ ์™„๋ฒฝํ•œ ๋ฐฉ์–ด๊ฐ€ ์•„๋‹ˆ๋ผ ๊ณต๊ฒฉ์˜ '์‹ค์ˆ˜'์˜€๋‹ค๋Š” ์‚ฌ์‹ค์„, ์šฐ๋ฆฌ๋Š” ๋ถˆํŽธํ•˜๊ฒŒ ๊ธฐ์–ตํ•ด์•ผ ํ•œ๋‹ค.

์ถœ์ฒ˜

โ€ป ๋ณธ๋ฌธ์˜ PoC ์ฝ”๋“œ๋Š” ๊ตฌ์กฐ ์ดํ•ด ๋ชฉ์ ์˜ ๊ต์œก์šฉ ๋ชจ์˜ ์ฝ”๋“œ์ด๋ฉฐ, ์‹ค์ œ TriStation ๋ช…๋ น ์ •์ˆ˜ยท์ปจํŠธ๋กค๋Ÿฌ ํŽ˜์ด๋กœ๋“œยท์ต์Šคํ”Œ๋กœ์ž‡์€ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ชจ๋“  ์‹ค์Šต์€ ์ธํ„ฐ๋„ท๊ณผ ๋ถ„๋ฆฌ๋œ ๊ฒฉ๋ฆฌ ๋žฉ(๊ฐ€์ƒ ๋””๋ ‰ํ„ฐ๋ฆฌ /tmp/lab)์—์„œ '์ง์ ‘ ๋งŒ๋“  ๋ชจ์˜ ์ปจํŠธ๋กค๋Ÿฌ'๋ฅผ ๋Œ€์ƒ์œผ๋กœ๋งŒ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋ฉฐ, ์‹ค์ œ ์„ค๋น„๋‚˜ ํƒ€์ธ์˜ ์‚ฐ์—… ์‹œ์Šคํ…œ์„ ๋Œ€์ƒ์œผ๋กœ ํ•œ ์‹คํ–‰์€ ๋ถˆ๋ฒ•์ž…๋‹ˆ๋‹ค.

ํ•จ๊ป˜ ์ฝ๊ธฐ: โš›๏ธ Stuxnet (2010) โ€” ์›์‹ฌ๋ถ„๋ฆฌ๊ธฐ๋ฅผ ํŒŒ๊ดดํ•œ ์„ธ๊ณ„ ์ตœ์ดˆ์˜ ์‚ฌ์ด๋ฒ„ ๋ฌด๊ธฐ ยท โšก Industroyer / CrashOverride โ€” ์ „๋ ฅ๋ง ํ”„๋กœํ† ์ฝœ์„ ๊ทธ๋Œ€๋กœ ๋งํ•œ ์•…์„ฑ์ฝ”๋“œ ยท ๐Ÿ”ฅ Flame (2012) โ€” MD5 ์ถฉ๋Œ๋กœ MS ์ธ์ฆ์„œ๋ฅผ ์œ„์กฐํ•œ ์ŠคํŒŒ์ด ํ”Œ๋žซํผ ยท ๐Ÿฆ… SolarWinds SUNBURST (2020) โ€” ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ฐ์—ผ์‹œ์ผœ ์‹ ๋ขฐ๋ฅผ ๋ฌด๊ธฐ๋กœ ์‚ผ์€ ์‚ฌ๊ฑด ยท โ˜ ๏ธ NotPetya (2017) โ€” ๊ฐ™์€ ํ•ด '๊ณต๊ธ‰๋ง์—์„œ ์‹œ์ž‘ํ•ด ์„ธ๊ณ„๋ฅผ ๋ฉˆ์ถ˜' ํŒŒ๊ดดํ˜• ์™€์ดํผ

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