Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

สร้าง Port Scanner ด้วย Ruby: คู่มือทีละขั้นตอนสำหรับการสแกนเครือข่าย

ทำไมถึงอยากเขียน port scanner?

การเขียน port scanner เป็นวิธีดีๆ เรียนรู้พื้นฐานของโปรโตคอล TCP ซึ่งเป็น transport layer ที่โปรโตคอลอินเทอร์เน็ตส่วนใหญ่ใช้ (รวม HTTP และ SSH)

มันยังเป็น exercise ดีสำหรับเรียนรู้ network programming ใน Ruby

มาเริ่มด้วยการพูดถึง port!

Port คืออะไร?

พูดถึง port แล้วเราพูดถึงอะไรอยู่ดี? Port ที่ระดับ OS คือแค่ "file descriptor" ที่เชื่อมโยงกับ process

File descriptor คือตัวเลขที่ใช้ reference ไปยัง I/O channel ที่เปิดอยู่ เช่น stdout (standard output), network socket หรือไฟล์

เมื่อ OS รับ TCP/IP packet มันจะดู destination port และพยายามหา process ที่กำลัง listen บน port นั้น

ถ้ามี process ที่ listen packet จะถูกส่งต่อไป

ช่วงของ Port (Port Ranges)

มี port ทั้งหมด 65,535 port แต่ในทางปฏิบัติไม่ได้ใช้ทั้งหมด

Port แบ่งได้ 3 กลุ่ม:

ช่วง ชื่อ ตัวอย่าง
1-1023 Well-known ports 22 (SSH), 80 (HTTP), 443 (HTTPS)
1024-49151 Registered ports 3306 (MySQL), 5432 (PostgreSQL)
49152-65535 Ephemeral ports Chrome, Firefox

Port ในช่วงที่ 2 ถูกจัดสรรโดย IANA (Internet Assigned Numbers Authority)

ช่วงที่ 3 ใช้สำหรับ port "dynamic" หรือ "ephemeral" คือ port ที่ฝั่ง client ใช้รับข้อมูลจาก server

พื้นฐานการสื่อสาร TCP

ตอนนี้คุณคุ้นเคยกับ port แล้ว แต็ยังไม่พร้อมเขียน port scanner

ก่อนหน้านี้ ต้องเข้าใจว่าการที่ port "เปิด" หมายถึงอะไร จากนั้นตรวจสอบพฤติกรรมของ port เปิดและ port ปิดที่ระดับเครือข่าย เพื่อแยกแยะได้

ล่ะจะรู้ได้ไงว่า port เปิดอยู่?

Port เปิดหมายถึงมีแอปพลิเคชัน listen อีกฝั่ง และเราเข้าถึงได้ (ไม่ถูกบล็อกโดย firewall)

มา看看 TCP connection ใหม่เริ่มต้นอย่างไร

สร้าง Port Scanner ด้วย Ruby: คู่มือทีละขั้นตอนสำหรับการสแกนเครือข่าย

TCP connection ใหม่เริ่มด้วย packet SYN ซึ่งเป็นสัญลักษณ์การเริ่ม connection ใหม่

มี 3 ผลลัพธ์ที่เป็นไปได้:

  • Server ตอบด้วย SYN/ACK — หมายถึงพร้อมสร้าง connection
  • Server ตอบด้วย packet RST — หมายถึง connection ถูกปฏิเสธ
  • Server ไม่ตอบเลย (บาง firewall ทำแบบนี้ เรียกว่า DROP policy)

ถ้า client รับ SYN/ACK ก็สามารถสร้าง connection เสร็จโดยส่ง packet ACK

สิ่งนี้เรียกว่า "three-way handshake"

หมายเหตุ: วิธีดูสิ่งนี้เกิดขึ้นจริงคือใช้เครื่องมือเครือข่ายอย่าง tcpdump หรือ wireshark ทำให้เห็นทุก packet ที่เข้า-ออกระบบ

ตัวอย่าง connection จาก tshark (command-line interface ของ wireshark):

สร้าง Port Scanner ด้วย Ruby: คู่มือทีละขั้นตอนสำหรับการสแกนเครือข่าย

ตอนนี้คุณเข้าใจพื้นฐานการสร้าง TCP connection แล้ว มาเขียน port scanner 簡単กัน

มาเขียน Port Scanner กัน!

วิธีง่ายที่สุดคือเปิด TCP connection ใหม่ด้วย TCPSocket` แล้วพึ่งพาข้อเท็จจริงว่า connection ที่ถูกปฏิเสธจะ raise exception Errno::ECONNREFUSED

โค้ด:

require 'socket'
PORT = ARGV[0] || 22
HOST = ARGV[1] || 'localhost'

begin
  socket = TCPSocket.new(HOST, PORT)
  status = "open"
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT
  status = "closed"
end

puts "Port #{PORT} is #{status}."

โค้ดนี้มีข้อจำกัด:

สแกนได้ทีละ port แต่บ่อยครั้งอยากสแกนช่วง port เพื่อดู service ที่เปิดอยู่บน host

ข้อจำกัดอีกอย่าง: port ที่ไม่ตอบสนอง (ผลลัพธ์ที่ 3) จะทำให้รอประมาณ 20 วินาทีจน timeout

แก้ได้ด้วย combo ของ connect_nonblock, IO.select และ Socket (แทน TCPSocket ที่ connect ทันทีเมื่อสร้าง object)

ตัวอย่าง:

require 'socket'
TIMEOUT = 2

def scan_port(port)
  socket = Socket.new(:INET, :STREAM)
  remote_addr = Socket.sockaddr_in(port, 'www.example.com')

  begin
    socket.connect_nonblock(remote_addr)
  rescue Errno::EINPROGRESS
  end

  _, sockets, _ = IO.select(nil, [socket], nil, TIMEOUT)

  if sockets
    p "Port #{port} is open"
  else
    # Port is closed
  end
end

PORT_LIST = [21,22,23,25,53,80,443,3306,8080]
threads = []

PORT_LIST.each { |i| threads << Thread.new { scan_port(i) } }
threads.each(&:join)

IO.select จะรอจนกว่า socket จะพร้อมรับข้อมูล (หมายถึงเปิด) หรือจนกว่า TIMEOUT จะหมด แล้วค่อยสรุปว่า port ปิดหรือไม่ตอบ

ทั้งหมดนี้ดีสำหรับการเรียนรู้ แต่ถ้าต้องการ port scanner จริง ควรใช้ nmap

Nmap เป็น open source และพัฒนามามากกว่า 15 ปี

นี่คือ output ของ nmap:

สร้าง Port Scanner ด้วย Ruby: คู่มือทีละขั้นตอนสำหรับการสแกนเครือข่าย

ข้อมูลเยอะมาก ลองเขียนคอมเมนต์ 2 สิ่งที่คุณเรียนรู้ใหม่จากบทความนี้ 🙂

สรุป

ในบทความนี้ คุณเรียนรู้ว่า port คืออะไร ช่วง port ต่างๆ การสร้าง TCP connection (three-way handshake) และการเขียน port scanner แบบพื้นฐานด้วย Ruby

ถ้าชอบบทความนี้ อย่าลืมแชร์ให้เพื่อนๆ ได้อ่านด้วยนะครับ 🙂