HackCert
Intermediate 9 min read May 25, 2026

Format Strings: মেমোরি করাপশন এবং সফটওয়্যার ক্র্যাশের জন্য দায়ী কোডিং দুর্বলতা!

Format String দুর্বলতা কীভাবে মেমোরি লিক, আরবিট্রারি রাইট এবং কোড এক্সিকিউশনে রূপান্তরিত হয় তার সম্পূর্ণ প্রযুক্তিগত বিশ্লেষণ।

Abdullah Al Mamun
Application Security Engineer
share
Format Strings: মেমোরি করাপশন এবং সফটওয়্যার ক্র্যাশের জন্য দায়ী কোডিং দুর্বলতা!
Overview

C প্রোগ্রামিং ভাষায় যখন একজন ডেভেলপার printf, fprintf, কিংবা syslog এর মতো ফাংশন কল করেন, তারা সাধারণত একটি Format String এবং কিছু আর্গুমেন্ট পাস করেন। এই Format String এ বিশেষ ডিরেক্টিভ থাকে যেমন %d, %s, %x যা আর্গুমেন্টগুলোকে নির্দিষ্ট ফরম্যাটে প্রিন্ট করে। কিন্তু একটি ছোট ভুল—যদি ব্যবহারকারীর ইনপুট সরাসরি Format String হিসেবে ব্যবহার করা হয়—এটি একটি গুরুতর নিরাপত্তা দুর্বলতার দরজা খুলে দেয়। Format String Vulnerability নামে পরিচিত এই বাগটি ১৯৯৯ সালে আবিষ্কৃত হওয়ার পর থেকে শত শত গুরুতর CVE এর কারণ হয়েছে এবং আজও মাঝে মাঝে নতুন কোডে দেখা যায়। এটি বুঝতে এবং প্রতিরোধ করতে প্রতিটি সিস্টেম প্রোগ্রামার এবং সিকিউরিটি গবেষকের জন্য অপরিহার্য।

Format String এর মৌলিক ধারণা

printf এর মতো Variadic ফাংশন একটি পরিবর্তনশীল সংখ্যক আর্গুমেন্ট গ্রহণ করে। উদাহরণ হিসেবে printf("Value: %d, Name: %s", number, name) এ ফাংশন প্রথমে Format String পড়ে এবং প্রতিটি ডিরেক্টিভের জন্য একটি আর্গুমেন্ট আশা করে। C ভাষায় Variadic ফাংশন আর্গুমেন্টের প্রকার বা সংখ্যা যাচাই করতে পারে না; এটি Format String এর উপর সম্পূর্ণ নির্ভরশীল।

এখন কল্পনা করুন একজন ডেভেলপার লিখলেন printf(user_input) যেখানে user_input ব্যবহারকারীর কাছ থেকে নেওয়া একটি স্ট্রিং। যদি ব্যবহারকারী সাধারণ পাঠ্য লেখেন, কোনো সমস্যা নেই। কিন্তু যদি তারা %x%x%x%x লিখেন, printf এই ডিরেক্টিভগুলোর জন্য স্ট্যাকে আর্গুমেন্ট খুঁজবে যা আসলে দেওয়া হয়নি। ফাংশনটি স্ট্যাক থেকে যাই হোক পড়বে এবং প্রিন্ট করবে—এর মধ্যে থাকতে পারে অন্য ফাংশনের লোকাল ভেরিয়েবল, Return Address, কিংবা সংবেদনশীল ডেটা।

এই সাধারণ দুর্বলতা থেকে আক্রমণকারী অনেক কিছু অর্জন করতে পারেন—মেমোরি কন্টেন্ট পড়া, ASLR বাইপাস করতে ঠিকানা জানা, এমনকি যেকোনো মেমোরি ঠিকানায় লেখা। এর জন্য বিশেষ Format Specifier প্রয়োজন যা C ভাষায় উপস্থিত আছে।

প্রধান Format Specifier এবং তাদের ক্ষমতা

Format String আক্রমণে কয়েকটি Specifier বিশেষ গুরুত্বপূর্ণ। %x হেক্সাডেসিমাল ফরম্যাটে একটি ইন্টিজার প্রিন্ট করে—এটি স্ট্যাক থেকে চার বাইট পড়ে। আক্রমণকারী এটি ব্যবহার করে স্ট্যাকের প্রতিটি অবস্থানের মান বের করতে পারেন। %s একটি স্ট্রিং প্রিন্ট করে—এটি একটি Pointer প্রত্যাশা করে এবং সেই ঠিকানা থেকে নাল টার্মিনেটর পর্যন্ত পড়ে। এর মাধ্যমে আক্রমণকারী যেকোনো মেমোরি ঠিকানার কন্টেন্ট পড়তে পারেন।

%n হলো সবচেয়ে বিপজ্জনক Specifier। এটি কিছু প্রিন্ট করে না; বরং এতদূর প্রিন্ট হওয়া অক্ষরের সংখ্যা একটি ইন্টিজার Pointer এ লেখে। এর মাধ্যমে আক্রমণকারী মেমোরিতে নির্বিচারে লেখা পরিচালনা করতে পারেন। আক্রমণকারী Output Width manipulate করে নির্দিষ্ট মান নিয়ন্ত্রণ করতে পারেন।

%hn দুটি বাইট লেখে, %hhn এক বাইট লেখে। এই বিভিন্নতা ব্যবহার করে আক্রমণকারী একটি ৩২ বা ৬৪ বিট ঠিকানায় ধাপে ধাপে নির্দিষ্ট মান লিখতে পারেন। Direct Parameter Access ব্যবহার করে—যেমন %7$x—আক্রমণকারী স্ট্যাকের সপ্তম অবস্থানে সরাসরি পৌঁছাতে পারেন কোনো পূর্ববর্তী মান না পড়েই।

আধুনিক glibc সংস্করণে FORTIFY_SOURCE এর কারণে %n ডিফল্টভাবে রিড অনলি Format String এ ব্লক করা হয়, কিন্তু সব সিস্টেমে নয়, এবং সব Variant এ নয়।

আক্রমণের ধাপ এবং Exploitation প্রক্রিয়া

Format String আক্রমণ সাধারণত দুটি প্রধান পর্যায়ে চলে—Information Disclosure এবং Memory Write। প্রথম পর্যায়ে আক্রমণকারী মেমোরি লেআউট বোঝেন। AAAA%x.%x.%x.%x.%x.%x.%x এর মতো পেইলোড পাঠিয়ে স্ট্যাকের মান একে একে পড়েন। নির্দিষ্ট অবস্থানে যখন AAAA (০x৪১৪১৪১৪১) দেখা যায়, আক্রমণকারী বুঝতে পারেন তারা ইনপুট বাফারে পৌঁছেছেন।

ASLR বাইপাস করতে আক্রমণকারীরা স্ট্যাক থেকে লাইব্রেরি বা প্রোগ্রাম কোডের ঠিকানা ফাঁস করেন। একটি libc ফাংশন ঠিকানা পেলে পুরো libc এর লোড ঠিকানা গণনা করা যায়, এবং সেখান থেকে system, execve এর মতো ফাংশনের অবস্থান। Stack Canary মান ফাঁস করে Stack Overflow আক্রমণের পথ খোলা যায়।

দ্বিতীয় পর্যায়ে আক্রমণকারী %n ব্যবহার করে নির্দিষ্ট ঠিকানায় লেখেন। প্রথমে ঠিকানা ইনপুট বাফারে রাখা হয়, তারপর Direct Parameter Access ব্যবহার করে সেই ঠিকানায় %n লেখা হয়। Output Width manipulate করে—যেমন %1000d—লেখা মান নিয়ন্ত্রণ করা হয়।

GOT Overwrite একটি ক্লাসিক প্রয়োগ। Global Offset Table এ printf, exit এর মতো ফাংশনের ঠিকানা থাকে। যদি আক্রমণকারী system এর ঠিকানা GOT এর printf এন্ট্রিতে লিখতে পারেন, পরবর্তী printf কল আসলে system হয়ে যাবে।

বাস্তব দুর্বলতার উদাহরণ

Format String দুর্বলতার ঐতিহাসিক উদাহরণ অনেক। WU-FTPD এর ২০০০ সালে আবিষ্কৃত দুর্বলতা ছিল প্রথম ব্যাপকভাবে শোষিত Format String বাগ। এর সাইট-কমান্ড লগিং কোডে ব্যবহারকারীর ইনপুট সরাসরি syslog এ পাঠানো হতো। আক্রমণকারীরা রিমোট থেকে FTP সার্ভারে রুট অ্যাক্সেস পেতে পারত।

CVE-2012-0809 ছিল sudo এর একটি Format String দুর্বলতা যেখানে -D ফ্ল্যাগ ডিবাগ মেসেজে ব্যবহারকারীর ইনপুট ফরম্যাট স্ট্রিং হিসেবে যাচ্ছিল। স্থানীয় Privilege Escalation সম্ভব ছিল।

OpenBSD এর pf ফায়ারওয়ালে ২০২০ সালে একটি Format String বাগ আবিষ্কৃত হয়েছিল। PostgreSQL এর কিছু সংস্করণে ত্রুটি মেসেজে এই দুর্বলতা পাওয়া গিয়েছিল। Android এর কিছু সিস্টেম সার্ভিসেও সাম্প্রতিক বছরগুলোতে Format String বাগ রিপোর্ট হয়েছে।

CTF প্রতিযোগিতায় Format String চ্যালেঞ্জ একটি সাধারণ বিভাগ। DEFCON, Plaid CTF, এবং Google CTF এ অসংখ্য চ্যালেঞ্জ এই ধারণার উপর ভিত্তি করে তৈরি। গবেষকদের জন্য এটি Exploit Development শিখার একটি চমৎকার ভিত্তি।

ভাষাগত এবং প্রোটোকল প্রসঙ্গ

Format String সমস্যা শুধু C তে সীমাবদ্ধ নয়। C++ এর iostream তুলনামূলকভাবে নিরাপদ কিন্তু printf স্টাইল ফাংশন এখনো ব্যবহৃত হয়। Objective C এর NSLog এ একই ধরনের দুর্বলতা সম্ভব। Python এর % অপারেটর এবং str.format দুর্বল যদি ব্যবহারকারীর ইনপুট ফরম্যাট স্ট্রিং হিসেবে ব্যবহৃত হয়।

PHP এর printf, sprintf ফাংশন একই দুর্বলতা ধারণ করে। Perl এ printf সিনট্যাক্স আলাদা কিন্তু একই নীতি প্রযোজ্য। JavaScript এ tagged template literal এর ভুল ব্যবহার অনুরূপ সমস্যা তৈরি করতে পারে যদিও মেমোরি করাপশন নয়।

Log4j এর CVE-2021-44228 দুর্বলতা যাকে Log4Shell বলা হয়, এটি Format String এর একটি আধুনিক বিবর্তন। Log4j এর Lookup Mechanism ব্যবহারকারী নিয়ন্ত্রিত ইনপুটকে কোড এক্সপ্রেশন হিসেবে ইন্টারপ্রেট করত। যদিও মেমোরি করাপশন নয়, মূল নীতি একই—Untrusted ইনপুটকে Format/Interpretation স্ট্রিং হিসেবে ব্যবহার।

লগিং লাইব্রেরিগুলো Format String আক্রমণের একটি বড় উৎস। অনেক ডেভেলপার মনে করেন লগ মেসেজ নিরাপদ, কিন্তু যদি ব্যবহারকারীর ইনপুট সরাসরি লগ ফরম্যাট স্ট্রিং এ যায়, দুর্বলতা একই।

Static এবং Dynamic Detection

Format String দুর্বলতা শনাক্ত করার বিভিন্ন কৌশল আছে। GCC এবং Clang এর -Wformat-security বা -Wformat=2 ফ্ল্যাগ সংকলনের সময় সন্দেহজনক printf ব্যবহার সম্পর্কে সতর্ক করে। যদি Format String একটি ধ্রুবক না হয়, কম্পাইলার সতর্কতা প্রকাশ করে।

Static Analysis টুল যেমন Coverity, Checkmarx, Fortify, এবং ওপেন সোর্স Flawfinder, RATS Format String বাগ খুঁজে পেতে দক্ষ। প্রতিটি printf-জাতীয় ফাংশন কল স্ক্যান করে দেখা হয় প্রথম আর্গুমেন্ট একটি String Literal কিনা।

Dynamic Analysis এ AddressSanitizer এবং MemorySanitizer Format String আক্রমণ চলাকালীন কিছু সমস্যা ধরতে পারে যদি তা অবৈধ মেমোরি অ্যাক্সেস ঘটায়। Fuzzing দিয়ে দীর্ঘ %s বা %n এর সংমিশ্রণ পাঠিয়ে দুর্বল ইনপুট পয়েন্ট খুঁজে বের করা যায়।

Symbolic Execution টুল যেমন angr Format String দুর্বলতা স্বয়ংক্রিয়ভাবে শনাক্ত এবং Exploit করতে পারে। আধুনিক Exploit Development পদ্ধতিতে এই টুলগুলো সম্পূর্ণ ওয়ার্কিং Exploit তৈরি করতে সক্ষম।

প্রতিরক্ষামূলক ব্যবস্থা

আধুনিক কম্পাইলার এবং রানটাইম পরিবেশে কিছু সুরক্ষা ব্যবস্থা যুক্ত হয়েছে। FORTIFY_SOURCE হলো glibc এর একটি বৈশিষ্ট্য যা কম্পাইল টাইমে নিরাপদ ফাংশন প্রতিস্থাপন করে। %n যদি একটি পঠনযোগ্য (Read Only) Format String থেকে আসে, FORTIFY_SOURCE এটি ব্লক করে।

Position Independent Executable এবং Stack Canary Format String আক্রমণকে কঠিন করে কিন্তু সম্পূর্ণ থামাতে পারে না কারণ Format String বাগ এই সুরক্ষাগুলো বাইপাস করার পথ দিতে পারে।

RELRO বা Read Only Relocation GOT কে রানটাইমে রিড অনলি করে দেয়, যার ফলে GOT Overwrite আক্রমণ অসম্ভব হয়। Full RELRO সাম্প্রতিক বাইনারিতে ডিফল্ট হয়ে যাচ্ছে।

Seccomp এবং অন্যান্য Sandbox প্রযুক্তি ব্যবহার করে এমনকি যদি Format String Exploit সফল হয়, ক্ষতি সীমিত রাখা যায়। প্রসেসকে শুধু প্রয়োজনীয় সিস্টেম কল ব্যবহারের অনুমতি দিয়ে আক্রমণকারীর ক্ষমতা সীমিত করা যায়।

প্রতিরোধ ও প্রতিকার

Format String দুর্বলতা প্রতিরোধে কিছু সহজ কিন্তু কার্যকর নীতি আছে। সবচেয়ে মৌলিক নিয়ম: কখনো ব্যবহারকারীর ইনপুট সরাসরি Format String হিসেবে ব্যবহার করবেন না। সঠিক উপায় হলো printf("%s", user_input) লেখা, printf(user_input) নয়। এই একটি পরিবর্তন প্রায় সব Format String দুর্বলতা প্রতিরোধ করে।

কোড পর্যালোচনায় printf, fprintf, sprintf, snprintf, syslog, এবং অন্যান্য সদৃশ ফাংশনের প্রতিটি কল পরীক্ষা করতে হবে। যদি প্রথম আর্গুমেন্ট একটি ভ্যারিয়েবল হয়, এটি একটি লাল পতাকা। নিরাপদ এবং উন্নত ভাষায় স্থানান্তরের কথা বিবেচনা করুন। Rust, Go, এবং অন্যান্য Memory Safe ভাষায় Format String বাগ প্রায় অসম্ভব কারণ এই ভাষাগুলোর Format System কম্পাইল টাইমে যাচাই করা হয়।

লগিং এর জন্য Structured Logging লাইব্রেরি ব্যবহার করুন যেমন zap, logrus Go তে; spdlog C++ এ। এই লাইব্রেরিগুলো Key Value প্যাটার্ন ব্যবহার করে এবং ব্যবহারকারী ইনপুট কখনো ফরম্যাট স্ট্রিং হিসেবে ব্যবহার করে না।

Continuous Integration পাইপলাইনে Static Analysis সক্রিয় রাখুন। কম্পাইলার সতর্কতা Warning হিসেবে নয়, Error হিসেবে বিবেচনা করুন। -Werror=format-security সব নতুন কোডে বাধ্যতামূলক করুন।

কর্মীদের নিরাপত্তা প্রশিক্ষণে Format String দুর্বলতা অন্তর্ভুক্ত করুন। যদিও আজকাল কম দেখা যায়, এটি C/C++ এবং Legacy কোডবেসে এখনো প্রাসঙ্গিক। CTF এবং ব্যবহারিক ল্যাব এই ধারণা শেখার চমৎকার উপায়।

Key Takeaways

Format String Vulnerability একটি ক্লাসিক সফটওয়্যার নিরাপত্তা বাগ যা দশকের পর দশক ধরে গুরুতর CVE এর কারণ হয়েছে। এর সরলতাই এর শক্তি—মাত্র একটি Format Specifier %x বা %n ব্যবহার করে আক্রমণকারী মেমোরি পড়তে, ASLR বাইপাস করতে, এবং পুরো প্রক্রিয়ার নিয়ন্ত্রণ গ্রহণ করতে পারেন। আধুনিক কম্পাইলার সতর্কতা, FORTIFY_SOURCE, RELRO এবং অন্যান্য প্রতিরক্ষা ব্যবস্থা এর প্রকোপ অনেক কমিয়েছে, কিন্তু সম্পূর্ণ নির্মূল করেনি। Legacy কোডবেস, এমবেডেড সিস্টেম, এবং অসাবধানী লগিং বাস্তবায়নে আজও এই বাগ পাওয়া যায়। ডেভেলপারদের জন্য সবচেয়ে গুরুত্বপূর্ণ পাঠ হলো: ব্যবহারকারীর ইনপুট কখনো সরাসরি Format String হিসেবে ব্যবহার করবেন না। এই একটি নিয়ম মেনে চলে এবং নিয়মিত Static Analysis ব্যবহার করে অধিকাংশ Format String আক্রমণ প্রতিরোধ করা সম্ভব। সাইবার নিরাপত্তা প্রায়শই জটিল মনে হয়, কিন্তু কিছু মৌলিক বাগ এড়াতে শুধু সচেতন কোডিং অভ্যাসই যথেষ্ট—Format String তার একটি উৎকৃষ্ট উদাহরণ।

আপনার জ্ঞান যাচাই করতে প্রস্তুত? আজই HackCert-এ Format Strings MCQ Quiz-টি দিন!

Related articles

back to all articles