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

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

by 민-Zero 2020. 3. 2.

7번 문제에 들어가면 Admin page라는 글씨와 auth버튼이 있으며 url에 val=1이라는 의심스러운 변수가 보인다.

auth 버튼을 누르게 되면 Acceess_Denied! 라는 알림창과 함께 별다른 반응이 없다. 그럼 페이지의 소스코드를 통해 알아보자.

 소스코드를 볼수있는 php부분을 제외한 php코드를 하나씩 분석해보면

 

$go=$_GET['val'];
if(!$go) { echo("url=index.php?val=1>"); }

go라는 변수해 get방식을 통해 val변수의 값을 받아온다. 값을 받아 오지 못한다면 if문의 조건이 참이 되어 실행되어 url에서 val이라는 변수에 1이란 값을 get방식으로 전달해준다. 따라서 무조건 맨처음 볼수 있는 페이지가 등장하게 된다.

 

if(preg_match("/2|-|\+|from|_|=|\\s|\*|\//i",$go)) exit("Access Denied!");

preg_match(정규표현식, 검색대상) 함수는 첫번째 인수로 정규표현식을 받아 두번재 인수로 들어오는 검색대상에 해당 정규표현식이 매칭되면 1을 반환하고 실패하면 0을 반환하는 함수이다.

따라서 go라는 변수에 /2|-|\+|from|_|=|\\s|\*|\//i 라는 정규표현식과 매칭 된다면 조건식을 만족하여 "Access Denied!"라는 메세지를 출력하며 스크립트가 중단된다. 그러므로 go라는 변수에 해당 정규표현식에 해당하는 값이 들어가지 않도록 해야한다.

 

$db = dbconnect();
$rand=rand(1,5);
if($rand==1){
  $result=mysqli_query($db,"select lv from chall7 where lv=($go)") or die("nice try!");
}
if($rand==2){
  $result=mysqli_query($db,"select lv from chall7 where lv=(($go))") or die("nice try!");
}
if($rand==3){
  $result=mysqli_query($db,"select lv from chall7 where lv=((($go)))") or die("nice try!");
}
if($rand==4){
  $result=mysqli_query($db,"select lv from chall7 where lv=(((($go))))") or die("nice try!");
}
if($rand==5){
  $result=mysqli_query($db,"select lv from chall7 where lv=((((($go)))))") or die("nice try!");
}

db라는 변수에 dbconnect() 결과를 저장한다. 그 다음 rand라는 변수에 1~5의 난수를 저장한다. 따라서 밑의 조건문중 무조건 하나가 실행되게 된다.

mysqli_query(연결객체, 쿼리) 함수는 mysqli_connect 를 통해 연결된 객체를 이용하여 MySQL 쿼리를 실행시키는 함수이다. 따라서 dbconnect()를 통해 연결된 객체 db에 select lv from chall7 where lv = ($go)라는 쿼리문을 실행시킨 결과가 반환되고 result변수에 저장된다.

die()함수는 exit처럼 입력받은 문자열을 메세지로 출력하며 스크립트를 종료시키는 함수이다.

따라서 쿼리문을 실행시킨 결과가 아무것도 없다면 or 연산으로 인해 die()함수가 실행되고 쿼리 결과가 있다면 die()함수가 실행되지 않을것이다.

 

$data=mysqli_fetch_array($result);
if(!$data[0]) { echo("query error"); exit(); }
if($data[0]==1){
  echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Access_Denied!')\"><p>");
}
elseif($data[0]==2){
  echo("<input type=button style=border:0;bgcolor='gray' value='auth' onclick=\"alert('Hello admin')\"><p>");
  solve(7);
}

mysqli_fetch_array() 함수는 쿼리문을 통해 가져온 결과의 레코드를 배열을 통해 받아오는 함수이다. 해당 배열은 레코드의 인덱스와 필드명으로 이루어져 있다.

id 이름 전화번호
abc 김OO 010-1234-5678

 만약 위와같은 레코드가 존재한다면

0 id 1 이름 2 전화번호
abc abc 김OO 김OO 010-1234-5678 010-124-5678

과같은 결과를 반환해 주는 함수이다.

따라서 mysqli_fetch_array()함수의 결과를 data라는 배열에 저장후 data[0] 값이 False를 의미하면 if(!$data[0]) 조건문으로 인해 query error라는 메세지와 함께 종료되고

1이란 값이 있을때 auth버튼을 클릭하면 Access Denied라는 알림창만 뜨게되고

2란 값이 있을때 auth버튼을 클릭하면 Hello admin이라는 알림창과 함께 해당 문제가 해결되게 된다.

 

결국 문제를 해결하기 위해서는

① $data[0] == 2가 되어야 하고 그러기 위해선 $result가 2라는 값을 가지도록 하면 된다.

② $result는 난수에 따라 입력되는 쿼리문이 달라지는데  그중 "select lv from chall7 where lv = ($go)" 로 하나를 임의로 선택 괄호가 한개일때의 쿼리문에서 $go에 저장되는 값을 조절하여 result에 2가 저장되도록 한다.

③ $go는 get방식으로 val에 저장된 값을 받아와 1로 초기화 되어 사용되므로 url을 통해 val을 조작하면 된다.

 

chall7 테이블에 lv 컬럼에 어떤값이 있는지 모르기 때문에 무작정 값을 입력하면 풀리지 않는다.

select lv from chall7 where lv = (val 조절해준 값) 의 쿼리문을 통해 2라는 값을 가지도록 하려면 chall2 테이블의 lv 컬럼의 값을 찾거나 lv컬럼에 값을 넣어야 한다.

또한 preg_match("/2|-|\+|from|_|=|\\s|\*|\//i",$go) 함수로 해당 정규표현식에 해당하는 값이 들어있으면 exit()함수가 호출되는 방법으로 get으로 들어오는 $go를 필터링 하므로 쿼리문을 고민해야 한다.

우선 정규표현식의 | 는 '또는'을 뜻하므로 |를 기준으로 나누어서 확인하면

2 - \+ from _ = \\s \* \/ /i
숫자2 기호- 1개 이상의 \ from 문자열 기호 _ 기호= 공백 0개 이상의 \ 기호 / 대소문자 구별 X

위와같은 경우는 사용할 수 없다.

 

select lv from chall7 where lv=($go) 우선 해당 쿼리문은 만족시키려면

select lv from chall7 where lv=(0) union select 2#)

노란색 부분이 넣어주어야 하는 값이다.

where 부분을 무효로 처리하기 위해 거짓에 대한 값을 넣어주고 직접적으로 2라는 값을 넣어주기 위해 union을 사용하여 값을 넣는다. 그리고 뒤의 die()함수가 실행되지 않도록 나머지부분을 주석처리 하기 위해 #을 추가한다. 즉,

SELECT lv FROM chall7 WHERE lv=(0)

UNION

SELECT (2)

와 같은 형식의 쿼리문이 전달되는 것이다. 정규표현식을 피하기 위해 false) union select 2#을 변경하면

0)union(select(char(50)))%23 가 된다.

공백을 피하기위해 공백을 괄호로 사용하고 2라는 값이 필터링 되기 때문에 char함수를 사용하여 아스키코드값을 사용하여 2를 전달하고 %23으로 #을 입력해주면 된다. 

 

성공적으로 문제가 풀리게 된다.

댓글