SimpleBoard 문제이다.
게시글을 클릭하면 read.php
에 idx
값으로 게시글의 내용을 조회한다.
게시판의 코드가 공개되어 있다.
<?php
if (isset($_GET['view-source'])){
if (array_pop(split("/",$_SERVER['SCRIPT_NAME'])) == "classes.php") {
show_source(__FILE__);
exit();
}
}
Class DB {
private $connector;
function __construct(){
$this->connector = mysql_connect("localhost", "SimpleBoard", "SimpleBoard_pz"); # localhost id passwd
mysql_select_db("SimpleBoard", $this->connector); # DB NAME = SimpleBoard
}
public function get_query($query){
$result = $this->real_query($query);
return mysql_fetch_assoc($result);
}
public function gets_query($query){
$rows = [];
$result = $this->real_query($query);
while ($row = mysql_fetch_assoc($result)) {
array_push($rows, $row);
}
return $rows;
}
public function just_query($query){
return $this->real_query($query);
}
private function real_query($query){
if (!$result = mysql_query($query, $this->connector)) {
die("query error");
}
return $result;
}
}
Class Board {
private $db;
private $table;
function __construct($table){
$this->db = new DB();
$this->table = $table;
}
public function read($idx){
$idx = mysql_real_escape_string($idx); # filterd : \\x00, \\n, \\r, \\, ', " and \\x1a
if ($this->read_chk($idx) == false){
$this->inc_hit($idx);
}
return $this->db->get_query("select * from {$this->table} where idx=$idx");
}
private function read_chk($idx){
if(strpos($_COOKIE['view'], "/".$idx) !== false) {
return true; # 포함되는 경우
} else {
return false; # 포함되지 않는 경우
}
}
private function inc_hit($idx){
$this->db->just_query("update {$this->table} set hit = hit+1 where idx=$idx"); # hit += 1
$view = $_COOKIE['view'] . "/" . $idx;
setcookie("view", $view, time()+3600, "/SimpleBoard/");
}
public function get_list(){
$sql = "select * from {$this->table} order by idx desc limit 0,10";
$list = $this->db->gets_query($sql);
return $list;
}
}
특이한 점은 query를 보내는 함수가 여러 개로 나누어져 있다는 점이었다.
real_query
함수를 통해 query를 보내는데, 보내는 방법에 따라 함수가 다르다. get_query
함수는 결과값을 associative array 형태로 받아오고, gets_query
함수는 결과값을 associative array 형태로 받아온 것을 $rows라는 array로 받아온다. (대충 이중 리스트 정도로 이해했다. 자세히는 모름). just_query
함수는 쿼리를 보낸 결과를 그대로 가져온다.
SQL injection 적용
idx = 0 or 1
을 입력하면 1번 게시글이 출력되며, SQL injection이 가능함을 알 수 있다. (사실 코드로 보아도 알 수 있다.)
코드에서 mysql_real_escape_string()
을 idx 인자에 대해서 적용하고 있기 때문에, \\x00, \\n, \\r, \\, ', " and \\x1a
는 필터링 되어 사용할 수 없다. 다행히 union select가 살아있기 때문에 이를 활용하기로 한다.
그런데, idx = 0 union select 1,2,3....
를 적용해도 결과값이 출력되지 않는 문제가 발생한다. 그 이유는 코드의 로직을 따라가다 보면 발견할 수 있다.