본문 바로가기
정보보안/wargame

[webhacking.kr] challenge(old). old-08

by 민-Zero 2020. 3. 19.

8번 문제에 들어가게 되면 hi guest라는 문구와 함께 페이지의 소스를 볼수있는 곳이 있다. 특별한 점은 없으므로 소스부터 확인해 보자.

소스는 다음과 같으므로 소스부터 분석해 보자.

 

$agent=trim(getenv("HTTP_USER_AGENT"));
$ip=$_SERVER['REMOTE_ADDR'];
if(preg_match("/from/i",$agent)){
  echo("<br>Access Denied!<br><br>");
  echo(htmlspecialchars($agent));
  exit();
}

getenv()함수는 인자값에 따라 환경변수의 값을 알려주는 함수이다. getenv("HTTP_USER_AGENT")는 웹사이트를 접속한 컴퓨터의 웹브라우저 정보를 알려주게 된다. 따라서 agent변수에는 웹브라우저 정보가 trim함수를 통해 양쪽 공백이 지워진채로 저장된다.

$_SERVER['RMOTE_ADDR']는 환경변수로 사이트에 접속한 사용자 ip를 가지고 있다. 따라서 ip변수에는 접속한 사용자의 ip가 저장되게 된다.

agent에 저장된 값을 preg_match함수를 통해 "/from/i"정규표현식과 매칭시켜 매칭된다면 해당 조건문을 실행하게 된다. 즉, 해당 정규표현식은 from이라는 문자열이 대소문자 구분없이 들어가 있다면 매칭되어 조건문이 실행된다.

조건문이 실행되면 Access Denied!라는 문구와 함께 agent에 저장된 값이 htmlspecialchars함수를 통해 html엔티티로 인코딩되어 출력되게 된다.

 

$db = dbconnect();
$count_ck = mysqli_fetch_array(mysqli_query($db,"select count(id) from chall8"));
if($count_ck[0] >= 70){ mysqli_query($db,"delete from chall8"); }

db변수를 Database와 연결된 객체로 만든다.

mtsqli_query() 함수를 통해 db와 연결된 객체를 사용하여 "select count(id) from chall8"라는 mysql 쿼리문을 실행시킨다.

mysqli_fetch_array 함수는 mysqli_query 를 통해 얻은 result set에서 레코드를 1개씩 리턴해주는 함수이다. 따라서 count_ck변수에 결과에대한 값들이 배열로 저장되고 0번째 인덱스 값이 70이상이라면 chall8이라는 테이블이 삭제된다.

 

$result = mysqli_query($db,"select id from chall8 where agent='".addslashes($_SERVER['HTTP_USER_AGENT'])."'");
$ck = mysqli_fetch_array($result);

addslashes()함수는 sql에서 의도치 않게 특수문자 처리되는 문자를 해결해기 위해 escape해주는 함수이다. 예를 들어 I'm 이라는 이름을 넣으려고 한다면 오류가 발생한다. 따라서 I\'m으로 escape해주는 과정이 필요하게 된다.

따라서 $_SERVER 환경변수로 받아온 사이트 접속한 클라이언트 프로그램 정보문자열을 쿼리문으로 추가해 결국 쿼리문은 select id from chall8 where agent='"클라이언트 프로그램 정보"'가 된다.

ck변수에 result변수에 저장된 쿼리문의 result set을 배열형태로 저장하게 된다.


if($ck){
  echo "hi <b>".htmlentities($ck[0])."</b><p>";
  if($ck[0]=="admin"){
    mysqli_query($db,"delete from chall8");
    solve(8);
  }
}

ck 변수에 정상적으로 값이 들어 있게되면 동작하는 조건문 이다. 이때 ck[0]의 값이 admin이라면 해당 문제가 풀리게 된다.


if(!$ck){
  $q=mysqli_query($db,"insert into chall8(agent,ip,id) values('{$agent}','{$ip}','guest')") or die("query error");
  echo("<br><br>done!  ({$count_ck[0]}/70)");
}

ck 변수에 값이 들어있지 않으면 동작하는 조건문으로 insert 쿼리문으로 chall8테이블에 값을 넣을 수 있다.

해당 쿼리문은 insert into chall8(agent,ip,id) values('{$agent}','{$ip}','guest')")이다. 

 

해당 문제를 풀기위해서는 ck[0]에 admin이라는 값이 들어가야 한다. 하지만 db의 첫번째 부분에 admin이라는 값이 들어있다는 보장이 없으므로 70개 이상의 데이터를 집어넣어 초기화 시킨후 ck에 데이터가 없을때 실행되는 insert 쿼리를 이용해 admin이라는 값을 넣어 주어야 한다. 해당 쿼리문에서 id가 admin이 아닌 guest로 값이 들어가는 것을 확인할 수 있으므로 뒤에 부분은 주석처리하고 원하는 값을 넣어 주어야 할 것 같다.

 

$agent 변수에 들어가는 HTTP_USER_AGENT 값이 User-Agent 라는 이름으로 헤더를 통해 전달되는 것을 확인할 수 있다. 따라서 헤더의 User-Agent 값에 'user-agent','ip','admin' 의 형식을 가지도록 해서 전달해 주어야 한다.

 

User-Agent 헤더에 "min', '0', 'admin')#"을 입력하여

insert into chall8(agent,ip,id) values('min', '0', 'admin')#','{$ip}','guest')") 으로 뒷부분이 주석처리 되어 insert 쿼리문이 실행되게 된다. 그 다음 다시 헤더값을 보내게 되면 ck의 값이 존재하고 첫번째 값을 admin으로 삽입해놨으므로 해당 문제가 해결되게 된다.

댓글