Skip to content

Fix modules\auxiliary\scanner\redis\redis_server #20408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 25, 2025
Merged

Conversation

xl4635
Copy link

@xl4635 xl4635 commented Jul 23, 2025

This change updates the redis_server module to correctly parse Redis commands that contain multiple words (e.g., CONFIG SET dir /tmp). Previously, the module would incorrectly split command arguments, potentially leading to execution failures or unexpected behavior.

Verification

  • Start msfconsole
  • use auxiliary/scanner/redis/redis_server
  • set RHOSTS <IP> (target a Redis server with unauthorized access enabled, i.e., no password)
  • set redis.command "MODULE LIST" or any multi-word Redis command (e.g., "CONFIG SET dir /tmp")
  • Verify the command is parsed and executed correctly
  • Verify no syntax errors or parsing issues occur

Before and after the modification, the module can correctly execute single-argument commands, such as INFO.
INFO_before_modify
INFO_after_modify
However, before the fix, the module was unable to properly handle multi-argument commands like MODULE LIST or MODULE LOAD ./exp.so.
MODULE_LOAD_exp_before_modify
After the fix, these multi-argument commands can now be executed successfully.
MODULE_LOAD_exp_after_modify

This issue was fundamentally caused by incorrect string splitting logic in the original implementation. The module treated multi-word strings as a single command word, which led to command parsing failures and prevented the correct command from being recognized.

Copy link
Contributor

@msutovsky-r7 msutovsky-r7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For some reason, this module was using different approach to send redis commands than other modules:

./exploits/linux/redis/redis_debian_sandbox_escape.rb:144:    info_output = redis_command('INFO')
./exploits/linux/redis/redis_debian_sandbox_escape.rb:168:    info_output = redis_command('INFO')
./exploits/linux/redis/redis_replication_cmd_exec.rb:79:    return CheckCode::Safe unless (config_data = redis_command('CONFIG', 'GET', '*')) && config_data =~ /dbfilename/
./exploits/linux/redis/redis_replication_cmd_exec.rb:81:    if (info_data = redis_command('INFO')) && /redis_version:(?<redis_version>\S+)/ =~ info_data
./exploits/linux/redis/redis_replication_cmd_exec.rb:136:    redis_command('SLAVEOF', srvhost, srvport.to_s)
./exploits/linux/redis/redis_replication_cmd_exec.rb:137:    redis_command('CONFIG', 'SET', 'dbfilename', module_file.to_s)
./exploits/linux/redis/redis_replication_cmd_exec.rb:144:    redis_command('MODULE', 'LOAD', "./#{module_file}")
./exploits/linux/redis/redis_replication_cmd_exec.rb:145:    redis_command('SLAVEOF', 'NO', 'ONE')
./exploits/linux/redis/redis_replication_cmd_exec.rb:154:    # redis_command('CONFIG', 'SET', 'dbfilename', 'dump.rdb')
./exploits/linux/redis/redis_replication_cmd_exec.rb:155:    # redis_command('MODULE', 'UNLOAD', "#{@module_init_name}")
./exploits/linux/redis/redis_replication_cmd_exec.rb:213:      redis_command(@module_cmd.to_s)
./exploits/linux/redis/redis_replication_cmd_exec.rb:224:    redis_command('shell.exec', cmd.to_s)
./auxiliary/gather/redis_extractor.rb:38:    response = redis_command('scan', offset)
./auxiliary/gather/redis_extractor.rb:55:    key_type = redis_command('TYPE', key)
./auxiliary/gather/redis_extractor.rb:61:      string_content = redis_command('get', key)
./auxiliary/gather/redis_extractor.rb:66:      list_content = redis_command('LRANGE', key, '0', '-1')
./auxiliary/gather/redis_extractor.rb:71:      set_content = redis_command('SMEMBERS', key)
./auxiliary/gather/redis_extractor.rb:76:      set_content = redis_command('ZRANGE', key, '0', '-1')
./auxiliary/gather/redis_extractor.rb:81:      hash_content = parse_redis_response(redis_command('HGETALL', key))
./auxiliary/gather/redis_extractor.rb:101:    if (info_data = redis_command('INFO', 'server')) && /redis_version:(?<redis_version>\S+)/ =~ info_data
./auxiliary/gather/redis_extractor.rb:154:    ks = redis_command('INFO', 'keyspace')
./auxiliary/gather/redis_extractor.rb:181:      redis_command('SELECT', space[0])
./auxiliary/scanner/redis/redis_server.rb:49:      return unless (data = redis_command(*command_parts))
grep: ./auxiliary/scanner/redis/.redis_server.rb.swp: binary file matches
./auxiliary/scanner/redis/file_upload.rb:61:    original_dir = (redis_command('CONFIG', 'GET', 'dir') || '').split(/\r\n/).last
./auxiliary/scanner/redis/file_upload.rb:62:    original_dbfilename = (redis_command('CONFIG', 'GET', 'dbfilename') || '').split(/\r\n/).last
./auxiliary/scanner/redis/file_upload.rb:64:      original_rdbcompression = (redis_command('CONFIG', 'GET', 'rdbcompression') || '').split(/\r\n/).last
./auxiliary/scanner/redis/file_upload.rb:68:    data = redis_command('CONFIG', 'SET', 'dir', dirname) || ''
./auxiliary/scanner/redis/file_upload.rb:72:    data = redis_command('CONFIG', 'SET', 'dbfilename', basename) || ''
./auxiliary/scanner/redis/file_upload.rb:81:      data = redis_command('CONFIG', 'SET', 'rdbcompression', 'no') || ''
./auxiliary/scanner/redis/file_upload.rb:91:      data = redis_command('FLUSHALL') || ''
./auxiliary/scanner/redis/file_upload.rb:102:    data = redis_command('SET', key, content) || ''
./auxiliary/scanner/redis/file_upload.rb:105:    data = redis_command('SAVE') || ''
./auxiliary/scanner/redis/file_upload.rb:116:    redis_command('CONFIG', 'SET', 'dir', original_dir)
./auxiliary/scanner/redis/file_upload.rb:117:    redis_command('CONFIG', 'SET', 'dbfilename', original_dbfilename)
./auxiliary/scanner/redis/file_upload.rb:119:      redis_command('CONFIG', 'SET', 'rdbcompression', original_rdbcompression)
./auxiliary/scanner/redis/file_upload.rb:121:    redis_command('DEL', key)
./auxiliary/scanner/redis/file_upload.rb:122:    redis_command('SAVE')
./auxiliary/scanner/redis/file_upload.rb:128:    return Exploit::CheckCode::Safe unless (config_data = redis_command('CONFIG', 'GET', '*')) && config_data =~ /dbfilename/
./auxiliary/scanner/redis/file_upload.rb:130:    if (info_data = redis_command('INFO')) && /redis_version:(?<redis_version>\S+)/ =~ info_data

All other modules are sending each word of command as separate argument. My educated guess is that this module was overlooked or someone was considering only single-word commands.

@github-project-automation github-project-automation bot moved this from Todo to In Progress in Metasploit Kanban Jul 25, 2025
@msutovsky-r7 msutovsky-r7 merged commit 1fb76b1 into rapid7:master Jul 25, 2025
19 checks passed
@github-project-automation github-project-automation bot moved this from In Progress to Done in Metasploit Kanban Jul 25, 2025
@msutovsky-r7 msutovsky-r7 added the rn-fix release notes fix label Jul 25, 2025
@msutovsky-r7
Copy link
Contributor

msutovsky-r7 commented Jul 25, 2025

Release Notes

Fixes argument passing to the redis_command function in auxiliary/scanner/redis/redis_server. The function expects each word as a separate argument (e.g., redis_command("LIST", "MODULES")). Previously, the module passed the command as a single string, resulting in unexpected responses from the Redis server. This update corrects the issue by properly splitting the command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug easy rn-fix release notes fix
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants