Wednesday, March 9, 2011

Linux #7 : Dos / DDos 공격에 대한 대처법. #1

수 많은 Site들이 Dos / DDos 공격으로 인해 피해를 보고 있다. 필자도 일본에선 별로 느껴보진 못했지만, 미국에 와서 Dos / Ddos 공격을 여러번 경험하고 실제 지금도 항상 Hacker들을 경계하고 있습니다. 그럼, Dos / DDos 공격이란 무엇인지 먼저 알아보죠.




Dos 공격이란 Daniel of Service attacks의 약자로 Network의 대역을 소비하게 만들거나, 대량의 request를 Server에 보내 connection을 증가시켜 Server를 사용 불능 상태로 만들거나, 더불어 Password를 알아내기 위해 여러 방법을 통해 접근을 시도하는 Brute Force 공격등도 행해지는데, 공격 주체가 1대의 host라면 특정가능하여 해당 IP를 제한하면 되지만 (여기까지가 Dos), 이 공격 주체가 특정할 수 없는 다수일 경우 DDos (Distributed Denial of Service) 공격이라하고, 이를 방지하기 위해선 다른 방법이 필요합니다.



그럼, Dos / DDos와 관련 된 여러 공격에 대한 대처 방법에 대해서 알아보도록 하죠.



SYN Flooding 공격



기본적으로 TCP Packet은 3 Way Handshaking에 의해 초기 Session이 이루어지게 되는데, 즉 SYN -> SYN / ACK -> ACK로 완료가 이루어져야 하지만 정상적인 Session으로 종료가 가능하지만, SYN Packet을 요청하고 SYN / ACK Packet을 Server에서 보내려고 할 때 보내려는 주소가 무의미한 주소여서 Server가 해당 Session을 대기상태 (SYN_RECV)에 놓게 되어 Server 대기 Queue의 증가로 인한 Server Resource가 증가로 정상동작을 할 수 없게 만드는 공격을 말합니다.



확인은 일단 Server가 비정상적으로 느려지고, 접속히 원활하지 않게 되며 netstat 명령어를 통해 SYN_RECV의 수를 체크 했을 시 SYN_RECV의 대기 Queue가 많이 증가하였다고 의심해 볼 필요가 있습니다.


# netstat -an
grep SYN_RECV

...

tcp 0 0 ::ffff:xxx.xxx.xxx.xxx:80 ::ffff:xxx.xxx.xxx.xxx:28719 SYN_RECV

tcp 0 0 ::ffff:xxx.xxx.xxx.xxx:80 ::ffff:xxx.xxx.xxx.xxx:61544 SYN_RECV

...


대처 방법은 아래와 같이 3가지가 있습니다.

첫번째는 Backlog size를 증가시켜 주는 방법으로 Default값에서 1024 ~ 그 이상의 값을 설정합니다.


# SYN Flooding attack block (need to increase backlog size)

net.ipv4.tcp_max_syn_backlog=16000


이 설정을 하게 됨으로 얼마정도 버틸 수 있게 되지만 지속적으로 공격을 당했을 경우 queue가 꽉차게 됨으로

두번째인 SYN Cookies도 활성화를 시켜야 합니다. SYN Cookies가 활성화가 되면 ACK 중에 포함된 확인 응답 번호를 계산하여, 올바른 Session인지를 확인하게 됩니다. Session이 올바른 경우 처리를 행하고, 그렇지 않은 경우의 Connection에 대해서는 System Resource를 낭비하는 걸 막을 수 있습니다.


# Activate SYN Cookies

net.ipv4.tcp_syncookies=1


1인 경우가 활성화된 상태이고, 0인 경우가 비활성 상태입니다. 이 두개의 값은


# echo '16000' > /proc/sys/net/ipv4/tcp_max_syn_backlog

# echo '1' > /proc/sys/net/ipv4/tcp_syncookies


와 같이 설정가능하고 또한,


# sysctl -w net.ipv4.tcp_max_syn_backlog=16000

# sysctl -w net.ipv4.tcp_syncookies=1


또, /etc/sysctl.conf에 추가를 한 뒤


# sysctl -p /etc/sysctl.conf


로도 가능합니다. /etc/sysctl.conf에 넣었을 경우 System Reboot시 자동으로 등록이 되게 됩니다. SYN Cookies를 활성화 하면 확인 응답번호의 계산에 따른 Packet처리에서 Overhead가 발생하게 되고, Packet 량에 따라 CPU 사용량이 증가하여 Bottleneck이 발생 할 가능성이 있습니다.

그리고, 마지막으로 Iptable을 이용하여 비정상적은 IP를 block하면 됩니다.



운영상의 편의를 위해 아래와 같이 Script를 작성해 보았습니다.


### Block_SYN_Flooding.sh

#!/bin/sh

# ================================================================

# -script name

# Block_SYN_Flooding.sh

# -script desc

# Block DDos Attack through the SYN Flooding

# -how to use

# ./Block_SYN_Flooding.sh $1 $2

# $1 Type

# $2 List File

# -NOTE

#

# -create date

# 2010/06/22 NHN USA)Jeff KIM

##################################################################

# start script

MENU_INIT="[ start
stop
start_pp
stop_pp ]"



function Printmsg {

TYPE="$1"

TEXT="$2"

echo "`date \"+%Y-%m-%d %H:%M\"` [$1] $2" >> $TMP/message.log

echo "`date \"+%Y-%m-%d %H:%M\"` [$1] $2"

logger -p daemon.notice "[$1] $2"

}



function Prompt {

MSG="$1"

echo -n "$MSG" 1>&2

read RESP

echo "$RESP"
sed 's#^[[:space:]]*##; s#[[:space:]]*$##'

}



function Startprocess {

PROC_START="$INIT_PROC"

Printmsg "INFO" "Start : $PRC_NAME"

if [ "$MODE" = "1" ];then

Printmsg "INFO" "Forced pressed 'y' : $PRC_NAME"

iRC=0

else

if [ -z "$PROC_START" ];then

Printmsg "INFO" "Do you want to start : $PRC_NAME?"

PROC_START=`Prompt "$DATE_TIME [INFO] Press ('y' for yes
'Enter' to exit): "`

if [ "$PROC_START" != "y" ];then

iRC=1

Printmsg "INFO" "Forced end to process : $PRC_NAME"

else

Printmsg "INFO" "Pressed 'y' : $PRC_NAME"

iRC=0

fi

fi

fi

}



function Endprocess {

Printmsg "INFO" "End to process : $PRC_NAME"

}



function Read_Iplist_File {

if [ "${IPS}" == "" ];then

Printmsg "ERROR" "Please, Input IP List File Path!"

else

if test -r ${IPS}

then

iRC=$?

if [ ${iRC} -eq 0 ];then

chkFile=1

else

chkFile=0

Printmsg "ERROR" "${2} File is not existed."

fi

fi

fi

}

if [ "${1}" == "" ];then

Printmsg "ERROR" "Please, Input Type (Usage: ${MENU_INIT} ) correctly!"

else

TYPE=${1}

IPS=${2}

case ${TYPE} in

start)

PRC_NAME="Start IPtables to block ${2}.........."

Startprocess

Read_Iplist_File

if [ "${chkFile}" == "1" ];then

for ipaddress in `cat ${2}`

do

Printmsg "NOTICE" "/sbin/iptables -A INPUT -s ${ipaddress} -j DROP"

/sbin/iptables -A INPUT -s ${ipaddress} -j DROP

done

/sbin/iptables -L

fi

/etc/init.d/iptables save

/etc/init.d/iptables restart

Endprocess

;;

stop)

PRC_NAME="Stop IPTables to remove ${2}.........."

Startprocess

Read_Iplist_File

if [ "${chkFile}" == "1" ];then

for ipaddress in `cat ${2}`

do

Printmsg "NOTICE" "/sbin/iptables -D INPUT -s ${ipaddress} -j DROP"

/sbin/iptables -D INPUT -s ${ipaddress} -j DROP

done

/sbin/iptables -L

fi

/etc/init.d/iptables save

/etc/init.d/iptables stop

Endprocess

;;

start_pp)

PRC_NAME="Start IPtables to block SYN,FIN,RST from ALL IPs.........."

Startprocess

Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP"

/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP"

/sbin/iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP"

/sbin/iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP

Printmsg "NOTICE" "/sbin/iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP"

/sbin/iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP

/sbin/iptables -L

/etc/init.d/iptables save

/etc/init.d/iptables restart

Endprocess

;;

stop_pp)

PRC_NAME="Stop IPTables to remove SYN,FIN,RST from ALL IPs.........."

Startprocess

Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP"

/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP

Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP"

/sbin/iptables -D INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP

Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP"

/sbin/iptables -D INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP

Printmsg "NOTICE" "/sbin/iptables -D INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP"

/sbin/iptables -D INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP

/sbin/iptables -L

/etc/init.d/iptables save

/etc/init.d/iptables stop

Endprocess

;;

*)

Printmsg "ERROR" "Please, Input Type (Usage: ${MENU_INIT} ) correctly!"

;;

esac

fi


위 Script에서 4개의 선택 메뉴가 있는데, MENU_INIT="[ start
stop
start_pp
stop_pp ]"

start / stop은 Block_SYN_Flooding.sh start [IP_List_file]과 같이 IP List File을 지정하면 각 IP를 Block 할 수 있도록 되어 있습니다.

Stop을 하면 해제가 되구요. start_pp / stop_pp에 대해서는 IP가 너무많아 대응하기 힘든 경우 SYN Flooding과 관련 된 것들을 Iptable에서 몽땅 Drop 해버리는 겁니다. 실제 Web Service에서 문제없이 사용하고 있습니다. 그러나, 제가 다른 서비스에 대해서는 보장 못 해드립니다. ^ ^;



SYN Flooding 공격에 대한 대처법은 여기까지입니다. Linux : Dos / DDos 공격에 대한 대처법. #2에서는 Smurf 공격 및 Spoofing 대처법 등에 대해서 다루도록 하겠습니다.