Dungeon Crawl Stone Soup
Dungeon Crawl Stone Soup, aka DCSS, aka
“crawl”, is an open source Rogue-like game, available at https://github.com/crawl/crawl. Public
DCSS servers (crawl.kelbi.org, crawl.develz.org, crawl.akrasiac.org, underhound.eu, crawl.beRotato.org, lazy-life.ddo.jp,
webzook.net, crawl.xtahua.com, crawl.project357.org) allow users to
play DCSS without compiling it or installing it on their own system. On these
servers, before starting a game players can upload their own .crawlrc
configuration file. These files can include Lua 5.1 scripts, and the global
environment for these scripts includes the load
and loadstring
functions. load and loadstring
can be used to load not just other Lua scripts, but also valid or invalid
Lua bytecode. Although Lua 5.1 includes a perfunctory bytecode verifier, there
are known weaknesses in that verifier which have been well documented in the
security community.
I was able
to adapt the exploit described at https://github.com/sghctoma/gamehack-defcon23/blob/master/demo6_logitech/redis_exploits/Redis_RCE_v2.pdf,
which was itself based on https://gist.github.com/corsix/6575486,
to attack DCSS and successfully launch a shell. My exploits are available at https://pastebin.com/36Z5iMa8 and https://pastebin.com/5Gkfvab4. They
achieve arbitrary memory reads and writes via the known bytecode weaknesses,
and then locate the system(3) function in libc. system
is then invoked repeatedly to execute the list of commands; in my exploits, these
are /usr/bin/reset
and /bin/sh.
The exploits can be used against any of the public DCSS servers to achieve
remote code execution.
My exploits assume the system is running 64-bit Linux. The above links describe how the
attack can be applied to 32-bit systems and Windows systems as well, so any
public DCSS system regardless of OS is vulnerable. But if you are on 64-bit
Linux, the only additional modification you need to make to my exploits is to this
line:
local
pht_offset_from_auxwrap = 0x8ba060
To defeat
ASLR, the exploits compute the beginning address of the text segment instead of
hardcoding it. They first find the memory address of the luaB_auxwrap function
and then subtract pht_offset_from_auxwrap. A real attacker would script a
brute-force attack to determine the correct value for pht_offset_from_auxwrap on a given server. There is a limited
range for the correct value, so a few hundred attempts should be enough. To
determine the correct value for a system without a brute-force attack, use /proc/<pid>/maps
to determine the start of the text segment and then subtract this value from &luaB_auxwrap.
First, luaB_auxwrap:
(gdb) print &luaB_auxwrap
$1 = (<text variable, no debug info> *)
0x555555e0e060 <luaB_auxwrap>
Next, the
start of the DCSS text segment:
$ ps -ef | grep crawl
dmenden+ 243 8 0 15:06 pts/0 00:00:00 gdb
./crawl
dmenden+ 249 243 1 15:08 pts/0
00:00:00 /home/dmendenhall/crawl/crawl-ref/source/crawl
$ cat /proc/249/maps
555555554000-55555605d000 r-xp 00000000 08:10 315161
/home/dmendenhall/crawl/crawl-ref/source/crawl
55555605d000-555556094000 r--p 00b08000 08:10
315161 /home/dmendenhall/crawl/crawl-ref/source/crawl
555556094000-5555560cb000 rw-p 00b3f000 08:10
315161 /home/dmendenhall/crawl/crawl-ref/source/crawl 5
[...]
The very
first number in this output (0x555555554000) is the start of the text segment.
Finally:
0x555555e0e060 - 0x555555554000 = 0x8ba060
Patch
To address
this vulnerability, I recommended either disabling load/loadstring entirely in the .crawlrc Lua environment, or disabling
bytecode loading from load/loadstring.
All bytecode chunks will begin with the byte 0x1b
(\27 decimal). No Lua script
chunks will begin with this byte, so testing this first byte should be enough
to block any bytecode.
I also
recommended disabling the printing of function addresses
{
print(function() end) },
as this
provides another vector for defeating ASLR (not used in my exploits).
Note that an
upgrade to Lua 5.2 or Lua 5.3 will not resolve the vulnerability. loadstring was renamed to load, but the ability to load bytecode
at runtime is otherwise still available. The bytecode verifier was also completely
removed starting in Lua 5.2, making other exploits possible. These other
exploits are also well-documented in the security community.
The DCSS developers ultimately fixed this vulnerability in commits fc522ff and 768f60d.
UPDATE 4/12/20: MITRE has issued CVE-2020-11722 for this bug
UPDATE 4/12/20: MITRE has issued CVE-2020-11722 for this bug
Comments
Post a Comment