R136A1

[webhacking.kr] Challenge old-59 :: SI 본문

WEB SECURITY/SQL Injection

[webhacking.kr] Challenge old-59 :: SI

r136a1x27 2022. 2. 23. 00:13

<?php
  include "../../config.php";
  if($_GET['view_source']) view_source();
  $db = dbconnect();
  if($_POST['lid'] && isset($_POST['lphone'])){
    $_POST['lid'] = addslashes($_POST['lid']);
    $_POST['lphone'] = addslashes($_POST['lphone']);
    $result = mysqli_fetch_array(mysqli_query($db,"select id,lv from chall59 where id='{$_POST['lid']}' and phone='{$_POST['lphone']}'"));
    if($result['id']){
      echo "id : {$result['id']}<br>lv : {$result['lv']}<br><br>";
      if($result['lv'] == "admin"){
      mysqli_query($db,"delete from chall59");
      solve(59);
    }
    echo "<br><a href=./?view_source=1>view-source</a>";
    exit();
    }
  }
  if($_POST['id'] && isset($_POST['phone'])){
    $_POST['id'] = addslashes($_POST['id']);
    $_POST['phone'] = addslashes($_POST['phone']);
    if(strlen($_POST['phone'])>=20) exit("Access Denied");
    if(preg_match("/admin/i",$_POST['id'])) exit("Access Denied");
    if(preg_match("/admin|0x|#|hex|char|ascii|ord|select/i",$_POST['phone'])) exit("Access Denied");
    mysqli_query($db,"insert into chall59 values('{$_POST['id']}',{$_POST['phone']},'guest')");
  }
?>
<html><head><title>Challenge 59</title></head><body>
<form method=post>
<table border=1>
<tr><td></td><td>ID</td><td>PHONE</td><td></td></tr>
<tr><td>JOIN</td><td><input name=id></td><td><input name=phone></td><td><input type=submit></td></tr>
<tr><td>LOGIN</td><td><input name=lid></td><td><input name=lphone></td><td><input type=submit></td></tr>
</form>
<br>
<a href=./?view_source=1>view-source</a>
</body></html>

id = 가입 아이디 / pw = 가입 패스워드

lid = 로그인 아이디 / lphone = 로그인 패스워드

 

첫번째 if문(로그인)

lid 값이 존재하고?

lphone 변수가 설정되어 있다면?

(그냥 $_POST['lid'] 와 isset($_POST['lphone']) 의 로직 상 차이점을 잘 모르겠는데...)

→ id의 경우 문자이기 때문에 공백으로 내지 않는 한 0을 써도 false가 아닌 true인데

phone의 경우 숫자이기 때문에 0을 쓰면 false가 되기 때문이라 추측

 

lid와 lphone에 대해 addslashes를 적용하고

select id,lv from chall59 where id='{$_POST['lid']}' and phone='{$_POST['lphone']}'

쿼리로 DB에서 결과를 받아온다

 

결과에서 id 값이 있을 경우, 결과를 출력해주고

lv이 admin이면 문제가 풀린다.

 

 

두번째if문(가입)

id 값이 존재하고

phone 변수가 설정되어 있다면

 

id와 phone에 대해 addslashes를 적용하고

 

id에서 admin이 대소문자 구분 없이 검출된다면

phone의 길이가 20 이상이라면

phone에서 admin, 0x, #, hex, char, ascii, ord, select가 대소문자 구분 없이 검출된다면

Access Denied 출력 후 종료

 

모두 아니라면

insert into chall59 values('{$_POST['id']}',{$_POST['phone']},'guest')

쿼리로 DB에 데이터를 삽입하게 된다.


첫번째 if문에서 공격을 시도하려면

select id,lv from chall59 where id='{$_POST['lid']}' and phone='{$_POST['lphone']}'

select 문에서 조건을 없애기 위해 id든 phone이든 or 1=1 과 같은 조건을 추가해야 하는데

두 변수 모두 ' 로 감싸여져 있고, addslashes가 있으므로 '로 끝내고 조건을 추가하지 못한다.

 

정직하게 풀려면 select 문이기 때문에 admin 레벨을 가진 계정의 id, phone을 다 알아야 한다.

 


두번째 if문에서 공격을 시도하라는 것 같다.

insert into chall59 values('{$_POST['id']}',{$_POST['phone']},'guest')

 

앞에서 세 번째에 admin을 넣고 문을 완성시킨 후 )로 끝내고  뒤를 주석 처리 하면 될 듯하다.

insert into chall59 values('아무거나',아무거나,'admin') #,'guest')

 

그런데 위에서 봤듯이, addslashes도 적용하고, id가 admin이면 안되고, phone의 길이가 20이상이면 안되고,

phone에서 admin, 0x, #, hex, char, ascii, ord, select를 못쓰기 때문에...

 

1. 짧게 하자 (아무거나가 아니라 1 쓰기)

2. 주석은 # 을 쓰면 안된다. ( -- 을 쓰기)

insert into chall59 values('아무거나',1,'admin'))-- ,'guest')

 

3. 어떻게 해도 phone에서 admin을 간접적으로(hex, char, ascii, ord) 완성 시킬 방법은 없다.

+ 가능하다 해도 1. 조건에 의해 길이로 막힌다.

=> reverse 변수를 사용한다는 힌트

insert into chall59 values('아무거나',1,reverse('nimda'))-- ,'guest')

문제점은 phone에는 ' 를 쓰지 못한다는 것과

이미 최소 한도로 사용했는데도 20글자를 넘어버린 것이다.

 

=> reverse 변수의 인자로 같은 구문의 변수를 전달할 수 있다는 힌트 => id 값을 사용해야 함

insert into chall59 values('nimda',1,reverse(id))-- ,'guest')

 

이렇게 가입 쿼리를 날리고, nimda, 1로 로그인하면 풀린다.


① insert문에서 SQL Injection하는 문제는 처음이었는데, values를 소괄호()로 받을 경우
   괄호를 끝내고 뒤의 것을 주석 처리하는 것이 포인트

 

주석에는 여러 종류가 있음. 보통 # 을 많이 써서 그걸 필터링 해놨는데 -- 도 주석이라 취약점 발생

    그리고 -- 은 뒤에 공백이 있어야 함

 

③ addslashes는 '(작은 따옴표) 처리한 변수를 오류가 나지 않게 끝내고 새로운 조건을 추가하는 것을 막기 위함이므로      ' 처리하지 않은 변수가 있을 경우 '를 하지 않고도 새로운 조건을 추가 할 수 있는 해당 부분을 노리면 된다.

 

④ 문자열을 전달하는 데 '(작은 따옴표)를 안쓰면 오류난다.

 

이번에는 라업 봤는데, SQL 내장 함수 https://blog.naver.com/foryunha/20139047181 를 찾아서 힌트를 얻는 것이 좋다.

Comments