vectorize logo transparent

Vulnerability Research: Behind Great Walls

By Vectorize

Table of Contents

For many years now, the People’s Republic of China (PRC) has been building its own advanced cybersecurity services to reduce its reliance on Western technology.

A lot of effort has been put into creating "home grown" Linux distributions, long before the recent embargo announcements, CHIPS Act, and other legislative efforts aimed at disrupting PRC’s development in IT technology.

The Kylin based Linux distributions are well known, and come in many flavors:

  • KylinOS, NeoKylin, UbuntuKylin and now (in 2023) finally openKylin.

Some of the PRC’s largest tech companies and institutions jointly contribute to the distributions’ development, and they have been hyped up by local media. So it is about time to have a look from the security research perspective, right?

I chose openKylin 1.0, since it was just recently released and freely available. It is also one of the very few distributions that offer images for several RISC-V platforms, so you can see that they are really serious about their independent tech strategy and intelligence solutions.

I will be using the x86-64 image for ease of use in this exploit development exercise, but I confirmed the vulnerabilities also exist on aarch64 (ARM 64), and most likely they are also present in the RISC-V images.

openKylin

So what are you going to do if your fancy kernel 0days are not yet ported by your local TAO office to work on RISC-V boxes?

Right: userland Python DBus services to the rescue!

I will keep it short and and focus on the useful service. There are a couple of candidates to choose from, but some are mitigated by fs.protected_symlinks and some others by missing files inside /sys on aarch64.

Here is the DBus service file which I am going to use:

[D-BUS Service]
Name=com.kylin.os.manager.systembus
Exec=/usr/bin/kylin-os-manager-systembus.py
User=root

Apparently, one of their own inventions, and here is the Python:

import os, sys
import signal
import dbus
import dbus.mainloop.glib
from gi.repository import GObject
from single import SingleInstance
#import gobject

if __name__ == '__main__':

[1] myapp = SingleInstance("/tmp/kylin-os-mananger-daemon-%d.pid" % os.getuid())

[...]

    if os.path.exists("/var/lib/apt/lists/lock"):
        os.remove("/var/lib/apt/lists/lock")
    from systemdbus.daemon import Daemon
    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
    GObject.threads_init()
    mainloop = GObject.MainLoop()
    #gobject.threads_init()
    #mainloop = gobject.MainLoop()
    signal.signal(signal.SIGINT, lambda : mainloop.quit())
    Daemon(dbus.SystemBus(), mainloop)
    mainloop.run()

and most importantly, the actual definition of the SingleInstance class:

# ensure that a single instance of the applet is running for each user
class SingleInstance(object):

    #Initialize, specifying a path to store pids
    def __init__(self,pidPath):

        self.pidPath = pidPath
        self.lasterror = False
        if os.path.exists(pidPath):
            # Make sure it is not a "stale" pidFile
[2]         pid = open(pidPath, 'r').read().strip()
            # Check list of running pids, if not running, it is stale, so overwrite
            pidRunning = subprocess.getoutput('ls -1 /proc | grep ^%s$' % pid)
            self.lasterror = True if pidRunning else False
        else:
            self.lasterror = False

At [1], a file from /tmp is used that does not already exist and can be filled by
attackers with arbitrary content. At [2] this content is used and assumed to be a PID (process ID).

But it can contain anything, not just numbers. So pid is actually a string and it is passed to the Python subprocess module, which inserts it into a command called with sh -c.

Things are so easy, the shell can be directly called with handcrafted magic:

kylin_shell

Since the vulnerable code is called on the instantiation of the object, it is not necessary to
pick any particular DBus method or parameters. We just need to trigger the DBus activation
without any annoying PolKit rules in the way.

You are my one and only: SingleInstance

The software reuse pattern tells students in cybersecurity training not to reinvent the wheel but to abstract common functions into modules or classes and re-use them.

Python supports developers with that, so it comes as a little surprise I also find SingleInstance in other flavors of Kylin: UbuntuKylin 23.04.

The DBus service has a different name this time:
com.kylin.assistant.systemdaemon and the name of the golden file is
/tmp/kylin-assistant-systemdbus-0.pid, but intelligence solutions work as smoothly as before with these changed parameters.

By Vectorize

Share Article

Check out next article

Hardware Security Research: Building a Backdoor Inside a CPU Core