Hacking & Security/Web2008. 6. 29. 04:05

Web Hacking 1탄 SQL Injection
Web Hacking 2탄 파일조작
Web Hacking 3탄 구멍난 자바스크립트


1. 시작하기
해커가 하나의 웹사이트를 공격하기 위해 많은 시간을 준비한다고 했습니다
그중에 가장 먼저 하는것 중에 하나가 바로 파라미터와 그 값에대한 목록 정리 입니다
즉 타겟 웹사이트의 모든 URL 파라미터를 GET, POST등으로 정리를 합니다

http://xxxxxxxx.com/pds/list1.php?cnum=2
http://xxxxxxxx.com/pds/list2.php?cnum=2&snum=21
http://xxxxxxxx.com/view.php?fnum=104591
http://xxxxxxxx.com/bbs/bbs.php?table=bbs_sw_qna
http://xxxxxxxx.com/bbs/bbs.php?f=write&table=bbs_sw_qna
http://xxxxxxxx.com/bbs/bbs.php?f=view&table=bbs_sw_qna&fid=48610&num=85973
...

이렇게 목록화 후 분석을 해보면 해당 파라미터가 어떤 역할을 하는지 무엇을 의미하는지 80%는 파악할 수 있습니다
bbs의 f에는 write와 view 라는 값이 주어지는것을 보니 액션에 대한 글쓰기를 할것이지 아니면 글조회를 할것인지를 나타내는것으로 보이며 table이라는 컬럼은 아마도 물리적 table이름을 말하는것 같군요
cnum은 아마도 카테고리 번호를 의미하는것이고 snum은 소카테고리번호를, num은 게시물 글번호를 의미하는것으로 추정됩니다
그럼 이 프로그램들이 어떻게 돌아가는지 대강 파악이 되고 이제부터 취약점이 있는지 체크해 나가보는겁니다

파라미터명이나 값에대해서는 유추할수 없이 프로그램 한다면 아마도 조금은 더 보안적으로 안전할 것입니다

다음은 파라미터로 적절치 않은 값을 넘겨주는 예를 알아볼 것이며, 파일 업로드, 다운로드 취약점에 대해 공부해 보겠습니다 ^^


2. 동적 파일 로딩 취약점
동적으로 어떤 특정 파일을 열어 그 파일 내용을 웹에서 보여주는 jsp가 있다고 합시다
대량의 html이나 txt 파일들을 읽어 웹에서 보여주는 로직들이 많지요 (저도 많이 썼습니다 -_-;)
동적으로 그 파일에 대한 정보를 파라미터로 읽어온다면 어떻게 될까요?

<%
String param = request.getParameter("param");

File file = new File(request.getRealPath("/")+param);

/* 파일 내용을 읽어 buffer에 저장 */
StringBuffer buffer = new StringBuffer();
if (file.exists()) {
    FileInputStream fis = new FileInputStream(file);
    byte b[] = new byte[1024];
    int read = 0;
    while ((read = fis.read(b)) != -1) {
        buffer.append(new String(b));
        b = new byte[1024];
    }
}
%>
파일내용
<%=buffer.toString()%>;


다음과 같은 요청을 보낸다면..

http://localhost/file.jsp?param=sample.txt



사용자 삽입 이미지

/ROOT/sample.txt  파일을 정상적으로 로딩하여 보여줄겁니다
하지만 다음과 같이 값을 준다면 어떻게 될까요?

http://localhost/file.jsp?param=../../../WINNT/system32/drivers/etc/services


사용자 삽입 이미지

저런 --;  시스템 파일들이 몽땅 조회가 되는군요..

Unix 계열이라면

http://localhost/file.jsp?param=../../../../../etc/passwd   ../ 올라가 보면서 찾아보는것도 좋겠죠 -_-


상위 디렉토리로 이동하면서 크래커는 passwd file을 볼수 있습니다.
앗 여기서 다들 뜨끔 하신가요? 저만 그러나 ^^;

그럼 어떻게 처리해야 할까요? ../ 만 막아선 될까요?
Unix에서는 ./.\. 도 상위로 가는 명령이죠
아시겠지만 cd ./.\. 하면 한단계 위로 올라간답니다
그러니 ../ 뿐만아니라 ./.\. 도 같이 막을수 있도록 파라미터 확실히 체크를 해야 합니다


3. 동적 include 파일 취약점
그럼 include는 어떨까요? 위처럼 동적으로 include 할 파일명을 받아 처리하는 구조 말이지요

<%
String param = request.getParameter("param");
%>

파일내용<br>
<jsp:include page="<%=param%>" flush="true"/>


http://localhost/include.jsp?param=sample.txt


라고 요청을 날렸습니다

사용자 삽입 이미지

네 정상적으로 sample.txt를 가지고 왔습니다
하지만 다음과 같은 요청이 가면 어떨까요?

사용자 삽입 이미지

네 역시나 기대를 저버리지 않고 web.xml을 기냥 출력해 버리는군요 (여기서 알아보기 쉽게 <를 나타나게 하였습니다)
문제는 /WEB-INF/ 밑에 기타 중요한 properties (데이터에비스 설정 properties)가 있다면 낭패입니다
그럼 어떻게 해야 할까요? 역시나 파라미터값을 필터링 하는수 밖에 없습니다

그래도 이정도면 약과 입니다 -_-;
다음을 보도록 하지요

JSP에서 include에는 대략 4가지 방법이 있습니다
<%@ include file="sample.jsp" %>
<jsp:include page="sample.jsp" flush="true" />
pageContext.include("sample.jsp")
<c:import url="sample.jsp" />

이 4가지가 무순 차이가 있을까요?
차이점을 쓸려다 보니 이번 강좌와 좀 동떨어지는것 같아 그건 제외하고 --;

문제는 동적으로 페이지가 include가 가능 하냐입니다
이점에서는 ①은 동적으로 설정이 가능하지 않으므로 안전한 include입니다 (서버측 include이니 당연합니다)
그럼 ②과 ③은 어떨까요? include할 페이지 설정이 동적으로 가능합니다
그래서 위와 같은 문제점이 있지요
하지만 ②, ③은 확장자가 jsp에만 반응을 하고 확장자가 jsp가 아닐 경우에는 그냥 텍스트로 include를 처리해 버립니다
④번은 좀 특이합니다 물론 동적으로도 가능하며 리모트로도 가능하다는 것입니다.

<%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %>
...
String param = request.getParameter("param");
<c:import url="<%=param%>"/>
...


http://localhost/include.jsp?param=http://www.jakartaproject.com/test.jsp


어떻게 될까요?
그나마 다행인것이 test.jsp 파일을 가지고 와서 서버에서 실행되는 것이 아니라 리모트에서 실행된후의 html 결과물만을 가지고 오는군요 그렇지 않으면 악성 스크립트를 활용할수있는 좋은 구멍 이었을텐데 말입니다 ^^;

다음표로 include 4가지 특성을 정리하였습니다

 유형

동적 실행

jsp 확장자에 반응

리모트 실행여부

<%@ include=

N

확장자에 상관없이 JSP로 실행

N

<jsp:include=

Y

확장자가 JSP만 JSP로 실행

N

pageContext.include

Y

확장자가 JSP만 JSP로 실행

N

<c:import url=

Y

확장자가 JSP만 JSP로 실행

Y









3. 파일 업로드 취약점
해커가 가장 많이 이용하고 좋아하는 취약점이 바로 이 파일 업로드입니다
이 부분은 누차 강조되어왔던 부분이라 대부분 아실겁니다
php 서버에는 php파일이, asp서버에는 asp파일이, jsp 서버에는 jsp파일이 업로드 되어서는 안됩니다
즉 서버에서 실행 가능한 파일을 올려선 안된다는 겁니다

악성 스크립트 침투 시나리오
악성 jsp 코드가 들어있는 test.jsp 파일을 글쓰기 화면을 통해 첨부 파일로 업로드 합니다
업로드 디렉토리가 /upload/ 라고 가정한다면 (업로드 디렉토리를 찾는건 그리 어렵지 않습니다)
    /upload/test.jsp 파일이 첨부파일을 통해 업로드 되었습니다
URL로 그 파일을 요청합니다  http://localhost/upload/test.jsp
    그러면 악성 코드가 들어있는 첨부파일이 웹서버에서 실행되어 버립니다
    test.jsp 파일이 서버의 파일을 모두 삭제하는 스크립트라면 큰일이지요
혹은 include 취약점을 이용해서 업로드한 스크립트를 include 시켜 실행 하기도 합니다


4. 파일 다운로드 취약점

간단한 jsp에서 일반적으로 사용하는 파일 다운로드 코드입니다
이역시 물리적 파일명 자체를 파라미터로 전달하고 있으며 이를 받아 파일을 다운로드 시키고 있습니다

<%@ page import="java.io.*"%><%

String param = request.getParameter("param");
File file = new File(getServletContext().getRealPath("/upload/")+param);

response.setContentType("application/smnet");
response.setHeader("Content-Disposition", "attachment;filename=다운로드;");
response.setHeader("Content-Length", String.valueOf(file.length()));
response.setHeader("Content-Transfer-Encoding", "binary;");

/* 파일이 존재하면 다운로드 */

if (file.exists()) {
    byte b[] = new byte[4096];
    BufferedInputStream bin = new BufferedInputStream(new FileInputStream(file));
    BufferedOutputStream outs = new BufferedOutputStream(response.getOutputStream());
    int read = 0;
    try {
        while ((read = bin.read(b)) != -1){
            outs.write(b,0,read);
        }
    } catch (Exception e) {
        System.out.println(e.getMessage());
    } finally {
        if (bin!=null) try { bin.close(); } catch (Exception sube) {}
        if (outs!=null) try { outs.close(); } catch (Exception sube) {}
    }
}

무엇이 잘못되었을까요?
다음과 같이 URL을 요청해 봅시다

http://loalhost/download.jsp?param=../WEB-INF/web.xml

사용자 삽입 이미지

그려면 요청한 web.xml 이 다운로드 됩니다
즉 이말은 웹 어플리케이션의 모든 파일(소스)을 다운로드해 볼수 있다는 것입니다
소스를 보게 된다면 웹해킹이 물론 더 쉬워지겠지요?


5. 업로드 및 다운로드 취약점 개선 시나리오
악성 jsp 코드가 들어있는 test.jsp 파일을 글쓰기 화면을 통해 첨부 파일로 업로드 합니다
/upload/test.jsp 로 업로드 후 파일명을 변경해 버립니다
    /upload/200601171137507804462_jsp 로 변경해 버립시다
    단순히 파일명만 변경해선 안됩니다 확장자도 제거해 버려야 합니다
    업로드 후 파일의 확장자가 두개 이상이던지, "." 이 이유없이 많다던지, "/" 나 "\" 캐릭터가 파일명에 있으면 부적절한 파일로 간주하고 지워버립시다
    그리고 200601171137507804462_jsp 명과 실제 파일명을 같이 데이터베이스에 저장합니다
URL로 그 파일을 요청합니다  http://localhost/upload/test.jsp
    당연히 파일이 존재하지 않음으로 Not Found가 나올것입니다
그럼 다음 URL로 요청해 봅시다 http://localhost/upload/200601171137507804462_jsp 
    어떻게 될까요?
    jsp 코드는 실행되지 않고 text로 jsp 코드를 그냥 브라우져에 뿌리게 됩니다
    즉 아무 상관이 없다는 것입니다
    200601171137507804462_jsp 도 유저에게 보여주어서는 안됩니다
    해당 URL 조차 허용하고 싶지 않다면 아래 web.xml에 security 제한을 둡시다
  

<security-constraint>
     <display-name>JSP and Upload File Protection</display-name>
     <web-resource-collection>
         <web-resource-name>SecureFile</web-resource-name>
         <url-pattern>/upload/*</url-pattern>
         <http-method>DELETE</http-method>
         <http-method>GET</http-method>
         <http-method>POST</http-method>
         <http-method>PUT</http-method>
     </web-resource-collection>
     <auth-constraint>
         <role-name>nobody</role-name>
     </auth-constraint>
</security-constraint>

<security-role>
    <description>Nobody should be in this role so JSP files are protected from direct access.</description>
    <role-name>nobody</role-name>
</security-role>

 

    그러면 http://localhost/upload/200601171137507804462_jsp 은 다음과 같은 메세지를 뿌립니다

사용자 삽입 이미지

그리고 파일 다운로드는 직접파일 링크가 아니라 response의 ouputStream을 통해 다운로드 시킵시다
   키값을 가지고 200601171137507804462_jsp 명을 얻어 다운로드 할수 있도록 프로그램 해야 합니다
   즉 다운로드 시킬때도 물리적 파일명을 파라미터로 전달하지 말고 해당 키값을 전달해서 파일명을 데이터베이스에서 얻어오도록 합니다

ps. 이런 취약점 때문만은 아니겠지만 업로드한 파일을 파일 시스템으로 저장하지 않고 데이터베이스에 직접 저장하기도 합니다

6. SQL Injection으로 인한 결과물을 파일로저장하여 다운받아 볼 수 있습니다
앞에서 살펴본 SQL Injection을 예를들어봅시다
아래 코드는 게시물 번호를 파라미터로 입력받아 해당 글번호를 조회하는 코드입니다
아래와 같은 코드가 있다고 한다면 UNION SQL Injection 피해를 볼수 있다고 하였습니다

String boardno = request.getParameter("boardno");
rs = stmt.executeQuery("SELECT boardno, boardtitle, boardcontent FROM board_test_t WHERE boardno = "+boardno);
    if (rs.next()) {
        out.println(rs.getString(1)+"<br>");
        out.println(rs.getString(2)+"<br>");
        out.println(rs.getString(3)+"<br>");
}

데이터베이스가 MySQL 이라고 한다면 URL 요청이 다음과 같다면 어떻게 될까요?

http://localhost:8080/test.jsp?boardno=1133971825719 UNION SELECT '1', userid, userpw FROM user_t INTO OUTFILE 'C:/Tomcat/webapps/ROOT/upload/hack.txt'



다음과 같이 에러 메세지가 납니다 하지만.. hack.txt란 파일은 생성이 됩니다
즉 user의 모든 아아디 비밀번호를 간편하게 파일로 다운받아 보는겁니다


HTTP Status 500 -

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception org.apache.jasper.JasperException org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:372) org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:292) org.apache.jasper.servlet.JspServlet.service(JspServlet.java:236) javax.servlet.http.HttpServlet.service(HttpServlet.java:802) com.jakartaproject.util.CharacterEncodingFilter.doFilter(CharacterEncodingFilter.java:28)

root cause java.lang.NullPointerException org.apache.jsp.test_jsp._jspService(test_jsp.java:64) org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:94) javax.servlet.http.HttpServlet.service(HttpServlet.java:802) org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:324)



그 다음 http://localhost:8080/upload/hack.txt 로 user_t 테이블에 있는 모든 사용자아이디와 비밀번호를 텍스트로 받아볼 수 있습니다. 아마 이방식으로 유저 아뒤/비번 많이 빼내신분들 많으실 겁니다 ㅎㅎ

톰캣 설치 경로는 어떻게 알까요?
톰캣설치경로 대부분 비슷할 겁니다 몇가지 해보면 금방 알수 있습니다


7. 마무리
그렇다면 어떻게 해야 이러한 피해를 최소화 할 수 있을까요? 나름데로 생각해 보았습니다

파라미터와 파라미터값에 대해 알아볼 수 없도록 최소한의 의미만 부여한다!!
   역시나 파라미터가 중요합니다 누구나 알아볼 수 있도록 하는 의미보다는 나름데로
   Naming rule을 정한다든지 암호화 하든지 하는것도 바람직 할 수 있다고 봅니다

파라미터값이 유효한 값인지 체크해 봅니다
   파라미터값을 최대값, 최소값으로 제한하고 꼭 있어야 하는 문자열이나 꼭 없어도 되는 문자열등을 체크합니다
   파라미터값 길이또한 최대, 최소값 제한을 둡니다

중요한 파라미터 값은 (파일명같은..)  절대로 받지 않고 다른 로직으로 생각해 봅시다
   파일명같은 중요한 값은 데이터베이스에 입력해 놓고 키값을 파라미터로 전달받아 파일명을 조회한 후 사용합니다

웹프로그램의 취약점 분석 툴들이 찾아보면 몇개 있습니다(대부분 유로더군요) 이를 이용하여 테스트해 봅니다

두서없이 생각나는데로 적었습니다
그나마 JSP는 다른 PHP나 ASP에 비해 상당히 보안에 안정적입니다 JDBC 마다 틀리겠지만요
해결 방법이 생각해 보면 더 많을겁니다
물론 서버단에서 막는 방법이 제일 좋겠지만요 몇가지 사례를 적어놓았으니 그다음은 응용하기에 달렸지요
Posted by skensita