From b015d5880aba8b0449f28ed5a3a1bbfcbd7e1ee0 Mon Sep 17 00:00:00 2001 From: spsf Date: Mon, 23 Feb 2026 22:43:12 -0600 Subject: [PATCH 1/7] load vuln fixed, sudo fixed --- Src/Hyperion-bash/bin/sudo | 8 ++-- .../lib/modules/CC-Tweaked/25_tty.kmod | 4 +- .../lib/modules/Hyperion/10_vfs.kmod | 40 ++++++++++++++----- .../lib/modules/Hyperion/30_userspace.kmod | 3 ++ .../lib/modules/Hyperion/40_auth.kmod | 12 +++--- .../lib/modules/Hyperion/92_permissions.kmod | 8 +++- 6 files changed, 50 insertions(+), 25 deletions(-) diff --git a/Src/Hyperion-bash/bin/sudo b/Src/Hyperion-bash/bin/sudo index b0ad7fb..a843cba 100644 --- a/Src/Hyperion-bash/bin/sudo +++ b/Src/Hyperion-bash/bin/sudo @@ -31,7 +31,7 @@ for j = i + 1, #cmdArgs do restArgs[#restArgs + 1] = cmdArgs[j] end local currentUid = syscall.getuid() local currentUser = syscall.getUsername(currentUid) or tostring(currentUid) -local targetUid = syscall.getuid(targetUser) +local targetUid = syscall.getuidbyname(targetUser) if not targetUid then print("sudo: user '" .. targetUser .. "' does not exist") syscall.exit(1) @@ -39,7 +39,7 @@ if not targetUid then end if currentUid ~= 0 then - printInline("[sudo] password for " .. currentUser .. ": ") + printInline("[sudo] password for root: ") local pw = "" while true do local ch = syscall.read(0) @@ -55,7 +55,7 @@ if currentUid ~= 0 then end end - local ok, err = syscall.elevate(currentUser, pw) + local ok, err = syscall.elevate("root", pw) if not ok then sleep(1) print("sudo: Authentication failure") @@ -63,7 +63,7 @@ if currentUid ~= 0 then return end - if targetUid ~= 0 then + if targetUid ~= currentUid then syscall.setuid(targetUid) end else diff --git a/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod b/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod index 560612a..9c5e168 100644 --- a/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod +++ b/Src/Hyperion-firmware-cct/lib/modules/CC-Tweaked/25_tty.kmod @@ -37,7 +37,7 @@ function peripheral.isPresent(name) end function peripheral.getType(peripheral) - if type(peripheral) == "string" then -- Peripheral name passed + if type(peripheral) == "string" then if native.isPresent(peripheral) then return native.getType(peripheral) end @@ -58,7 +58,7 @@ function peripheral.getType(peripheral) end function peripheral.hasType(peripheral, peripheral_type) - if type(peripheral) == "string" then -- Peripheral name passed + if type(peripheral) == "string" then if native.isPresent(peripheral) then return native.hasType(peripheral, peripheral_type) end diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod index d984820..980f53f 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod @@ -5,14 +5,14 @@ kernel.vfs = vfs vfs.mounts = {["$"] = "/"} vfs.disks = kernel.disks --- Metafile format (version 1) --- File header: 1 byte = version (0x01) +-- Metafile format (version 2) +-- File header: 1 byte = version (0x02) -- Per-entry: -- 1 byte = name length -- N bytes = name -- 1 byte = entry type (0x00 = regular, 0x01 = symlink) --- 1 byte = owner uid --- 1 byte = group gid +-- 2 bytes = owner uid (little-endian uint16) +-- 2 bytes = group gid (little-endian uint16) -- 2 bytes = perms (little-endian uint16) -- bit 0 = world-write bit 1 = world-read -- bit 2 = group-write bit 3 = group-read @@ -24,12 +24,16 @@ vfs.disks = kernel.disks -- 1 byte = cmeta length -- N bytes = cmeta (for symlinks: the link target path) -- +-- Version 1: +-- 1 byte name len, N bytes name, 1 byte etype, 1 byte owner, +-- 1 byte group, 2 bytes perms (little-endian), 1 byte cmeta len, N bytes cmeta +-- -- Version 0: -- No file header. Per-entry: -- 1 byte name len, N bytes name, 1 byte owner, 1 byte group, -- 1 byte perms (low 7 bits only), 1 byte cmeta len, N bytes cmeta -local META_VERSION = 0x01 +local META_VERSION = 0x02 local function bit_is_set(num, bit) return math.floor(num / (2 ^ bit)) % 2 == 1 @@ -41,8 +45,9 @@ local function parseMetafile(raw) local p = 1 local version = 0 - if raw:byte(1) == META_VERSION then - version = META_VERSION + local firstByte = raw:byte(1) + if firstByte == 0x02 or firstByte == 0x01 then + version = firstByte p = 2 end @@ -54,13 +59,22 @@ local function parseMetafile(raw) local etype, owner, group, perms, cmeta - if version == META_VERSION then + if version == 0x02 then + -- v2: etype(1) + owner(2) + group(2) + perms(2) = 7 bytes + if p + 6 > #raw then break end + etype = raw:byte(p); p = p + 1 + owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 + group = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 + perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 + elseif version == 0x01 then + -- v1: etype(1) + owner(1) + group(1) + perms(2) = 5 bytes if p + 4 > #raw then break end etype = raw:byte(p); p = p + 1 owner = raw:byte(p); p = p + 1 group = raw:byte(p); p = p + 1 perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 else + -- v0: owner(1) + group(1) + perms(1) = 3 bytes if p + 2 > #raw then break end etype = 0x00 owner = raw:byte(p); p = p + 1 @@ -85,12 +99,16 @@ end local function makeMetafile(meta) local out = string.char(META_VERSION) for name, m in pairs(meta) do - local plo = m.perms % 256 - local phi = math.floor(m.perms / 256) % 256 + local plo = m.perms % 256 + local phi = math.floor(m.perms / 256) % 256 + local olo = (m.owner or 0) % 256 + local ohi = math.floor((m.owner or 0) / 256) % 256 + local glo = (m.group or 0) % 256 + local ghi = math.floor((m.group or 0) / 256) % 256 out = out .. string.char(#name) .. name .. string.char(m.etype or 0x00) - .. string.char(m.owner, m.group, plo, phi) + .. string.char(olo, ohi, glo, ghi, plo, phi) .. string.char(#m.cmeta) .. m.cmeta end return out diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod index 8dbc342..fa11794 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod @@ -3,6 +3,7 @@ local args = {...} local kernel = args[1] kernel._G = _G + local function readonly(tbl) return setmetatable({}, { __index = function(_, key) @@ -49,8 +50,10 @@ local function readonly(tbl) __metatable = false }) end +local origLoad = load kernel._U = readonly(kernel._G) kernel.allowGlobalOverwrites = true kernel._U._G = kernel._U +kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end kernel.allowGlobalOverwrites = false diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod index 111c79d..71c7459 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod @@ -591,13 +591,13 @@ function auth.elevate(targetUsername, password) local task = kernel.currentTask local prevUid = task.uid - task.uid = uid - task.euid = uid - task.gid = tonumber(entry[2]) or uid - task.egid = tonumber(entry[2]) or uid - kernel.uid = uid + task.uid = 0 + task.euid = 0 + task.gid = 0 + task.egid = 0 + kernel.uid = 0 - kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> " .. tostring(uid) .. " (" .. targetUsername .. ")") + kernel.log("AUTH: elevate uid=" .. tostring(prevUid) .. " -> 0 (via " .. targetUsername .. ")") return true, uid end diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod index 9f1a90e..0a926d3 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod @@ -13,15 +13,19 @@ local RW____ = P.OWNER_R + P.OWNER_W local RWXRWXRWX = PERM.RWXRWXRWX local SUID_755 = PERM.SUID_755 -local META_VERSION = 0x01 +local META_VERSION = 0x02 local rootDisk = kernel.disks["$"] local function makeEntry(name, etype, owner, group, perms, cmeta) cmeta = cmeta or "" local plo = perms % 256 local phi = math.floor(perms / 256) % 256 + local olo = (owner or 0) % 256 + local ohi = math.floor((owner or 0) / 256) % 256 + local glo = (group or 0) % 256 + local ghi = math.floor((group or 0) / 256) % 256 return string.char(#name) .. name - .. string.char(etype, owner, group, plo, phi) + .. string.char(etype, olo, ohi, glo, ghi, plo, phi) .. string.char(#cmeta) .. cmeta end From a6d2f6dca72d6b1c7f6f933b5a520f64a2149a84 Mon Sep 17 00:00:00 2001 From: spsf Date: Mon, 23 Feb 2026 23:05:13 -0600 Subject: [PATCH 2/7] /home/user owned by user, user starts in cwd /home/user --- Src/Hyperion-bash/bin/chgrp | 10 +- Src/Hyperion-bash/bin/chmod | 8 +- Src/Hyperion-bash/bin/chown | 10 +- Src/Hyperion-bash/bin/hysh | 8 +- Src/Hyperion-bash/bin/login | 3 + Src/Hyperion-core/sbin/init.lua | 3 + .../lib/modules/Hyperion/10_vfs.kmod | 54 ++++++- .../lib/modules/Hyperion/30_userspace.kmod | 4 +- .../lib/modules/Hyperion/40_auth.kmod | 2 + .../lib/modules/Hyperion/92_permissions.kmod | 138 +++++++++++++----- .../lib/modules/Hyperion/99_final.kmod | 5 + 11 files changed, 199 insertions(+), 46 deletions(-) create mode 100644 Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod diff --git a/Src/Hyperion-bash/bin/chgrp b/Src/Hyperion-bash/bin/chgrp index 51508f8..241af61 100644 --- a/Src/Hyperion-bash/bin/chgrp +++ b/Src/Hyperion-bash/bin/chgrp @@ -64,12 +64,18 @@ local newGid = resolveGid(groupStr) local function chgrpPath(path) local stat = syscall.stat(path) if not stat then - print(name .. ": cannot stat '" .. path .. "': No such file or directory") + print(name .. ": cannot stat '" .. path .. "': no such file or directory") return false end local ok, err = pcall(syscall.chown, path, stat.owner, newGid) if not ok then - print(name .. ": cannot change group of '" .. path .. "': " .. tostring(err)) + local msg = tostring(err) + if msg:find("EPERM") or msg:find("EACCES") then + msg = "operation not permitted (must be root)" + elseif msg:find("ENOENT") then + msg = "no such file or directory" + end + print(name .. ": cannot change group of '" .. path .. "': " .. msg) return false end return true diff --git a/Src/Hyperion-bash/bin/chmod b/Src/Hyperion-bash/bin/chmod index 6e97737..269a1e2 100644 --- a/Src/Hyperion-bash/bin/chmod +++ b/Src/Hyperion-bash/bin/chmod @@ -220,7 +220,13 @@ local function chmodPath(path) local ok, cerr = pcall(syscall.chmod, path, newPerms) if not ok then - print(name .. ": cannot change permissions of '" .. path .. "': " .. tostring(cerr)) + local msg = tostring(cerr) + if msg:find("EACCES") or msg:find("EPERM") then + msg = "permission denied" + elseif msg:find("ENOENT") then + msg = "no such file or directory" + end + print(name .. ": cannot change permissions of '" .. path .. "': " .. msg) return false end return true diff --git a/Src/Hyperion-bash/bin/chown b/Src/Hyperion-bash/bin/chown index 4cb32ad..ffabaf4 100644 --- a/Src/Hyperion-bash/bin/chown +++ b/Src/Hyperion-bash/bin/chown @@ -95,14 +95,20 @@ end local function chownPath(path) local stat = syscall.stat(path) if not stat then - print(name .. ": cannot stat '" .. path .. "': No such file or directory") + print(name .. ": cannot stat '" .. path .. "': no such file or directory") return false end local uid = newUid ~= nil and newUid or stat.owner local gid = newGid ~= nil and newGid or stat.group local ok, err = pcall(syscall.chown, path, uid, gid) if not ok then - print(name .. ": cannot change owner of '" .. path .. "': " .. tostring(err)) + local msg = tostring(err) + if msg:find("EPERM") or msg:find("EACCES") then + msg = "operation not permitted (must be root)" + elseif msg:find("ENOENT") then + msg = "no such file or directory" + end + print(name .. ": cannot change owner of '" .. path .. "': " .. msg) return false end return true diff --git a/Src/Hyperion-bash/bin/hysh b/Src/Hyperion-bash/bin/hysh index 5e45fdb..a1a8912 100644 --- a/Src/Hyperion-bash/bin/hysh +++ b/Src/Hyperion-bash/bin/hysh @@ -17,7 +17,13 @@ local commandHistory = {} local terminate = false syscall.setEnviron("SHELL","rtbash") syscall.setEnviron("PATH","/bin/") -syscall.chdir("/") +local _home = syscall.getEnviron("HOME") +if _home and _home ~= "" then + local ok = pcall(syscall.chdir, _home) + if not ok then syscall.chdir("/") end +else + syscall.chdir("/") +end local oldWD = "" for i = 1, 16 do diff --git a/Src/Hyperion-bash/bin/login b/Src/Hyperion-bash/bin/login index 912e952..ce0756f 100644 --- a/Src/Hyperion-bash/bin/login +++ b/Src/Hyperion-bash/bin/login @@ -96,6 +96,9 @@ local function spawnShell(username, uid, shell, homedir) end local chdirOk, chdirErr = pcall(syscall.chdir, homedir) + if not chdirOk then + pcall(syscall.chdir, "/") + end local ok, err = pcall(syscall.execspawn, shell, username .. ":shell") if not ok then diff --git a/Src/Hyperion-core/sbin/init.lua b/Src/Hyperion-core/sbin/init.lua index b95bce5..574c93a 100644 --- a/Src/Hyperion-core/sbin/init.lua +++ b/Src/Hyperion-core/sbin/init.lua @@ -14,6 +14,9 @@ for i,v in pairs(kernel.processes) do end, i) end +if not fs.exists("/bin/startup") then + fs.mkdir("/bin/startup") +end local files = fs.list("/bin/startup") if not files then error("Failed to list /bin/startup") end for i,v in ipairs(files) do diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod index 980f53f..20ed71d 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod @@ -164,6 +164,9 @@ local function resolveMount(normalPath) return vfs.disks[mountId], diskPath end +-- Expose parser for use by other modules (e.g. permissions seeder) +vfs._parseMetafile = parseMetafile + local function readMetaEntry(disk, parentDiskPath, filename) if filename == ".meta" then error("Cannot open metafile") end local mp @@ -177,6 +180,15 @@ local function readMetaEntry(disk, parentDiskPath, filename) if not ok or not f then return nil end local raw = f.read(65535) if f.close then f.close() end + + -- Auto-upgrade: if this .meta file is not v2, rewrite it as v2 now + if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then + local upgraded = makeMetafile(parseMetafile(raw)) + local wok, wf = pcall(function() return disk:open(mp, "w") end) + if wok and wf then wf.write(upgraded); if wf.close then wf.close() end end + raw = upgraded + end + local parsed = parseMetafile(raw) return parsed[filename] end @@ -418,6 +430,8 @@ function vfs.open(path, mode) if not disk then error("NODISK") end local meta = getFileMeta(path) + local isNew = (mode == "w" or mode == "a") and not disk:fileExists(diskPath) + checkperms(meta, mode == "r" and "r" or "w") local handle @@ -426,6 +440,22 @@ function vfs.open(path, mode) if type(handle) ~= "table" then error("ENFILE") end end + -- If this is a newly created file, stamp it with the creating user's ownership + if isNew then + local euid = (task and (task.euid or task.uid)) or kernel.uid + local egid = (task and task.gid) or 0 + local norm = normalizePath(path) + local parent = norm:match("^(.*)/[^/]+$") or "/" + if parent == "" then parent = "/" end + local name = norm:match("[^/]+$") + if name then + local entry = { etype=0x00, owner=euid, group=egid, + perms=vfs.PERM.RW_R_R, cmeta="" } + pcall(writeMetaEntry, parent, name, entry, false) + meta = entry + end + end + local fobj = newFileObj(handle, mode, path, meta, disk:type(diskPath)) if mode == "r" and bit_is_set(meta.perms, 6) then fobj.suid_owner = meta.owner @@ -592,15 +622,33 @@ function vfs.listdir(path) end function vfs.mkdir(path) + local norm = normalizePath(path) + local parent = norm:match("^(.*)/[^/]+$") or "/" + if parent == "" then parent = "/" end + local parentMeta = getFileMeta(parent) + checkperms(parentMeta, "w") local disk, diskPath = resolvePath(path) - local meta = getFileMeta(path) - checkperms(meta, "w") disk:makeDirectory(diskPath) + -- Stamp the new directory with the creating user's ownership + local task = kernel.currentTask + local euid = (task and (task.euid or task.uid)) or kernel.uid + local egid = (task and task.gid) or 0 + local name = norm:match("[^/]+$") + if name then + local entry = { etype=0x00, owner=euid, group=egid, + perms=vfs.PERM.RWX_RX, cmeta="" } + pcall(writeMetaEntry, parent, name, entry, false) + end end function vfs.remove(path) + local norm = resolveSymlinks(path, true) + local parent = norm:match("^(.*)/[^/]+$") or "/" + if parent == "" then parent = "/" end + local parentMeta = getFileMeta(parent) + checkperms(parentMeta, "w") + local meta = getFileMeta(path, true) - checkperms(meta, "w") if kernel.unixSockets and kernel.unixSockets[path] then kernel.unixSockets[path] = nil diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod index fa11794..d2fab03 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod @@ -50,10 +50,10 @@ local function readonly(tbl) __metatable = false }) end -local origLoad = load +--local origLoad = load kernel._U = readonly(kernel._G) kernel.allowGlobalOverwrites = true kernel._U._G = kernel._U -kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end +--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end kernel.allowGlobalOverwrites = false diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod index 71c7459..821a64d 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod @@ -394,6 +394,8 @@ function auth.newUser(username, password, gid, homedir, shell) if kernel.vfs.mkdir and not kernel.vfs.exists(homedir) then kernel.vfs.mkdir(homedir) + -- Homedir must be owned by the new user, not root + pcall(kernel.vfs.chown, homedir, uid, uid) end kernel.log("AUTH: new user '" .. username .. "' uid=" .. tostring(uid)) diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod index 0a926d3..da3e8d4 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod @@ -51,12 +51,106 @@ end local REG = 0x00 -if rootDisk:fileExists(".meta") then - kernel.log("Permissions already seeded, skipping.", "INFO") -else +-- All known /bin entries with their permissions +local BIN_ENTRIES = { + {"cat", REG, 0, 0, RWX_RX_RX}, + {"chattr", REG, 0, 0, RWX_RX_RX}, + {"chgrp", REG, 0, 0, RWX_RX_RX}, + {"chmod", REG, 0, 0, RWX_RX_RX}, + {"chown", REG, 0, 0, RWX_RX_RX}, + {"chroot", REG, 0, 0, RWX_RX_RX}, + {"clear", REG, 0, 0, RWX_RX_RX}, + {"echo", REG, 0, 0, RWX_RX_RX}, + {"hfetch", REG, 0, 0, RWX_RX_RX}, + {"help", REG, 0, 0, RWX_RX_RX}, + {"hysh", REG, 0, 0, RWX_RX_RX}, + {"hyshex", REG, 0, 0, RWX_RX_RX}, + {"id", REG, 0, 0, RWX_RX_RX}, + {"install", REG, 0, 0, RWX_RX_RX}, + {"ln", REG, 0, 0, RWX_RX_RX}, + {"login", REG, 0, 0, SUID_755 }, + {"loimgcreate", REG, 0, 0, RWX_RX_RX}, + {"looptest", REG, 0, 0, RWX_RX_RX}, + {"losetup", REG, 0, 0, RWX_RX_RX}, + {"ls", REG, 0, 0, RWX_RX_RX}, + {"lsusers", REG, 0, 0, RWX_RX_RX}, + {"lua", REG, 0, 0, RWX_RX_RX}, + {"luaold", REG, 0, 0, RWX_RX_RX}, + {"micro", REG, 0, 0, RWX_RX_RX}, + {"mkdir", REG, 0, 0, RWX_RX_RX}, + {"mount", REG, 0, 0, RWX_RX_RX}, + {"passwd", REG, 0, 0, RWX_RX_RX}, + {"ps", REG, 0, 0, RWX_RX_RX}, + {"pwd", REG, 0, 0, RWX_RX_RX}, + {"readlink", REG, 0, 0, RWX_RX_RX}, + {"sed", REG, 0, 0, RWX_RX_RX}, + {"socktest", REG, 0, 0, RWX_RX_RX}, + {"spm", REG, 0, 0, RWX_RX_RX}, + {"su", REG, 0, 0, SUID_755 }, + {"sudo", REG, 0, 0, SUID_755 }, + {"sysdump", REG, 0, 0, RWX_RX_RX}, + {"umount", REG, 0, 0, RWX_RX_RX}, + {"useradd", REG, 0, 0, RWX_RX_RX}, + {"userdel", REG, 0, 0, RWX_RX_RX}, + {"usermod", REG, 0, 0, RWX_RX_RX}, + {"whoami", REG, 0, 0, RWX_RX_RX}, + {"yes", REG, 0, 0, RWX_RX_RX}, + {"startup", REG, 0, 0, RWX_RX_RX}, +} + +-- Merge entries: always ensure all known entries exist with correct permissions. +-- This handles both fresh installs and upgrades (adds missing entries, upgrades +-- the on-disk format to v2 by rewriting). +local function mergeMeta(dir, entries) + local diskDir = dir + if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end + local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta") + + -- Read existing meta (may be v1 or v2) + local existing = {} + local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end) + if rok and rf then + local raw = rf.read(65535) + if rf.close then rf.close() end + -- Parse using the VFS parser (handles v0/v1/v2) + existing = kernel.vfs and kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw) or {} + end + + -- Add any missing entries (don't overwrite existing customised perms) + for _, e in ipairs(entries) do + if not existing[e[1]] then + existing[e[1]] = { + etype = e[2] or 0x00, + owner = e[3] or 0, + group = e[4] or 0, + perms = e[5] or RWX_RX_RX, + cmeta = e[6] or "", + } + end + end + + -- Write back as v2 + local data = string.char(META_VERSION) + for name, m in pairs(existing) do + data = data .. makeEntry(name, m.etype or 0x00, m.owner or 0, m.group or 0, m.perms or RWX_RX_RX, m.cmeta or "") + end + + local ok, err = pcall(function() + local f = rootDisk:open(metaPath, "w") + f.write(data) + f.close() + end) + if not ok then + kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8) + end +end + +local freshInstall = not rootDisk:fileExists(".meta") + +if freshInstall then kernel.log("Seeding filesystem permissions...", "INFO") - -- / + -- / (only on fresh install — these dirs are stable) writeMeta("/", { {"bin", REG, 0, 0, RWX_RX_RX}, {"boot", REG, 0, 0, RWX_RX_RX}, @@ -71,38 +165,10 @@ else {"var", REG, 0, 0, RWX_RX_RX}, }) - -- /bin - writeMeta("/bin", { - {"cat", REG, 0, 0, RWX_RX_RX}, - {"clear", REG, 0, 0, RWX_RX_RX}, - {"echo", REG, 0, 0, RWX_RX_RX}, - {"hfetch", REG, 0, 0, RWX_RX_RX}, - {"hysh", REG, 0, 0, RWX_RX_RX}, - {"hyshex", REG, 0, 0, RWX_RX_RX}, - {"install", REG, 0, 0, RWX_RX_RX}, - {"login", REG, 0, 0, SUID_755 }, - {"ls", REG, 0, 0, RWX_RX_RX}, - {"lua", REG, 0, 0, RWX_RX_RX}, - {"luaold", REG, 0, 0, RWX_RX_RX}, - {"mkdir", REG, 0, 0, RWX_RX_RX}, - {"ps", REG, 0, 0, RWX_RX_RX}, - {"pwd", REG, 0, 0, RWX_RX_RX}, - {"spm", REG, 0, 0, RWX_RX_RX}, - {"su", REG, 0, 0, SUID_755 }, - {"sudo", REG, 0, 0, SUID_755 }, - {"sysdump", REG, 0, 0, RWX_RX_RX}, - {"whoami", REG, 0, 0, RWX_RX_RX}, - {"yes", REG, 0, 0, RWX_RX_RX}, - {"startup", REG, 0, 0, RWX_RX_RX}, - {"ln", REG, 0, 0, RWX_RX_RX}, - {"readlink", REG, 0, 0, RWX_RX_RX}, - }) - writeMeta("/bin/startup", { {"test.lua", REG, 0, 0, RWX_RX_RX}, }) - -- /etc writeMeta("/etc", { {"passwd", REG, 0, 0, RW_R_R}, {"shadow", REG, 0, 0, RW____}, @@ -113,12 +179,10 @@ else {"secret", REG, 0, 0, RW____}, }) - -- /sbin writeMeta("/sbin", { {"init.lua", REG, 0, 0, RWX_RX_RX}, }) - -- /boot writeMeta("/boot", { {"kernel.lua", REG, 0, 0, RW_R_R }, {"boot.cfg", REG, 0, 0, RW_R_R }, @@ -129,7 +193,6 @@ else {"oc", REG, 0, 0, RWX_RX_RX}, }) - -- /lib writeMeta("/lib", { {"sys", REG, 0, 0, RWX_RX_RX}, {"modules", REG, 0, 0, RWX_RX_RX}, @@ -141,6 +204,11 @@ else }) kernel.log("Filesystem permissions seeded.", "INFO") +else + kernel.log("Permissions already seeded, merging /bin updates...", "INFO") end +-- Always merge /bin — adds missing entries and upgrades format to v2 +mergeMeta("/bin", BIN_ENTRIES) + kernel.log("Permission module loaded.", "INFO") diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod new file mode 100644 index 0000000..230a54f --- /dev/null +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod @@ -0,0 +1,5 @@ +local args = {...} +local kernel = args[1] + +local origLoad = load +--kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end \ No newline at end of file From 8798a2f4fef6a0bebca1f13ef8dff8e8f46954d7 Mon Sep 17 00:00:00 2001 From: spsf Date: Mon, 23 Feb 2026 23:26:21 -0600 Subject: [PATCH 3/7] 2 potential vulnerability fixes --- .../lib/modules/Hyperion/01_stdlib.kmod | 9 +++---- .../lib/modules/Hyperion/10_vfs.kmod | 27 +++++++++++++------ .../lib/modules/Hyperion/30_userspace.kmod | 6 ++--- .../lib/modules/Hyperion/99_final.kmod | 7 ++--- 4 files changed, 26 insertions(+), 23 deletions(-) diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod index 5ada732..5fa01c0 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod @@ -1,4 +1,7 @@ -- :Minify:-- +local kernel = ... +kernel.allowGlobalOverwrites = true + function string.hasSuffix(str, suffix) return string.sub(str, #suffix + 1) == suffix end @@ -68,30 +71,24 @@ end local function serialize(tbl, seen) seen = seen or {} - -- If we've seen this table before, return a placeholder to prevent infinite loops if seen[tbl] then return '"[Circular Reference]"' end - -- Mark this table as seen seen[tbl] = true local output = "{" local first = true for i, v in pairs(tbl) do - -- Handle comma placement more cleanly if not first then output = output .. "," end first = false - -- Serialize Key if type(i) == "string" then output = output .. "[\"" .. i .. "\"]=" elseif type(i) == "number" then output = output .. "[" .. tostring(i) .. "]=" end - -- Serialize Value if type(v) == "table" then - -- Pass the 'seen' table down to the recursive call output = output .. serialize(v, seen) elseif type(v) == "string" then output = output .. "[=[" .. v .. "]=]" diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod index 20ed71d..d15989a 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod @@ -60,21 +60,18 @@ local function parseMetafile(raw) local etype, owner, group, perms, cmeta if version == 0x02 then - -- v2: etype(1) + owner(2) + group(2) + perms(2) = 7 bytes if p + 6 > #raw then break end etype = raw:byte(p); p = p + 1 owner = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 group = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 elseif version == 0x01 then - -- v1: etype(1) + owner(1) + group(1) + perms(2) = 5 bytes if p + 4 > #raw then break end etype = raw:byte(p); p = p + 1 owner = raw:byte(p); p = p + 1 group = raw:byte(p); p = p + 1 perms = raw:byte(p) + raw:byte(p+1) * 256; p = p + 2 else - -- v0: owner(1) + group(1) + perms(1) = 3 bytes if p + 2 > #raw then break end etype = 0x00 owner = raw:byte(p); p = p + 1 @@ -114,6 +111,22 @@ local function makeMetafile(meta) return out end +local function validateComponent(part) + if part == "" then + error("EINVAL: empty path component", 2) + end + if part:sub(1, 2) == ".." then + error("EINVAL: illegal path component '" .. part .. "'", 2) + end + if part:find("\0", 1, true) then + error("EINVAL: null byte in path component", 2) + end + if part:find("/", 1, true) then + error("EINVAL: slash in path component", 2) + end + return true +end + local function normalizePath(path) local task = kernel.currentTask local cwd = task.cwd or "/" @@ -122,7 +135,9 @@ local function normalizePath(path) for part in path:gmatch("[^/]+") do if part == ".." then if #parts > 0 then table.remove(parts) end - elseif part ~= "." and part ~= "" then + elseif part == "." or part == "" then + else + validateComponent(part) table.insert(parts, part) end end @@ -164,7 +179,6 @@ local function resolveMount(normalPath) return vfs.disks[mountId], diskPath end --- Expose parser for use by other modules (e.g. permissions seeder) vfs._parseMetafile = parseMetafile local function readMetaEntry(disk, parentDiskPath, filename) @@ -181,7 +195,6 @@ local function readMetaEntry(disk, parentDiskPath, filename) local raw = f.read(65535) if f.close then f.close() end - -- Auto-upgrade: if this .meta file is not v2, rewrite it as v2 now if raw and #raw > 0 and raw:byte(1) ~= META_VERSION then local upgraded = makeMetafile(parseMetafile(raw)) local wok, wf = pcall(function() return disk:open(mp, "w") end) @@ -440,7 +453,6 @@ function vfs.open(path, mode) if type(handle) ~= "table" then error("ENFILE") end end - -- If this is a newly created file, stamp it with the creating user's ownership if isNew then local euid = (task and (task.euid or task.uid)) or kernel.uid local egid = (task and task.gid) or 0 @@ -629,7 +641,6 @@ function vfs.mkdir(path) checkperms(parentMeta, "w") local disk, diskPath = resolvePath(path) disk:makeDirectory(diskPath) - -- Stamp the new directory with the creating user's ownership local task = kernel.currentTask local euid = (task and (task.euid or task.uid)) or kernel.uid local egid = (task and task.gid) or 0 diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod index d2fab03..6947cac 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod @@ -50,10 +50,8 @@ local function readonly(tbl) __metatable = false }) end ---local origLoad = load +local origLoad = load kernel._U = readonly(kernel._G) -kernel.allowGlobalOverwrites = true kernel._U._G = kernel._U ---kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end -kernel.allowGlobalOverwrites = false +kernel._U.load = function(a, b, c, d) return origLoad(a, b, c, d or kernel._U) end \ No newline at end of file diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod index 230a54f..15ee521 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod @@ -1,5 +1,2 @@ -local args = {...} -local kernel = args[1] - -local origLoad = load ---kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end \ No newline at end of file +local kernel = ... +kernel.allowGlobalOverwrites = false \ No newline at end of file From 6bb7f03a3e2a79e6c804853401589a5c6c651af6 Mon Sep 17 00:00:00 2001 From: spsf Date: Mon, 23 Feb 2026 23:50:37 -0600 Subject: [PATCH 4/7] file permissions fixes --- .../lib/modules/Hyperion/10_vfs.kmod | 57 ++-- .../lib/modules/Hyperion/92_permissions.kmod | 301 ++++++++++-------- 2 files changed, 193 insertions(+), 165 deletions(-) diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod index d15989a..6024265 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod @@ -111,43 +111,52 @@ local function makeMetafile(meta) return out end -local function validateComponent(part) - if part == "" then - error("EINVAL: empty path component", 2) - end - if part:sub(1, 2) == ".." then - error("EINVAL: illegal path component '" .. part .. "'", 2) - end - if part:find("\0", 1, true) then - error("EINVAL: null byte in path component", 2) - end - if part:find("/", 1, true) then - error("EINVAL: slash in path component", 2) - end - return true -end +local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@ %(%)%[%] ]+$" local function normalizePath(path) local task = kernel.currentTask local cwd = task.cwd or "/" - if path:sub(1,1) ~= "/" then path = cwd .. "/" .. path end - local parts = {} - for part in path:gmatch("[^/]+") do - if part == ".." then - if #parts > 0 then table.remove(parts) end - elseif part == "." or part == "" then + + if path:sub(1, 1) ~= "/" then + path = cwd .. "/" .. path + end + + local stack = {} + local i = 1 + local len = #path + while i <= len do + local j = path:find("/", i, true) + local comp + if j then + comp = path:sub(i, j - 1) + i = j + 1 else - validateComponent(part) - table.insert(parts, part) + comp = path:sub(i) + i = len + 1 + end + + if comp == "" or comp == "." then + elseif comp == ".." then + if #stack > 0 then + table.remove(stack) + end + else + if not comp:match(SAFE_COMPONENT_PATTERN) then + error("EINVAL: illegal characters in path component: " .. comp, 2) + end + table.insert(stack, comp) end end - local result = "/" .. table.concat(parts, "/") + + local result = "/" .. table.concat(stack, "/") + local root = task and task.root if root and root ~= "/" then if result ~= root and result:sub(1, #root + 1) ~= root .. "/" then result = root end end + return result end diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod index da3e8d4..85274c8 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod @@ -29,110 +29,47 @@ local function makeEntry(name, etype, owner, group, perms, cmeta) .. string.char(#cmeta) .. cmeta end -local function writeMeta(dir, entries) - local diskDir = dir - if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end - local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta") - - local data = string.char(META_VERSION) - for _, e in ipairs(entries) do - data = data .. makeEntry(e[1], e[2] or 0x00, e[3], e[4], e[5], e[6]) - end - - local ok, err = pcall(function() - local f = rootDisk:open(metaPath, "w") - f.write(data) - f.close() - end) - if not ok then - kernel.log("permissions: failed to write " .. metaPath .. ": " .. tostring(err), "WARN", 8) - end -end - local REG = 0x00 --- All known /bin entries with their permissions -local BIN_ENTRIES = { - {"cat", REG, 0, 0, RWX_RX_RX}, - {"chattr", REG, 0, 0, RWX_RX_RX}, - {"chgrp", REG, 0, 0, RWX_RX_RX}, - {"chmod", REG, 0, 0, RWX_RX_RX}, - {"chown", REG, 0, 0, RWX_RX_RX}, - {"chroot", REG, 0, 0, RWX_RX_RX}, - {"clear", REG, 0, 0, RWX_RX_RX}, - {"echo", REG, 0, 0, RWX_RX_RX}, - {"hfetch", REG, 0, 0, RWX_RX_RX}, - {"help", REG, 0, 0, RWX_RX_RX}, - {"hysh", REG, 0, 0, RWX_RX_RX}, - {"hyshex", REG, 0, 0, RWX_RX_RX}, - {"id", REG, 0, 0, RWX_RX_RX}, - {"install", REG, 0, 0, RWX_RX_RX}, - {"ln", REG, 0, 0, RWX_RX_RX}, - {"login", REG, 0, 0, SUID_755 }, - {"loimgcreate", REG, 0, 0, RWX_RX_RX}, - {"looptest", REG, 0, 0, RWX_RX_RX}, - {"losetup", REG, 0, 0, RWX_RX_RX}, - {"ls", REG, 0, 0, RWX_RX_RX}, - {"lsusers", REG, 0, 0, RWX_RX_RX}, - {"lua", REG, 0, 0, RWX_RX_RX}, - {"luaold", REG, 0, 0, RWX_RX_RX}, - {"micro", REG, 0, 0, RWX_RX_RX}, - {"mkdir", REG, 0, 0, RWX_RX_RX}, - {"mount", REG, 0, 0, RWX_RX_RX}, - {"passwd", REG, 0, 0, RWX_RX_RX}, - {"ps", REG, 0, 0, RWX_RX_RX}, - {"pwd", REG, 0, 0, RWX_RX_RX}, - {"readlink", REG, 0, 0, RWX_RX_RX}, - {"sed", REG, 0, 0, RWX_RX_RX}, - {"socktest", REG, 0, 0, RWX_RX_RX}, - {"spm", REG, 0, 0, RWX_RX_RX}, - {"su", REG, 0, 0, SUID_755 }, - {"sudo", REG, 0, 0, SUID_755 }, - {"sysdump", REG, 0, 0, RWX_RX_RX}, - {"umount", REG, 0, 0, RWX_RX_RX}, - {"useradd", REG, 0, 0, RWX_RX_RX}, - {"userdel", REG, 0, 0, RWX_RX_RX}, - {"usermod", REG, 0, 0, RWX_RX_RX}, - {"whoami", REG, 0, 0, RWX_RX_RX}, - {"yes", REG, 0, 0, RWX_RX_RX}, - {"startup", REG, 0, 0, RWX_RX_RX}, -} - --- Merge entries: always ensure all known entries exist with correct permissions. --- This handles both fresh installs and upgrades (adds missing entries, upgrades --- the on-disk format to v2 by rewriting). local function mergeMeta(dir, entries) local diskDir = dir if diskDir:sub(1,1) == "/" then diskDir = diskDir:sub(2) end local metaPath = (diskDir == "" and ".meta" or diskDir .. "/.meta") - -- Read existing meta (may be v1 or v2) local existing = {} local rok, rf = pcall(function() return rootDisk:open(metaPath, "r") end) if rok and rf then local raw = rf.read(65535) if rf.close then rf.close() end - -- Parse using the VFS parser (handles v0/v1/v2) - existing = kernel.vfs and kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw) or {} + existing = (kernel.vfs._parseMetafile and kernel.vfs._parseMetafile(raw)) or {} end - -- Add any missing entries (don't overwrite existing customised perms) for _, e in ipairs(entries) do - if not existing[e[1]] then - existing[e[1]] = { - etype = e[2] or 0x00, - owner = e[3] or 0, - group = e[4] or 0, - perms = e[5] or RWX_RX_RX, - cmeta = e[6] or "", - } - end + local name = e[1] + local etype = e[2] or REG + local owner = e[3] or 0 + local group = e[4] or 0 + local perms = e[5] or RWX_RX_RX + local cmeta = e[6] or "" + existing[name] = { + etype = etype, + owner = owner, + group = group, + perms = perms, + cmeta = cmeta, + } end - -- Write back as v2 local data = string.char(META_VERSION) for name, m in pairs(existing) do - data = data .. makeEntry(name, m.etype or 0x00, m.owner or 0, m.group or 0, m.perms or RWX_RX_RX, m.cmeta or "") + data = data .. makeEntry( + name, + m.etype or REG, + m.owner or 0, + m.group or 0, + m.perms or RWX_RX_RX, + m.cmeta or "" + ) end local ok, err = pcall(function() @@ -145,70 +82,152 @@ local function mergeMeta(dir, entries) end end -local freshInstall = not rootDisk:fileExists(".meta") +kernel.log("Seeding filesystem permissions...", "INFO") -if freshInstall then - kernel.log("Seeding filesystem permissions...", "INFO") +mergeMeta("/", { + {"bin", REG, 0, 0, RWX_RX_RX}, + {"boot", REG, 0, 0, RWX_RX_RX}, + {"dev", REG, 0, 0, RWX_RX_RX}, + {"etc", REG, 0, 0, RWX_RX_RX}, + {"home", REG, 0, 0, RWX_RX_RX}, + {"lib", REG, 0, 0, RWX_RX_RX}, + {"root", REG, 0, 0, RW____ }, + {"sbin", REG, 0, 0, RWX_RX_RX}, + {"tmp", REG, 0, 0, RWXRWXRWX}, + {"usr", REG, 0, 0, RWX_RX_RX}, + {"var", REG, 0, 0, RWX_RX_RX}, +}) - -- / (only on fresh install — these dirs are stable) - writeMeta("/", { - {"bin", REG, 0, 0, RWX_RX_RX}, - {"boot", REG, 0, 0, RWX_RX_RX}, - {"dev", REG, 0, 0, RWX_RX_RX}, - {"etc", REG, 0, 0, RWX_RX_RX}, - {"home", REG, 0, 0, RWX_RX_RX}, - {"lib", REG, 0, 0, RWX_RX_RX}, - {"root", REG, 0, 0, RW____ }, - {"sbin", REG, 0, 0, RWX_RX_RX}, - {"tmp", REG, 0, 0, RWXRWXRWX}, - {"usr", REG, 0, 0, RWX_RX_RX}, - {"var", REG, 0, 0, RWX_RX_RX}, - }) +mergeMeta("/boot", { + {"kernel.lua", REG, 0, 0, RW_R_R }, + {"boot.cfg", REG, 0, 0, RW_R_R }, + {"safeboot.cfg", REG, 0, 0, RW_R_R }, + {"fstab", REG, 0, 0, RW_R_R }, + {"initfs", REG, 0, 0, RW_R_R }, + {"cct", REG, 0, 0, RWX_RX_RX}, + {"oc", REG, 0, 0, RWX_RX_RX}, +}) - writeMeta("/bin/startup", { - {"test.lua", REG, 0, 0, RWX_RX_RX}, - }) +mergeMeta("/boot/cct", { + {"boot.lua", REG, 0, 0, RW_R_R}, + {"initdisks", REG, 0, 0, RW_R_R}, + {"eeprom", REG, 0, 0, RW_R_R}, +}) - writeMeta("/etc", { - {"passwd", REG, 0, 0, RW_R_R}, - {"shadow", REG, 0, 0, RW____}, - {"pam.d", REG, 0, 0, RWX_RX_RX}, - }) +mergeMeta("/boot/oc", { + {"boot.lua", REG, 0, 0, RW_R_R}, + {"initfs.lua",REG, 0, 0, RW_R_R}, + {"eeprom", REG, 0, 0, RW_R_R}, +}) - writeMeta("/etc/pam.d", { - {"secret", REG, 0, 0, RW____}, - }) +mergeMeta("/sbin", { + {"init.lua", REG, 0, 0, RWX_RX_RX}, +}) - writeMeta("/sbin", { - {"init.lua", REG, 0, 0, RWX_RX_RX}, - }) +mergeMeta("/bin", { + {"cat", REG, 0, 0, RWX_RX_RX}, + {"chattr", REG, 0, 0, RWX_RX_RX}, + {"chgrp", REG, 0, 0, RWX_RX_RX}, + {"chmod", REG, 0, 0, RWX_RX_RX}, + {"chown", REG, 0, 0, RWX_RX_RX}, + {"chroot", REG, 0, 0, RWX_RX_RX}, + {"clear", REG, 0, 0, RWX_RX_RX}, + {"echo", REG, 0, 0, RWX_RX_RX}, + {"hfetch", REG, 0, 0, RWX_RX_RX}, + {"help", REG, 0, 0, RWX_RX_RX}, + {"hysh", REG, 0, 0, RWX_RX_RX}, + {"hyshex", REG, 0, 0, RWX_RX_RX}, + {"id", REG, 0, 0, RWX_RX_RX}, + {"install", REG, 0, 0, RWX_RX_RX}, + {"ln", REG, 0, 0, RWX_RX_RX}, + {"login", REG, 0, 0, SUID_755 }, + {"loimgcreate", REG, 0, 0, RWX_RX_RX}, + {"looptest", REG, 0, 0, RWX_RX_RX}, + {"losetup", REG, 0, 0, RWX_RX_RX}, + {"ls", REG, 0, 0, RWX_RX_RX}, + {"lsusers", REG, 0, 0, RWX_RX_RX}, + {"lua", REG, 0, 0, RWX_RX_RX}, + {"luaold", REG, 0, 0, RWX_RX_RX}, + {"micro", REG, 0, 0, RWX_RX_RX}, + {"mkdir", REG, 0, 0, RWX_RX_RX}, + {"mount", REG, 0, 0, RWX_RX_RX}, + {"passwd", REG, 0, 0, RWX_RX_RX}, + {"ps", REG, 0, 0, RWX_RX_RX}, + {"pwd", REG, 0, 0, RWX_RX_RX}, + {"readlink", REG, 0, 0, RWX_RX_RX}, + {"sed", REG, 0, 0, RWX_RX_RX}, + {"socktest", REG, 0, 0, RWX_RX_RX}, + {"spm", REG, 0, 0, RWX_RX_RX}, + {"startup", REG, 0, 0, RWX_RX_RX}, + {"su", REG, 0, 0, SUID_755 }, + {"sudo", REG, 0, 0, SUID_755 }, + {"sysdump", REG, 0, 0, RWX_RX_RX}, + {"umount", REG, 0, 0, RWX_RX_RX}, + {"useradd", REG, 0, 0, RWX_RX_RX}, + {"userdel", REG, 0, 0, RWX_RX_RX}, + {"usermod", REG, 0, 0, RWX_RX_RX}, + {"whoami", REG, 0, 0, RWX_RX_RX}, + {"yes", REG, 0, 0, RWX_RX_RX}, +}) - writeMeta("/boot", { - {"kernel.lua", REG, 0, 0, RW_R_R }, - {"boot.cfg", REG, 0, 0, RW_R_R }, - {"safeboot.cfg", REG, 0, 0, RW_R_R }, - {"fstab", REG, 0, 0, RW_R_R }, - {"initfs", REG, 0, 0, RW_R_R }, - {"cct", REG, 0, 0, RWX_RX_RX}, - {"oc", REG, 0, 0, RWX_RX_RX}, - }) +mergeMeta("/bin/startup", { + {"test.lua", REG, 0, 0, RWX_RX_RX}, +}) - writeMeta("/lib", { - {"sys", REG, 0, 0, RWX_RX_RX}, - {"modules", REG, 0, 0, RWX_RX_RX}, - {"crypto", REG, 0, 0, RWX_RX_RX}, - {"store", REG, 0, 0, RWX_RX_RX}, - {"snip", REG, 0, 0, RW_R_R }, - {"io", REG, 0, 0, RW_R_R }, - {"bit32", REG, 0, 0, RW_R_R }, - }) +mergeMeta("/lib", { + {"sys", REG, 0, 0, RWX_RX_RX}, + {"modules", REG, 0, 0, RWX_RX_RX}, + {"crypto", REG, 0, 0, RWX_RX_RX}, + {"store", REG, 0, 0, RWX_RX_RX}, + {"snip", REG, 0, 0, RW_R_R }, + {"io", REG, 0, 0, RW_R_R }, + {"bit32", REG, 0, 0, RW_R_R }, +}) - kernel.log("Filesystem permissions seeded.", "INFO") -else - kernel.log("Permissions already seeded, merging /bin updates...", "INFO") -end +mergeMeta("/lib/sys", { + {"fs", REG, 0, 0, RW_R_R}, + {"hpv", REG, 0, 0, RW_R_R}, + {"ipc", REG, 0, 0, RW_R_R}, + {"term", REG, 0, 0, RW_R_R}, + {"init", REG, 0, 0, RW_R_R}, +}) --- Always merge /bin — adds missing entries and upgrades format to v2 -mergeMeta("/bin", BIN_ENTRIES) +mergeMeta("/lib/modules", { + {"Hyperion", REG, 0, 0, RWX_RX_RX}, +}) -kernel.log("Permission module loaded.", "INFO") +mergeMeta("/lib/modules/Hyperion", { + {"01_stdlib.kmod", REG, 0, 0, RW_R_R}, + {"10_vfs.kmod", REG, 0, 0, RW_R_R}, + {"11_require.kmod", REG, 0, 0, RW_R_R}, + {"12_devfs.kmod", REG, 0, 0, RW_R_R}, + {"12_tmpfs.kmod", REG, 0, 0, RW_R_R}, + {"13_loopdev.kmod", REG, 0, 0, RW_R_R}, + {"14_keventd.kmod", REG, 0, 0, RW_R_R}, + {"19_fstab.kmod", REG, 0, 0, RW_R_R}, + {"20_signals.kmod", REG, 0, 0, RW_R_R}, + {"20_socket.kmod", REG, 0, 0, RW_R_R}, + {"26_tty.kmod", REG, 0, 0, RW_R_R}, + {"30_userspace.kmod", REG, 0, 0, RW_R_R}, + {"40_auth.kmod", REG, 0, 0, RW_R_R}, + {"45_hypervisor.kmod", REG, 0, 0, RW_R_R}, + {"47_dbg.kmod", REG, 0, 0, RW_R_R}, + {"50_gpio.kmod", REG, 0, 0, RW_R_R}, + {"70_stdlibadv.kmod", REG, 0, 0, RW_R_R}, + {"90_init.kmod", REG, 0, 0, RW_R_R}, + {"91_login.kmod", REG, 0, 0, RW_R_R}, + {"92_permissions.kmod", REG, 0, 0, RW_R_R}, + {"99_final.kmod", REG, 0, 0, RW_R_R}, +}) + +mergeMeta("/etc", { + {"passwd", REG, 0, 0, RW_R_R }, + {"shadow", REG, 0, 0, RW____ }, + {"pam.d", REG, 0, 0, RWX_RX_RX}, +}) + +mergeMeta("/etc/pam.d", { + {"secret", REG, 0, 0, RW____}, +}) + +kernel.log("Filesystem permissions seeded.", "INFO") From e77a8b36367bf0a95581f7512e470e787771406b Mon Sep 17 00:00:00 2001 From: spsf Date: Tue, 24 Feb 2026 00:01:39 -0600 Subject: [PATCH 5/7] AsyncSyscall3 exploit fix --- .../lib/modules/Hyperion/30_userspace.kmod | 6 +++--- .../lib/modules/Hyperion/45_hypervisor.kmod | 16 +++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod index 6947cac..2621138 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod @@ -21,7 +21,7 @@ local function readonly(tbl) error("Attempt to modify global variable '" .. k .. "'", 2) end, - __pairs = function() + __pairs = function(self) local function iter(_, key) local nextKey, value = next(tbl, key) if type(value) == "table" then @@ -29,7 +29,7 @@ local function readonly(tbl) end return nextKey, value end - return iter, tbl, nil + return iter, self, nil end, __ipairs = function() @@ -54,4 +54,4 @@ local origLoad = load kernel._U = readonly(kernel._G) kernel._U._G = kernel._U -kernel._U.load = function(a, b, c, d) return origLoad(a, b, c, d or kernel._U) end \ No newline at end of file +kernel._U.load = function(a,b,c,d) return origLoad(a,b,c,d or kernel._U) end diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod b/Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod index f59d3a1..c23be56 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod +++ b/Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod @@ -5,6 +5,8 @@ local sys = {} local nextpid = 2 kernel.exitMain = false +local resumeWithTimeout = coroutine.resumeWithTimeout + local function bit_is_set(num, bit) return math.floor(num / (2 ^ bit)) % 2 == 1 end @@ -206,10 +208,14 @@ function sys.kill(pid) return false, "Task does not exist" elseif task.status == "Z" then return false, "Task is already dead" - else - task.status = "Z" - return true end + local caller = kernel.currentTask + local ceuid = caller and (caller.euid or caller.uid) or kernel.uid + if ceuid ~= 0 and task.uid ~= (caller and caller.uid or kernel.uid) then + return false, "EPERM" + end + task.status = "Z" + return true end function sys.stop(pid) @@ -352,7 +358,7 @@ function kernel.main() if task.sigq and #task.sigq ~= 0 and task.sigh then local coro = coroutine.create(task.sigh) if kernel.config.preempt then - coroutine.resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1)) + resumeWithTimeout(coro, task.timeSlice, table.remove(task.sigq, 1)) else coroutine.resume(coro, table.remove(task.sigq, 1)) end @@ -363,7 +369,7 @@ function kernel.main() local ret if kernel.config.preempt then - ret = { coroutine.resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } + ret = { resumeWithTimeout(task.coro, task.timeSlice, table.unpack(task.syscallReturn)) } else ret = { coroutine.resume(task.coro, table.unpack(task.syscallReturn)) } end From 62a03bfe6bfa8514b1ac9d02d3ae8185baf07cd7 Mon Sep 17 00:00:00 2001 From: spsf Date: Tue, 24 Feb 2026 00:34:59 -0600 Subject: [PATCH 6/7] Potential windows case insensitive filesystem issue fix --- Src/Hyperion-bash/bin/hfetch | 6 +----- Src/Hyperion-bash/bin/hysh | 8 ++++---- Src/Hyperion-bash/bin/hyshex | 8 ++++---- Src/Hyperion-bash/bin/login | 4 ++-- .../lib/modules/{Hyperion => hyperion}/01_stdlib.kmod | 0 .../lib/modules/{Hyperion => hyperion}/10_vfs.kmod | 1 + .../lib/modules/{Hyperion => hyperion}/11_require.kmod | 0 .../lib/modules/{Hyperion => hyperion}/12_devfs.kmod | 0 .../lib/modules/{Hyperion => hyperion}/12_tmpfs.kmod | 0 .../lib/modules/{Hyperion => hyperion}/13_loopdev.kmod | 0 .../lib/modules/{Hyperion => hyperion}/14_keventd.kmod | 0 .../lib/modules/{Hyperion => hyperion}/19_fstab.kmod | 0 .../lib/modules/{Hyperion => hyperion}/20_signals.kmod | 0 .../lib/modules/{Hyperion => hyperion}/20_socket.kmod | 0 .../lib/modules/{Hyperion => hyperion}/26_tty.kmod | 4 ++-- .../lib/modules/{Hyperion => hyperion}/30_userspace.kmod | 0 .../lib/modules/{Hyperion => hyperion}/40_auth.kmod | 0 .../lib/modules/{Hyperion => hyperion}/45_hypervisor.kmod | 0 .../lib/modules/{Hyperion => hyperion}/47_dbg.kmod | 0 .../lib/modules/{Hyperion => hyperion}/50_gpio.kmod | 0 .../lib/modules/{Hyperion => hyperion}/70_stdlibadv.kmod | 0 .../lib/modules/{Hyperion => hyperion}/90_init.kmod | 0 .../lib/modules/{Hyperion => hyperion}/91_login.kmod | 0 .../modules/{Hyperion => hyperion}/92_permissions.kmod | 4 ++-- .../lib/modules/{Hyperion => hyperion}/99_final.kmod | 0 build.py | 4 ++-- 26 files changed, 18 insertions(+), 21 deletions(-) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/01_stdlib.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/10_vfs.kmod (99%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/11_require.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/12_devfs.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/12_tmpfs.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/13_loopdev.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/14_keventd.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/19_fstab.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/20_signals.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/20_socket.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/26_tty.kmod (99%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/30_userspace.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/40_auth.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/45_hypervisor.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/47_dbg.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/50_gpio.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/70_stdlibadv.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/90_init.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/91_login.kmod (100%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/92_permissions.kmod (98%) rename Src/Hyperion-kernel/lib/modules/{Hyperion => hyperion}/99_final.kmod (100%) diff --git a/Src/Hyperion-bash/bin/hfetch b/Src/Hyperion-bash/bin/hfetch index 392282f..a640241 100644 --- a/Src/Hyperion-bash/bin/hfetch +++ b/Src/Hyperion-bash/bin/hfetch @@ -33,7 +33,6 @@ local host_str = syscall.getHost() or "Unknown" local cc_ver = host_str:match("ComputerCraft ([%d%.]+)") or host_str local info = { - -- {label, value} label=nil means print value as-is (userhost / separator) {nil, userhost}, {nil, string.rep("-", #userhost)}, {"OS", syscall.version() or "Unknown"}, @@ -42,7 +41,7 @@ local info = { {"Uptime", formatUptime(syscall.getUptime() or 0)}, {"Tasks", tostring(#(syscall.getTasks() or {}))}, {"Shell", syscall.getEnviron("SHELL") or "Unknown"}, - {"Terminal", "TTY1"}, + {"Terminal", "tty1"}, {"UID", tostring(syscall.getuid())}, {"Packages", "n/a (spm)"}, } @@ -71,15 +70,12 @@ local lines = math.max(#logo, #info) for i = 1, lines do local logo_str = logo[i] or string.rep(" ", 36) - -- print logo segment in cyan c(C_LOGO) printInline(logo_str) - -- print separator pipe c(C_LABEL) printInline("| ") - -- print info segment local row = info[i] if row then if row[1] == nil and i == 1 then diff --git a/Src/Hyperion-bash/bin/hysh b/Src/Hyperion-bash/bin/hysh index a1a8912..a1bc15a 100644 --- a/Src/Hyperion-bash/bin/hysh +++ b/Src/Hyperion-bash/bin/hysh @@ -1,6 +1,6 @@ --:Minify:-- -syscall.open("/dev/tty/TTY1","r") --stdin (Device 0) -syscall.open("/dev/tty/TTY1","w") --stdout (Device 1) +syscall.open("/dev/tty/tty1","r") --stdin (Device 0) +syscall.open("/dev/tty/tty1","w") --stdout (Device 1) syscall.open("/dev/null","w") --stderr (device 2) local success, errorMsg = xpcall(function() @@ -957,8 +957,8 @@ local function runCommand(command) end local proc = syscall.spawn(function(...) - syscall.open("/dev/tty/TTY1","r") - syscall.open("/dev/tty/TTY1","w") + syscall.open("/dev/tty/tty1","r") + syscall.open("/dev/tty/tty1","w") syscall.open("/dev/null","w") local ok2, msg = pcall(program, ...) if not ok2 then printError(progName, msg) end diff --git a/Src/Hyperion-bash/bin/hyshex b/Src/Hyperion-bash/bin/hyshex index 0ff03ce..b69309a 100644 --- a/Src/Hyperion-bash/bin/hyshex +++ b/Src/Hyperion-bash/bin/hyshex @@ -1,6 +1,6 @@ --:Minify:-- -syscall.open("/dev/tty/TTY1","r") -syscall.open("/dev/tty/TTY1","w") +syscall.open("/dev/tty/tty1","r") +syscall.open("/dev/tty/tty1","w") syscall.open("/dev/null","r") syscall.devctl(1,"clear") syscall.devctl(1,"sfgc",1) @@ -57,8 +57,8 @@ while true do printInline("> ") end proc = syscall.spawn(function(...) - syscall.open("/dev/tty/TTY1","r") - syscall.open("/dev/tty/TTY1","w") + syscall.open("/dev/tty/tty1","r") + syscall.open("/dev/tty/tty1","w") syscall.open("/dev/null","w") program(...) end, path, nil, {table.unpack(split, 2)}) diff --git a/Src/Hyperion-bash/bin/login b/Src/Hyperion-bash/bin/login index ce0756f..a81c82e 100644 --- a/Src/Hyperion-bash/bin/login +++ b/Src/Hyperion-bash/bin/login @@ -1,6 +1,6 @@ --:Minify:-- -syscall.open("/dev/tty/TTY1", "r") --stdin (fd 0) -syscall.open("/dev/tty/TTY1", "w") --stdout (fd 1) +syscall.open("/dev/tty/tty1", "r") --stdin (fd 0) +syscall.open("/dev/tty/tty1", "w") --stdout (fd 1) syscall.open("/dev/null", "w") --stderr (fd 2) diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/01_stdlib.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/01_stdlib.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/01_stdlib.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod similarity index 99% rename from Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod index 6024265..60d90ba 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod @@ -141,6 +141,7 @@ local function normalizePath(path) table.remove(stack) end else + comp = comp:lower() if not comp:match(SAFE_COMPONENT_PATTERN) then error("EINVAL: illegal characters in path component: " .. comp, 2) end diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/11_require.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/11_require.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/11_require.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/11_require.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/12_devfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/12_devfs.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/12_devfs.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/12_tmpfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/12_tmpfs.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/12_tmpfs.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/12_tmpfs.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/13_loopdev.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/13_loopdev.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/13_loopdev.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/13_loopdev.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/14_keventd.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/14_keventd.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/14_keventd.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/14_keventd.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/19_fstab.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/19_fstab.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/19_fstab.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/19_fstab.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/20_signals.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/20_signals.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/20_signals.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/20_signals.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/20_socket.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/20_socket.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/20_socket.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/20_socket.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/26_tty.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/26_tty.kmod similarity index 99% rename from Src/Hyperion-kernel/lib/modules/Hyperion/26_tty.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/26_tty.kmod index ba120a2..0edd9ec 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/26_tty.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/26_tty.kmod @@ -385,11 +385,11 @@ kernel.processes.cctmond = function() end end -newtty(apis.term, "TTY1", fifo.pop) +newtty(apis.term, "tty1", fifo.pop) for i,v in ipairs({peripheral.find("monitor")}) do v.setTextScale(.5) v.write("Initializing...") - newtty(v,"TTY"..tostring(i+1),function () end) + newtty(v,"tty"..tostring(i+1),function () end) end \ No newline at end of file diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/30_userspace.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/30_userspace.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/30_userspace.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/40_auth.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/40_auth.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/40_auth.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/45_hypervisor.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/45_hypervisor.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/45_hypervisor.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/47_dbg.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/47_dbg.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/47_dbg.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/47_dbg.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/50_gpio.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/50_gpio.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/50_gpio.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/50_gpio.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/70_stdlibadv.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/70_stdlibadv.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/70_stdlibadv.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/70_stdlibadv.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/90_init.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/90_init.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/90_init.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/90_init.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/91_login.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/91_login.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/91_login.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/91_login.kmod diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod similarity index 98% rename from Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod index 85274c8..cb84c38 100644 --- a/Src/Hyperion-kernel/lib/modules/Hyperion/92_permissions.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/92_permissions.kmod @@ -193,10 +193,10 @@ mergeMeta("/lib/sys", { }) mergeMeta("/lib/modules", { - {"Hyperion", REG, 0, 0, RWX_RX_RX}, + {"hyperion", REG, 0, 0, RWX_RX_RX}, }) -mergeMeta("/lib/modules/Hyperion", { +mergeMeta("/lib/modules/hyperion", { {"01_stdlib.kmod", REG, 0, 0, RW_R_R}, {"10_vfs.kmod", REG, 0, 0, RW_R_R}, {"11_require.kmod", REG, 0, 0, RW_R_R}, diff --git a/Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/99_final.kmod similarity index 100% rename from Src/Hyperion-kernel/lib/modules/Hyperion/99_final.kmod rename to Src/Hyperion-kernel/lib/modules/hyperion/99_final.kmod diff --git a/build.py b/build.py index d8fceeb..f6e3b23 100644 --- a/build.py +++ b/build.py @@ -203,7 +203,7 @@ def _make_firstboot_kmod(users): lines.append("do") lines.append(" local ok, err = pcall(function()") - lines.append(" kernel.vfs.remove('/lib/modules/Hyperion/50_firstboot_users.kmod')") + lines.append(" kernel.vfs.remove('/lib/modules/hyperion/50_firstboot_users.kmod')") lines.append(" end)") lines.append(" if not ok then") lines.append(" kernel.log('FIRSTBOOT: could not self-delete: ' .. tostring(err), 'WARN')") @@ -215,7 +215,7 @@ def _make_firstboot_kmod(users): def inject_makeusers(users, arch): base = BUILD_ROOT / "$" if arch else BUILD_ROOT - kmod_path = base / "lib" / "modules" / "Hyperion" / "50_firstboot_users.kmod" + kmod_path = base / "lib" / "modules" / "hyperion" / "50_firstboot_users.kmod" kmod_path.parent.mkdir(parents=True, exist_ok=True) kmod_path.write_text(_make_firstboot_kmod(users), encoding="utf-8") print(" Wrote first-boot user setup -> " + str(kmod_path.relative_to(BUILD_ROOT))) From ab1e847d1c1394c7ca72af4c30e1e62fafd6710c Mon Sep 17 00:00:00 2001 From: spsf Date: Tue, 24 Feb 2026 00:48:20 -0600 Subject: [PATCH 7/7] Fix exploit with trailing whitespace --- Src/Hyperion-kernel/boot/kernel.lua | 13 ++++++++++--- .../lib/modules/hyperion/10_vfs.kmod | 9 +++++++-- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Src/Hyperion-kernel/boot/kernel.lua b/Src/Hyperion-kernel/boot/kernel.lua index 4136dca..326b527 100644 --- a/Src/Hyperion-kernel/boot/kernel.lua +++ b/Src/Hyperion-kernel/boot/kernel.lua @@ -184,9 +184,16 @@ end kernel.log("Gathering modules") for _, i in ipairs(ifs.list("/lib/modules")) do - for _,v in ipairs(ifs.list("/lib/modules/"..i)) do - local prior=tonumber(v:sub(1,2)) - modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v + local modlist = ifs.list("/lib/modules/"..i) + if not modlist then + kernel.log("WARNING: could not list /lib/modules/"..i.." (skipping)", "WARN", 8) + else + for _,v in ipairs(modlist) do + local prior=tonumber(v:sub(1,2)) + if prior then + modules[prior+1][#modules[prior+1]+1]="/lib/modules/"..i.."/"..v + end + end end end diff --git a/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod index 60d90ba..51f3301 100644 --- a/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod +++ b/Src/Hyperion-kernel/lib/modules/hyperion/10_vfs.kmod @@ -111,7 +111,7 @@ local function makeMetafile(meta) return out end -local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@ %(%)%[%] ]+$" +local SAFE_COMPONENT_PATTERN = "^[A-Za-z0-9_.+%-%@%(%)%[%]]+$" local function normalizePath(path) local task = kernel.currentTask @@ -135,6 +135,8 @@ local function normalizePath(path) i = len + 1 end + comp = comp:match("^%s*(.-)%s*$") + if comp == "" or comp == "." then elseif comp == ".." then if #stack > 0 then @@ -145,6 +147,9 @@ local function normalizePath(path) if not comp:match(SAFE_COMPONENT_PATTERN) then error("EINVAL: illegal characters in path component: " .. comp, 2) end + if comp == ".meta" then + error("EINVAL: reserved path component: " .. comp, 2) + end table.insert(stack, comp) end end @@ -192,7 +197,7 @@ end vfs._parseMetafile = parseMetafile local function readMetaEntry(disk, parentDiskPath, filename) - if filename == ".meta" then error("Cannot open metafile") end + if filename == ".meta" then error("EACCES: Cannot open metafile") end local mp if parentDiskPath == "/" then mp = ".meta"