CVE-2022-37298 (Python, published 2022-10-20T11:15Z)
Description: value: Shinken Solutions Shinken Monitoring Version 2.4.3 affected is vulnerable to Incorrect Access Control. The SafeUnpickler class found in shinken/safepickle.py implements a weak authentication scheme when unserializing objects passed from monitoring nodes to the Shinken monitoring server.
Severity: nan (CVSS v3 base score: 9.8)
Weakness: CWE-287 \u2014 Improper Authentication
When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
Repository: https://github.com/naparuba/shinken
Files changed:
Changelog
THANKS
setup.py
shinken/safepickle.py
test/test_safe_pickle.py
Commit message: Fix: Dailymotion team (Nicolas Perraud) found a way to bypass pickle.loads protection and execute code from daemon if you can exchange with it's internal port. Now we whitelist allowed class only :)

Bonus: update the changelog, Thanks & setup.py so we can release soon :)


Vulnerable code:
3.0.03 - X/X/2017
    version="2.4.3",

# Unpickle but strip and remove all __reduce__ things
# so we don't allow external code to be executed
        'copy_reg': set(['_reconstructor']),
        '__builtin__': set(['object', 'set']),


        if module not in cls.PICKLE_SAFE and not module.startswith('shinken.'):
        if not module.startswith('shinken.') and name not in cls.PICKLE_SAFE[module]:






        
        

        print "Creating payload"
        print "Payload", buf
        #self.assertEqual('HARD', host.state_type)
        print "Now loading payload"
        #SafeUnpickler.loads(buf)
        print should_not_change

Fixed code:
2.4.4 - XX/02/2022
        Enh: (Christophe Simon) Force problem/impact evaluation
        Enh: (Christophe Simon) added strict object name conflict policy
        Enh: (Christophe Simon) better objects balancing between schedulers (#1999)
        Enh: (Christophe Simon) Maintenance checks (#1929)
        Enh: (Nicolas DUPEUX) Add an option to dump configuration as build by shinken to a json file (#1954)
        Enh: (Lionel Sausin) Add instructions to use --install-scripts in pip (#1890)
        Enh: (Christophe Simon) extend duplicate_foreach to host/hostgroups (#1905)
        Enh: (Christophe Simon) Made memory free an opt-in option
        Enh: (Christophe Simon) Explicitly frees memory when receiving new conf
        Enh: (Christophe Simon) Harmonized graceful restart
        Enh: (David Durieux) Add support of proxy socks5 for shinken cli (#1583)
        Enh: (Mateusz Korniak) Add info about exit error code when check finished with error/was signalled. (#1766)
        Fix: Dailymotion team (Nicolas Perraud) found a way to bypass pickle.loads protection and execute code from daemon if you can exchange with it's internal port. Now we whitelist allowed class only :)
        Fix: (Christophe Simon) Enforced downtime state after retention load
        Fix: (V. D'AGOSTINO) Update about.rst to fix a typo
        Fix: (Flavien Peyre) multiple notification way when using a contact template (#1867)
        Fix: (Dani Rodríguez) Update typographic errors and explain things in the help messages (#1968)
        Fix: (Dani Rodríguez) brok queues not producing broks (#1971)
        Fix: (David Gil) Pin CherryPy dependpency < 9.0.0 (#1983)
        Fix: (Vladimir Kazarin) Update dmz-monitoring.rst (#1980)
        Fix: (Muhammad Zeeshan Qazi) #1961 : Replace #!/usr/bin/python to #!/usr/bin/python2 (#1979)
        Fix: (Dani Rodríguez) Fixes calls to cProfile in shinken binaries (#1967)
        Fix: (wilfriedroset) shinken typo (#1974)
        Fix: (efficks) CherryPy >= 3 required (#1943)
        Fix: (David Gil) osmatch in nmap discovery process (#1944)
        Fix: (Konstantin Shalygin) modules_manager: ignore '.git' dir when load modules. (#1883)
        Fix: (Christophe Simon) http_client exceptions management
        Fix: (Christophe Simon) OSX (darwin) platform detection
        Fix: (Christophe Simon) unicode string parisng in shinken cmdline
        Fix: (Olivier Hanesse) missing ini file for shinken.io
        Fix: (Olivier Hanesse) #1857 Set default value for proxy/proxy_socks5 for shinken cli
        Fix: (Olivier Hanesse) #1845 Add missing paramter for statsd
        Fix: (Nicolas Le Manchet) Improve systemd units reliability
        Fix: (Frédéric MOHIER) Iterate correctly in the Services object (#1842)


        MISC FIXES
        Fix: (Yann 'Ze' Richard) missing email header to prevent "Out Of Office" messages (#2014)
        Fix: (se4598*) #1951: Email notification in text mode (#1986)
        Enh: (Arthur Lutz) starttls and username/password for SMTP
        Fix: (George Shuklin) trace on sening smtp messages
        Doc: (rdmo) than -> as
        Doc: (dodofox) specify host inheritance for contact/contactgroup (#1872)
        Doc: (Elliott Peay) Fix default reactionner port in docs
        Fix: (Konstantin Shalygin) Encode attachment. (#1817)
        Fix: (Marc Remy) Typo: Update some doc files. (#1821)
To Nicolas Perraud for his warn about a RCE in the daemon exchange
    version="2.4.4",
# Unpickle but strip and remove all __reduce__ things so we don't allow external code to be executed
        'copy_reg'                                : set(['_reconstructor']),
        '__builtin__'                             : set(['object', 'set']),
        
        # Be sure to white list all we need to pickle.loads so user cannot exploit other modules (like bottle ^^)
        'shinken.acknowledge'                     : set(['Acknowledge']),
        'shinken.basemodule'                      : set(['BaseModule']),
        'shinken.borg'                            : set(['Borg']),
        'shinken.check'                           : set(['Check']),
        'shinken.brok'                            : set(['Brok']),
        'shinken.commandcall'                     : set(['CommandCall']),
        'shinken.comment'                         : set(['Comment']),
        'shinken.complexexpression'               : set(['ComplexExpressionNode']),
        'shinken.contactdowntime'                 : set(['ContactDowntime']),
        'shinken.daterange'                       : set(['Timerange',
                                                         'Daterange',
                                                         'CalendarDaterange',
                                                         'StandardDaterange',
                                                         'MonthWeekDayDaterange',
                                                         'MonthDateDaterange',
                                                         'WeekDayDaterange',
                                                         'MonthDayDaterange',
                                                         ]),
        'shinken.dependencynode'                  : set(['DependencyNode']),
        'shinken.downtime'                        : set(['Downtime']),
        'shinken.discovery.discoverymanager'      : set(['DiscoveredHost']),
        'shinken.eventhandler'                    : set(['EventHandler']),
        'shinken.external_command'                : set(['ExternalCommand']),
        'shinken.graph'                           : set(['Graph']),
        'shinken.message'                         : set(['Message']),
        'shinken.modulesctx'                      : set(['ModulesContext']),
        'shinken.modulesmanager'                  : set(['ModulesManager']),
        'shinken.notification'                    : set(['Notification']),
        'shinken.objects.command'                 : set(['DummyCommand', 'Command', 'Commands']),
        'shinken.objects.arbiterlink'             : set(['ArbiterLink', 'ArbiterLinks']),
        'shinken.objects.businessimpactmodulation': set(['Businessimpactmodulation', 'Businessimpactmodulations']),
        'shinken.objects.brokerlink'              : set(['BrokerLink', 'BrokerLinks']),
        'shinken.objects.checkmodulation'         : set(['CheckModulation', 'CheckModulations']),
        'shinken.objects.config'                  : set(['Config']),
        'shinken.objects.contact'                 : set(['Contact', 'Contacts']),
        'shinken.objects.contactgroup'            : set(['Contactgroup', 'Contactgroups']),
        'shinken.objects.discoveryrule'           : set(['Discoveryrule', 'Discoveryrules']),
        'shinken.objects.discoveryrun'            : set(['Discoveryrun', 'Discoveryruns']),
        'shinken.objects.escalation'              : set(['Escalation', 'Escalations']),
        'shinken.objects.hostdependency'          : set(['Hostdependency', 'Hostdependencies']),
        'shinken.objects.host'                    : set(['Host', 'Hosts']),
        'shinken.objects.hostescalation'          : set(['Hostescalation', 'Hostescalations']),
        'shinken.objects.itemgroup'               : set(['Itemgroup', 'Itemgroups']),
        'shinken.objects.hostgroup'               : set(['Hostgroup', 'Hostgroups']),
        'shinken.objects.hostextinfo'             : set(['HostExtInfo', 'HostsExtInfo']),
        'shinken.objects.item'                    : set(['Item', 'Items']),
        'shinken.objects.macromodulation'         : set(['MacroModulation', 'MacroModulations']),
        'shinken.objects.matchingitem'            : set(['MatchingItem']),
        'shinken.objects.pack'                    : set(['Pack', 'Packs']),
        'shinken.objects.notificationway'         : set(['NotificationWay', 'NotificationWays']),
        'shinken.objects.module'                  : set(['Module', 'Modules']),
        'shinken.objects.pollerlink'              : set(['PollerLink', 'PollerLinks']),
        'shinken.objects.reactionnerlink'         : set(['ReactionnerLink', 'ReactionnerLinks']),
        'shinken.objects.realm'                   : set(['Realm', 'Realms']),
        'shinken.objects.receiverlink'            : set(['ReceiverLink', 'ReceiverLinks']),
        'shinken.objects.resultmodulation'        : set(['Resultmodulation', 'Resultmodulations']),
        'shinken.objects.satellitelink'           : set(['SatelliteLink', 'SatelliteLinks']),
        'shinken.objects.schedulingitem'          : set(['SchedulingItem']),
        'shinken.objects.schedulerlink'           : set(['SchedulerLink', 'SchedulerLinks']),
        'shinken.objects.service'                 : set(['Service', 'Services']),
        'shinken.objects.servicedependency'       : set(['Servicedependency', 'Servicedependencies']),
        'shinken.objects.serviceescalation'       : set(['Serviceescalation', 'Serviceescalations']),
        'shinken.objects.serviceextinfo'          : set(['ServiceExtInfo', 'ServicesExtInfo']),
        'shinken.objects.servicegroup'            : set(['Servicegroup', 'Servicegroups']),
        'shinken.objects.timeperiod'              : set(['Timeperiod', 'Timeperiods']),
        'shinken.objects.trigger'                 : set(['Trigger', 'Triggers']),
        
    
    
        if module not in cls.PICKLE_SAFE:
        if name not in cls.PICKLE_SAFE[module]:
    
    
import sys



    
    
    
    
    
        
        print("Creating payload")
        print("Payload", buf)
        # self.assertEqual('HARD', host.state_type)
        print("Now loading payload")
        
        
        # SafeUnpickler.loads(buf)
        
        
        print (should_not_change)
    
    
    # Thanks to security team @Dailymotion, we did have a RCE that ook like a return into libc
    # exploit: they are using the bottle._load code (that blindly __import__) so the injected
    # code will finally be executed. And as it's shinken.webui.bottle, it's ok with the
    # safe_pickle filter. Smart ^^
    def test_safe_pickle_exploit_rce(self):
        ###### Phase 1: can be exploited
        # Arrange
        rce_path = '/rce_exploited'
        if rce_path in sys.path:
            sys.path.remove(rce_path)
        
        payload = b"""cshinken.webui.bottlewebui
_load
(S'sys:path.append("%s")'
tR.""" % rce_path
        
        # Act
        print("Now loading payload")
        pickle.loads(payload)
        
        # Assert
        self.assertTrue(rce_path in sys.path)
        
        ##### Phase 2: no more exploitable by calling the good one
        # Arrange
        sys.path.remove(rce_path)
        
        
        # SafeUnpickler.loads(buf)
        def launch_safe_pickle():
            SafeUnpickler.loads(payload)
        # Act
        self.assertRaises(ValueError, launch_safe_pickle)
        
        # Assert
        self.assertTrue(rce_path not in sys.path)
    
    
    # Thanks to security team @Dailymotion, we did have a RCE that ook like a return into libc
    # exploit: they are using the bottle._load code (that blindly __import__) so the injected
    # code will finally be executed. And as it's shinken.webui.bottle, it's ok with the
    # safe_pickle filter. Smart ^^
    def test_safe_pickle_exploit_rce_can_load(self):
        ###### Phase 1: can be exploited
        # Arrange
        
        payload = pickle.dumps(Brok('void', {}))
        
        # Act
        b = SafeUnpickler.loads(payload)
        
        # Assert
        self.assertTrue(isinstance(b, Brok))