সরাসরি পর্বে যান
পর্ব ৫৬ · ৮০-এর মধ্যে · মডিউল ৯

SIEM আর detection rule — Sigma, correlation আর false positive কমানো

SIEM and Detection Rules — Sigma, Correlation & Reducing False Positives

Taught in Bangla, this lesson follows a raw log to an alert through parsing, normalization and enrichment. It designs detections from behavior and ATT&CK techniques, writes and tests Sigma rules on a toy matcher, builds threshold, sequence and correlation rules with sliding windows, applies detection as code, tunes with narrow time-limited exclusions, introduces UEBA, and measures precision, volume, coverage and MTTD.

এই পর্বে যা শিখবেন

  • SIEM-এর সাতটি কাজ, আর একটি raw log কীভাবে alert হয়
  • আচরণ আর ATT&CK technique থেকে detection নকশা করা
  • Sigma rule পড়া, লেখা আর খেলনা matcher-এ মিলিয়ে দেখা
  • Threshold, sequence আর correlation rule, আর sliding window-এর হিসাব
  • Detection as code: version, test, review, deploy
  • মেয়াদসহ ছাড় দিয়ে tuning, আর precision, coverage ও MTTD মাপা

১ · অফিসের রেজিস্টার খাতা থেকে কন্ট্রোল রুম

একটি বড় মার্কেট ভাবুন। গেটের দারোয়ানের খাতা আছে, লকার রুমের আলাদা খাতা, হিসাব বিভাগের আলাদা রেকর্ড, আর CCTV-র ফুটেজ। প্রতিটি খাতায় সত্য লেখা আছে। কিন্তু রাত দুইটায় একজন গেট দিয়ে ঢুকল, পাঁচবার ভুল চাবি দিল, তারপর লকার খুলল, এই গল্পটি কোনো এক খাতায় নেই। গল্পটি পেতে হলে সব খাতা এক টেবিলে এনে সময় ধরে মেলাতে হয়। কন্ট্রোল রুম ঠিক এটাই করে।

SIEM (Security Information and Event Management) নিরাপত্তার সেই কন্ট্রোল রুম। নামটি Gartner-এর বিশ্লেষকদের ২০০৫ সালের দিকের লেখা থেকে ছড়ায় বলে সাধারণত ধরা হয়। পর্ব ৫৫-এ আপনি জেনেছেন কোন log রাখতে হয়। এই পর্বের প্রশ্ন হলো, সেই log থেকে কাজের সতর্কতা কীভাবে বের হবে। Sensor-এর দিক থেকে পর্ব ২৩ একই গল্প বলেছে।

SIEM-এর সাতটি কাজ

From many log sources to alerts and reports, with a tuning loop

SIEM pipeline: log উৎস থেকে alert আর রিপোর্ট Server Firewall Cloud App ১ সংগ্রহ · Collect ২ এক ভাষায় আনা · Normalise ৩ প্রসঙ্গ যোগ · Enrich ৪ রাখা ও খোঁজা · Search ৫ সম্পর্ক মেলানো · Correlate ৬ সতর্কতা · Alert ৭ রিপোর্ট · Report tuning
ছবি ৫৬.১ — ধাপগুলো সরলরেখায় আঁকা, তবে বেগুনি ভাঙা দাগটিই আসল কাজ: ফল দেখে rule ঠিক করা চলতেই থাকে।
কাজসহজ কথায়বাদ পড়লে কী হয়
CollectAgent, syslog বা API দিয়ে log source থেকে ঘটনা আনাযে উৎস আসে না, সেখানে আপনি অন্ধ
Normaliseআলাদা format-কে একই ঘরের নামে আনাএক rule সব উৎসে চলে না
EnrichAsset, মানুষ আর ঠিকানার তথ্য জোড়াকোন alert জরুরি, বোঝা কঠিন
Searchদ্রুত খোঁজা, পুরোনো ঘটনা দেখাতদন্ত ধীর হয়
Correlateবহু ঘটনা মিলিয়ে নকশা ধরাএকক ঘটনার ভিড়ে গল্প হারায়
AlertRule মিললে analyst-কে জানানোসব কিছু পড়ে থাকে, কেউ দেখে না
Reportপরিমাপ, audit আর compliance-এর প্রমাণউন্নতি হচ্ছে কি না জানা যায় না

কোন টুল?

উন্মুক্ত আর বাণিজ্যিক দুই ধরনের পণ্যই আছে। নিচের নামগুলো পরিচিত উদাহরণ, কোনো সুপারিশ নয়। Feature আর দাম সময়ের সাথে বদলায়, তাই নিজের ডেটার পরিমাণ, দলের দক্ষতা আর আইনি চাহিদা দেখে যাচাই করে নিন।

উদাহরণধরনQuery বা rule-এর ভাষা
Wazuhউন্মুক্ত উৎসের, host agent-কেন্দ্রিক প্ল্যাটফর্মনিজের XML-ভিত্তিক rule
Elastic SecurityElastic stack-এর ওপর নিরাপত্তা অংশKQL (Kibana), EQL, ES|QL
Splunkবাণিজ্যিক log প্ল্যাটফর্মSPL
Microsoft SentinelCloud-ভিত্তিক SIEMKQL (Kusto)

সতর্কতা: “KQL” নামটি দুই জায়গায় দুই জিনিস। Kibana Query Language আর Microsoft-এর Kusto Query Language আলাদা ভাষা। এই পর্বে KQL বলতে Kusto বোঝানো হয়েছে।

দায়িত্ব: SIEM-এ মানুষের নাম, ঠিকানা আর কাজের রেকর্ড জমে। তাই এটি নিজেই সংবেদনশীল ব্যবস্থা। Access কম রাখুন, কর্মীদের জন্য লিখিত নীতি রাখুন, মেয়াদ ঠিক করুন। আইনের সীমা জানতে পর্ব ৬ দেখুন। এই পর্বের সব উদাহরণ নিজের system-এর log পড়ার জন্য, আর ঠিকানাগুলো documentation-এর জন্য সংরক্ষিত।

২ · Raw log থেকে alert — normalise আর enrich

নানা ভাষার মানুষ এক ঘরে কথা বললে একজন দোভাষী লাগে। Log-এর অবস্থাও তাই। একটি উৎস লেখে src, আরেকটি clientIP, তৃতীয়টি IpAddress. Parsing লেখা থেকে ঘর আলাদা করে। Normalisation সেই ঘরগুলোকে একটি common schema-য় আনে। Elastic Common Schema (ECS), Microsoft-এর ASIM, Splunk-এর CIM আর উন্মুক্ত OCSF এই চেষ্টারই উদাহরণ। সবার নকশা আলাদা, তাই এক schema-র rule অন্যটিতে সরাসরি না-ও চলতে পারে।

নিচের stepper একটি মাত্র log লাইন অনুসরণ করে। পরিবেশটি কাল্পনিক: একটি অনলাইন বাজারের নিজের server web1.

  1. Rawsshdআসল লাইন
  2. Parsefieldsঘর আলাদা
  3. Normaliseschemaএক ভাষা
  4. Enrichcontextপ্রসঙ্গ
  5. Correlateruleনকশা মিলল
  6. Alerttriageমানুষের কাছে
Raw logবছর আর সময় অঞ্চল নেই
Oct 31 10:02:12 web1 sshd[820]: Failed password
for sadia from 203.0.113.7 port 40120 ssh2
Parseলেখা থেকে ঘর
time    = Oct 31 10:02:12
host    = web1
process = sshd
result  = Failed password
account = sadia
from    = 203.0.113.7
NormaliseCommon schema, সময় UTC-তে
{
  "event_time": "2026-10-31T04:02:12Z",
  "event_category": "authentication",
  "outcome": "failure",
  "user": "sadia",
  "src_ip": "203.0.113.7",
  "host": "web1",
  "app": "sshd"
}
Enrichযা log-এ ছিল না
host_owner    payments-team
host_crit     high
internet      true
user_dept     finance
privileged    false
mfa           false
src_internal  false
Correlatesrc_ip = 203.0.113.7
10:02:01-10:02:12
failure × 6  (limit 5 in 5m)
10:02:15
success  user=sadia
rule matched: failed logons then success
AlertTriage-এর সারিতে
title:    Failed logons then success
severity: high
entity:   user=sadia host=web1
why:      6 failures, then success, no MFA
owner:    payments-team
runbook:  RB-017
  1. Raw log. web1-এর sshd একটি লাইন লিখল। এটি মানুষ পড়তে পারে। কিন্তু খেয়াল করুন, এতে বছর নেই, সময় অঞ্চলও নেই।
  2. Parse. Parser লেখা ভেঙে ঘর বের করল। এই ধাপ ভঙ্গুর। Software update-এ লাইনের গঠন বদলালে parser চুপচাপ ব্যর্থ হতে পারে, আর rule আর কিছু পাবে না।
  3. Normalise. ঘরের নাম হলো schema-র নাম, ফলাফল হলো failure. স্থানীয় সময় ধরা হলো UTC+6, তাই 10:02:12 হলো 04:02:12Z. ভুল সময় অঞ্চল ধরলে পুরো correlation নষ্ট হয়।
  4. Enrich. Enrichment যোগ করল যা log-এ ছিল না: server-টি গুরুত্বপূর্ণ, internet থেকে দেখা যায়, মালিক payments-team, আর sadia-র হিসাবে MFA নেই। একই ব্যর্থ login এখন অনেক বেশি অর্থবহ।
  5. Correlate. SIEM একই src_ip-এর ঘটনা সময় ধরে মেলাল: ৬টি ব্যর্থতা, সীমা ৫, আর ১০:০২:১৫-এ সফল login. Correlation rule মিলে গেল।
  6. Alert. Alert-এ শুধু “কিছু ঘটেছে” লেখা নেই। আছে severity high, কোন হিসাব, কোন যন্ত্র, কেন সন্দেহ, মালিক কে, আর runbook নম্বর। এবার triage শুরু হবে। এটি শুধু একটি সন্দেহ, প্রমাণিত হামলা নয়।

Context কেন নিয়ম বদলে দেয়

একই ঘটনা ভিন্ন প্রসঙ্গে ভিন্ন অর্থ বহন করে। রাত তিনটায় ব্যাংকের লকার রুমে ঢোকা আর দুপুরে ঢোকা এক কথা নয়। একই ভাবে, asset inventory আর identity তথ্য alert-কে ওপরে বা নিচে নামাতে পারে।

প্রসঙ্গউৎসকীভাবে কাজে লাগে
Asset-এর গুরুত্ব ও মালিকAsset inventoryPayment server-এ severity বাড়ে, alert সঠিক দলে যায়
মানুষ ও ভূমিকাHR বা directoryPrivileged account বা ছুটিতে থাকা কর্মীর login বেশি গুরুত্ব পায়
Service account কি নাIdentity তালিকাService account-এর হঠাৎ interactive login সন্দেহজনক
ঠিকানার ধরনIP পরিকল্পনা, threat intelনিজের scanner না বাইরের অচেনা ঠিকানা, তা পরিষ্কার হয়
পরিবর্তনের সময়Change ticketঅনুমোদিত কাজের সময়ের alert আলাদা করে দেখা যায়

Enrichment সঠিক থাকলেই কাজে লাগে। পুরোনো asset তালিকা ভুল আত্মবিশ্বাস দেয়। আরেকটি বিষয়, যে সময় ঘটনা ঘটেছে সেই সময়ের তথ্য জুড়তে হয়। DHCP-তে একটি IP আজ এক যন্ত্রের, কাল আরেকটির হতে পারে। Threat intelligence-এর ব্যবহার পর্ব ৫৭-তে আসবে।

খোঁজার দুটি উদাহরণ

একই প্রশ্ন, দুই ভাষায়: “গত ১ দিনে কোন উৎস থেকে সবচেয়ে বেশি ব্যর্থ login?” টেবিল ও field-এর নাম কাল্পনিক, আর এই query কোনো আসল SIEM-এ চালানো হয়নি। ধরন বোঝার জন্য দেখুন।

index=auth action=failure earliest=-1d
| stats count AS failures dc(user) AS users BY src_ip
| sort - failures
| head 10
auth_events
| where timestamp > ago(1d) and outcome == "failure"
| summarize failures = count(), users = dcount(user) by src_ip
| top 10 by failures desc

৩ · Detection লেখা শুরু হয় আচরণ থেকে

পাড়ায় চুরি ঠেকাতে একজন চোরের ছবি টাঙানো সহজ। কিন্তু সে মাথা কামিয়ে এলে ছবি কাজ করে না। তার চেয়ে টেকসই নিয়ম হলো, “রাতে কেউ বন্ধ দোকানের তালায় হাত দিলে বাঁশি।” পর্ব ৫-এর pyramid of pain-এর কথাও এটাই। IP বা hash বদলানো সহজ, কাজের ধরন বদলানো কঠিন। তাই ভালো detection engineering শুরু হয় আচরণ থেকে, পণ্য বা IP তালিকা থেকে নয়।

  1. প্রশ্ন: কোন ক্ষতি নিয়ে চিন্তিত? পর্ব ৪-এর threat model বা আগের ঘটনা থেকে শুরু করুন।
  2. আচরণ: এক বাক্যে লিখুন। যেমন, “একটি উৎস অনেকবার login-এ ব্যর্থ হয়ে তারপর সফল হলো।”
  3. Technique: MITRE ATT&CK-এ মেলান। এই আচরণের কাছাকাছি হলো T1110 (Brute Force), যার tactic Credential Access. T1110.001 Password Guessing, T1110.003 Password Spraying আর T1110.004 Credential Stuffing এর sub-technique. সংখ্যা পরে বদলাতে পারে, তাই attack.mitre.org-এ মিলিয়ে নিন।
  4. Log: কোন log-এ এটি দেখা যায়, আর সেই log কি সত্যিই SIEM-এ আসছে? না এলে rule লিখে লাভ নেই, আগে log আনুন। এটি একটি detection gap.
  5. স্বাভাবিক কাজ: নিরীহ কোন কাজ দেখতে একই রকম? ভুল password দেওয়া মানুষ, scanner, পুরোনো password-ওয়ালা script.
  6. সাড়া: Alert এলে analyst কী করবেন? উত্তর না থাকলে rule-টি এখনো প্রস্তুত নয়।

ATT&CK-এর detection strategy অংশে T1110-এর জন্য “অনেক ব্যর্থতার পরে সাফল্য” ধরনের analytic-এর বর্ণনা আছে। প্রকাশিত rule হুবহু না নিয়ে এগুলোকে শুরুর চিন্তা হিসেবে নিন, কারণ আপনার log আর স্বাভাবিক আচরণ আলাদা।

৪ · Sigma — log-এর জন্য একটি সাধারণ rule ভাষা

প্রতিটি SIEM-এর নিজের query ভাষা। একটি ভালো rule SPL-এ লিখলে KQL-এ আবার লিখতে হয়। Sigma এই সমস্যার উত্তর। এটি YAML-এ লেখা, পণ্য-নিরপেক্ষ একটি rule format. ধারণাটি হলো, network-এর জন্য Snort rule বা ফাইলের জন্য YARA যা, log-এর জন্য Sigma তাই। একটি converter পরে rule-কে নির্দিষ্ট SIEM-এর query-তে রূপান্তর করে। প্রকল্পটির শুরু ২০১৭ সালের দিকে, Florian Roth আর Thomas Patzke-র নাম এর সাথে জড়িত। এখন sigmahq.io-তে specification আছে, যার v2.1.0 প্রকাশিত হয় ২০২৫ সালের আগস্টে।

নিচের rule একটি সাধারণ আচরণ দেখে: Windows-এর স্থানীয় Administrators দলে কেউ নতুন সদস্য যোগ করল। Event ID 4732 এই ঘটনা লেখে, আর S-1-5-32-544 হলো Administrators দলের পরিচিত SID. ATT&CK-এ T1098 (Account Manipulation) technique এর কাছাকাছি। পর্ব ৩১-এ এই log-এর জগৎ দেখেছেন।

title: Member Added to Local Administrators Group
id: 6f1c2d3e-8a47-4b19-9e52-7c0d4a1b3f60
status: test
description: Detects a user added to the local Administrators group on a Windows host.
references:
  - https://attack.mitre.org/techniques/T1098/
author: ABCL TECH course example
date: 2026-10-31
tags:
  - attack.persistence
  - attack.privilege-escalation
  - attack.t1098
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4732
    TargetSid: S-1-5-32-544
  filter_machine:
    SubjectUserName|endswith: '$'
  filter_deploy:
    SubjectUserName:
      - svc-deploy
      - svc-patch
    Computer|startswith: build-
  condition: selection and not 1 of filter_*
falsepositives:
  - Approved administrator onboarding
level: medium
অংশঅর্থ
title, descriptionRule-এর নাম আর সংক্ষিপ্ত ব্যাখ্যা। title বাধ্যতামূলক
idএলোমেলো UUID version 4, যা rule-কে স্থায়ী পরিচয় দেয়
statusstable, test, experimental, deprecated বা unsupported
tagsATT&CK-এর tactic ও technique, যেমন attack.t1098
logsourceকোন log দেখবে: product, service, category. বাধ্যতামূলক
detectionএক বা একাধিক নামযুক্ত শর্ত, আর তাদের জোড়া condition. বাধ্যতামূলক
falsepositivesকোন নিরীহ কাজে এটি মিলতে পারে, যা analyst-এর কাজে লাগে
levelinformational, low, medium, high বা critical
Detection অংশের তিন নিয়ম
  • Map-এর ভেতরের field-গুলো AND: সবগুলো মিলতে হবে।
  • একটি field-এর list-এর মানগুলো OR: যেকোনো একটি মিললেই হলো।
  • Modifier: |contains, |startswith, |endswith, |re, |cidr মেলানোর ধরন বদলায়। সাধারণ string তুলনায় হরফের ছোট-বড় ধরা হয় না।

নিচের কোড আসল Sigma engine নয়। এটি একই rule-এর ছোট রূপ, যা স্মৃতির ভেতরের ৬টি বানানো event-এ চলে। AND, OR আর not কীভাবে ফল ঠিক করে, এখানে হাতে-কলমে দেখুন।

// Toy Sigma-like matcher on in-memory events. Not a real Sigma engine.
const rule = {
  selection: { EventID: 4732, TargetSid: 'S-1-5-32-544' },
  filter_machine: { 'SubjectUserName|endswith': '$' },
  filter_deploy: { SubjectUserName: ['svc-deploy', 'svc-patch'],
                   'Computer|startswith': 'build-' },
};  // condition: selection and not 1 of filter_*

const events = [
  { id: 'e1', EventID: 4732, TargetSid: 'S-1-5-32-544', SubjectUserName: 'rahim', Computer: 'pay-db1' },
  { id: 'e2', EventID: 4732, TargetSid: 'S-1-5-32-544', SubjectUserName: 'PAY-DB1$', Computer: 'pay-db1' },
  { id: 'e3', EventID: 4732, TargetSid: 'S-1-5-32-544', SubjectUserName: 'SVC-DEPLOY', Computer: 'build-07' },
  { id: 'e4', EventID: 4732, TargetSid: 'S-1-5-32-544', SubjectUserName: 'svc-deploy', Computer: 'pay-db1' },
  { id: 'e5', EventID: 4732, TargetSid: 'S-1-5-32-555', SubjectUserName: 'rahim', Computer: 'pay-db1' },
  { id: 'e6', EventID: 4624, TargetSid: '-', SubjectUserName: 'rahim', Computer: 'pay-db1' },
];

const low = v => String(v).toLowerCase();       // Sigma strings ignore case by default
const ops = {
  eq: (a, b) => low(a) === low(b),
  endswith: (a, b) => low(a).endsWith(low(b)),
  startswith: (a, b) => low(a).startsWith(low(b)),
};
function matchMap(map, ev) {                    // keys are AND, list values are OR
  return Object.entries(map).every(([key, want]) => {
    const [field, op = 'eq'] = key.split('|');
    return [].concat(want).some(w => field in ev && ops[op](ev[field], w));
  });
}
for (const ev of events) {
  const sel = matchMap(rule.selection, ev);
  const fm = matchMap(rule.filter_machine, ev), fd = matchMap(rule.filter_deploy, ev);
  const alert = sel && !(fm || fd);
  console.log(`${ev.id} selection=${sel} filter_machine=${fm} filter_deploy=${fd} -> ${alert ? 'ALERT' : 'no alert'}`);
}

e1 … -> ALERT: rahim একজন মানুষ, কোনো filter মেলেনি। e2 বাদ, কারণ PAY-DB1$ $ দিয়ে শেষ, অর্থাৎ machine account. e3 বাদ, কারণ filter_deploy-এর দুটি শর্তই মিলেছে, আর SVC-DEPLOY বড় হাতের হলেও মিলেছে। সবচেয়ে শিক্ষণীয় e4: একই svc-deploy এবার pay-db1-এ, তাই filter_deploy=false আর ALERT. ছাড়ে যন্ত্রের শর্ত থাকায় deploy account চুরি হলে payment server-এ ধরা পড়বে। e5 অন্য দল, e6 অন্য Event ID, তাই selection=false.

এবার পুরো পথটি দেখুন। একটি rule কীভাবে ধারণা থেকে deploy পর্যন্ত যায়, আর test কোথায় ফল বদলে দেয়।

আচরণএক বাক্যে
একজন মানুষ বা account একটি server-এর
Administrators দলে নতুন সদস্য যোগ করল।
মানচিত্রTechnique আর log
ATT&CK : T1098 Account Manipulation
Log    : Windows Security, EventID 4732
যাচাই  : group-change audit চালু আছে কি?
         log SIEM-এ আসছে কি?  হ্যাঁ
প্রথম খসড়াv1, কোনো filter নেই
selection:
  EventID: 4732
  TargetSid: S-1-5-32-544
condition: selection
Test v1৬টি নমুনা event
e1 rahim        pay-db1  ALERT  ঠিক
e2 PAY-DB1$     pay-db1  ALERT  ভুল
e3 SVC-DEPLOY   build-07 ALERT  ভুল
e4 svc-deploy   pay-db1  ALERT  ঠিক
e5, e6                   নীরব   ঠিক
ফল: ৪টি alert, ২টি ভুল
Test v2সরু filter যোগ হলো
condition: selection and not 1 of filter_*

e1 ALERT, e4 ALERT
e2, e3, e5, e6 নীরব
ফল: ২টি alert, ০টি ভুল
Deployধীরে ধীরে
pull request → review → test চলল → merge
status: test
দুই সপ্তাহ shadow mode: শুধু গণনা, কাউকে ডাকা নয়
ফল ভালো হলে: alert চালু, পরে status: stable
  1. আচরণ লেখা। কোনো পণ্য বা IP নয়, শুধু কাজের বর্ণনা। এক বাক্যে লিখতে না পারলে rule-ও অস্পষ্ট হবে।
  2. মানচিত্র আর log. Technique T1098, log হলো Event ID 4732. Audit চালু না থাকলে এই ঘটনা লেখাই হবে না, তাই আগে এটি যাচাই করা হলো। এবার উত্তর “হ্যাঁ”।
  3. প্রথম খসড়া। সবচেয়ে সরল শর্ত দিয়ে শুরু। এতে ধরা পড়ার ক্ষমতা সর্বোচ্চ, কিন্তু নিরীহ কাজও মিলবে।
  4. Test v1. ৬টি নমুনায় ৪টি alert. e2 আর e3 অনুমোদিত স্বয়ংক্রিয় কাজ, তাই ভুল। এখানে precision ২ ÷ ৪ = ৫০%.
  5. Test v2. Machine account আর build যন্ত্রের deploy account বাদ দেওয়া হলো। ২টি alert, দুটিই ঠিক। ওপরের কোডের আউটপুটও ঠিক এই ফল দেয়।
  6. Deploy. অন্য একজন review করলেন। Rule প্রথমে shadow mode-এ চলল, যেখানে ফল গোনা হয় কিন্তু analyst-কে ডাকা হয় না। দুই সপ্তাহের ফল ভালো হলে alert চালু হবে। “stable” পরে আসে, শুরুতেই নয়।

Sigma জাদু নয়: Rule-এর TargetSid নামটি আপনার SIEM-এ অন্য নামে থাকতে পারে। Converter-এর field mapping ভুল হলে query ঠিকই চলবে, শুধু কিছু মিলবে না। আর নীরব rule দেখতে ভালো rule-এর মতোই। তাই রূপান্তরের পরে একটি নিরীহ পরীক্ষামূলক event দিয়ে দেখুন rule সত্যিই alert দেয় কি না।

৫ · Threshold, sequence আর correlation

একবার ভুল password দেওয়া কিছুই প্রমাণ করে না। মোবাইল ব্যাংকিংয়ের PIN আপনিও ভুল দিয়েছেন। সন্দেহ জন্মায় সংখ্যা, ক্রম আর সম্পর্ক থেকে। তাই detection rule-এর তিনটি বড় ধরন আছে।

একক ঘটনা

একটি event মিললেই alert. যেমন Administrators দলে নতুন সদস্য।

Sigma detection rule

Threshold

নির্দিষ্ট সময়ে গণনা সীমা ছাড়াল। যেমন ৫ মিনিটে একটি উৎস থেকে ≥ ৫ ব্যর্থতা।

event_count, value_count

Sequence

ঘটনা নির্দিষ্ট ক্রমে ঘটল। যেমন ব্যর্থতার ঝাঁক, তারপর সাফল্য।

temporal_ordered

বহু উৎসের correlation

আলাদা উৎসের ঘটনা একই হিসাব বা যন্ত্রে মিলল। যেমন VPN login, তারপর নতুন admin.

temporal, field aliases

কী ধরে গুনবেন: group-by

একই ব্যর্থ login, কিন্তু কিসের ভিত্তিতে গোনা হচ্ছে তার ওপর ধরা পড়ে ভিন্ন আচরণ।

গণনাযা ধরেযা ফসকায়
এক উৎস, মোট ব্যর্থতাএক ঠিকানা থেকে ঝাঁকে ঝাঁকে চেষ্টা (brute force)বহু ঠিকানায় ছড়ানো চেষ্টা
এক উৎস, ভিন্ন account-এর সংখ্যাPassword spraying বা credential stuffing-এর ধরনএকটি account-এ কেন্দ্রীভূত চেষ্টা
এক account, ভিন্ন উৎসের সংখ্যাএকটি হিসাবের ওপর বহু ঠিকানা থেকে চেষ্টাবহু account-এ ছড়ানো চেষ্টা

তাই একটি rule যথেষ্ট নয়, কয়েকটি rule একে অপরের ফাঁক ঢাকে। প্রতিরোধও আলাদা থাকুক: পর্ব ১৫-এর login throttling আর পর্ব ১৬-এর MFA অনেক ঘটনা আগেই থামায়। Detection সেগুলোর বিকল্প নয়।

Fixed bin বনাম sliding window

অনেকে সময়কে ০–৫, ৫–১০ মিনিটের বাক্সে ভাগ করে গোনেন। সহজ, কিন্তু একটি ফাঁদ আছে। ঝাঁকটি বাক্সের সীমায় পড়লে দুই ভাগ হয়ে যায়, আর দুই বাক্সই সীমার নিচে থাকে। Sliding window প্রতিটি নতুন event-এ ঠিক আগের ৫ মিনিট দেখে, তাই এই ফাঁদ নেই। নিচের ছবিতে সীমা ৫।

একই ৬টি ব্যর্থতা, দুই রকম হিসাব

A burst split by a fixed 5-minute bin, caught by a sliding window

Fixed bin আর sliding window-এর তুলনা বাক্স ১: ৪টি বাক্স ২: ২টি 240290310330345 300s Sliding: ৫টি → alert ব্যর্থতা সাফল্য 310s-এ alert
ছবি ৫৬.২ — বাক্সের সীমা ৩০০ সেকেন্ডে ঝাঁকটিকে দুই ভাগ করেছে, তাই কোনো বাক্সেই alert নেই। Sliding window-তে সেই সীমার কোনো অর্থ নেই।

নিচের কোডে সময় বানানো, এক সেকেন্ডও সত্যি অপেক্ষা হয় না। তিনটি উৎসের ব্যবহার তিন রকম। সীমা ৫ মিনিটে ৫টি, আর সাফল্য ১০ মিনিটের মধ্যে এলে গুরুতর।

// Toy login events with a simulated clock (t = seconds). No network, no real logs.
const F = 'fail', S = 'success';
const row = (src, type, ...ts) => ts.map(t => ({ t, src, type }));
const events = [
  ...row('203.0.113.7', F, 240, 250, 270, 290, 310, 330), ...row('203.0.113.7', S, 345),
  ...row('198.51.100.20', F, 100, 400, 700, 1000), ...row('198.51.100.20', S, 1010),
  ...row('198.51.100.30', F, 60, 70, 80, 90, 100, 110),
].sort((a, b) => a.t - b.t);
const WINDOW = 300, LIMIT = 5, THEN = 600;     // 5 minutes, 5 failures, 10 minutes

function fixedBins(src) {                      // 0-299, 300-599, ...
  const bins = {};
  for (const e of events) if (e.src === src && e.type === F) {
    const b = Math.floor(e.t / WINDOW); bins[b] = (bins[b] || 0) + 1;
  }
  return Math.max(...Object.values(bins));
}
function sliding(src) {
  const fails = []; let fired = null, success = null;
  for (const e of events) {
    if (e.src !== src) continue;
    if (e.type === S && fired !== null && e.t - fired <= THEN) { success = e.t; break; }
    if (e.type !== F) continue;
    fails.push(e.t);
    while (e.t - fails[0] >= WINDOW) fails.shift();  // drop what left the window
    if (fails.length >= LIMIT && fired === null) fired = e.t;
  }
  return { fired, success };
}
for (const src of [...new Set(events.map(e => e.src))]) {
  const { fired, success } = sliding(src), maxBin = fixedBins(src);
  const verdict = success ? 'HIGH: fail burst then success' : fired ? 'MEDIUM: fail burst only' : 'no alert';
  console.log(`${src} max_in_fixed_bin=${maxBin} fixed=${maxBin >= LIMIT ? 'alert' : 'none'} ` +
              `sliding_fired_at=${fired} success_at=${success} -> ${verdict}`);
}

203.0.113.7-এর লাইনে max_in_fixed_bin=4 fixed=none, অর্থাৎ বাক্সের হিসাবে এটি ফসকে যেত। Sliding window-তে sliding_fired_at=310, আর success_at=345 তাই ফল HIGH. 198.51.100.30 দ্রুত ৬ বার ব্যর্থ, কিন্তু সাফল্য নেই, তাই MEDIUM. কম গুরুতর, তবে দেখার মতো। 198.51.100.20-এর ব্যর্থতা ৫ মিনিটে একটি করে, তাই no alert. এই ফলটি একই সাথে ভালো ও বিপজ্জনক: সাধারণ মানুষের ধীর ভুল এখানে শান্ত থাকে, কিন্তু ধীরে চলা ক্ষতিকর চেষ্টাও এই rule-এ ধরা পড়ে না। সে জন্য লম্বা সময়ের আলাদা rule লাগে।

SIEM ছাড়াই: awk দিয়ে গণনা

ছোট server-এ SIEM না থাকলেও ধারণাটি কাজে লাগে। নিচের Bash block কোনো আসল log পড়ে না, নিজের ভেতরে বানানো নমুনা পড়ে। নিজের system-এ একই কৌশল চালাতে পারেন, যেখানে log পড়ার অনুমতি আপনার আছে। পর্ব ৩০-এ auth log-এর জায়গা দেখেছেন।

# Fixed sample built here; only addresses reserved for documentation.
awk -v limit=5 '
{
  for (i = 1; i <= NF; i++) if ($i == "from") { src = $(i + 1); user = $(i - 1) }
  if ($0 ~ /Failed password/) {
    fails[src]++
    if (!((src, user) in seen)) { seen[src, user] = 1; users[src]++ }
  } else if ($0 ~ /Accepted password/ && fails[src] >= limit) {
    after[src] = user
  }
}
END {
  for (s in fails) {
    level = "ok"
    if (fails[s] >= limit) level = "MEDIUM"
    if (s in after) level = "HIGH success_for=" after[s]
    printf "%-15s failed=%-2d users=%d %s\n", s, fails[s], users[s], level
  }
}' <<'LOG' | sort -t= -k2,2nr
Oct 31 10:01:02 web1 sshd[812]: Failed password for rahim from 198.51.100.20 port 51022 ssh2
Oct 31 10:01:40 web1 sshd[812]: Accepted password for rahim from 198.51.100.20 port 51022 ssh2
Oct 31 10:02:01 web1 sshd[820]: Failed password for admin from 203.0.113.7 port 40110 ssh2
Oct 31 10:02:03 web1 sshd[820]: Failed password for invalid user test from 203.0.113.7 port 40112 ssh2
Oct 31 10:02:05 web1 sshd[820]: Failed password for sadia from 203.0.113.7 port 40114 ssh2
Oct 31 10:02:07 web1 sshd[820]: Failed password for sadia from 203.0.113.7 port 40116 ssh2
Oct 31 10:02:09 web1 sshd[820]: Failed password for sadia from 203.0.113.7 port 40118 ssh2
Oct 31 10:02:12 web1 sshd[820]: Failed password for sadia from 203.0.113.7 port 40120 ssh2
Oct 31 10:02:15 web1 sshd[820]: Accepted password for sadia from 203.0.113.7 port 40122 ssh2
Oct 31 10:03:00 web1 sshd[830]: Failed password for invalid user oracle from 198.51.100.30 port 40200 ssh2
Oct 31 10:03:02 web1 sshd[830]: Failed password for invalid user guest from 198.51.100.30 port 40202 ssh2
Oct 31 10:03:04 web1 sshd[830]: Failed password for invalid user demo from 198.51.100.30 port 40204 ssh2
Oct 31 10:03:06 web1 sshd[830]: Failed password for invalid user web from 198.51.100.30 port 40206 ssh2
Oct 31 10:03:08 web1 sshd[830]: Failed password for invalid user ftp from 198.51.100.30 port 40208 ssh2
LOG

203.0.113.7 পেয়েছে failed=6 users=3 আর HIGH success_for=sadia. 198.51.100.30-এ failed=5 কিন্তু users=5, যা spraying-এর ধরনের মতো দেখায়, আর সাফল্য নেই, তাই MEDIUM. 198.51.100.20 একবার ভুল করে ঢুকেছে: ok. কোডে from শব্দটি খুঁজে তার আগের-পরের ঘর নেওয়া হয়েছে, কারণ invalid user থাকলে লাইনের গঠন বদলায়। নির্দিষ্ট ঘরের নম্বর ধরে লিখলে ভুল হতো, যা parser-এর ভঙ্গুরতার ছোট উদাহরণ। এই script-এ সময়ের সীমা নেই, পুরো ফাইল গোনে। আর username-এর ঘরটি বাইরে থেকে আসা লেখা, তাই log injection মাথায় রাখুন।

৬ · Mini detection — অনেক ব্যর্থ login, তারপর সাফল্য

এবার সব জোড়া দিই। লক্ষ্য একটি rule: একটি উৎস থেকে ৫ মিনিটে ≥ ৫ ব্যর্থ network login, তারপর ১০ মিনিটের মধ্যে একই উৎস থেকে সফল login. Sigma-তে এটি চারটি অংশ: দুটি base rule, একটি গণনা, আর একটি ক্রম। correlation অংশের নাম আর ঘর sigmahq.io-র specification মিলিয়ে লেখা। এখানে logon type 3 হলো network, 10 হলো Remote Desktop.

title: Failed Network Logon
name: failed_network_logon
id: 2b7e9a14-53c6-4d0f-a1e8-96f3c27d5b41
status: test
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4625
    LogonType:
      - 3
      - 10
  filter_local:
    IpAddress: '-'
  condition: selection and not filter_local
level: low
---
title: Successful Network Logon
name: successful_network_logon
id: c94d1f07-2ea3-4b68-8d15-3a70e6b2f9c8
status: test
logsource:
  product: windows
  service: security
detection:
  selection:
    EventID: 4624
    LogonType:
      - 3
      - 10
  filter_local:
    IpAddress: '-'
  condition: selection and not filter_local
level: low
---
title: Many Failed Logons From One Source
name: many_failed_logons
id: 81a6e3d2-7f04-4c5b-b2d9-05e4c8f1a763
status: test
correlation:
  type: event_count
  rules:
    - failed_network_logon
  group-by:
    - IpAddress
  timespan: 5m
  condition:
    gte: 5
level: medium
---
title: Failed Logons Followed by Success From One Source
id: 3e05b7c9-d162-4a8e-9f37-b18c6d24e05a
status: test
description: Five or more failed logons from a source, then a success from the same source.
author: ABCL TECH course example
date: 2026-10-31
tags:
  - attack.credential-access
  - attack.t1110
correlation:
  type: temporal_ordered
  rules:
    - many_failed_logons
    - successful_network_logon
  group-by:
    - IpAddress
  timespan: 10m
falsepositives:
  - A user who mistypes a password many times, then succeeds
  - A shared NAT or VPN address used by many people
level: high

লক্ষ করুন, group-by লেখা হয় hyphen দিয়ে, আর many_failed_logons নিজেই একটি correlation, যাকে শেষ অংশ ব্যবহার করছে। Spraying-এর জন্য একই base rule-এ আলাদা একটি গণনা জোড়া যায়:

title: One Source Fails Against Many Accounts
id: a4d82f6c-19b7-4e03-8c5a-f27e01d6b394
status: test
correlation:
  type: value_count
  rules:
    - failed_network_logon
  group-by:
    - IpAddress
  timespan: 15m
  condition:
    field: TargetUserName
    gte: 10
level: medium

কতটা যাচাই হয়েছে: এই পর্বের Sigma rule-গুলো YAML হিসেবে parse করা হয়েছে, আর ২০২৬ সালের অক্টোবরে Sigma-র প্রকাশিত JSON schema-র সাথে মেলানো হয়েছে (একটি ছোট নিজস্ব script দিয়ে)। কোনোটিই sigma-cli দিয়ে রূপান্তর করা হয়নি, কোনো SIEM-এও চালানো হয়নি। সব SIEM-এর converter সব correlation ধরন সমর্থন করে না, তাই নিজের lab-এ পরীক্ষা করে নিন।

যে SIEM-এ Sigma correlation নেই, সেখানে একই ধারণা তার নিজের ভাষায় লিখতে হয়। নিচের দুটি কাল্পনিক টেবিলের ওপর লেখা, চালানো হয়নি।

index=auth (action=failure OR action=success)
| sort 0 _time
| streamstats time_window=5m count(eval(action="failure")) AS failures BY src_ip
| where action="success" AND failures>=5
| table _time src_ip user failures
let bursts = auth_events
  | where outcome == "failure"
  | summarize failures = count(), last_fail = max(timestamp)
      by src_ip, bin(timestamp, 5m)
  | where failures >= 5;
bursts
| join kind=inner (
    auth_events
    | where outcome == "success"
    | project src_ip, user, success_time = timestamp
  ) on src_ip
| where success_time between (last_fail .. (last_fail + 10m))
| project src_ip, user, failures, last_fail, success_time

দুটি উদাহরণে পার্থক্যটি খেয়াল করুন। SPL-ধাঁচের query-তে streamstats চলন্ত সময়ের জানালা নেয়, আর সাফল্যের ঠিক আগের ৫ মিনিট গোনে। KQL-ধাঁচের query bin() ব্যবহার করে, তাই ওপরের ছবির fixed bin-এর ফাঁদ এখানে রয়ে গেছে। সহজে পড়ার জন্য query-টি এমন রাখা হয়েছে। বাস্তবে ছোট ধাপের বাক্স বা পণ্যের নিজস্ব sequence সুবিধা দেখুন, যেমন Elastic-এর EQL-এ আলাদা sequence query আছে।

এই rule কী ধরে না

  • ধীর চেষ্টা: ঘণ্টায় একটি ব্যর্থতা সীমা ছোঁয় না।
  • ছড়ানো উৎস: চেষ্টা বহু ঠিকানা থেকে এলে IpAddress ধরে গোনা কাজ করে না।
  • এক চেষ্টায় সঠিক password: Password আগেই ফাঁস হলে ব্যর্থতাই থাকে না। তার জন্য লাগে impossible travel-এর মতো আচরণের সংকেত।
  • ভাগ করা ঠিকানা: এক NAT বা VPN-এর পেছনে বহু মানুষ থাকলে নিরীহ ঘটনাও মিলতে পারে।

৭ · Detection as code — version, test, review, deploy

কেউ যদি SIEM-এর screen-এ সরাসরি rule বদলায়, ছয় মাস পরে কেউ জানবে না কে, কেন, কী বদলেছিল। Detection as code-এর ধারণা সহজ: rule-কে সফটওয়্যারের মতো সামলানো। পর্ব ৪৯-এর test-এর শৃঙ্খলা এখানেও খাটে।

ধাপকী হয়কেন
VersionRule আর ছাড়ের ফাইল repository-তে থাকেইতিহাস, কারণ আর ফিরে যাওয়ার পথ
ValidateSyntax, বাধ্যতামূলক ঘর আর tag স্বয়ংক্রিয় যাচাইটাইপের ভুল আগেই ধরা পড়ে
Testনমুনা event-এ চালানো: যা ধরার কথা ধরল কি, যা না ধরার কথা ধরল কিনতুন বদল পুরোনো ক্ষমতা নষ্ট করল কি না, দেখা যায়
ReviewPull request-এ অন্য একজন পড়েনলেখকের অন্ধ জায়গা ধরা পড়ে
DeployCI/CD দিয়ে, প্রথমে shadow modeহাতের ভুল কমে, ধীরে চালু হয়
MonitorVolume, precision আর “rule নীরব কেন” দেখাLog বন্ধ হলে rule চুপচাপ মরে যায়

Test-এর মূল অংশ হলো নমুনা event: কয়েকটি আসল ধরনের (true positive) আর কয়েকটি নিরীহ কিন্তু কাছাকাছি ধরনের (false positive) ঘটনা। প্রতিবার ভুল ধরা পড়লে একটি নতুন নমুনা যোগ করুন। এটি একটি regression test. নিচের harness একটি ইচ্ছাকৃত দুর্বল খসড়া আর তার সংশোধিত রূপ পরীক্ষা করে।

// Detection test harness. Toy rule, toy events, simulated day number. Run before every deploy.
const seq = (src, failTimes, okTime) => [...failTimes.map(t => ({ t, src, ok: false })), { t: okTime, src, ok: true }];
const exceptions = [{ src: '198.51.100.5', owner: 'secops', ticket: 'SEC-2041', expiresDay: 30 }];

function ruleV1(evs, day) {                    // first draft
  return evs.filter(e => !e.ok).length > 5 && evs.some(e => e.ok);
}
function ruleV2(evs, day) {                    // fixed after the tests failed
  if (exceptions.some(x => x.src === evs[0].src && day < x.expiresDay)) return false;
  const seen = [];
  for (const e of [...evs].sort((a, b) => a.t - b.t)) {
    if (e.ok && seen.length >= 5) return true;
    if (!e.ok) { seen.push(e.t); while (e.t - seen[0] >= 300) seen.shift(); }
  }
  return false;
}
const tests = [
  ['TP burst then success', seq('203.0.113.7', [0, 5, 10, 15, 20, 25], 30), 1, true],
  ['TP exactly 5 failures', seq('203.0.113.8', [0, 5, 10, 15, 20], 30), 1, true],
  ['FP user mistypes 3 times', seq('198.51.100.20', [0, 20, 40], 60), 1, false],
  ['FP success came first', seq('198.51.100.21', [5, 6, 7, 8, 9, 10], 0), 1, false],
  ['FP slow, 1 fail per 10 min', seq('198.51.100.22', [0, 600, 1200, 1800, 2400, 3000], 3010), 1, false],
  ['FP scanner, exception active', seq('198.51.100.5', [0, 1, 2, 3, 4, 5], 6), 12, false],
  ['TP scanner, exception expired', seq('198.51.100.5', [0, 1, 2, 3, 4, 5], 6), 31, true],
];
function run(name, rule) {
  const failed = tests.filter(([, evs, day, want]) => rule(evs, day) !== want);
  console.log(`--- ${name}`);
  failed.forEach(([label, , , want]) => console.log(`FAIL ${label}: expected ${want}`));
  const ok = tests.length - failed.length;
  console.log(`${ok}/${tests.length} passed -> ${failed.length ? 'do not deploy' : 'ready for review'}`);
}
run('rule v1', ruleV1);
run('rule v2', ruleV2);

rule v1-এর ফল 3/7 passed -> do not deploy. চারটি ব্যর্থতা চারটি আলাদা শিক্ষা। TP exactly 5 failures: > লেখা হয়েছিল, দরকার >=. FP success came first: ক্রম দেখা হয়নি। FP slow, 1 fail per 10 min: সময়ের জানালা নেই। FP scanner, exception active: ছাড়ের তালিকা পড়াই হয়নি। rule v2 দেয় 7/7 passed -> ready for review. শেষ test-টি লক্ষ করুন: মেয়াদ ৩০তম দিনে শেষ, তাই ৩১তম দিনে scanner আবার alert দেয়, আর সেটিই প্রত্যাশিত। “Review-এর জন্য প্রস্তুত” মানে rule সম্পূর্ণ নয়, মানে শুধু এই ৭টি নমুনা পার হয়েছে।

নিজের lab-এ আসল event তৈরি করে যাচাই করতে Atomic Red Team-এর মতো উন্মুক্ত test সংগ্রহ অনেকে ব্যবহার করেন। এটি শুধু নিজের বা লিখিত অনুমতি পাওয়া system-এ চলবে। আরও গভীরে যেতে Palantir-এর প্রকাশিত Alerting and Detection Strategy (ADS) কাঠামো দেখতে পারেন, যেখানে প্রতিটি rule-এর সাথে লক্ষ্য, অন্ধ জায়গা, false positive আর যাচাইয়ের পথ লিখে রাখা হয়।

৮ · Tuning আর মাপ — alert fatigue কমানো

পর্ব ২৩-এ দেখেছেন, alert fatigue মানুষের দোষ নয়, নকশার ফল। সেখানেই base-rate fallacy-র হিসাবও আছে। ছোট করে মনে করি। ধরা যাক ১০,০০,০০০ ঘটনায় ১০টি আসল, rule ৯৯% আসল ঘটনা ধরে, আর ১% নিরীহ ঘটনায় ভুল করে। তখন TP ৯.৯, FP ০.০১ × ৯,৯৯,৯৯০ ≈ ১০,০০০, আর precision প্রায় ০.১%.

Correlation এখানে সাহায্য করে। ধরুন দ্বিতীয় একটি শর্ত যোগ হলো, যা ৯০% আসল ঘটনায় আর ১% নিরীহ ঘটনায় থাকে, আর দুই শর্ত পরস্পর স্বাধীন। এই স্বাধীনতা একটি অনুমান, বাস্তবে তা পুরো মেলে না। তখন TP ৯.৯ × ০.৯ ≈ ৮.৯, FP ≈ ১০০, precision ৮.৯ ÷ ১০৮.৯ ≈ ৮.২%. প্রায় ৮০ গুণ ভালো। দাম হলো recall ৯৯% থেকে ৮৯% হলো। এই লেনদেনটিই সব tuning-এর মূল কথা।

Tuning-এর আগে কারণ খুঁজুন: ভুল alert-গুলো কোথা থেকে আসছে? Allowlist বা ছাড় শেষ উপায়, প্রথম নয়। নিচের কাল্পনিক rule এক সপ্তাহে ৮৪০টি alert দিয়েছে। প্রতিটি alert দেখতে ১০ মিনিট লাগলে সেটি ১৪০ ঘণ্টার কাজ। সব পর্যালোচনা করে ১৪টি আসল পাওয়া গেছে। পুরোনো ডেটায় replay করে বিকল্পগুলো তুলনা করুন।

এক সপ্তাহের ফল, replay করে (আসল ঘটনা ১৪টি)
অবস্থা
মোট alert
TP
FP
Precision
Recall
সক্রিয় ছাড়
  1. শুরু। ৮৪০টি alert, ১৪টি আসল। Precision ১৪ ÷ ৮৪০ ≈ ১.৭%. Analyst-রা rule-টি দেখলেই বিরক্ত হন।
  2. কারণ খোঁজা। Alert ভাগ করে দেখা গেল: ৬১০টি অনুমোদিত vulnerability scanner 198.51.100.5 থেকে, ১৫০টি পুরোনো password-ওয়ালা একটি monitoring script থেকে। সংখ্যা বদলায়নি, শুধু কারণ জানা গেল।
  3. পুরো subnet ছাড়। সবচেয়ে সহজ পথ। Scanner আর ওই script দুটিই 198.51.100.0/24-এ। পুরো subnet বাদ দিলে ৭৮৩টি alert যায়, থাকে ৫৭টি। Precision ১৯.৩% দেখতে সুন্দর। কিন্তু ৩টি আসল ঘটনাও হারাল, recall ১১ ÷ ১৪ ≈ ৭৮.৬%, আর ছাড়ের কোনো মেয়াদ নেই। বাতিল।
  4. সরু ছাড়, ৩০ দিন। শুধু scanner-এর ঠিকানা, তার account আর নির্ধারিত সময়। ৬১০টি যায়, থাকে ২৩০টি, TP ১৪. মালিক, ticket আর ১ ডিসেম্বরের মেয়াদ লেখা হলো। Precision ১৪ ÷ ২৩০ ≈ ৬.১%, recall ১০০%.
  5. মূল কারণ সারানো। Monitoring script-এর password ঠিক করা হলো। এটি ছাড় নয়, আসল সমাধান। আরও ১৫০টি গেল, থাকল ৮০টি। Precision ১৪ ÷ ৮০ = ১৭.৫%, recall ১০০%. সংখ্যায় ধাপ ৩-এর চেয়ে একটু কম, কিন্তু কোনো আসল ঘটনা হারায়নি। তাই শুধু precision দেখে রায় দেওয়া যায় না। কাজ এখন ১৩.৩ ঘণ্টায় নামল।
  6. মেয়াদ শেষে যাচাই। ১ ডিসেম্বরের আগে মালিককে জিজ্ঞেস করা হলো। জানা গেল পুরোনো scanner-টি নভেম্বরেই বাতিল হয়েছে, আর নতুন scan এই পথে আসে না। তাই ছাড়টি মুছে গেল। মেয়াদ না থাকলে সেই ঠিকানা পরে নতুন কোনো যন্ত্র পেলে তা চিরকাল এই rule থেকে অদৃশ্য থাকত।

মেয়াদসহ ছাড় কেমন দেখতে

Sigma-র rule ফাইলে মেয়াদের কোনো প্রমিত ঘর আছে বলে আমাদের জানা নেই। তাই অনেক দল ছাড় আলাদা ফাইলে রাখে, আর CI মেয়াদ যাচাই করে। নিচের format কোনো মান নয়, শুধু একটি উদাহরণ।

# exceptions.yml: our own team format, not part of the Sigma standard
- rule: 3e05b7c9-d162-4a8e-9f37-b18c6d24e05a
  match:
    src_ip: 198.51.100.5
    user: svc-scan
    window: Tue 01:00-03:00
  owner: secops-team
  ticket: SEC-2041
  reason: Approved weekly scan of our own servers
  created: 2026-11-01
  expires: 2026-12-01
Tuning-এর চার হাতিয়ার
  • ছাড়: সরু, সাথে মালিক, কারণ, ticket আর মেয়াদ। পুরো host বা পুরো subnet নয়। এটি এক ধরনের suppression.
  • নির্দিষ্ট field: contains-এর বদলে পুরো মান, LogonType-এর মতো বাড়তি শর্ত। পর্ব ২৩-এর /admin উদাহরণটি মনে করুন।
  • Severity: শুধু ব্যর্থতার ঝাঁক low বা medium, সাথে সাফল্য high, সাথে critical asset আরও ওপরে। সব alert “critical” হলে কোনোটিই নয়।
  • ব্যবস্থা: কম আস্থার সংকেত রাতে কাউকে না জাগিয়ে শুধু রিপোর্ট বা তদন্তের প্রসঙ্গে রাখা।

কী মাপবেন

মাপসূত্র বা অর্থউদাহরণ হিসাবফাঁদ
PrecisionTP ÷ (TP + FP)১৪ ÷ ৮০ = ১৭.৫%একা দেখলে recall হারানো চোখে পড়ে না
Alert volumeপ্রতি rule-এ, প্রতি analyst-এ alert৮০ × ১০ মিনিট ≈ ১৩.৩ ঘণ্টাকম মানেই ভালো নয়, log বন্ধও হতে পারে
Coverageপরীক্ষিত rule আছে এমন অগ্রাধিকার technique১৮ ÷ ৪০ = ৪৫%Rule থাকা আর কাজ করা এক নয়
MTTDঘটনা শুরু থেকে ধরা পড়া পর্যন্ত গড় সময়নিচে দেখুনশুধু ধরা পড়া ঘটনাই হিসাবে ঢোকে

MTTD-র একটি পূর্ণ হিসাব। পাঁচটি ঘটনা ধরা পড়তে সময় লেগেছে ১২, ২০, ৪৫, ১৮০ আর ২,৮৮০ মিনিট। গড় = ৩,১৩৭ ÷ ৫ = ৬২৭.৪ মিনিট, প্রায় ১০.৫ ঘণ্টা। কিন্তু median মাত্র ৪৫ মিনিট। একটি দুই দিনের ঘটনা গড়কে টেনে তুলেছে। তাই গড়ের সাথে median আর সবচেয়ে ধীর ঘটনাটিও দেখুন। MTTD শুধু যা ধরা পড়েছে তা গোনে, আর যা কখনো ধরা পড়েনি তা হিসাবেই নেই। Dwell time এর কাছের ধারণা, যার সংজ্ঞা উৎসভেদে একটু আলাদা হয়।

৯ · UEBA — “এই মানুষটির জন্য এটি স্বাভাবিক কি?”

পুরোনো দারোয়ান জানেন, করিম সাহেব প্রতিদিন সকাল নয়টায় আসেন। একদিন রাত তিনটায় এলে তিনি একটু ভাবেন। করিম সাহেবের কোনো নিয়ম ভাঙেনি, কিন্তু আচরণ বদলেছে। UEBA (User and Entity Behaviour Analytics) এই ধারণা log-এ আনে। প্রতিটি হিসাব, যন্ত্র বা service-এর জন্য একটি baseline শেখা হয়, আর তার থেকে বড় বিচ্যুতিতে risk score বাড়ে।

এটি ভালো কাজ করতে পারে যেখানে নির্দিষ্ট rule লেখা কঠিন, যেমন চুরি হওয়া account বা ভেতরের মানুষের অপব্যবহার। কিন্তু পর্ব ২৩-এর anomaly-র ফাঁদ এখানেও আছে: concept drift, দূষিত baseline, আর ব্যাখ্যা করা কঠিন ফল। ঈদের আগের সপ্তাহে হিসাব বিভাগের অস্বাভাবিক ব্যস্ততা কোনো অপরাধ নয়। মানুষের আচরণ মাপা গোপনীয়তার প্রশ্নও তোলে। লিখিত নীতি, সীমিত access আর মানুষের সিদ্ধান্ত ছাড়া শুধু score দেখে কারও বিরুদ্ধে ব্যবস্থা নেওয়া ঠিক নয়। ধরাবাঁধা rule আর UEBA তাই বিকল্প নয়, পরিপূরক। Endpoint-এর দিক পর্ব ৫৯-এ, আর SOC-এর দৈনন্দিন কাজ পর্ব ৬০-এ আসবে।

আরও গভীরে: Axelsson (“The Base-Rate Fallacy and the Difficulty of Intrusion Detection”, ACM TISSEC, ২০০০) দেখান, false alarm-এর হারই detection-এর আসল সীমা। Bianco-র Pyramid of Pain (২০১৩) ব্যাখ্যা করে কেন আচরণের rule বেশি টেকে। আধুনিক ধারায় rule-কে কোডের মতো দেখা হয়, আর schema নিয়ে OCSF-এর মতো উন্মুক্ত চেষ্টা চলছে। Sigma-র correlation অংশের সূক্ষ্ম নিয়ম specification-এর নিজের লেখাতেই পড়ে নেওয়া ভালো, কারণ converter-ভেদে আচরণ আলাদা হতে পারে।

পরীক্ষা ও ইন্টারভিউতে:

  • SIEM আর log management-এর পার্থক্য? Log management রাখে ও খোঁজে। SIEM তার ওপর normalise, correlate আর alert করে।
  • Normalisation কেন দরকার? আলাদা উৎসে একই field নামে এক rule চালাতে আর ঘটনা মেলাতে।
  • Sigma কী? YAML-এ লেখা পণ্য-নিরপেক্ষ log rule format, যা converter দিয়ে SIEM query হয়।
  • Fixed bin-এর সমস্যা কী? ঝাঁক বাক্সের সীমায় ভেঙে দুই দিকেই সীমার নিচে থাকতে পারে। Sliding window এটি এড়ায়।
  • Noisy rule নিয়ে কী করবেন? উৎস বিশ্লেষণ, মূল কারণ সারানো, সরু ও মেয়াদসহ ছাড়, replay করে recall যাচাই।

১০ · AI-এর যুগে detection

AI একটি দ্রুত সহকারী। সে rule-এর খসড়া লিখতে, log-এর সারাংশ করতে, query এক ভাষা থেকে অন্য ভাষায় নিতে পারে। অনেক SIEM-এ এখন এমন সহায়ক আছে। কিন্তু সে আপনার log, আপনার field আর আপনার স্বাভাবিক আচরণ দেখে না। তাই AI-এর দেওয়া rule একটি খসড়া, যা বাকি সব rule-এর মতোই test আর review পার হবে। Log পড়ানো হলে আরেকটি ঝুঁকি আছে। Username-এর মতো ঘর বাইরে থেকে আসে, তাই log-এর লেখা পর্ব ৫১-এর prompt injection হয়ে উঠতে পারে।

AI-এর সাধারণ ভুলকীভাবে ধরবেন
নেই এমন technique ID, Event ID বা Sigma field (hallucination)attack.mitre.org, vendor-এর documentation আর sigmahq.io-তে মেলান
আপনার SIEM-এ নেই এমন field নামনিজের আসল event-এর সাথে মেলান, নিরীহ test event চালান
খুব ঢিলা contains, ফলে আকাশছোঁয়া alertপুরোনো ডেটায় replay করে volume দেখুন
“শব্দ কমাতে” পুরো host বাদ দেওয়া, মেয়াদ ছাড়াReview-এ আটকান, সরু ছাড়ের নিয়ম মানুন
Triage-এ অতি আত্মবিশ্বাস: “এটি নিরীহ”মূল event নিজে পড়ুন, অনিশ্চয়তা জিজ্ঞেস করুন
Prompt-এ পাঠানো log-এ ব্যক্তিগত তথ্য বা secretআগে সরান বা বদলান, প্রতিষ্ঠানের নীতি মানুন

নিচের prompt-টি AI-কে খসড়া লিখতে নয়, আপনার rule-এর সমালোচনা করতে বলে। আসল ঠিকানা আর নাম বদলে তবেই rule পাঠান।

আমি নিজের প্রতিষ্ঠানের SIEM-এর জন্য নিচের Sigma rule লিখেছি। এটি রক্ষার কাজে, শুধু আমাদের নিজের log-এ চলবে। [এখানে নাম ও ঠিকানা বদলানো rule পেস্ট করুন] ১. কোন কোন নিরীহ কাজে এটি মিলতে পারে? তালিকা দাও। ২. কোন আচরণ এই rule ফসকাবে? অন্ধ জায়গাগুলো বলো। ৩. ৫টি নমুনা event-এর ধরন বলো: ৩টি যা ধরা উচিত, ২টি যা ধরা উচিত নয়। ৪. প্রতিটি ATT&CK ID, Event ID বা field নামের জন্য বলো কোথায় যাচাই করব। নিশ্চিত না হলে "নিশ্চিত নই" লেখো, বানিয়ে বলবে না। ৫. কোনো ছাড় প্রস্তাব করলে তা সরু হবে, মালিক আর মেয়াদসহ।

AI-এর লেখা rule চালুর আগে

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

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন। এগুলো আপনাকে rule-লেখক থেকে detection engineer-এর চিন্তায় নিয়ে যাবে।

প্র ০১SIEM ঠিকঠাক চলছে কি না কীভাবে বুঝবেন? নীরবতা কি ভালো খবর?

নীরবতার দুটি অর্থ হতে পারে: কিছু ঘটছে না, অথবা আপনি আর দেখতে পাচ্ছেন না। বাইরে থেকে দুটি একই রকম লাগে। মহল্লার পাহারাদারের বাঁশি না বাজার কারণ শান্তি হতে পারে, আবার তিনি ঘুমিয়েও থাকতে পারেন।

Equifax-এর ২০১৭ সালের ঘটনা এর পরিচিত উদাহরণ। মার্কিন House Oversight Committee-র ২০১৮ সালের রিপোর্ট অনুযায়ী, একটি প্রধান সিস্টেমের network traffic দেখার যন্ত্রটি security certificate-এর মেয়াদ ফুরিয়ে যাওয়ায় প্রায় ১৯ মাস নিষ্ক্রিয় ছিল। ফলে ডেটা বেরিয়ে যাওয়ার সময় তা চোখে পড়েনি। রিপোর্টে আরও বলা হয়, শত শত certificate-এর মেয়াদ পেরিয়ে গিয়েছিল। ২০১৭ সালের জুলাইয়ে certificate নবায়ন করার পরেই সন্দেহজনক traffic ধরা পড়ে। লক্ষ করুন, এখানে সমস্যা rule-এর মান নয়। Sensor-টিই কাজ করছিল না, আর কেউ জানত না।

তাই detection-এর ওপরও detection লাগে:

  • Heartbeat: প্রতিটি log source-এর “শেষ event কখন এসেছে” মাপুন। অস্বাভাবিক নীরবতায় alert.
  • Parser-এর স্বাস্থ্য: Parse ব্যর্থতার হার দেখুন। Software update লাইনের গঠন বদলাতে পারে।
  • Canary test: নিয়মিত একটি নিরীহ পরীক্ষামূলক event পাঠান, আর দেখুন পুরো পথ পেরিয়ে alert আসে কি না।
  • Certificate আর license: মেয়াদ নিজেও নজরদারির বিষয়। পর্ব ১২-এ এর ব্যবস্থাপনা দেখেছেন।

এই অভ্যাসকে অনেকে “synthetic” পরীক্ষা বলেন। খরচ সামান্য, কিন্তু এটি সবচেয়ে বিপজ্জনক ব্যর্থতা ধরে, যেটি কোনো শব্দ করে না।

ছোট দলের জন্যও এটি কঠিন নয়। একটি সাপ্তাহিক তালিকাই যথেষ্ট: কোন উৎস গত সাত দিনে চুপ ছিল, কোন rule একবারও alert দেয়নি, আর কেন। উত্তর না জানলে সেটিই আপনার পরের কাজ।

মূল উপলব্ধি: Detection ব্যবস্থা নিজেই একটি system, যা নষ্ট হতে পারে। শূন্য alert কোনো প্রমাণ নয়, যতক্ষণ না আপনি দেখিয়েছেন যে ঘটনা ঘটলে alert আসত।

প্র ০২Sigma যদি “একবার লিখলে সব জায়গায় চলে”, তাহলে প্রতিষ্ঠানগুলো নিজের rule আবার লেখে কেন?

Sigma একটি পুরোনো সমস্যার সমাধানের চেষ্টা। M রকম rule উৎস আর N রকম SIEM থাকলে, প্রতিটি জোড়ার জন্য আলাদা অনুবাদ লাগে। মাঝখানে একটি সাধারণ format বসালে M × N কাজ প্রায় M + N হয়ে যায়। Compiler-এর IR বা C toolchain-এর আলোচনায় এই নকশা দেখা যায়। Snort rule network-এ যা করেছে, YARA ফাইলে যা করেছে, Sigma log-এ সেই ভাষা দিতে চেয়েছে। এর ফলে বিশ্লেষকেরা বড় উন্মুক্ত rule সংগ্রহ ভাগ করে নিতে পেরেছেন।

তবু তিনটি জিনিস রূপান্তরে হারায়:

  • ঘরের মিল: Rule বলে TargetSid, আপনার schema হয়তো অন্য নাম রাখে। এই মিলের ভার থাকে conversion pipeline-এ, যা আপনাকেই সাজাতে হয়।
  • ক্ষমতার ফারাক: সব backend সব modifier বা correlation ধরন সমর্থন করে না। Specification-এর নতুন অংশ সব জায়গায় একসাথে পৌঁছায় না।
  • স্বাভাবিকের ফারাক: অন্যের rule তার পরিবেশের জন্য tune করা। আপনার deploy account, scanner আর ব্যবসার সময় আলাদা। এক ব্যাংকের স্বাভাবিক আরেক কারখানার সন্দেহ।

তাই বাস্তব পথটি মাঝামাঝি। ভাগ করা rule দিয়ে শুরু, নিজের পরিবেশে test, নিজের ছাড়, আর নিজের সংগ্রহে version রাখা। কিছু rule পণ্যের নিজের ভাষায় লিখতে হয়, কারণ সেখানে এমন সুবিধা থাকে যা সাধারণ format-এ নেই। Portable ভাষার একটি লুকানো সুবিধাও আছে: SIEM বদলাতে হলে পুরো জ্ঞান আটকে থাকে না।

একটি ব্যবহারিক অভ্যাস হলো, বাইরে থেকে আনা প্রতিটি rule-কে নতুন কর্মীর মতো দেখা। প্রথমে shadow mode, তারপর নিজের ডেটায় পরীক্ষা, আর ভালো ফল দেখালে তবেই দায়িত্ব। এতে সংগ্রহ বড় হয়, কিন্তু আস্থা হারায় না।

মূল উপলব্ধি: Sigma ধারণাকে বহনযোগ্য করে, আস্থাকে নয়। অনুবাদ হওয়া rule একটি ভালো শুরু। আপনার log-এ test না হওয়া পর্যন্ত সেটি প্রমাণিত নয়।

প্র ০৩Threshold rule থাকলেও ধীরে চলা চেষ্টা কেন ধরা পড়ে না, আর রক্ষক কী করবেন?

প্রতিটি threshold একটি সীমারেখা, আর সীমারেখার নিচে যা আছে তা rule-এর কাছে অদৃশ্য। সীমা কমালে ভুল alert বাড়ে, তাই সীমাটি সবসময় একটি আপস। এখানেই ধীর কাজের ঝুঁকি।

একটি প্রকাশিত উদাহরণ Microsoft-এর। প্রতিষ্ঠানটি ২০২৪ সালের জানুয়ারিতে জানায়, ২০২৩ সালের নভেম্বরের শেষ দিক থেকে Midnight Blizzard নামে পরিচিত একটি দল password spray ব্যবহার করে একটি পুরোনো, non-production test tenant-এর account-এ ঢোকে। Microsoft-এর বর্ণনা অনুযায়ী চেষ্টা ছিল সীমিত সংখ্যক account-এ, আর সংখ্যায় কম। ওই account-এ MFA চালু ছিল না। ঘটনাটি ধরা পড়ে ১২ জানুয়ারি। এই বিবরণ থেকে রক্ষকের শিক্ষাগুলো গুরুত্বপূর্ণ, ঘটনার পুনর্গঠন নয়।

  • প্রতিরোধ আগে: Detection-এর ওপর সব ভার না দিয়ে পুরোনো ও test account-সহ সব হিসাবে MFA রাখা। ধীর চেষ্টা MFA-র দেয়াল এমনিতে পার হতে পারে না।
  • Inventory: “Non-production” মানে নজরের বাইরে নয়। ভুলে যাওয়া হিসাব আর tenant-ই বড় ঝুঁকি।
  • লম্বা জানালা: ৫ মিনিটের পাশাপাশি ২৪ ঘণ্টা বা ৭ দিনের গণনা, আর ভিন্ন account-এর সংখ্যা। খরচ বেশি, তাই নির্দিষ্ট গুরুত্বপূর্ণ অংশে চালান।
  • সাফল্যের সংকেত: ব্যর্থতা না গুনে “অচেনা জায়গা থেকে সফল login”, “পুরোনো account হঠাৎ সক্রিয়” দেখা।
  • Hunting: যা rule-এ ধরা পড়ে না, তা মানুষ অনুমান ধরে খোঁজেন। সেটি পর্ব ৫৮-এর বিষয়।

আরেকটি কথা। সীমা বেছে নেওয়ার সময় আন্দাজ না করে নিজের পুরোনো ডেটা দেখুন: স্বাভাবিক দিনে একটি উৎস থেকে সর্বোচ্চ কতটি ব্যর্থতা আসে? সেই সংখ্যার ওপর সীমা বসান, আর কারণটি rule-এর পাশে লিখে রাখুন।

মূল উপলব্ধি: কোনো threshold ঠিক সংখ্যা নয়, প্রতিটি একটি অন্ধ জায়গা ঘোষণা করে। সেই জায়গাটি লিখে রাখুন, আর অন্য স্তর দিয়ে ঢাকুন।

প্র ০৪একটি ছোট alert কীভাবে একটি বিশাল ঘটনা খুলে দিতে পারে? এখান থেকে context আর মানুষের ভূমিকা নিয়ে কী শেখা যায়?

২০২০ সালের SolarWinds ঘটনাটি মাসের পর মাস অনেক প্রতিষ্ঠানের চোখ এড়িয়েছিল। FireEye-এর প্রধান নির্বাহী Kevin Mandia-র পরে দেওয়া বর্ণনা এবং সংবাদ প্রতিবেদন অনুযায়ী, তাদের নিজেদের ক্ষেত্রে সূত্রটি এসেছিল সাধারণ একটি ঘটনা থেকে: একজন কর্মীর হিসাবে MFA-র জন্য একটি নতুন যন্ত্র নিবন্ধিত হলো। Alert-এর পরে যাচাই করে দেখা গেল, কর্মী নিজে এটি করেননি। সেই সুতো ধরে তদন্ত এগোয়। বিবরণের খুঁটিনাটি নিয়ে প্রকাশিত উৎসগুলোর মধ্যে সামান্য পার্থক্য আছে, তাই শিক্ষাটুকুই নিন।

লক্ষ করুন এটি কোন ধরনের detection. কোনো জটিল malware signature নয়। এটি একটি পরিচয়-সংক্রান্ত আচরণ, যা বহু প্রতিষ্ঠানে প্রতিদিন স্বাভাবিকভাবে ঘটে। নতুন ফোন নিলে মানুষ নতুন যন্ত্রই নিবন্ধন করেন। তাহলে সংকেতটি কাজে লাগল কেন? তিনটি কারণে:

  • ঘটনাটি log-এ ছিল: Identity log-এ দেখা না গেলে কিছুই হতো না। পর্ব ৫৫-এর “কী রাখবেন” প্রশ্নের দাম এখানে।
  • মানুষকে জিজ্ঞেস করা গেল: Enrichment সবচেয়ে সরল রূপে: এই কর্মী কি জানেন? একটি ফোন কলের চেয়ে ভালো context কম আছে।
  • কেউ বিষয়টি ফেলে রাখেননি: বহু alert-এ ডুবে থাকা দলে এমন “ছোট” ঘটনা বন্ধ হয়ে যেতে পারত। Alert fatigue ঠিক এখানেই দাম নেয়।

নকশার শিক্ষা হলো, উচ্চ-মূল্যের identity পরিবর্তনগুলো আলাদা করে দেখা। MFA যন্ত্র যোগ, password reset, admin দলে সদস্য বৃদ্ধি, নতুন API key. এগুলো সংখ্যায় কম, আর নিশ্চিত করা সহজ। অনেক প্রতিষ্ঠান স্বয়ংক্রিয়ভাবে কর্মীকে বার্তা পাঠিয়ে নিশ্চিত করে নেয়।

মূল উপলব্ধি: ঘটনার আকার আর সংকেতের আকার এক নয়। সস্তা, নিশ্চিত করা যায় এমন সংকেত আর সময় দিতে পারা মানুষ, এই জুটি অনেক দামি পণ্যের চেয়ে বেশি কাজ করতে পারে।

প্র ০৫“ATT&CK-এর ৮০% coverage” বা “MTTD ১০ মিনিট” — এমন সংখ্যা কতটা বিশ্বাস করবেন?

মাপ ছাড়া উন্নতি বোঝা যায় না, কিন্তু মাপ নিজেই লক্ষ্য হয়ে গেলে নষ্ট হয়। অর্থনীতিবিদ Charles Goodhart-এর ১৯৭৫ সালের পর্যবেক্ষণ থেকে আসা এই ধারণা, যাকে সাধারণত Goodhart's law বলা হয়, নিরাপত্তা দলেও খাটে।

Coverage-এর ফাঁদ: ATT&CK-এর একটি technique অনেক রকমভাবে করা যায়। T1110-এর একটি rule থাকলে ঘরটি সবুজ হয়, কিন্তু সেটি হয়তো শুধু এক ধরন, এক platform, আর এক log source দেখে। তাই অভিজ্ঞ দলগুলো সাধারণত মানচিত্রকে সম্পূর্ণতার পরীক্ষা হিসেবে নয়, অগ্রাধিকার ঠিক করার হাতিয়ার হিসেবে দেখে। ১০০% সবুজ মানচিত্র মানে নিরাপদ নয়, মানে ঘরগুলোতে কিছু একটা আছে। তাই সৎ প্রশ্ন: আপনার threat model-এর গুরুত্বপূর্ণ technique-গুলোর কতটিতে test করা, চালু, এখনো alert দিতে পারে এমন rule আছে?

MTTD-র ফাঁদ: এই পর্বে দেখেছেন, একটি লম্বা ঘটনা গড় বদলে দেয়। তার চেয়েও বড় কথা, হিসাবে শুধু ধরা পড়া ঘটনা ঢোকে। “ঘটনা শুরুর” সময়ও প্রায়ই অনুমান। Analyst-কে শুধু দ্রুত ticket বন্ধের ওপর মাপলে তাঁরা তাড়াহুড়ো করে বন্ধ করবেন। Alert কম আনার লক্ষ্য দিলে দলগুলো rule বন্ধ করে দিতে পারে, আর সংখ্যা ভালো দেখাবে।

ভালো অভ্যাস:

  • একটি সংখ্যা নয়, জোড়ায় মাপুন: precision-এর সাথে recall, volume-এর সাথে পরীক্ষিত coverage.
  • নিজের lab-এ অনুমোদিত নিয়মিত পরীক্ষা চালান, যাতে “ধরা পড়ার কথা” আর “ধরা পড়ল” মেলানো যায়। পর্ব ৫০-এর অনুমোদিত পরীক্ষার ফলও এখানে কাজে আসে।
  • Median আর সবচেয়ে ধীর ঘটনা দেখান, শুধু গড় নয়।
  • প্রতিটি বড় ঘটনার পরে জিজ্ঞেস করুন, “কোন rule ধরতে পারত?” দোষ খোঁজা নয়, পর্ব ৫৪-এর দোষারোপহীন শিক্ষা।

মূল উপলব্ধি: সংখ্যা প্রশ্ন শুরু করে, শেষ করে না। প্রতিটি মাপের সাথে জানতে হবে এটি কী দেখে না।

অনুশীলন

সব অনুশীলন নিজের ল্যাপটপ, খাতা বা নিজের lab-এ করুন। অন্যের system-এ কিছু চালাবেন না।

  1. পূর্ণ হিসাব: একটি rule এক সপ্তাহে ৫০০টি alert দিল, যার ২৫টি আসল। ওই সপ্তাহে মোট ৩০টি আসল ঘটনা ছিল। প্রতি alert-এ ১২ মিনিট লাগে। Precision, recall আর মোট কাজের ঘণ্টা বের করুন। Tuning-এর পরে alert ১০০টি, TP ২৪ হলে কী দাঁড়ায়?

    আগে: Precision ২৫ ÷ ৫০০ = ৫%. Recall ২৫ ÷ ৩০ ≈ ৮৩.৩%. কাজ ৫০০ × ১২ ÷ ৬০ = ১০০ ঘণ্টা।

    পরে: Precision ২৪ ÷ ১০০ = ২৪%. Recall ২৪ ÷ ৩০ = ৮০%. কাজ ১০০ × ১২ ÷ ৬০ = ২০ ঘণ্টা।

    ৮০ ঘণ্টা বাঁচল, দাম একটি আসল ঘটনা। সেটি কোন ঘটনা, দেখা জরুরি। যদি সেটি সবচেয়ে গুরুতরটি হয়, তবে এই বিনিময় ঠিক নয়। সংখ্যা সিদ্ধান্ত নেয় না, শুধু সিদ্ধান্তটি পরিষ্কার করে।

  2. Rule সমালোচনা: একজন সহকর্মী লিখলেন: একটি Sigma rule, যার selection হলো EventID: 4625, condition: selection, level: critical. শব্দ কমাতে তিনি যোগ করলেন SubjectUserName|contains: svc মিললে বাদ, আর কোনো মালিক বা মেয়াদ লেখেননি। অন্তত চারটি সমস্যা বলুন, আর ঠিক করুন।
    • একক ব্যর্থতায় alert: প্রতিটি ভুল password-ই alert হবে। Threshold বা ক্রম দরকার।
    • critical অযৌক্তিক: একটি ব্যর্থ login গুরুতর নয়। Severity প্রমাণের সাথে মিলিয়ে দিন, low থেকে শুরু করুন।
    • ঢিলা ছাড়: contains: svc সব “svc” নামের হিসাবকে অদৃশ্য করে, এমনকি svcfake-ও। পুরো নাম আর যন্ত্রের শর্ত দিন।
    • Field যাচাই: 4625-এ ব্যর্থ হিসাবের নাম সাধারণত TargetUserName-এ থাকে। SubjectUserName দিয়ে ছাড় লিখলে হয়তো উদ্দেশ্যই পূরণ হয় না। আসল event দেখুন।
    • মালিক, মেয়াদ, test নেই: ছাড় আলাদা ফাইলে ticket আর মেয়াদসহ রাখুন, আর নমুনা event যোগ করুন।
  3. আউটপুট অনুমান: sliding_login.js-এর নিয়ম (WINDOW = 300, LIMIT = 5, আর e.t - fails[0] >= WINDOW হলে পুরোনোটি বাদ) ধরে একটি উৎসের ব্যর্থতার সময় ০, ১০০, ২০০, ২৯৯, ৩০০, ৩১০. fired-এর মান কত হবে? শেষ ব্যর্থতাটি না থাকলে কী হতো?

    ২৯৯-তে তালিকা [০, ১০০, ২০০, ২৯৯], সংখ্যা ৪। ৩০০-তে ৫টি হয়, কিন্তু ৩০০ − ০ = ৩০০, যা >= 300, তাই ০ বাদ পড়ে, থাকে ৪টি। ৩১০-এ তালিকা [১০০, ২০০, ২৯৯, ৩০০, ৩১০], সংখ্যা ৫, আর ৩১০ − ১০০ = ২১০, কিছু বাদ পড়ে না। তাই fired = 310.

    ৩১০ না থাকলে fired থাকত null. মাত্র এক সেকেন্ডের সীমারেখা ফল বদলে দেয়, তাই এমন সীমার ধারে test রাখা জরুরি। >= আর >-এর পার্থক্যও এখানে ধরা পড়ে।

  4. Test বাড়ান: detection_tests.js-এর tests তালিকায় একটি নতুন সারি যোগ করুন: একটি অফিসের NAT ঠিকানা 198.51.100.40-এর পেছনে ৬ জন কর্মী সকালে একবার করে password ভুল দিলেন, তারপর একজন সফল হলেন। সবার সময় ৫ মিনিটের মধ্যে। প্রত্যাশা false ধরলে কী হবে? আপনি কী বদলাবেন?

    সারিটি হতে পারে ['FP office NAT', seq('198.51.100.40', [0, 30, 60, 90, 120, 150], 160), 1, false]. চালালে rule v2-এ এটি FAIL হবে, ফল 7/8 passed -> do not deploy, কারণ rule শুধু ঠিকানা দেখে, মানুষ দেখে না।

    এটি rule-এর একটি সত্যিকার সীমা। সমাধানের বিকল্প: event-এ user যোগ করে “এক উৎস + একই user” গোনা, তবে তখন spraying-এর ধরন ফসকায়, তাই আলাদা rule লাগবে। অথবা enrichment দিয়ে অফিসের NAT চিনে severity কমানো, পুরো বাদ না দেওয়া। সিদ্ধান্তটি নমুনাসহ repository-তে থাকুক।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি detection rule তৈরির ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. আচরণ আর ATT&CK technique ঠিক করা
  2. দরকারি log আসছে কি না যাচাই করা
  3. Rule-এর খসড়া লেখা
  4. নমুনা event-এ test করা
  5. অন্য একজনের review
  6. Shadow mode-এ deploy করে ফল মাপা

SIEM-এ log normalisation-এর প্রধান উদ্দেশ্য কী?

  • Log-এর আকার ছোট করে জায়গা বাঁচানো
  • ভিন্ন উৎসের field একই নাম ও অর্থে আনা, যাতে এক rule সব উৎসে চলে
  • Asset-এর মালিক আর গুরুত্ব যোগ করা
  • পুরোনো log মুছে ফেলা

একটি Sigma rule-এর condition হলো selection and not filter. একটি event selection-এর সব শর্ত মেলে, আর filter-এর সব শর্তও মেলে। ফল কী?

  • Alert হবে, কারণ selection মিলেছে
  • Rule ত্রুটি দেবে, কারণ দুটি একসাথে মিলতে পারে না
  • Alert হবে না
  • Threshold পূরণ হলে তবেই alert হবে

৫ মিনিটের fixed bin-এ সীমা ৫। ব্যর্থতা ঘটল ৪:৪০, ৪:৪৫, ৪:৫০, ৪:৫৫, ৫:০৫ আর ৫:১০-এ (মিনিট:সেকেন্ড)। বাক্স ০–৫ আর ৫–১০ মিনিট। কী ঘটবে?

  • Alert হবে না, যদিও অল্প সময়ে ৬টি ব্যর্থতা ঘটেছে
  • প্রথম বাক্সেই alert হবে
  • দ্বিতীয় বাক্সে alert হবে
  • দুই বাক্স যোগ হয়ে alert হবে

একটি rule ৮০টি alert দিল, যার ১৪টি আসল। ওই সময়ে মোট আসল ঘটনা ছিল ১৪টি। Precision আর recall কত?

  • Precision ১০০%, recall ১৭.৫%
  • Precision ১৭.৫%, recall ১০০%
  • দুটোই ১৭.৫%
  • Precision ৮২.৫%, recall ১০০%

একটি অনুমোদিত scanner প্রতি সপ্তাহে শত শত ভুল alert তৈরি করছে। কোন ব্যবস্থাটি সবচেয়ে ভালো?

  • Rule-টি পুরো বন্ধ করা
  • Scanner-এর পুরো subnet স্থায়ীভাবে বাদ দেওয়া
  • পুরো rule-এর severity সবচেয়ে নিচে নামানো
  • Scanner-এর ঠিকানা, account আর সময়ে সীমিত ছাড়, সাথে মালিক, ticket ও মেয়াদ

AI একটি Sigma rule লিখে দিল, আর বলল এটি T1110 ধরে। চালুর আগে সবচেয়ে ভালো পদক্ষেপ কোনটি?

  • AI-কে শুধু জিজ্ঞেস করা, “এটি কি ঠিক আছে?”
  • YAML সঠিক কি না দেখেই deploy করা
  • Technique আর field প্রাথমিক উৎসে মিলিয়ে নমুনা event-এ test, replay আর review করা
  • Level critical করে চালু করা, যাতে ভুল হলেও চোখে পড়ে

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. SIEM-এর সাত কাজ: collect, normalise, enrich, search, correlate, alert, report. কন্ট্রোল রুম চলে context দিয়ে।
  2. Detection শুরু হয় আচরণ, technique আর log source থেকে, IP তালিকা থেকে নয়।
  3. Sigma ধারণাকে বহনযোগ্য করে, তবে field mapping আর test আপনার দায়িত্ব।
  4. Threshold + sequence + context precision বাড়ায়। Sliding window fixed bin-এর ফাঁদ এড়ায়।
  5. Rule-ও কোড: version, test, review, deploy. ছাড় হবে সরু, মালিকসহ, মেয়াদসহ। মাপুন precision, volume, coverage আর MTTD, জোড়ায় জোড়ায়।

পরের পর্বে: Rule-এর ভেতরে বাইরের জ্ঞান কীভাবে আসে? IOC, TTP আর STIX দিয়ে threat intelligence-কে কাজে লাগানোর পথ দেখব, আর শিখব কোন তথ্য আসলে কাজের।

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

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান