Webhacking.kr Challenge 9



Challenge 9 에 접속하면 다음과 같은 화면을 보게된다.

시작하자마자 Web-Server에서 Authentication을 요구하는데...

내가 어떻게 알아


종종 위의 인증창에서 SQL-Injection을 시도하는거라고 생각하는 입문자분들이 계신데,

여기서 하는거 아닙니다!

고생하지마세요!


처음엔 게싱으로 뭐 ..admin/admin 도 쳐보고 test/test도 쳐보고 에라이 아니네,

취소를 누르면 다음과 같은 화면을 보게됩니다.





인증에 실패했기때문에, 401 Unauthorized 에러를 만나게 되는데

당연한 결과다.


이 부분에서 어떻게 하면 저 인증을 우회할 수 있을까

하고 열심히 검색을 해볼거다.

웹 인증 우회, 웹 로그인 우회 등등

자세한 문서를 찾아보고 싶으면 'bypass apache authentication' 이런식으로 검색하면 많이 찾아볼 수 있다.


이 부분을 인증 우회하는 부분과, 우회가 되는 이유를 설명하자면 이렇다.

apache 에서는 mod_auth 와 mod_access 모듈을 통해 위와같은 인증을 설정 할 수 있다.



<Directory "/var/www/html">

      ServerName ...

      ....

      AllowOverride AuthConfig

      ....

</Directory>

이렇게 AllowOverride 지시어를 AuthConfig 로 설정함으로써 인증을 요구할 수 있고,

이에 대한 계정과 세부설정은 아파치에 포함된

htpasswd

도구를 통해서 설정할 수 있다.


그리고 설정된 인증 정보들을 가지고 누굴,어느상황에서 인증을 요구하고 통과시켜줄지

어떤 메세지를 보여줄지 등을 설정할 수 있는데,

이는 httpd.conf 같은 서버 설정파일이나, .htaccess 파일에 다음과 같은 지시어를 넣어 설정한다.




AuthType Basic
 AuthName "Login plz ^-^"
 AuthUserFile /usr/local/apache/passwd/passwords

 <Limit GET POST>

         Require valid-user

 </Limit>



자료를 보호하기 위해 인증을 거는것 좋다. 인증 자체가 취약하고 우회당하는것이 아니라,

위처럼 설정시의 실수, 오류로 인증이 우회 된다.


위의 설정 파일을 해석하면, 인증을 거는데 Limit 지시자를 통해 GET 과 POST 요청에 대해서만 인증을 요구한다.

Limit을 아예 걸지 않으면 모를까,

요즘은 이런 실수가 거의 없지만, 예전에는 GET과 POST에 대해서 인증을 거는 설정이 기본적이였다.

아마 검색등을 통해 들어올때나 브라우저를 이용하면 GET과 POST 메소드를 사용하기때문인지...


아무튼 우리가 해결하려는 WebHacking.kr 9번 문제는 위와같은 Limit가 걸려있는 문제일 것이다.

위와 같이 설정이 되어있다면, GET 과 POST메소드를 제외한 다른 메소드를 이용하면 정상적인 요청/응답이 '인증없이' 가능하다는 것.


한번 테스트 해보자.

HTTP Request에서 사용할 수 있는 Method는 GET이나 POST를 제외하고도,

OPTIONS, HEAD, PUT 등 여러가지가 더 있다.

body에 대해서도 reponse 를 받아야하니, OPTIONS 와 PUT으로 요청해보자.






사진처럼, OPTIONS와 PUT으로 요청했을때 인증없이 '200 OK'응답을 받았고, 응답에도 HTML 코드를 잘 받게된다.

즉 해당 페이지 내부로 접근하는데, 인증을 하지 않고 접근한것.

순전히 서버관리자의 실수로 인증이 필요한 페이지의 인증이 우회된다.


페이지 내부내용을 접근했는데, 이제 시작이지..

인증시에 메세지를 보았으면... 여긴 SQL Injeciton World.

이제 우리가 원하는 Key를 획득하기 위해선.. SQL Injection을 해야함. 흑


일단 페이지를 탐색해봅시다.



이런 간단한 화면만 존재할 뿐...

참 우리는 인증을 우회해서 들어왔기 때문에, 버튼을 누를때 다른 요청을 보낼때도

GET, POST가 아닌 OPTIONS나 PUT메소드를 이용해서 보내줘야한다.

파로스 등 프록시 툴을 사용하거나, 순전히 HTTP Request를 직접 보내 작업해야한다.





1,2,3 번을 들어가보면

1,2번엔 Apple 과 Banana

3번엔 Secret 이라며 Hint를 준다.

hint는 length가 11, column은 id와 no 라고 한다.

SQL Injection을 해야하는데 좋은 정보..인듯


아무튼 파라미터를 변조하여 SQL Injection을 시작해봅시다.

이 문제에서 어려웠던 점은

필터링이 정말 많은 부분이 필터링 되어 있어서

지금까지 해왔던 방식으로 SQL Injection을 시도하면 전부 Access Denied 를 만나게 된다.


먼저 Blind SQL Injection을 진행하려면 쿼리 결과에 따른 참과 거짓값을 구분할 부분이 있어야하고,

해당 문제에서 필터링 하고 있는 문자는 사용할 수 없다.


먼저 어떤 결과를 통해 참과 거짓을 구분할 지 생각해보면,

우리는 보통 참과 거짓값을 구분할때 뭔가, Success 또는 Fail 이란 문구나 0, 1이란 숫자를 보고 구분해왔다.


이 페이지는 위와 같은 결과를 확인하기는 어렵지만,

어떠한 바뀌는 값에 따라 결과가 달라지는 것이 있는데,

내가 이용할 부분은 no의 파라미터가 1, 2, 3에 따라 페이지 결과가 바뀌는 부분을 이용할 것이다.


즉 내가 원하는 쿼리문이 참일 경우 결과가 3이 되게하고, 거짓일 경우 0이나 1, 2.

순서는 상관없겠지만, 그런 방법으로 참거짓에 따라 보이는 결과물을 구분할 수 있으면

우리가 원하는 Blind SQL Injection 을 할 수 있다.



그럼 두번째로 우리가 원하는 쿼리문을 먼저 작성하고, 그에 따라 결과가 달라지게 해야한다.

그런데 쿼리문을 질의해보면 공백,=,select 등 우리가 원하는 결과를 내는데 큰 어려움이 있다.

필터링이..아주그냥... 내가 주로 사용하던 공백필터우회하는 0x0a 역시 필터링 된다....


그럼 일단 공백을 사용할 수 없고, select 나 = 를 사용할 수 없으니.

원하는 결과를 어떻게 낼지 생각해봐야한다.


MySQL 에는 많은 내장함수가 존재한다.

'a가 1일 때 를 만족하는 조건이면 b를 출력, 만족하지 못하면 c를 출력'

간단히 생각해보면 우리가 아주 흔히 볼 수 있는 로직이다.

'if(a=1) print(b) else (c)'

간단한 조건문의 로직이다.


MySQL 에도 내장함수로 IF 함수가 존재한다.


 mysql> SELECT IF(1>2,2,3);

            -> 3


위와 같은 함수이다.


첫번째 파라미터에 조건,

두번째 파라미터에 조건이 참일 경우 출력할 값

세번째 파라미터에 조건이 거짓일 경우 출력할 값

을 적어주면 조건에 따른 결과 값이 출력된다.


이 문제에서는 IF 함수는 필터링 되지 않고, 함수를 계속 이용하면 공백없이도 쿼리문 작성이 가능하다.


 SELECT ? FROM ? WHERE no=?


위와 같은 쿼리문을 가지고 있는 페이지 일 것이고,

no 에 우리가 입력하는 쿼리문이 작성될 것.


그리고 값을 비교할 수 있는 ' =, >, <' 가 막혀있으니 연산자로 비교할 순 없으나,

비교할 수 있는 다른 지시자들이 분명히 있다.

예를들어 'in' 이다. a가 bcdef 중 같은 값이 존재하는지 등을 비교할 때 사용하는 지시자이다.

이 문제에선 in 역시 필터링 되지 않는다.

그리고 다행히 Blind SQL Injection에 필요한 'substr' 또한 필터링 되지 않고있다.

다음과 같은 쿼리문을 작성할 수 있겠다.



SELECT ? FROM ? WHERE no=IF((substr(id,1,1)in('a')),3,1)


그런데 이 쿼리의 문제는 또 in 안에 들어가는 비교대상자들이다.

문자, 문자열, ascii, char, ascii 등등이 필터링 된다.

id의 1번째 자리가 a 라고 가정하면 in 안에는 'a', char(97) 등을 넣어주어야

조건을 만족시킬 수 있는데,

문자를 비교할 수 있는 다른 방법을 찾아야한다.


여러 방법 중 내가 사용한 방법은 hex값을 이용하는 것이다.

MySQL 에서 ASCII 코드의 헥스값 과 문자는 비교가 가능한 대상이다.

예를들어 다음과 같다.


SELECT ? FROM ? WHERE 'a'=0x61

위 쿼리의 WHERE 조건은 참이다.

헥스값 0x61 은 ASCII코드상으로 97, 'a'에 해당한다.

그리고 MySQL 에선 그 헥스값을 문자로 인식한다.


MySQL> SELECT 0x61;

             -> a


즉 우리는 쿼리의 IN 안에 비교할 문자에 해당하는 아스키코드의 헥스값을 넣으면 비교가 가능하다.

그리고 헥스값은 이 문제에서 필터링 하고 있지 않다.



  SELECT ? FROM ? WHERE no=IF((substr(id,1,1)in(0x61)),3,0)

이렇게 했을때,

id의 첫번째 자리가 a일 경우 no=3 이 되어

내용에 Secret 과 Hint를 보여줄 것이고,

거짓일 경우 no=0 이 되니,

내용에 Password:

를 보여줄 것 이다.


그럼 이 쿼리를 가지고,

11자리의 아이디를 알아내는 스크립트를 작성해보자.

필자는 Python 을 애용하므로, Python으로 스크립트를 제작한다.


내가 작성한 스크립트는 첨부파일에서 다운로드할 수 있다.



 

challege9.py





스크립트를 잘 돌려보면 다음과 같이 Flag 를 알아낼 수 있다.



해당 아이디를 pw에 입력하면 해당 문제를 Solved 한다.





'Security > Wargame' 카테고리의 다른 글

[Webhacking.kr] All Clear  (0) 2015.08.06
[Webhacking.kr] Challenge 9  (5) 2015.08.05
[Webhacking.kr] Challenge 7  (0) 2014.10.07
[Webhacking.kr] Challenge 6  (0) 2014.10.07
[Webhacking.kr] Challenge 5  (0) 2014.07.10
[Webhacking.kr] Challenge 4  (0) 2014.07.10

티스토리 툴바