Union Based SQLi
SQL 질의문 결과가 화면에 보이는지 안보이는지에 따라 SQL 인젝션 공격 기법이 달라진다. 주소 검색, 게시글 리스트 등 화면에 결과가 보이는 경우와 로그인, 아이디 중복 체크 등 화면에 나오지 않는 경우가 있다.
이번에 알아볼 것은 결과가 보이는 곳에서 사용하는 Union SQLi이다.
Union SQL Injection
두 개의 select 문을 합칠 때 사용하는 union을 사용한 SQL 인젝션이다. 위에 말했듯 데이터 질의 결과가 화면에 보이면 사용한다. union을 사용할 때 고려해야 하는 것이 있는데 먼저 메인 쿼리의 컬럼 개수를 알아야 한다. 쿼리들의 컬럼 개수가 다르면 오류가 발생하는 것을 확인한 적 있었다. 컬럼 개수를 찾는데는 order by를 이용한다. order by는 데이터를 지정된 컬럼으로 정렬해주는 쿼리이다.
select id from member order by password (asc/desc)
order by 구문의 예시로 들어보았다. id를 password를 기준으로 정렬하는데 기본적으로는 asc (ascending) , 오름차순으로 정렬되어 있다. 내림차순으로 정렬하려면 desc를 붙이면 된다.
그런데 order by에 숫자도 사용할 수 있다! 숫자를 사용하면 컬럼의 개수가 같거나 작으면 정상적으로 작동한다. 이러한 트릭을 사용하여 컬럼의 개수를 알아낸다.
select * from member order by 5
이와 같이 사용했을 땐 결과가 정상적으로 나오는데 만약 order by 6 으로 바꿨더니 나오지 않는다면 member 테이블의 컬럼 개수가 5개라 오류가 발생해서 결과가 나오지 않았구나! 하면 된다.
union sqli를 활용할 수 있는 대표적인 게시판에서 order by를 사용해보면 order by 5 일때는 검색 결과가 보이는데?? order by 6을 하니까 결과가 안보이면 컬럼의 개수가 5개구나 하고 정보를 얻을 수 있다.
근데 컬럼의 개수는 알았는데 뭘 해야 하지??
컬럼의 개수는 5개지만! 화면에 보이는 컬럼은 5개 전부가 아니기 때문에 이를 확인해 줄 필요가 있다.
select ~ from ~ union select '1','2','3','4','5' #
컬럼의 개수를 알았으니 위와 같이 5개를 union select 시켜주며 어디 부분에서 출력이 일어나는지 조사한다. 다만 위의 코드는 그 자리가 string이라고 판단하여 집어넣었으며 원래 자리가 int인지 string인지 확인을 한 후 타입에 맞게 작성해야 한다!
(확인하는 방법은 union null, null, ... 이런 식으로 null 부터 넣어보고 숫자, 문자를 사용해가며 확인한다)
1, 3, 5번 컬럼에서 출력이 나온다고 가정해보자. 이제 본격적으로 인젝션에 들어간다.
1. 데이터베이스 확인하기
mysql에서 데이터베이스를 확인하는 명령어는 select database() 이다.
empty' union select '1','2','3','4','5' #
->
empty' union select database(),'2','3','4','5' #
'select database()' 를 출력이 되는 1, 3, 5 자리 중 1번 자리에 넣은 예시이다.
이렇게 하면 데이터베이스의 이름을 확인할 수 있다. 현재 db의 이름은 'db_sqli' 로 가정하자.
다음으로는 테이블 이름을 알아내야 한다. 이번엔 페이로드 3 번째 자리에 입력을 해본다. 출력이 되는 자리라면 어디에 해도 상관은 없다.
2. 테이블명 확인
select table_name from information_schema.tables where table_schema = 'db_sqli'
->
empty' union select '1','2',table_name,'4','5' from information_schema.tables where table_schema= 'db_sqli'#
위의 query문이 테이블 명을 찾는 query다. where 절을 사용하지 않으면 db 시스템 테이블까지 전부 나와서 찾기가 굉장히 힘들어지므로 현재 db로 한정한다. table_schema는 db를 의미한다고 보면 된다. mysql은 schema와 db를 같은 의미로 본다. 공격 쿼리로 알아낸 테이블 이름을 'login1'이라 한다.
+ information_schema.tables : 데이터베이스에 있는 모든 테이블 정보를 가지고 있는 테이블이다.
db라고 볼 수 있는 table_schema와, 각 테이블의 이름을 가지고 있는 table_name 필드 그리고 테이블의 타입 등을 가지고 있는 것을 볼 수 있다.
3. 컬럼명 확인
select column_name from information_schema.columns where table_name = 'login1'
->
empty' union select select column_name,'2','3','4','5' from information_schema.columns
where table_name = 'login1'#
column의 이름을 알아내는 query다. login1 테이블의 컬럼 이름을 조회하는 방법이다. 페이로드에 집어넣어 출력하면 컬럼들의 이름이 나온다. 이제 나온 컬럼들로 원하는 정보를 확인할 수 있다.
empty' union select id,'2',pass,'4','5' from login1#
마지막으로는 원하는 페이로드를 작성하여 인젝션한다!
성공!!
뭔가 복잡해 보일 수는 있는데 나중에 해볼 Blind SQLi에 비하면....
