ruby, dup2 and rinetd
When you are attacking an xinetd-based process model, there’s no reliable way to know if the child process seg-faulted. The accept’ing socket is always alive and GDB’s follow-fork-mode doesn’t quite help us with this since child processes are being spawned and killed all the time.
Well, you could run xinetd in foreground mode (using -dontfork), but that still doesn’t tell us the exit status of the child processes that are responding to incoming requests. What we need is a version of xinetd that tells us reliably that the child process died. Armed with that knowledge and correlating back to the attacks that caused the segfault, we can then fire up our favorite debugger to inspect the root cause of the problem.
Yeah, we could go through all the dup2, fork nastiness in C. Why bother? The following ruby snippet pretty much does what we need in under 16 lines. Granted that the port number to listen on and the program to exec are hard coded, but it gives us what we need.
require 'socket' server = TCPServer.new 12345 loop do connect = server.accept if fork.nil? $stdout.reopen connect $stdin.reopen connect $stderr.reopen connect exec '/usr/libexec/ftpd', '-d' end puts "coredump" if Process.wait2[1].coredump? connect.close end
Ruby’s IO#reopen is the equivalent of dup2. We wait for an incoming connection, dup the IO objects so that stdout, stdin and stderr are all pointing to the TCP socket and then exec the process. In the parent process, we invoke wait2 which returns an array of [pid, status].
Here’s a fuller version that allows you to pass in the listening port as well as the command to run as arguments.