phantom

joinmail 파라미터에 임의의 값을 입력한 후 요청을 보내면 INSERT 문이 실행되어 DB에 값이 저장된다.
이 때, IP 컬럼에는 사용자의 공인 IP 주소가 자동으로 입력되어 저장되고
email 컬럼에는 사용자가 joinmail 파라미터에 입력한 값이 저장된다.

마지막 쿼리문을 살펴보면 $result['email'] === $_GET['email'] 값이 일치해야 문제 풀이를 성공할 수 있다.
입력한 값과 테이블에 저장된 email 값이 동일하려면 email에 저장되는 값을 사용자가 입력한 값으로 조작할 수 있어야 한다.
이를 위해서는 INFORAMTION_SCHEMA.processlist 의 INFO 컬럼을 이용하거나, 입력된 쿼리문 자체를 조회하는 Quine Query 를 활용할 수 있다.

일단 $result['email'] === $_GET['email'] 참이 될 수 있는 조건을 살펴보자.
쿼리문에서는 아래와 같이 no=1 and email='{$_GET[email]}' 조건으로 조회된 행의 $result['email'] 값이 $_GET[email] 과 일치해야 한다.
몇 가지 테스트를 진행한 결과 WHERE NO=1 행은 이미 데이터가 존재해서 바로 데이터를 삽입할 수 없었고,
NO != 1 인 행에 데이터를 삽입하면 $result['email'] === $_GET['email'] 조건을 만족하기 위해 데이터 길이가 길어졌다.
(조건을 만족하기 위한 데이터는 컬럼 길이를 초과해서 테이블에 저장되지 않았다.)
$query = "select email from prob_phantom where no=1 and email='{$_GET[email]}'";
어떻게 풀 수 있을까 고민하다가 위 방법으로는 풀이가 어렵다고 느껴져 방향을 바꿨다.
이 문제는 데이터 삽입 시 prob_phantom 테이블의 별칭(alias)을 정해주는 것으로 쉽게 해결할 수 있었는데,
INSERT, UPDATE 절 사용 시에 자체적으로 자기 자신의 테이블 값을 참조하여 사용하는 방법을 발견했다.
- 만약 자기 자신의 테이블에서 값을 가져와 데이터를 추가/변경할 때 별칭을 설정해주지 않으면 아래 오류가 발생하지만

아래 쿼리와 같이 AS를 이용하여 가상 테이블(pp) 처럼 구성해주면,
prob_phantom 테이블에 저장된 값을 바로 추출해서 INSERT 절에 사용할 수 있다.

추출한 이메일 정보를 email 파라미터에 담아 전달하면 클리어 성공이다.
(오히려 너무 간단하게 풀 수 있어서 오히려 당황했던 문제다.. ..)

🐛.. 🐛.. 🐛..