반응형
개요
- Packet payload에 연속 된 HEX 값을 가지는 Garbage DATA packet을 drop 하기 위한 목적
- iptables nfqueue 모듈을 이용하여 python에서 packet을 처리하고자 함.
필터링 조건
iptables
- 3way-handshaking 후에 client에서 인입되는 첫번째 psh ack packet만을 nfqueue로 보내 python에서 처리
- iptables 정책 설명
- Client SYN 전송 -> Client src ip를 SYN2 set -> SERVER로 SYN 전송
- SERVER에 Client로 SYN ACK 전송
- Client ACK 전송 -> Client src ip가 SYN2 에 있으면 ACK2 set 하고 SYN2에서 삭제 -> SERVER로 ACK 전송
- Client PSH ACK 전송 -> Client src ip가 ACK2에 있으면 nfqueue로 보내고 ACK2에서 삭제
- python에서 필터링 조건에 의해 drop or forward 결정
python
- packet payload에 처음 3 Byte 값을 서로 비교하여 같은 HEX 값을 가지면 drop
- payload.data1 == payload.data2 == payload.data3 => pkt.drop()
- packet payload에 2번째 Byte 값이 특정 hex값을 가지지 않으면 drop
- payload.data2 != [<사용자 정의>] => pkt.drop()
설치 및 설정
iptables
- 인라인 방식으로 물리 구성되고 transparent 하고 동작하는 linux 머신이므로 iptables FORWARD 체인에 설정
- WHITELIST chain : 최상위 우선순위를 가지는 chain으로 python에서 조건을 모두 만족하는 src ip는 WHITELIST chain 으로 accept 정책이 들어감.
- BLACKLIST chain : WHITELIST 다음 순위를 가지는 chain으로 python에서 조건이 어긋나는 src ip는 BLACKLIST chain으로 drop 정책이 들어감
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [845:52660]
:OUTPUT ACCEPT [0:0]
:LEARN2 - [0:0]
:LEARN2_ACK - [0:0]
:LEARN2_PA - [0:0]
:SERVICE - [0:0]
:WHITELIST - [0:0]
:BLACKLIST - [0:0]
:LIMIT - [0:0]
:CONTROL - [0:0]
:CONNLIMIT - [0:0]
# 모든 packet을 SERVICE chain 으로 보냄
-A FORWARD -j SERVICE
# WHITELIST chain은 python에서 처리하며 무조건 accept 되야하는src ip.
-A SERVICE -j WHITELIST
# BLACKLIST chain은 python에서 처리하며 무조건 drop 되야하는 src ip.
-A SERVICE -j BLACKLIST
# CONTROL chain으로 보낼 dstip,port를 설정
-A SERVICE -d <dst ip> -p tcp --dport <dst port> -j CONTROL
-A CONTROL -p tcp -m state --state NEW -j LIMIT
-A CONTROL -p tcp -m state --state NEW -j CONNLIMIT
-A CONTROL -j LEARN2
-A CONTROL -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
# limit 모듈을 이용하여 패킷 흐름 제한(qos)
-A LIMIT -d <dst ip> -p tcp --dport <dst port> -m limit --limit 10/s --limit-burst 10 -j RETURN
# connlimit 모듈을 이용하여 connection 제한
-A CONNLIMIT -d <dst ip> -p tcp --dport <dst port> -m connlimit --connlimit-above 3 --connlimit-mask 32 --connlimit-saddr -j REJECT --reject-with tcp-reset
-A CONNLIMIT -j RETURN
# syn flag를 가지는 packet의 src ip를 SYN2에 넣음.
-A LEARN2 -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m recent --set --name SYN2 --mask 255.255.255.255 --rsource -j RETURN
# ack flag를 가지는 packet의 src ip를 SYN2에서 비교하여 있으면 LEARN2_ACK으로 보냄
-A LEARN2 -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG ACK -m recent --rcheck --name SYN2 --mask 255.255.255.255 --rsource -j LEARN2_ACK
# psh ack flag를 가지는 packet의 src ip를 ACK2와 비교하여 있으면 LEARN2_PA로 보냄
-A LEARN2 -p tcp -m tcp --tcp-flags PSH,ACK PSH,ACK -m recent --rcheck --name ACK2 --mask 255.255.255.255 --rsource -j LEARN2_PA
# ack flag를 가지는 packet의 src ip를 ACK2와 비교하여 있으면,
# 그리고 packet length가 60~1514Bytes 사이값이 아니면 return
-A LEARN2 -p tcp -m tcp --tcp-flags FIN,SYN,RST,PSH,ACK,URG ACK -m recent --rcheck --name ACK2 --mask 255.255.255.255 --rsource -m length ! --length 60:1514 -j RETURN
# ACK2에 있으면 DROP
-A LEARN2 -p tcp -m recent --rcheck --name ACK2 --mask 255.255.255.255 --rsource -m comment --comment ACK_DROP -j DROP
# ACK2 에 src ip를 set
-A LEARN2_ACK -p tcp -m recent --set --name ACK2 --mask 255.255.255.255 --rsource
# SYN2에 src ip를 삭제
-A LEARN2_ACK -p tcp -m recent --remove --name SYN2 --mask 255.255.255.255 --rsource
-A LEARN2_ACK -j RETURN
# ACK2에 src ip를 삭제
-A LEARN2_PA -p tcp -m recent --remove --name ACK2 --mask 255.255.255.255 --rsource
# nfqueue 1번에 packet 보냄
-A LEARN2_PA -p tcp -m tcp --tcp-flags PSH,ACK PSH,ACK -j NFQUEUE --queue-num 1
COMMIT
python
- 모듈 설치
pip3 install NetfilterQueue
pip3 install python-iptables
yum install libnetfilter_queue-devel
- 소스
#!/usr/bin/python3
import struct
import sys
import datetime
import iptc
import subprocess
from time import time
from netfilterqueue import NetfilterQueue
# 로그
def log_insert(result,srcip,srcport,data,dstip,dstport):
now = datetime.datetime.now()
b = now.strftime('%Y-%m-%d %H:%M:%S')
a = '[{}] [{}] [{}:{}] [{}] [{}:{}]'.format(result, b, srcip, srcport, data, dstip, dstport)
try:
f = open("/opt/nfqueue/String_Checker.log", 'a')
except:
print("ERROR: can't open file")
sys.exit(1)
f.write(a)
f.write('\n')
f.close()
# iptables 삽입
def ipt_insert(srcip,target,target_chain,expire):
num1 = 0
if num1 == 0:
b_time = expire
n_time = int(time())
f_time = n_time+b_time
rule = iptc.Rule()
rule.protocol = "tcp"
rule.target = rule.create_target(target)
if target == 'REJECT':
rule.target.reject_with = "tcp-reset"
rule.src = srcip
match = rule.create_match("comment")
match.set_parameter('comment', "expire=%s" % f_time)
chain = iptc.Chain(iptc.Table(iptc.Table.FILTER), target_chain)
chain.insert_rule(rule)
# src ip를 string으로 변환
def ip_to_string(ip):
return ".".join(map(lambda n: str(ip>>n & 0xff), [24,16,8,0]))
# packet 값 추출 및 필터링 조건
def print_and_accept(pkt):
pl = pkt.get_payload()
src_ip = struct.unpack('>I', pl[12:16])[0]
dst_ip = struct.unpack('>I', pl[16:20])[0]
tcp_offset = (struct.unpack('>B', pl[0:1])[0] & 0xf) * 4
tmp = struct.unpack('>B', pl[tcp_offset+12:tcp_offset+13])[0]
data_offset = ((tmp & 0xf0) >> 4) * 4
src_port = struct.unpack('>H', pl[tcp_offset+0:tcp_offset+2])[0]
dst_port = struct.unpack('>H', pl[tcp_offset+2:tcp_offset+4])[0]
data1 = pl[tcp_offset + data_offset:tcp_offset + data_offset + 1]
data2 = pl[tcp_offset + data_offset + 1:tcp_offset + data_offset + 2]
data3 = pl[tcp_offset + data_offset + 2:tcp_offset + data_offset + 3]
data_log = pl[tcp_offset + data_offset:tcp_offset + data_offset + 6]
num = 0
srcip = "%s/255.255.255.255" % ip_to_string(src_ip)
# packet payload data 1~3Byte 각 값이 같을 경우 num=0
if data1.hex() == data2.hex() and data3.hex() == data2.hex():
num = 0
# packet payload data 2Byte 자리 값이 var중 일치하지 하면 num+=1
else:
var = ["00","01","02","03","04","05","45"]
for plo in var:
if data2.hex() == plo:
num = num+1
# iptables 정책 삽입 및 로그 기록
if num == 0:
pkt.drop()
ipt_insert(srcip,'REJECT','BLACKLIST',60)
log_insert('REJECT',ip_to_string(src_ip),src_port,data_log.hex(),ip_to_string(dst_ip),dst_port)
else:
ipt_insert(srcip,'ACCEPT','WHITELIST',172800)
log_insert('ACCEPT',ip_to_string(src_ip),src_port,data_log.hex(),ip_to_string(dst_ip),dst_port)
pkt.accept()
# nfqueue bind
nfqueue = NetfilterQueue()
nfqueue.bind(1, print_and_accept)
try:
nfqueue.run()
except KeyboardInterrupt:
print
WHITELIST, BLACKLIST 정책 삭제
- python에서 iptables로 정책 삽입시 comment로 시간값을 주고 넣음
> iptables -L BLACKLIST -nv
0 0 DROP all -- * * 1.1.1.1 0.0.0.0/0 /* expire=1652428067 */
- 정책 삭제를 위한 것으로 정책 삽입시 "현 unixtime + 정책을 유지할 시간(unixtime)" 값을 comment로 넣음
match.set_parameter('comment', "expire=%s" % f_time)
- 정책 삭제 스크립트
iptables -L BLACKLIST -n --line-numbers | perl -ne 'next unless /(^\d+).*expire=(\d+)/; if ($2 < time)tables -D 'BLACKLIST' $1\n"; }'
반응형
'Language > Python' 카테고리의 다른 글
Python | List 요소값 기준 정렬(itemgetter) (0) | 2022.09.28 |
---|---|
[Python] File Access Time 을 이용한 파일 삭제 (0) | 2022.05.20 |
ubuntu12.04 paramiko module 수동 설치 (0) | 2022.04.26 |