1. ๋ฐฐ๊ฒฝ/์ญ์ฌ โ ์ธ์ , ๋๊ฐ, ์ ์ ๋ช ํ๊ฐ
2012๋ 5์ 28์ผ, ์ด๋ ๊ตญ๊ฐ CERT ์ฐํ MAHER ์ผํฐ์ ์นด์คํผ์คํค ๋ฉ, ํ๊ฐ๋ฆฌ ๋ถ๋คํ์คํธ๊ณต๋ CrySyS ๋ฉ์ด ๋์์ ํ๋์ ์ ์ฑ์ฝ๋๋ฅผ ๊ณต๊ฐํ๋ค. ์ด๋ฆ์ Flame(๋ณ์นญ FlamerยทsKyWIper). CrySyS ๋ฉ์ ํํ์ ์ด๋ฌ๋ค โ "์ฐ๋ฆฌ ์ค๋ฌด์์ ๋ง๋ ๊ฐ์ฅ ์ ๊ตํ ์ ์ฑ์ฝ๋์ด๋ฉฐ, ์ด์ฉ๋ฉด ์ง๊ธ๊น์ง ๋ฐ๊ฒฌ๋ ๊ฐ์ฅ ๋ณต์กํ ์ ์ฑ์ฝ๋๋ค." ์นด์คํผ์คํค๋ "Stuxnet๋ณด๋ค 20๋ฐฐ ๋ณต์กํ๋ค"๊ณ ํํํ๊ณ , ์ ์ฒด ๋ถ์์๋ 10๋ ์ด ๊ฑธ๋ฆด ์๋ ์๋ค๊ณ ๋ง๋ถ์๋ค.
์ถ๋ฐ์ ์ 2012๋
4์ ์ด๋ ์์ ๋ถ์ ์ฌ๊ณ ์๋ค. ์ด๋ ๋น๊ตญ์ ์ ์ ํฐ๋ฏธ๋์ ์ธํฐ๋ท์์ ๋ฌผ๋ฆฌ์ ์ผ๋ก
๋ถ๋ฆฌํ๊ณ , ๊ตญ์ํต์ ์ฌ๋ ๊ทธ ์์ธ์ "Wiper"๋ผ๊ณ ๋ถ๋ ๋ค. ๊ตญ์ ์ ๊ธฐํต์ ์ฐํฉ(ITU)์ ์์ฒญ์
๋ฐ์ ์นด์คํผ์คํค๊ฐ ์กฐ์ฌ์ ๋ค์ด๊ฐ๊ณ , ๊ณง ์ค๋ ๊ตญ๊ฐ ๊ณ ๊ฐ๋ค์์๋ง ๋ํ๋๋ ํน์ MD5 ํด์ยทํ์ผ๋ช
ํจํด์
๋ฐ๊ฒฌํ๋ค. ๋ด๋ถ ๋ชจ๋ ์ด๋ฆ(FROG.DefaultAttacks.A-InstallFlame)์์ ๋ฐ์จ
"Flame"์ด ๊ทธ๋ ๊ฒ ์ธ์์ ๋์๋ค. ์นด์คํผ์คํค๋ ์ด ์ํธํ๋ ๋ํ ํ๋ซํผ์ด
Stuxnet(2010)๊ณผ ๊ฐํ ๊ณ๋ณด ๊ด๊ณ์ ์๋ค๊ณ
๊ฒฐ๋ก ์ง์๋ค โ ์ด๊ธฐ Stuxnet์ ๋ค์ด ์๋ USB ์ ํ ์ฝ๋๊ฐ Flame์ ํน์ ๋ชจ๋๊ณผ ๊ฑฐ์ ๋์ผํ๊ณ ,
๋ ์
์ฑ์ฝ๋๋ ๊ฐ์ ์ ๋ก๋ฐ์ด ์ทจ์ฝ์ 2๊ฐ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฌ์ฉํ๊ณ ์์๋ค.
Flame์ด ๋ณด์์ฌ์ ๋จ๊ธด ๊ฒฐ์ ์ ์ ์ฐ์ ํฌ๊ธฐ๊ฐ ์๋๋ค. MD5 ํด์ ์ถฉ๋(chosen-prefix collision)๋ก ๋ง์ดํฌ๋ก์ํํธ์ ์ฝ๋์๋ช ์ธ์ฆ์๋ฅผ ์์กฐํด, ์ ์ฑ ๊ตฌ์ฑ์์๋ฅผ "๋ง์ดํฌ๋ก์ํํธ๊ฐ ์๋ช ํ ๊ฒ"์ฒ๋ผ ๋ณด์ด๊ฒ ๋ง๋ ์ฌ๊ฑด์ด๋ค. ํด์ ์๊ณ ๋ฆฌ์ฆ ํ๋๊ฐ PKI ์ ๋ขฐ ์ฌ์ฌ ์ ์ฒด๋ฅผ ๋ ๋ฐ์น๊ณ ์์์ ์ค์ ์์ ์ฆ๋ช ํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ ์ ๊ณ์ MD5 โ SHA-2 ์ ๋ฉด ์ ํ์ด ์๋น๊ฒจ์ก๋ค.
- ์ ์ฑ์ฝ๋๋ช : Flame (Worm.Win32.Flame) ยท ๋ณ์นญ Flamer / sKyWIper
- ๊ณต๊ฐ: 2012-05-28 (MAHERยท์นด์คํผ์คํคยทCrySyS ๋์ ๋ฐํ)
- ํ๋ ๊ธฐ๊ฐ: ์นด์คํผ์คํค ์ถ์ ์ต์ 2010-02๋ถํฐ ยท CrySyS ๊ด์ธก ํ์ผ๋ช 2007-12 ยท ์์กฐ๋ ํ์ผ ์๊ฐ์ 1994๋ ๊น์ง ์๊ธ
- ๊ท๋ชจ: ์ฝ 20 MB ยท Lua + C++ ํ์ด๋ธ๋ฆฌ๋ ยท SQLite DB ยท ์ํธํ ๋ฐฉ์ 5์ข
- ๊ฐ์ผ: ์ฝ 1,000๋ (2012-05 ์นด์คํผ์คํค ์ถ์ ) ยท ์ด๋ยท์ด์ค๋ผ์ยทํ๋ ์คํ์ธยท์๋จยท์๋ฆฌ์ยท๋ ๋ฐ๋ ผยท์ฌ์ฐ๋ยท์ด์งํธ, "์๋์ ๋ค์๊ฐ ์ด๋"
- C2: ์์์ยท์ ๋ฝยท๋ถ๋ฏธ์ ๊ฑธ์น ์ฝ 80๊ฐ ์๋ฒ (์ดํ ์ฑํฌํ ๋ถ์ ๊ทผ๊ฑฐ)
- ํต์ฌ ๊ธฐ์ : MD5 ์ ํ ์ ๋์ฌ ์ถฉ๋๋ก ์์กฐ ์ฝ๋์๋ช ์ธ์ฆ์ ์์ฑ โ MS ๋ฃจํธ CA๊น์ง ์ฌ์ฌ ์ฐ๊ฒฐ
- ๋์: MS ๋ณด์ ๊ณต์ง 2718704 / KB2718704(2012-06-03) โ ๋ฌธ์ ์ธ์ฆ์ ํ๊ธฐ + ํด์ ๊ฐํ
- ์ดํ: 2012-06-08 ์ฑํฌํ C2๊ฐ "suicide" ๋ช ๋ น ์ ์ก(์ ๋ ์ํญ) ยท 2019๋ Flame 2.0 ์ฌ๋ฐ๊ฒฌ(2014๋ ๊ฒฝ ์ปดํ์ผ, ํ์์คํฐํ)
2. ๊ฐ์ผยท์ ํ ๊ฒฝ๋ก โ ์์ ๋ถ ์ฑ ์์์ ๊ฐ์ ์ธต ์ฌ๋ฌด์ค๋ก
Flame์ ์์ฒ๋ผ ๋๋์ผ๋ก ํผ์ง๋ ๊ฒ์ ๋ชฉํ๋ก ํ์ง ์์๋ค. ํ์ ์ ๊ฐ์ฒญยท์์ง์ด์๊ณ , ๊ทธ๋์ ์ ํ ๊ฒฝ๋ก๋ "์กฐ์ง ๋ด๋ถ"์ ๋ง์ถฐ ์ค๊ณ๋๋ค.
- โ ์คํผ์ดํผ์ฑ + ์ ๋ก๋ฐ์ด: ํ์ ๋ง์ถคํ ๋ฌธ์(Word RTF ๊ณ์ด ๋ฑ)๋ก ์ด๊ธฐ ์นจํฌ. ์นด์คํผ์คํค๋ Flame์ด ์ต์ 4~5๊ฐ์ ์ ๋ก๋ฐ์ด๋ฅผ ์กฐํฉํ๋ค๊ณ ๋ถ์ํ๋ค โ ์ด๊ธฐ ๊ฐ์ผ์ฉ ๋ฌธ์ ์ทจ์ฝ์ (CVE-2010-3333 ๊ณ์ด), ๊ถํ ์์น์ฉ ์ทจ์ฝ์ , ๊ทธ๋ฆฌ๊ณ ๋ค์ ๋์ฌ ๋คํธ์ํฌ ์ ํ์ฉ ์ทจ์ฝ์ ์ด๋ค.
- โก LAN ์๋ ์ ํ (์ด ๋ฌธ์์ ํต์ฌ): ๊ฐ์ ์๋ธ๋ท์ ๋ค๋ฅธ PC๋ฅผ ์ฐพ์๋ด๋ฉด ์ธ์ ์คํ๋ฌ ๊ฒฐํจ(MS10-061 ๊ณ์ด, Stuxnet์ด ์ผ๋ ๊ฒ๊ณผ ๊ฐ์ ์ทจ์ฝ์ )์ ๋ค์ ์ฌ์ฉํด ์๊ฒฉ์ผ๋ก ์ฝ๋๋ฅผ ๋ฃ์ ๊ฒ์ผ๋ก ๋ณด๊ณ ๋๋ค. ์ฌ๊ธฐ์ NBNS/WINS ์ด๋ฆ ์กฐํ์ WPAD(์๋ ํ๋ก์ ํ์) ์คํธํ์ ๊ฒฐํฉํด, ์๊ธฐ๋ฅผ "์ฌ๋ด ์ ํ ์ ๋ฐ์ดํธ ์๋ฒ"๋ก ์์ฅํ๋ค.
- โข ์์กฐ ์ธ์ฆ์ ๋ฐฐํฌ: ์์ฅํ ์ ๋ฐ์ดํธ ์๋ฒ๊ฐ ๋ด๋ ค์ฃผ๋ ๊ตฌ์ฑ์์๋ MD5 ์ถฉ๋๋ก ์์กฐํ ๋ง์ดํฌ๋ก์ํํธ ์ธ์ฆ์๋ก ์๋ช ๋ผ ์์๋ค. ํผํด PC์ Windows Update ํด๋ผ์ด์ธํธ๋ "MS๊ฐ ์๋ช ํ ์ ํ"์ผ๋ก ํ๋จํ๊ณ ์ค์ค๋ก ์ค์นยท์คํํ๋ค โ ์ฆ ํจ์น ์ธํ๋ผ ์์ฒด๊ฐ ๊ฐ์ผ ๊ฒฝ๋ก๋ก ์ ํ๋๋ค.
- โฃ USBยท๋ธ๋ฃจํฌ์ค ํ์ฐ: ์ด๋์ ์ ์ฅ๋งค์ฒด ๊ฐ์ผ ๋ชจ๋(
Frog๊ณ์ด)๊ณผ ํจ๊ป, ๊ฐ์ผ PC๋ฅผ ๋ธ๋ฃจํฌ์ค ๋น์ฝ์ฒ๋ผ ๋์์์ผ ์ฃผ๋ณ ๊ธฐ๊ธฐ์์ ์ฐ๋ฝ์ฒยท๊ธฐ๊ธฐ ์ ๋ณด๋ฅผ ์์งํ๋ค. - โค ์กฐ์ง ๋ฐ์ผ๋ก: ์์ง ๋ฐ์ดํฐ๋ ์์์ยท์ ๋ฝยท๋ถ๋ฏธ์ ํฉ์ด์ง ์ฝ 80๊ฐ C2๋ก ์ ์ก๋๋ค. ์นด์คํผ์คํค๊ฐ ์ด C2๋ค์ ์ฑํฌํํ ๊ฒฐ๊ณผ, "์๋์ ๋ค์์ ํ์ ์ด ์ด๋ ๋ด๋ถ"์ด๋ฉฐ ์๊ตฌ ๋ฐ์ดํฐ๋ AutoCAD ๋๋ฉดยทPDFยทํ ์คํธ ๋ฌธ์์ ์ง์ค๋ผ ์์๋ค.
3. ๋์ ์๋ฆฌ ์์ธ โ ๊ท๋ชจ๊ฐ ์๋๋ผ ๊ตฌ์กฐ๊ฐ ๋ฌด๊ธฐ์๋ค
20 MB๋ 2012๋ ๊ธฐ์ค์ผ๋ก ๋ง๋ ์ ๋๋ ํฌ๊ธฐ๋ค. ๋น์ ์ ์ฑ์ฝ๋๋ ๋ณดํต ์์ญ~์๋ฐฑ KB์๊ณ , ํ์ ์คํ์ด๋ "์๊ณ ์กฐ์ฉํ" ๊ฒ์ด ์์์ด์๋ค. Flame์ ์ ๋ฐ๋๋ก ๊ฐ๋ค.
- ํ๋ฌ๊ทธ์ธ ํ๋ซํผ: ๋ณธ์ฒด๋ ๋ก๋์ ๊ฐ๊น๋ค. Lua ์คํฌ๋ฆฝํธ
+ ์ปดํ์ผ๋ C++๋ก ์์ฑ๋์ด ์๊ณ , ์นจํฌ ํ ํ์ํ ๊ธฐ๋ฅ๋ง ๋ชจ๋๋ก ๋ด๋ ค๋ฐ์ ์คํํ๋ค.
๊ฐ์ฒญ(
Munchโ ํค๋ก๊น ยท๋คํธ์ํฌ ์ค๋ํ), ์คํฌ๋ฆฐ์ท, ์ค๋์ค ๋ น์, ์ค์นด์ดํ ๋ํ ๊ธฐ๋ก, USB ์ ํ(Frog), ์ค์บ๋(Snack) ๋ฑ์ด ๋ชจ๋๋ก ๋ถ๋ฆฌ๋ผ ์๋ค. - ์๋ ์คํ: ๋ชจ๋์ ํ๋ก์ธ์ค์ ์ฃผ์ ํ๋, ํ๋ก์ธ์ค๊ฐ ์ค์ค๋ก ๋ณด๊ณ ํ๋ ๋ก๋๋ ๋ชจ๋ ๋ชฉ๋ก์๋ ๋ํ๋์ง ์๊ฒ ํ๋ค. ํด๋น ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ์ฌ์ฉ์ ๋ชจ๋์์ ์ฝ๊ธฐยท์ฐ๊ธฐยท์คํ ๊ถํ์ด ์ ํ๋๋๋ก ๋ณดํธ๋๋ค. ์ฆ "๋ชจ๋ ๋ชฉ๋ก์ ๋ฌผ์ด๋ณด๋" ์ ํต์ ํ์ง๊ฐ ๋ฌด๋ ฅํ๋๋ค.
- ์ง์์ฑ: ๊ฐ์ง ์ค๋์ค ๋๋ผ์ด๋ฒ ์๋น์ค๋ฅผ ์ค์นํด ๋ถํ ๋๋ง๋ค ์ด์๋ฌ๋ค. ์ฌ์ฉ์๋ "์ฌ์ด๋ ๋๋ผ์ด๋ฒ"๋ก ๋ณด์ง๋ง ์ค์ ๋ก๋ Flame์ ์์ฃผ ์ง์ ์ด๋ค.
- ๋ฐ์ดํฐ ๊ด๋ฆฌ: ์์ง ๊ฒฐ๊ณผ๋ SQLite ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๊ตฌ์กฐํํด ์ ์ฅํ๋ค. ์์งยท๋ถ๋ฅยท๋ณด๊ณ ๊ฐ ํ ๋ฉ์ด๋ฆฌ์ ์ ๋ณด ์์คํ ์ผ๋ก ๋์๊ฐ ์ ์ด๋ค.
- ๋ฎ์ ์๊ทธ๋์ฒ: ๋ด๋ถ ์ฝ๋๋ ์๋ ค์ง ์ ์ฑ์ฝ๋์ ์ ์ฌ์ ์ด ๊ฑฐ์ ์๊ณ , ์ํธํ ๋ฐฉ์์ 5์ข ํผ์ฉํ๋ค. ์ํฐ๋ฐ์ด๋ฌ์ค ์ ํ์ ์๋ณํด ํ์ผ ํ์ฅ์ ๋ฑ ์๊ธฐ ํ๋์ ์ ํ๋ณ๋ก ๋ฐ๊พธ๋ ํํผ ๋ก์ง๋ ์์๋ค.
- ์ํญ(kill): ํต์ ์๋ฒ๊ฐ "suicide" ๋ชจ๋์ ๋ณด๋ด๋ฉด ๊ฐ์ผ ํ์ ์ ์ค์ค๋ก ์ ๋ถ ์ง์ด๋ค. 2012๋ 6์ 8์ผ, ๋ ธ์ถ๋ ์ง 10์ผ ๋ง์ ์ค์ ๋ก ์ด ๋ช ๋ น์ด ๊ด์ธก๋๋ค โ ์ฆ๊ฑฐ ์ธ๋ฉธ์ด ์ค๊ณ์ ํฌํจ๋ผ ์์๋ ๊ฒ์ด๋ค.
4. ํต์ฌ ๊ธฐ์ โ MD5 ์ถฉ๋๋ก ๋ง์ดํฌ๋ก์ํํธ ์ธ์ฆ์๋ฅผ ์์กฐํ๋ค
Flame์ ๋ฐฑ๋ฏธ๋ ์์กฐ ์ฝ๋์๋ช ์ธ์ฆ์๋ค. ์ ์ฐจ๋ ์ด๋ ๋ค.
- ๊ณต๊ฒฉ์๋ค์ ๋ง์ดํฌ๋ก์ํํธ๊ฐ ๋ฐ๊ธํ "Microsoft Enforced Licensing Intermediate PCA" ๊ด๋ จ ์ธ์ฆ์ ์ค ํ๋, ์ฆ ํฐ๋ฏธ๋ ์๋ฒ ๋ผ์ด์ ์ฑ ์๋น์ค์ฉ ์ธ์ฆ์๊ฐ (์ค๊ณ ์๋์ ๋ฌ๋ฆฌ) ์ฝ๋์๋ช ์ด ๊ฐ๋ฅํ๊ฒ ์ค์ ๋ผ ์๊ณ ์ฌ์ ํ MD5๋ฅผ ์ฐ๊ณ ์๋ค๋ ์ฌ์ค์ ์ฐพ์๋๋ค.
- ๊ทธ ์ธ์ฆ์์ TBSCertificate(์๋ช ๋์ ๋ณธ๋ฌธ)์ ํด์๊ฐ ์ถฉ๋ํ๋ ๋ค๋ฅธ ๋ณธ๋ฌธ์ ๋ง๋ค์ด ์์กฐ ์ธ์ฆ์๋ฅผ ๋ง๋ค์๋ค. ๋ ๋ณธ๋ฌธ์ ๋ฐ์ดํธ์ด์ด ์๋ก ๋ค๋ฅด์ง๋ง MD5 ๊ฐ์ด ๊ฐ์ผ๋ฏ๋ก, ๊ธฐ์กด ์๋ช ๊ฐ์ด ์์กฐ ์ธ์ฆ์์๋ ๊ทธ๋๋ก ์ ํจํ ๊ฒ์ฒ๋ผ ํต๊ณผ๋๋ค.
- ๊ทธ ๊ฒฐ๊ณผ ์์กฐ ์ธ์ฆ์๋ ๋ง์ดํฌ๋ก์ํํธ ๋ฃจํธ CA๊น์ง ์ ๋ขฐ ์ฌ์ฌ์ด ์ด์ด์ก๋ค. 2008๋ ์ฐ๊ตฌ์ง์ด ์์กฐ CA ์ธ์ฆ์๋ก ๊ฐ๋ ์ ์ฆ๋ช ํ์ง๋ง, ์ค์ ๊ณต๊ฒฉ์ ์ฐ์ธ ๊ฒ์ Flame์ด ์ฒ์์ด์๋ค(๊ฒ๋ค๊ฐ ๊ธฐ์กด ๊ธฐ๋ฒ์ ๊ฐ๋ํ ์ ๋ณํ ์ ํ ์ ๋์ฌ ์ถฉ๋์ด์๋ค).
- ์ฌ๊ธฐ์ WPAD/NBNS ์คํธํ์ด ๊ฒฐํฉ๋๋ฉด์, ๊ฐ์ ๋คํธ์ํฌ์ PC๋ค์ด "์ ๋ขฐํ ์ ์๋ MS ์ ๋ฐ์ดํธ"๋ฅผ ์ค์ค๋ก ์ค์นํ๋ ์๋ ํ์ฐ ๊ฒฝ๋ก๊ฐ ์์ฑ๋๋ค.
๋ง์ดํฌ๋ก์ํํธ๋ 2012๋ 6์ 3์ผ ๋ณด์ ๊ณต์ง 2718704(KB2718704)๋ก ๋ฌด๋จ ๋ฐ๊ธ๋ ์ธ์ฆ์๋ค์ ํ๊ธฐํ๊ณ , ์ฝ๋์๋ช ยท์ ๋ฐ์ดํธ ๊ฒ์ฆ์์ MD5 ์ฌ์ฉ์ ์ค๋จ(SHA-2๋ก ์ ํ)ํ๋ค. MD5๋ ์ด๋ "์ฐ๊ตฌ์ค ์ด์ผ๊ธฐ"์์ "์ค์ ๋ฌด๊ธฐ"๋ก ๋์ด๊ฐ๋ค.
5. ํผํด ๊ท๋ชจ/์ํฅ โ ์ซ์๋ ์๊ณ , ํ๊ธ์ ์ปธ๋ค
- ๊ฐ์ผ ๊ท๋ชจ: ์ฝ 1,000๋. ์์ด ์๋์๊ธฐ์ "๋๋"์ ์๋์ง๋ง, ํ์ ์ ์ฑ๊ฒฉ์ด ๋ฌ๋๋ค โ ์ ๋ถ๊ธฐ๊ดยท๊ต์ก๊ธฐ๊ดยทํน์ ๊ฐ์ธ, ์ฑํฌํ ๋ถ์์ ์๋์ ๋ค์๊ฐ ์ด๋ ๋ด๋ถ์๋ค.
- ์์ง ๋ฒ์: ํค๋ณด๋ ์ ๋ ฅ, ํ๋ฉด, ๋ง์ดํฌ ์ค๋์ค, ์ค์นด์ดํ ํตํ, ์ฃผ๋ณ ๋ธ๋ฃจํฌ์ค ๊ธฐ๊ธฐ ์ฐ๋ฝ์ฒ, ๋ฌธ์ ํ์ผ๊น์ง. ์คํ์ด ๋๊ตฌ๋ก๋ ์ด๋ก์ ์ธ '์ ๋ฐฉ์ ์์ง ํ๋ซํผ'์ด์๋ค.
- ์ ์ฑ ์ ํ์ฅ: 2012๋ 6์ 19์ผ ์์ฑํดํฌ์คํธ๋ Flame์ด ๋ฏธ๊ตญ NSAยทCIA์ ์ด์ค๋ผ์ ๊ตฐ์ด ์ต์ 5๋ ์ ๋ถํฐ ๊ณต๋ ๊ฐ๋ฐํ๋ค๊ณ ๋ณด๋ํ๋ค(์ฝ๋๋ค์ Olympic Games, ์ด๋ ํต ํ๋ก๊ทธ๋จ์ ๊ฒจ๋ ์ฌ์ด๋ฒ ์์ ์ค๋น ๋จ๊ณ). ๋ฏธ๊ตญ์ ๊ณต์ ๋ถ์ธํ๋ค.
- ์ ๊ณ ์ํฅ: โ ์ฝ๋์๋ช ยทPKI์์ MD5 ํด์ถ์ด ๊ฐ์๋๊ณ , โก "์๋ช ์ด ์์ผ๋ฉด ์์ ํ๋ค"๋ ์ด์ ๊ดํ์ด ์ฌ๊ฒํ ๋์ผ๋ฉฐ, โข ํ์ ํ ์คํ์ด ํ๋ซํผ์ ๋ํ ๋ชจ๋ ๋จ์ ์ถ์ ยท๊ท์(attribution) ๋ถ์์ด ์ ์ฐฉ๋๋ค.
- ๊ฒ์์ ์ฐ์ฅ: 2019๋ Guerrero-SaadeยทCutler๊ฐ Flame 2.0์ ๋ฐํํ๋ค. ํ์ผ ์๊ฐ์ ์์กฐ(ํ์์คํฐํ)ํด '์ํญ ์ด์ '์ ๋ง๋ค์ด์ง ๊ฒ์ฒ๋ผ ๊พธ๋ช์ง๋ง, ์ปดํ์ผ ์ค๋ฅ๋ก ์ค์ ์ปดํ์ผ ์์ (2014๋ ๊ฒฝ)์ด ๋๋ฌ๋ฌ๋ค. ๊ฒ๊ฑฐ๋์ง ์์ ์ฝ๋๋ ๊ณ์ ์งํํ๋ค.
6. PoC โ ๊ต์ก์ฉ ๋ชจ์ ์ฝ๋ 4์ข (์ค์ ์ ์ฑ์ฝ๋ ์๋)
์๋ ์ฝ๋๋ ๋์ ์๋ฆฌ ์ฌํ์ฉ ๋ชจ์ ์ฝ๋๋ค. ์ค์ Flame ์ํยท์ค์ MD5 ์ถฉ๋์ยท์ค์
์ต์คํ๋ก์ยทC2 ์ฃผ์๋ ํฌํจํ์ง ์๋๋ค. ๋ชจ๋ ์ธํฐ๋ท๊ณผ ๋ถ๋ฆฌ๋ ๊ฒฉ๋ฆฌ ๋ฉ(๋ก์ปฌ VM,
๋ฃจํ๋ฐฑ ์ ์ฉ)์์ ์คํ์ ์ ์ ๋ก ํ๋ฉฐ, PoC #4์ ์๋ฒ๋ 127.0.0.1์๋ง ๋ฐ์ธ๋ฉ๋๋ค.
PoC โ ์ ํ ์ ๋์ฌ ์ถฉ๋์ ๊ณจ๊ฒฉ โ ์ถ์ ํด์์์ ์ง์ ์ฐพ์๋ณธ๋ค
MD5 ์ถฉ๋ ์ฝ๋๋ฅผ ๊ทธ๋๋ก ์ฐ๋ ๋์ , ํ์ค ํด์ ์ถ๋ ฅ์ 20๋นํธ๋ก ์๋ฅธ ์ถ์ ๋ชจ๋ธ์ ๋ง๋ค์ด ๊ฐ์ ์์ผ ๊ณต๊ฒฉ(meet-in-the-middle) ๊ณจ๊ฒฉ์ ์คํํ๋ค. ์๋ก ๋ค๋ฅธ ๋ ์ธ์ฆ์ ๋ณธ๋ฌธ (์ ํ/์์กฐ)์ ๊ฐ๊ฐ ์ถฉ๋ ์ ๋ฏธ์ฌ๋ฅผ ๋ถ์ฌ ๊ฐ์ ํด์๊ฐ์ ์ป๋ ๊ฒ์ด ๋ชฉํ๋ค.
#!/usr/bin/env python3
"""Flame(2012) ๊ต์ก์ฉ PoC #1 โ '์ ํ ์ ๋์ฌ ์ถฉ๋(chosen-prefix collision)'์ ๊ตฌ์กฐ ์ฌํ.
์ค์ MD5 ์ถฉ๋์์ด๋ Flame ์ธ์ฆ์ ์๋ฃ๋ ํฌํจํ์ง ์๋๋ค.
์ฌ๊ธฐ์๋ '์ถฉ๋์ ์ฐพ์ ์ ์์ ๋งํผ ์ฝํ ํด์'๋ฅผ ๋ง๋๋ ๋์ , ํ์ค ํด์(SHA-256)์
์ถ๋ ฅ์ 20๋นํธ๋ก ์๋ผ ์ถ์ ๋ชจ๋ธ์ ๋ง๋ ๋ค. MD5 ์ฒ๋ผ '์ถฉ๋ ์ ํญ์ฑ์ด ์ฌ์ค์ ์๋'
ํด์ ์์์ ์ ํ ์ ๋์ฌ ์ถฉ๋์ด ์ด๋ป๊ฒ ๊ณ์ฐ๋๋์ง, ๊ทธ ๊ณจ๊ฒฉ(์์ผ ๊ณต๊ฒฉ /
meet-in-the-middle)์ ๊ทธ๋๋ก ์คํํด ๋ณด๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
H(A || suffix_A) == H(B || suffix_B) ์ธ ์ ๋ฏธ์ฌ ์ ์ฐพ๊ธฐ
์คํ: python3 poc1_collision.py (๊ฒฉ๋ฆฌ ๋ฉ ์ ์ฉ, ๋คํธ์ํฌ ์ ๊ทผ ์์)
"""
import hashlib
OUT_BITS = 20 # MD5=128๋นํธ์๋ ๊ฒ์ 20๋นํธ๋ก ์ถ์
OMASK = (1 << OUT_BITS) - 1
def toy_hash(data: bytes) -> int:
"""์ถ์ ํด์ ๋ชจ๋ธ: SHA-256 ์ถ๋ ฅ์ 20๋นํธ๋ก ์๋ฅธ๋ค.
(์ถ๋ ฅ์ด ์งง์ ์ถฉ๋์ด ํํ๋ฉฐ, 'ํด์๊ฐ ๊นจ์ง ํด์'์ ๊ฑฐ๋์ ํ๋ด๋ธ๋ค.)"""
return int.from_bytes(hashlib.sha256(data).digest()[:4], "big") & OMASK
def chosen_prefix_collision(prefix_a: bytes, prefix_b: bytes, tries=1 << 16):
"""A ์ ๋์ฌ์ฉ ์ ๋ฏธ์ฌ ์ฌ์ ์ ๋ง๋ ๋ค B ์ ๋์ฌ์์ ์กฐํ(meet-in-the-middle).
MD5 ๊ณต๊ฒฉ๋ ๋์ผํ ๊ตฌ์กฐ๋ก, Stevens ๋ฑ์ ์๊ณ ๋ฆฌ์ฆ์ ์ด ํ์์
'Joux ํน์ฑ + ๊ทผ์ ์ถฉ๋ ๋ธ๋ก' ์ผ๋ก ์ต์ ํํ ๊ฒ์ด๋ค."""
table = {}
for i in range(tries):
s = i.to_bytes(3, "big")
table.setdefault(toy_hash(prefix_a + s), s)
for j in range(tries):
s = j.to_bytes(3, "big")
h = toy_hash(prefix_b + s)
if h in table:
return table[h], s, h
raise SystemExit("์ถฉ๋ ๋ฏธ๋ฐ๊ฒฌ โ OUT_BITS ๋ฅผ ์ค์ด์ธ์")
if __name__ == "__main__":
# '์ ํ ์ธ์ฆ์ ๋ณธ๋ฌธ'๊ณผ '์์กฐ ์ธ์ฆ์ ๋ณธ๋ฌธ' ์ญํ ์ ํ๋ ์๋ก ๋ค๋ฅธ ๋ ์ ๋์ฌ
CERT_GOOD = b"TBSCertificate|issuer=Microsoft Enforced Licensing PCA|key=GOOD|"
CERT_EVIL = b"TBSCertificate|issuer=Microsoft Enforced Licensing PCA|key=EVIL|"
sa, sb, h = chosen_prefix_collision(CERT_GOOD, CERT_EVIL)
good, evil = CERT_GOOD + sa, CERT_EVIL + sb
print("[์ถ์ ํด์: ์ถ๋ ฅ %d๋นํธ] ์ ํ ์ ๋์ฌ ์ถฉ๋ ๋ฐ๊ฒฌ" % OUT_BITS)
print(" ์ ๋ฏธ์ฌ A :", sa.hex(), "| ์ ๋ฏธ์ฌ B :", sb.hex())
print(" H(์ ํ||A) =", hex(h), " H(์์กฐ||B) =", hex(h))
print(" ๋์ผ ํด์? ->", toy_hash(good) == toy_hash(evil))
print(" ๋ ๋ฐ์ดํธ์ด ์์ฒด๋ ๋ค๋ฅธ๊ฐ? ->", good != evil)
print()
print("[๊ฒ์ฆ] ๊ฐ์ ๋ ๋ฐ์ดํธ์ด์ '์๋ฅด์ง ์์' SHA-256 ์ผ๋ก ๊ณ์ฐํ๋ฉด ์ฆ์ ๊ฐ๋ผ์ง๋ค")
print(" sha256(์ ํ||A) =", hashlib.sha256(good).hexdigest()[:32])
print(" sha256(์์กฐ||B) =", hashlib.sha256(evil).hexdigest()[:32])
print(" -> ์ถ๋ ฅ์ 20๋นํธ์์ 256๋นํธ๋ก ๋๋๋ฆฌ๋ ๊ฒ๋ง์ผ๋ก ์ด ๊ณต๊ฒฉ์ ์ฌ๋ผ์ง๋ค")
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
[์ถ์ ํด์: ์ถ๋ ฅ 20๋นํธ] ์ ํ ์ ๋์ฌ ์ถฉ๋ ๋ฐ๊ฒฌ
์ ๋ฏธ์ฌ A : 00aa64 | ์ ๋ฏธ์ฌ B : 000000
H(์ ํ||A) = 0x66956 H(์์กฐ||B) = 0x66956
๋์ผ ํด์? -> True
๋ ๋ฐ์ดํธ์ด ์์ฒด๋ ๋ค๋ฅธ๊ฐ? -> True
sha256(์ ํ||A) = a9966956aca8d181f8319a164568b2ff
sha256(์์กฐ||B) = 62b66956033f8262f5d2af119f10f635
-> ์ถ๋ ฅ์ 20๋นํธ์์ 256๋นํธ๋ก ๋๋๋ฆฌ๋ ๊ฒ๋ง์ผ๋ก ์ด ๊ณต๊ฒฉ์ ์ฌ๋ผ์ง๋ค
PoC โก ๊ฒ์ฆ๊ธฐ๋ ์ ์์กฐ ์ธ์ฆ์๋ฅผ ํต๊ณผ์์ผฐ๋ (๋ชจ์)
๊ฐ์ ์ถฉ๋์์ ์ถ์ X.509 ๊ฒ์ฆ๊ธฐ์ ํต๊ณผ์ํจ๋ค. ๊ฒ์ฆ๊ธฐ๋ "์ธ์ฆ์ ๋ณธ๋ฌธ ํด์ == ์๋ช ์ ๋ด๊ธด ํด์"๋ฉด ํต๊ณผ๋ก ํ์ ํ๋ค โ Flame์ด ์ค์ ๋ก ๋ซ์ ์ง์ ์ด ๋ฐ๋ก ์ด ์ ์ ๋ค. ์ทจ์ฝ ํด์ ๋ชจ๋ธ๊ณผ SHA-256 ๋ชจ๋ธ์ ๋๋ํ ๋๋ ค ์ฐจ์ด๋ฅผ ๋ณธ๋ค.
#!/usr/bin/env python3
"""Flame(2012) ๊ต์ก์ฉ PoC #2 โ 'ํด์ ์ถฉ๋๋ก ์ฝ๋์๋ช
๊ฒ์ฆ์ ํต๊ณผ์ํค๋' ํ๋ฆ(๋ชจ์).
Flame ์ ํต์ฌ์ ์ฝ๋์๋ช
์ธ์ฆ์๋ฅผ ํด์ ์ถฉ๋๋ก ์์กฐํด ๋ง์ดํฌ๋ก์ํํธ ๋ฃจํธ CA
๊น์ง ์ ๋ขฐ ์ฌ์ฌ์ด ์ด์ด์ง๊ฒ ๋ง๋ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์๋ PoC #1 ์ ์ถ์ ํด์๋ฅผ
'๋น์์ ์ทจ์ฝ ํด์(MD5 ๊ณ์ด)' ๋ชจ๋ธ๋ก, SHA-256 ์ '๊ฐํ ์ดํ(SHA-2)' ๋ชจ๋ธ๋ก ๋๊ณ
๊ฐ์ ๊ฒ์ฆ๊ธฐ๋ฅผ ๋ ๋ฒ ๋๋ฆฐ๋ค.
- ์ทจ์ฝ ๋ชจ๋ธ : ์์กฐ ์ธ์ฆ์๊ฐ '์๋ช
์ผ์น' ๋ก ์น์ธ๋๋ค (2012๋
6์ ์ด์ ์ํฉ)
- ๊ฐํ ๋ชจ๋ธ : ์์กฐ ์ธ์ฆ์๊ฐ ๊ฑฐ๋ถ๋๋ค (KB2718704 ์ดํ ์ํฉ)
์คํ: python3 poc2_verify_bypass.py (๊ฒฉ๋ฆฌ ๋ฉ ์ ์ฉ, ๋คํธ์ํฌ ์ ๊ทผ ์์)
"""
import hashlib
import sys
sys.path.insert(0, "/tmp")
from poc1_collision import OUT_BITS, toy_hash, chosen_prefix_collision # ์ถ์ ํด์ ์ฌ์ฌ์ฉ
def verify(cert_body: bytes, signed_digest, hashfn) -> bool:
"""์ถ์ X.509 ๊ฒ์ฆ๊ธฐ. ์ค์ ๊ฒ์ฆ์ RSA ์๋ช
๊ฒ์ฆ๊น์ง ํฌํจํ์ง๋ง,
Flame ์ด ์ค์ ๋ก ๋ซ์ ์ง์ ์ '์ธ์ฆ์ ๋ณธ๋ฌธ ํด์ == ์๋ช
์ ๋ด๊ธด ํด์'
๋ผ๋ ์ ์ ์์ผ๋ฏ๋ก ๊ทธ ์ง์ ๋ง ๋ชจ๋ธ๋งํ๋ค."""
return hashfn(cert_body) == signed_digest
if __name__ == "__main__":
GOOD = b"TBSCertificate|serial=0x1B|key=MS_TERMINAL_SERVER_GOOD|"
EVIL = b"TBSCertificate|serial=0x1B|key=ATTACKER_CONTROLLED_EVIL|"
sa, sb, _ = chosen_prefix_collision(GOOD, EVIL)
good_cert, evil_cert = GOOD + sa, EVIL + sb
# ๋ง์ดํฌ๋ก์ํํธ๊ฐ '์ ํ' ์ธ์ฆ์์ ๋ํด ์๋ช
ํ์ ๋ ๋ด๊ธด ๋ค์ด์ ์คํธ
ms_digest = toy_hash(good_cert)
print("[2012๋
6์ ์ด์ โ MD5 ๊ณ์ด ์ทจ์ฝ ํด์ (์ฌ๊ธฐ์๋ %d๋นํธ ์ถ์ ๋ชจ๋ธ)]" % OUT_BITS)
print(" ์๋ช
์ ๋ด๊ธด ๋ค์ด์ ์คํธ :", hex(ms_digest))
print(" ์ ํ ์ธ์ฆ์ ๊ฒ์ฆ :", verify(good_cert, ms_digest, toy_hash))
print(" ์์กฐ ์ธ์ฆ์ ๊ฒ์ฆ :", verify(evil_cert, ms_digest, toy_hash), " <-- ํต๊ณผ (๋ฌธ์ !)")
print()
sha = lambda d: hashlib.sha256(d).hexdigest()[:16]
print("[KB2718704 ์ดํ โ SHA-256 ๊ฐ์ ]")
ms_digest_sha = sha(good_cert)
print(" ์๋ช
์ ๋ด๊ธด ๋ค์ด์ ์คํธ :", ms_digest_sha)
print(" ์ ํ ์ธ์ฆ์ ๊ฒ์ฆ :", verify(good_cert, ms_digest_sha, sha))
print(" ์์กฐ ์ธ์ฆ์ ๊ฒ์ฆ :", verify(evil_cert, ms_digest_sha, sha), " <-- ๊ฑฐ๋ถ (์ ์)")
print()
print("๊ตํ: ํด์ ์๊ณ ๋ฆฌ์ฆ์ '์๋ช
์ฒด๊ณ ์ ์ฒด'๋ฅผ ๋ ๋ฐ์น๋ ๋ถํ์ด๋ค.")
print(" MD5โSHA-2 ์ ํ + ๋ฌธ์ ์ธ์ฆ์ ํ๊ธฐ(KB2718704)๋ก๋ง ๋ซํ๊ณ ,")
print(" ๊ฐ์ ๊ณ์ด์ ์ถฉ๋ ๊ณต๊ฒฉ์ 2008๋
(์์กฐ CA ์ธ์ฆ์), 2017๋
(SHA-1, SHAttered)์ ๋ฐ๋ณต๋๋ค.")
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
[2012๋
6์ ์ด์ โ MD5 ๊ณ์ด ์ทจ์ฝ ํด์ (์ฌ๊ธฐ์๋ 20๋นํธ ์ถ์ ๋ชจ๋ธ)]
์๋ช
์ ๋ด๊ธด ๋ค์ด์ ์คํธ : 0xf3fe1
์ ํ ์ธ์ฆ์ ๊ฒ์ฆ : True
์์กฐ ์ธ์ฆ์ ๊ฒ์ฆ : True <-- ํต๊ณผ (๋ฌธ์ !)
[KB2718704 ์ดํ โ SHA-256 ๊ฐ์ ]
์๋ช
์ ๋ด๊ธด ๋ค์ด์ ์คํธ : b5ef3fe168bbc968
์ ํ ์ธ์ฆ์ ๊ฒ์ฆ : True
์์กฐ ์ธ์ฆ์ ๊ฒ์ฆ : False <-- ๊ฑฐ๋ถ (์ ์)
PoC โข ์จ์ ๋ชจ๋ ์ฐพ๊ธฐ โ "๋ชจ๋ ๋ชฉ๋ก"์ ๋ฏฟ์ง ์๋ ํ์ง
Flame์ ๋ชจ๋์ ํ๋ก์ธ์ค๊ฐ ๋ณด๊ณ ํ๋ ๋ชจ๋ ๋ชฉ๋ก์ ๋ํ๋์ง ์๋๋ค. ๊ทธ๋์ ํ์ง์ ์ถ๋ฐ์ ์ "๋ฌด์์ด ๋ก๋๋๋"๊ฐ ์๋๋ผ "์ค๋ช ๋์ง ์๋ ๋ฉ๋ชจ๋ฆฌยท๋์คํฌ ํ์ ์ด ์๋"๋ค. ํฉ์ฑ ๋ฐ์ดํฐ๋ก ๊ทธ ๋์กฐ ๋ก์ง์ ์คํํ๋ค.
#!/usr/bin/env python3
"""Flame(2012) ๊ต์ก์ฉ PoC #3 โ ๋ชจ๋ํ ์
์ฑ์ฝ๋์ '์๋ ๋ชจ๋' ํ์ง (๋ชจ์ ๋ฐ์ดํฐ).
Flame ์ ๋ชจ๋์ ํ๋ก์ธ์ค๊ฐ ์ค์ค๋ก ๋ณด๊ณ ํ๋ '๋ก๋๋ ๋ชจ๋ ๋ชฉ๋ก'์ ๋ํ๋์ง ์๊ฒ
๋ฉ๋ชจ๋ฆฌ์๋ง ์กด์ฌํ๋ค. ์ฆ ์ ์์ ์ธ ๋ชจ๋ ์ด๊ฑฐ API ๋ก๋ ์ ๋ณด์ธ๋ค. ์ด PoC ๋
'๋ณด๊ณ ๋ ๋ชจ๋ ๋ชฉ๋ก' ๊ณผ '์ค์ ๋ฉ๋ชจ๋ฆฌ ํ์ ' ์ ๋์กฐํด ๊ทธ ๊ฐ๊ทน์ ์ฐพ์๋ด๋
ํ์ง ๋ก์ง์ ํฉ์ฑ ๋ฐ์ดํฐ๋ก ์คํํ๋ค. (์ค์ ์ฃผ์
/์๋ ๊ธฐ๋ฒ ์ฝ๋๋ ์์)
์คํ: python3 poc3_hidden_module_audit.py (๊ฒฉ๋ฆฌ ๋ฉ ์ ์ฉ)
"""
import json
# โโ 1. ํฉ์ฑ(toy) ๋ฐ์ดํฐ: ์นจํด ํธ์คํธ ํ ๋๋ฅผ ํ๋ด๋ธ๋ค โโโโโโโโโโโโโโโโโโโโโโโโโ
HOST = {
"name": "win-lab-07",
"reported_modules": [ # ํ๋ก์ธ์ค๊ฐ '๋ณด๊ณ ํ๋' ๋ชจ๋ (์ ์ ์ด๊ฑฐ ๊ฒฐ๊ณผ)
{"name": "kernel32.dll", "path": r"C:\Windows\System32\kernel32.dll"},
{"name": "user32.dll", "path": r"C:\Windows\System32\user32.dll"},
{"name": "ws2_32.dll", "path": r"C:\Windows\System32\ws2_32.dll"},
],
"memory_regions": [ # ์ค์ ๋ฉ๋ชจ๋ฆฌ ์ค์บ ๊ฒฐ๊ณผ (๊ฐ์ ์ฃผ์ ๊ธฐ์ค)
{"base": "0x400000", "size": 262144, "prot": "RX", "backed_by_file": True, "name": "app.exe"},
{"base": "0x7A0000", "size": 131072, "prot": "RWX", "backed_by_file": False, "name": None},
{"base": "0x9C0000", "size": 624640, "prot": "RWX", "backed_by_file": False, "name": None},
{"base": "0xB00000", "size": 40960, "prot": "RW", "backed_by_file": True, "name": "config.db"},
],
"disk_artifacts": [ # ๋์คํฌ์ ๋จ์ ํ์ (ํ์ผ/๋ ์ง์คํธ๋ฆฌ๋ฅ)
{"path": r"C:\Windows\system32\msvcrt10.dll", "type": "file", "note": "์๋ช
์์"},
{"path": r"HKLM\SYSTEM\CurrentControlSet\Services\mssndsrv", "type": "registry",
"note": "๊ฐ์ง ์ค๋์ค ๋๋ผ์ด๋ฒ ์๋น์ค(์ง์์ฑ)"},
],
}
MIN_ANON_RWX = 64 * 1024 # ์ต๋ช
RWX ์์ญ ํฌ๊ธฐ ์๊ณ๊ฐ
def audit(host, min_size=MIN_ANON_RWX):
"""์ ์ ์ด๊ฑฐ ๋ชฉ๋ก์ ์๋ '์ด๋ฆ ์๋ RWX ๋ฉ๋ชจ๋ฆฌ' ๋ฅผ ์ฐพ์๋ธ๋ค."""
findings = []
listed = {m["path"].lower() for m in host["reported_modules"]}
for r in host["memory_regions"]:
anon = (r["name"] is None) and (not r["backed_by_file"])
if anon and r["size"] >= min_size and "W" in r["prot"] and "X" in r["prot"]:
findings.append({
"host": host["name"], "rule": "anon_rwx_region",
"severity": "high",
"detail": "ํ์ผ๋ก ์ค๋ช
๋์ง ์๋ %s ์์ญ %d bytes @ %s"
% (r["prot"], r["size"], r["base"]),
})
for a in host["disk_artifacts"]:
if a["type"] == "file" and "์๋ช
์์" in a["note"]:
findings.append({"host": host["name"], "rule": "unsigned_module_on_disk",
"severity": "medium", "detail": a["path"]})
if a["type"] == "registry" and "์ค๋์ค" in a["note"]:
findings.append({"host": host["name"], "rule": "suspicious_persistence",
"severity": "medium",
"detail": "%s โ %s" % (a["path"], a["note"])})
# ๋ชจ๋ ์ ์์ฒด์ ์ด์: ๋ฉ๋ชจ๋ฆฌ ํ์ ๋๋น ์ด๊ฑฐ ๋ชฉ๋ก์ด ์ง๋์น๊ฒ ์งง์
named = sum(1 for r in host["memory_regions"] if r["name"])
if len(listed) < named:
findings.append({"host": host["name"], "rule": "module_enum_mismatch",
"severity": "high",
"detail": "์ด๊ฑฐ %d๊ฐ < ๋ช
๋ช
๋ ์์ญ %d๊ฐ" % (len(listed), named)})
return findings
if __name__ == "__main__":
res = audit(HOST)
print("ํธ์คํธ:", HOST["name"])
print(json.dumps(res, ensure_ascii=False, indent=2))
print("\nํ์ง ๊ฑด์:", len(res), "โ '๋ชจ๋ ์ด๊ฑฐ ๊ฒฐ๊ณผ'๋ง ๋ฏฟ์ผ๋ฉด 0๊ฑด, ๋ฉ๋ชจ๋ฆฌ+RWX+์๋ช
๋์กฐ๋ก %d๊ฑด" % len(res))
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ, ์์ฝ):
ํธ์คํธ: win-lab-07
[anon_rwx_region] ํ์ผ๋ก ์ค๋ช
๋์ง ์๋ RWX ์์ญ 131072 bytes @ 0x7A0000 (high)
[anon_rwx_region] ํ์ผ๋ก ์ค๋ช
๋์ง ์๋ RWX ์์ญ 624640 bytes @ 0x9C0000 (high)
[unsigned_module_on_disk] C:\Windows\system32\msvcrt10.dll (medium)
[suspicious_persistence] HKLM\SYSTEM\...\Services\mssndsrv โ ๊ฐ์ง ์ค๋์ค ๋๋ผ์ด๋ฒ (medium)
ํ์ง ๊ฑด์: 4 โ '๋ชจ๋ ์ด๊ฑฐ ๊ฒฐ๊ณผ'๋ง ๋ฏฟ์ผ๋ฉด 0๊ฑด, ๋ฉ๋ชจ๋ฆฌ+RWX+์๋ช
๋์กฐ๋ก 4๊ฑด
PoC โฃ WPAD/NBNS ์คํธํ + "๊ฐ์ง ์ ๋ฐ์ดํธ ์๋ฒ" ์ ํ (127.0.0.1 ํ์ )
Flame์ LAN ์ ํ๋ฅผ ๋ฃจํ๋ฐฑ ์์์๋ง ์ฌํํ๋ค. ๊ฐ์ง ์ ๋ฐ์ดํธ ์๋ฒ(๋ชจ์ ๋งค๋ํ์คํธ๋ง ์ ๊ณต)๋ฅผ ๋์ฐ๊ณ , WPAD ์๋ ํ๋ก์ ํ์์ ํ๋ด ๋ธ ํด๋ผ์ด์ธํธ๊ฐ ๊ทธ ์๋ฒ์์ ๋งค๋ํ์คํธ๋ฅผ ๋ฐ๋๋ค. ์ด์ด์ ๋ฐฉ์ด ์คํฌ๋ฆฝํธ๊ฐ "WPAD๊ฐ ์ฌ๋ด ์ ๋ฏธ์ฌ๋ก ํด์๋์ง ์์" + "์ ๋ฐ์ดํธ๋ฅผ ์ฌ๋ด WSUS๊ฐ ์๋ ํธ์คํธ์์ ๋ฐ์" ๋ ์ ํธ๋ก ํ์งํ๋ค. ์ค์ ํ์ด๋ก๋ ๋ฐฐํฌยท์ค์น๋ ํ์ง ์๋๋ค.
#!/usr/bin/env python3
"""Flame(2012) ๊ต์ก์ฉ PoC #4 โ WPAD/NBNS ์คํธํ + '๊ฐ์ง ์
๋ฐ์ดํธ ์๋ฒ' LAN ์ ํ(๋ชจ์).
Flame ์ ๊ฐ์ LAN ์์์ WINS/NBNS ์ด๋ฆ ์กฐํ์ WPAD(์๋ ํ๋ก์ ์ค์ )๋ฅผ ์
์ฉํด
์์ ์ '์ ํ ๋ง์ดํฌ๋ก์ํํธ ์
๋ฐ์ดํธ ์๋ฒ' ๋ก ์์ฅํ๊ณ , ์์กฐ ์ธ์ฆ์๋ก ์๋ช
๋
๊ตฌ์ฑ ์์๋ฅผ ๋ฐฐํฌํด ๋ค๋ฅธ PC ๊ฐ ์ค์ค๋ก ์ค์นํ๊ฒ ๋ง๋ค์๋ค.
์ด PoC ๋ ๊ทธ ๋ฉ์ปค๋์ฆ์ **127.0.0.1 ์์์๋ง** ์ฌํํ๋ค.
1) 127.0.0.1:8412 ์ '๊ฐ์ง ์
๋ฐ์ดํธ ์๋ฒ' ๋ฅผ ๋์ด๋ค (mock ๋งค๋ํ์คํธ๋ง ์ ๊ณต)
2) ์๋ ํ๋ก์(WPAD) ํ์์ ํ๋ด ๋ธ ํด๋ผ์ด์ธํธ๊ฐ ๊ทธ ์๋ฒ์์ ๋งค๋ํ์คํธ๋ฅผ ๋ฐ๋๋ค
3) ๋ฐฉ์ด ์คํฌ๋ฆฝํธ๊ฐ 'WPAD ์ง์ + ๋น์ธ๊ฐ ์
๋ฐ์ดํธ ์๋ฒ ์ ์' ์ ๋ก๊ทธ์์ ํ์งํ๋ค
์ค์ ์ํฐํฉํธ ๋ฐฐํฌยท์ค์นยท์คํ์ ํ์ง ์์ผ๋ฉฐ, ์ธ๋ถ ๋คํธ์ํฌ๋ก ๋๊ฐ์ง ์๋๋ค.
์คํ: python3 poc4_wpad_rogue_wsus.py (๊ฒฉ๋ฆฌ ๋ฉ ์ ์ฉ)
"""
import http.server
import json
import socketserver
import threading
import urllib.request
ROGUE_PORT = 0 # 0 = ์ด์์ฒด์ ๊ฐ ๋น ํฌํธ๋ฅผ ๋ฐฐ์ (๋ฉ ํ๊ฒฝ ์ถฉ๋ ๋ฐฉ์ง)
CORP_WSUS = "10.20.30.11" # ๊ฐ์์ ์ฌ๋ด ์ ํ ์
๋ฐ์ดํธ ์๋ฒ
LOG = [] # ํฉ์ฑ ๋ก๊ทธ(ํ์ง ๋์)
def rogue_server():
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self): # noqa: N802
# ํ๋ก์ ๊ฒฝ์ ์์ฒญ์ ์ ๋ URI(http://wpad/...)๋ก ๋ค์ด์จ๋ค
if "/updates/manifest" in self.path:
body = json.dumps({
"note": "๊ต์ก์ฉ ๋ชจ์ ๋งค๋ํ์คํธ โ ์ค์ ํ์ด๋ก๋ ์์",
"server": "ROGUE-WSUS(127.0.0.1)",
"artifact": {"name": "mock-update.cab", "sha256": "0" * 64},
}).encode()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(body)
else:
self.send_response(404)
self.end_headers()
def log_message(self, *a): # ์ฝ์ ์ก์ ์ต์
pass
socketserver.TCPServer.allow_reuse_address = True
srv = socketserver.TCPServer(("127.0.0.1", ROGUE_PORT), H)
threading.Thread(target=srv.serve_forever, daemon=True).start()
return srv
def victim_client(victim, proxy_ip, proxy_port):
"""WPAD ์๋ ํ๋ก์ ํ์ โ ํ๋ก์๋ฅผ ํตํด ์
๋ฐ์ดํธ ๋งค๋ํ์คํธ ์์ฒญ."""
LOG.append({"evt": "wpad_query", "host": victim, "answer": proxy_ip, "suffix": "local"})
import urllib.error
proxy = urllib.request.ProxyHandler({"http": "http://%s:%d" % (proxy_ip, proxy_port)})
opener = urllib.request.build_opener(proxy)
url = "http://wpad/updates/manifest.xml" # ์ด๋ฆ ํด์์ ํ๋ก์๊ฐ ๋์ ์ํ
try:
with opener.open(url, timeout=5) as r:
data = json.loads(r.read().decode())
except Exception as e: # ํ๋ก์ ๋ฏธ์ฌ์ฉ ์ ์คํจ โ ์ ์
data = {"error": str(e)}
LOG.append({"evt": "update_fetch", "host": victim,
"server_ip": proxy_ip, "url": url,
"artifact": data.get("artifact", {}).get("name", "none")})
return data
def detect(log, corp_wsus=CORP_WSUS):
"""WPAD ์ง์๊ฐ ์ฌ๋ด ์ ๋ฏธ์ฌ๋ก ํด์๋์ง ์์๊ฑฐ๋, ์
๋ฐ์ดํธ๋ฅผ ์ฌ๋ด WSUS ๊ฐ ์๋
ํธ์คํธ์์ ๋ฐ์ ๊ฒฝ์ฐ๋ฅผ '๊ณ ์ ๋ขฐ' ์ด์์ผ๋ก ๋ถ๋ฅํ๋ค."""
alerts = []
wpad_hosts = {e["host"] for e in log if e["evt"] == "wpad_query" and e["suffix"] != "corp.example.com"}
for e in log:
if e["evt"] == "wpad_query" and e["suffix"] != "corp.example.com":
alerts.append({"rule": "wpad_resolved_outside_corp", "host": e["host"],
"detail": "wpad ์ง์ ์๋ต=%s (์ฌ๋ด ์ ๋ฏธ์ฌ ์๋)" % e["answer"]})
if e["evt"] == "update_fetch" and e["server_ip"] != corp_wsus:
alerts.append({"rule": "update_from_untrusted_host", "host": e["host"],
"detail": "์
๋ฐ์ดํธ ์๋ฒ=%s (์ฌ๋ด WSUS=%s ์๋)" % (e["server_ip"], corp_wsus)})
return alerts, wpad_hosts
if __name__ == "__main__":
srv = rogue_server()
port = srv.server_address[1]
print("๊ฐ์ง ์
๋ฐ์ดํธ ์๋ฒ ๊ธฐ๋: 127.0.0.1:%d (๊ต์ก์ฉ ๋ชจ์)" % port)
try:
victim_client("pc-sales-04", "127.0.0.1", port) # ๋ก์ปฌ ๋ฃจํ๋ฐฑ = '๊ณต๊ฒฉ์ ํ๋ก์' ์ญํ
finally:
srv.shutdown()
srv.server_close()
print("โโ ํฉ์ฑ ๋ก๊ทธ โโ")
for e in LOG:
print(" ", e)
alerts, hosts = detect(LOG)
print("\nโโ ํ์ง ๊ฒฐ๊ณผ โโ")
for a in alerts:
print(" [%s] %s โ %s" % (a["rule"], a["host"], a["detail"]))
print("\nWPAD ๋ฅผ ์ฌ๋ด ๋ฐ์ผ๋ก ํด์ํ ํธ์คํธ:", ", ".join(sorted(hosts)) or "์์")
print("โ ๊ตํ: 'WPAD ์๋ ํ์ ๋๊ธฐ + NBNS ์ฐจ๋จ + ์
๋ฐ์ดํธ ์๋ฒ ๊ณ ์ (์ ์ฑ
)' ์ด")
print(" ์ด ์ ํ ๊ฒฝ๋ก๋ฅผ ํต์งธ๋ก ๋ซ๋๋ค (KB2718704 ๋ ์ ๋ขฐ ์ฌ์ฌ์ ๋ง์ง๋ง ๊ตฌ๋ฉ์ ๋ง์๋ค).")
์คํ ๊ฒฐ๊ณผ(์ด๋ฒ ์คํ ์ค์ ์ถ๋ ฅ):
๊ฐ์ง ์
๋ฐ์ดํธ ์๋ฒ ๊ธฐ๋: 127.0.0.1:41127 (๊ต์ก์ฉ ๋ชจ์)
โโ ํฉ์ฑ ๋ก๊ทธ โโ
{'evt': 'wpad_query', 'host': 'pc-sales-04', 'answer': '127.0.0.1', 'suffix': 'local'}
{'evt': 'update_fetch', 'host': 'pc-sales-04', 'server_ip': '127.0.0.1',
'url': 'http://wpad/updates/manifest.xml', 'artifact': 'mock-update.cab'}
โโ ํ์ง ๊ฒฐ๊ณผ โโ
[wpad_resolved_outside_corp] pc-sales-04 โ wpad ์ง์ ์๋ต=127.0.0.1 (์ฌ๋ด ์ ๋ฏธ์ฌ ์๋)
[update_from_untrusted_host] pc-sales-04 โ ์
๋ฐ์ดํธ ์๋ฒ=127.0.0.1 (์ฌ๋ด WSUS=10.20.30.11 ์๋)
๋ ํ๋์ ํน์ง โ Flame์ Lua๋ก ๊ธฐ๋ฅ์ ํ์ฅํ๋ค. ์๋๋ ๊ทธ ๊ตฌ์กฐ๋ฅผ ํ๋ด ๋ธ ๋ฌดํดํ ๋ชจ๋ ์์๋ค(๋ก์ปฌ ๊ฐ์ฌ ๋ก๊ทธ ํ ์ค์ ๋จ๊ธธ ๋ฟ์ด๋ฉฐ, ์์งยท์ ์ก ๊ธฐ๋ฅ์ ์๋ค).
-- flame_like_module.lua : ํ๋ฌ๊ทธ์ธ ๊ตฌ์กฐ ์ฌํ์ฉ (๊ต์ก์ฉยท๋ฌดํด)
local M = {}
function M.tick(cfg) -- ๋ก๋๊ฐ ์ฃผ๊ธฐ์ ์ผ๋ก ํธ์ถํ๋ ์ง์
์
local f = assert(io.open(cfg.audit_log, "a"))
f:write("[audit] module tick ", os.date("%Y-%m-%d %H:%M:%S"), "\n")
f:close()
return true -- ์คํจํด๋ ๋ณธ์ฒด๋ ๊ณ์ ๋์(๋ชจ๋ ๊ฒฉ๋ฆฌ)
end
return M
7. ๋ฐฉ์ด ๋ฐฉ๋ฒ/๊ตํ โ Flame์ด ๋จ๊ธด ์ฒดํฌ๋ฆฌ์คํธ
- ํด์ยท์๋ช ์๊ณ ๋ฆฌ์ฆ์ ํ๋ํํ๋ผ: MD5ยทSHA-1 ๊ธฐ๋ฐ ์๋ช ๊ฒ์ฆ์ "์๋ช ์ด ์๋ค"๋ ์ด์ ๋ง์ผ๋ก ์ ๋ขฐ๋ฅผ ์ฃผ์ง ๋ชปํ๋ค. ์ฝ๋์๋ช ยทTLSยท์ ๋ฐ์ดํธ ๋งค๋ํ์คํธ์ ํด์๋ฅผ SHA-256 ์ด์์ผ๋ก ๊ฐ์ ํ๊ณ , ์ ๋ขฐ ์ ์ฅ์์ ์ทจ์ฝ CAยท๋ง๋ฃ ์ธ์ฆ์๋ฅผ ์ ๋ฆฌํ๋ค. (Flame์ ๊ตํ์ด ๊ทธ๋๋ก KB2718704๋ค.)
- ์๋ ํ๋ก์ ํ์์ ๋๊ณ ์ด๋ฆ ํด์์ ํต์ ํ๋ผ: WPAD ์๋ ํ์ ๋นํ์ฑํ, NBNS/WINS ์ฐจ๋จ, ์ ๋ฐ์ดํธ ์๋ฒ๋ฅผ ์ ์ฑ ์ผ๋ก ๊ณ ์ . "์ ๋ฐ์ดํธ๋ ํญ์ ์ฌ๋ด WSUS์์๋ง" ์ด ํ ์ค์ด Flame์ LAN ์ ํ ๊ฒฝ๋ก๋ฅผ ํต์งธ๋ก ๋ซ๋๋ค.
- ๋ชจ๋ ์ด๊ฑฐ๋ฅผ ์ ๋ขฐํ์ง ๋ง๋ผ: ํ์ง ์ ํธ๋ '์ด๋ฆ ์๋ ๋ชฉ๋ก'์ด ์๋๋ผ ํ์ผ๋ก ์ค๋ช ๋์ง ์๋ RWX ๋ฉ๋ชจ๋ฆฌ, ์๋ช ์๋ ์์คํ ๊ฒฝ๋ก ํ์ผ, ์ ์ฒด๋ถ๋ช ์ ์๋น์ค ๋ฑ๋ก์ด๋ค. PoC โข์ 4๊ฐ์ง ๊ท์น์ด ์ต์ ์ธํธ๋ค.
- ๋๋ผ์ด๋ฒยท์๋น์ค ๋ณ๊ฒฝ์ ๊ฐ์ํ๋ผ: ๊ฐ์ง ์ค๋์ค ๋๋ผ์ด๋ฒ ๊ฐ์ ์์ฅ ์ง์์ฑ์ ๋ฌด๊ฒฐ์ฑ ๋ชจ๋ํฐ๋ง(๋๋ผ์ด๋ฒ ๋ก๋ยท์๋น์ค ์์ฑ ์ด๋ฒคํธ)์ผ๋ก ์กํ๋ค.
- ๋คํธ์ํฌ ๋ถ๋ฆฌ๋ง์ผ๋ก๋ ๋ถ์กฑํ๋ค: ํ์ ์กฐ์ง์ ์ด๋ฏธ ํฐ๋ฏธ๋์ ์ธํฐ๋ท์์ ๋ถ๋ฆฌํ ์ํ์์ง๋ง, ๋ด๋ถ LANยทUSBยท๋ธ๋ฃจํฌ์ค๊ฐ ๋จ์ ์์๋ค. ์์ด๊ฐญ์ "์ธํฐ๋ท ์ฐจ๋จ"์ด ์๋๋ผ ๋ชจ๋ ๋งค์ฒด์ ํต์ ๋ฅผ ๋ปํ๋ค.
- ์ฆ๊ฑฐ ๋ณด์กด์ ์ค๊ณํ๋ผ: ์ํญ(kill) ๊ธฐ๋ฅ์ ์ฌ๊ณ ๋์์ ์ ์ด๋ค. ๋ก๊ทธ๋ฅผ ์คํํธ์คํธ๋ก ์ฆ์ ์ ์กยท๋ณด์กดํ์ง ์์ผ๋ฉด, ๊ณต๊ฒฉ์๋ ํ์ ์ ์ค์ค๋ก ์ง์ฐ๊ณ ์ฌ๋ผ์ง๋ค.
8. ์ถ์ฒ
- Wikipedia โ Flame (malware) โ en.wikipedia.org/wiki/Flame_(malware)
- Wikipedia โ Duqu / Stuxnet ๊ณ๋ณด โ en.wikipedia.org/wiki/Duqu ยท en.wikipedia.org/wiki/Stuxnet
- CrySyS Lab (BME) โ sKyWIper (a.k.a. Flame) analysis report (2012-05-28) โ crysys.hu/skywiper/skywiper.pdf
- Microsoft ๋ณด์ ๊ณต์ง 2718704 โ Unauthorized Digital Certificates Could Allow Spoofing (2012-06-03) โ learn.microsoft.com/โฆ/2718704
- ์นด์คํผ์คํค ์ํ์ด๋ฆฌ์คํธ โ Flame ๊ด๋ จ ๋ถ์ ์๋ฆฌ์ฆ(MD5 ์ถฉ๋ยท์ ํยท์ํญ) โ securelist.com
- Washington Post (2012-06-19) โ U.S., Israel developed Flame computer virus to slow Iranian nuclear efforts โ washingtonpost.com/โฆ/gJQA6xBPoV_story.html
- Guerrero-Saade & Cutler โ Flame 2.0 ์ฌ๋ฐ๊ฒฌ(2019) โ virusbulletin.com
โป ๋ณธ๋ฌธ์ PoC ์ฝ๋๋ ๊ตฌ์กฐ ์ดํด ๋ชฉ์ ์ ๊ต์ก์ฉ ๋ชจ์ ์ฝ๋์ด๋ฉฐ, ์ค์ Flame ์ํยท์ค์ MD5 ์ถฉ๋์ยท ์ค์ ์ต์คํ๋ก์ยทC2 ์ฃผ์ยท์ ํฌ ๊ธฐ๋ฅ์ ํฌํจํ์ง ์์ต๋๋ค. ๋ชจ๋ ์ค์ต์ ์ธํฐ๋ท๊ณผ ๋ถ๋ฆฌ๋ ๊ฒฉ๋ฆฌ ๋ฉ VM์ ์ ์ ๋ก ํฉ๋๋ค.
ํจ๊ป ์ฝ๊ธฐ: โ๏ธ Stuxnet (2010) โ ์์ด๊ฐญ์ ๋์ ์ธ๊ณ ์ต์ด์ ์ฌ์ด๋ฒ ๋ฌด๊ธฐ ยท ๐ฅ Code Red (2001) โ ํ์ผ๋ฆฌ์ค ๋ฉ๋ชจ๋ฆฌ ์์ฃผ ์์ ์ํ ยท โก Industroyer (2016) โ ํ๋กํ ์ฝ์ ๊ทธ๋๋ก ๋งํ ICS ์ ์ฑ์ฝ๋ ยท ๐ฆ SolarWinds SUNBURST (2020) โ ๊ณต๊ธ๋งยท์๋ช ์ ๋ขฐ์ ๋ถ๊ดด ยท โ ๏ธ NotPetya (2017) โ ์ ๋ขฐ๋ฐ๋ ์ ๋ฐ์ดํธ ์ฑ๋์ ๋ฌด๊ธฐ๋ก ์ด ์์ดํผ