Moved from d0cs4vage.blogspot.com to here

I’ve recently taken a break from one of my current personal side projects to practice some open-source bug hunting and exploit-dev. The bug I’ve found and am going to explain in this post is rather useless (it’s not remotely exploitable), but it was a good exercise nonetheless. Since you can’t remotely pwn somebody else with it without social engineering, I’m not waiting till it gets patched either :^) So, on with the show…

The bug I found is a simple one in wireshark. You can check out the latest code from wireshark with:

svn co http://anonsvn.wireshark.org/wireshark

Also, for most of my examples, I used one of the recent dev wireshark builds because you can get the pdbs for them. You could go to http://www.wireshark.org/download/automated/win32/ and download a more recent version of the exe and pdbs if you want (wireshark doesn’t archive all of the dev builds it makes). The vulnerable code is in epan/dissectors/packet-snmp.c in the snmp_usml_password_to_key_sha1 function. Can you spot the problem?

3057 /*
3058    SHA1 Password to Key Algorithm COPIED from RFC 3414 A.2.2
3059  */
3060 
3061 static void
3062 snmp_usm_password_to_key_sha1(const guint8 *password, guint passwordlen,
3063                   const guint8 *engineID, guint engineLength,
3064                   guint8 *key)
3065 {
3066     sha1_context     SH;
3067     guint8     *cp, password_buf[72];
3068     guint32      password_index = 0;
3069     guint32      count = 0, i;
3070 
3071     sha1_starts(&SH);   /* initialize SHA */
3072 
3073     /**********************************************/
3074     /* Use while loop until we've done 1 Megabyte */
3075     /**********************************************/
3076     while (count < 1048576) {
3077         cp = password_buf;
3078         for (i = 0; i < 64; i++) {
3079             /*************************************************/
3080             /* Take the next octet of the password, wrapping */
3081             /* to the beginning of the password as necessary.*/
3082             /*************************************************/
3083             *cp++ = password[password_index++ % passwordlen];
3084         }
3085         sha1_update (&SH, password_buf, 64);
3086         count += 64;
3087     }
3088     sha1_finish(&SH, key);
3089 
3090     /*****************************************************/
3091     /* Now localize the key with the engineID and pass   */
3092     /* through SHA to produce final key                  */
3093     /* May want to ensure that engineLength <= 32,       */
3094     /* otherwise need to use a buffer larger than 72     */
3095     /*****************************************************/
3096     memcpy(password_buf, key, 20);
3097     memcpy(password_buf+20, engineID, engineLength);
3098     memcpy(password_buf+20+engineLength, key, 20);
3099 
3100     sha1_starts(&SH);
3101     sha1_update(&SH, password_buf, 40+engineLength);
3102     sha1_finish(&SH, key);
3103     return;
3104  }

Besides the actual vuln, I was surprised that this function was copied from an RFC, with few modifications. This is what it looks like in RFC 3414 A.2.2:

void password_to_key_sha(
      u_char *password,    /* IN */
      u_int   passwordlen, /* IN */
      u_char *engineID,    /* IN  - pointer to snmpEngineID  */
      u_int   engineLength,/* IN  - length of snmpEngineID */
      u_char *key)         /* OUT - pointer to caller 20-octet buffer */
   {
      SHA_CTX     SH;
      u_char     *cp, password_buf[72];
      u_long      password_index = 0;
      u_long      count = 0, i;

      SHAInit (&SH);   /* initialize SHA */

      /**********************************************/
      /* Use while loop until we've done 1 Megabyte */
      /**********************************************/
      while (count < 1048576) {
         cp = password_buf;
         for (i = 0; i < 64; i++) {
             /*************************************************/
             /* Take the next octet of the password, wrapping */
             /* to the beginning of the password as necessary.*/
             /*************************************************/
             *cp++ = password[password_index++ % passwordlen];
         }
         SHAUpdate (&SH, password_buf, 64);
         count += 64;
      }
      SHAFinal (key, &SH);          /* tell SHA we're done */

      /*****************************************************/
      /* Now localize the key with the engineID and pass   */
      /* through SHA to produce final key                  */
      /* May want to ensure that engineLength <= 32,       */
      /* otherwise need to use a buffer larger than 72     */
      /*****************************************************/
      memcpy(password_buf, key, 20);
      memcpy(password_buf+20, engineID, engineLength);
      memcpy(password_buf+20+engineLength, key, 20);

      SHAInit(&SH);
      SHAUpdate(&SH, password_buf, 40+engineLength);
      SHAFinal(key, &SH);
      return;
   }

What really surprised me though was that the comments in the code actually warn about the vuln.

Yes, the vuln is on line 3097 where the engineID is copied into the password_buf. If an engineId is sufficiently large, the buffer will be overflowed. But what actually uses this function? (Like I mentioned earlier, it’s not remotely exploitable, so don’t get your hopes up :^) Calls to this function end up having call stacks like this:

0:000> k
ChildEBP RetAddr  
0012fb88 008bee28 libwireshark!snmp_usm_password_to_key_sha1
0012fba8 008c07af libwireshark!set_ue_keys+0x58
0012fbc0 008c060a libwireshark!ue_se_dup+0x10f
0012fbdc 006e017a libwireshark!renew_ue_cache+0x5a
0012fbe8 006d19a3 libwireshark!uat_load+0x18a
0012fc04 0069faad libwireshark!uat_load_all+0x53
0012fc44 0069ff59 libwireshark!init_prefs+0x6d
0012fc58 00420673 libwireshark!read_prefs+0x19
0012fcb4 0041e5c8 wireshark!read_configuration_files+0x23
0012ff18 00420a81 wireshark!main+0x6b8
0012ff30 00521bae wireshark!WinMain+0x61
0012ffc0 7c817067 wireshark!__tmainCRTStartup+0x140
0012fff0 00000000 kernel32!BaseProcessStart+0x23

This function is responsible for creating a key based on a sha1 hash of a password that is configured by the user in the wireshark preferences and is called when wireshark is opened, regardless if it is opening a pcap or capturing network traffic. The key is then supposed to be used to help decode SNMP traffic. You can configure preferences through the wireshark UI by going to

Edit->Preferences->Protocols->SNMP->Users Table (Edit...)

and adding a new user/password, or you can directly edit the preferences file at

Platform Location
WINDOWS %APPDATA%\Wireshark\snmp_users
*NIX ~/.wireshark/snmp_users