Skip to content

Instantly share code, notes, and snippets.

@amkisko
Last active October 15, 2023 08:36
Show Gist options
  • Save amkisko/e0bf0f845bac886bc82566787906d28b to your computer and use it in GitHub Desktop.
Save amkisko/e0bf0f845bac886bc82566787906d28b to your computer and use it in GitHub Desktop.
Simple DPI with notifications using PacketFu, Redis, Discord and Telegram
# AUTHOR: Andrei Makarov (github.com/amkisko)
require "packetfu"
require "redis"
require "discordrb/webhooks"
require "telegram/bot"
require "time"
require "httpx"
require "dotenv"
Dotenv.load
$notifications = []
$redis = Redis.new
$monitor_name = ENV["MONITOR_NAME"]
$guard_name = ENV["GUARD_NAME"]
$service_name = ENV["SERVICE_NAME"]
["#{$monitor_name}:*", "#{$guard_name}:*"].each do |pattern|
$redis.scan_each(match: pattern) do |key|
puts "Removing #{key}"
$redis.del(key)
end
end
def addr_info(addr)
url = "http://ip-api.com/json/#{addr}?fields=status,message,country,countryCode,region,regionName,city,zip,lat,lon,timezone,isp,org,as,asname,reverse,mobile,proxy,hosting,query"
JSON.parse(HTTPX.get(url).body, symbolize_names: true)
rescue StandardError => e
p e
end
def monitor
puts "Starting monitor"
iface = PacketFu::Utils.default_int
ip = PacketFu::Utils.default_ip
port = ENV["EXTERNAL_PORT"]
proto = ENV["EXTERNAL_PROTO"]
cap = PacketFu::Capture.new(iface: iface)
cap.bpf(filter: "ip host #{ip} and #{proto} port #{port}")
cap.start
cap.stream.each do |p|
pkt = PacketFu::Packet.parse p
next unless pkt.ip_daddr == ip && pkt.udp_dst == port.to_i
pk = "#{$monitor_name}:#{proto}://#{pkt.ip_saddr}:#{pkt.udp_src}@#{pkt.ip_daddr}:#{pkt.udp_dst}"
dt = begin
val = $redis.get(pk)
Time.parse(val)
rescue TypeError, ArgumentError
nil
end
if dt.nil? || (Time.now.to_i - dt.to_i > 5*60)
info = [addr_info(pkt.ip_saddr)].map do |data|
[data.dig(:reverse), data.dig(:isp), data.dig(:country), data.dig(:city), data.dig(:mobile) ? "mobile" : nil, data.dig(:proxy) ? "proxy" : nil, data.dig(:hosting) ? "hosting" : nil].compact.join(" ")
end.first
$notifications << ["Detected activity", pkt.ip_saddr.to_s, info].compact.join(" ")
end
$redis.set(pk, Time.now)
sleep(1)
end
end
def telegram_msg(bot, msg)
send_notification("Bot envoked by #{msg.from.first_name} with #{msg.text}")
if msg.text == "/start"
bot.api.send_message(chat_id: msg.chat.id, text: "Hello, #{msg.from.first_name}")
elsif msg.text == "/stop"
bot.api.send_message(chat_id: msg.chat.id, text: "Bye, #{msg.from.first_name}")
elsif msg.text == "/status"
seed_set = !!$redis.get("#{$guard_name}:seed")
service_chat_id_eq = "#{msg.chat.id}" == $redis.get("#{$service_name}:service_chat_id")
text = "Seed: #{seed_set}\nService: #{service_chat_id_eq}\n"
bot.api.send_message(chat_id: msg.chat.id, text: text)
elsif msg.text == "/#{$guard_name}"
seed = "#{((0..9).to_a+('A'..'Z').to_a).shuffle.take(6).join}"
$redis.set("#{$guard_name}:seed", seed)
bot.api.send_message(chat_id: msg.chat.id, text: "New #{$guard_name}")
puts "New seed: #{seed}"
elsif msg.text.start_with? "/#{$guard_name}"
seed = $redis.get("#{$guard_name}:seed")
if seed && msg.text.gsub(/^\/#{$guard_name}/, "").strip == "seed #{seed}"
$redis.del("#{$guard_name}:seed")
$redis.set("#{$service_name}:service_chat_id", msg.chat.id)
bot.api.send_message(chat_id: msg.chat.id, text: "Welcome, #{msg.from.first_name} (#{msg.chat.id})")
else
bot.api.send_message(chat_id: msg.chat.id, text: "Not authenticated")
end
end
end
def telegram_bot
puts "Starting telegram bot"
Telegram::Bot::Client.run(ENV["TELEGRAM_TOKEN"]) do |bot|
bot.listen do |message|
telegram_msg(bot, message)
sleep(1)
end
end
puts "Stopping telegram bot"
end
def send_notification(content)
client = Discordrb::Webhooks::Client.new(url: ENV["DISCORD_WEBHOOK_URL"])
client.execute do |builder|
builder.content = content
end
Telegram::Bot::Client.run(ENV["TELEGRAM_TOKEN"]) do |bot|
chat_id = $redis.get("#{$service_name}:service_chat_id")
if chat_id
bot.api.send_message(chat_id: chat_id, text: content)
end
end
end
def notifier
puts "Starting notifier"
while 1
sleep(1)
next unless $notifications.size > 0
data = $notifications.shift
send_notification(data)
end
puts "Stopping notifier"
end
threads = [
Thread.new{monitor},
Thread.new{telegram_bot},
Thread.new{notifier}
]
threads.each(&:join)
TELEGRAM_TOKEN="123456:ABCDEF"
DISCORD_WEBHOOK_URL="https://discordapp.com/api/webhooks/123456/abcdefg"
EXTERNAL_IP="123.123.123.123"
EXTERNAL_PORT=2345
EXTERNAL_PROTO="udp"
MONITOR_NAME="packetfu"
GUARD_NAME="watchdog"
SERVICE_NAME="monitor"
source "https://rubygems.org"
gem "dotenv"
gem "packetfu"
gem "redis"
gem "discordrb-webhooks"
gem "telegram-bot-ruby"
gem "httpx"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment