본문 바로가기

WarGame/webhacking.kr

[webhacking.kr] Challenge 09 :: HTTP Basic Authetication


(해당 글에서는 Authentication과 Authorization을 구분하지 않겠습니다. Authentication이라고 명명하는 곳도 있고 Authorization이라고 명명하는 곳도 있습니다. HTTP Header 이름이 "Authorization"이라서 그런 것 같습니다. 두 용어 모두 "인증절차"라고 이해하면서 읽어 주시기 바랍니다. 저는 "인증"에 포커스를 두고 "Authentication"이라고 명명하겠습니다.)


Challenge 09는 다음과 같습니다.



처음에는 위와 같이 나타나는 폼이 PHP와 Script로 만들어진 폼인지 알았습니다. 그러나 Proxy Tool로 확인한 결과 GET이나 POST 값은 없었고 취소를 클릭하면 "Unauthorized"라는 Error 페이지가 출력되었습니다. 그러나 Request Header에서 다음과 같이 "Authorization" 이라는 HTTP Header를 확인할 수 있었습니다.



이 Header를 검색해보면 HTTP Authentication 이라는 것이 검색되면서 인증 방식으로 "Basic", "Digest" 등등이 있다고 나올 것 입니다. "Basic" 방식은 id와 pw를 ":(콜론)"으로 묶은 값("id:pw")을 Base64로 인코딩하고, "Digest" 방식은 해당 값을 MD5로 인코딩된다고 합니다. 그러면서 복호화하기 쉽기 때문에 위와 같은 방식으로 인증을 해서는 안된다는 설명을 검색할 수 있을 것 입니다. 대부분의 검색 결과가 이와 같이 나옵니다. 그러고 보니 위의 Request Header에서도 "Authorization" 헤더 다음에 "Basic"이라는 문자열과 Base64로 인코딩된 값을 확인 할  수 있습니다.


이 인증을 우회하기 위해 여러가지 검색도 해보고 CRLF 방식으로 HTTP Splitting을 시도해도 잘 되지 않았습니다. 그러다가 "wowhacker"의 달팽이님이 쓰신 문서를 찾아냈습니다. 


bypass_apache_web_authorization.pdf


해당 HTTP 인증 방식에는 취약점이 존재합니다. 실제 Apache에서 HTTP 인증을 설정하는 방법은 해당 사이트에 자세하게 나와 있으니 참고하시기 바랍니다. (https://www.linux.co.kr/home2/board/subbs/board.php?bo_table=lecture&wr_id=216)


해당 취약점은 다음과 같이 아파치에서 인증을 설정하는 부분(.htaccess)에 있습니다.


 

 <Limit GET POST>

 order deny, allow

 deny from all

 </Limit>



위와 같이 인증을 적용할 HTTP Method를 "GET"과 "POST"만 지정해 놓고 다른 Method를 지정해 놓치 않으면 다른 Method를 이용해서 Request를 요청하였을 때 인증 절차를 우회할 수 있는 취약점이 존재합니다.


이 취약점을 이용하여 HTTP Method를 조작하여 Request를 보내면 다음과 같은 결과를 확인 할 수 있습니다.




인증 절차를 거치지 않고 "index.php" 페이지로 들어온 것입니다. 이 곳에서 "3" 링크로 이동하면 다음과 같은 Hint를 확인 할 수 있습니다. 



Column으로 id와 no이 존재하고 id 길이가 11자리 인 것을 암시하는 것 같습니다. Injection Vector는 "no" 파라미터로 정하고 SQL Injection 공격을 시작하겠습니다. 


"no" 파라미터가 3이면 힌트 페이지가 출력되고 1,2이면 각각 Apple, Banana 페이지가 출력됩니다. 4이면 그냥 password를 입력하는 페이지가 출력됨으로 이 반응을 이용하여 Injection을 하면 됩니다. 저번 글에서(http://limjunyoung.tistory.com/108) MySQL의 IF() 함수를 사용했었는데, 본 문제에서도 IF() 함수가 유용할 것 같습니다. 그럼 바로 힌트에서 주어진 "id" Column이 진짜 11자리인지 다음과 같은 Query를 전송해 보겠습니다.


1
no=IF(length(id)=11,3,4)
cs


위와 같이 Query를 보내면 "Access Denied" 문자열이 출력됩니다. "<", ">", "=" 문자가 필터링되어 있습니다. 이 것을 우회하는 방법은 IN Clause를 이용하는 것 입니다. 그러면 다음과 같은 Query를 만들 수 있습니다.


1
no=IF(length(id)IN(11),3,4)
cs


"IN(11)"은 "=11"과 같은 의미를 갖게 됩니다. 또 IN(subquery) 이와 같이 서브쿼리를 삽입할 수도 있습니다.

위의 Query의 결과로 힌트 페이지가 출력되는 것을 보니, SQL Injection이 잘 먹히는 것을 확인 할 수 있습니다. 이제 다음과 같이 substr() 함수를 사용하여 Blind SQL Injection 방식으로 id를 알아내야 합니다.


1
no=IF(substr(id,1,1)IN(0x61),3,4)
cs


위와 같은 Query로 id를 한 글자씩 알아낼 수 있습니다. Python과 같은 Script 언어를 이용하거나, Burp Suite의 Intruder 기능을 이용하면 보다 편리하게 히결할 수 있습니다.


 

위와 같이 입력했을 때, id에 정답 이외에도 "apple"과 "banana" Records가 들어 있을 것 입니다. 하지만 IF(substr(), 3, 4)라고 입력하면 no=3에 해당하는 값만 substr()로 가져옵니다. 마찬가지로 IF(substr(), 1, 4)라고 입력하면 no=1에 해당하는 "apple"만 짤라서 가져옵니다. 



실제 id를 알아내도 "pw" 파라미터에 해당 id를 입력하면 문제가 잘 풀리지 않습니다. 정확하게 어떻게 해결하는지는 알아 없었으나, 여러 페이지에서 해당 값을 입력하다 보면 어느 순간 해결이 되었습니다. 어떤 오류인 것인지 정확하게 알 수 없었습니다.