SimpleBoard 문제이다.

게시글을 클릭하면 read.phpidx 값으로 게시글의 내용을 조회한다.

게시판의 코드가 공개되어 있다.

<?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....를 적용해도 결과값이 출력되지 않는 문제가 발생한다. 그 이유는 코드의 로직을 따라가다 보면 발견할 수 있다.