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

A03: কমান্ড ইনজেকশন ও অন্যান্য ইনজেকশন

A03: Command & other injection
৮ মিনিট পড়া মধ্যবর্তী · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • কমান্ড ইনজেকশন কীভাবে ঘটে এবং SQL ইনজেকশনের সাথে এর মূল সাদৃশ্য কী
  • LDAP ইনজেকশন ও XML/XXE ইনজেকশন — সংক্ষিপ্ত পরিচিতি
  • কেন shell=False-এর মতো নিরাপদ API argument-গুলো আলাদা রাখে, এবং তা কেন গুরুত্বপূর্ণ
  • Allowlist regex ভ্যালিডেশন দিয়ে ইনপুট প্রত্যাখ্যান করার প্র্যাকটিক্যাল প্যাটার্ন

১ · কমান্ড ইনজেকশন — একই root cause, ভিন্ন ইন্টারপ্রেটার

L20-এ আমরা দেখেছি অবিশ্বস্ত ইনপুট একটি SQL ইন্টারপ্রেটারের সাথে মিশে গেলে কী হয়। ঠিক একই মূল সমস্যা — কমান্ড ইনজেকশনCommand Injectionঅবিশ্বস্ত ইনপুট সরাসরি একটি OS শেল কমান্ডে কনক্যাটেনেট করার ফলে, attacker অতিরিক্ত কমান্ড ইনজেক্ট করতে পারে। — একটি ভিন্ন ইন্টারপ্রেটারে (SQL ইঞ্জিনের বদলে OS শেল) ঘটতে পারে। যদি একটি অ্যাপ্লিকেশন এভাবে একটি কমান্ড তৈরি করে —

"ping " + user_input

— তাহলে attacker যদি user_input-এ 8.8.8.8; rm -rf /-এর মতো কিছু দেয়, শেল ;-কে "একটি কমান্ড শেষ, নতুন কমান্ড শুরু" হিসেবে বুঝে নেয় — অর্থাৎ মূল ping কমান্ডের পরে সম্পূর্ণ আলাদা, attacker-নিয়ন্ত্রিত একটি দ্বিতীয় কমান্ড চলে যায়।

LDAP ইনজেকশন
অবিশ্বস্ত ইনপুট একটি LDAP (ডিরেক্টরি সার্ভিস) কোয়েরিতে কনক্যাটেনেট হলে, ঠিক SQLi-এর মতোই attacker কোয়েরির লজিক বদলে দিতে পারে — যেমন একটি অথেন্টিকেশন ফিল্টার বাইপাস করা।
XML/XXE ইনজেকশন
XXE (XML External Entity) — অবিশ্বস্ত XML পার্স করার সময় "external entity" রেজোলিউশন বন্ধ না থাকলে, attacker সার্ভারের লোকাল ফাইল পড়ার জন্য একটি বিশেষ entity সংজ্ঞায়িত করতে পারে।
এই পুরো ক্যাটাগরির প্যাটার্ন সবসময় একই: অবিশ্বস্ত ডেটা + একটি ইন্টারপ্রেটার যা ডেটা ও কোড আলাদা করতে ব্যর্থ হয় = ইনজেকশন। ইন্টারপ্রেটারটা SQL, শেল, LDAP নাকি XML পার্সার — মূল সমস্যা একই থেকে যায়।

২ · প্রতিরক্ষা — নিরাপদ API ও allowlist ভ্যালিডেশন

মূল ফিক্স ঠিক L20-এর মতোই — কখনো ইন্টারপ্রেটার-কমান্ড স্ট্রিং কনক্যাটেনেশন দিয়ে তৈরি করবেন না। Python-এ, উদাহরণস্বরূপ, subprocess.run([...], shell=False)-এর মতো API argument-গুলো একটি লিস্ট হিসেবে আলাদাভাবে পাঠায় — শেল কখনো এই argument-গুলো নিজে পার্স করে না, তাই ; বা &&-এর মতো কোনো ক্যারেক্টার বিশেষ অর্থ পায় না, শুধুই ডেটা থেকে যায়। এর সাথে যোগ করুন allowlist ইনপুট ভ্যালিডেশন — ইনপুটকে শুধুমাত্র প্রত্যাশিত প্যাটার্নের (যেমন একটি বৈধ হোস্টনেম/IP) সাথে মিলিয়ে দেখা, না মিললে সম্পূর্ণ প্রত্যাখ্যান।

Python
import re

def build_ping_command_insecure(host):
    # ভালনারেবল: ইউজার ইনপুট সরাসরি কমান্ড স্ট্রিং-এ জোড়া হচ্ছে
    return f"ping -c 4 {host}"

def build_ping_command_secure(host):
    # allowlist ভ্যালিডেশন -- হোস্টনেম/IP-তে শুধুমাত্র অক্ষর, সংখ্যা, ডট ও হাইফেন অনুমোদিত
    allowlist_pattern = r'^[a-zA-Z0-9.\-]+$'
    if not re.match(allowlist_pattern, host):
        raise ValueError(f"অবৈধ হোস্টনেম প্রত্যাখ্যাত: {host!r}")
    return f"ping -c 4 {host}"   # শুধু allowlist পাশ করা ইনপুটই এখানে পৌঁছায়

safe_host = "8.8.8.8"
malicious_host = "8.8.8.8; rm -rf /"

print("=== ইনসিকিউর কমান্ড বিল্ডার (স্ট্রিং কনক্যাটেনেশন) ===")
cmd = build_ping_command_insecure(malicious_host)
print("তৈরি হওয়া কমান্ড স্ট্রিং:", cmd)
print("লক্ষ্য করুন: শেলে চালালে ';' এর পরের অংশ একটি সম্পূর্ণ নতুন, আলাদা কমান্ড হিসেবে চলত।")
print("(এখানে আমরা এটি শুধু একটি স্ট্রিং হিসেবে দেখাচ্ছি -- বাস্তবে কখনো execute করা হচ্ছে না।)")

print()
print("=== সিকিউর কমান্ড বিল্ডার (allowlist ভ্যালিডেশন) ===")
try:
    cmd_safe = build_ping_command_secure(malicious_host)
    print("তৈরি হওয়া কমান্ড:", cmd_safe)
except ValueError as e:
    print("প্রত্যাখ্যাত:", e)

print()
print("বৈধ হোস্টের জন্য সিকিউর ভার্সন স্বাভাবিকভাবেই কাজ করে:")
print(build_ping_command_secure(safe_host))

    
মূল কথা · Key takeaway

লক্ষ্য করুন সিকিউর ভার্সনে আমরা ; নির্দিষ্ট করে ব্লক করিনি — বরং শুধু কী অনুমোদিত তা সংজ্ঞায়িত করেছি (allowlist), এবং যা কিছু সেই প্যাটার্নে মেলে না তা সম্পূর্ণ প্রত্যাখ্যান করেছি। এই "allowlist, blocklist নয়" নীতি L20-এর প্যারামিটারাইজড-কোয়েরি নীতির সাথে একই দর্শন শেয়ার করে — সম্ভাব্য সব বিপজ্জনক প্যাটার্ন খুঁজে ব্লক করার চেষ্টা করার বদলে, শুধু যা নিরাপদ তা নির্দিষ্ট করে দিন।

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

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

প্র ০১ কমান্ড ইনজেকশন ও SQL ইনজেকশনের (L20) মূল সাদৃশ্য কী, এবং পার্থক্যটা ঠিক কোথায়?

দুটোরই মূল কারণ এক: অবিশ্বস্ত ইনপুট সরাসরি একটি ইন্টারপ্রেটারের কমান্ড/কোয়েরি স্ট্রিং-এ কনক্যাটেনেট হয়ে যাওয়া, যাতে ইনপুটের ভেতরের বিশেষ ক্যারেক্টার (SQL-এ ', শেল-এ ;) সিনট্যাক্স হিসেবে ব্যাখ্যা হয়ে যায়। পার্থক্য শুধু কোন ইন্টারপ্রেটার এই ভুল কনক্যাটেনেশন গ্রহণ করছে — একটি ডেটাবেস ইঞ্জিন, নাকি OS শেল।

প্র ০২ কেন subprocess.run([...], shell=False)-এর মতো একটি API subprocess.run("ping " + host, shell=True)-এর চেয়ে নিরাপদ?

shell=True দিলে পুরো স্ট্রিংটি প্রথমে একটি শেল ইন্টারপ্রেটার দিয়ে পার্স হয় — যেখানে ;, &&, |-এর মতো ক্যারেক্টার বিশেষ অর্থ বহন করে। shell=False ও argument-লিস্ট ফর্ম্যাটে প্রতিটি argument সরাসরি প্রোগ্রামে পাঠানো হয়, কোনো শেল-পার্সিং ধাপ ছাড়াই — তাই ইনপুটের ভেতরের বিশেষ ক্যারেক্টার নিছক ডেটা থেকে যায়, সিনট্যাক্স হিসেবে ব্যাখ্যা হয় না।

প্র ০৩ XXE ইনজেকশন কীভাবে "external entity" নামক একটি XML ফিচারের অপব্যবহার করে — এটি প্রতিরোধের সহজ উপায় কী হতে পারে ধারণা করুন?

XML ফরম্যাটে "external entity" একটি ফিচার যা একটি ডকুমেন্টের ভেতরে বাইরের একটি রিসোর্স (যেমন একটি লোকাল ফাইল বা URL) রেফারেন্স করার অনুমতি দেয়। যদি একটি XML পার্সার ডিফল্টভাবে এই ফিচার চালু রাখে এবং অবিশ্বস্ত XML পার্স করে, attacker একটি entity সংজ্ঞায়িত করে সার্ভারের সংবেদনশীল লোকাল ফাইল পড়তে পারে। সহজ প্রতিরোধ: XML পার্সারে external entity resolution সম্পূর্ণ ডিফল্টভাবে বন্ধ রাখা, শুধুমাত্র প্রয়োজন হলেই সুনির্দিষ্টভাবে চালু করা (secure-by-default নীতি)।

অনুশীলন

  1. চিন্তা করুন: একটি ওয়েব অ্যাপ যদি ইউজারকে একটি ফাইলের নাম দিয়ে একটি রিপোর্ট জেনারেট করতে দেয় এবং ভেতরে ভেতরে একটি শেল কমান্ড চালায়, কমান্ড ইনজেকশনের ঝুঁকি এড়াতে কী কী পদক্ষেপ নেওয়া উচিত তালিকা করুন।

    প্রথমত, ফাইলের নাম একটি allowlist regex দিয়ে যাচাই করা (শুধু অনুমোদিত ক্যারেক্টার)। দ্বিতীয়ত, যদি সম্ভব হয় শেল কমান্ড পুরোপুরি এড়িয়ে সরাসরি প্রয়োজনীয় লাইব্রেরি ফাংশন ব্যবহার করা। তৃতীয়ত, শেল ব্যবহার করতেই হলে shell=False ও argument-লিস্ট ফর্ম্যাট ব্যবহার করা, যাতে ফাইলের নাম কখনো শেল-সিনট্যাক্স হিসেবে পার্স না হয়।

  2. পরীক্ষা করুন: উপরের কোড সেলে build_ping_command_secure-কে "google.com" ও "google.com && echo hacked" — দুটি ইনপুট দিয়ে আলাদাভাবে চালিয়ে ফলাফল তুলনা করুন।

    "google.com"-এর জন্য regex ^[a-zA-Z0-9.\-]+$ সম্পূর্ণ মিলে যাবে (শুধু অক্ষর ও ডট), তাই কমান্ড স্বাভাবিকভাবে তৈরি হবে। "google.com && echo hacked"-এ স্পেস ও & ক্যারেক্টার থাকায় regex মিলবে না, ফাংশন ValueError রেইজ করে ইনপুটটি সম্পূর্ণ প্রত্যাখ্যান করবে — কমান্ড তৈরিই হবে না।

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

আগের পাঠ
L20 · A03: SQL ইনজেকশন