mailbox รหัสรั่ว ส่งสแปมออก 1,786 ฉบับ โดยที่ CSF/LFD ไม่เห็น
เครื่องมือกันบุกรุกบนเมลเซิร์ฟเวอร์เกือบทั้งชุดถูกออกแบบมาจับ "คนที่ยังเข้าไม่ได้" ไม่ได้ออกแบบมาจับ "คนที่เข้าได้แล้ว" — พอคนร้ายถือรหัสที่ถูกต้องมาตั้งแต่วันแรก ตัวนับ failed login ก็ไม่เคยขยับ และไม่มีกฎไหนยิงเลยตลอดหนึ่งเดือน
สรุปใน 1 นาที
- อาการ/ปัญหา
- mailbox ถูกขโมยรหัสแล้วยิงสแปมวันละ 50-75 ฉบับใต้เพดาน 200 นาน 4 สัปดาห์ โดย CSF/LFD/BFM ไม่ขยับ
- ใครควรอ่าน
- คนดูแลเมลเซิร์ฟเวอร์ที่เจอบัญชีส่งสแปมออก
- ทางแก้แบบสั้น
- เครื่องมือเหล่านั้นนับแค่ auth ที่ล้มเหลว ให้ใช้คำถามชี้ขาดแยกว่าโดนแฮ็กหรือแค่โดนถล่ม ตรวจด้วยชุดคำสั่ง forensics บน exim แล้วทำตาม playbook ขอปลดจาก blocklist
พอดีเจอเคสที่ลูกค้าหลายรายบนเครื่องเดียวกันแจ้งพร้อมกันว่าส่งเมลไปปลายทางฝั่งอเมริกาไม่ออก ทั้งที่ไม่ได้แก้อะไรเลย ผมเลยไล่จากคิวเมลย้อนกลับไปเรื่อย ๆ สุดท้ายไปจบที่ mailbox กล่องเดียวของลูกค้าอีกราย ที่ถูกขโมยรหัสไปใช้ยิงสแปมออกนอกอยู่เงียบ ๆ สี่สัปดาห์เต็ม โดยที่ CSF/LFD และ Brute Force Monitor ของ DirectAdmin ไม่ขยับเลยสักครั้ง
เคสนี้ผมว่ามันสอนอะไรได้เยอะกว่าเคสโดน brute-force ธรรมดา เลยเก็บมาเขียนไว้ทั้งกลไกที่ทำให้ระบบมองไม่เห็น ชุดคำสั่งที่ใช้สืบจริง กับดักคำสั่งที่เกือบทำให้สรุปผิด และลำดับการขอปลดออกจาก blocklist ที่ห้ามสลับขั้นตอน
โดเมน ชื่อกล่อง และหมายเลข IP ในบทความเป็นค่าสมมติทั้งหมด (ใช้ช่วง TEST-NET ตามมาตรฐาน) ส่วนตัวเลขปริมาณ พฤติกรรม และรูปแบบบรรทัด log คือของจริงจากเครื่องจริง
อาการที่เห็นก่อน คือปลายน้ำสุด ๆ
จุดตั้งต้นไม่ใช่ alert ของระบบความปลอดภัยครับ แต่เป็น warning เรื่องคิวเมลค้างของ DirectAdmin กับเสียงบ่นของลูกค้ารายอื่นที่ไม่ได้ทำอะไรผิดเลย
exim -bpc # จำนวนเมลค้างคิว
exim -bp | head -40 # ค้างจากใคร ไปไหน
เปิดดูแล้วเห็นชัดว่าค้างจากผู้ส่งรายเดียว ปลายทางเป็นผู้ให้บริการอีเมลฟรีในสหรัฐฯ ทั้งก้อน จากนั้นไปดูว่าปลายทางเขา "พูดว่าอะไร" กับเรา
grep -ai 'found on one or more DNSBL' /var/log/exim/mainlog | tail -5
grep -aci 'found on one or more DNSBL' /var/log/exim/mainlog
554 ... <your-server-ip> found on one or more DNSBLs
https://postmaster.<ผู้ให้บริการปลายทาง>/smtp-error-codes.php#BL000110
นับได้ 169 ครั้งในวันเดียว และเช็คแล้ว IP สาธารณะของเครื่อง (shared IP ทั้งเครื่อง) ติดทั้ง Spamhaus ZEN และ cbl.abuseat.org เรียบร้อย แปลว่ากว่าจะรู้เรื่อง ความเสียหายเกิดไปหมดแล้ว
คำถามชี้ขาดข้อเดียว: มี IP ต่างชาติ login สำเร็จไหม
ก่อนจะไปเรื่องอื่น ผมขอวางคำถามนี้ไว้ก่อน เพราะมันคือเส้นแบ่งระหว่าง "โดนแฮ็กจริง" กับ "โดนถล่มแต่ไม่แตก" และเป็นจุดที่ถ้ารีบสรุปคือกล่าวหาลูกค้าผิดตัว
ข้อสำคัญคืออย่าตัดสินจากจำนวน failed auth เด็ดขาด ให้ดูอย่างเดียวว่ามี auth ที่ สำเร็จ จากเครือข่ายที่ไม่ควรมีหรือเปล่า
[email protected]
LOGS="/var/log/exim/mainlog.4 /var/log/exim/mainlog.3 /var/log/exim/mainlog.2 \
/var/log/exim/mainlog.1 /var/log/exim/mainlog"
for L in $LOGS; do grep -aE " <= .*A=[a-z_]+:$ACCT" "$L"; done \
| while IFS= read -r l; do
ip=$(echo "$l" | grep -oE '\[[0-9.]+\]' | tail -1 | tr -d '[]')
cc=$(geoiplookup "$ip" 2>/dev/null | grep -oE '[A-Z]{2},' | head -1 | tr -d ',')
echo "$cc $ip"
done | sort | uniq -c | sort -rn
สำเร็จเฉพาะจากเครือข่ายที่คาดไว้
กล่องเทียบในเคสเดียวกันนี้: login สำเร็จจาก 10 IP เป็นออฟฟิศลูกค้าในไทยทั้งหมด ส่วน IP ที่ล้มเหลวอย่างเดียวมีถึง 516 IP สำเร็จศูนย์ราย เฉลี่ยวันละ 158 IP ต่างชาติ ยิงคนละ 1-2 ครั้งเพื่อหลบ threshold ต่อ IP = distributed brute-force คลาสสิก แปลว่า username อยู่ในลิสต์เป้า credential-stuffing แต่รหัสยังไม่แตก
สำเร็จจากเครือข่ายที่ไม่ควรมี
อีกกล่องบนเครื่องเดียวกัน ขึ้นมาว่า 201 ครั้งจาก instance บนคลาวด์รายใหญ่ โผล่เหนือ IP ออฟฟิศไทยทุกตัว แบบนี้จบ ไม่ต้องเถียงต่อ ส่วนกล่องหลักของเคสนี้ยิ่งชัด เพราะ login สำเร็จจาก IP เดียวในยุโรปตะวันออก และ auth ล้มเหลวจาก IP นั้น = 0 ครั้ง ตลอด 4 สัปดาห์
auth fail เป็นศูนย์ตั้งแต่ครั้งแรกแปลว่าคนร้ายไม่ได้เดารหัส แต่ถือรหัสที่ถูกต้องมาตั้งแต่แรก — ต้นตอไม่ได้อยู่ที่เซิร์ฟเวอร์ แต่อยู่ที่เครื่องปลายทางของลูกค้าหรือการใช้รหัสซ้ำจากบริการอื่นที่ข้อมูลรั่ว
ทำไมของที่ติดตั้งครบแล้วยังมองไม่เห็น
ตรงนี้คือแกนของบทความ ไม่ใช่เรื่องคอนฟิกผิดหรือลืมเปิดอะไรครับ แต่เป็นเรื่องขอบเขตของเครื่องมือเอง
trigger ฝั่งเมลของ CSF/LFD ทั้งชุด — LF_SMTPAUTH, LF_POP3D, LF_IMAPD, LF_DISTATTACK, LF_DISTSMTP — และ BFM ของ DirectAdmin ทั้งหมดทำงานแบบเดียวกันคือ นับ auth ที่ล้มเหลว แล้วเทียบ threshold พอคนร้าย login สำเร็จทุกครั้ง ตัวนับก็ไม่เคยขยับ ไม่มีกฎไหนยิงเลยสักข้อ
เครื่องมือกัน brute-force ถูกออกแบบมาจับ คนที่ยังเข้าไม่ได้ ไม่ได้ออกแบบมาจับ คนที่เข้าได้แล้ว — สัญญาณที่จับเคสแบบนี้ได้คือ ปริมาณเมลขาออกต่อกล่อง ไม่ใช่จำนวน login ที่ล้มเหลว
ชั้นถัดมาคือ per-mailbox daily limit ของ DirectAdmin ซึ่งมีอยู่จริงและทำงานถูกต้อง แต่ค่ากลางที่ /etc/virtual/user_limit ตั้งไว้ 200 ฉบับ/วัน สูงเกินกว่าจะจับพฤติกรรม low-and-slow ได้ คนร้ายส่งวันละ 50-75 ฉบับ คิดเป็นแค่ 25-37% ของเพดาน เลยนิ่งอยู่ได้เป็นเดือน
แยกตาม log แต่ละรอบจะเห็นรูปทรงของการเผาช้า ๆ ชัดมาก: 327 / 324 / 522 / 397 / 216 ฉบับ ก้อนสุดท้ายคือวันที่คนร้ายเร่งจนชนเพดาน 200 เป็นครั้งแรก แล้ว DirectAdmin ถึงจะขึ้น warning ให้เห็น
grep -a 'emails have just been sent by' /var/log/directadmin/system.log | tail -20
2026:07:15-20:28:34: Warning: 200 emails have just been sent by
[email protected] (user=dauser). Limit: 200
หลังชนเพดาน Exim temp-reject ต่ออีก 16 ครั้ง คิวเลยค้างอยู่ 18 ฉบับ ปลายทางเป็นฟรีเมลรายเดียวกันทั้งหมด นั่นแหละคือคิวที่เห็นตอนเริ่มบทความ
ส่วนอีกกล่องที่โดนยิงจาก instance บนคลาวด์ กลับหลุดออกไปแค่ประมาณ 205 ฉบับ ไม่ใช่พันกว่า เพราะรอบนั้น LFD ยิง AUTHRELAY ขึ้นมาทัน — ตัวเดียวในสแต็กเดิมที่จับ signal ฝั่ง relay/outbound ได้จริง ใครปิดไว้ผมแนะนำให้เปิดคืนครับ
อ้อ ระบบ auto-whitelist ที่ผมเคยเขียนถึงเรื่องปลดแบนออฟฟิศลูกค้า ตัวนั้นใช้เงื่อนไข "login สำเร็จ" เป็นตัวตัดสิน ซึ่งปลอดภัยดีกับ brute-force แต่ไม่ช่วยอะไรเลยกับบัญชีที่ถูกขโมยรหัส เพราะคนร้ายก็ login สำเร็จเหมือนกัน ต้องรู้ขอบเขตของเครื่องมือตัวเองด้วย
อ่านบรรทัดของ exim ให้ออกก่อน
หลักฐานทั้งหมดอยู่ในบรรทัด <= ของ mainlog ครับ ดูตัวอย่างจริง (ค่าถูกแทนแล้ว)
grep -a "A=login:$ACCT" /var/log/exim/mainlog | head -5
2026-07-15 16:39:43 1xxxxx-000000xxxxx-xxxx <= [email protected]
H=(mailhost) [203.0.113.10] P=esmtpsa X=TLS1.3:TLS_AES_256_GCM_SHA384:256
A=login:[email protected] S=3549
T="Immediate Opening - Part-Time & Flexible" for [email protected]
จุดที่ต้องอ่านให้ออก: A=login: คือ auth สำเร็จ ไม่ใช่ "พยายาม" (ถ้าล้มเหลวจะขึ้นเป็น authenticator failed ... ([email protected]) คนละรูปแบบกันเลย) · P=esmtpsa คือ submission ที่ผ่านทั้ง auth และ TLS · H=(...) คือชื่อที่ client ประกาศเอง ปลอมได้ตามใจ ส่วนวงเล็บเหลี่ยม [...] ตัวท้ายสุดคือ IP จริง · T="..." คือ subject ใช้จัดกลุ่มแคมเปญได้
ชุดคำสั่งที่ใช้สืบจริง
ทั้งหมดรันเป็น root บนเครื่องเมลโดยตรง log ย้อนหลังมีแค่ mainlog กับ mainlog.1 ถึง .4 ประมาณ 4 สัปดาห์ ตัวเลขทุกตัวในบทความเลยถูกจำกัดด้วย retention นี้
หาผู้ส่งที่ auth แล้วส่งมากสุดของวันนี้ — อันนี้คือตัวที่ควรมีเป็น cron ตั้งแต่แรก
grep -a "^$(date +%F)" /var/log/exim/mainlog \
| grep -oE 'A=[a-z_]+:[^ ]+' | sed 's/A=[a-z_]*://' | sort | uniq -c | sort -rn | head
เทียบปริมาณส่งของแต่ละกล่องกับ limit ของกล่องนั้นเอง ไฟล์ usage ของ DirectAdmin เก็บเป็น 1 อักขระต่อเมล 1 ฉบับ เลยนับด้วย wc -c หลังตัด newline ทิ้ง
for u in /etc/virtual/*/usage/*; do
n=$(tr -d '\n' < "$u" 2>/dev/null | wc -c); [ "$n" -gt 50 ] || continue
box=$(echo "$u" | sed -E 's#/etc/virtual/([^/]+)/usage/(.*)#\2@\1#')
lim=$(cat "$(dirname "$(dirname "$u")")/limit/$(basename "$u")" 2>/dev/null)
printf '%-45s sent=%-5s limit=%s\n' "$box" "$n" "${lim:-default}"
done | sort -k2 -rn | head
ได้ตัวต้องสงสัยแล้วก็ดูว่ามันยิงแคมเปญอะไร ไปที่ไหน และปริมาณต่อ log แต่ละรอบเป็นเท่าไหร่
# แคมเปญอะไรบ้าง
for L in $LOGS; do grep -aE " <= .*A=[a-z_]+:$ACCT" "$L"; done \
| grep -oE 'T="[^"]+"' | sort | uniq -c | sort -rn | head
# ยิงไปโดเมนปลายทางไหนบ้าง
for L in $LOGS; do grep -aE " <= .*A=[a-z_]+:$ACCT" "$L"; done \
| grep -oE 'for [^ ]+@[^ ]+' | sed 's/.*@//' | sort | uniq -c | sort -rn | head
# ปริมาณต่อไฟล์ log (เห็นการเผาช้า ๆ ข้ามสัปดาห์)
for L in $LOGS; do printf '%-32s %s\n' "$L" "$(grep -acE " <= .*A=[a-z_]+:$ACCT" "$L")"; done
อยากได้ภาพรวมต่อบัญชีเป็นตารางเดียว OK/FAIL/ประเทศ/rDNS ใช้ตัวนี้ครับ ตัวนี้แหละที่กันไม่ให้กล่าวหาลูกค้าผิด เพราะ OK สูงจาก IP แปลก = แตกแล้ว ส่วน FAIL สูงล้วนโดยไม่มี OK เลย = โดนถล่มเฉย ๆ
[email protected]
LOG=/var/log/exim/mainlog
echo "OK=$(grep -c "A=[a-z_]*:$ACCT" $LOG) FAIL=$(grep "set_id=$ACCT" $LOG | grep -c failed)"
( grep "A=[a-z_]*:$ACCT" $LOG | sed -nE 's/.*\[([0-9.]+)\].*/\1/p'
grep "set_id=$ACCT" $LOG | grep failed | sed -nE 's/.*\[([0-9.]+)\].*/\1/p'
) | sort -u | while read ip; do
ok=$(grep -F "$ip" $LOG | grep -c "A=[a-z_]*:$ACCT")
fail=$(grep -F "$ip" $LOG | grep "set_id=$ACCT" | grep -c failed)
cc=$(geoiplookup $ip | grep -oE '[A-Z]{2},' | head -1 | tr -d ',')
rdns=$(getent hosts $ip | awk '{print $2}' | head -1)
printf '%-16s OK=%-3s FAIL=%-4s %s %s\n' "$ip" "$ok" "$fail" "$cc" "$rdns"
done
แล้วก็อย่าเดา DirectAdmin username ของเจ้าของโดเมนจากชื่อโดเมน ผมพลาดตรงนี้มาแล้ว ถามไฟล์เอาตรง ๆ ดีกว่า
OWNER=$(grep -rl '^example.com$' /usr/local/directadmin/data/users/*/domains.list \
| sed -E 's#.*/users/([^/]+)/.*#\1#')
echo "$OWNER"
กับดักคำสั่งที่ทำให้ระบบพังเงียบ ๆ
ส่วนนี้ผมว่ามีค่าที่สุดในบทความ เพราะทุกข้อคือของที่ "ไม่ error" แต่ให้คำตอบผิด แล้วเราก็เชื่อมันไปเต็ม ๆ
บรรทัด exim มีวงเล็บเหลี่ยมได้สองชุด เช่น H=host.tld ([192.0.2.50]) [203.0.113.10] — ชุดแรกคือ LAN IP ที่ client ประกาศเองใน EHLO ชุดหลังคือ IP จริงภายนอก โค้ดเดิมของผมใช้ head -1 เลยได้ IP วงในมาตลอด พอเช็คว่าเป็น private range ก็ continue ข้ามไป ผลคือ client ที่ประกาศ LAN IP ถูกข้ามทั้งหมด ทั้ง geo-check และตัวช่วย whitelist ตายเงียบ ๆ โดยไม่มี error สักบรรทัด ใช้ tail -1 เสมอ ถูกทั้งกรณีมีชุดเดียวและสองชุด
geoiplookup พิมพ์ผลว่า GeoIP Country Edition: TH, Thailand — คำว่า "GeoIP" มีอักษรใหญ่สองตัวนำหน้าอยู่ก่อน เขียน grep -oE '[A-Z]{2}' เปล่า ๆ เลยได้คำว่า IP กลับมาทุกครั้ง แล้วการเช็คประเทศทั้งระบบพังแบบไม่มีใครรู้ อันนี้ผมโดนมาสองรอบ ต้องเขียนเป็น grep -oE '[A-Z]{2},' | head -1 | tr -d ',' คือมีคอมมาต่อท้ายเพื่อบังคับให้ตรงเฉพาะรหัสประเทศ
ถ้าเครื่อง resolve ผ่าน public resolver รายใหญ่ Spamhaus จะ ปฏิเสธ query แล้วตอบกลับเป็น 127.255.255.254 ซึ่งหน้าตาเหมือนผลว่า "ติด" มาก คนอ่านผลแล้วตกใจเปล่ากันเยอะ ให้ยืนยันซ้ำที่หน้าเว็บ check.spamhaus.org เสมอ อย่าเชื่อคำตอบ DNS ตรง ๆ
ที่เหลืออีกสี่ข้อคือ: mainlog ใช้วันที่แบบ ISO (2026-07-15) แต่ /var/log/lfd.log ใช้ syslog (Jul 15) กรองผิดฟอร์แมตแล้วได้ผลลัพธ์ว่างเปล่าโดยไม่มี error · grep บน exim log ต้องใส่ -a เสมอ ไม่งั้นเจอ byte แปลก ๆ แล้วมันตอบ Binary file matches แล้วไม่พิมพ์อะไรเลย · จับคู่ IP อย่าใช้ substring เพราะ 74.125 ไปตรงกับ 174.125.x.x ด้วย ให้ escape จุดหรือ anchor ด้วยวงเล็บเหลี่ยม · และตอน set_id= ต้องตัดด้วย [^ )]+ เพราะมันอยู่ในวงเล็บ
ตัวเช็ค DNSBL ที่ผมใช้ประจำ ต้องกลับลำดับ octet ของ IP เซิร์ฟเวอร์ก่อน (สมมติ IP คือ 1.2.3.4 ก็ใส่เป็น 4.3.2.1)
for bl in zen.spamhaus.org bl.spamcop.net b.barracudacentral.org \
dnsbl.sorbs.net psbl.surriel.com cbl.abuseat.org; do
printf '%-28s %s\n' "$bl" "$(dig +short 4.3.2.1.$bl A | tr '\n' ' ')"
done
Playbook ห้ามเลือด — ลำดับนี้ห้ามสลับ
- ยืนยันก่อนลงมือ 10 นาที: ดูคิว -> หาผู้ส่งอันดับต้น -> ถามคำถามชี้ขาด ถ้าไม่มีการ login สำเร็จจาก IP แปลก ให้หยุด อย่าเพิ่งไปบอกลูกค้าว่าโดนแฮ็ก
- เก็บหลักฐานก่อนแตะอะไรทั้งนั้น: บรรทัดดิบจาก mainlog + subject + ปลายทาง + จำนวนต่อ log แต่ละรอบ พอรีเซ็ตรหัสไปแล้วของพวกนี้ยังอยู่ก็จริง แต่คิวกับสถานะจะเปลี่ยน
- บล็อก IP คนร้าย แล้วรีเซ็ตรหัส mailbox ทันที สองอย่างนี้ต้องทำคู่กันเสมอ บล็อกอย่างเดียวเขาย้าย IP มาใหม่ได้ในนาทีเดียว
- ล้างสแปมในคิว โดยดูรายการก่อนแล้วค่อยลบ เพราะเมลจริงของลูกค้าคนอื่นอาจติดอยู่ในคิวเดียวกัน
- รัดเพดานการส่งของกล่องนั้นลง แล้วทบทวนค่ากลางทั้งเครื่องด้วย
- ตรวจซ้ำทั้งเครื่องว่ามีกล่องอื่นโดนแบบเดียวกันอีกไหม ไม่ใช่ตรวจแค่โดเมนที่ร้องเรียนมา
# 1. บล็อก IP คนร้าย
csf -d 203.0.113.10 "spam via compromised [email protected] 2026-07-15"
# 2. เปลี่ยนรหัส mailbox ผ่าน DA API
OWNER=$(grep -rl '^example.com$' /usr/local/directadmin/data/users/*/domains.list \
| sed -E 's#.*/users/([^/]+)/.*#\1#')
URL=$(/usr/local/directadmin/directadmin api-url --user="$OWNER")
curl -sk "${URL}/CMD_API_POP" \
--data-urlencode action=modify \
--data-urlencode domain=example.com \
--data-urlencode user=user \
--data-urlencode passwd="<new>" --data-urlencode passwd2="<new>"
# คาดหวัง: error=0&text=Account%20password%20changed
# 3. ยืนยันว่ารหัสเปลี่ยนจริง
ls -l /etc/virtual/example.com/last_password_change/user
# 4. ล้างสแปมในคิว — ดูก่อน ลบทีหลัง
exiqgrep -i -f 'example\.com'
exiqgrep -i -f 'example\.com' | xargs -r exim -Mrm
# 5. ลดเพดานของกล่องที่โดนลง (เคสนี้จาก 200 -> 50/วัน)
echo 50 > /etc/virtual/example.com/limit/user
ผลจริงคือคิวจาก 18 กลับมาเป็น 0 และไม่มีเมลจริงของลูกค้าโดนลบไปด้วยสักฉบับ
เคสที่รหัสรั่วโดยไม่มี failed auth เลย แปลว่ารหัสถูกขโมย ไม่ใช่ถูกเดา ต้นตอมักเป็น infostealer บนเครื่องที่เปิด mailbox นั้นอยู่ ถ้าส่งรหัสใหม่เข้ากล่องเดิม เท่ากับยื่นให้เขาซ้ำในนาทีเดียวกัน ต้องส่ง out-of-band เท่านั้น โทรหรือแชท และอีกข้อ ถ้ากล่องนั้นมีหลายเครื่องใช้ร่วมกัน ต้องนัดอัปเดตพร้อมกันทีเดียว ไม่งั้นเครื่องที่ยังถือรหัสเก่าจะวนลอง auth จนกลายเป็นอุปกรณ์รหัสค้างตัวใหม่ แล้ว LF_SMTPAUTH ก็จะแบน IP ออฟฟิศ ทั้งออฟฟิศส่งเมลไม่ได้อีกรอบ
ขอ delist ให้ผ่านตั้งแต่รอบแรก
ข้อสำคัญที่สุดคือ ห้ามยื่นขอปลดก่อนตัดต้นทางเสร็จ ยื่นแล้วสแปมยังไหลอยู่ = โดนขึ้นบัญชีกลับทันที และรอบถัดไปจะยากขึ้นกับบางเจ้า ผมเลยทำเป็น gate ว่าห้าไม่ครบไม่ยื่น
1) ต้นทางถูกตัดจริง — รหัสเปลี่ยนแล้ว IP ถูกบล็อกแล้ว และตรวจซ้ำแล้วว่าไม่มี authenticated send แปลก ๆ อีก · 2) คิวสะอาด ไม่มีสแปมค้างรอ retry เพราะระบบจะยิงหลักฐานใหม่ใส่ปลายทางเรื่อย ๆ · 3) เพดานส่งถูกรัดแล้วทั้งกล่องที่โดนและค่ากลางทั้งเครื่อง · 4) ลูกค้ารับทราบว่าต้องสแกน endpoint ที่ใช้กล่องนั้น ไม่งั้นรหัสใหม่รั่วซ้ำทางเดิม · 5) ตรวจแล้วว่าไม่มีกล่องอื่นในเครื่องโดนแบบเดียวกัน
ครบแล้วค่อยไล่ยื่นทีละที่: Spamhaus (ZEN/CSS/PBL) เข้าหน้า check.spamhaus.org ใส่ IP เซิร์ฟเวอร์แล้วทำตามฟอร์ม removal · cbl.abuseat.org ตอนนี้รวมอยู่ในระบบ Spamhaus แล้ว เช็คและปลดผ่านหน้าเดียวกัน ปกติหลุดเองเมื่อทราฟฟิกสแปมหยุด · ผู้ให้บริการเมลปลายทางรายใหญ่ แต่ละเจ้ามีฟอร์มของตัวเอง เจ้าที่ตอบ 554 ... found on DNSBLs มักแนบลิงก์หน้า error code มาให้ในบรรทัด reject อยู่แล้ว เข้าลิงก์นั้นตรง ๆ · DNSBL อื่น อย่าง spamcop / barracuda / sorbs / psbl ให้เช็คก่อน ติดค่อยยื่น หลายเจ้าหมดอายุเองใน 24-48 ชั่วโมงถ้าไม่มีสแปมใหม่
ตอนกรอกฟอร์มทุกเจ้าถามคล้ายกันหมดครับ: ระบุว่าเป็น mailbox เดียวที่ถูก compromise ไม่ใช่นโยบายของโฮสต์ · ระบุวันเวลาที่ตัดต้นทางและมาตรการที่ทำไปแล้ว · ระบุมาตรการกันซ้ำ · และอย่าใส่ข้อมูลลูกค้าเกินจำเป็น
ยื่นแล้วอย่าเพิ่งวางมือ ให้รันเช็ค DNSBL ซ้ำวันละครั้งจนหลุดครบ แล้วดู found on one or more DNSBL ใน mainlog ควบคู่ไปด้วย ตัวหลังนี่จริงกว่า เพราะเป็นเสียงจากปลายทางจริงไม่ใช่ผล lookup
กันซ้ำ: เลิกเฝ้า failed login อย่างเดียว
ส่วนตัวผมสรุปจากเคสนี้ว่าสิ่งที่ต้องเปลี่ยนไม่ใช่ "เพิ่มความเข้มของ brute-force rule" เพราะกฎพวกนั้นทำงานถูกต้องอยู่แล้ว มันแค่มองคนละทิศกับปัญหา ที่ต้องเพิ่มคือสัญญาณฝั่งขาออก
ที่ผมทำเพิ่มคือ cron รันคำสั่งนับผู้ส่ง authenticated ต่อวันกับคำสั่งเทียบ usage กับ limit แล้ว alert เมื่อกล่องไหนใช้เกินสัก 50% ของเพดานตัวเอง หรือกล่องที่ปกติส่งแทบเป็นศูนย์แล้วจู่ ๆ ขึ้นมาติดอันดับ top sender ตัว alert ที่จะจับเคสนี้ได้ตั้งแต่วันแรกจริง ๆ คือ alert ตอน login สำเร็จจากประเทศหรือ ASN ที่ไม่เคยเห็นสำหรับกล่องนั้น เพราะคนร้ายมาจาก IP เดียวประเทศเดียวตลอดสี่สัปดาห์
เรื่องเพดานก็ควรรื้อด้วย ค่ากลาง 200/วันทั้งเครื่องใช้ไม่ได้กับทุกกล่องหรอกครับ กล่องที่รับอย่างเดียวหรือกล่องระบบควรตั้งต่ำมาก ๆ ส่วนกล่องที่ส่งจริงก็ตั้งตามการใช้งานจริงของมัน ใครจะตั้งตัวเลขเท่าไหร่ก็ตามสะดวก แต่อย่าใช้ค่าเดียวทั้งเครื่องแล้วคิดว่าคุมได้
ปิดท้ายด้วยประเด็นที่เจ็บที่สุดของเคสนี้ครับ — shared IP แปลว่าความเสี่ยงร่วม กล่องเดียวของลูกค้ารายเดียวทำให้ผู้เช่ารายอื่นทั้งเครื่องส่งเมลไม่ออก ทั้งที่เขาไม่ได้ทำอะไรผิดเลย ทิศทางที่ควรเดินต่อคือบังคับ app password หรือ 2FA สำหรับ submission, จำกัดประเทศที่ submission ได้สำหรับกล่องที่ใช้ในประเทศอย่างเดียว, และดันเมลขาออกผ่าน smarthost ที่มี rate limit ของตัวเอง ส่วนต้นตอจริงของรหัสที่รั่วยังอยู่ที่ endpoint ของลูกค้าเสมอ เซิร์ฟเวอร์ทำได้แค่จำกัดความเสียหาย ตรงไหนผมอธิบายตกหล่นหรือพิมพ์ผิด เดี๋ยวกลับมาแก้เพิ่มให้ภายหลังครับ
อยากให้เราทำให้ไหม?
ให้เราช่วยติดตั้ง ตั้งค่า ย้ายระบบ หรือแก้ปัญหาเซิร์ฟเวอร์ เพื่อให้คุณมีเวลาไปทำงานที่สำคัญกว่า
เคยเจองานแบบนี้ไหม? ติดตรงไหน?
มาแลกวิธีกันต่อในบอร์ด LinuxThai



ความคิดเห็น 0
ยังไม่มีความคิดเห็น — เป็นคนแรกเลย