pgsqlpot 2.0.0__tar.gz → 2.0.1__tar.gz
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.
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/CHANGELOG.md +16 -0
- {pgsqlpot-2.0.0/pgsqlpot.egg-info → pgsqlpot-2.0.1}/PKG-INFO +6 -1
- pgsqlpot-2.0.1/core/httpclient.py +71 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/tools.py +2 -5
- {pgsqlpot-2.0.0/pgsqlpot → pgsqlpot-2.0.1}/honeypot.py +1 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/couch.py +5 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/datadog.py +5 -9
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/discord.py +4 -9
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/elastic.py +2 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/nlcvapi.py +4 -10
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/telegram.py +8 -12
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/xmpp.py +1 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/Dockerfile +1 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/INSTALL.md +2 -2
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/INSTALLWIN.md +3 -3
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/etc/honeypot.cfg.base +19 -8
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1/pgsqlpot}/honeypot.py +1 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1/pgsqlpot.egg-info}/PKG-INFO +6 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot.egg-info/SOURCES.txt +1 -1
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot.egg-info/requires.txt +5 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/setup.cfg +0 -3
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/setup.py +9 -2
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/LICENSE +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/MANIFEST.in +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/__init__.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/config.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/logfile.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/output.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/paths.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/core/protocol.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/__init__.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/hpfeed.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/influx2.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/jsonlog.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/kafka.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/localsyslog.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/mongodb.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/mysql.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/postgres.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/redisdb.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/rethinkdblog.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/slack.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/socketlog.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/sqlite.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/output_plugins/textlog.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/__init__.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/cli.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/PLUGINS.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/TODO.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/datadog/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/discord/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/geoipupdtask.ps1 +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/mysql/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/mysql/READMEWIN.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/mysql/mysql.sql +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/postgres/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/postgres/READMEWIN.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/postgres/postgres.sql +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/slack/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/sqlite3/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/sqlite3/READMEWIN.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/sqlite3/sqlite3.sql +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/docs/telegram/README.md +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/etc/honeypot.cfg +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/test/.gitignore +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot/data/test/test.py +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot.egg-info/dependency_links.txt +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot.egg-info/entry_points.txt +0 -0
- {pgsqlpot-2.0.0 → pgsqlpot-2.0.1}/pgsqlpot.egg-info/top_level.txt +0 -0
|
@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.0.1]
|
|
9
|
+
|
|
10
|
+
### Added in version 2.0.1
|
|
11
|
+
|
|
12
|
+
* Nothing
|
|
13
|
+
|
|
14
|
+
### Changed in version 2.0.1
|
|
15
|
+
|
|
16
|
+
* Increased the version number
|
|
17
|
+
* The `datadog`, `discord`, `nlcvapi`, and `telegram` plugins now use a secure
|
|
18
|
+
connection (HTTPS) by default
|
|
19
|
+
* The `elastic` plugin now warns if the `ssl` is set while certificate
|
|
20
|
+
verification (`verify_certs`) is off
|
|
21
|
+
* The `couch` plugin now uses authentication mechanism that does not pass the
|
|
22
|
+
username and password in the URL
|
|
23
|
+
|
|
8
24
|
## [2.0.0]
|
|
9
25
|
|
|
10
26
|
### Added in version 2.0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgsqlpot
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A PostgeSQL Honeypot
|
|
5
5
|
Home-page: https://gitlab.com/bontchev/pgsqlpot
|
|
6
6
|
Author: Vesselin Bontchev
|
|
@@ -43,11 +43,13 @@ Requires-Dist: twisted>=21; python_version >= "3"
|
|
|
43
43
|
Provides-Extra: couchdb
|
|
44
44
|
Requires-Dist: couchdb; extra == "couchdb"
|
|
45
45
|
Provides-Extra: datadog
|
|
46
|
+
Requires-Dist: certifi; extra == "datadog"
|
|
46
47
|
Requires-Dist: cryptography<=2.8; python_version < "3" and extra == "datadog"
|
|
47
48
|
Requires-Dist: pyOpenSSL<=18.0.0; python_version < "3" and extra == "datadog"
|
|
48
49
|
Requires-Dist: cryptography; python_version >= "3" and extra == "datadog"
|
|
49
50
|
Requires-Dist: pyOpenSSL; python_version >= "3" and extra == "datadog"
|
|
50
51
|
Provides-Extra: discord
|
|
52
|
+
Requires-Dist: certifi; extra == "discord"
|
|
51
53
|
Provides-Extra: elastic
|
|
52
54
|
Requires-Dist: elasticsearch<=7.13; python_version < "3" and extra == "elastic"
|
|
53
55
|
Requires-Dist: numpy<=1.16.6; python_version < "3" and extra == "elastic"
|
|
@@ -71,6 +73,7 @@ Provides-Extra: mysql
|
|
|
71
73
|
Requires-Dist: PyMySQL; python_version < "3" and extra == "mysql"
|
|
72
74
|
Requires-Dist: mysqlclient>=1.3.12; python_version >= "3" and extra == "mysql"
|
|
73
75
|
Provides-Extra: nlcvapi
|
|
76
|
+
Requires-Dist: certifi; extra == "nlcvapi"
|
|
74
77
|
Requires-Dist: pyOpenSSL<=18.0.0; python_version < "3" and extra == "nlcvapi"
|
|
75
78
|
Requires-Dist: pyOpenSSL; python_version >= "3" and extra == "nlcvapi"
|
|
76
79
|
Provides-Extra: postgres
|
|
@@ -87,12 +90,14 @@ Requires-Dist: slack-sdk; python_version >= "3" and extra == "slack"
|
|
|
87
90
|
Provides-Extra: socketlog
|
|
88
91
|
Provides-Extra: sqlite
|
|
89
92
|
Provides-Extra: telegram
|
|
93
|
+
Requires-Dist: certifi; extra == "telegram"
|
|
90
94
|
Provides-Extra: textlog
|
|
91
95
|
Provides-Extra: xmpp
|
|
92
96
|
Requires-Dist: xmpppy>=0.7.3; extra == "xmpp"
|
|
93
97
|
Provides-Extra: all
|
|
94
98
|
Requires-Dist: Automat<20; python_version < "3" and extra == "all"
|
|
95
99
|
Requires-Dist: PyMySQL; python_version < "3" and extra == "all"
|
|
100
|
+
Requires-Dist: certifi; extra == "all"
|
|
96
101
|
Requires-Dist: confluent-kafka; python_version >= "3" and extra == "all"
|
|
97
102
|
Requires-Dist: confluent-kafka<1.0; python_version < "3" and extra == "all"
|
|
98
103
|
Requires-Dist: couchdb; extra == "all"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import absolute_import
|
|
2
|
+
|
|
3
|
+
from io import open
|
|
4
|
+
|
|
5
|
+
from twisted.internet.ssl import Certificate, trustRootFromCertificates
|
|
6
|
+
from twisted.python.log import msg
|
|
7
|
+
from twisted.web.client import Agent, BrowserLikePolicyForHTTPS
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _LegacyWebClientContextFactory(object):
|
|
11
|
+
@staticmethod
|
|
12
|
+
def build():
|
|
13
|
+
from twisted.internet.ssl import ClientContextFactory
|
|
14
|
+
|
|
15
|
+
class WebClientContextFactory(ClientContextFactory):
|
|
16
|
+
def getContext(self, hostname, port):
|
|
17
|
+
return ClientContextFactory.getContext(self)
|
|
18
|
+
|
|
19
|
+
return WebClientContextFactory()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _load_trust_root_from_pem_bundle(pem_path):
|
|
23
|
+
with open(pem_path, 'r', encoding='utf-8') as fh:
|
|
24
|
+
content = fh.read()
|
|
25
|
+
certs = []
|
|
26
|
+
end_marker = '-----END CERTIFICATE-----'
|
|
27
|
+
for chunk in content.split(end_marker):
|
|
28
|
+
chunk = chunk.strip()
|
|
29
|
+
if not chunk:
|
|
30
|
+
continue
|
|
31
|
+
pem = chunk + '\n' + end_marker + '\n'
|
|
32
|
+
certs.append(Certificate.loadPEM(pem))
|
|
33
|
+
if not certs:
|
|
34
|
+
return None
|
|
35
|
+
return trustRootFromCertificates(certs)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _trust_root(ca_certs):
|
|
39
|
+
if ca_certs:
|
|
40
|
+
try:
|
|
41
|
+
return _load_trust_root_from_pem_bundle(ca_certs)
|
|
42
|
+
except Exception as e:
|
|
43
|
+
msg('Failed to load CA bundle {}: {}'.format(ca_certs, e))
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
import certifi
|
|
48
|
+
return _load_trust_root_from_pem_bundle(certifi.where())
|
|
49
|
+
except Exception:
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def create_http_agent(reactor, pool, plugin_name, verify_tls=True, ca_certs=None):
|
|
54
|
+
"""
|
|
55
|
+
Build a Twisted Agent with sane HTTPS defaults.
|
|
56
|
+
verify_tls=True uses BrowserLikePolicyForHTTPS for cert + hostname checks.
|
|
57
|
+
verify_tls=False falls back to a legacy context factory for compatibility.
|
|
58
|
+
"""
|
|
59
|
+
if verify_tls:
|
|
60
|
+
return Agent(
|
|
61
|
+
reactor,
|
|
62
|
+
contextFactory=BrowserLikePolicyForHTTPS(trustRoot=_trust_root(ca_certs)),
|
|
63
|
+
pool=pool
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
msg('{}: TLS certificate verification is disabled.'.format(plugin_name))
|
|
67
|
+
return Agent(
|
|
68
|
+
reactor,
|
|
69
|
+
contextFactory=_LegacyWebClientContextFactory.build(),
|
|
70
|
+
pool=pool
|
|
71
|
+
)
|
|
@@ -15,7 +15,7 @@ from twisted.python.log import msg
|
|
|
15
15
|
try:
|
|
16
16
|
from urllib.request import urlopen
|
|
17
17
|
except ImportError:
|
|
18
|
-
from
|
|
18
|
+
from urllib2 import urlopen # type: ignore
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
if version_info[0] >= 3:
|
|
@@ -85,10 +85,7 @@ def stop_plugins(cfg):
|
|
|
85
85
|
|
|
86
86
|
def get_public_ip(ip_reporter):
|
|
87
87
|
try:
|
|
88
|
-
|
|
89
|
-
return urlopen(ip_reporter).read().decode('latin1', errors='replace').encode('utf-8')
|
|
90
|
-
else:
|
|
91
|
-
return decode(urlopen(ip_reporter).read())
|
|
88
|
+
return decode(urlopen(ip_reporter, timeout=10).read())
|
|
92
89
|
except:
|
|
93
90
|
return None
|
|
94
91
|
|
|
@@ -23,7 +23,7 @@ from twisted.internet.reactor import listenTCP, run
|
|
|
23
23
|
from twisted.python.log import msg
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
__VERSION__ = '2.0.
|
|
26
|
+
__VERSION__ = '2.0.1'
|
|
27
27
|
__description__ = 'A PostgreSQL Honeypot'
|
|
28
28
|
__license__ = 'GPLv3'
|
|
29
29
|
__uri__ = 'https://gitlab.com/bontchev/pgsqlpot'
|
|
@@ -13,6 +13,7 @@ from twisted.python.log import msg
|
|
|
13
13
|
class Output(output.Output):
|
|
14
14
|
|
|
15
15
|
def start(self):
|
|
16
|
+
scheme = CONFIG.get('output_couch', 'scheme', fallback='http')
|
|
16
17
|
host = CONFIG.get('output_couch', 'host', fallback='localhost')
|
|
17
18
|
port = CONFIG.getint('output_couch', 'port', fallback=5984)
|
|
18
19
|
username = CONFIG.get('output_couch', 'username', fallback='pgsqlpot', raw=True)
|
|
@@ -20,7 +21,10 @@ class Output(output.Output):
|
|
|
20
21
|
db_name = CONFIG.get('output_couch', 'database', fallback='pgsqlpot')
|
|
21
22
|
|
|
22
23
|
try:
|
|
23
|
-
|
|
24
|
+
base_url = '{}://{}:{}'.format(scheme, host, port)
|
|
25
|
+
couchserver = Server(base_url)
|
|
26
|
+
if username:
|
|
27
|
+
couchserver.resource.credentials = (username, password)
|
|
24
28
|
|
|
25
29
|
if db_name in couchserver:
|
|
26
30
|
self.couch_db = couchserver[db_name]
|
|
@@ -10,18 +10,13 @@ from platform import node
|
|
|
10
10
|
|
|
11
11
|
from core import output
|
|
12
12
|
from core.config import CONFIG
|
|
13
|
+
from core.httpclient import create_http_agent
|
|
13
14
|
from core.tools import to_bytes
|
|
14
15
|
|
|
15
16
|
from twisted.internet import reactor
|
|
16
17
|
from twisted.python.log import msg
|
|
17
18
|
from twisted.web import client, http_headers
|
|
18
19
|
from twisted.web.client import FileBodyProducer
|
|
19
|
-
from twisted.internet.ssl import ClientContextFactory
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class WebClientContextFactory(ClientContextFactory):
|
|
23
|
-
def getContext(self, hostname, port):
|
|
24
|
-
return ClientContextFactory.getContext(self)
|
|
25
20
|
|
|
26
21
|
|
|
27
22
|
class QuietHTTP11ClientFactory(client._HTTP11ClientFactory):
|
|
@@ -33,16 +28,17 @@ class Output(output.Output):
|
|
|
33
28
|
self.url = CONFIG.get('output_datadog', 'url')
|
|
34
29
|
self.api_key = CONFIG.get('output_datadog', 'api_key', fallback='')
|
|
35
30
|
if len(self.api_key) == 0:
|
|
36
|
-
msg('
|
|
31
|
+
msg('output_datadog: API key is not defined.')
|
|
37
32
|
self.ddsource = CONFIG.get('output_datadog', 'ddsource', fallback='pgsqlpot')
|
|
38
33
|
self.ddtags = CONFIG.get('output_datadog', 'ddtags', fallback='env:dev')
|
|
39
34
|
self.service = CONFIG.get('output_datadog', 'service', fallback='honeypot')
|
|
40
35
|
self.hostname = CONFIG.get('output_datadog', 'hostname', fallback=node())
|
|
36
|
+
verify_tls = CONFIG.getboolean('output_datadog', 'verify_tls', fallback=True)
|
|
37
|
+
ca_certs = CONFIG.get('output_datadog', 'ca_certs', fallback=None)
|
|
41
38
|
|
|
42
|
-
contextFactory = WebClientContextFactory()
|
|
43
39
|
myQuietPool = client.HTTPConnectionPool(reactor)
|
|
44
40
|
myQuietPool._factory = QuietHTTP11ClientFactory
|
|
45
|
-
self.agent =
|
|
41
|
+
self.agent = create_http_agent(reactor, myQuietPool, 'output_datadog', verify_tls, ca_certs)
|
|
46
42
|
|
|
47
43
|
def stop(self):
|
|
48
44
|
pass
|
|
@@ -13,14 +13,13 @@ from time import gmtime, strftime, time
|
|
|
13
13
|
|
|
14
14
|
from core import output
|
|
15
15
|
from core.config import CONFIG
|
|
16
|
+
from core.httpclient import create_http_agent
|
|
16
17
|
from core.tools import decode, to_bytes
|
|
17
18
|
|
|
18
19
|
from twisted.internet import reactor
|
|
19
|
-
from twisted.internet.ssl import ClientContextFactory
|
|
20
20
|
from twisted.internet.task import deferLater
|
|
21
21
|
from twisted.python.log import msg
|
|
22
22
|
from twisted.web.client import (
|
|
23
|
-
Agent,
|
|
24
23
|
FileBodyProducer,
|
|
25
24
|
HTTPConnectionPool,
|
|
26
25
|
_HTTP11ClientFactory,
|
|
@@ -29,11 +28,6 @@ from twisted.web.client import (
|
|
|
29
28
|
from twisted.web.http_headers import Headers
|
|
30
29
|
|
|
31
30
|
|
|
32
|
-
class WebClientContextFactory(ClientContextFactory):
|
|
33
|
-
def getContext(self, hostname, port):
|
|
34
|
-
return ClientContextFactory.getContext(self)
|
|
35
|
-
|
|
36
|
-
|
|
37
31
|
class QuietHTTP11ClientFactory(_HTTP11ClientFactory):
|
|
38
32
|
noisy = False
|
|
39
33
|
|
|
@@ -43,10 +37,11 @@ class Output(output.Output):
|
|
|
43
37
|
def start(self):
|
|
44
38
|
self.url = to_bytes(CONFIG.get('output_discord', 'url'))
|
|
45
39
|
self.delay = CONFIG.getfloat('output_discord', 'delay', fallback=2.0)
|
|
46
|
-
|
|
40
|
+
verify_tls = CONFIG.getboolean('output_discord', 'verify_tls', fallback=True)
|
|
41
|
+
ca_certs = CONFIG.get('output_discord', 'ca_certs', fallback=None)
|
|
47
42
|
pool = HTTPConnectionPool(reactor)
|
|
48
43
|
pool._factory = QuietHTTP11ClientFactory
|
|
49
|
-
self.agent =
|
|
44
|
+
self.agent = create_http_agent(reactor, pool, 'output_discord', verify_tls, ca_certs)
|
|
50
45
|
self.last_sent = 0
|
|
51
46
|
self.requests_list = []
|
|
52
47
|
|
|
@@ -44,6 +44,8 @@ class Output(output.Output):
|
|
|
44
44
|
kwargs['verify_certs'] = verify_certs
|
|
45
45
|
if verify_certs and ca_certs:
|
|
46
46
|
kwargs['ca_certs'] = ca_certs
|
|
47
|
+
elif not verify_certs:
|
|
48
|
+
msg('output_elastic: SSL is enabled but certificate verification is disabled.')
|
|
47
49
|
|
|
48
50
|
# Create client
|
|
49
51
|
self.es = Elasticsearch(hosts=hosts, **kwargs)
|
|
@@ -8,15 +8,14 @@ from json import dumps, loads
|
|
|
8
8
|
|
|
9
9
|
from core import output
|
|
10
10
|
from core.config import CONFIG
|
|
11
|
+
from core.httpclient import create_http_agent
|
|
11
12
|
from core.tools import decode, geolocate, to_bytes
|
|
12
13
|
|
|
13
14
|
from geoip2.database import Reader
|
|
14
15
|
|
|
15
16
|
from twisted.internet import reactor
|
|
16
|
-
from twisted.internet.ssl import ClientContextFactory
|
|
17
17
|
from twisted.python.log import msg
|
|
18
18
|
from twisted.web.client import (
|
|
19
|
-
Agent,
|
|
20
19
|
FileBodyProducer,
|
|
21
20
|
HTTPConnectionPool,
|
|
22
21
|
_HTTP11ClientFactory,
|
|
@@ -25,12 +24,6 @@ from twisted.web.client import (
|
|
|
25
24
|
from twisted.web.http_headers import Headers
|
|
26
25
|
|
|
27
26
|
|
|
28
|
-
class WebClientContextFactory(ClientContextFactory):
|
|
29
|
-
|
|
30
|
-
def getContext(self, hostname, port):
|
|
31
|
-
return ClientContextFactory.getContext(self)
|
|
32
|
-
|
|
33
|
-
|
|
34
27
|
class QuietHTTP11ClientFactory(_HTTP11ClientFactory):
|
|
35
28
|
noisy = False
|
|
36
29
|
|
|
@@ -40,10 +33,11 @@ class Output(output.Output):
|
|
|
40
33
|
def start(self):
|
|
41
34
|
self.host = CONFIG.get('output_nlcvapi', 'host', fallback='https://api.nlcv.bas.bg/v1.0/honeypot')
|
|
42
35
|
self.geoip = CONFIG.getboolean('output_nlcvapi', 'geoip', fallback=True)
|
|
43
|
-
|
|
36
|
+
verify_tls = CONFIG.getboolean('output_nlcvapi', 'verify_tls', fallback=True)
|
|
37
|
+
ca_certs = CONFIG.get('output_nlcvapi', 'ca_certs', fallback=None)
|
|
44
38
|
pool = HTTPConnectionPool(reactor)
|
|
45
39
|
pool._factory = QuietHTTP11ClientFactory
|
|
46
|
-
self.agent =
|
|
40
|
+
self.agent = create_http_agent(reactor, pool, 'output_nlcvapi', verify_tls, ca_certs)
|
|
47
41
|
|
|
48
42
|
if self.geoip:
|
|
49
43
|
geoipdb_city_path = CONFIG.get('output_nlcvapi', 'geoip_citydb', fallback='data/GeoLite2-City.mmdb')
|
|
@@ -10,6 +10,7 @@ from time import gmtime, strftime, time
|
|
|
10
10
|
|
|
11
11
|
from core import output
|
|
12
12
|
from core.config import CONFIG
|
|
13
|
+
from core.httpclient import create_http_agent
|
|
13
14
|
from core.tools import decode, encode
|
|
14
15
|
|
|
15
16
|
try:
|
|
@@ -18,11 +19,9 @@ except ImportError:
|
|
|
18
19
|
from urllib.parse import quote_plus
|
|
19
20
|
|
|
20
21
|
from twisted.internet import reactor
|
|
21
|
-
from twisted.internet.ssl import ClientContextFactory
|
|
22
22
|
from twisted.internet.task import deferLater
|
|
23
23
|
from twisted.python.log import msg
|
|
24
24
|
from twisted.web.client import (
|
|
25
|
-
Agent,
|
|
26
25
|
HTTPConnectionPool,
|
|
27
26
|
_HTTP11ClientFactory,
|
|
28
27
|
readBody
|
|
@@ -30,12 +29,6 @@ from twisted.web.client import (
|
|
|
30
29
|
from twisted.web.http_headers import Headers
|
|
31
30
|
|
|
32
31
|
|
|
33
|
-
class WebClientContextFactory(ClientContextFactory):
|
|
34
|
-
|
|
35
|
-
def getContext(self, hostname, port):
|
|
36
|
-
return ClientContextFactory.getContext(self)
|
|
37
|
-
|
|
38
|
-
|
|
39
32
|
class QuietHTTP11ClientFactory(_HTTP11ClientFactory):
|
|
40
33
|
noisy = False
|
|
41
34
|
|
|
@@ -49,11 +42,12 @@ class Output(output.Output):
|
|
|
49
42
|
self.bot_token = CONFIG.get('output_telegram', 'bot_token')
|
|
50
43
|
self.chat_id = CONFIG.get('output_telegram', 'chat_id')
|
|
51
44
|
self.delay = CONFIG.getfloat('output_telegram', 'delay', fallback=2.0)
|
|
45
|
+
verify_tls = CONFIG.getboolean('output_telegram', 'verify_tls', fallback=True)
|
|
46
|
+
ca_certs = CONFIG.get('output_telegram', 'ca_certs', fallback=None)
|
|
52
47
|
|
|
53
|
-
contextFactory = WebClientContextFactory()
|
|
54
48
|
pool = HTTPConnectionPool(reactor)
|
|
55
49
|
pool._factory = QuietHTTP11ClientFactory
|
|
56
|
-
self.agent =
|
|
50
|
+
self.agent = create_http_agent(reactor, pool, 'output_telegram', verify_tls, ca_certs)
|
|
57
51
|
self.last_sent = 0
|
|
58
52
|
self.requests_list = []
|
|
59
53
|
|
|
@@ -83,8 +77,10 @@ class Output(output.Output):
|
|
|
83
77
|
self._drain()
|
|
84
78
|
|
|
85
79
|
def _drain(self):
|
|
86
|
-
"""
|
|
87
|
-
|
|
80
|
+
"""
|
|
81
|
+
Send the next queued message if the rate-limit window has elapsed.
|
|
82
|
+
If messages remain, schedule another drain after self.delay seconds.
|
|
83
|
+
"""
|
|
88
84
|
if not self.requests_list:
|
|
89
85
|
return
|
|
90
86
|
now = time()
|
|
@@ -52,7 +52,7 @@ if version_info[0] < 3:
|
|
|
52
52
|
|
|
53
53
|
xmpp.simplexml.ustr = _ustr
|
|
54
54
|
xmpp.transports.ustr = _ustr
|
|
55
|
-
# xmpp.protocol also does 'from .simplexml import ustr'
|
|
55
|
+
# xmpp.protocol also does 'from .simplexml import ustr' - patch it too
|
|
56
56
|
import xmpp.protocol as _xmpp_protocol
|
|
57
57
|
if hasattr(_xmpp_protocol, 'ustr'):
|
|
58
58
|
_xmpp_protocol.ustr = _ustr
|
|
@@ -166,7 +166,7 @@ and `Dockerfile` into the working directory.
|
|
|
166
166
|
The configuration for the honeypot is stored in `etc/honeypot.cfg.base` and
|
|
167
167
|
`etc/honeypot.cfg`. Both files are read on startup but entries from
|
|
168
168
|
`etc/honeypot.cfg` take precedence. The `.base` file contains the default
|
|
169
|
-
settings and should not be edited
|
|
169
|
+
settings and should not be edited - it may be overwritten by upgrades. All
|
|
170
170
|
your customisations should go into `etc/honeypot.cfg`.
|
|
171
171
|
|
|
172
172
|
To run with a standard configuration there is no need to change anything.
|
|
@@ -394,7 +394,7 @@ $ source ~/pgsqlpot-env/bin/activate
|
|
|
394
394
|
|
|
395
395
|
where `[plugin_list]` is as explained above.
|
|
396
396
|
|
|
397
|
-
Note that `pgsqlpot init` is safe to re-run
|
|
397
|
+
Note that `pgsqlpot init` is safe to re-run - it never overwrites files that
|
|
398
398
|
you have already edited or created (such as `etc/honeypot.cfg`). It only
|
|
399
399
|
copies files that are missing, so any new defaults added by an upgrade are
|
|
400
400
|
picked up automatically.
|
|
@@ -32,7 +32,7 @@ programs (if they are not already present):
|
|
|
32
32
|
|
|
33
33
|
- **Database server** (optional). If you want the honeypot to send the data
|
|
34
34
|
it collects to a local database server (e.g., MySQL), make sure to install
|
|
35
|
-
it
|
|
35
|
+
it - again, for all users and not just for the current one.
|
|
36
36
|
|
|
37
37
|
- The latest version of the **VS C++ redistributable** (required by some of
|
|
38
38
|
the database output plugins, e.g., for MySQL). Version 14 or higher should
|
|
@@ -186,7 +186,7 @@ the working directory.
|
|
|
186
186
|
The configuration for the honeypot is stored in `etc\honeypot.cfg.base` and
|
|
187
187
|
`etc\honeypot.cfg`. Both the `*.cfg.base` and the `*.cfg` files are read on
|
|
188
188
|
startup but entries from the `*.cfg` files take precedence. The `*.base` files
|
|
189
|
-
contain the default settings and should not be edited
|
|
189
|
+
contain the default settings and should not be edited - they may be overwritten
|
|
190
190
|
by future updates. All your customisations should go into the `*.cfg` files.
|
|
191
191
|
|
|
192
192
|
To run with a standard configuration there is no need to change anything.
|
|
@@ -405,7 +405,7 @@ PS C:\pgsqlpot-workdir> C:\pgsqlpot-env\Scripts\activate.ps1
|
|
|
405
405
|
|
|
406
406
|
where `[plugin_list]` is as explained above.
|
|
407
407
|
|
|
408
|
-
Note that `pgsqlpot init` is safe to re-run
|
|
408
|
+
Note that `pgsqlpot init` is safe to re-run - it never overwrites files that
|
|
409
409
|
you have already edited or created (such as `etc\honeypot.cfg`). It only
|
|
410
410
|
copies files that are missing, so any new defaults added by an upgrade are
|
|
411
411
|
picked up automatically.
|
|
@@ -72,6 +72,8 @@ log_path = log
|
|
|
72
72
|
#
|
|
73
73
|
#[output_couch]
|
|
74
74
|
#enabled = false
|
|
75
|
+
# Optional transport scheme: http (default) or https
|
|
76
|
+
#scheme = http
|
|
75
77
|
#host = localhost
|
|
76
78
|
#port = 5984
|
|
77
79
|
#username = pgsqlpot
|
|
@@ -91,6 +93,10 @@ log_path = log
|
|
|
91
93
|
#[output_datadog]
|
|
92
94
|
#enabled = false
|
|
93
95
|
#url = https://http-intake.logs.datadoghq.com/api/v2/logs
|
|
96
|
+
# Verify TLS certificates and hostnames for HTTPS connections (default: true)
|
|
97
|
+
#verify_tls = true
|
|
98
|
+
# Optional path to a PEM CA bundle (used when verify_tls = true)
|
|
99
|
+
#ca_certs = /path/to/ca-bundle.pem
|
|
94
100
|
#api_key = abcdef1234567890fedcba0987654321
|
|
95
101
|
#ddsource = pgsqlpot
|
|
96
102
|
#ddtags = env:dev
|
|
@@ -102,6 +108,10 @@ log_path = log
|
|
|
102
108
|
#
|
|
103
109
|
#[output_discord]
|
|
104
110
|
#enabled = false
|
|
111
|
+
# Verify TLS certificates and hostnames for HTTPS connections (default: true)
|
|
112
|
+
#verify_tls = true
|
|
113
|
+
# Optional path to a PEM CA bundle (used when verify_tls = true)
|
|
114
|
+
#ca_certs = /path/to/ca-bundle.pem
|
|
105
115
|
# Delay between messages (for rate limiting)
|
|
106
116
|
#delay = 2.0
|
|
107
117
|
#url = https://discord.com/api/webhooks/id/token
|
|
@@ -114,13 +124,6 @@ log_path = log
|
|
|
114
124
|
#port = 9200
|
|
115
125
|
#index = pgsqlpot
|
|
116
126
|
#
|
|
117
|
-
# type has been deprecated since ES 6.0.0
|
|
118
|
-
# use _doc which is the default type. See
|
|
119
|
-
# https://stackoverflow.com/a/53688626 for
|
|
120
|
-
# more information
|
|
121
|
-
#
|
|
122
|
-
#type = _doc
|
|
123
|
-
#
|
|
124
127
|
# set pipeline = geoip to map src_ip to
|
|
125
128
|
# geo location data. You can use a custom
|
|
126
129
|
# pipeline but you must ensure it exists
|
|
@@ -231,6 +234,10 @@ log_path = log
|
|
|
231
234
|
#[output_nlcvapi]
|
|
232
235
|
#enabled = false
|
|
233
236
|
#host = https://api.nlcv.bas.bg/v1.0/honeypot
|
|
237
|
+
# Verify TLS certificates and hostnames for HTTPS connections (default: true)
|
|
238
|
+
#verify_tls = true
|
|
239
|
+
# Optional path to a PEM CA bundle (used when verify_tls = true)
|
|
240
|
+
#ca_certs = /path/to/ca-bundle.pem
|
|
234
241
|
# Whether to store geolocation data in the database
|
|
235
242
|
#geoip = true
|
|
236
243
|
# Location of the databases used for geolocation
|
|
@@ -332,6 +339,10 @@ log_path = log
|
|
|
332
339
|
#
|
|
333
340
|
#[output_telegram]
|
|
334
341
|
#enabled = false
|
|
342
|
+
# Verify TLS certificates and hostnames for HTTPS connections (default: true)
|
|
343
|
+
#verify_tls = true
|
|
344
|
+
# Optional path to a PEM CA bundle (used when verify_tls = true)
|
|
345
|
+
#ca_certs = /path/to/ca-bundle.pem
|
|
335
346
|
#bot_token = 123456789:AbCDEfGhiJkLmnOpQRstUVWxYZ
|
|
336
347
|
#chat_id = 987654321
|
|
337
348
|
# Delay between messages (for rate limiting)
|
|
@@ -347,7 +358,7 @@ log_path = log
|
|
|
347
358
|
# XMPP logging module
|
|
348
359
|
#
|
|
349
360
|
#[output_xmpp]
|
|
350
|
-
#enabled=false
|
|
361
|
+
#enabled = false
|
|
351
362
|
#server = conference.pgsqlpot.local
|
|
352
363
|
#user = pgsqlpot@pgsqlpot.local
|
|
353
364
|
#password = secret
|
|
@@ -23,7 +23,7 @@ from twisted.internet.reactor import listenTCP, run
|
|
|
23
23
|
from twisted.python.log import msg
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
__VERSION__ = '2.0.
|
|
26
|
+
__VERSION__ = '2.0.1'
|
|
27
27
|
__description__ = 'A PostgreSQL Honeypot'
|
|
28
28
|
__license__ = 'GPLv3'
|
|
29
29
|
__uri__ = 'https://gitlab.com/bontchev/pgsqlpot'
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pgsqlpot
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.1
|
|
4
4
|
Summary: A PostgeSQL Honeypot
|
|
5
5
|
Home-page: https://gitlab.com/bontchev/pgsqlpot
|
|
6
6
|
Author: Vesselin Bontchev
|
|
@@ -43,11 +43,13 @@ Requires-Dist: twisted>=21; python_version >= "3"
|
|
|
43
43
|
Provides-Extra: couchdb
|
|
44
44
|
Requires-Dist: couchdb; extra == "couchdb"
|
|
45
45
|
Provides-Extra: datadog
|
|
46
|
+
Requires-Dist: certifi; extra == "datadog"
|
|
46
47
|
Requires-Dist: cryptography<=2.8; python_version < "3" and extra == "datadog"
|
|
47
48
|
Requires-Dist: pyOpenSSL<=18.0.0; python_version < "3" and extra == "datadog"
|
|
48
49
|
Requires-Dist: cryptography; python_version >= "3" and extra == "datadog"
|
|
49
50
|
Requires-Dist: pyOpenSSL; python_version >= "3" and extra == "datadog"
|
|
50
51
|
Provides-Extra: discord
|
|
52
|
+
Requires-Dist: certifi; extra == "discord"
|
|
51
53
|
Provides-Extra: elastic
|
|
52
54
|
Requires-Dist: elasticsearch<=7.13; python_version < "3" and extra == "elastic"
|
|
53
55
|
Requires-Dist: numpy<=1.16.6; python_version < "3" and extra == "elastic"
|
|
@@ -71,6 +73,7 @@ Provides-Extra: mysql
|
|
|
71
73
|
Requires-Dist: PyMySQL; python_version < "3" and extra == "mysql"
|
|
72
74
|
Requires-Dist: mysqlclient>=1.3.12; python_version >= "3" and extra == "mysql"
|
|
73
75
|
Provides-Extra: nlcvapi
|
|
76
|
+
Requires-Dist: certifi; extra == "nlcvapi"
|
|
74
77
|
Requires-Dist: pyOpenSSL<=18.0.0; python_version < "3" and extra == "nlcvapi"
|
|
75
78
|
Requires-Dist: pyOpenSSL; python_version >= "3" and extra == "nlcvapi"
|
|
76
79
|
Provides-Extra: postgres
|
|
@@ -87,12 +90,14 @@ Requires-Dist: slack-sdk; python_version >= "3" and extra == "slack"
|
|
|
87
90
|
Provides-Extra: socketlog
|
|
88
91
|
Provides-Extra: sqlite
|
|
89
92
|
Provides-Extra: telegram
|
|
93
|
+
Requires-Dist: certifi; extra == "telegram"
|
|
90
94
|
Provides-Extra: textlog
|
|
91
95
|
Provides-Extra: xmpp
|
|
92
96
|
Requires-Dist: xmpppy>=0.7.3; extra == "xmpp"
|
|
93
97
|
Provides-Extra: all
|
|
94
98
|
Requires-Dist: Automat<20; python_version < "3" and extra == "all"
|
|
95
99
|
Requires-Dist: PyMySQL; python_version < "3" and extra == "all"
|
|
100
|
+
Requires-Dist: certifi; extra == "all"
|
|
96
101
|
Requires-Dist: confluent-kafka; python_version >= "3" and extra == "all"
|
|
97
102
|
Requires-Dist: confluent-kafka<1.0; python_version < "3" and extra == "all"
|
|
98
103
|
Requires-Dist: couchdb; extra == "all"
|
|
@@ -15,6 +15,7 @@ service_identity
|
|
|
15
15
|
twisted>=21
|
|
16
16
|
|
|
17
17
|
[all]
|
|
18
|
+
certifi
|
|
18
19
|
couchdb
|
|
19
20
|
hpfeeds>=3.0.0
|
|
20
21
|
looseversion
|
|
@@ -51,6 +52,7 @@ slack-sdk
|
|
|
51
52
|
couchdb
|
|
52
53
|
|
|
53
54
|
[datadog]
|
|
55
|
+
certifi
|
|
54
56
|
|
|
55
57
|
[datadog:python_version < "3"]
|
|
56
58
|
cryptography<=2.8
|
|
@@ -61,6 +63,7 @@ cryptography
|
|
|
61
63
|
pyOpenSSL
|
|
62
64
|
|
|
63
65
|
[discord]
|
|
66
|
+
certifi
|
|
64
67
|
|
|
65
68
|
[elastic]
|
|
66
69
|
|
|
@@ -113,6 +116,7 @@ PyMySQL
|
|
|
113
116
|
mysqlclient>=1.3.12
|
|
114
117
|
|
|
115
118
|
[nlcvapi]
|
|
119
|
+
certifi
|
|
116
120
|
|
|
117
121
|
[nlcvapi:python_version < "3"]
|
|
118
122
|
pyOpenSSL<=18.0.0
|
|
@@ -148,6 +152,7 @@ slack-sdk
|
|
|
148
152
|
[sqlite]
|
|
149
153
|
|
|
150
154
|
[telegram]
|
|
155
|
+
certifi
|
|
151
156
|
|
|
152
157
|
[textlog]
|
|
153
158
|
|
|
@@ -166,12 +166,13 @@ def make_pypi_readme():
|
|
|
166
166
|
extras={
|
|
167
167
|
'couchdb': ['couchdb'],
|
|
168
168
|
'datadog': [
|
|
169
|
+
'certifi',
|
|
169
170
|
'cryptography<=2.8 ; python_version < "3"',
|
|
170
171
|
'pyOpenSSL<=18.0.0 ; python_version < "3"',
|
|
171
172
|
'cryptography ; python_version >= "3"',
|
|
172
173
|
'pyOpenSSL ; python_version >= "3"',
|
|
173
174
|
],
|
|
174
|
-
'discord': [],
|
|
175
|
+
'discord': ['certifi'],
|
|
175
176
|
'elastic': [
|
|
176
177
|
'elasticsearch<=7.13 ; python_version < "3"',
|
|
177
178
|
'numpy<=1.16.6 ; python_version < "3"',
|
|
@@ -199,6 +200,7 @@ extras={
|
|
|
199
200
|
'mysqlclient>=1.3.12 ; python_version >= "3"',
|
|
200
201
|
],
|
|
201
202
|
'nlcvapi': [
|
|
203
|
+
'certifi',
|
|
202
204
|
'pyOpenSSL<=18.0.0 ; python_version < "3"',
|
|
203
205
|
'pyOpenSSL ; python_version >= "3"',
|
|
204
206
|
],
|
|
@@ -217,7 +219,7 @@ extras={
|
|
|
217
219
|
],
|
|
218
220
|
'socketlog': [],
|
|
219
221
|
'sqlite': [],
|
|
220
|
-
'telegram': [],
|
|
222
|
+
'telegram': ['certifi'],
|
|
221
223
|
'textlog': [],
|
|
222
224
|
'xmpp': ['xmpppy>=0.7.3'],
|
|
223
225
|
}
|
|
@@ -262,6 +264,11 @@ setup(
|
|
|
262
264
|
'Topic :: Security',
|
|
263
265
|
],
|
|
264
266
|
python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*',
|
|
267
|
+
options={
|
|
268
|
+
'bdist_wheel': {
|
|
269
|
+
'python_tag': 'py2.py3',
|
|
270
|
+
}
|
|
271
|
+
},
|
|
265
272
|
|
|
266
273
|
# -------------------------------------------------------------------------
|
|
267
274
|
# Installed packages:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|