old-18 페이지는 "SQL INJECTION" 이름을 가지고 있습니다.
아래에 "view-source" 링크가 있습니다. 새 탭을 열고서 링크를 들어갔습니다.
소스코드 분석
색 표시가 되어있는 php 구문이 이번 문제의 핵심입니다.
db가 언급되는 것으로 보아 데이터베이스를 다루는 것이며, 사용자 정보가 저장된 것으로 생각됩니다.
페이지의 제목이 SQL INJECTION인 만큼 정보를 알아낼 수 있는 SQL 삽입을 통해서
데이터베이스에 비정상적 동작을 하도록 유도하는 것이 문제 해결의 실마리입니다.
이를 위해서는 데이터베이스에 접근하는 구문을 이해하여 어떻게 동작하는지 알아야 합니다.
<?php
if($_GET['no']){
$db = dbconnect();
if(preg_match("/ |\/|\(|\)|\||&|select|from|0x/i",$_GET['no'])) exit("no hack");
$result = mysqli_fetch_array(mysqli_query($db,"select id from chall18 where id='guest' and no=$_GET[no]")); // admin's no = 2
if($result['id']=="guest") echo "hi guest";
if($result['id']=="admin"){
solve(18);
echo "hi admin!";
}
}
?>
preg_match() 함수는 해당하는 문자열과 일치하는 값이 있으면 1, 없다면 0을 반환합니다.
if문의 조건으로 삽입하여 일치하는 문자열이 있으면 exit()문을 실행하고,
없다면 다음 문장을 진행하는 형태입니다.
preg_match() 함수의 첫 인자는 정규 표현식을 통해 검색하고자 하는 문자들을 받아들입니다.
정규식 표현 방법은 슬래시(/)를 사용하는 것과 RegExp() 함수를 사용하는 것이 있습니다.
위의 문장에서는 슬래시를 통해 표현되어있습니다.
정규 표현식에 대한 자세한 설명은 해당 링크에 있습니다.
https://developer.mozilla.org/ko/docs/Web/JavaScript/Guide/%EC%A0%95%EA%B7%9C%EC%8B%9D
슬래시를 기준으로 틀 안에 있는 문자 리스트는 공백 문자( ), 슬래시(/), 괄호 열기( '(' ), 괄호 닫기( ')' ),
파이프( | ), 앰퍼샌드(&), select, from, 16진수 표현(0x) 입니다.
해당하는 문자는 $_GET['no']에서 찾아냅니다. $는 변수를 의미합니다.
$_GET['no'] 변수는 사용자가 입력한 값을 저장하며,
해당 값이 데이터베이스에 있는 no의 값과 일치하면 결과를 출력합니다.
"select id from chall18 where id='guest' and no=$_GET[no]" 문장을 확인해 보겠습니다.
select ~ from ~ where ...
SQL을 학습하신 분들은 쿼리(query) 문장이라는 것을 알 수 있으실 것입니다.
문장에서는 guest로의 접근만 가능하도록 id='guest'로 고정했습니다.
and는 앞과 뒤의 조건이 모두 참(True)여야 참(True)을 출력합니다.
따라서 데이터베이스에서 id와 no는 한 쌍의 데이터를 의미합니다.
로그인 아이디와 비밀번호가 일치해야 접속이 되는 것처럼
guest라는 id와 $_GET[no] no값이 일치해야 허용이 된다는 의미입니다.
문제에서 제시된 php 코드에서는 주석으로 admin의 no는 2라고 명시하였습니다.
이제 어떻게 풀어낼 수 있는지를 알아내면 되겠습니다.
SQL INJECTION 활용하기
select id from chall18 where id='guest' and no=$_GET[no]
위의 SQL 문장에서 최종적으로 뽑아오는 데이터는 id 값임을 알 수 있습니다.
코드에 적용되어있는 SQL 문장의 취약점을 활용하여 문제를 해결하겠습니다.
접근할 부분은 id='guest' and no=$_GET[no] 입니다.
id='guest'로 명시되어있기 때문에 이 값을 수정할 수는 없습니다.
하지만 사용자가 입력하는 부분인 $_GET[no]는 조작할 수 있습니다.
guest에 해당하는 no의 값은 1입니다. (실제로 페이지에서 시도했습니다.)
id='guest' and no=1이 참이라는 의미이며
사용자가 입력한 1에 일치하는 id 값으로 guest를 가져옵니다.
no가 1이 아닌 다른 수가 들어가면 and 연산에 의해 참이 나올 수 없게 되어
id 값으로 어떤 것도 가져올 수 없습니다.
그렇다면 id='guest' and no=$_GET[no]에서 앞의 id를 무시하고 뒤의 사용자 입력 값만을
인식시킬 수 있게 된다면 어떻게 될까요?
or 연산과 admin에 해당하는 2를 활용해 보겠습니다.
id='guest' and no=0 or no=2
괄호가 없기 때문에 앞에서부터 순서대로 진행됩니다.
id='guest' and no=0 에서는 no값이 다르기 때문에 거짓(false)입니다.
그럼 이제 or no=2 연산만 남았습니다.
false or no=2는 다른 볼 것 없이 no 값이 2인 데이터가 있는지를 물어봅니다.
no=2에 해당하는 id가 없다면 둘 다 false이기 때문에 최종적으로 false,
no=2에 해당하는 id가 있다면 일치하는 데이터를 id 값으로 가져오는 것입니다.
no=2에 해당하는 id는 admin이기 때문에 admin 값을 id로 가져오게 됩니다.
?? 관리자 데이터에 접근하게 된 것입니다..!
풀이 적용
참고해야 할 부분으로 처음 정규식에서 제시된 문자들입니다.
제시된 문자들 중 하나라도 사용자 입력 값에 들어있다면 exit()에 의해 해결할 수 없습니다.
사용자 입력으로 들어가야 할 값은 0 or no=2라는 것을 알았습니다.
하지만 제한된 문자로 공백 문자( )가 존재하기 때문에 그대로 적으면 해결 불가...
우회하기 위한 방법을 적용해야 합니다.
해당 방법에 대한 자료를 참고하여 활용하였습니다.
공백 문자를 우회하는 방법으로 %0a가 있다고 하여 적용해 보았습니다.
그렇다면 사용자 입력으로 들어갈 값은 = 0%0aor%0ano=2
텍스트 박스에 값을 입력하고 제출을 누르면 특별한 변화는 발생하지 않습니다.
하지만 URL을 확인하시면 값이 들어가 있는 부분을 확인할 수 있습니다.
no= 다음 부분을 사용자 입력에 사용했던 값 그대로 다시 입력하고 엔터를 눌러서 시도합니다.
hi admin과 함께 성공했다는 출력을 확인할 수 있습니다!
'작업 > Webhacking.kr' 카테고리의 다른 글
webhacking.kr old-6 이해하기 (0) | 2020.09.02 |
---|---|
webhacking.kr old-24 이해하기 (0) | 2020.09.01 |
webhacking.kr old-17 이해하기 (0) | 2020.08.30 |
webhacking.kr old-16 이해하기 (0) | 2020.08.30 |
webhacking.kr old-14 이해하기 (0) | 2020.08.30 |