반응형

 


이번 9번 같은 문제는 상당히 이해하기가 어려울 수 있습니다.

개인적으로 의식의 흐름대로 정리한 결과이며 정리한 결과가 정답이 아닐 수 있습니다.

또한 다소 내용이 복잡할 수 있습니다.


 


1. 문제 설명


 

먼저 문제를 클릭해보자.

 

 

이렇게 숫자가 1,2,3이 나와있고 Password를 입력하는 폼이 있다.

 

1번을 눌러보자.

 

 

1번을 눌러보니 Apple이라는 문구를 볼 수 있다

 

역시 Password를 입력하는 폼이 있다.

 

그럼 2번을 눌러보자.

 

 

이번에는 Banana이다.

 

3번을 눌러보자

 

 

3번을 눌러보니 Secret이라는 문구와 함께

 

컬럼 id와 no이 있다고 알려준다.

 

또한 no 3's id is password이라는 문구가 주어진다.

 

해석이 맞는지는 모르겠지만 3번의 id는 패스워드라는 의미이다.

 

즉, 3번의 ID를 찾으면 그것이 Password이고 이게 이번 8번 문제에서의 원하는 값인것 같다.

 

일단 공격 할 수 있는 포인트는 다음과 같이 2곳이라고 생각했다.

  1. 숫자를 의미하는 no
  2. Password를 입력하는 곳

숫자를 의미하는 noURL에서 볼 수 있다.

  1. webhacking.kr/challenge/web-09/?no=1
  2. webhacking.kr/challenge/web-09/?no=2
  3. webhacking.kr/challenge/web-09/?no=3

nopassword싱글 쿼터(')를 입력해보았다.

 

no' 를 넣으면 다음과 같은 URL이 나온다.

  • webhacking.kr/challenge/web-09/?no=%27

Password에 ' 에 넣으면 다음과 같은 URL이 나온다.

  • webhacking.kr/challenge/web-09/index.php?pw=%27

 

하지만 pw싱글 쿼터를 넣으면 다시 redirect가 되지만 no싱글 쿼터를 입력하면 

 

다음과 같이 Access Denied이라는 문구를 볼 수 있다.

 

 

이로써 공격할 포인트는 password가 아니라 no이라는것을 알 수있다.

 

왜냐? 싱글 쿼터를 입력하였을때 Access Denied이 뜨는것은

 

SQLi를 방지하기 위해 필터링이 되었음을 보여주는것이다.

 

그렇다면 이 문제는 어떻게 풀어야할까?

 

먼저 no필터링이 안되는것을 살펴봐야한다.

 

또한 필터링에 걸리지 않은 SQL문을 가지고 no 3의 ID를 알아내면 이번 문제를 해결 할 수 있을것 같다.

 

하지만 어떤것이 필터링이 된건지 이전의 문제처럼 소스 코드를 볼 수있는곳이 없다.

 

따라서 하나하나 넣어보면서 반응을 봐야한다.

  • 필터링: ', or, ||, and, &&, 공백(%20,%0a), >, <, =, from, select

몇 번 시도하니 위에 처럼 정리가 되었는데 일단 SQLi 문제를 풀때 공백과 from, select가 필터링 되면

 

SQLi를 작성하기 거의 힘들다. 

 

따라서 이문제를 풀기위해서는 경험상 블라인드 SQLi를 이용해야한다.

 

따라서 바이패스 되는 문자는 공백이 당연히 안되니

 

공백을 대신할 수 있는 괄호가 필수적으로 필요할것이다.

 

no에 괄호를 넣어보자.

 

Access Denied는 뜨지 않는다.

 

즉, 괄호를 활용 할 수 있다.


또한 selectfrom을 넣을 수 없다.

 

따라서 ascii 함수와 substr를 활용해서 3에 대한 ID 값 한글자씩 뽑아야한다.

 

따라서 ascii 함수도 필터링 되어있는지 보자.

 

webhacking.kr/challenge/web-09/?no=ascii

 

  • 필터링: ', or, ||, and, 공백(%20,%0a), >, <, =, from, select, ascii

ascii 함수가 필터링 되어 있다면 substr로 뽑아야한다.

 

substr를 넣어보자.

 

Access Denied가 뜨지 않는다.

 

따라서 substr은 사용할 수 있다는 의미이다.

 

substr로 함께 쓰이는 함수는 항상 if문이다. (substr 설명 클릭)

 

if문은 if(조건, , 거짓)이다.

 

if문안에 조건문을 실행하였을때 참이면

 

if문의 두번째 인자인 , 거짓이면 if문의 세번째 인자인 거짓을 반환한다.

 

예를들어, if(1+1=2, 1,0)이면 조건문에 1+1=2이므로 참이되어 1이 반환되는 의미이다. 

 

그렇다면 if문을 활용해서 어떻게 블라인드 SQLi를 수행할까?

 

위에서 설명하였듯이 if문과 substr를 사용할 수 있다고 하였다.

 

if(substr(id,1,1)=id의 첫번째 hex값, 1,2)

 

이렇게 작성해보았다.

 

위에 페이로드를 조금 풀어서 설명하면 substr 함수로 id의 첫번째 글자 1개를 가져온다는 의미이다.

 

그게 id 첫번째의 hex값이 만약 a(0x61)이면 1이 반환이 되고 a가 아니면 2를 의미한다.

 

즉 1이 반환되면 Apple이라는 문구를 볼 수 있을것이고 2가 반환되면 Banana를 볼 수 있다.

 

넣어보자.

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)=0x61,1,2)

 

Access Denied가 뜬다.

 

그렇다면 여기서 =이 필터링 되어있다는것도 알 수 있다.

 

따라서 =를 대신할 수있는 likein을 넣어보자.

 

like를 먼저 넣어보자.

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)like(0x61),1,2

 

 

????

 

Apple이 보인다.

 

그럼 id의 첫번째 값이 a라는 소리인데 혹시 잘못 생각할 수도있으니 

 

webhacking.kr/challenge/web-09/?no=if(substr(id,1,1)like(0x62),1,2)

 

a가 아닌 b를 넣어보자.

 

Apple이라는 문구가 없다.

 

따라서 블라인드 SQLi가 먹혔다.

 

이걸 가지고 이제 문제를 풀어보자!


2. 문제 풀이


위의 문제 설명에서 substr, if, like 또는 in을 사용 할 수 있는것을 확인하였다.

 

substr로 문자를 뽑아 내기 위해서는 3번의 id의 전체 길이를 알아야한다.

 

그래서 코드를 작성해보았다.

webhacking.kr/challenge/web-09/?no=if(length(id)like(5),1,2)일 때는

 

위의 실행 결과 처럼 ID의 길이가 5자리라고 나온다.

 

ID의 자리가 5자리면 'Apple'이다.

 

우리가 원하는 ID값은 no의 값이 3번일때이다.

 

그렇다면 webhacking.kr/challenge/web-09/?no=if(length(id)like(6),2,1) 일때는

 

길이가 6일때는 Banana이니깐 참이다.

 

따라서 if문에서는 참인 결과인 2가 반환되고 no에는 2가 들어가기때문에 Banana 문구가 나와야한다.

 

Apple이 나온다.

 

이게 뭘까?

 

왜 Apple이 나올까???

 

???????????????????????

 

위의 쿼리를 다시 보면 id의 길이가 6이면(banana)이면 참이니깐 2를 반환해서 Banana가 나와야한다.

 

테이블의 정보가 아래 처럼 있다고 가정하자.

 

 

그리고 밑에 쿼리문이 있다고 가정하자.

위의 쿼리문을 다시 해석하자면 id의 길이가 5이면 참인 1이 리턴될것이고

 

id의 길이가 5가 아니면 2리턴될것이다.

 

하지만 여기서 length(id),id의 길이 물었으나 어떤 no의 대한 id인지

 

정보가 없으니 no이 1에서 3까지 전부다 위의 쿼리를 실행하면서

 

조사할것이다라고 생각을 하였다.

 

그 후 다음과 같이 총 3개의 쿼리를 날려 보았다.

 

 

첫번째만 Apple이라는 문구가 나오고 나머지 2개의 쿼리에서는 어떠한 문구를 볼 수 없었다.

 

많은 생각에 걸쳐 다음과 같이 결론을 내렸다.

 

  1. 총 3개의 id에 대해서 조사를 진행한다.
  2. 그 중 첫번째 no이 1일때를 조사한다.
  3. no이 1일때의 id는 apple이다.
  4. 길이가 5자리이다.
  5. 따라서 if문의 최종적인 리턴값은 1이다.
  6. if문의 최종적인 리턴값인 1과 현재 no이 1일때 조사하고있으니 1=1이다.
  7. 1=1이니 최종적으로 true가 Apple이 반환된다.

다시 한번 예를들어 보자.

 

id의 길이가 6일때를 보자.

 

결과부터 보자면 다음과 같은 결과가 나온다.

 

 

아까 위에서 no이 1번에서 3번까지 있기 때문에 총 3번을 조사한다고 하였다.

 

no이 1일때를 살펴보자.

  1. no이 1일때를 보니 id값이 Apple로 총 id의 길이가 5이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 거짓이 되므로 if문의 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1, 또한 현재 테이블에서 조사하고 있는 no이 1이다.
  4. if문의 최종적인 리턴값 1과 현재 조사하고있는 no의 1이 같으니 1=1이다.
  5. 따라서 1=1이기 때문에 결과적으로 True니 Apple이 나온다.

no이 2일때를 살펴보자.

  1. no이 2일때를 보니 id값이 Banana로 총 id의 길이가 6이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 참이 되므로 if문의 참인 2가 리턴된다.
  3. if문의 최종적인 리턴값은 2, 따라서 no=2로 들어가기 때문에 no이 2인 Banana가 리턴된다.
  4. if문의 최종적인 리턴값 2과 Banana의 no이 2로 같다.

따라서 2=2이기 때문에 결과적으로 True니 Bananna이 나온다.

no이 3일때를 살펴보자.

  1. no이 3일때를 보니 id값이 ????로 총 id의 길이가 ????이다.
  2. 따라서 id의 길이가 6인지를 판단했을때 이는 거짓이 되므로 if문의 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1, 따라서 no=1로 들어가기 때문에 no이 1인 Apple이 리턴된다.
  4. if문의 최종적인 리턴값 1과 3번의 id값인 ?????이 같지 않다.
  5. 따라서 1=3이 아니기 때문에 결과적으로 false이니 무시된다.

 

이렇게 총 3번을 돌면서 판단하는데 제일 먼저 값이 나온 no이 1일때의 Apple이 출력되고

 

뒤의 Banana도 쿼리상 출력은 되었지만 첫번째 Apple만 출력되는것이다.

 

그렇다면 no이 3일때의 id의 길이를 구해보자.

 

앞에서 한 방식 그대로 적용하기 위해 아래처럼 쿼리를 작성하였다.

 

이렇게 no이 3일때의 길이가 1에서부터 증가해서 만약 20까지 범위를 주었다면

 

20까지 증가하면서 no이 3일때의 길이가 맞을때 Secret이라는 문구를 볼 수 있다면

 

그것이 no 3의 길이라고 생각하고 코드를 작성하였다.

 

코드를 작성하고 결과 값을 보니깐 다음과 같았다.

 

 

위의 결과를 보면 id의 길이가 5일때 빼고는 나머지가 다 Apple이 나왔다.

 

왜 이렇게 나온지 살펴보자.

 

길이가 1일때 - if(length(id)like(1),3,1)

  1. 길이가 1인것은 테이블에 존재하지 않는다.
  2. 존재하지 않기 때문에 if문에서는 거짓인 1이 리턴된다.
  3. if문의 최종적인 리턴값은 1
  4. if문의 최종적인 리턴값 1과 no이 1인 경우는 Apple이다.
  5. Apple의 no은 1이다.
  6. if문의 최종적인 리턴값과 5번의 no을 비교했을때 같다.
  7. 따라서 Apple이 나온다.

길이가 5일때 - if(length(id)like(5),3,1)

  1. 길이가 5인것을 no이 1~3까지 순차적으로 조사한다.
  2. no이 1일때의 id의 길이를 보니 Apple로 총 5자리이다.
  3. if문에서 length(5)를 물었기 때문에 참이므로 if문의 참인 3을 반환한다.
  4. if문의 최종적인 리턴값은 3이다.
  5. if문의 최종적인 리턴값은 3이지만 현재 검색하는 no은 1일때이다.
  6. 리턴값과 현재 조사한 no이 다르기 때문에 값이 나오지 않는다.

길이가 6일때 - if(length(id)like(6),3,1)

  1. 길이가 6인것을 no이 1~3까지 순차적으로 조사한다.
  2. no이 1일때의 id의 길이를 보니 Apple로 총 5자리이다.
  3. if문에서 length(6)를 물었기 때문에 거짓이므로 if문의 거짓인 1을 반환한다.
  4. if문의 최종적인 리턴값은 1이다.
  5. if문의 최종적인 리턴값은 1이고 현재 검색하는 no은 1이다.
  6. 리턴값과 현재 조사한 no이 같기 때문에 값이 Apple이 나온다.
  7. no이 1일때가 끝나고 no이 2로 넘어간다.
  8. no이 2일때 id의 길이를 보니 Banana로 총 6자리이다.
  9. if문에서 length(6)를 물었기 때문에 참이므로 if문의 참인 3을 반환한다.
  10. if문의 최종적인 리턴값은 3이다.
  11. if문의 최종적인 리턴값은 3이고 현재 검색하는 no은 2이다.
  12. 리턴값과 현재 조사한 no이 다르기 때문에 값이 나오지 않는다.

여기까지 보고 느낀점은

 

no이 3일때의 id의 길이와 값을 구하기 위해서는 if문의 참일때에는 3이 들어가야하고

 

거짓을때는 1,2가 아닌 다른 숫자를 써야한다는것이다.

 

왜냐하면 이 이유는 위에 과정을 보면 알 수 있다.

 

따라서 0과 또는 4이상의 숫자를 아무 숫자나 입력하면 된다.

 

4를 넣어서 다시 코드를 돌려보자.

 

 

id의 길이가 11일때 no이 3일때 볼 수있었던 Secret이 나온다.

 

이는 위의 과정처럼 혼자서 생각해보자!

 

끝으로 이제 문제를 해결 할 수 있을것 같다.

 

substr을 활용해서 no이 3일때 id의 길이가 11자인 값을 뽑아내보자.

 

나온 결과를 Password 입력에 입력해보자.

 

하지만 안된다.

 

조금만 다시 생각해보니 MYSQL은 소문자 대문자를 구별하지 않는다.

 

따라서 다시 소문자로 바꿔서 입력해보니 Clear 되었다.

 

 

참 설명이 지저분하고 어렵다.

 

하지만 이렇게 하나 하나 풀어서 설명하는 곳이 잘 없어서 혼자 이해하는데 힘들었다.

 

이렇게 이해한것도 틀린거일 수도 있다.

 

지금까지 9문제를 풀었는데 가장 이해하기 어려웠던 문제인것 같다.

반응형

'웹 해킹 > Webhacking.kr' 카테고리의 다른 글

11. old-11  (0) 2021.11.09
10. old-10  (0) 2021.11.08
8. old-08  (0) 2021.11.05
7. old-07  (0) 2021.11.04
6. old-06  (0) 2021.11.03

+ Recent posts