'string'에 해당되는 글 4건

  1. 2014.09.25 Bash Vulnerability Code Injection Attack
  2. 2012.08.28 GNU Awk 사용자 가이드/함수
  3. 2010.04.08 Linux : using iptables string-matching filter to block vulnerability scanners (1)
2014.09.25 14:42

Bash Vulnerability Code Injection Attack


bash_ld_preload.c

#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

static void __attribute__ ((constructor)) strip_env(void);
extern char **environ;

static void strip_env()
{
	char *p,*c;
	int i = 0;
	for (p = environ[i]; p!=NULL;i++ ) {
		c = strstr(p,"=() {");
		if (c != NULL) {
			*(c+2) = '\0';
		}
		p = environ[i];
	} 

}


  • Compile it:
gcc bash_ld_preload.c -fPIC -shared -Wl,-soname,bash_ld_preload.so.1 -o bash_ld_preload.so
  • Copy bash_ld_preload.so to /lib:
cp bash_ld_preload.so /lib/

If you wish to apply this workaround across the entire system:

  • Add the following to /etc/ld.so.preload on a line by itself:
/lib/bash_ld_preload.so
  • Restart all relevant services or reboot the system.

Note that this is potentially very dangerous. It is recommend that you just apply this workaround to specific services that may be exploitable on your system. This can be achieved by adding bash_ld_preload.so to the LD_PRELOAD environment variable in the script that will initialize the service. For example, for httpd on Red Hat Enterprise Linux 6:

  • Add the following two lines at the top of /etc/init.d/httpd, after the #! line:
LD_PRELOAD=/lib/bash_ld_preload.so
export LD_PRELOAD
  • Then restart httpd:
service httpd restart


Workaround: Using mod_security:

The following mod_security rules can be used to reject HTTP requests containing data that may be interpreted by Bash as function definition if set in its environment. They can be used to block attacks against web services, such as attacks against CGI applications outlined above.

Request Header values:

SecRule REQUEST_HEADERS "^\(\) {" "phase:1,deny,id:1000000,t:urlDecode,status:400,log,msg:'CVE-2014-6271 - Bash Attack'"

SERVER_PROTOCOL values:

SecRule REQUEST_LINE "\(\) {" "phase:1,deny,id:1000001,status:400,log,msg:'CVE-2014-6271 - Bash Attack'"

GET/POST names:

SecRule ARGS_NAMES "^\(\) {" "phase:2,deny,id:1000002,t:urlDecode,t:urlDecodeUni,status:400,log,msg:'CVE-2014-6271 - Bash Attack'"

GET/POST values:

SecRule ARGS "^\(\) {" "phase:2,deny,id:1000003,t:urlDecode,t:urlDecodeUni,status:400,log,msg:'CVE-2014-6271 - Bash Attack'"

File names for uploads:

SecRule  FILES_NAMES "^\(\) {"  "phase:2,deny,id:1000004,t:urlDecode,t:urlDecodeUni,status:400,log,msg:'CVE-2014-6271  - Bash Attack'"

These may result in false positives but it's unlikely, and they can log them and keep an eye on it. You may also want to avoid logging as this could result in a significant amount of log files.

Workaround: Using IPTables:

A note on using IPTables string matching:

iptables using -m string --hex-string '|28 29 20 7B|'

Is not a good option because the attacker can easily send one or two characters per packet and avoid this signature easily. However, it may provide an overview of automated attempts at exploiting this vulnerability.


$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

 vulnerable

 this is a test


$ env x='() { :;}; echo vulnerable' bash -c "echo this is a test"

 bash: warning: x: ignoring function definition attempt

 bash: error importing function definition for `x'

 this is a test




출처 : access.redhat.com




Trackback 0 Comment 0
2012.08.28 10:13

GNU Awk 사용자 가이드/함수

함수란 입력받은 Input을 일정한 규칙에 따라 처리한 뒤 Output을 내놓는 것이다. 예를 들어 어떤 함수 x에 input을 넣었을 때 input값에 1만큼 증가한 값이 output으로 나온다면 이 함수는 +1을 해주는 함수라고 할 수 있다. awk에서 함수는 보통 다음과 같은 기본형을 갖는다

function(input1,input2,...)
  return

input의 개수는 함수마다 다르며, 사용자 정의 함수의 경우 사용자가 필요한 만큼 지정해 줄 수 있다.

awk에서는 함수가 크게 두 부류로 나누어진다. 하나는 awk에 이미 내장되어 있는 내장함수Built-In Function인데, 이 함수들은 사용자가 원하는 경우 언제든지 불러와서 사용할 수 있다. 다른 하나는 사용자 정의 함수로서, 사용자가 프로그램의 목적에 따라 임의로 규칙을 만들어낸 함수를 가르킨다. 사용자 함수의 이름과 형식은 자유롭게 정할 수 있으나 이미 내장된 함수들의 이름을 사용할 수는 없다.

이 문서의 전반부는 awk에 내장된 함수들의 종류를 설명하고, 후반부에서는 사용자 정의 함수의 작성방법을 간단히 설명한다.


내장 함수

내장함수는 사용자가 만드는 awk프로그램에서 항상 사용할 수 있는 함수이다. awk의 내장 함수는 다섯 가지 정도로 분류된다.

Numeric Functions

Numeric Function은 수치 정보를 처리하는 함수들을 뜻한다. 아래의 목록은 awk에 내장된 수치 함수의 이름과 설명을 담고 있다.

함수명설명
int(x)x에서 가장 가까운 정수 값을 return한다
sqrt(x)x의 양의 제곱근을 return
exp(x)x를 지수로, 자연상수 e를 밑으로 하는 지수함수 return
log(x)x의 자연로그값을 return
sin(x)x의 sin값을 return
cos(x)x의 cos값을 return
atan(y,x)x의 arctangent값을 return
rand()0과 1사이의 범위에서 random number를 return
srand([x])rand 기능과 관련하여 시작점이나 seed 설정

String-Manipulation Functions

문자열 함수는 하나 또는 여러 문자열의 text를 보거나, 변화시키는 기능을 갖고 있다. 아래의 목록은 문자열 함수의 이름과 기능을 나열한 것이다.

함수명설명
asort#(source[,dest])array source에서 요소들을 정렬해서 return
asorti(source[,dest])rray source에서 요소들을 index를 기준으로 정렬해서 return
index(in,find)주어진 string이 포함된 문자열을 검색해서 return
length(string)주어진 input의 철자 수를 세서 return
match(string, regexp, [, array])string에서 regular expression의 위치를 return
split(string,array,[,fieldsep])fieldsep에 따라 string을 쪼개고 각각을 저장
sprintf(format, expression1,...)printf와 같은 용도이지만 값을 return하기만 할 뿐 출력하지 않는다
strtonum(str)문자열을 숫자로 변환, 문자열의 종류에 따라 8진수,16진로 파악하기도 함
sub(regexp, replacement[, target])regexp에 해당하는 문자열을 replace로 대치시킴
gsub(regexp, replacement[, target])매치되는 모든 string을 replace로 대치시킴
gensub(regexp, replacement[, target])original string의 값이 변하지 않은 채로 매치되는 값들만 return
substr(string, start[, length])start에서 시작해 length만큼의 길이까지 문자열을 return한다
tolower(string)해당 문자열의 대문자를 소문자로 바꿔서 출력
toupper(string)해당 문자열의 소문자를 대문자로 바꿔서 출력

Input/Output Function

입출력 데이터와 관련된 함수들의 목록은 아래와 같다.

함수명설명
close(filename [,how])filename에 해당하는 파일을 닫는다
system(command)operating-system의 명령어를 실행하고 이를 awk에 return
getline()처리된 output을 다음 레코드의 input으로 받는다.
printf()내용을 그대로 return하면서 안에서 변수를 순서대로 받아 입력한 string과 같이 출력

Time Functions

awk에서 제공하는 시간정보 관련 함수이다.

함수명설명
systime()현재시간을 초단위까지 return
mktime(datespec)systime으로부터 받은 시간을 dataspec에 입력
strftime([format[,timestamp[,ufc-flag]]])ufc-flag에서 받은 시각(표준시각)을 문자로 표현, 기본 포맷인 경우 '%a,%A...'와 같은 문자열로 출력된다.

Bit-Manipulation Functions

비트값을 처리하는 Function으로써 불 대수값을 출력하는 함수와 자리를 이동하며 처리하는 함수로 구성된다. 목록은 다음과 같다.

함수명설명
and(v1,v2)입력된 값들의 AND연산 값 출력 1 1인 경우 1출력
or(v1,v2)입력된 값들의 or연산 값 출력 0 0인 경우를 제외하고 1출력
xor(v1,v2)입력된 값들의 xor연산 값 출력, 1 0 또는 0 1인 경우 1 출력
lshift(val)카운트된 bit만큼 왼쪽으로 이동한 값을 return
rshift(val)카운트된 bit만큼 오른쪽으로 이동한 값을 return

사용자 정의 함수

사용자 정의 함수는 awk 프로그램의 내부 어디에서든 만들고 사용할 수 있다. 일단 프로그램 내에서 함수가 정의된 후에는 다시 정의하지 않고 바로 적용하여 편리하게 사용할 수 있다.

함수의 기본 정의방식

awk에서 함수는 보통 다음과 같은 형식으로 만들어진다.

 function name(parameter1, parameter2,..)
        {
                body- of- function
        }

name은 함수의 이름을 가리키고, parameter는 함수에 들어가는 input을 의미한다. body of function은 본격적인 함수의 내용을 가리킨다.

함수의 예시

m부터 n까지를 더한 결과값을 내는 함수를 만들어보자. 임의로 함수의 이름을 sum(m, n)으로 정하자. 소스 코드는 아래와 같다

 function sum(start, end){ 
        total = 0;
        for (i = start; i<= end; i++) {
                total = total + i;
        }
        return total;
}

위와 같이 프로그램 내에서 함수를 정의한 후 그 다음부터는 같은 연산이 필요할 때마다 함수로 간단하게 입력해서 사용할 수 있다.


출처 : ko.wikibooks.org


Trackback 0 Comment 0
2010.04.08 20:40

Linux : using iptables string-matching filter to block vulnerability scanners

Does "w00tw00t.at.ISC.SANS.DFind:)" sound familiar to you ? If you own one ore more servers, there are a lot of chances you found it in your logs and that it gave you headaches or even nightmares trying unsuccessfully to get rid of it.

It always looks like this in your apache logs :

 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [16/Nov/2008:07:43:58] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400
 213.251.134.23 [17/Nov/2008:05:16:31] "GET /w00tw00t.at.ISC.SANS.DFind:) HTTP/1.1" 400

We can see here that 213.251.134.23, a small (compromised) server hosted by OVH, asked for the "/w00tw00t.at.ISC.SANS.DFind:)" web page and that apache politely told it to bugger off by sending a HTTP 400 code (BAD_REQUEST) and wrote to its error log file the reason why :

 client sent HTTP/1.1 request without hostname (see RFC2616 section 14.23)

Apache rejected that request because it is non RFC2616-compliant. Any HTTP 1.1 request should contain at least 2 fields in its headers, one of them being the "Host:" field as per follows :

 GET /requested_page.html HTTP/1.1
 Host: website.com

In our case, the hostname is missing and thus it is rejected. This also means that you do not have to worry, you haven't been hacked !
That's just a simple vulnerability scanner named DFind that loves to show off in your logs.
There are several variants including, amongst them :
 /w00tw00t.at.ISC.SANS.DFind:)
 /w00tw00t.at.ISC.SANS.test0:)
 /w00tw00t.at.ISC.SANS.MSlog:)
 /w00tw00t.at.ISC.SANS.ntsvc:)

That particular string is only used as part of DFind webserver banner scanner, that is, it only wants to fetch your HTTP server name, nothing else.
Let's see exactly how it works :
 DFind.exe (v1.0.9, 73,728 bytes) - disassembly listing

 :00406486 mov esi, 0040F1F8                   ; esi points to the
                                               ; "GET /w00tw00t.at.ISC.SANS.DFind:)
                                               ; HTTP/1.1" string.
 :0040648B lea edi, dword ptr [ebp+FFFFFAD4]   ; edi = destination buffer
 :00406491 repz
 :00406492 movsd                               ; Copy esi string => edi
 :00406493 movsw
 :00406495 movsb
 :00406496 push 00000000
 :00406498 push 0000002E                       ; 46 bytes == size of
 :0040649A lea eax, dword ptr [ebp+FFFFFAD4]   ; buffer (w00tw00t string
 :004064A0 push eax                            ;  + CR/LF/CR/LF).
 :004064A1 push [ebp-2C]                       ; Socket desciptor.
 :004064A4 Call dword ptr [0040C0E4]           ; Call send() to
 :004064AA cmp eax, FFFFFFFF                   ; post the request.
 :004064AD jne 004064CB                        ; Give up if any error.
 :004064AF mov eax, dword ptr [00410EE0]       ; Internal flag use to
 :004064B4 dec eax                             ; decrement number of scan
 :004064B5 mov dword ptr [00410EE0], eax       ; attempts left.
 :004064BA push [ebp-2C]                       ; Close the sockect
 :004064BD Call dword ptr [0040C0C4]           ; closesocket()
 :004064C3 or eax, FFFFFFFF                    ; error ?
 :004064C6 jmp 00409E01
 ...
 :00406537 push 00000000                       ; Call recv() and
 :00406539 push 000001F4                       ; Fetch maxi 500 (0x1f4)
 :0040653E lea eax, dword ptr [ebp+FFFFF8DC]   ; characters returned
 :00406544 push eax                            ; by the server.
 :00406545 push [ebp-2C]                       ;
 :00406548 Call dword ptr [0040C0E8]           ; call recv().
 :0040654E mov dword ptr [ebp+FFFFFAD0], eax   ; Save number of bytes
 :00406554 push 00000000                       ; returned.
 :00406556 push [ebp-2C]
 :00406559 Call dword ptr [0040C0B0]           ; Call shutdown().
 :0040655F cmp dword ptr [ebp+FFFFFAD0], 5     ; Ensure we received at least
 :00406566 jge 00406584                        ; 5 bytes otherwise give up.
 ...
 :00406584 push 0040F1F0                       ; Offset holding the string
 :00406589 lea eax, dword ptr [ebp+FFFFF8DC]   ; "Server:"
 :0040658F push eax
 :00406590 Call dword ptr [0040C088]           ; Call strstr()
 :00406596 pop ecx                             ; to find that sub string
 :00406597 pop ecx                             ;
 :00406598 test eax, eax                       ; Check if we found it (eax must
 :0040659A je 004068FA                         ; point to the first occurence)
                                               ; otherwise give up.
 :004065A0 push 0040D184                       ; Offset pointing to LF (/n).
 :004065A5 lea eax, dword ptr [ebp+FFFFF8DC]
 :004065AB push eax
 :004065AC Call dword ptr [0040C084]           ; Call strtok() to
 :004065B2 pop ecx                             ; find that LF.
 :004065B3 pop ecx
 :004065B4 mov dword ptr [ebp+FFFFF8D8], eax
 :004065BA cmp dword ptr [ebp+FFFFF8D8], 0     ; Give up if we cannot
 :004065C1 je 004068FA                         ; find it.
 ...
 :004065E8 Call 0040B952                       ; Call strlen() to fetch
 :004065ED pop ecx                             ; total number of bytes
                                               ; from "Server:"
 :004065EE dec eax                             ; to the LF.
 :004065EF mov dword ptr [ebp+FFFFF4B8], eax   ; <= hmmm, does not check
 :004065F5 cmp dword ptr [00410EE4], 1         ; retcode after the call
 :004065FC jne 0040677F                        ; to strlen.
 :00406602 push dword ptr [00410DF8]
 :00406608 push dword ptr [ebp+FFFFF8D8]
 :0040660E Call dword ptr [0040C088]           ; Call strstr()
 :00406614 pop ecx
 :00406615 pop ecx
 :00406616 test eax, eax
 :00406618 je 00406764
 :0040661E mov eax, dword ptr [00410EC4]       ; Internal flag which
 :00406623 inc eax                             ; increment the number of
 :00406624 mov dword ptr [00410EC4], eax       ; signatures found.
 :00406629 push dword ptr [ebp+FFFFF4B8]       ; Our pointer (strlen retcode),
                                               ; is reused but still hasn't
 :0040662F push dword ptr [ebp+FFFFF8D8]       ; been checked (could be dangerous...).
 :00406635 lea eax, dword ptr [ebp+FFFFF4BC]   ; Destination buffer
 :0040663B push eax                            ;
 :0040663C Call dword ptr [0040C044]           ; Call strncpy().
 :00406642 add esp, 0000000C                   ; Adjust stack.
 :00406645 lea eax, dword ptr [ebp+FFFFF4BC]   ; Fetch buffer address.
 ...

Self-explanatory : send a GET request and fetch the HTTP server name.


How to get rid of it ?

  • Fail2ban ? Using such an application or any similar tool, would be useless : by default, DFind will hit your server with 5 requests (on ports 80, 81, 443, 8000 and 8080 respectively), then disappear and will come back later again with a different IP address and so on. Blocking its IP afterwards will have no effect at all.
  • installing a NIDS (Network Intrusion Detection System) ? Installing such a software in order to block a simple 28-byte string may be too much of a hassle...
  • using mod_rewrite or mod_security ? Even if, strangely, we can find on some websites few mod_security rules to block DFind, they are completely useless. mod_security, contrary to common belief, is not a firewall but only a simple module/plugin. All incoming packets are fetched by apache which listens to the HTTP port 80 and it will forward them to mod_security, and not vice versa (hence it shows how dangerous it is to think your apache server is fully protected by mod_security). In our case, apache reject the request and will never forward it to mod_security.

    We will simply use the excellent iptables and 3 of its modules : String match, Recent and TCP.


    Before going any further it is important to note that the following rules are perfect to get rid of "script-kiddies" randomly scanning IPs with widely available vulnerability scanners, but they may under no circumstances apply to an attack led by an experienced hacker specifically targeting your server that only some tougher server/firewall configuration rules could block.

    The String match module :

    String match is string-matching filter that can reject any unwanted packet with the -m string opion. To understand how it works, all you have to do is to ask it :

     # iptables -m string --help
    
     STRING match v1.3.8 options:
     --from                       Offset to start searching from
     --to                         Offset to stop searching
     --algo                       Algorithm
     --string [!] string          Match a string in a packet
     --hex-string [!] string      Match a hex string in a packet
    
    
  • --from : packet offset to stat searching from. By default, searching starts from offset 0.
  • --to : packet offset to stop searching. That option and the previous one are quite interesting and usefull because we can limit our search inside a packet instead of filtering it all and thus save time and CPU cycles. By default, it will search through the whole packet, the maximum limit being set at 65,535 bytes.
  • --algo : the algorithm to use. There are two : Boyer-Moore (bm) and Knuth-Pratt-Morris (kmp). We'll use the first one.
  • --string : text search pattern (ie : 'abcd'). It is CaSe sensitive.
  • --hex-string : search pattern in hexadecimal format. The pattern must be delimited by the '|' sign. Hex characters can be separated by a space (ie : '|61 62 63 64|') or not (ie : '|61626364|').

    As there are few variants of our "w00tw00t", we are going to filter on their common part :

     GET /w00tw00t.at.ISC.SANS.
    
    
    That's 26 bytes, to which we will add 44 more bytes (including a dozen for the "Options" field of the TCP/IP packet), making a total of 70 bytes, our search length (--to parameter) :
     # iptables -I INPUT -d xxx.xxx.xxx.xxx -p tcp --dport 80 -m string --to 70 \
     --algo bm --string 'GET /w00tw00t.at.ISC.SANS.' -j DROP
    
    
    Replace the 'xxx.xxx.xxx.xxx' string by your server IP.

    If your server has multiple and consecutive IP's, you can use only one rule to include them all with the -m iprange parameter.
    Example with the 5 consecutive IP's 1.0.0.1, 1.0.0.2, 1.0.0.3, 1.0.0.4 and 1.0.0.5 :

     # iptables -I INPUT -p tcp --dport 80 -m iprange --dst-range 1.0.0.1-1.0.0.5 \
     -m string --to 70 --algo bm --string 'GET /w00tw00t.at.ISC.SANS.' -j DROP
    
    
    Have a look at your rule by typing :
    # iptables -L INPUT -nvx
    
     pkts  bytes target ...
     0     0     DROP   ... STRING match "GET /w00tw00t.at.ISC.SANS." ALGO name bm TO 70
    
    
    Wait few hours until your sticky DFind friend come back and, although your apache error log will nicely remain empty, you could still find out how many packets were blocked by iptables :
    # iptables -L INPUT -nvx
    
     pkts  bytes target ...
     64    5504  DROP   ... STRING match "GET /w00tw00t.at.ISC.SANS." ALGO name bm TO 70
    
    
    We can see in that example that 64 packets were dropped for a total of 5,504 bytes, or 86 bytes/packets, the typical DFind sequence.

    That's cool but also quite a brutal (and not really recommended) approach.
    For sure it worked in our example but there are two major problems :

    • it filters all incoming packets to port 80, regardless of what kind of packets it is.
    • it can cause errors ("false positive"). Although the chances are very small, they must be taken into consideration.
    We need therefore to setup a rule that will only filter the packet we need to filter, that is the very first one containing the HTTP request (GET, POST ...) and thus overcome the disadvantages of the string module used alone. All other incoming packets will be ignored.

    Let's see how works a TCP connection :

    • the client connects to the server by sending a SYN (synchronization) packet.
    • the server responds by sending a SYN + ACK (Synchronize + Acknowledgment) packet.
    • the client responds with an ACK (Acknowledgment) packet.
    • at this time, the communication is established, the client can send its PSH + ACK (Push + Acknowledgment) packets, the server will respond and so on until the connection is closed.

    The TCP module :

    To find the right packet, we must be able to identify it. We will use the TCP module and its '--tcp-flags' parameters :

      # iptables -p tcp --help
    
      TCP v1.3.8 options:
     --tcp-flags [!] mask comp     match when TCP flags & mask == comp
                                   (Flags: SYN ACK FIN RST URG PSH ALL NONE)
    
    

    mask matches the flags that should not be set, comp the flags that need to be. There can be one or more flags in each field. Multiple flags should be comma-separated.

    Example : to find out if an incoming packet to port 80 is a ACK sequence, it must have an activated ACK flag but no PSH,SYN,ACK flags :

      # iptables -A INPUT -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 80 -j ACCEPT
    
    

    That's it : now we are able to determine the type of each incoming or outgoing packet, simply by looking at its flag(s).

    The Recent module :

    We still have one problem to solve : throughout the connection, we will send and receive a lot of packets however the only one we want to filter is the first PSH+ACK packet that comes right after the SYN/SYN+ACK/ACK 3-way handshake sequence.
    For that purpose, the Recent match module is perfect :

      # iptables -m recent --help
    
        --set          Add source address to list, always matches.
        --update       Match if source address in list, also update last-seen time.
        --remove       Match if source address in list, also removes that address from list.
        --name name    Name of the recent list to be used.  DEFAULT used if none given.
        ...
        ...
    
    

    This is a very complex module. It has several parameters but we'll only use the 4 ones displayed above. It can create a list with the IP and timestamp used in a packet and allows us to monitor/match any recent event regarding the current connection. The list can be customized (parameter '--name') or be the default one (DEFAULT).
    • --set : we'll use it to create the list at the beginning of any connection (SYN packet).
    • --update : we'll use it to update our list during the SYN+ACK and ACK.
    • --remove : we'll use it to delete our list as soon as we receive the very first PSH+ACK packet (which contains the HTTP request to filter). Hence, once the list is deleted we will ignore any further incoming packet from the current connection.

    Let's try with the following example :

     #!/bin/bash
    
     # just for our test ! flush all rules...
     iptables -F
     # and delete all chains :
     iptables -X
    
     # create our w00t chain :
     iptables -N w00t
    
     # redirect packets to our chain
     # (we use localhost just for the test) :
     iptables -A INPUT -d 127.0.0.1 -j w00t
    
     # we're looking for the first packet which should
     # only have the SYN flag set (we could also use '--syn' instead
     # of '--tcp-flags ALL SYN') and we create our list with '--set' :
     iptables -A w00t -m recent -p tcp --tcp-flags ALL SYN --dport 80 --set
    
     # wait for the SYN,ACK packet and update our list with '--update'
     # (it's an outgoing packet so we use '--sport 80') :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK SYN,ACK --sport 80 --update
    
     # wait for the client ACK and update our list
     # upon receipt :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 80 --update
    
     # we received our 3-way handshake, so from now and then
     # the connection is established
    
     # we're now waiting for the first incoming PSH,ACK.
     # It will contain the HTTP (GET, POST, HEAD...) we are looking for. As soon
     # as we get it, we delete our list with '--remove' so that we will ignore any
     # further packets :
     iptables -A w00t -m recent -p tcp --tcp-flags ACK,PSH PSH,ACK --dport 80 --remove
    
    

    To ensure that everything is working perfectly, we will test locally with a simple HTTP GET request by calling the HTML page you are currently reading. There should be 7 requests (1 HTML page, 1 CSS + 1 JS file and 4 pictures). We will verifiy if all packets were properly intercepted / ignored by our rules.

    Let's see the whole HTTP request with a packet sniffer (Wireshark) :

    Total : 27 packets
    SYN : 2 packets (No 1 and 13)
    SYN, ACK : 2 packets (No 2 and 14)
    ACK (incoming) : 6 packets (No 3, 7, 15, 19, 26 and 27)
    The HTTP requests we are looking for and wish to filter : 7 packets (No 4, 8, 11, 16, 20, 22 and 24) which are the GET used to request the HTML page , the CSS and the JS files as well as the 4 pictures.
    The last 10 packets do not interest us.

    Let's check with iptables now :

     # iptables -L -nvx
    
     Chain INPUT
      pkts  bytes target  ...
       27   18596 w00t    ...
    
     Chain w00t (1 references)
      pkts  bytes target  ...
        2     120         ...recent:SET name:DEFAULT side:source tcp dpt:80 flags:0x3F/0x02
        2     120         ...recent:UPDATE name:DEFAULT side:source tcp spt:80 flags:0x1A/0x12
        6     312         ...recent:UPDATE name:DEFAULT side:source tcp dpt:80 flags:0x1A/0x10
        7    5412         ...recent:REMOVE name:DEFAULT side:source tcp dpt:80 flags:0x18/0x18
    
    

    We see that there were a total of 27 packets and that our first rule hooked the 2 SYN (0x02), the second one the 2 SYN+ACK (0x12), the third one the 6 ACK (0x10) and that our fourth rule only hooked the 1st PSH+ACK (0x18) packet of each request.
    The other 10 packets have indeed been ignored.

    Setting up the rules :

    Now that we can isolate the right packet from the HTTP request, we can use the String module to filter it.

  • Example #1 :

    For the first example, we build a simple filter rule to drop the packet containing the string "GET /w00tw00t.at.ISC.SANS." :

     #!/bin/bash
    
     # create our w00t chain :
     iptables -N w00t
    
     # redirect TPC traffic to our chain :
     iptables -A INPUT -p tcp -j w00t
    
     # look for the SYN packet and create the list :
     iptables -A w00t -m recent -p tcp --syn --dport 80 --set
    
     # look for the SYN,ACK packet and update the list :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK SYN,ACK --sport 80 --update
    
     # look for the ACK packet and update the list :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 80 --update
    
     # this is the right packet : look for our string pattern and DROP it if we found it.
     # Delete our list, we do not want to filter any further packet from that connection
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 80 --remove \
     -m string --to 70 --algo bm --string "GET /w00tw00t.at.ISC.SANS." -j DROP
    
    

    Once the rules are setup, iptables can display the total of packets and bytes dropped with the command :

     # iptables -L w00t -nvx 

    You can also block some other well known vulnerability scanners used by script-kiddies when then will try to connect to your server by using its IP address instead of it domain (or sub-domain) name in the 'Host:' field of the HTTP request. That is :

     GET / HTTP/1.1
     Host: xxx.xxx.xxx.xxx
    
    
    where xxx.xxx.xxx.xxx is your server IP instead of :
     GET / HTTP/1.1
     Host: yourdomain.tld
    
    

    If you add one or more rules to our anti-w00t00t chain, you must not forget that only the last one should remove the list, all previous ones should keep updating it. For instance, adding a filter based on the 'Host:' variable. We increase the searching lenght to 700 bytes because there could be other variables in the HTTP request between the 'GET' and 'Host:' lines :

     ...
     ...
    
     # 1st filtering rule on our paquet + list update :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 80 --update \
      -m string --to 70 --algo bm --string "GET /w00tw00t.at.ISC.SANS." -j DROP
    
     # 2nd filtering rule on the same packet + list deletion :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 80 --remove \
      -m string --to 700 --algo bm --string 'Host: xxx.xxx.xxx.xxx' -j DROP
    
    
    Replace 'xxx.xxx.xxx.xxx' with your server IP address.

    Note : Host filtering with the string module will block a lot of scanners, howere do not rely only on that. A lot of scanners could get through so you should filter the playload with, for instance, mod_security. Using iptables to search for a specific string can be efficient when you want to block a scanner like DFind because you know in advance how it works and because you can't block it afterwards (unless you modify Apache source code), but don't rely on this method to block unknown scanners.

  • Example #2 :

    This set of rules is more sophisticated and also does much more than the previous one :

    - generic search : some script-kiddies modify the string "GET /w00tw00t.at.ISC.SANS." using hexeditors (eg : "GET /test.w00t:)"). To solve the problem, we can filter on the fact that the HTTP request is non RFC-compliant, that is, is contains the string "HTTP/1.1" followed by 2 CR/LF. We can convert it to hex values ("HTTP/1.1" => "0x485454502f312e310" and "CR/LF/CR/LF" => "0x0d0a0d0a") and use the --hex-string parameter.
    - IP blacklist : we can blacklist for, say, 6 hours any IP with the ipt_recent module. We'll use the --update parameter so that if the scanner come back we'll keep it blacklisted for another 6 hours.
    - connection reset : instead of dropping the packet (-j DROP), we can reject it and immediately close the connection (-p tcp -j REJECT --reject-with tcp-reset). Using DROP isn't really interesting here, because when we catch the packet we are looking for, the connection has already been established (the packet follows the 3-way handshake sequence) hence the scanner knows there is a HTTP server listening. No need to try to make it believe the contrary, it would be a waste of time.

     #!/bin/bash
    
     # add the following lines at the beginning of your iptables rules :
    
     # allow loopback
     iptables -A INPUT -i lo -j ACCEPT
    
     # check if IP is blacklisted (present in the w00tlist).
     # If so, reject it right away and update the list for another 6 hours :
     iptables -A INPUT -p tcp -m recent --name w00tlist --update --seconds 21600 -j DROP
    
     # create w00tchain chain which will add the IP to the w00tlist
     # blacklist and will reset the connection (don't forget the
     # '-p tcp' parameter needed for '--reject-with tcp-reset') :
     iptables -N w00tchain
     iptables -A w00tchain -m recent --set --name w00tlist -p tcp \
     -j REJECT --reject-with tcp-reset
    
     # create our w00t chain :
     iptables -N w00t
    
     # redirect TCP packets to our chain :
     iptables -A INPUT -p tcp -j w00t
    
     #####################################################
     # put here all your iptables rules (ie : accept already established
     # connections, etc) :
     iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
     ...
     ...
     ...
    
     #####################################################
    
     # w00t chain :
     # look for the SYN packet and create the list :
     iptables -A w00t -m recent -p tcp --syn --dport 80 --set
    
     # look for the SYN,ACK packet and update the list :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK SYN,ACK --sport 80 --update
    
     # look for the ACK packet and update the list :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,SYN,ACK ACK --dport 80 --update
    
     # look for the hexadecimal string in the first PSH+ACK.
     # If found, redirect to w00tchain in order to blacklist the IP and
     # to close the connection.
     # Delete our list, we do not want to filter any further packet from that connection :
     iptables -A w00t -m recent -p tcp --tcp-flags PSH,ACK PSH,ACK --dport 80 --remove \
     -m string --to 80 --algo bm --hex-string '|485454502f312e310d0a0d0a|' -j w00tchain
    
    
    

    Yet again, you can see how many packets were rejected using the command :

     # iptables -L w00t -nvx 

    And you can also see all blacklisted IP's with :

     # cat /proc/net/ipt_recent/w00tlist



    Conclusion

    It is possible to filter other ports (SSH etc) for instance to block buffer overflow attempt (--hex-string '|90 90 90 90 90 90|'). However, do not try to systematically filter any kind of string pattern ( eg : antispam filtering on your SMTP port !) because not only it is not the main purpose of iptables, but it is possible and easy for an attacker to split those patterns into smaller packets and thus avoid detection.

    Do not forget the most important : RTFM

     # man iptables
    
    
    Or, for a specific parameter :
    # iptables [PARAMETER] --help
    

  • 출처 : http://spamcleaner.org/

    Trackback 0 Comment 1
    1. Favicon of http://nurturehq.com chetanM 2010.10.14 15:35 address edit & del reply

      Hi i just wana to use same for blocking ssl. will it be possible ?