본문 바로가기

WarGame/webhacking.kr

[webhacking.kr] Challenge 13 :: Blind SQL Injection 4



Summary 

  • "<", ">", "=" 문자 필터링을 우회하기 위해 IN() Clause를 사용합니다.
  • 공백 필터링을 우회하기 위한 방법은 "%09(Tab)", "%0a(LF)", "%0d(CR)", "/**/(주석)", "()", "+" 등이 있습니다.
  • "Limit" 문자열 필터링을 우회하기 위해 MAX(), MIN(), GROUP_CONCAT() 함수를 사용합니다.



Challenge 13은 다음과 같습니다.



"제출" 입력폼과 "Auth" 입력폼이 있습니다. "제출" 입력폼은 "no"라는 파라미터를 GET방식으로 전송하고, "Auth"는 "password" 파라미터를 GET 방식으로 전송합니다. "Auth" 입력폼은 최종 정답을 전송하는 폼으로 보입니다.


"제출" 입력폼이 Injection Vector라는 것을 짐작할 수 있습니다. 이 입력폼에 숫자 1을 전송하면 Result라는 테이블에 숫자 "1"이 출력되고, 다른 숫자를 입력하면 숫자 "0"이 출력되는 것을 확인 할 수 있습니다. 이와 같은 반응으로 Blind 공격을 수행할 수 있습니다.


먼저 Blind 공격을 수행 가능한지 알아보기 위해 다음과 같은 Query를 전송합니다.


1
no=IF(LENGTH(no)IN(1),1,2)
cs


"<", ">", "=" 문자가 필터링 되어 있기 때문에 IN()으로 우회하였습니다. 해당 Query 전송 결과 Result 테이블에 숫자 "1"이 출력됨으로 공격이 수행 가능한 것을 확인할 수 있습니다.


이번에는 "HINT"에 나와있는 flag Column의 자리 수를 알아보기 위해 다음과 같은 Query를 전송합니다.


1
no=IF(LENGTH(flag)IN(10),1,2)
cs


Blind 공격으로 IN(1) ~ IN(...) 까지 찾아보지만 결과가 나오지 않습니다. 이 것은 flag Column과 no Column은 서로 다른 테이블에 존재하는 것을 뜻합니다. "HINT"에 flag Column의 테이블명이 나와 있으므로 다음과 같이 SELECT 구문을 사용해야 합니다. 


1
no=IF(SUBSTR((SELECT%0dflag%0dFROM%0dprob13password),1,1)IN(0x61),1,2)
cs


"공백", "+", "%09(Tab)", "()" 등이 필터링되어 "%0d(CR)" 문자를 사용해서 우회하였습니다. 그러나 다음과 같이 Query를 전송해도 값이 출력되지 않습니다. 실제 MySQL 환경에서 문제와 비슷한 테이블을 만들어 놓고 실습해 본 결과 다음과 같은 ERROR가 출력됩니다.


1
ERROR 1242 (21000): Subquery returns more than 1 row
cs


즉 실제 flag Column에 값이 1개 이상이라는 뜻입니다. LIMIT을 사용해 보지만 필터링 되어 있습니다. 만약 flag Column에 데이터가 여러개 이더라도 데이터가 동일하다면 DISTINCT() 함수로 중복을 제거하여 1개의 데이터만 출력할 수 있을 것이라는 추측을 해보고 공격을 시도해 보지만, 결과가 나오지 않습니다. 결국 flag Column에는 동일한 데이터가 아닌 서로 다른 데이터가 여러개 들어 있는 것으로 추측 됩니다. ORDER BY가 필터링 되어 있기 때문에 COUNT() 함수를 사용하여 flag Column의 갯수를 추측할 수 있고 해당 갯수는 2개입니다.


이제 각각의 데이터를 추출하기 위해 LIMIT 대신에 MAX(), MIN(), GROUP_CONCAT() 함수를 사용할 수 있습니다. MAX(), MIN() 함수는 각각 최대값과 최소값을 출력하는 함수이고 GROUP_CONCAT() 함수는 Column의 모든 데이터를 ",(콤마)"로 묶어서 출력하게 됩니다. 실제 문제에서는 GROUP_CONCAT()도 필터링되어 있어 MAX()와 MIN() 함수를 사용해야 합니다.


1
no=IF(SUBSTR((SELECT%0dLENGTH(MAX(flag))%0dFROM%0dprob13password),1,1)IN(20),1,2)
cs


위와 같이 flag Column명을 MAX() 함수로 감싸 최대값만 출력할 수 있습니다. MAX() 함수를 LENGTH() 함수로 감싸면 flag Column의 최대값의 길이를 알 수 있습니다. 본격적인 공격에 앞서 길이를 파악하는 것이 편리합니다.


해당 결과, flag Column의 최대값은 4자리이고, 최소값은 20자리라는 결과가 나왔습니다. 그러므로 최소값이 본 문제의 정답이라는 것을 짐작할 수 있습니다. 이제 다음과 같은 Query로 본격적인 Blind SQL Injection을 시작할 수 있습니다.


1
no=IF(SUBSTR((SELECT%0dMIN(flag))%0dFROM%0dprob13password),1,1)IN(0x61),1,2)
cs