Elaborating the comment:
Assumptions
- The long running script cannot be modified.
- The long running script will call an executable file that can be modified (for the sake of the example, lets assume that the executable file is
/usr/local/bin/callable
).
- You've permissions to rename
/usr/local/bin/callable
and create a new file under that file path and name.
- Either the long running script is running as root, or the
/usr/local/bin/callable
must be able to perform privilege escalation with the setuid
bit set.
- You'll need
gdb
installed.
- You'll need to have
gcc
installed if the long running script isn't running as root.
Risks
- If this is a critical system and security is a moderate to major concern, do not use any of the following procedures.
- Although unlikely to happen, but attaching to a running process and injecting calls to it may cause unexpected or undefined behaviours. If this is a critical system doing some critical procedures, do not use any of the following procedures.
- Generally, all these procedures are a bad idea, but they represent one possible solution. But...
- Use at your own risk.
Procedures (for long running script running as root)
bash# mv /usr/local/bin/callable /usr/local/bin/callable.orig
bash# cat > /usr/local/bin/callable << EOF
> #!/bin/bash
>
> echo -e "attach ${PPID}\ncall setenv(\"VAR_NAME\", \"some_value\", 1)\ndetach" | /usr/bin/gdb >& /dev/null
>
> /usr/local/bin/callable.orig
>
> EOF
bash# chmod 755 /usr/local/bin/callable
Procedures (for long running script NOT running as root)
bash# mv /usr/local/bin/callable /usr/local/bin/callable.orig
bash# cat > /usr/local/bin/callable.c << EOF
> #include <stdio.h>
> #include <sys/types.h>
> #include <unistd.h>
> #include <stdlib.h>
> int main(void) {
> char inject[128]; /* You may want to increase this size, based on your environment variables that will affect the size of the string */
> uid_t save_uid = getuid();
> gid_t save_gid = getgid();
> sprintf(inject, "echo -e \"attach %u\ncall setenv(\\\"VAR_NAME\\\", \\\"some_value\\\", 1)\ndetach\" | /usr/bin/gdb >& /dev/null", getppid());
> setreuid(0, 0);
> setregid(0, 0);
> system(inject);
> setregid(save_gid, save_gid);
> setreuid(save_uid, save_uid);
> system("/usr/local/bin/callable.orig");
> return 0;
> }
> EOF
bash# gcc -o /usr/local/bin/callable /usr/local/bin/callable.c
bash# rm -f /usr/local/bin/callable.c
bash# chown root:long_running_script_exclusive_group /usr/local/bin/callable
bash# chmod 4750 /usr/local/bin/callable
Bonus
- Instead of intercepting, you can, as you stated, use a cronjob to attach to the process with
gdb
(this will, at least, avoid you to intecept the long running script with another script and, in the worst case, the need to create a setuid
binary to do it). You will, however, need to know or fetch the PID of the long running script shell process (as it is changing for each time it is called). It is also prone to failure, due to syncing problems (the script may not be running when the crontab triggers).
References