Browse Source

add antissh bot proof of concept

William Pitcock 1 year ago
parent
commit
fce3caedef
3 changed files with 102 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 12
    0
      antissh.conf.example
  3. 89
    0
      antissh.py

+ 1
- 0
.gitignore View File

@@ -94,3 +94,4 @@ ENV/
94 94
 # Rope project settings
95 95
 .ropeproject
96 96
 
97
+antissh.conf

+ 12
- 0
antissh.conf.example View File

@@ -0,0 +1,12 @@
1
+[host]
2
+host = irc.dereferenced.org
3
+port = 6667
4
+ssl = false
5
+oper = x x
6
+modes = +s +c
7
+nickname = antissh
8
+kline_cmd = KLINE 86400 *@{ip} :Vulnerable SSH daemon found on this host.  Please fix your SSH daemon and try again later.\r\n
9
+
10
+[target]
11
+host = 162.220.112.99
12
+port = 6667

+ 89
- 0
antissh.py View File

@@ -0,0 +1,89 @@
1
+#!/usr/bin/env python3
2
+# dependencies: asyncssh, asyncio-irc
3
+
4
+import asyncio
5
+import asyncssh
6
+import sys
7
+import re
8
+from asyncirc import irc
9
+from configparser import ConfigParser
10
+
11
+config = ConfigParser()
12
+config.read(sys.argv[1])
13
+
14
+TARGET_IP = config.get('target', 'ip', fallback='162.220.112.99')
15
+TARGET_PORT = config.getint('target', 'port', fallback=6667)
16
+HOST = config.get('host', 'hostname', fallback='irc.dereferenced.org')
17
+PORT = config.getint('host', 'port', fallback=6667)
18
+USE_SSL = config.getboolean('host', 'ssl', fallback=False)
19
+OPER = config.get('host', 'oper', fallback='x x')
20
+NICKNAME = config.get('host', 'nickname', fallback='antissh')
21
+MODES = config.get('host', 'modes', fallback='+s +c')
22
+KLINE_CMD_TEMPLATE = config.get('host', 'kline_cmd', fallback='KLINE 86400 *@{ip} :Vulnerable SSH daemon found on this host.  Please fix your SSH daemon and try again later.\r\n')
23
+
24
+# advanced users only
25
+# charybdis uses:
26
+# *** Notice -- Client connecting: kaniini_ (~kaniini@127.0.0.1) [127.0.0.1] {users} [William Pitcock]
27
+# re.findall(r'\[[0-9a-f\.:]+\]', message)
28
+IP_REGEX = re.compile(r'\[[0-9a-f\.:]+\]')
29
+POSITIVE_HIT_STRING = b'Looking up your hostname'
30
+DEFAULT_CREDENTIALS = [
31
+    ('ADMIN', 'ADMIN'),
32
+    ('root', '')
33
+]
34
+
35
+
36
+async def check_with_credentials(ip, target_ip, target_port, username, password):
37
+    """Checks whether a given username or password works to open a direct TCP session."""
38
+    try:
39
+        async with asyncssh.connect(ip, username=username, password=password, known_hosts=None) as conn:
40
+            try:
41
+                reader, writer = await conn.open_connection(target_ip, target_port)
42
+            except asyncssh.Error:
43
+                return False
44
+
45
+            writer.write(b'\r\n')
46
+            writer.write_eof()
47
+
48
+            response = await reader.read()
49
+            return POSITIVE_HIT_STRING in response
50
+    except (asyncssh.Error, OSError):
51
+        return False
52
+
53
+
54
+async def check_with_credentials_group(ip, target_ip, target_port, credentials_group=DEFAULT_CREDENTIALS):
55
+    futures = [check_with_credentials(ip, target_ip, target_port, c[0], c[1]) for c in credentials_group]
56
+    results = await asyncio.gather(*futures)
57
+
58
+    return True in results
59
+
60
+
61
+async def check_connecting_client(bot, ip):
62
+    result = await check_with_credentials_group(ip, TARGET_IP, TARGET_PORT)
63
+    if result:
64
+        print('found vulnerable SSH daemon at', ip)
65
+        bot.writeln(KLINE_CMD_TEMPLATE.format(ip=ip))
66
+
67
+
68
+def main():
69
+    bot = irc.connect(HOST, PORT, use_ssl=USE_SSL)
70
+    bot.register(NICKNAME, "antissh", "antissh proxy checking bot")
71
+
72
+    @bot.on('irc-001')
73
+    def handle_connection_start(message):
74
+        bot.writeln("OPER {}\r\n".format(OPER))
75
+        bot.writeln("MODE {0} {1}\r\n".format(NICKNAME, MODES))
76
+
77
+    @bot.on('notice')
78
+    def handle_connection_notice(message, user, target, text):
79
+        if 'connecting' not in text:
80
+            return
81
+
82
+        ip = IP_REGEX.findall(text)[0][1:-1]
83
+        asyncio.ensure_future(check_connecting_client(bot, ip))
84
+
85
+    asyncio.get_event_loop().run_forever()
86
+
87
+
88
+if __name__ == '__main__':
89
+    main()