Linux effect of ptrace TRACEME call - linux

I have the following code. It simply calls ptrace(PTRACE_TRACEME) before going into an infinite loop.
I have two issues:
After executing the binary, I can't attach with gdb even if I am root.
With ptrace(PTRACE_TRACEME), I can't terminate the process with Ctrl-C (SIGINT). it simply stops.
Can someone explain what's going on? Thank you in advance.
PS: I know that most debuggers fork a child which then calls ptrace(PTRACE_TRACEME) before execve. No need to remind me of this.
#include <sys/ptrace.h>
#include <sys/reg.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(int argc, char **argv) {
printf("my pid : %d\n", getpid());
ptrace(PTRACE_TRACEME);
while(1){
printf("euid : %d\n", geteuid());
sleep(2);
}
return 0;
}

after executing this binary, I can't attach gdb even if I am root.
From man ptrace:
ERRORS
EPERM The specified process cannot be traced. This could be
because the parent has insufficient privileges (the required
capability is CAP_SYS_PTRACE); non-root processes cannot trace
processes that they cannot send signals to or those running
set-user-ID/set- group-ID programs, for obvious reasons.
Alternatively, the process may already be being traced, or be init(8) (PID 1).
with ptrace(PTRACE_TRACEME), I can't terminate the process with Ctrl-C (SIGINT). it simply stops.
From man ptrace:
DESCRIPTION
While being traced, the child will stop each time a signal is
delivered, even if the signal is being ignored. (The exception is
SIGKILL, which has its usual effect.) The parent will be notified at
its next wait(2) and may inspect and modify the child process
while it is stopped. The parent then causes the child to continue,
optionally ignoring the delivered signal (or even delivering a
different signal instead).

Related

Debug messages not being printed on the console

I am trying to enable printing the debug messages on the console.
#include <linux/kernel.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int test_hello_init(void)
{
printk(KERN_INFO"%s: In init\n", __func__);
return 0;
}
static void test_hello_exit(void)
{
printk(KERN_INFO"%s: In exit\n", __func__);
}
module_init(test_hello_init);
module_exit(test_hello_exit);
To get the Info messages on the console, i executed the following command: dmesg -n7
cat /proc/sys/kernel/printk
7 4 1 7
When I load the module using insmod, i don't get any message on the terminal, while it is available when I type dmesg. What mistake i am making here.
Messages from kernel are not printed on terminal (unless it's specified as console= in kernel cmdline). They are appended to kernel log, which exists in kernel. It's accessible to user space programs via device file /dev/kmsg. This file is read by dmesg command in order to print kernel log content on terminal.

Why can GDB mask tracee's SIGKILL when attaching to the tracee

The signal(7) man page says that SIGKILL cannot be caught, blocked, or ignored. But I just observed that after attaching to a process with GDB, I can no longer send SIGKILL to that process (similarly, other signal cannot be delivered either). But after I detach and quit GDB, SIGKILL is delivered as usual.
It seems to me that GDB has blocked that signal (on behalf of the tracee) when attaching, and unblocked it when detaching. However, the ptrace(2) man page says:
While being traced, the tracee will stop each time a signal is delivered, even if the signal is being ignored. (An exception is SIGKILL, which has its usual effect.)
So why does it behave this way? What tricks is GDB using?
Here is an trivial example for demonstration:
1. test program
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
/* Simple error handling functions */
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
struct sigaction act;
void sighandler(int signum, siginfo_t *info, void *ptr) {
printf("Received signal: %d\n", signum);
printf("signal originate from pid[%d]\n", info->si_pid);
}
int
main(int argc, char *argv[])
{
printf("Pid of the current process: %d\n", getpid());
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGQUIT, &act, NULL);
while(1) {
;
}
return 0;
}
If you try to kill this program using SIGKILL (i.e., using kill -KILL ${pid}), it will die as expected. If you try to send it SIGQUIT (i.e., using kill -QUIT ${pid}), those printf statements get executed, as expected. However, if you have attached it with GDB before sending it signal, nothing will happen:
$ ##### in shell 1 #####
$ gdb
(gdb) attach ${pid}
(gdb)
/* now that gdb has attached successfully, in another shell: */
$ #### in shell 2 ####
$ kill -QUIT ${pid} # nothing happen
$ kill -KILL ${pid} # again, nothing happen!
/* now gdb detached */
##### in shell 1 ####
(gdb) quit
/* the process will receive SIGKILL */
##### in shell 2 ####
$ Killed # the tracee receive **SIGKILL** eventually...
FYI, I am using a CentOS-6u3 and uname -r result in 2.6.32_1-16-0-0. My GDB version is: GNU gdb (GDB) Red Hat Enterprise Linux (7.2-56.el6) and my GCC version is: gcc (GCC) 3.4.6 20060404 (Red Hat 3.4.6-19.el6). An old machine...
Any idea will be appreciated ;-)
$ ##### in shell 1 #####
$ gdb
(gdb) attach ${pid}
(gdb)
The issue is that once GDB has attached to ${pid}, the inferior (being debugged) process is no longer running -- it is stopped.
The kernel will not do anything to it until it is either continued (with the (gdb) continue command), or it is no longer being traced ((gdb) detach or quit).
If you issue continue (either before or after kill -QUIT), you'll see this:
(gdb) c
Continuing.
kill -QUIT $pid executed in another shell:
Program received signal SIGQUIT, Quit.
main (argc=1, argv=0x7ffdcc9c1518) at t.c:35
35 }
(gdb) c
Continuing.
Received signal: 3
signal originate from pid[123419]
kill -KILL executed in another window:
Program terminated with signal SIGKILL, Killed.
The program no longer exists.
(gdb)

Signal handling with qemu-user

On my machine I have an aarch64 binary, that is statically compiled. I run it using qemu-aarch64-static with the -g 6566 flag. In another terminal I start up gdb-multiarch and connect as target remote localhost:6566.
I expect the binary to raise a signal for which I have a handler defined in the binary. I set a breakpoint at the handler from inside gdb-multiarch after connecting to remote. However, when the signal arises, the breakpoint is not hit on gdb-multiarch. Instead, on the terminal that runs the binary, I get a message along the lines of :-
[1] + 8388 suspended (signal) qemu-aarch64-static -g 6566 ./testbinary
Why does this happen? How can I set a breakpoint on the handler and debug it? I've tried SIGCHLD and SIGFPE.
This works for me with a recent QEMU:
$ cat sig.c
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
void handler(int sig) {
printf("In signal handler, signal %d\n", sig);
return;
}
int main(void) {
printf("hello world\n");
signal(SIGUSR1, handler);
raise(SIGUSR1);
printf("done\n");
return 0;
}
$ aarch64-linux-gnu-gcc -g -Wall -o sig sig.c -static
$ qemu-aarch64 -g 6566 ./sig
and then in another window:
$ gdb-multiarch
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.2) 7.7.1
[etc]
(gdb) set arch aarch64
The target architecture is assumed to be aarch64
(gdb) file /tmp/sigs/sig
Reading symbols from /tmp/sigs/sig...done.
(gdb) target remote :6566
Remote debugging using :6566
0x0000000000400c98 in _start ()
(gdb) break handler
Breakpoint 1 at 0x400e44: file sig.c, line 6.
(gdb) c
Continuing.
Program received signal SIGUSR1, User defined signal 1.
0x0000000000405c68 in raise ()
(gdb) c
Continuing.
Breakpoint 1, handler (sig=10) at sig.c:6
6 printf("In signal handler, signal %d\n", sig);
(gdb)
As you can see, gdb gets control both immediately the process receives the signal and then again when we hit the breakpoint for the handler function.
Incidentally, (integer) dividing by zero is not a reliable way to provoke a signal. This is undefined behaviour in C, and the implementation is free to do the most convenient thing. On x86 this typically results in a SIGFPE. On ARM you will typically find that the result is zero and execution will continue without a signal. (This is a manifestation of the different behaviour of the underlying hardware instructions for division between the two architectures.)
i was doing some R&D for your answer and find following answer
"Internally, bad memory accesses result in the Mach exception EXC_BAD_ACCESS being sent to the program. Normally, this is translated into a SIGBUS UNIX signal. However, gdb intercepts Mach exceptions directly, before the signal translation. The solution is to give gdb the command set dont-handle-bad-access 1 before running your program. Then the normal mechanism is used, and breakpoints inside your signal handler are honored."
The link is gdb: set a breakpoint for a SIGBUS handler
It perhaps help you by considering that qemu does not change the functionality of base operations

Passing Commands into a Binary that runs a shell using a bash script

New to bash scripting so having a little teething problem and was wondering if someone could clear up some trouble I'm having, I have a simple C file called test that creates a shell as seen below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
execl("/bin/sh", "/bin/sh", (void *) NULL);
perror("exec");
return 1;
}
I want to create a bash script to execute this file which I have done below, but then on execution I then wish to send commands to the shell that the binary creates - is this possible I am trying the following to no avail:
#!bin/bash
/var/testfolder/test; # execute the test c file to spawn the shell
??? #don't know how to pass commands to the new shell created :(
Your compiled C binary has the SETUID permission i suppose? With the binary and arguments you can execute any shell like this with this binary permission:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main( int argc, char ** argv )
{
execv("/bin/sh", argv );
perror("exec");
return 1;
}
Output of a test script to print arguments and current process:
$ ./test_execv ./test.sh foo bar
Executing $0: ./test.sh
Args $*: foo bar
process:
PID TTY TIME CMD
3300 pts/1 00:00:08 bash
3498 pts/1 00:00:00 sh
3499 pts/1 00:00:00 ps
Security issue
If you can execute a script to a root shell, anyone can do this. I think you just have to add some of your scripts (only those needed) with a sudo permission to be run as root from your the needed account.
Try this:
#!/bin/bash
/var/testfolder/test <<EOF
cmdtopass1
cmdtopass2
cmdtopass3
EOF

Can a Linux process block external signals but accept signals from its own process?

I am trying to setup a Linux process, which blocks SIGTERM that is sent from kill command (or any other process), but allows SIGTERM to be sent from within itself (through kill(2) system call).
Is it possible?
Here is an example program that I wrote, but it SIG_BLOCKS both external and internal signals, so it doesn't do what I want:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
sigset_t sigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGTERM);
sigprocmask(SIG_BLOCK, &sigs, 0);
printf("Sleeping 30 secs, try killing me! (pid: %d)\n", getpid());
sleep(30);
printf("About to call kill\n");
kill(getpid(), SIGTERM);
printf("This never happens!\n");
return 1;
}
The output is:
Sleeping 30 secs, try killing me! (pid: 29416)
About to call kill
This never happens!
But it should be:
Sleeping 30 secs, try killing me! (pid: 29416)
About to call kill
Because the process should get killed from within through kill(getpid(), SIGTERM).
Not sure if this is what you're after, but you can set up a signal handler using sigaction with the SA_SIGINFO flag, have your SIGTERM handler only call _exit if siginfo.si_pid is your PID
According to what I test if you don't block the signal. you can see the expected behaviour.
But if you blocked the signal, the kill will return with value 0, and since the program continue to execute,it'll print the line and exit. I'm using ubuntu 12.04LTS for testing.

Resources