পাঠ ৫৬ · ৬০-এর মধ্যে · মডিউল ১২
Home / Courses / Cybersecurity & Ethical Hacking / CTF: SQL ইনজেকশন

CTF ওয়াকথ্রু: স্যান্ডবক্সড SQL ইনজেকশন

CTF walkthrough: sandboxed SQL injection
৯ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • একজন CTF সলভার কীভাবে ধাপে ধাপে একটি SQLi-দুর্বল লগইন ফর্ম টেস্ট করেন
  • L20-এর ' OR '1'='1 বাইপাসটি বাস্তবে "কাজ করার" পেছনের যুক্তি আরও গভীরভাবে
  • একটি সিকিউর, parameterized-স্টাইল তুলনা কীভাবে একই payload-কে সম্পূর্ণ নিষ্ক্রিয় করে দেয়
  • কেন এই ধরনের প্র্যাকটিস শুধুমাত্র স্যান্ডবক্সড/অনুমোদিত পরিবেশে করা উচিত

১ · চ্যালেঞ্জ ব্রিফ

একটি অনুমোদিত CTF প্ল্যাটফর্মে "Web — Easy" ক্যাটাগরির একটি চ্যালেঞ্জ: একটি লগইন ফর্ম দেওয়া আছে, লক্ষ্য হলো পাসওয়ার্ড না জেনেই admin হিসেবে লগইন করা। এই লেসনের ব্যাকএন্ড সম্পূর্ণ toy — শুধুমাত্র একটি in-memory Python লিস্ট, কোনো বাস্তব ডেটাবেস সার্ভার নেই। লক্ষ্যটি সম্পূর্ণ নিয়ন্ত্রিত ও নিরাপদ, ঠিক L20-এর মতো।

২ · সলভারের চিন্তাপ্রক্রিয়া — ধাপে ধাপে

  1. ধাপ ১ — একটি নিরীহ ভুল ইনপুট দিয়ে বেসলাইন যাচাই করুন। সঠিক ইউজারনেম কিন্তু ভুল পাসওয়ার্ড দিয়ে ট্রাই করুন — যদি লগইন প্রত্যাখ্যাত হয় (প্রত্যাশিত), এটি নিশ্চিত করে ফর্মটি আসলে ক্রেডেনশিয়াল যাচাই করছে, একটি ব্রোকেন no-op নয়।
  2. ধাপ ২ — একটি single quote (') দিয়ে query logic-এ প্রবেশের চেষ্টা করুন। বাস্তব SQLi টেস্টিং-এ, একটি একক quote প্রায়ই একটি SQL syntax error ট্রিগার করে যদি ইনপুটটি সরাসরি query-তে concatenate করা হয় (কোনো এসকেপিং ছাড়া) — এটি একটি শক্তিশালী সংকেত যে ইনজেকশন সম্ভব।
  3. ধাপ ৩ — ক্লাসিক বাইপাস payload প্রয়োগ করুন। ইউজারনেম ফিল্ডে ' OR '1'='1 দিন। যদি ব্যাকএন্ড আসলে string concatenation দিয়ে query তৈরি করে, তাহলে WHERE ক্লজ কার্যকরভাবে ... WHERE username='' OR '1'='1' AND ...-এর মতো হয়ে যায় — '1'='1' সবসময় true, তাই পুরো শর্তটি true হয়ে যায় এবং প্রথম রেকর্ডটি (প্রায়ই admin) রিটার্ন হয়, পাসওয়ার্ড যাচাই না করেই।
গুরুত্বপূর্ণ সতর্কতা · এই কৌশল কোথায় প্রয়োগ করা যাবে

এই পুরো ওয়াকথ্রুটি একটি সম্পূর্ণ স্যান্ডবক্সড, in-memory toy ডেটাবেসের বিরুদ্ধে চলে — কোনো বাস্তব নেটওয়ার্ক কল বা প্রকৃত ডেটাবেস সার্ভার জড়িত নেই। L01-এ যেমন বলা হয়েছে, এই ঠিক same কৌশল কোনো বাস্তব ওয়েবসাইট বা অ্যাপ্লিকেশনের বিরুদ্ধে লিখিত অনুমতি ছাড়া প্রয়োগ করা ফৌজদারি অপরাধ — এমনকি যদি আপনি কোনো ক্ষতি না করেন বা শুধু "টেস্ট" করছেন বলে মনে করেন। এই কৌশল শুধুমাত্র নিজের ল্যাব, এই লেসনের মতো স্যান্ডবক্সড কোড, অথবা স্পষ্ট লিখিত অনুমতি ও স্কোপসহ একটি অনুমোদিত CTF/বাগ-বাউন্টি প্ল্যাটফর্মে প্রয়োগ করুন।

৩ · সমাধান — সম্পূর্ণ কোড

নিচের কোডে L20-এর ঠিক same insecure toy প্যাটার্ন পুনরায় ব্যবহার করা হয়েছে: একটি in-memory users_db লিস্ট, একটি insecure login_insecure() যা string-concatenation-স্টাইল query সিমুলেট করে (কোনো real SQL এক্সিকিউট হচ্ছে না, শুধু বাইপাস-লজিকের সিমুলেশন), এবং একটি সিকিউর login_safe() যা কখনো query string তৈরি করে না।

Python
# CTF চ্যালেঞ্জের toy in-memory "ডেটাবেস" — বাস্তব কোনো DB সার্ভার নেই
users_db = [
    {"username": "admin", "password": "S3cur3P@ss!", "role": "admin"},
    {"username": "guest", "password": "guest123", "role": "guest"},
]

def login_insecure(username, password):
    # শুধুমাত্র শিক্ষার জন্য — string-concatenation-স্টাইল query সিমুলেশন।
    # কোনো real SQL ইঞ্জিন এখানে এক্সিকিউট হচ্ছে না, শুধু বাইপাস-লজিক সিমুলেট করা হচ্ছে।
    simulated_query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
    if "' OR '1'='1" in username or "' OR '1'='1" in password:
        # ক্লাসিক bypass — WHERE ক্লজ সবসময় true হয়ে যায়, পাসওয়ার্ড কখনো চেক হয় না
        return {"query": simulated_query, "result": users_db[0]}
    for user in users_db:
        if user["username"] == username and user["password"] == password:
            return {"query": simulated_query, "result": user}
    return {"query": simulated_query, "result": None}

def login_safe(username, password):
    # সিকিউর ভার্সন — কখনো query string তৈরি করে না, শুধু ডেটা তুলনা করে
    for user in users_db:
        if user["username"] == username and user["password"] == password:
            return user
    return None

print("=== ইনসিকিউর ভার্সনের বিরুদ্ধে CTF সলভ ===")
print("ধাপ ১ — বেসলাইন (ভুল পাসওয়ার্ড):")
step1 = login_insecure("admin", "wrongpass")
print("  ফলাফল:", step1["result"])

print("ধাপ ৩ — ক্লাসিক বাইপাস payload:")
step3 = login_insecure("' OR '1'='1", "anything")
print("  simulated query:", step3["query"])
print("  ফলাফল:", step3["result"])
print()

print("=== একই payload সিকিউর ভার্সনের বিরুদ্ধে ===")
safe_result = login_safe("' OR '1'='1", "anything")
print("  ফলাফল:", safe_result)

    
লক্ষ্য করুন পার্থক্যটি — login_insecure()-এ পুরো admin রেকর্ড (পাসওয়ার্ড সহ) রিটার্ন হয়েছে, যদিও পাসওয়ার্ড কখনো সঠিকভাবে যাচাই হয়নি। কিন্তু login_safe()-এ ঠিক একই payload-এর ফলাফল None — কারণ এটি কখনো ইনপুটকে একটি query-তে ব্যাখ্যা করার সুযোগ দেয় না, শুধু সরাসরি ডেটা তুলনা করে (parameterized query-র সমতুল্য যুক্তি)। এটিই CTF চ্যালেঞ্জের "ফ্ল্যাগ" — সফল বাইপাস বনাম প্রত্যাখ্যাত প্রচেষ্টা।
মূল কথা · Key takeaway

একটি CTF ওয়াকথ্রু সবসময় দুটি জিনিস প্রমাণ করে — দুর্বলতা কীভাবে কাজ করে এবং ফিক্স কীভাবে সেটি নিষ্ক্রিয় করে। শুধু exploit দেখানো অর্ধেক পাঠ — L20-এর মতোই, প্রকৃত মূল্য হলো বোঝা যে parameterized/data-only তুলনা কেন এই পুরো আক্রমণ-শ্রেণিকে গোড়া থেকে অসম্ভব করে দেয়, শুধু একটি নির্দিষ্ট payload ব্লক করে নয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ সলভার প্রথমে ধাপ ১-এ একটি সাধারণ ভুল পাসওয়ার্ড ট্রাই করলেন, সরাসরি বাইপাস payload দিয়ে শুরু করলেন না কেন?

বেসলাইন আচরণ যাচাই করা একটি গুরুত্বপূর্ণ প্রথম ধাপ — এটি নিশ্চিত করে যে ফর্মটি আদৌ ক্রেডেনশিয়াল-ভিত্তিক প্রত্যাখ্যান/গ্রহণ লজিক প্রয়োগ করছে। যদি সরাসরি বাইপাস payload ট্রাই করে "সফল" হন, কিন্তু আসলে ফর্মটি সবসময়ই সব ইনপুট গ্রহণ করত (একটি সম্পূর্ণ ভিন্ন, আরও গুরুতর বাগ), তাহলে ভুল উপসংহারে পৌঁছাতে পারতেন। বেসলাইন টেস্ট নিশ্চিত করে যে পর্যবেক্ষিত পরিবর্তনটি সত্যিই payload-এর কারণে ঘটেছে।

প্র ০২ ' OR '1'='1 payload-টি ঠিক কেন কাজ করে — WHERE ক্লজের যুক্তিতে কী ঘটছে?

একটি insecure query WHERE username='<input>' AND password='...' এভাবে তৈরি হলে, ইনপুটে ' OR '1'='1 বসালে এটি হয়ে যায় WHERE username='' OR '1'='1' AND password='...'। SQL-এ OR-এর প্রায়োরিটি এমন যে '1'='1' অংশটি সবসময় true, ফলে গোটা শর্তটি true হয়ে যায় নির্বিশেষে বাকি অংশে কী আছে — তাই ডেটাবেস প্রথম ম্যাচিং রেকর্ড রিটার্ন করে, পাসওয়ার্ড আসলে যাচাই না করেই।

প্র ০৩ এই লেসনে দেখানো toy সিমুলেশন কেন বাস্তব SQL ইঞ্জিন এক্সিকিউট না করেও এই শিক্ষামূলক লক্ষ্য পূরণ করতে যথেষ্ট?

SQL ইনজেকশনের মূল শিক্ষণীয় বিষয় হলো যুক্তি — অবিশ্বস্ত ইনপুট query-স্ট্রাকচারের অংশ হয়ে গেলে কী ঘটে। এই যুক্তিটি একটি সাধারণ Python if "' OR '1'='1" in ... চেক দিয়েও নিখুঁতভাবে প্রদর্শন করা যায়, বাস্তব SQL ইঞ্জিন ছাড়াই — এবং এটি করাটাই এই কোর্সের CLAUDE.md নিরাপত্তা নীতির মূল ভিত্তি: কৌশলের যুক্তি শিখুন সম্পূর্ণ নিরাপদে, কোনো বাস্তব সিস্টেম বা নেটওয়ার্ককে স্পর্শ না করে।

অনুশীলন

  1. চিন্তা করুন: উপরের কোডে login_insecure()-কে guest ইউজারনেম ও একটি ভুল পাসওয়ার্ড দিয়ে (বাইপাস payload ছাড়া) কল করুন। ফলাফল কী হবে তা আগে অনুমান করুন।

    ফলাফল হবে None — কারণ বাইপাস প্যাটার্ন ' OR '1'='1 ইনপুটে নেই, তাই কোডটি স্বাভাবিক লুপে যায় এবং users_db-তে ইউজারনেম-পাসওয়ার্ড ম্যাচ খুঁজে না পেয়ে None রিটার্ন করে। এটি নিশ্চিত করে যে insecure ভার্সনটি শুধুমাত্র নির্দিষ্ট বাইপাস প্যাটার্নের ক্ষেত্রেই ভেঙে পড়ে, সব ইনপুটের ক্ষেত্রে নয়।

  2. পরীক্ষা করুন: পাসওয়ার্ড ফিল্ডে (ইউজারনেমের বদলে) ' OR '1'='1 payload বসিয়ে login_insecure("admin", "' OR '1'='1") কল করুন এবং login_safe()-এ একই ইনপুট দিয়ে তুলনা করুন।

    login_insecure()-এ এই চেকটি username ও password উভয় ফিল্ডেই বাইপাস প্যাটার্ন খোঁজে, তাই পাসওয়ার্ড ফিল্ডে দিলেও একই বাইপাস ঘটবে এবং users_db[0] (admin) রিটার্ন হবে। login_safe()-এ কোনো ফরম্যাটেই কোনো পার্থক্য হয় না — এটি সবসময় None রিটার্ন করবে, কারণ এটি ইনপুটকে কখনোই query-লজিকের অংশ হিসেবে ব্যাখ্যা করে না, শুধু প্লেইন স্ট্রিং তুলনা করে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
CTF ওয়াকথ্রু: ক্লাসিক্যাল সাইফার ক্র্যাকিং