저번 주차에서 SQL injection이 무엇인지? 그리고 SQL Injection을 통해 인증을 우회하는 방법을 공부해보았습니다.
이번 주차에서는 SQL Injection을 사용하여 데이터베이스의 데이터를 추출하는 방법을 알아보겠습니다.
웹 서버에서 데이터베이스를 사용하기 위해 SQL 쿼리를 사용하여 데이터베이스에게 명령을 내리는 과정이 있을 것입니다.
전 시간에 했던 로그인도 우리에게 아이디와 비밀번호를 입력받아 SQL 쿼리문을 만들어 해당 아이디와 비밀번호인 데이터가 있는지 데이터베이스에 확인해서 있으면 달라고 하는 명령을 내리는 것입니다.
즉, SQL Injection은 이때 우리의 입력을 받기 때문에 우리가 원하는 데이터베이스에서 수행해줬으면 하는 명령을 하게하기위해 쿼리문을 조작하는 것입니다.
SQL Inejction을 사용하면 저번 시간에 해봤던 것처럼 인증 우회가 가능한 것은 알았습니다. 뿐만아니라 데이터베이스를 사용하는 것이기 때문에 데이터 추출도 가능합니다.
데이터를 추출할 때 발생할 수 있는 상황이 두 가지가 있습니다.
1. 데이터를 가져온 것을 화면에 출력하는 상황 : 게시판
- 게시판 같은 경우 아마도? 데이터베이스에 저장된 게시판 테이블에서 게시판제목, 작성자, 게시판내용 ..등 데이터를 가져와서 우리에게 보여줘야 할 것입니다. 이렇게 데이터를 가져온 것 우리에게 직접적으로 보여주는 상황
2. 데이터를 가져온 것을 화면에 출력하지 않고 처리하는 상황 : 로그인
- 로그인 같은 경우는 데이터베이스에 저장된 아이디와 비밀번호를 가져와 맞는 지 확인하고 로그인 성공여부를 판단합니다. 즉, 우리에게 아이디나 비밀번호를 보여주지않고 서버에서 알아서 처리하는 상황
이 두가지 상황에서 이번 시간에는 SQL 쿼리 결과가 화면에 출력되는 상황에 대한 데이터 추출 방법을 보겠습니다.
만약, 게시판에서 SQL Injection이 가능한 경우
select * from board where idx='[게시판 번호]';
아마도 위의 코드처럼 board 테이블에서 원하는 게시판 번호인 게시판 데이터를 가져와서 우리에게 보여줄 것입니다.
하지만, 우리가 무슨 입력을 넣어도 서버에 이미 저장되어있는 쿼리는 조작을 못하기 때문에 board안에서만 데이터를 가져올 수 밖에 없습니다.
이때 사용할 수 있는 SQL 문 중에서 union 명령이 있습니다. union은 저번 과제에서 해봐서 간단하게 아시겠지만 다시 설명하면
union을 사용하면 select를 두 번 사용하게 해주며 두 select 결과를 합쳐줍니다.
즉, select를 한번더 사용할 수 있기때문에 이미 정해져있는 board테이블에서 select하고 다른 테이블에서 select한 결과를 합칠 수 있다는 뜻입니다.
select * from score union select name, id, password from users;
위와 같이 union select 를 사용한 경우 다음과 같이 나옵니다.
이렇게 union을 사용하면 서버에서 사용하는 테이블을 벗어나 다른 테이블의 데이터도 가져올 수 있을 것입니다.
union 시 주의해야 할 것이있습니다.
결과에서 알 수 있듯 union 뒤에 select한 결과를 아래에 이어서 붙이며 컬럼명은 앞의 select한 컬럼명을 따릅니다.
한 테이블인 것처럼 합쳐서 결과를 가져옵니다. 그렇기 때문에 select하는 두 컬럼의 개수를 맞추어 주어야합니다.
컬럼 수를 다르게 score는 *로 모든 컬럼(3개) users는 name, id, password, email(4개)로 맞춰주면 에러가 납니다.
이것을 사용하여 서버에서 사용하고 있는 쿼리의 SELECT가 몇 개의 컬럼을 가져오는지 알 수 있습니다.
서버에서 사용하는 select가 몇 개의 컬럼을 가져오는지 아는 방법이 위에 것 말고도 또 있습니다.
그것은 바로 order by를 사용하는 것입니다.
order by는 원하는 컬럼을 오름차순(ASC) 또는 내림차순(DESC) 으로 정렬할 수 있습니다.
order by는 (select 문 ~ order by [컬럼명] [asc, desc])로 사용할 수 있습니다.
이름 순으로 정렬한 결과가 확인 가능합니다.
하지만 이때 컬럼명을 사용하여 정렬하는 것 뿐만아니라 select하는 컬럼명의 인덱스로도 정렬할 수 있습니다.
위의 사진처럼 order by 2를 적으면 select하는 2번째 컬럼에 대해 정렬한다는 뜻이 됩니다.
인덱스인데 0부터 시작이 아니라 1부터 시작입니다!! 헷갈릴 수도...
여기까지 order by 가 무엇이고 어떻게 사용하는 지 알아보았습니다.
근데 어떻게 이것을 사용하여 컬럼의 개수를 알 수 있을까요?
바로 인덱스를 사용하여 정렬하는 것을 사용하는 데 정렬을 원하는 인덱스가 select하는 컬럼수를 초과하여 실행하면 오류가 납니다.
그렇기 때문에 이것을 사용하면 서버의 select하는 컬럼의 수를 알 수 있을 것입니다.
이제 SQL Injection으로 데이터를 추출하기위한 기본적인 SQL 문법을 배웠습니다.
실제로 SQL Injection을 통해 어떻게 데이터를 추출하는지 알아보겠습니다.
기본적으로 UNION SQL Injection을 사용하기위해서 절차를 따르면 쉽게 데이터를 추출할 수 있습니다.
무조건 따르는게 좋음!!
1. SQL Injection 포인트 찾기
2. 컬럼 개수 찾기
3. 출력되는 컬럼 위치 찾기
4. DB 이름 확인하기
5. Table 이름 확인하기
6. 컬럼 이름 확인하기
7. 데이터 추출하기
위와 같이 순서대로 해나가다 보면 데이터를 쉽게 가져올 수 있을 것입니다.
하나씩 실제 실습해보며 알아보겠습니다.
1. SQL Injection 포인트 찾기
당연히 DB를 사용하지도 않는 곳에 우리가 아무리 입력해도 SQL Injection이 성공할리가 없겠죠!!!
DB를 사용하는 곳이라도 해당 부분이 SQL Injection이 가능한지도 알아봐야합니다.
만약, SQL Injection이 가능하다!!! 그러면 어떻게 서버가 입력을 처리하는지! 어떤 쿼리문이 적혀있는지 추측을 해봐야합니다.
게임을 검색하몀 해당 게임의 데이터가 출력되는 사이트가 있습니다. overwatch 를 검색하니 overwatch에 대한 정보가 나옵니다.
오잉?? o 만 입력했더니 게임 name에 o가 들어간 게임의 정보가 모두 나오는 것 같습니다. 그러면 우리는 like를 통해 패턴 검색을 한다고 추측해볼 수 있습니다.
$word = 입력한 단어
select name, score, production from games where name like '%$word%'
아마 이렇게 되어있지 않을까?? 쿼리문을 만들어 보았습니다. 물론 아닐 수도 있습니다...
서버가 어떻게 데이터를 처리하는 지는 확인했습니다. SQL Injection이 가능할까요?
overwatch%' and '1%'='1를 입력해주었습니다. 근데 우리가 인증 우회할 때는 and '1'='1 였는데 '1%'='1 를 입력해주었습니다. 왜 이렇게 입력했는지 아실까요?
바로 이미 서버에 설정되어있는 쿼리문을 맞춰주기 위해서 입니다.
// overwatch%' and '1'='1 입력 시
select name, score, production from games where name like '%overwatch%' and '1'='1%'
// overwatch%' and '1%'='1 입력 시
select name, score, production from games where name like '%overwatch%' and '1%'='1%'
위에 코드 처럼 %'로 끝나기 때문에 문자를 똑같이 맞춰주기 위해 '1%'='1 로 적어주었습니다.
저렇게 하면 위에 것은 and 뒤가 거짓으로 name에대해 맞는 데이터가 없어 아무것도 출력하지 않게 됩니다.
그래서 결론!! overwatch%' and '1%'='1 를 입력했더니 우리가 생각한데로 overwatch의 정보가 나오네요 SQL Injection은 가능합니다.
2. 컬럼 개수 찾기
우리는 SQL Injection 포인트를 찾고 어떻게 서버가 데이터를 처리하는지 알아봤습니다.
또한, 출력되는 결과에서 name, score, production 이렇게 3개가 출력되기 때문에 우리는 3개일 것이다 추측만 했습니다.
하지만, 출력이 3개가 되는 것이지 select할 때 몇 개의 컬럼을 가져오는지는 모릅니다.
실제로 10개의 컬럼을 select하고 3개의 컬럼만 사용할 수도 있기때문입니다. 이것은 개발자 마음...
그렇기 때문에 우리는 union을 사용하기 위해 컬럼 개수를 알아내야 합니다. 우리는 쉽게 컬럼 개수를 알아내는 방법을 알죠?!
order by를 사용합니다. 만약 우리가 사진처럼 입력하면 아마 이렇게 작동하겠죠?
select * from games where name like '%overwatch%' order by n #%'
// n은 증가시키며 확인
이제 order by 부분 뒤에 숫자 부분만 바꿔가며 컬럼 개수를 확인합니다. 일단 4를 입력하니 성공하는 것을 보니 4개 이상입니다.
오!! 5를 입력하니 500 에러 서버측 에러가 뜹니다. 5를 입력했더니 컬럼 개수를 초과하여 인덱스하여 에러가 났습니다.
그럼 컬럼 개수는 4개인 것을 우리는 알 수 있습니다.
3. 출력되는 컬럼 위치 찾기
서버에서 select하는 컬럼은 4개인데 3개만 출력됐습니다. 그렇기 때문에 우리는 몇번째 컬럼이 화면에 출력되는 것인지 알아야 우리가 원하는 데이터를 출력할 수 있겠죠!
출력되는 컬럼위치 찾는 방법은 간단합니다.
// overwatch%' union select 1,2,3,4 #
select * from games where name like '%overwatch%' union select 1,2,3,4 #
위 코드처럼 입력해보는 것입니다. 컬럼의 개수만큼 union select해주면 각 숫자가 출력되는 것을 보고 해당 컬럼이 몇번째 컬럼인지 알 수있습니다. 아마 결과를 보면 바로 이해할 수 있을 겁니다.
1,2,3,4가 overwatch 컬럼 아래 위치하며 각 컬럼이 몇번째 인지 알 수 있습니다.
즉, 서버에서 가져온 1번째 컬럼은 사용을 안하고, 2번째 컬럼은 name, 3번째 컬럼은 score, 4번째 컬럼은 production이 됩니다.
이렇게 출력되는 컬럼의 위치까지 구했습니다.
그럼 union select할 준비는 마쳤고 사용할 수 있습니다.
4. DB이름 확인
union select로 다른 테이블의 데이터를 가져오려고 하는데 우리는 다른 테이블의 이름을 모르고 있기 때문에 접근할 수가 없습니다.
테이블의 이름을 알기 전 DB의 이름 먼저 알아내야 그 안에 있는 테이블의 이름을 알 수 있습니다.
현재 우리가 사용하고 있는 DB의 이름을 아는 방법은 SELECT database();를 사용하면 됩니다.
이것을 서버측에서 고정되어있는 쿼리문에 SQL Injection을 통해 사용해야 합니다. 어떻게 할까요?
select * from games where name like '%overwatch%' union select 1,2,3,4 #%'
우리는 overwatch%' union select 1,2,3,4 #를 사용하면 union select를 사용할 수 있다까지 알고 있습니다.
출력되는 2,3,4 중 아무데나 database()를 사용하면 됩니다. 그렇게 하면 select database()로 가져온 DB이름을 출력하게 됩니다.
우린 이렇게 DB이름도 알아냈습니다.
계속해서 table이름도 알아보겠습니다.
5. Table 이름 확인
테이블 이름을 알아보기 전에 알아야할 것이 있습니다. 각 DBMS별로 데이터베이스 정보를 저장하는 테이블이 있습니다.
이 데이터베이스에는 데이터베이스 이름, 테이블 이름, 컬럼 명 등이 모두 저장되어있습니다.
MySQL에서 공부 중이니 MySQL에서 우리가 필요한 정보를 저장해놓은 만능 데이터베이스 이름은 information_schema 데이터베이스 입니다.
그 중 각 데이터베이스 별로 테이블을 저장해놓은 information_schema의 테이블은 tables라는 테이블입니다.
??똑같은 말이 반복되서 헷갈릴 수 있습니다.
information_schema라는 테이블에 이름이 tables라는 테이블에 데이터베이스별로 저장되어있는 테이블의 이름이 보관되어있습니다.
information_schema.tables 를 사용하면 해당 테이블에 접근할 수 있습니다. '.'으로 구분하며 앞은 데이터베이스 이름, 뒷 부분은 테이블 이름입니다.
예를 들어 저의 score 테이블은 test 데이터 베이스에 있습니다. 그럼 test.score로 접근할 수 있습니다.
아! information_schema.tables를 보여주면 이해하기 편할 것 같네요!
이렇게 tables 테이블에 talbe_schema에는 데이터베이스 이름, table_name에 테이블 이름을 확인할 수 있습니다. 뿐만 아니라 다른 정보도 저장되어있는 것을 볼 수 있습니다.
이제 데이터베이스 이름, 테이블 이름이 어딘가에 저장되어있다는 것은 이해했을 것입니다.
그럼 어떻게 테이블 이름을 알아볼 수 있을까요? 바로~~
select table_name from information_schema.tables where table_schema='segfault_sql' #
이렇게 테이블에서 우리가 원하는 데이터베이스 안의 테이블을 모두 가져올 수 있습니다.
여기서 끝이 아니죠...
union select를 사용해서 우리는 찾아야합니다.
select * from games where name like '%overwatch%' union select 1,table_name,3,4
from information_schema.tables where table_schema='segfault_sql' #%'
// 입력: overwatch%' union select 1,table_name,3,4 from information_schema.tables where table_schema='segfault_sql' #
이렇게 하면 segfault_sql에 있는 테이블 이름을 출력할 수 있습니다.
이때 where table_schema 조건을 사용하여 해당 데이터베이스의 테이블 이름을 찾아야 합니다. 아니면 찾은 테이블이름이 어느 데이터베이스에 속해있는지 알 수 없습니다.
아~~ 우리가 이때까지 games라 생각했던 테이블이름은 아마 game인 것 같습니다. 그리고 member??테이블이 있네요 아마 여기에는 아이디와 비밀번호 정보가 저장되어있을 것 같습니다.
이제 member테이블의 컬럼 이름을 알아내어 원하는 정보를 가져와봅시다.
6. 컬럼 이름 확인
컬럼 이름 확인하는 것도 table이름 확인하는 것과 같습니다.
이번에는 information_schema의 tables 테이블이 아닌 columns 테이블을 확인해야 합니다.
select * from information_schema.columns를 해보니 사진처럼 데이터베이스 이름, 해당 데이터베이스의 테이블 이름, 테이블의 컬럼명을 확인할 수 있습니다. 물론 다른 정보도 출력되고있습니다.
이것도 union select로 결합하여 사용해야합니다.
select * from game where name like '%overwatch%' union select 1,column_name,3,4
from information_schema.columns where table_name='member' #%'
// 입력: overwatch%' union select 1,column_name,3,4 from information_schema.columns where table_name='member' #
이때도 where table_name 조건을 주어 원하는 테이블의 컬럼명을 검색하여 혼돈이 없도록 합니다.
이렇게 member 테이블의 컬럼명까지 가져오기에 성공했습니다.
드디어 이제 어떤 데이터베이스를 사용하고, 어떤 테이블을 사용하며, 어떤 컬럼명이 있는지 확인했기 때문에 마지막 데이터만 가져올일만 남았습니다.
7. 데이터 추출
솔직히 데이터 추출 부분은 앞의 내용을 이해했다면 금방할 수 있을 것입니다.
우리가 가져올 데이터는 아이디와 패스워드라고 했을 때 어떻게 입력하면 될까요?
select * from game where name like '%overwatch%' union select 1,id,pass,4
from member #%'
// 입력: overwatch%' union select 1,id,pass,4 from member #
짜잔~~ 이렇게 원하는 데이터를 가져왔습니다.
이렇게 절차를 밟아 한단계씩 해나간다면 쉽게 해결할 수 있습니다.
여기까지 데이터를 화면에 출력할 때 사용할 수 있는 데이터 추출 방법을 알아보았습니다.
다음은 아마? 데이터를 화면에 출력하지 않을 때 사용할 수 있는 방법을 할 것 같습니다.
정리를 하다보니 엄청 길어져서 제가 무슨 말을 했는지... 요약은 잘 했는지... 잘 모르겠습니다.
암튼 이번주차 정리는 마치고 과제를 하며 마무리해보겠습니다.
과제
1. member 테이블에서 doldol 계정만 출력시키기 (한 계정의 데이터만 출력)
2. CTF 문제 풀기
+ 웹 개발 이어하기
'웹 해킹' 카테고리의 다른 글
웹 해킹 공부 일기장 2 - 과제 (챌린지1) (2) | 2023.12.01 |
---|---|
웹 해킹 공부 일기장 2 - 과제 (0) | 2023.12.01 |
웹 해킹 공부 일기장 1 - 과제 (챌린지) (0) | 2023.11.27 |
웹 해킹 공부 일기장 1 - 과제 (0) | 2023.11.26 |
웹 해킹 공부 일기장 1 - 2 (인증 우회) (4) | 2023.11.24 |