Write-Up
if(preg_match('/prob|_|\.|\(\)|#|-/i', $_GET[pw])) exit("No Hack ~_~");
if(strlen($_GET[pw])>6) exit("No Hack ~_~");
$query = "select id from prob_nightmare where pw=('{$_GET[pw]}') and id!='admin'";
if($result['id']) solve("nightmare");
위의 필터링 조건에서 눈여겨 봐야할 부분은 strlen() 으로 길이를 제한 하고 있다는 점과 주석 기호가 필터링되고 있다는 것이다.
주석 필터링에 대한 우회방법은 아래와 같다.
--(공백) : -- 뒤에 공백이 있어야 주석 처리가 가능하다.
# : 뒤에 공백이 없어도, 주석 처리가 가능하다.
/* */ : /* 와 */ 사이 문자열 주석 처리가 가능하다.
;%00
?pw=%27)=0;%00
위 우회 방법 중 ;%00 을 사용하여, 위와 같은 payload 작성 시, 아래와 같은 query가 완성되며, 문제를 해결 할 수 있다.
select id from prob_nightmare where pw=('')=0;') and id!='admin'
해당 문제를 혼자 힘으로는 풀지 못했는데, 그 이유는 ('')=0 과 같은 query를 생각해내지 못했기 때문이다. 그래서 아래와 같이 실습을 통해 ('')=0 및 auto type cast에 관해서도 알아본다.
실습
위 그림 1은 members 테이블의 속성 정보를 나타낸다.
select 구문을 통해 members 테이블을 조회해보았고, where 절을 통해 id=0이라는 조건을 추가해 query를 구성해 보았다. 위 그림 1에서 보았다싶이 id 필드(컬럼)의 속성은 varchar 였다. 즉 id 와 0 의 타입이 맞지 않기에 auto type cast가 발생해 0=0 꼴이 되어, members 테이블이 모두 조회된다.
아래에서 관련 경우를 좀 더 살펴본다.
아래 그림 6을 통해 해당 Challenge( nightmare )가 왜 해결됬는지를 확인할 수 있다.
('') = 0 : 빈 문자열을 숫자 0으로 강제 변환하여 비교한다. 이는 결과적으로 빈 문자열이 0으로 해석되고, 0=0 으로 true가 반환되기에 해당 문제가 해결될 수 있었다.
참고
https://dev.mysql.com/doc/refman/8.3/en/type-conversion.html
https://stackoverflow.com/questions/21762075/mysql-automatically-cast-convert-a-string-to-a-number
https://limjunyoung.tistory.com/121