/*************************************************************************** * payload.cc -- Retrieval of UDP payloads. * * * ***********************IMPORTANT NMAP LICENSE TERMS************************ * * * The Nmap Security Scanner is (C) 1996-2022 Nmap Software LLC ("The Nmap * * Project"). Nmap is also a registered trademark of the Nmap Project. * * * * This program is distributed under the terms of the Nmap Public Source * * License (NPSL). The exact license text applying to a particular Nmap * * release or source code control revision is contained in the LICENSE * * file distributed with that version of Nmap or source code control * * revision. More Nmap copyright/legal information is available from * * https://nmap.org/book/man-legal.html, and further information on the * * NPSL license itself can be found at https://nmap.org/npsl/ . This * * header summarizes some key points from the Nmap license, but is no * * substitute for the actual license text. * * * * Nmap is generally free for end users to download and use themselves, * * including commercial use. It is available from https://nmap.org. * * * * The Nmap license generally prohibits companies from using and * * redistributing Nmap in commercial products, but we sell a special Nmap * * OEM Edition with a more permissive license and special features for * * this purpose. See https://nmap.org/oem/ * * * * If you have received a written Nmap license agreement or contract * * stating terms other than these (such as an Nmap OEM license), you may * * choose to use and redistribute Nmap under those terms instead. * * * * The official Nmap Windows builds include the Npcap software * * (https://npcap.com) for packet capture and transmission. It is under * * separate license terms which forbid redistribution without special * * permission. So the official Nmap Windows builds may not be * * redistributed without special permission (such as an Nmap OEM * * license). * * * * Source is provided to this software because we believe users have a * * right to know exactly what a program is going to do before they run it. * * This also allows you to audit the software for security holes. * * * * Source code also allows you to port Nmap to new platforms, fix bugs, * * and add new features. You are highly encouraged to submit your * * changes as a Github PR or by email to the dev@nmap.org mailing list * * for possible incorporation into the main distribution. Unless you * * specify otherwise, it is understood that you are offering us very * * broad rights to use your submissions as described in the Nmap Public * * Source License Contributor Agreement. This is important because we * * fund the project by selling licenses with various terms, and also * * because the inability to relicense code has caused devastating * * problems for other Free Software projects (such as KDE and NASM). * * * * The free version of Nmap is distributed in the hope that it will be * * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Warranties, * * indemnification and commercial support are all available through the * * Npcap OEM program--see https://nmap.org/oem/ * * * ***************************************************************************/ /* $Id$ */ #include "nmap.h" #include #include #include #include #include #include #include "NmapOps.h" #include "nbase.h" #include "payload.h" #include "utils.h" #include "nmap_error.h" #include "scan_lists.h" extern NmapOps o; struct payload { std::string data; payload (const char *c, size_t n) : data(c, n) {} /* Extra data such as source port goes here. */ /* If 2 payloads are equivalent according to this operator, we'll only keep * the first one, so be sure you update it when adding other attributes. */ bool operator==(const payload& other) const { return data == other.data; } }; /* The key for the payload lookup map is a (proto, port) pair. */ struct proto_dport { u8 proto; u16 dport; proto_dport(u8 proto, u16 dport) { this->proto = proto; this->dport = dport; } bool operator<(const proto_dport& other) const { if (proto == other.proto) return dport < other.dport; else return proto < other.proto; } }; static std::map > portPayloads; static std::vector uniquePayloads; // for accounting /* Newlines are significant because keyword directives (like "source") that follow the payload string are significant to the end of the line. */ typedef enum token_type { TOKEN_ERROR = -1, TOKEN_EOF = 0, TOKEN_NEWLINE, TOKEN_SYMBOL, TOKEN_STRING, } token_t; struct token { token_t type; size_t len; char text[1024]; }; static unsigned long line_no; /* Get the next token from fp. The return value is the token type, or -1 on error. The token type is also stored in token->type. For TOKEN_SYMBOL and TOKEN_STRING, the text is stored in token->text and token->len. The text is null terminated. */ static token_t next_token(FILE *fp, struct token *token) { unsigned int i, tmplen; int c; token->len = 0; /* Skip whitespace and comments. */ while (isspace(c = fgetc(fp)) && c != '\n') ; switch(c) { case EOF: token->type = TOKEN_EOF; break; case '\n': line_no++; token->type = TOKEN_NEWLINE; break; case '#': while ((c = fgetc(fp)) != EOF && c != '\n') ; if (c == EOF) { token->type = TOKEN_EOF; } else { line_no++; token->type = TOKEN_NEWLINE; } break; case '"': token->type = TOKEN_STRING; i = 0; while ((c = fgetc(fp)) != EOF && c != '\n' && c != '"') { if (i + 1 >= sizeof(token->text)) return TOKEN_ERROR; if (c == '\\') { token->text[i++] = '\\'; if (i + 1 >= sizeof(token->text)) return TOKEN_ERROR; c = fgetc(fp); if (c == EOF) return TOKEN_ERROR; } token->text[i++] = c; } if (c != '"') return TOKEN_ERROR; token->text[i] = '\0'; if (cstring_unescape(token->text, &tmplen) == NULL) return TOKEN_ERROR; token->len = tmplen; break; default: token->type = TOKEN_SYMBOL; i = 0; token->text[i++] = c; while ((c = fgetc(fp)) != EOF && (isalnum(c) || c == ',' || c == '-')) { if (i + 1 >= sizeof(token->text)) return TOKEN_ERROR; token->text[i++] = c; } ungetc(c, fp); token->text[i] = '\0'; token->len = i; break; } return token->type; } /* Loop over fp, reading tokens and adding payloads to the global payloads map as they are completed. Returns -1 on error. */ static int load_payloads_from_file(FILE *fp) { struct token token; unsigned long firstline = 0; line_no = 1; token_t type = next_token(fp, &token); for (;;) { unsigned short *ports; int count; bool duplicate = false; /* Skip everything (unknown keywords from previous payload, unknown file * keywords, etc.) until the next payload entry or EOF */ while (type != TOKEN_EOF && !(type == TOKEN_SYMBOL && strcmp(token.text, "udp") == 0)) type = next_token(fp, &token); if (type == TOKEN_EOF) break; firstline = line_no; type = next_token(fp, &token); if (type != TOKEN_SYMBOL) { fprintf(stderr, "Expected a port list at line %lu of %s.\n", line_no, PAYLOAD_FILENAME); return -1; } getpts_simple(token.text, SCAN_UDP_PORT, &ports, &count); if (ports == NULL) { fprintf(stderr, "Can't parse port list \"%s\" at line %lu of %s.\n", token.text, line_no, PAYLOAD_FILENAME); return -1; } while(TOKEN_NEWLINE == (type = next_token(fp, &token))) ; // skip newlines if (type != TOKEN_STRING) { log_write(LOG_STDERR, "Payload missing data at line %lu of %s.\n", line_no, PAYLOAD_FILENAME); // Try a new payload free(ports); continue; } struct payload *portPayload = NULL; // Peek at the next significant token struct token peek_token; while (TOKEN_NEWLINE == (type = next_token(fp, &peek_token))) ; // skip newlines // If it's a string continuation, see if we can squeeze it into the current token. while (type == TOKEN_STRING) { if (token.len + peek_token.len < sizeof(token.text)) { // Next string fits in this one's buffer! memcpy(token.text + token.len, peek_token.text, peek_token.len); token.len += peek_token.len; } else { // Token is full if (portPayload == NULL) { // Allocate new payload portPayload = new struct payload (token.text, token.len); } else { // append token to current payload portPayload->data.append(token.text, token.len); } // peek_token becomes the previous token token = peek_token; } // Keep peeking forward while (TOKEN_NEWLINE == (type = next_token(fp, &peek_token))) ; // skip newlines } // If the string is still going, but we got an error, abandon this payload. if (type == TOKEN_ERROR && peek_token.type == TOKEN_STRING) { log_write(LOG_STDERR, "Error parsing payload data at line %lu of %s.\n", line_no, PAYLOAD_FILENAME); if (portPayload) delete portPayload; // maybe we can pick up at the next payload. type = next_token(fp, &token); free(ports); continue; } // Otherwise, stash the last token in the payload and move on. if (portPayload == NULL) { // Allocate new payload portPayload = new struct payload (token.text, token.len); } else { // append token to current payload portPayload->data.append(token.text, token.len); } token = peek_token; // Here we would parse additional keywords like "source" that we might care about. // Make sure these payloads are actually unique! for (std::vector::const_iterator it = uniquePayloads.begin(); it != uniquePayloads.end(); ++it) { if (**it == *portPayload) { // Probably not what they intended. log_write(LOG_STDERR, "Duplicate payload on line %lu of %s.\n", firstline, PAYLOAD_FILENAME); // Since they're functionally equivalent, only keep one copy. duplicate = true; delete portPayload; portPayload = *it; break; } } if (!duplicate) { uniquePayloads.push_back(portPayload); duplicate = false; } for (int p = 0; p < count; p++) { const struct proto_dport key(IPPROTO_UDP, ports[p]); std::vector &portPayloadVector = portPayloads[key]; // Ports are unique, and we ensured payloads are unique earlier, so no chance of duplicate here. portPayloadVector.push_back(portPayload); if (portPayloadVector.size() > MAX_PAYLOADS_PER_PORT) { fatal("Number of UDP payloads for port %u exceeds the limit of %u.\n", ports[p], MAX_PAYLOADS_PER_PORT); } } free(ports); } return 0; } /* Ensure that the payloads map is initialized from the nmap-payloads file. This function keeps track of whether it has been called and does nothing after it is called the first time. */ int init_payloads(void) { static bool payloads_loaded = false; char filename[256]; FILE *fp; int ret; if (payloads_loaded) return 0; payloads_loaded = true; if (nmap_fetchfile(filename, sizeof(filename), PAYLOAD_FILENAME) != 1) { error("Cannot find %s. UDP payloads are disabled.", PAYLOAD_FILENAME); return 0; } fp = fopen(filename, "r"); if (fp == NULL) { gh_perror("Can't open %s for reading.\n", filename); return -1; } /* Record where this data file was found. */ o.loaded_data_files[PAYLOAD_FILENAME] = filename; ret = load_payloads_from_file(fp); fclose(fp); return ret; } void free_payloads(void) { std::vector::iterator vec_it; for (vec_it = uniquePayloads.begin(); vec_it != uniquePayloads.end(); ++vec_it) { delete *vec_it; } uniquePayloads.clear(); portPayloads.clear(); } /* Get a payload appropriate for the given UDP port. For certain selected ports a payload is returned, and for others a zero-length payload is returned. The length is returned through the length pointer. */ const char *udp_port2payload(u16 dport, size_t *length, u8 index) { static const char *payload_null = ""; std::map >::const_iterator portPayloadIterator; std::vector::const_iterator portPayloadVectorIterator; const proto_dport key(IPPROTO_UDP, dport); int portPayloadVectorSize; portPayloadIterator = portPayloads.find(key); if (portPayloadIterator != portPayloads.end()) { const std::vector& portPayloadVector = portPayloads.find(key)->second; portPayloadVectorSize = portPayloadVector.size(); index %= portPayloadVectorSize; if (portPayloadVectorSize > 0) { portPayloadVectorIterator = portPayloadVector.begin(); while (index > 0 && portPayloadVectorIterator != portPayloadVector.end()) { index--; portPayloadVectorIterator++; } assert (index == 0); assert (portPayloadVectorIterator != portPayloadVector.end()); const std::string &data = (*portPayloadVectorIterator)->data; *length = data.size(); return data.data(); } else { *length = 0; return payload_null; } } else { *length = 0; return payload_null; } } /* Get a payload appropriate for the given UDP port. If --data-length was used, returns the global random payload. Otherwise, for certain selected ports a payload is returned, and for others a zero-length payload is returned. The length is returned through the length pointer. */ const char *get_udp_payload(u16 dport, size_t *length, u8 index) { if (o.extra_payload != NULL) { *length = o.extra_payload_length; return o.extra_payload; } else { return udp_port2payload(dport, length, index); } } u8 udp_payload_count(u16 dport) { std::map >::const_iterator portPayloadIterator; const proto_dport key(IPPROTO_UDP, dport); size_t portPayloadVectorSize = 0; portPayloadIterator = portPayloads.find(key); if (portPayloadIterator != portPayloads.end()) { portPayloadVectorSize = portPayloadIterator->second.size(); } return portPayloadVectorSize; }