📄 exploit_squid-ntlm-auth.c
字号:
/* * squid_ntlm_authentication buffer overflow exploit attempt * * Copyright (C) 2006 Sourcefire, Inc. All Rights Reserved * * Writen by Patrick Mullen <pmullen@sourcefire.com> * * This file may contain proprietary rules that were created, tested and * certified by Sourcefire, Inc. (the "VRT Certified Rules") as well as * rules that were created by Sourcefire and other third parties and * distributed under the GNU General Public License (the "GPL Rules"). The * VRT Certified Rules contained in this file are the property of * Sourcefire, Inc. Copyright 2005 Sourcefire, Inc. All Rights Reserved. * The GPL Rules created by Sourcefire, Inc. are the property of * Sourcefire, Inc. Copyright 2002-2005 Sourcefire, Inc. All Rights * Reserved. All other GPL Rules are owned and copyrighted by their * respective owners (please see www.snort.org/contributors for a list of * owners and their respective copyrights). In order to determine what * rules are VRT Certified Rules or GPL Rules, please refer to the VRT * Certified Rules License Agreement. */#include "sf_snort_plugin_api.h"#include "sf_snort_packet.h"#include <string.h>/* declare detection functions */int ruleSQUID_NTLM_AUTHeval(void *);/* declare references */static RuleReference ruleSQUID_NTLM_AUTHref0 = { "url", /* type */ "www.idefense.com/application/poi/display?id=107" /* value */};static RuleReference ruleSQUID_NTLM_AUTHcve = { "cve", /* type */ "2004-0541"};static RuleReference *ruleSQUID_NTLM_AUTHrefs[] = { &ruleSQUID_NTLM_AUTHref0, &ruleSQUID_NTLM_AUTHcve, NULL};/* Declare rule options */static FlowFlags ruleSQUID_NTLM_AUTHflow = { FLOW_ESTABLISHED|FLOW_TO_SERVER};static RuleOption ruleSQUID_NTLM_AUTHoption0 = { OPTION_TYPE_FLOWFLAGS, { &ruleSQUID_NTLM_AUTHflow }};static ContentInfo ruleSQUID_NTLM_AUTHcontent = { (u_int8_t *)"Proxy-Authorization:",/* pattern to search for */ 0, /* depth */ 0, /* offset */ CONTENT_NOCASE, /* flags */ NULL, /* holder for boyer/moore info */ NULL, /* holder for byte representation of "NetBus" */ 0, /* holder for length of byte representation */ 0 /* holder of increment length */};static RuleOption ruleSQUID_NTLM_AUTHoption1 = { OPTION_TYPE_CONTENT, { &ruleSQUID_NTLM_AUTHcontent }};static PCREInfo ruleSQUID_NTLM_AUTHpcre = { "^Proxy-Authorization:\\s*NTLM\\s+", /* pattern to search for */ NULL, /* holder for compiled pattern */ NULL, /* holder for compiled pattern flags */ PCRE_CASELESS | PCRE_DOTALL | PCRE_MULTILINE, /* compile flags */ CONTENT_BUF_NORMALIZED /* content flags */};static RuleOption ruleSQUID_NTLM_AUTHoption2 = { OPTION_TYPE_PCRE, { &ruleSQUID_NTLM_AUTHpcre }};RuleOption *ruleSQUID_NTLM_AUTHoptions[] = { &ruleSQUID_NTLM_AUTHoption0, &ruleSQUID_NTLM_AUTHoption1, &ruleSQUID_NTLM_AUTHoption2, NULL};/* Rule definition */Rule ruleSQUID_NTLM_AUTH = { /* rule header */ { IPPROTO_TCP, /* proto */ EXTERNAL_NET, /* SRCIP */ "any", /* SRCPORT */ 0, /* DIRECTION */ HOME_NET, /* DSTIP */ "3128", /* DSTPORT */ }, /* metadata */ { 3, /* genid (HARDCODED!!!) */ 10481, /* 75216960-b72b-4a3e-ade8-704024e42969 sigid */ 3, /* 775e122c-bf74-4c92-936c-5ef9bee77fae */ "attempted-user", /* classification */ 0, /* hardcoded priority XXX NOT PROVIDED BY GRAMMAR YET! */ "EXPLOIT squid NTLM Authorization buffer overflow exploit attempt", /* message */ ruleSQUID_NTLM_AUTHrefs /* ptr to references */#ifdef HAS_METADATA ,NULL#endif }, ruleSQUID_NTLM_AUTHoptions, /* ptr to rule options */ &ruleSQUID_NTLM_AUTHeval, /* ptr to rule detection function */ 0, /* am I initialized yet? */ 0, /* number of options */ 0 /* don't alert */};/* Detection functions *//* Our lookup table for decoding base64 */unsigned char decode64tab[256] = { 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,62 ,100,100,100, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,100,100,100, 99,100,100, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,100,100,100,100,100, 100, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100};/* Given a string, removes header folding (\r\n followed by linear whitespace) * and exits when the end of a header is found, defined as \n followed by a * non-whitespace.*/int unfold_header(const uint8_t *inbuf, u_int32_t inbuf_size, uint8_t *outbuf, u_int32_t outbuf_size, u_int32_t *output_bytes) { const uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; u_int32_t n = 0; int httpheaderfolding = 0; cursor = inbuf; endofinbuf = inbuf + inbuf_size; outbuf_ptr = outbuf; /* Keep adding chars until we get to the end of the line. If we get to the end of the line and the next line starts with a tab or space, add the space to the buffer and keep reading. If the next line does not start with a tab or space, stop reading because that's the end of the header. */ while((cursor < endofinbuf) && (n < outbuf_size)) { if(((*cursor == ' ') || (*cursor == '\t')) && (httpheaderfolding != 2)) { /* Spaces are valid except after CRs */ *outbuf_ptr++ = *cursor; httpheaderfolding = 0; } else if((*cursor == '\n') && (httpheaderfolding != 1)) { /* Can't have multiple LFs in a row, but if we get one it needs to be followed by at least one space */ httpheaderfolding = 1; } else if((*cursor == '\r') && !httpheaderfolding) { /* CR needs to be followed by LF and can't start a line */ httpheaderfolding = 2; } else if(!httpheaderfolding) { *outbuf_ptr++ = *cursor; n++; } else { /* We have reached the end of the header */ /* Unless we get multiple CRs, which is suspicious, but not for us to decide */ break; } cursor++; } *output_bytes = outbuf_ptr - outbuf; return(0);}/* base64decode assumes the input data terminates with '=' and/or at the end of the input buffer * at inbuf_size. If extra characters exist within inbuf before inbuf_size is reached, it will * happily decode what it can and skip over what it can't. This is consistent with other decoders * out there. So, either terminate the string, set inbuf_size correctly, or at least be sure the * data is valid up until the point you care about.*/int base64decode(uint8_t *inbuf, u_int32_t inbuf_size, uint8_t *outbuf, u_int32_t outbuf_size, u_int32_t *bytes_written) { uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; uint8_t base64data[4], *base64data_ptr; /* temporary holder for current base64 chunk */ uint8_t tableval_a, tableval_b, tableval_c, tableval_d; u_int32_t n; u_int32_t max_base64_chars; /* The max number of decoded base64 chars that fit into outbuf */ int error = 0; /* This algorithm will waste up to 4 bytes but we really don't care. At the end we're going to copy the exact number of bytes requested. */ max_base64_chars = (outbuf_size / 3) * 4 + 4; /* 4 base64 bytes gives 3 data bytes, plus an extra 4 to take care of any rounding */ base64data_ptr = base64data; endofinbuf = inbuf + inbuf_size; /* Strip non-base64 chars from inbuf and decode */ n = 0; *bytes_written = 0; cursor = inbuf; outbuf_ptr = outbuf; while((cursor < endofinbuf) && (n < max_base64_chars)) { if(decode64tab[*cursor] != 100) { *base64data_ptr++ = *cursor; n++; /* Number of base64 bytes we've stored */ if(!(n % 4)) { /* We have four databytes upon which to operate */ if((base64data[0] == '=') || (base64data[1] == '=')) { /* Error in input data */ error = 1; break; } /* retrieve values from lookup table */ tableval_a = decode64tab[base64data[0]]; tableval_b = decode64tab[base64data[1]]; tableval_c = decode64tab[base64data[2]]; tableval_d = decode64tab[base64data[3]]; if(*bytes_written < outbuf_size) { *outbuf_ptr++ = (tableval_a << 2) | (tableval_b >> 4); (*bytes_written)++; } if((base64data[2] != '=') && (*bytes_written < outbuf_size)) { *outbuf_ptr++ = (tableval_b << 4) | (tableval_c >> 2); (*bytes_written)++; } else { break; } if((base64data[3] != '=') && (*bytes_written < outbuf_size)) { *outbuf_ptr++ = (tableval_c << 6) | tableval_d; (*bytes_written)++; } else { break; } /* Reset our decode pointer for the next group of four */ base64data_ptr = base64data; } } cursor++; } if(error) return(-1); else return(0);}int ruleSQUID_NTLM_AUTHeval(void *p) { SFSnortPacket *sp = (SFSnortPacket *)p; const u_int8_t *cursor; int n; /* cruft */ /* NTLMSSP AUTH header, before size information (after base64 decode) */ /* ntlmssp_auth_base64_header is the binary decode of "TlRMTVNTUAADAAAA" */ unsigned char ntlmssp_auth_base64_header[] = "\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00"; /* Data for holding our base64 data */ unsigned char base64data[8192], decoded_data[16]; u_int32_t remaining_bytes, output_bytes, num_bytes_extracted; /* Fields we are retrieving from the NTLMSSP_AUTH data */ unsigned int password_len, password_max_len; /* General sanity checking */ if(sp == NULL) return RULE_NOMATCH; if(sp->payload == NULL) return RULE_NOMATCH; /* call flow match */ if (checkFlow(sp, ruleSQUID_NTLM_AUTHoptions[0]->option_u.flowFlags) <= 0 ) return RULE_NOMATCH; /* call content match */ if (contentMatch(sp, ruleSQUID_NTLM_AUTHoptions[1]->option_u.content, &cursor) <= 0) { return RULE_NOMATCH; } /* call pcre match */ if (pcreMatch(p, ruleSQUID_NTLM_AUTHoptions[2]->option_u.pcre, &cursor) <= 0) { return RULE_NOMATCH; } remaining_bytes = sp->payload_size - (cursor - sp->payload); /* unfold the header */ unfold_header(cursor, remaining_bytes, base64data, sizeof(base64data), &output_bytes); /* Decode the base64 data */ n = base64decode(base64data, output_bytes, decoded_data, sizeof(decoded_data), &num_bytes_extracted); if((n < 0) || (num_bytes_extracted < 16)) return RULE_NOMATCH; /* Compare the static portion of the buffer. memcmp != 0 if they are different */ if(memcmp(decoded_data, ntlmssp_auth_base64_header, 12)) return RULE_NOMATCH; /* Read the password length values, stored in the data as little-endian. */ password_len = decoded_data[12]; password_len += decoded_data[13] << 8; password_max_len = decoded_data[14]; password_max_len += decoded_data[15] << 8; /* Both NTLMv1 and NTLMv2 use 24 bytes in their challenge response (http://en.wikipedia.org/wiki/NTLM). Plus, that's the size of the buffer in squid's NTLM module (25 bytes = 24 bytes data + NUL) */ if((password_len > 24) || (password_max_len > 24)) return RULE_MATCH; return RULE_NOMATCH;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -