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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
|
# main logic
from sys_init import setup
setup()
import pyinotify
import subprocess
import re
import pwd
import os
import json
import atexit
import fcntl
def get_ssh_port(pid):
'''
/var/log/secure
Jan 31 07:50:28 vultr sshd[43690]: Accepted publickey for root from 210.10.76.5 port 43730 ssh2: ED25519 SHA256:qz9ffMCb3vPlabn3ZHee00qIPBxkDiUiVSorcUkGdII
Jan 31 07:50:28 vultr sshd[43690]: pam_unix(sshd:session): session opened for user root(uid=0) by root(uid=0)
Jan 31 07:50:29 vultr sshd[43693]: Received disconnect from 210.10.76.5 port 43730:11: disconnected by user
Jan 31 07:50:29 vultr sshd[43693]: Disconnected from user root 210.10.76.5 port 43730
Jan 31 07:50:29 vultr sshd[43690]: pam_unix(sshd:session): session closed for user root
'''
'''
lsof -i -n
sshd 1845 root 4u IPv4 23137 0t0 TCP 45.32.108.159:ssh->210.10.76.5:45460 (ESTABLISHED)
sshd 1848 root 4u IPv4 23137 0t0 TCP 45.32.108.159:ssh->210.10.76.5:45460 (ESTABLISHED)
sshd 1848 root 8u IPv4 23259 0t0 TCP *:44699 (LISTEN)
sshd 1848 root 9u IPv6 23260 0t0 TCP *:44699 (LISTEN)
'''
'''
# less efficient but readable
pid = '33216'
pids = []
port = -1
for l in lines:
l = l.split()
if pid == l[1]:
commonstring = l[8]
for l2 in lines:
if commonstring in l2.split():
pids.append(l2.split()[1])
for i in pids:
for l in lines:
l = l.split()
if i in l[1] and "*:" in l[8]:
port = l[8][2:]
'''
try:
lines = subprocess.check_output("lsof -i -n | grep sshd", shell=True, text=True).splitlines()
pid = str(pid)
pids = {l.split()[1] for l in lines
if pid == l.split()[1] or
l.split()[8] in [x.split()[8] for x in lines if x.split()[1] == pid]}
port = next(l.split()[8][2:] for l in lines if l.split()[1] in pids and "*:" in l.split()[8])
return port
except:
return -1
def get_keyname(fingerprint):
auth_file = os.path.join(os.path.expanduser(f"~{user}"), ".ssh", "authorized_keys")
try:
result = subprocess.run(['ssh-keygen', '-lf', f'{auth_file}'], capture_output=True, text=True, shell=False, check=True)
# Raises CalledProcessError if command fails, for check = true
#256 SHA256:d9CBxSLBLzLjYGZDCsO6V+lddlN/elK1hGDBS56cbTo user@host (ED25519)
for line in result.stdout.strip().split('\n'):
parts = line.split()
if fingerprint in parts[1]:
return parts[2]
return 'not found'
except subprocess.CalledProcessError as e:
print(f"Error executing command: {e}")
return None
except Exception as e:
print(f"Error processing output: {e}")
return None
def write_data(data):
with open('/tmp/ssh_sessions.json', 'w') as f:
# Get exclusive lock for writing
fcntl.flock(f.fileno(), fcntl.LOCK_EX)
try:
# Clear file and write new data
f.seek(0)
json.dump(data, f)
f.truncate()
finally:
fcntl.flock(f.fileno(), fcntl.LOCK_UN)
def handle_log_change(event):
global last_position
with open(event.pathname, 'r') as f:
# Go to end and read new lines
f.seek(last_position)
new_lines = f.readlines()
last_position = f.tell()
for line in new_lines:
#print(f"New log: {line.strip()}")
line = line.strip()
if f"Accepted publickey for {user}" in line:
pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1)
port = get_ssh_port(pid)
keyname = get_keyname(line.split()[15])
srcip = line.split()[10]
#print(pid, port, keyname, srcip)
#ssh_sessions[pid] = [srcip, keyname, port]
ssh_sessions[srcip] = {
'srcip': srcip,
'pid' : pid,
'key': keyname,
'pubport': port,
}
write_data(ssh_sessions)
if "pam_unix(sshd:session): session closed" in line:
pid = re.search(r'\[(\d+)\]', line.split()[4]).group(1)
if pid in ssh_sessions:
del ssh_sessions[pid]
else:
print("WARNING! PID NOT FOUND IN SESSION CACHE!")
write_data(ssh_sessions)
print("db: ",ssh_sessions)
user = "root"
# init last_position
global last_position
with open('/var/log/secure', 'r') as f:
f.seek(0, 2)
last_position = f.tell()
global ssh_sessions
ssh_sessions = {}
wm = pyinotify.WatchManager()
wm.add_watch('/var/log/secure', pyinotify.IN_MODIFY, handle_log_change)
notifier = pyinotify.Notifier(wm)
print("Watching /var/log/secure...")
notifier.loop()
|