File Download 취약점

외부 입력 값에 대해 경로 조작에 사용될 수 있는 문자를 필터링 하지 않으면, 예상 밖의 접근 제한 영역에 대한 경로 문자열 구성이 가능해져 시스템 정보누출, 서비스 장애 등을 유발 시킬 수 있는 취약점

출처: http://skynarciss.tistory.com/37 [IT 보물창고]


1. 대상 확인

작성된 게시글의 첨부파일을 다운받는 페이지를 살펴봅시다.

업로드된 파일명을 클릭하면 다운로드를 받을 수 있습니다.

Burp Suite를 이용하여 파일 다운로드 요청이 어떻게 전송되는지 살펴보겠습니다.


파일 다운로드를 하기 위해 fileDown.jsp로 요청을 보내는데, 파일명을 GET방식으로 fileName이라는 파라미터에 값을 셋팅하여 전송하고있습니다.


GET방식이기 때문에, 같은 url을 입력하면 파일을 다운 받을 수 있습니다.

 


2. 로직

이 게시판에서 파일을 다운받게 해주는 로직을 추측해보고, 실제 소스코드를 봅시다.

1. GET 파라미터인 fileName에 파일명이 전달된다. 

2. 파일 업로드 시 파일을 저장한 저장 디렉토리의 위치에 파일명을 덧붙혀 파일경로를 완성한다.

3. 해당 파일 경로에 있는 파일을 클라이언트로 전송한다.


실제 소스코드를 보면 위에서 추측한 로직과 동일한 것을 알 수 있습니다.

이 게시판은 C드라이브 아래에 있는 save_dir 디렉토리에 파일을 저장하고 있었네요.


3. 공격

File Download 취약점이 존재하는 지 알아 보기 위해 디렉터리 이동 명령어들이 실행 되는지를 아래와 같이 확인해 볼 수 있습니다.

파라미터는 test/../148609......jpg 의 형태로 입력이 되었습니다.

이 파일 명이 의미하는 바는 잘 아시다시피 현재 경로에서 test디렉터리 아래 -> 한 단계 상위 -> 에 있는 148609......jpg가 됩니다. test디렉터리 아래 -> 한 단계 상위는 서로 상쇄되어 결국 148609......jpg가 되는 것인데,

이 테스트의 의미는 디렉터리 이동 명령어들이 필터에 걸리지 않는 지, 혹은 다른 조치가 되어 있는 지를 확인하는데 의미가 있습니다.

위 테스트를 하였을 때 성공적으로 튤립 그림을 다운받을 수 있었으므로, File Download취약점이 있다고 보여집니다.


파일 이동 명령어가 실행이 되는 상태라면 자유자재로 서버의 파일들을 다운 받을 수 있습니다.(물론 원하는 파일의 위치를 알아야겠지만요)

이 게시물에서는 예시로 C:/Window/System32 경로에 test.txt파일을 만들어서 공격 예시를 들어보도록 하겠습니다.


구조는 어렵지 않고 다만 파라미터에 원하는 경로를 조립하여 넘겨주었습니다.

결과적으로 해당 경로에있는 test.txt파일을 다운 받을 수 있었습니다.



4. 조치 방법

각 개발 프레임 워크 별 설정방법이 있는 것 같습니다만, 우리 게시판 프로젝트는 순수 jsp로 짜여진 게시판이므로ㅠㅠ 간단한 필터링을 통해 조치를 해보도록 하겠습니다.

위의 소스코드와 같이 입력받은 파라미터를 곧바로 사용하지 않고 디렉터리 이동에 사용되는 문자("\.", "..", "/"들을 공백으로 치환해주었습니다.

그리고 파일이 존재 하지 않는 경우에는 (더 좋은 처리가 있겠지만..) 더미 파일을 전송하도록 하여 처리를 했습니다.






'Study > WEB' 카테고리의 다른 글

jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

SQL

RDBMS(Relation DataBase Management System)의 데이터를 관리하기 위해 설계 된 특수 목적 프로그래밍 언어로써 쉽게 말해 데이터베이스를 핸들링 하기 위해 사용하는 언어입니다.


SQL Injection

응용 프로그램 보안 상의 허점을 의도적으로 이용해, 개발자가 생각지 못한 SQL문을 실행되게 함으로써 데이터베이스를 비정상적으로 조작하는 코드 인젝션 공격 방법입니다.

[출처] 위키백과(https://ko.wikipedia.org/wiki/SQL_%EC%82%BD%EC%9E%85)


Blind SQL Injection

SQL Injection의 결과를 이용하여 참, 거짓 값을 구별 할 수 있을 때 사용할 수 있는 방법으로 장님이 길을 지팡이로 짚으며 가는 듯한 과정이 있어 이를 Blind SQL Injection이라고 합니다.


1. 대상 확인

언제나 처럼 게시판을 이용하여 정리해보도록 하겠습니다.

먼저 게시판의 검색 부분을 살펴보겠습니다.

검색조건을 선택하고 검색 키워드를 입력한 후 검색 버튼을 누르면 해당하는 내용을 검색 할 수 있습니다.

검색 조건은 작성자 / 키워드 / 작성자 + 키워드이며 공란인 경우 아래와 같은 알럿메시지를 표출합니다.

검색에 이용되는 키워드는 쿼리 작성에 사용이 될텐데 검색 후 URL에 파라미터가 표시되지 않는 것으로 보아 POST방식으로 파라미터가 전달 되고 있는 것 같습니다.

검색 할 당시의 패킷을 Burp Suite로 확인 해보도록 하겠습니다.


위에서 추측한대로 검색에 관련된 파라미터들(검색 조건/검색 키워드)이 POST방식으로 전달되고 있는 것을 확인 할 수 있습니다. (searching_condition=title&searching_keyword=TE)


2. 우회 과정

이 키워드에 전달되는 파라미터에 쿼리를 인젝션하여 특정 게시물의 비밀번호를 알아내보겠습니다.

결론부터 이야기하자면 인젝션하여 서버에서 실행 될 쿼리의 예시는 아래와 같습니다.

SELECT letterNum, userID, letterTitle, secretYN
FROM board
WHERE userID LIKE '%USER%'

AND ((ascii(substr((SELECT letterPW from board where letterNum = 6), 1, 1))) = 48)#%'

현재 서버에서 실행되고 있는 쿼리는 

SELECT letterNum, userID, letterTitle, secretYN
FROM board
WHERE userID LIKE '%{INPUT}%'

의 형태이고, 이 INPUT안에 쿼리를 삽입하여 위처럼 공격 쿼리를 만들어 내야합니다.

(지금은 직접 개발한 게시판이기 때문에 DB정보와 쿼리문의 구조를 알고있지만,

실제로는 추측/유추 해야하는 과정이 필요합니다.)

따라서 INPUT에 삽입될 쿼리는 아래와 같습니다.

USER%'

AND ((ascii(substr((SELECT letterPW from board where letterNum = 6), 1, 1))) = 48)#%'

AND 뒷 부분이 핵심이므로 이 부분을 상세히 살펴보도록하겠습니다.

1) SELECT letterPW FROM board WHERE letterNum = 6

-> 이 예시에서는 게시글 번호 6번 글에 대한 패스워드를 유추하고 있습니다. 따라서 6번 게시물의 패스워드를 조회해 옵니다.


2) substr( 6번게시물 패스워드, 1, 1)

-> mysql에서 실행되는 함수로 문자열의 첫 번째 글자부터 한 글자를 조회해옵니다.

-> 결과적으로 6번 게시물 패스워드의 맨 첫 글자를 가져올 수 있습니다.


3) ascii(문자)

-> 해당 문자를 아스키 코드 값으로 변환해줍니다.


4) 결과

6번 게시물 패스워드의 첫 글자의 아스키코드가 아스키코드 값 48과 같은 지를 반환합니다.

위의 결과가 true인 경우, 원래의 쿼리와 결과가 종합되어 모든 게시물이 출력되고(true and true이므로),

false인 경우 게시물 조회 결과가 없음으로 표시됩니다.


패스워드를 한 글자씩 찾는 스크립트가 필요합니다. 

스크립트 풀 소스는 첨부 하고, 주요 부분만 확인하도록 하겠습니다.  : )

첨부 :  blinSqlInjection.py

실습환경이기때문에, 비밀번호는 숫자와 영대소문자로만 이루어져있다는 가정을 하였습니다.

스크립트는 파이썬으로 작성하였으며 스크립트 실행 전 설치해야할 파이썬 라이브러리로는

http리퀘스트를 위한 requests와 결과를 파싱하기 위한 Beautifulsoup가 있습니다. 


헤더 값은 Burp Suite에서 얻은 패킷의 값과 동일하게 맞추어주고, 파라미터를 변경합니다.

우리가 수정해서 삽입할 쿼리를 완성시킨 executeSql을 키워드 파리미터에 셋팅해줍니다.

requests를 이용하여 리퀘스트를 보내고 받은 결과를 BeautifulSoup를 사용하여 파싱합니다.

해당 게시판의 list.jsp 응답 내용 중 "총 게시물 : x개" 로 body에 응답이 오는데 이 부분을 파싱하여

0개인 경우에는 찾지 못한 것으로, 0개 이상인 경우에는 찾은 것으로 판별하였습니다.


아래는 파싱하여 게시물의 갯수를 int형으로 변환하여 리턴해주는 함수입니다. 

첫 째줄에서는 공백을 제거하였고, ":"로 스플릿한 뒷부분을 공백제거하여 이 후

첫 글자를 int형으로 변환한 후 리턴해줍니다.


비교할 검사 문자는 0~9, a ~ z, A ~ Z 입니다. 각 문자들이 아스키 코드표 상 연속되어 존재하지 않기 때문에

구간 구간 점프를 뛰어야하는 부분을 if문으로 걸러주었으며, 찾는 문자 구간에 문자가 없으면 패스워드의 끝으로 인식하고 있습니다. ( elif int(asciiNum) > 122 부분)

문자를 찾은 경우에는 targetPW라는 필드에 찾은 문자를 덧 붙혀주고, 검사 문자 순서를 다음으로 넘긴 후

비교 문자를 0('48')로 초기화 시켜주고, 쿼리문을 갱신합니다.


위 과정을 모두 마친 후 추출해낸 패스워드를 확인합니다.


스크립트 실행 결과


3. 조치 방법

1. jspBoardProject#2(XSS) 에서 조치했던 것 처럼 입력 값에 필터링을 수행하여 검사할 수 있습니다.

2. JAVA/JSP에서는 쿼리 실행을 Statement클래스를 사용하지 않고 PreparedStatement를 사용하는 방법이 있습니다.


필터링에 관해서는 앞서 다뤄본 방식과 별반 다르지 않기 때문에, PreparedStatement를 사용하는 방법으로 조치해보도록 하겠습니다.

먼저, 문제가 되는 부분의 소스코드를 살펴보면,

아래와 같이 변수가 쿼리에 직접적으로 연결되는 구조로 Injection하기 너무나 편리하게 되어있는 것을 확인 할 수 있습니다.



이 부분을 PreparedStatement 인터페이스를 사용하여 다음과 같이 조치 할 수 있습니다.

바뀐 점은 일반 Statement인터페이스에서는 쿼리와 Input value가 직접적으로 연결되어 실행이 되지만,

PreparedStatement에서는 쿼리문에서는 ? 로 인자 표시를 하고 후에 setString과 같은 메서드를 사용하여

변수를 추가 해주고있습니다.

코드 수정 후 스크립트 결과를 보면 아래와 같이 조치가 된 것을 확인 해볼 수 있습니다.



여담.

order by 뒷 부분 역시 PreparedStatement방식으로 사용 해보려고했으나, setString(4, sorting_cond); 와 같은 방식으로 추가했을 때 sorting_cond가 상수 값으로 인식되어 정상적으로 정렬이 되지 않았습니다.

외부에서 직접적으로 입력을 받는 부분이 아니기때문에 문제가 없을 것으로 생각되어 기존처럼 쿼리에 연결을 하였습니다.

'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

파라미터 변조

- 클라이언트와 서버 사이에서 주고받는 파라미터 값을 조작

- 매개변수라고 하며 웹페이지상에서 request 대한 값을 가지고 있는 변수

- 사용자 기밀정보, 허가, 상품의 개수, 가격 등을 조작

- 이러한 정보는 쿠키나 숨겨진  필드, UR 쿼리 문자열 등에 저장


출처: http://elmosec.tistory.com/4 [보알못 엘모의 보안 겅부 필기 공책 :)]


게시판으로 예시를 들어보도록 하겠습니다.

게시판의 메인 페이지입니다. 여러 글들이 있고, 그 중 비밀글인 글들이 보입니다.

파라미터 변조를 통해 비밀글에 접근을 해보도록 하겠습니다.


1. 대상 확인

1.1) 비밀글을 열람하려고 글을 클릭하면 비밀번호를 묻는 페이지가 나오고, 비밀번호가 틀리면 비밀번호가 틀렸다는 얼럿이 나타납니다. 


1.2) 서버에 날아가는 요청을 프록시 툴인 Burp Suite로 분석을 해보겠습니다.


우선, 요청 url은 openSecret_ok.jsp이며,

POST방식으로 입력한 패스워드가 평문으로 전송되고, 비밀 게시글의 번호가 idx라는 GET파라미터로 전송되고 있습니다.

이 페이지(openSecret_ok.jsp)에서 글의 인덱스와 비밀번호를 파라미터로 받아서 패스워드 검사를 하였는지, 비밀번호가 틀렸다는 알럿 메시지가 뜨는 자바스크립트를 응답으로 받았습니다.


2. 로직 분석

메인 아이디어는 아래와 같습니다. 이 게시판 서비스는 openSecret.jsp에서 비밀번호를 입력 받은 후 openSecret_ok.jsp에서 인증을 하여 인증에 성공한 경우 view.jsp에서 서비스가 실행 되는 구조로써 인증과 서비스 로직이 분리되어 있어 이 점을 이용하였습니다.


3. 우회 시나리오

1) openSecret_ok.jsp 에서 인증가능한 파라미터를 전달합니다.

 - 하나의 새 글을 작성하여 패스워드를 알고있는 글을 만들었습니다.

2) openSecret_ok.jsp의 인증을 따낸 후 view에 글을 열람할 인덱스를 전달 할 것이므로, 이 인덱스를 우리가 원하는 비밀글의 인덱스로 변경하여 전달합니다.


사실 비밀글이 아닌 곳에서 view.jsp에 전달되는 get 파라미터만 변경하여도 비밀글에 접근이 가능했었습니다..ㅋㅋ


조치 방안에서는 이 두 가지 문제점 모두 수정하였습니다. :)


4. 우회 과정

4.1 시나리오에서 언급했던 것 처럼 새 글을 비밀번호 1234로 하여 작성하였습니다. 

4.2 비밀번호 입력 후 패킷을 변조해봅시다.

패스워드를 1234로 변경하였고(입력할 때 부터 1234로 입력해도됩니다.), 인덱스가 원래 4였지만 18(우리가 게시한 글의 인덱스)로 수정하였습니다.

 

4.3 인증에 성공하자 예상한 대로 view.jsp에 GET방식으로 파라미터를 인덱스로 던지고 있습니다. 이 부분을 4(비밀게시글 인덱스)로 수정해보겠습니다.


4.4 우회 성공

5. 조치 과정

5.1 조치 전 소스 코드

openSecret_ok.jsp



view.jsp


위 로직의 가장 큰 문제점(본인이 짰지만..)이라고 생각한 건 위에서 말한 것 처럼 인증 로직과 서비스로직이 분리되어 있기 때문에 이 같은 취약점이 발생했다고 생각했습니다.

openSecret_ok.jsp에서는 인증만을 수행하고 view.jsp에서는 글을 보여주는 서비스 로직만 있으므로, 생각해낸 방법은 openSecret_ok페이지를 삭제하여 인증 과정을 view.jsp페이지에 넣는 방법을 사용해보기로 하였습니다.

5.1 조치 후 소스 코드

먼저, openSecret.jsp의 소스에서 입력 내용을 openSeceret_ok.jsp로 전송하던 부분을 view.jsp로 전송하도록 수정하였습니다.


이후 view.jsp에서는 pw를 추가로 파라미터로 수신하며, 비밀글 여부 체크 후 비밀번호 인증하는 로직을 추가하여 조치하였습니다.


'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#2(XSS)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

XSS

XSS(Cross-site Scripting)는 웹 상에서 가장 기초적인 취약점 공격 방법의 일종으로, 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법을 말합니다.

스크립트가 삽입 가능할 때 쿠키 탈취, 악성코드 유포, 특정 사이트로 납치 등 많은 공격이 가능 합니다.


게시판으로 예시를 보도록 하겠습니다.

게시판에 글을 작성 하며 글 내용에 알림창을 띄우는 간단한 자바스크립트를 삽입하였습니다.



해당 글을 열람하기위해 제목을 누르는 순간 삽입한 javascript 코드가 실행 되는 것을 확인 할 수 있습니다.


게시판의 글 작성 로직을 살펴보면

1. write.jsp에서 작성자 / 제목 / pw 등의 입력을 받는다.

2. write.jsp에서 입력 값의 유효성을 검사한다.

3. wirte_ok.jsp에서 게시글을 DB에 등록한다.

위 과정을 통하여 글이 작성되는 것을 살펴 볼수 있습니다. 


아래의 자바스크립트 코드는 위 과정 중 2번에 해당하는 부분으로 클라이언트가 글을 작성 한 후 그 컨텐츠의 유효성을 체크하는 코드입니다. 

보이는 것처럼 글을 작성할 당시 각 필드의 값이 빈 값인지 여부만을 확인 하고 있습니다. 


이 코드만 보면.. 글 제목과 작성자 란에도 스크립트 삽입이 가능하며, 메인페이지만 열어도 스크립트가 실행되어 방문자 모두가 스크립트를 실행하게 될 수 도있 습니다.

이제 이 유효성 검사 부분을 수정하여 XSS를 방어하는 방법 중 입력 내용을 필터링 하도록 수정해보도록 하겠습니다.


각 필드의 공백 유효성 검사 한 후 공백이 아닌 경우 스크립트에 포함되는 문자열들을 빈문자열 혹은 다른 문자열로 대체하도록 필터링을 하는 코드를 추가하였습니다.


결과로 같은 스크립트를 삽입하였을 때 아래와 같이 게시글이 필터링되어 게시됩니다.


   



'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#1  (0) 2017.02.06

JSP를 사용하여 간단하게 게시판을 만들고 취약점을 찾아 진단, 조치까지 해보기로 하였습니다.

게시판을 만드는 과정은 소스코드를 첨부 : jspBoardProject.zip하는 것으로 생략하고

취약점을 진단하고 조치하는 과정에 초점을 맞추어 블로깅을 해보도록 하겠습니다.

급박하게 개발하느라 발로 짠부분도 있고, 후에 취약점 진단을 하기위해 일부러 취약하게 짠점도 있는데 

양해부탁드립니다 ㅠㅠ


개발 환경은 아래와 같습니다.

개발환경 

1. IDE : STS 3.8.3 / Dynamic Web Project

2. DB: mysql


간단하게 게시판의 형태를 살펴보도록 하겠습니다.

게시판의 메인화면입니다.



분석해볼 게시판의 기능은 크게 

1. 일반/비밀 게시글 작성

2. 파일첨부 기능


3. 게시글 정렬 기능


4. 게시글 검색 기능

5. 게시글 삭제 기능


6. 게시글 수정 기능

일반적으로 게시판이 가지는 기능을 가지고 있습니다.

블로깅은 취약점 진단/해당 취약점 조치 식으로 진행 해보도록하겠습니다.

'Study > WEB' 카테고리의 다른 글

jspBoardProject#5(File Download)  (0) 2017.02.08
jspBoardProject#4(SQL Injection / Blind SQL Injection)  (0) 2017.02.07
jspBoardProject#3(파라미터 변조)  (0) 2017.02.06
jspBoardProject#2(XSS)  (0) 2017.02.06

+ Recent posts