cowrie 2.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (322) hide show
  1. backend_pool/README.md +43 -0
  2. backend_pool/__init__.py +0 -0
  3. backend_pool/libvirt/__init__.py +0 -0
  4. backend_pool/libvirt/backend_service.py +216 -0
  5. backend_pool/libvirt/guest_handler.py +112 -0
  6. backend_pool/libvirt/network_handler.py +100 -0
  7. backend_pool/libvirt/snapshot_handler.py +33 -0
  8. backend_pool/nat.py +127 -0
  9. backend_pool/pool_server.py +225 -0
  10. backend_pool/pool_service.py +481 -0
  11. backend_pool/ssh_exec.py +109 -0
  12. backend_pool/telnet_exec.py +156 -0
  13. backend_pool/util.py +81 -0
  14. cowrie/__init__.py +12 -0
  15. cowrie/_version.py +34 -0
  16. cowrie/commands/__init__.py +61 -0
  17. cowrie/commands/adduser.py +104 -0
  18. cowrie/commands/apt.py +219 -0
  19. cowrie/commands/awk.py +233 -0
  20. cowrie/commands/base.py +1117 -0
  21. cowrie/commands/base64.py +150 -0
  22. cowrie/commands/bash.py +83 -0
  23. cowrie/commands/busybox.py +107 -0
  24. cowrie/commands/cat.py +139 -0
  25. cowrie/commands/chmod.py +145 -0
  26. cowrie/commands/chpasswd.py +102 -0
  27. cowrie/commands/crontab.py +78 -0
  28. cowrie/commands/curl.py +470 -0
  29. cowrie/commands/dd.py +146 -0
  30. cowrie/commands/dig.py +202 -0
  31. cowrie/commands/du.py +131 -0
  32. cowrie/commands/env.py +36 -0
  33. cowrie/commands/ethtool.py +82 -0
  34. cowrie/commands/find.py +114 -0
  35. cowrie/commands/finger.py +129 -0
  36. cowrie/commands/free.py +114 -0
  37. cowrie/commands/fs.py +661 -0
  38. cowrie/commands/ftpget.py +247 -0
  39. cowrie/commands/gcc.py +309 -0
  40. cowrie/commands/git.py +172 -0
  41. cowrie/commands/groups.py +92 -0
  42. cowrie/commands/ifconfig.py +82 -0
  43. cowrie/commands/iptables.py +482 -0
  44. cowrie/commands/last.py +49 -0
  45. cowrie/commands/locate.py +99 -0
  46. cowrie/commands/ls.py +231 -0
  47. cowrie/commands/lspci.py +44 -0
  48. cowrie/commands/nc.py +135 -0
  49. cowrie/commands/netstat.py +205 -0
  50. cowrie/commands/nohup.py +26 -0
  51. cowrie/commands/perl.py +125 -0
  52. cowrie/commands/ping.py +126 -0
  53. cowrie/commands/python.py +135 -0
  54. cowrie/commands/scp.py +226 -0
  55. cowrie/commands/service.py +121 -0
  56. cowrie/commands/sleep.py +45 -0
  57. cowrie/commands/ssh.py +152 -0
  58. cowrie/commands/sudo.py +137 -0
  59. cowrie/commands/tar.py +101 -0
  60. cowrie/commands/tee.py +157 -0
  61. cowrie/commands/tftp.py +138 -0
  62. cowrie/commands/ulimit.py +47 -0
  63. cowrie/commands/uname.py +183 -0
  64. cowrie/commands/uniq.py +87 -0
  65. cowrie/commands/unzip.py +132 -0
  66. cowrie/commands/uptime.py +24 -0
  67. cowrie/commands/wc.py +136 -0
  68. cowrie/commands/wget.py +368 -0
  69. cowrie/commands/which.py +32 -0
  70. cowrie/commands/yum.py +330 -0
  71. cowrie/core/__init__.py +0 -0
  72. cowrie/core/artifact.py +97 -0
  73. cowrie/core/auth.py +264 -0
  74. cowrie/core/cef.py +117 -0
  75. cowrie/core/checkers.py +147 -0
  76. cowrie/core/config.py +74 -0
  77. cowrie/core/credentials.py +101 -0
  78. cowrie/core/network.py +113 -0
  79. cowrie/core/output.py +245 -0
  80. cowrie/core/realm.py +57 -0
  81. cowrie/core/ttylog.py +92 -0
  82. cowrie/core/utils.py +135 -0
  83. cowrie/data/arch/bsd-aarch64-lsb +0 -0
  84. cowrie/data/arch/bsd-aarch64-msb +0 -0
  85. cowrie/data/arch/bsd-bfin-msb +0 -0
  86. cowrie/data/arch/bsd-mips-lsb +0 -0
  87. cowrie/data/arch/bsd-mips-msb +0 -0
  88. cowrie/data/arch/bsd-mips64-lsb +0 -0
  89. cowrie/data/arch/bsd-mips64-msb +0 -0
  90. cowrie/data/arch/bsd-powepc-msb +0 -0
  91. cowrie/data/arch/bsd-powepc64-lsb +0 -0
  92. cowrie/data/arch/bsd-riscv64-lsb +0 -0
  93. cowrie/data/arch/bsd-sparc-msb +0 -0
  94. cowrie/data/arch/bsd-sparc64-msb +0 -0
  95. cowrie/data/arch/bsd-x32-lsb +0 -0
  96. cowrie/data/arch/bsd-x64-lsb +0 -0
  97. cowrie/data/arch/linux-aarch64-lsb +0 -0
  98. cowrie/data/arch/linux-aarch64-msb +0 -0
  99. cowrie/data/arch/linux-alpha-lsb +0 -0
  100. cowrie/data/arch/linux-am33-lsb +0 -0
  101. cowrie/data/arch/linux-arc-lsb +0 -0
  102. cowrie/data/arch/linux-arc-msb +0 -0
  103. cowrie/data/arch/linux-arm-lsb +0 -0
  104. cowrie/data/arch/linux-arm-msb +0 -0
  105. cowrie/data/arch/linux-avr32-lsb +0 -0
  106. cowrie/data/arch/linux-bfin-lsb +0 -0
  107. cowrie/data/arch/linux-c6x-lsb +0 -0
  108. cowrie/data/arch/linux-c6x-msb +0 -0
  109. cowrie/data/arch/linux-cris-lsb +0 -0
  110. cowrie/data/arch/linux-frv-msb +0 -0
  111. cowrie/data/arch/linux-h8300-msb +0 -0
  112. cowrie/data/arch/linux-hppa-msb +0 -0
  113. cowrie/data/arch/linux-hppa64-msb +0 -0
  114. cowrie/data/arch/linux-ia64-lsb +0 -0
  115. cowrie/data/arch/linux-m32r-msb +0 -0
  116. cowrie/data/arch/linux-m68k-msb +0 -0
  117. cowrie/data/arch/linux-microblaze-msb +0 -0
  118. cowrie/data/arch/linux-mips-lsb +0 -0
  119. cowrie/data/arch/linux-mips-msb +0 -0
  120. cowrie/data/arch/linux-mips64-lsb +0 -0
  121. cowrie/data/arch/linux-mips64-msb +0 -0
  122. cowrie/data/arch/linux-mn10300-lsb +0 -0
  123. cowrie/data/arch/linux-nios-lsb +0 -0
  124. cowrie/data/arch/linux-nios-msb +0 -0
  125. cowrie/data/arch/linux-powerpc-lsb +0 -0
  126. cowrie/data/arch/linux-powerpc-msb +0 -0
  127. cowrie/data/arch/linux-powerpc64-lsb +0 -0
  128. cowrie/data/arch/linux-powerpc64-msb +0 -0
  129. cowrie/data/arch/linux-riscv64-lsb +0 -0
  130. cowrie/data/arch/linux-s390x-msb +0 -0
  131. cowrie/data/arch/linux-sh-lsb +0 -0
  132. cowrie/data/arch/linux-sh-msb +0 -0
  133. cowrie/data/arch/linux-sparc-msb +0 -0
  134. cowrie/data/arch/linux-sparc64-msb +0 -0
  135. cowrie/data/arch/linux-tilegx-lsb +0 -0
  136. cowrie/data/arch/linux-tilegx-msb +0 -0
  137. cowrie/data/arch/linux-tilegx64-lsb +0 -0
  138. cowrie/data/arch/linux-tilegx64-msb +0 -0
  139. cowrie/data/arch/linux-x64-lsb +0 -0
  140. cowrie/data/arch/linux-x86-lsb +0 -0
  141. cowrie/data/arch/linux-xtensa-msb +0 -0
  142. cowrie/data/arch/osx-x32-lsb +0 -0
  143. cowrie/data/arch/osx-x64-lsb +0 -0
  144. cowrie/data/arch/src/README +10 -0
  145. cowrie/data/arch/src/nop-arc.asm +4 -0
  146. cowrie/data/arch/src/nop-bfin.asm +4 -0
  147. cowrie/data/arch/src/nop-hppa64.asm +4 -0
  148. cowrie/data/arch/src/nop-ia64.asm +4 -0
  149. cowrie/data/arch/src/nop-mips.asm +4 -0
  150. cowrie/data/arch/src/nop-mips64.asm +4 -0
  151. cowrie/data/arch/src/nop-osx.asm +4 -0
  152. cowrie/data/arch/src/nop.asm +4 -0
  153. cowrie/data/cmdoutput.json +955 -0
  154. cowrie/data/fs.pickle +0 -0
  155. cowrie/data/honeyfs/etc/group +44 -0
  156. cowrie/data/honeyfs/etc/host.conf +1 -0
  157. cowrie/data/honeyfs/etc/hostname +1 -0
  158. cowrie/data/honeyfs/etc/hosts +7 -0
  159. cowrie/data/honeyfs/etc/inittab +69 -0
  160. cowrie/data/honeyfs/etc/issue +2 -0
  161. cowrie/data/honeyfs/etc/issue.net +2 -0
  162. cowrie/data/honeyfs/etc/motd +7 -0
  163. cowrie/data/honeyfs/etc/passwd +21 -0
  164. cowrie/data/honeyfs/etc/resolv.conf +2 -0
  165. cowrie/data/honeyfs/etc/shadow +21 -0
  166. cowrie/data/honeyfs/proc/cpuinfo +50 -0
  167. cowrie/data/honeyfs/proc/meminfo +42 -0
  168. cowrie/data/honeyfs/proc/modules +92 -0
  169. cowrie/data/honeyfs/proc/mounts +14 -0
  170. cowrie/data/honeyfs/proc/net/arp +3 -0
  171. cowrie/data/honeyfs/proc/version +1 -0
  172. cowrie/data/pool_configs/aarch64_guest.xml +64 -0
  173. cowrie/data/pool_configs/default_filter.xml +33 -0
  174. cowrie/data/pool_configs/default_guest.xml +66 -0
  175. cowrie/data/pool_configs/default_network.xml +12 -0
  176. cowrie/data/pool_configs/wrt_arm_guest.xml +44 -0
  177. cowrie/data/pool_configs/wrt_x86_guest.xml +81 -0
  178. cowrie/data/txtcmds/bin/df +7 -0
  179. cowrie/data/txtcmds/bin/dmesg +572 -0
  180. cowrie/data/txtcmds/bin/enable +58 -0
  181. cowrie/data/txtcmds/bin/izconfig +1 -0
  182. cowrie/data/txtcmds/bin/mount +7 -0
  183. cowrie/data/txtcmds/bin/stty +0 -0
  184. cowrie/data/txtcmds/bin/sync +0 -0
  185. cowrie/data/txtcmds/bin/ulimit +16 -0
  186. cowrie/data/txtcmds/usr/bin/clear +1 -0
  187. cowrie/data/txtcmds/usr/bin/emacs +1 -0
  188. cowrie/data/txtcmds/usr/bin/getconf +1 -0
  189. cowrie/data/txtcmds/usr/bin/killall +1 -0
  190. cowrie/data/txtcmds/usr/bin/locate +1 -0
  191. cowrie/data/txtcmds/usr/bin/lscpu +21 -0
  192. cowrie/data/txtcmds/usr/bin/make +1 -0
  193. cowrie/data/txtcmds/usr/bin/nano +1 -0
  194. cowrie/data/txtcmds/usr/bin/nproc +1 -0
  195. cowrie/data/txtcmds/usr/bin/pico +1 -0
  196. cowrie/data/txtcmds/usr/bin/pkill +0 -0
  197. cowrie/data/txtcmds/usr/bin/top +1 -0
  198. cowrie/data/txtcmds/usr/bin/vi +1 -0
  199. cowrie/data/txtcmds/usr/bin/vim +1 -0
  200. cowrie/data/txtcmds/usr/sbin/vipw +2 -0
  201. cowrie/insults/__init__.py +0 -0
  202. cowrie/insults/insults.py +246 -0
  203. cowrie/llm/README.md +60 -0
  204. cowrie/llm/llm.py +222 -0
  205. cowrie/output/README.md +16 -0
  206. cowrie/output/abuseipdb.py +496 -0
  207. cowrie/output/axiom.py +58 -0
  208. cowrie/output/crashreporter.py +79 -0
  209. cowrie/output/csirtg.py +99 -0
  210. cowrie/output/cuckoo.py +173 -0
  211. cowrie/output/datadog.py +67 -0
  212. cowrie/output/discord.py +46 -0
  213. cowrie/output/dshield.py +174 -0
  214. cowrie/output/elasticsearch.py +125 -0
  215. cowrie/output/graylog.py +61 -0
  216. cowrie/output/greynoise.py +95 -0
  217. cowrie/output/hpfeeds3.py +118 -0
  218. cowrie/output/influx.py +217 -0
  219. cowrie/output/jsonlog.py +81 -0
  220. cowrie/output/localsyslog.py +74 -0
  221. cowrie/output/malshare.py +104 -0
  222. cowrie/output/misp.py +590 -0
  223. cowrie/output/mongodb.py +132 -0
  224. cowrie/output/mysql.py +291 -0
  225. cowrie/output/oraclecloud.py +118 -0
  226. cowrie/output/postgresql.py +268 -0
  227. cowrie/output/prometheus.py +197 -0
  228. cowrie/output/redis.py +62 -0
  229. cowrie/output/remotesyslog.py +45 -0
  230. cowrie/output/rethinkdblog.py +47 -0
  231. cowrie/output/reversedns.py +114 -0
  232. cowrie/output/rmq.py +134 -0
  233. cowrie/output/s3.py +98 -0
  234. cowrie/output/slack.py +209 -0
  235. cowrie/output/socketlog.py +42 -0
  236. cowrie/output/splunk.py +120 -0
  237. cowrie/output/sqlite.py +209 -0
  238. cowrie/output/telegram.py +64 -0
  239. cowrie/output/textlog.py +59 -0
  240. cowrie/output/virustotal.py +500 -0
  241. cowrie/output/xmpp.py +97 -0
  242. cowrie/pool_interface/__init__.py +0 -0
  243. cowrie/pool_interface/client.py +126 -0
  244. cowrie/pool_interface/handler.py +73 -0
  245. cowrie/python/__init__.py +0 -0
  246. cowrie/python/logfile.py +54 -0
  247. cowrie/scripts/__init__.py +0 -0
  248. cowrie/scripts/asciinema.py +136 -0
  249. cowrie/scripts/createdynamicprocess.py +62 -0
  250. cowrie/scripts/createfs.py +197 -0
  251. cowrie/scripts/fsctl.py +781 -0
  252. cowrie/scripts/playlog.py +143 -0
  253. cowrie/shell/__init__.py +0 -0
  254. cowrie/shell/avatar.py +79 -0
  255. cowrie/shell/command.py +210 -0
  256. cowrie/shell/customparser.py +62 -0
  257. cowrie/shell/filetransfer.py +235 -0
  258. cowrie/shell/fs.py +626 -0
  259. cowrie/shell/honeypot.py +699 -0
  260. cowrie/shell/protocol.py +392 -0
  261. cowrie/shell/pwd.py +223 -0
  262. cowrie/shell/server.py +92 -0
  263. cowrie/shell/session.py +93 -0
  264. cowrie/shell/stdio.py +90 -0
  265. cowrie/ssh/__init__.py +0 -0
  266. cowrie/ssh/channel.py +113 -0
  267. cowrie/ssh/connection.py +68 -0
  268. cowrie/ssh/factory.py +186 -0
  269. cowrie/ssh/forwarding.py +188 -0
  270. cowrie/ssh/keys.py +129 -0
  271. cowrie/ssh/session.py +81 -0
  272. cowrie/ssh/transport.py +288 -0
  273. cowrie/ssh/userauth.py +185 -0
  274. cowrie/ssh_proxy/README.md +44 -0
  275. cowrie/ssh_proxy/__init__.py +0 -0
  276. cowrie/ssh_proxy/client_transport.py +184 -0
  277. cowrie/ssh_proxy/protocols/__init__.py +0 -0
  278. cowrie/ssh_proxy/protocols/base_protocol.py +89 -0
  279. cowrie/ssh_proxy/protocols/exec_term.py +104 -0
  280. cowrie/ssh_proxy/protocols/port_forward.py +42 -0
  281. cowrie/ssh_proxy/protocols/sftp.py +374 -0
  282. cowrie/ssh_proxy/protocols/ssh.py +437 -0
  283. cowrie/ssh_proxy/protocols/term.py +226 -0
  284. cowrie/ssh_proxy/server_transport.py +434 -0
  285. cowrie/ssh_proxy/userauth.py +37 -0
  286. cowrie/ssh_proxy/util.py +17 -0
  287. cowrie/telnet/__init__.py +0 -0
  288. cowrie/telnet/factory.py +78 -0
  289. cowrie/telnet/session.py +165 -0
  290. cowrie/telnet/transport.py +114 -0
  291. cowrie/telnet/userauth.py +152 -0
  292. cowrie/telnet_proxy/README.md +14 -0
  293. cowrie/telnet_proxy/__init__.py +0 -0
  294. cowrie/telnet_proxy/client_transport.py +70 -0
  295. cowrie/telnet_proxy/handler.py +341 -0
  296. cowrie/telnet_proxy/server_transport.py +204 -0
  297. cowrie/test/__init__.py +0 -0
  298. cowrie/test/fake_server.py +56 -0
  299. cowrie/test/fake_transport.py +182 -0
  300. cowrie/test/proxy_compare.py +101 -0
  301. cowrie/test/test_awk.py +54 -0
  302. cowrie/test/test_base64.py +43 -0
  303. cowrie/test/test_base_commands.py +334 -0
  304. cowrie/test/test_cat.py +51 -0
  305. cowrie/test/test_chmod.py +118 -0
  306. cowrie/test/test_echo.py +224 -0
  307. cowrie/test/test_ftpget.py +52 -0
  308. cowrie/test/test_ls.py +57 -0
  309. cowrie/test/test_network.py +84 -0
  310. cowrie/test/test_proxy.py +105 -0
  311. cowrie/test/test_tee.py +63 -0
  312. cowrie/test/test_tftp.py +37 -0
  313. cowrie/test/test_uniq.py +50 -0
  314. cowrie/test/test_utils.py +90 -0
  315. cowrie-2.7.0.data/scripts/cowrie +240 -0
  316. cowrie-2.7.0.dist-info/METADATA +190 -0
  317. cowrie-2.7.0.dist-info/RECORD +322 -0
  318. cowrie-2.7.0.dist-info/WHEEL +5 -0
  319. cowrie-2.7.0.dist-info/entry_points.txt +5 -0
  320. cowrie-2.7.0.dist-info/licenses/LICENSE.rst +31 -0
  321. cowrie-2.7.0.dist-info/top_level.txt +3 -0
  322. twisted/plugins/cowrie_plugin.py +255 -0
backend_pool/README.md ADDED
@@ -0,0 +1,43 @@
1
+ QEMU/libvirt Python examples to handle a guest
2
+
3
+ # Developer Guide
4
+ We'll start by looking at the classes that compose the Backend Pool, from "outermost" to the inner, specific classes.
5
+
6
+ ## pool_server.py
7
+ The main interface of the backend pool is exposed as a TCP server in _pool\_server.py_. The protocol is a very simple
8
+ wire protocol, always composed of an op-code, a status code (for responses), and any needed data thereafter.
9
+
10
+ ## pool_service.py
11
+ The server interfaces exposes a producer-consumer infinite loop that runs on _pool\_service.py_.
12
+
13
+ The **producer** is an infinite loop started by the server, and runs every 5 seconds. It creates VMs up to the
14
+ configured limit, checks which VMs become available (by testing if they accept SSH and/or Telnet connections), and
15
+ destroys VMs that are no longer needed.
16
+
17
+ **Consumer** methods are called by server request, and basically involve requesting and freeing VMs. All operations on
18
+ shared data in the producer-consumer are guarded by a lock, since there may be concurrent requests. The lock protects
19
+ the _guests_ list, which contains references for each VM backend (in our case libvirt/QEMU instances).
20
+
21
+ Since we won't be dealing with a very large number of VMs (never more than 100, we find that a single simple lock is
22
+ enough.
23
+
24
+ The Pool Service expects to find a "backend service" with a given interface:
25
+ * A method to initialise the backend interface and environment (start_backend), stop it and destroy the current
26
+ environment (stop_backend), and shutdown it permanently for the current execution (shutdown_backend).
27
+ * A method to create a new guest (create_guest)
28
+ * A method to destroy a guest (destroy_guest)
29
+
30
+ Currently the service supports a libvirt/QEMU backend. However, by splitting the logic from generic guest handling /
31
+ interaction with main Cowrie, from the logic to create guests in a low-level perspective, we hope to ease development
32
+ of different kinds of backends in the future.
33
+
34
+ ## libvirt classes
35
+ The main class for libvirt is _backend\_service.py_, and implements the interface discussed above. Guest, network and
36
+ snapshot handlers deal with those specific components of libvirt's handling.
37
+
38
+ Initialising libvirt involves connecting to the running system daemon, creating a network filter to restrict guest's
39
+ Internet access, and creating a "cowrie" network in libvirt.
40
+
41
+ Guest creation is started by creating a snapshot from the base qcow2 image defined in the configs, and instantiating
42
+ a guest from the XML provided. The Guest Handler replaces templates ("{guest_name}") with user configs for the wanted
43
+ guest. If the XML provided does not contain templates, then no replacement takes place, naturally.
File without changes
File without changes
@@ -0,0 +1,216 @@
1
+ """
2
+ backend service
3
+ """
4
+
5
+ # Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
6
+ # See the COPYRIGHT file for more information
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import random
12
+ import sys
13
+ import uuid
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from collections.abc import Callable
18
+
19
+ from twisted.python import log
20
+
21
+ from cowrie.core.config import CowrieConfig
22
+
23
+ import backend_pool.libvirt.guest_handler
24
+ import backend_pool.libvirt.network_handler
25
+ import backend_pool.util
26
+
27
+
28
+ class LibvirtError(Exception):
29
+ pass
30
+
31
+
32
+ class LibvirtBackendService:
33
+ def __init__(self) -> None:
34
+ # lazy import to avoid exception if not using the backend_pool
35
+ # and libvirt not installed (#1185)
36
+ import libvirt
37
+
38
+ libvirt_uri: str = CowrieConfig.get(
39
+ "backend_pool", "libvirt_uri", fallback="qemu:///system"
40
+ )
41
+
42
+ # open connection to libvirt
43
+ self.conn = libvirt.open(libvirt_uri)
44
+ if self.conn is None:
45
+ log.msg(
46
+ eventid="cowrie.backend_pool.libvirtd",
47
+ format="Failed to open connection to libvirtd at %(uri)s",
48
+ uri=libvirt_uri,
49
+ )
50
+ raise LibvirtError()
51
+
52
+ self.filter = None
53
+ self.network = None
54
+
55
+ # signals backend is ready to be operated
56
+ self.ready: bool = False
57
+
58
+ # table to associate IPs and MACs
59
+ seed: int = random.randint(0, sys.maxsize)
60
+ self.network_table = backend_pool.util.generate_network_table(seed)
61
+
62
+ log.msg(
63
+ eventid="cowrie.backend_pool.libvirtd",
64
+ format="Connection to libvirtd established at %(uri)s",
65
+ uri=libvirt_uri,
66
+ )
67
+
68
+ def start_backend(self) -> None:
69
+ """
70
+ Initialises QEMU/libvirt environment needed to run guests.
71
+ Namely starts networks and network filters.
72
+ """
73
+ # create a network filter
74
+ self.filter = backend_pool.libvirt.network_handler.create_filter(self.conn)
75
+
76
+ # create a network for the guests (as a NAT)
77
+ self.network = backend_pool.libvirt.network_handler.create_network(
78
+ self.conn, self.network_table
79
+ )
80
+
81
+ # service is ready to be used (create guests and use them)
82
+ self.ready = True
83
+
84
+ def stop_backend(self) -> None:
85
+ log.msg(
86
+ eventid="cowrie.backend_pool.libvirtd",
87
+ format="Doing libvirtd clean shutdown...",
88
+ )
89
+
90
+ self.ready = False
91
+
92
+ self.destroy_all_cowrie()
93
+
94
+ def shutdown_backend(self) -> None:
95
+ self.conn.close() # close libvirt connection
96
+
97
+ log.msg(
98
+ eventid="cowrie.backend_pool.libvirtd",
99
+ format="Connection to libvirtd closed successfully",
100
+ )
101
+
102
+ def get_mac_ip(self, ip_tester: Callable[[str], bool]) -> tuple[str, str]:
103
+ """
104
+ Get a MAC and IP that are not being used by any guest.
105
+ """
106
+ # Try to find a free pair 500 times.
107
+ retries = 0
108
+ while retries < 500:
109
+ mac = random.choice(list(self.network_table.keys()))
110
+ ip = self.network_table[mac]
111
+ if ip_tester(ip):
112
+ return mac, ip
113
+
114
+ retries += 1
115
+
116
+ raise LibvirtError()
117
+
118
+ def create_guest(self, ip_tester):
119
+ """
120
+ Returns an unready domain and its snapshot information.
121
+
122
+ Guarantee that the IP is free with the ip_tester function.
123
+ """
124
+ if not self.ready:
125
+ return
126
+
127
+ # create a single guest
128
+ guest_unique_id = uuid.uuid4().hex
129
+ guest_mac, guest_ip = self.get_mac_ip(ip_tester)
130
+ dom, snapshot = backend_pool.libvirt.guest_handler.create_guest(
131
+ self.conn, guest_mac, guest_unique_id
132
+ )
133
+ if dom is None:
134
+ log.msg(
135
+ eventid="cowrie.backend_pool.libvirtd", format="Failed to create guest"
136
+ )
137
+ return None
138
+
139
+ return dom, snapshot, guest_ip
140
+
141
+ def destroy_guest(self, domain, snapshot):
142
+ if not self.ready:
143
+ return
144
+
145
+ try:
146
+ # destroy the domain in QEMU
147
+ domain.destroy()
148
+
149
+ # we want to remove the snapshot if either:
150
+ # - explicitely set save_snapshots to False
151
+ # - no snapshot dir was defined (using cowrie's root dir) - should not happen but prevent it
152
+ if (
153
+ (
154
+ not CowrieConfig.getboolean(
155
+ "backend_pool", "save_snapshots", fallback=True
156
+ )
157
+ or CowrieConfig.get("backend_pool", "snapshot_path", fallback=None)
158
+ is None
159
+ )
160
+ and os.path.exists(snapshot)
161
+ and os.path.isfile(snapshot)
162
+ ):
163
+ os.remove(snapshot) # destroy its disk snapshot
164
+ except Exception as error:
165
+ log.err(
166
+ eventid="cowrie.backend_pool.libvirtd",
167
+ format="Error destroying guest: %(error)s",
168
+ error=error,
169
+ )
170
+
171
+ def __destroy_all_guests(self) -> None:
172
+ domains = self.conn.listDomainsID()
173
+ # if not domains:
174
+ # log.msg(
175
+ # eventid="cowrie.backend_pool.libvirtd",
176
+ # format="Could not get domain list",
177
+ # )
178
+
179
+ for domain_id in domains:
180
+ d = self.conn.lookupByID(domain_id)
181
+ if d.name().startswith("cowrie"):
182
+ try:
183
+ d.destroy()
184
+ except KeyboardInterrupt:
185
+ pass
186
+
187
+ def __destroy_all_networks(self) -> None:
188
+ networks = self.conn.listNetworks()
189
+ # if not networks:
190
+ # log.msg(
191
+ # eventid="cowrie.backend_pool.libvirtd",
192
+ # format="Could not get network list",
193
+ # )
194
+
195
+ for network in networks:
196
+ if network.startswith("cowrie"):
197
+ n = self.conn.networkLookupByName(network)
198
+ n.destroy()
199
+
200
+ def __destroy_all_network_filters(self) -> None:
201
+ network_filters = self.conn.listNWFilters()
202
+ # if not network_filters:
203
+ # log.msg(
204
+ # eventid="cowrie.backend_pool.libvirtd",
205
+ # format="Could not get network filters list",
206
+ # )
207
+
208
+ for nw_filter in network_filters:
209
+ if nw_filter.startswith("cowrie"):
210
+ n = self.conn.nwfilterLookupByName(nw_filter)
211
+ n.undefine()
212
+
213
+ def destroy_all_cowrie(self) -> None:
214
+ self.__destroy_all_guests()
215
+ self.__destroy_all_networks()
216
+ self.__destroy_all_network_filters()
@@ -0,0 +1,112 @@
1
+ # Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
2
+ # See the COPYRIGHT file for more information
3
+ from __future__ import annotations
4
+ import os
5
+ import sys
6
+ from configparser import NoOptionError
7
+
8
+ from twisted.python import log
9
+
10
+ from cowrie.core.config import CowrieConfig
11
+
12
+ import backend_pool.libvirt.snapshot_handler
13
+ import backend_pool.util
14
+
15
+
16
+ class QemuGuestError(Exception):
17
+ pass
18
+
19
+
20
+ def create_guest(connection, mac_address, guest_unique_id):
21
+ # lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
22
+ import libvirt
23
+
24
+ # get guest configurations
25
+ configuration_file: str = os.path.join(
26
+ CowrieConfig.get(
27
+ "backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
28
+ ),
29
+ CowrieConfig.get("backend_pool", "guest_config", fallback="default_guest.xml"),
30
+ )
31
+
32
+ version_tag: str = CowrieConfig.get("backend_pool", "guest_tag", fallback="guest")
33
+ base_image: str = CowrieConfig.get("backend_pool", "guest_image_path")
34
+ hypervisor: str = CowrieConfig.get(
35
+ "backend_pool", "guest_hypervisor", fallback="qemu"
36
+ )
37
+ memory: int = CowrieConfig.getint("backend_pool", "guest_memory", fallback=128)
38
+ qemu_machine: str = CowrieConfig.get(
39
+ "backend_pool", "guest_qemu_machine", fallback="pc-q35-3.1"
40
+ )
41
+
42
+ # check if base image exists
43
+ if not os.path.isfile(base_image):
44
+ log.msg(
45
+ eventid="cowrie.backend_pool.guest_handler",
46
+ format="Base image provided was not found: %(base_image)s",
47
+ base_image=base_image,
48
+ )
49
+ os._exit(1)
50
+
51
+ # only in some cases, like wrt
52
+ kernel_image: str = CowrieConfig.get(
53
+ "backend_pool", "guest_kernel_image", fallback=""
54
+ )
55
+
56
+ # get a directory to save snapshots, even if temporary
57
+ try:
58
+ # guest configuration, to be read by qemu, needs an absolute path
59
+ snapshot_path: str = backend_pool.util.to_absolute_path(
60
+ CowrieConfig.get("backend_pool", "snapshot_path")
61
+ )
62
+ except NoOptionError:
63
+ snapshot_path = os.getcwd()
64
+
65
+ # create a disk snapshot to be used by the guest
66
+ disk_img: str = os.path.join(
67
+ snapshot_path, f"snapshot-{version_tag}-{guest_unique_id}.qcow2"
68
+ )
69
+
70
+ if not backend_pool.libvirt.snapshot_handler.create_disk_snapshot(
71
+ base_image, disk_img
72
+ ):
73
+ log.msg(
74
+ eventid="cowrie.backend_pool.guest_handler",
75
+ format="There was a problem creating the disk snapshot.",
76
+ )
77
+ raise QemuGuestError()
78
+
79
+ guest_xml = backend_pool.util.read_file(configuration_file)
80
+ guest_config = guest_xml.format(
81
+ guest_name="cowrie-" + version_tag + "_" + guest_unique_id,
82
+ disk_image=disk_img,
83
+ base_image=base_image,
84
+ kernel_image=kernel_image,
85
+ hypervisor=hypervisor,
86
+ memory=memory,
87
+ qemu_machine=qemu_machine,
88
+ mac_address=mac_address,
89
+ network_name="cowrie",
90
+ )
91
+
92
+ try:
93
+ dom = connection.createXML(guest_config, 0)
94
+ if dom is None:
95
+ log.err(
96
+ eventid="cowrie.backend_pool.guest_handler",
97
+ format="Failed to create a domain from an XML definition.",
98
+ )
99
+ sys.exit(1)
100
+ except libvirt.libvirtError as e:
101
+ log.err(
102
+ eventid="cowrie.backend_pool.guest_handler",
103
+ format="Error booting guest: %(error)s",
104
+ error=e,
105
+ )
106
+ raise
107
+ log.msg(
108
+ eventid="cowrie.backend_pool.guest_handler",
109
+ format="Guest %(name)s has booted",
110
+ name=dom.name(),
111
+ )
112
+ return dom, disk_img
@@ -0,0 +1,100 @@
1
+ # Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
2
+ # See the COPYRIGHT file for more information
3
+ from __future__ import annotations
4
+ import os
5
+ import sys
6
+
7
+ from twisted.python import log
8
+
9
+ from cowrie.core.config import CowrieConfig
10
+
11
+ import backend_pool.util
12
+
13
+
14
+ def create_filter(connection):
15
+ # lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
16
+ import libvirt
17
+
18
+ filter_file: str = os.path.join(
19
+ CowrieConfig.get(
20
+ "backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
21
+ ),
22
+ CowrieConfig.get(
23
+ "backend_pool", "nw_filter_config", fallback="default_filter.xml"
24
+ ),
25
+ )
26
+
27
+ filter_xml = backend_pool.util.read_file(filter_file)
28
+
29
+ try:
30
+ return connection.nwfilterDefineXML(filter_xml)
31
+ except libvirt.libvirtError as e:
32
+ log.err(
33
+ eventid="cowrie.backend_pool.network_handler",
34
+ format="Filter already exists: %(error)s",
35
+ error=e,
36
+ )
37
+ return connection.nwfilterLookupByName("cowrie-default-filter")
38
+
39
+
40
+ def create_network(connection, network_table):
41
+ # lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
42
+ import libvirt
43
+
44
+ # TODO support more interfaces and therefore more IP space to allow > 253 guests
45
+ network_file: str = os.path.join(
46
+ CowrieConfig.get(
47
+ "backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
48
+ ),
49
+ CowrieConfig.get(
50
+ "backend_pool", "network_config", fallback="default_network.xml"
51
+ ),
52
+ )
53
+
54
+ network_xml = backend_pool.util.read_file(network_file)
55
+
56
+ template_host: str = "<host mac='{mac_address}' name='{name}' ip='{ip_address}'/>\n"
57
+ hosts: str = ""
58
+
59
+ # generate a host entry for every possible guest in this network (253 entries)
60
+ it = iter(network_table)
61
+ for guest_id in range(0, 253):
62
+ vm_name = "vm" + str(guest_id)
63
+
64
+ key = next(it)
65
+ hosts += template_host.format(
66
+ name=vm_name, mac_address=key, ip_address=network_table[key]
67
+ )
68
+
69
+ network_config = network_xml.format(
70
+ network_name="cowrie",
71
+ iface_name="virbr2",
72
+ default_gateway="192.168.150.1",
73
+ dhcp_range_start="192.168.150.2",
74
+ dhcp_range_end="192.168.150.254",
75
+ hosts=hosts,
76
+ )
77
+
78
+ # create a transient virtual network
79
+ try:
80
+ net = connection.networkCreateXML(network_config)
81
+ if net is None:
82
+ log.msg(
83
+ eventid="cowrie.backend_pool.network_handler",
84
+ format="Failed to define a virtual network",
85
+ )
86
+ sys.exit(1)
87
+
88
+ # set the network active
89
+ # not needed since apparently transient networks are created as active; uncomment if persistent
90
+ # net.create()
91
+
92
+ except libvirt.libvirtError as e:
93
+ log.err(
94
+ eventid="cowrie.backend_pool.network_handler",
95
+ format="Network already exists: %(error)s",
96
+ error=e,
97
+ )
98
+ return connection.networkLookupByName("cowrie")
99
+
100
+ return net
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
2
+ # See the COPYRIGHT file for more information
3
+
4
+ from __future__ import annotations
5
+
6
+ import getpass
7
+ import shutil
8
+ import subprocess
9
+
10
+
11
+ def create_disk_snapshot(source_img: str, destination_img: str) -> bool:
12
+ try:
13
+ shutil.chown(source_img, getpass.getuser())
14
+ except PermissionError:
15
+ # log.msg('Should have root to create snapshot')
16
+ pass
17
+
18
+ # could use `capture_output=True` instead of `stdout` and `stderr` args in Python 3.7
19
+ out = subprocess.run(
20
+ [
21
+ "qemu-img",
22
+ "create",
23
+ "-f",
24
+ "qcow2",
25
+ "-F",
26
+ "qcow2",
27
+ "-b",
28
+ source_img,
29
+ destination_img,
30
+ ],
31
+ capture_output=True,
32
+ )
33
+ return out.returncode == 0
backend_pool/nat.py ADDED
@@ -0,0 +1,127 @@
1
+ from __future__ import annotations
2
+ from threading import Lock
3
+ from typing import Any
4
+ from typing import TYPE_CHECKING
5
+
6
+ if TYPE_CHECKING:
7
+ from twisted.python import failure
8
+ from twisted.internet.interfaces import IAddress
9
+ from twisted.internet import protocol
10
+ from twisted.internet import reactor
11
+ from twisted.internet.protocol import connectionDone
12
+
13
+
14
+ class ClientProtocol(protocol.Protocol):
15
+ server_protocol: ServerProtocol
16
+
17
+ def dataReceived(self, data: bytes) -> None:
18
+ assert self.server_protocol.transport is not None
19
+ self.server_protocol.transport.write(data)
20
+
21
+ def connectionLost(self, reason: failure.Failure = connectionDone) -> None:
22
+ assert self.server_protocol.transport is not None
23
+ self.server_protocol.transport.loseConnection()
24
+
25
+
26
+ class ClientFactory(protocol.ClientFactory):
27
+ def __init__(self, server_protocol: ServerProtocol) -> None:
28
+ self.server_protocol = server_protocol
29
+
30
+ def buildProtocol(self, addr: IAddress) -> ClientProtocol:
31
+ client_protocol = ClientProtocol()
32
+ client_protocol.server_protocol = self.server_protocol
33
+ self.server_protocol.client_protocol = client_protocol
34
+ return client_protocol
35
+
36
+
37
+ class ServerProtocol(protocol.Protocol):
38
+ def __init__(self, dst_ip: str, dst_port: int):
39
+ self.dst_ip: str = dst_ip
40
+ self.dst_port: int = dst_port
41
+ self.client_protocol: ClientProtocol
42
+ self.buffer: list[bytes] = []
43
+
44
+ def connectionMade(self):
45
+ reactor.connectTCP(self.dst_ip, self.dst_port, ClientFactory(self))
46
+
47
+ def dataReceived(self, data: bytes) -> None:
48
+ self.buffer.append(data)
49
+ self.sendData()
50
+
51
+ def sendData(self) -> None:
52
+ if not self.client_protocol:
53
+ reactor.callLater(0.5, self.sendData) # type: ignore[attr-defined]
54
+ return
55
+
56
+ assert self.client_protocol.transport is not None
57
+ for packet in self.buffer:
58
+ self.client_protocol.transport.write(packet)
59
+ self.buffer = []
60
+
61
+ def connectionLost(self, reason: failure.Failure = connectionDone) -> None:
62
+ assert self.client_protocol.transport is not None
63
+ self.client_protocol.transport.loseConnection()
64
+
65
+
66
+ class ServerFactory(protocol.Factory):
67
+ def __init__(self, dst_ip: str, dst_port: int) -> None:
68
+ self.dst_ip: str = dst_ip
69
+ self.dst_port: int = dst_port
70
+
71
+ def buildProtocol(self, addr: IAddress) -> ServerProtocol:
72
+ return ServerProtocol(self.dst_ip, self.dst_port)
73
+
74
+
75
+ class NATService:
76
+ """
77
+ This service provides a NAT-like service when the backend pool
78
+ is located in a remote machine. Guests are bound to a local
79
+ IP (e.g., 192.168.150.0/24), and so not accessible from a remote
80
+ Cowrie. This class provides TCP proxies that associate accessible
81
+ IPs in the backend pool's machine to the internal IPs used by
82
+ guests, like a NAT.
83
+ """
84
+
85
+ def __init__(self):
86
+ self.bindings: dict[int, Any] = {}
87
+ self.lock = Lock() # we need to be thread-safe just in case, this is accessed from multiple clients
88
+
89
+ def request_binding(
90
+ self, guest_id: int, dst_ip: str, ssh_port: int, telnet_port: int
91
+ ) -> tuple[int, int]:
92
+ with self.lock:
93
+ # see if binding is already created
94
+ if guest_id in self.bindings:
95
+ # increase connected
96
+ self.bindings[guest_id][0] += 1
97
+
98
+ return (
99
+ self.bindings[guest_id][1]._realPortNumber,
100
+ self.bindings[guest_id][2]._realPortNumber,
101
+ )
102
+ else:
103
+ nat_ssh = reactor.listenTCP( # type: ignore[attr-defined]
104
+ 0, ServerFactory(dst_ip, ssh_port), interface="0.0.0.0"
105
+ )
106
+ nat_telnet = reactor.listenTCP( # type: ignore[attr-defined]
107
+ 0, ServerFactory(dst_ip, telnet_port), interface="0.0.0.0"
108
+ )
109
+ self.bindings[guest_id] = [1, nat_ssh, nat_telnet]
110
+
111
+ return nat_ssh._realPortNumber, nat_telnet._realPortNumber
112
+
113
+ def free_binding(self, guest_id: int) -> None:
114
+ with self.lock:
115
+ self.bindings[guest_id][0] -= 1
116
+
117
+ # stop listening if no one is connected
118
+ if self.bindings[guest_id][0] <= 0:
119
+ self.bindings[guest_id][1].stopListening()
120
+ self.bindings[guest_id][2].stopListening()
121
+ del self.bindings[guest_id]
122
+
123
+ def free_all(self):
124
+ with self.lock:
125
+ for guest_id in self.bindings:
126
+ self.bindings[guest_id][1].stopListening()
127
+ self.bindings[guest_id][2].stopListening()