// 불펌스크립트시작 // 불펌스크립트 끝

1. SQL Injection 공격 원리

- SQL Injection 공격을 이해하기 위해서는 데이터가 어떤 과정으로 저장되고 사용되는지 알아야 한다.

- 게시판에서 회원가입을 할 때 아이디와 패스워드를 입력한 정보는 모두 어디에 저장이 되었을까?

- 이 게시판에서는 회원가입 정보를 board 데이터베이스의 member 테이블에 저장한다.

- 아이디는 bId 열에, 패스워드는 bPass 열에 각각 저장되어있다.

 

 

- 로그인 페이지 파일명은 default.htm이다.

- 등록된 사용자의 아이디와 패스워드를 입력 받아 member 테이블에서 동일한 아이디와 패스워드를 가진 레코드를 찾아 사용자가 게시판에 접근하는 것을 허용한다.

- 계정이 존재하지 않거나 패스워드가 올바르지 않으면 잘못된 로그인임을 알려줄 것이다.

- 아래 소스를 보면 login_ck.asp 파일로 정보를 보내고 있음을 확인할 수 있다.

 

 

- login_ck.asp 파일을 열어보면 아이디(memberid)와 패스워드(pass1)를 받아서 SQL 쿼리문(strSQL)을 만들고 실행시켜 member 테이블의 정보를 가져온다.

- 입력된 아이디와 패스워드에 대한 결과 값이 있다면, 즉 실제로 등록된 사용자라면 board_list.asp가 실행되고, 아이디와 패스워드를 잘못 입력하거나 등록되지 않은 사용자라면 잘못된 로그인입니다라는 메시지를 보낸다.

 

 

2. 공격 방법

▶ 인증 우회 ( Authentication Bypass )

⊙ 인증 우회

- 가장 널리 이용되는 SQL Injection 방법은 OR 연산을 사용하는 것이다.

- 입력 폼에 `or '1'='1 문을 입력하여 결과 값을 참으로 만들어 우회하는 방법이다.

- 예를 들어, 로그인 화면에서 아이디는 TEST_ID로 하고, 패스워드는 test로 해서 로그인을 시도할 경우 패스워드가 틀려서 잘못된 로그인이라는 메시지를 받게 된다.

- 하지만, 패스워드에 test' or '1'='1을 입력하면 login_ck.asp 파일에서 회원 로그인을 처리하기 위해 데이터베이스에 요청하는 쿼리문을 최종적으로 select * from member where bId = 'TEST_ID' and bPass = 'test' or '1'='1'과 같이 전달되고 이 때 where 절 뒤쪽에 임의로 삽입된 oR 조건에 의해서 OR 앞이나 뒤의 두 조건 중에 하나라도 참이면 참이 되어 결과 값을 반환한다. 여기에서 1과 1은 같은 값이므로 앞에 어떠 값이 오더라도 항상 참.

- 여기서 한 가지 더 주의할 점은 홑따옴표(')의 사용이다. 아래의 소스 코드에서 알 수 있듯이 아이디와 패스워드로 SQL 문을 생성

 

 

- 이 때, TEST_ID와 test은 홑따옴표 안에 들어간다. 이미 알고 있는 것처럼 문법상 홑따옴표는 2개를 쌍으로 사용해야 한다.

- 따라서, SQL 문을 조작할 때도 에러가 나지 않도록 주의해야 한다.

- test' or '1'='1을 입력하면 아래와 같은 SQL 문이 만들어진다.

 

 

- 모든 사이트가 동일한 형식으로 쿼리문을 생성하는 것은 아니다. 여기에서도 test' or '1'='1 뿐만 아니라 'or 1=1도 공격에 이용할 수 있다.

 

 

⊙ 패스워드 우회

- 패스워드가 틀려도 로그인에 성공할 수 있는지 확인하기 위해 로그인 창에 쿼리문을 입력해보자

 

 

 

- 로그인에 성공하였지만 TEST_ID가 아니고 admin으로 로그인 되었을까..

- SQL 문을 한번 더 보게 되면, 연산자  우선순위로 볼 때, AND는 OR보다 먼저 계산된다.

- 따라서, (bId='TEST_ID' and bPass='test')가 먼저 계산되어 그 결과가 FALSE로 되고, FALSE or (1=1)이 그 다음에 계산된다.

- 즉, FALSE or TRUE가 되어 결과적으로 TRUE가 되고 결국 4)와 같이 member 테이블에서 모든 레코드 값을 가져오게된다.

 

 

- 가져올 아이디 값이 정해지지 않았기 때문에, 가장 위쪽의 레코드를 읽어온다.

- 따라서, member 테이블에는 아래와 같이 2개의 계정만 존재하고, TEST_ID보다 admin이 위에 있다.

- 따라서, 가장 위에 있는 레코드의 admin 값을 가져오게 되어 admin으로 로그인에 성공한 것이다.

- 많은 경우에 관리자(admin)가 가장 위에 존재하기 때문에 이런 방법으로 관리자로 로그인 할 수도 있다.

 

 

⊙ 주석문을 이용한 admin 로그인

- 1)의 SQL 문을 2)처럼 바꾸면, 주석문(--) 이후의 문자열은 주석 처리되어 무시되므로 결과적으로 3)의 SQL 문이 된다.

 

 

- 이것을 로그인 페이지에서 입력해보자. ( 이 때도 홑따옴표를 잊지 말자.)

 

 

-  admin으로 로그인에 성공했다. 이제 이 게시판의 관리자 권한을 갖게 된 것이다.

 

 

▶ 시스템 명령어 실행

- xp_cmdshell은 MS-SQL DB에 잇는 master DB의 확장 프로시저로서, 이것을 이용하면 시스템 명령어(OS Call)를 실행할 수 있다.

- SQL 쿼리 분석기 창에서 xp_cmdshell을 사용하기 위해 등록할 떄는 다음과 같은 명령어를 실행하면 된다.

 

등록

 sp_addextendedproc 'xp_cmdshell', 'xplog70.dll

제거

 sp_dropextendedproc 'xp_cmdshell' 또는

 C:\Program Files\Microsoft SQL Server\MSSQL\Binn에서 xplog70.dll 삭제

 

⊙ 두가지 이상의 쿼리문 실행

- 두 가지 이상의 명령을 연속해서 사용할 때는, 세미콜론(;) 표시를 이용하여 사용할 수 있다.

- 먼저 member 테이블의 내용을 확인하고, 자기 자신의 시스템에 ping 명령을 수행하는 ping 127.0.0.1을 실핼하였다.

 

 

⊙ 시스템에 폴더 생성하기

- 앞서 테스트한 ping 127.0.0.1이 포함된 SQL injection 명령을 게시판에서 실행하면, 공격이 성공하여 잘 실행이 되어도 결과를 확인할 방법이 없다. 그렇기 때문에 폴더를 만들어 시스템 명령어가 잘 실행되었는지 확인하자.

- 폴더를 생성하는 명령은 다음과 같다.

 

 

- 로그인 페이지의 아이디 입력 폼에 위의 쿼리문을 입력하고 싶어도 할 수 없을 것이다.

- 왜냐하면 아래의 예제에서 그 내용을 확인할 수 있듯이 입력 값의 길이가 15로 제한되어 있기 때문이다.

- 이렇게 길이에 제한이 있어 원하는 SQL Injection 문을 직접 입력할 수 없는 경우도 있다.

 

 

- 이럴 때는 프록시 툴을 이용하여 길이 제약을 피할 수 있다.

- 예로들면 오디세우스 프록시 툴을 이용하여 SQL Injection 문을 서버로 전달할 것이다.

- 먼저 admin/admin 계정으로 로그인할 때 전송되는 내용을 확인해보자.

 

 

- memberid=admin 부분을 아래와 같이 조작하여 전송하면 길이 제한에 상관없이 시스템 명령어를 실행할 수 잇다.

- URL 인코딩을 위해 공백은 %20으로 변경해야 한다.

 

 

 

- admin 계정으로 로그인되었지만 게시판 리스트만 보일 뿐 특별한 내용은 없다.

- 명령어가 제대로 실행되었는지 실제로 폴더를 확인해보면 C:\board 아래에 test 폴더가 생성된 것을 확인할 수 있다.

 

 

▶ 테이블 정보 열람하기

⊙ 집계함수

- 집계는 이미 계산된 것들을 모아서 계산하는 것을 말한다.

- SQL의 집계 함수에는 avg(), sum(), count() 등이 있다.

- 즉, 이미 계산된 결과를 어떤 기준으로 그룹화 하는 함수들이다.

- 그룹화하는 기준을 집계 키라고 하는데, GROUP BY 절의 칼럼이나 연산식을 집계 키로 사용할 수 있다.

- GROUP BY 절에 사용된 집계 키들만 SELECT 목록에 사용할 수 있다는 것이다. 그렇지 않으면 에러가 발생!

- 예를 들어, 쿼리 분석기에서 아래의 명령을 실행. 여기에서 member.bId는 테이블명.필드명으로 특정 필드를 가리키는 표현 방식

 

 

- 아래와 같이 SELECT 절에는 bId, bPass, bName, bMail이 있고 GROUP BY절에는 member.bId만 있을 경우에 에러가 발생한다.

- 왜냐하면 bPass, bName, bMail은 GROUP BY에서 사용하는 집계 키가 아니라서 SELECT 절에 사용할 수 없기 때문이다.

 

 

⊙ 테이블 명 획득

- GROUP BY 절이 어떤 칼럼을 기준으로 그룹을 만들 때 HAVING는 결과를 다시 한번 더 필터링 하는 역할을 한다.

- 이것은 GROUP BY 절에 대한 조건, 즉 일반적인 SELECT 절의 WHERE와 비슷하다.

- HAVING는 단독으로 쓰일 수 없으며 GROUP BY 절과 함께 사용되어야 한다.

- 이 점을 이용하여 다음과 같은 명령을 검색어로 입력하면 에러가 발생하는 것을 확인 할 수 있다.

 

 

- 에러는 'bbs.idx' 열이 집계 함수에 없고, GROUP BY 절이 없으므로 SELECT 목록에서 사용할 수 없다는 내용이다.

- 이 에러 메시지에는 bbs라는 테이블 명과, idx라는 필드명을 언급함으로써 중요한 정보를 노출하고 있다.

 

 

⊙ 필드 명 획득

- 이전에 획득한 테이블 명과 필드 명을 다시 이용하여 검색 쿼리문에서 사용하면 bbs 테이블에 있는 다른 필드 명도 알아낼 수 있다.

 

 

- 입력 값으로 group by 조건에 idx를 추가하였다.

- 이렇게 하면 집계 키에 idx가 있으므로 그 다음에 있는 tId를 볼 수 있다.

- 하지만 이것은 집계 키에 없으므로 bbs.tId를 사용할 수 없다는 에러를 발생시킨다.

- 이런 방식으로 계속해서 아래와 같이 반복하면 bbs 테이블의 전체 필드 명을 알 수 있다.

 

 

 

 

 

 

 

- 이 과정을 통해 bbs라는 테이블에 idx, tId, tName, tMail, tTitile, tContent, tFilename, tfilepath, tRead, tDate라는 10개의 열리 존재한다는 것을 알 수 있다.

- 로그인 페이지에도 위와 동일한 방식으로 테이블의 필드 명을 알아낼 수 있다.

 

 

- SQL 쿼리 분석기에서 로그인 페이지에 대해 위의 과정을 실행하여 결과를 확인해보자.

- 쿼리 분석기에는 에러가 발생해도 끝까지 실행하기 때문에 전체 에러 메시지를 확인할 수 있다.

 

- 결국, member 테이블에 idx, bId, bPass, bName, bPost, bAssr1, bAddr2, bPhone, bMail, bDate 열이 있으며 로그인 페이지에는 이것을 이용하여 쿼리를 하고 있다는 것을 짐작할 수 있다.

 

⊙ 필드 타입 획득

- 이제 UNION과 sum()함수를 이욯아여 필드 타입을 알아보자

- sum() 함수에는 숫자형만 사용할 수 있고 UNION을 사용하기 위해서는 두 테이블의 필드 수가 동일해야 한다.

- admin으로 로그인을 한 후 게시판 리스트 하단에 게시글을 검색하는 입력 폼에 테스트

- 아래의 board_search.asp 파일을 열어 확인해보면 검색어(Keyword)를 받아, Select * From bbs Where tTitle like '%keyword%'을 생성

- 쿼리문이 제대로 실행되면 제목에 keyword가 포함되어 있는 게시글을 찾아서 보여줄 것이다.

 

- 검색어에 union을 사용하여 member 테이블과 bbs 테이블의 필드 타입을 알아낼 수 있다.

 

 

 

- sum() 함수는 varchar 타입을 사용할 수 없기 때문에 에러가 발생하게 되고 bId가 varchar타입인 것을 알 수 있다.

- 동일한 방법으로 member 테이블의 bId, bPass, bName, bpost, bAddr1, bAddr2, bPhone, bMail과 bbs 테이블의 tId, tName, tMail, tTitle, tContent, tfilename, ffilepath가 varchar 타입을 사용한다는 것을 확인할 수 있다.

 

 

 

- 그리고 member.bDate와 bbs.tDate가 datetime 데이터 형식을 사용한다는 것도 확인하였다.

- 이제 member.idx와 bbs.idx, bbs.tRead만 확인하면 된다.

- 아래의 결과는 UNION에서 필드의 개수를 맞추지 않았기 때문에 에러가 발생했다는 것이며, 이는 데이터 형식에 대한 에러가 아니기 때문에 idx가 숫자형이라는 것을 유추할 수 있다.

 

 

 

- 실제 우리가 사용하는 테이블과 그 내용이 동일함을 알 수 있다.

 

member 테이블

bbs 테이블

필드 명

필드 타입

필드 명

필드 타입

idx

숫자

idx

숫자

bId

varchar

tId

varchar

bPass

varchar

tName

varchar

bName

varchar

tMail

varchar

bPost

varchar

tTitile

varchar

bAddr1

varchar

tContent

varchar

bAddr2

varchar

tfilename

varchar

bPhone

varchar

tfilepath

varchar

bMail

varchar

tRead

숫자

bDate

datetime

tDate

datetime

 

 

+ Recent posts