Commit 17e20b1c authored by Robert David Graham's avatar Robert David Graham
Browse files

banners

parent ed38f1ff
Loading
Loading
Loading
Loading
+35 −30
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ print_nmap_help(void)
"TIMING AND PERFORMANCE:\n"
"  --max-rate <number>: Send packets no faster than <number> per second\n"
"FIREWALL/IDS EVASION AND SPOOFING:\n"
"  -S <IP_Address>: Spoof source address\n"
"  -S/--source-ip <IP_Address>: Spoof source address\n"
"  -e <iface>: Use specified interface\n"
"  -g/--source-port <portnum>: Use given port number\n"
"  --ttl <val>: Set IP time-to-live field\n"
@@ -83,8 +83,9 @@ print_nmap_help(void)
"  -h: Print this help summary page.\n"
"EXAMPLES:\n"
"  masscan -v -sS 192.168.0.0/16 10.0.0.0/8 -p 80\n"
"SEE THE MAN PAGE (http://nmap.org/book/man.html) FOR MORE OPTIONS AND EXAMPLES\n"
"}\n");
"  masscan 23.0.0.0/0 -p80 -output-format binary --output-filename internet.scan\n"
"SEE (https://github.com/robertdavidgraham/masscan) FOR MORE HELP\n"
"\n");
}


@@ -210,7 +211,8 @@ masscan_save_state(struct Masscan *masscan)


    strcpy_s(filename, sizeof(filename), "paused.scan");
    fprintf(stderr, "                                                                      \r");
    fprintf(stderr, "                                   "
                    "                                   \r");
    fprintf(stderr, "saving resume file to: %s\n", filename);

    err = fopen_s(&fp, filename, "wt");
@@ -305,7 +307,8 @@ void ranges_from_file(struct RangeList *ranges, const char *filename)
            /* parse the address range */
            range = range_parse_ipv4(address, &offset, (unsigned)i);
            if (range.begin == 0xFFFFFFFF && range.end == 0) {
                fprintf(stderr, "%s:%u:%u: bad range spec: %s\n", filename, line_number, offset, address);
                fprintf(stderr, "%s:%u:%u: bad range spec: %s\n", 
                        filename, line_number, offset, address);
            } else {
                rangelist_add_range(ranges, range.begin, range.end);
            }
@@ -455,7 +458,8 @@ int EQUALS(const char *lhs, const char *rhs)
 * or from the "config-file" parser for normal options.
 ***************************************************************************/
void
masscan_set_parameter(struct Masscan *masscan, const char *name, const char *value)
masscan_set_parameter(struct Masscan *masscan, 
                      const char *name, const char *value)
{

    if (EQUALS("conf", name) || EQUALS("config", name)) {
@@ -466,24 +470,32 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val
        }
        sprintf_s(masscan->ifname, sizeof(masscan->ifname), "%s", value);
    }
    else if (EQUALS("adapter-ip", name) || EQUALS("adapter.ip", name) || EQUALS("adapterip", name)) {
    else if (EQUALS("adapter-ip", name) || EQUALS("source-ip", name) 
             || EQUALS("source-address", name) || EQUALS("spoof-ip", name)
             || EQUALS("spoof-address", name)) {
        /* Send packets FROM this IP address */
            struct Range range;

            range = range_parse_ipv4(value, 0, 0);
            if (range.begin == 0 && range.end == 0) {
                fprintf(stderr, "CONF: bad IPv4 address: %s=%s\n", name, value);
                fprintf(stderr, "CONF: bad source IPv4 address: %s=%s\n", 
                        name, value);
                return;
            }

            masscan->adapter_ip = range.begin;
    } else if (EQUALS("adapter-port", name) || EQUALS("adapterport", name)) {
    } else if (EQUALS("adapter-port", name) || EQUALS("source-port", name)) {
        /* Send packets FROM this port number */
        unsigned x = strtoul(value, 0, 0);
        if (x > 65535) {
            fprintf(stderr, "error: %s=<n>: expected number less than 1000\n", name);
            fprintf(stderr, "error: %s=<n>: expected number less than 1000\n", 
                    name);
        } else {
            masscan->adapter_port = x;
        }
    } else if (EQUALS("adapter-mac", name) || EQUALS("adapter.mac", name) || EQUALS("adaptermac", name) || EQUALS("spoof-mac", name)) {
    } else if (EQUALS("adapter-mac", name) || EQUALS("spoof-mac", name)
               || EQUALS("source-mac", name)) {
        /* Send packets FROM this MAC address */
        unsigned char mac[6];

        if (parse_mac_address(value, mac) != 0) {
@@ -493,7 +505,7 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val

        memcpy(masscan->adapter_mac, mac, 6);
    }
    else if (EQUALS("router-mac", name) || EQUALS("router.mac", name) || EQUALS("routermac", name)) {
    else if (EQUALS("router-mac", name) || EQUALS("router", name)) {
        unsigned char mac[6];

        if (parse_mac_address(value, mac) != 0) {
@@ -503,7 +515,7 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val

        memcpy(masscan->router_mac, mac, 6);
    }
    else if (EQUALS("rate", name) || EQUALS("maxrate", name) || EQUALS("max.rate", name) ) {
    else if (EQUALS("rate", name) || EQUALS("max-rate", name) ) {
        double rate = 0.0;
        double point = 10.0;
        unsigned i;
@@ -538,11 +550,7 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val
        rangelist_parse_ports(&masscan->ports, value);
        masscan->op = Operation_Scan;
    }
    else if (
            EQUALS("exclude-ports", name) || EQUALS("exclude-port", name) ||
            EQUALS("exclude.ports", name) || EQUALS("exclude.port", name) ||
            EQUALS("excludeports", name) || EQUALS("excludeport", name)
        ) {
    else if (EQUALS("exclude-ports", name) || EQUALS("exclude-port", name)) {
        rangelist_parse_ports(&masscan->exclude_port, value);
    }
    else if (EQUALS("range", name) || EQUALS("ranges", name) || EQUALS("ip", name) || EQUALS("ipv4", name)) {
@@ -571,13 +579,9 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val
    else if (
                EQUALS("exclude", name) ||
                EQUALS("exclude-range", name) ||
                EQUALS("excluderange", name) ||
                EQUALS("exclude-ranges", name) ||
                EQUALS("excluderanges", name) ||
                EQUALS("exclude-ip", name) ||
                EQUALS("excludeip", name) ||
                EQUALS("exclude-ipv4", name) ||
                EQUALS("excludeipv4", name)
                EQUALS("exclude-ipv4", name)
                ) {
        const char *ranges = value;
        unsigned offset = 0;
@@ -626,19 +630,20 @@ masscan_set_parameter(struct Masscan *masscan, const char *name, const char *val
            masscan->op = Operation_DebugIF;
        }
    } else if (EQUALS("dns-servers", name)) {
        fprintf(stderr, "nmap(%s): DNS lookups will never be supported by this code\n", name);
        fprintf(stderr, "nmap(%s): unsupported: DNS lookups too synchronous\n", 
                name);
        exit(1);
    } else if (EQUALS("echo", name)) {
        masscan_echo(masscan, stdout);
        exit(1);
    } else if (EQUALS("excludefile", name) || EQUALS("exclude-file", name) || EQUALS("exclude.file", name)) {
    } else if (EQUALS("excludefile", name)) {
        ranges_from_file(&masscan->exclude_ip, value);
    } else if (EQUALS("host-timeout", name)) {
        fprintf(stderr, "nmap(%s): unsupported: this is an asynchronous tool, so no timeouts\n", name);
        exit(1);
    } else if (EQUALS("iflist", name)) {
        masscan->op = Operation_List_Adapters;
    } else if (EQUALS("includefile", name) || EQUALS("include-file", name) || EQUALS("include.file", name)) {
    } else if (EQUALS("includefile", name)) {
        ranges_from_file(&masscan->targets, value);
    } else if (EQUALS("ip-options", name)) {
        fprintf(stderr, "nmap(%s): unsupported: maybe soon\n", name);
+17 −6
Original line number Diff line number Diff line
@@ -141,7 +141,12 @@ open_rotate(struct Output *output, const char *filename)
            "syn", "tcp" );
        break;
    case Output_Binary:
        fwrite( "mass" "can/" "1.0\0", 1, 12, fp);
        {
            char firstrecord[2+'a'];
            memset(firstrecord, 0, 2+'a');
            sprintf_s(firstrecord, 2+'a', "masscan/1.1");
            fwrite( firstrecord, 1, 2+'a', fp);
        }
        break;
    default:
        LOG(0, "output: ERROR: unknown format\n");
@@ -191,7 +196,12 @@ close_rotate(struct Output *out, FILE *fp)
        );
        break;
    case Output_Binary:
        fwrite( "mass" "can/" "1.0\0", 1, 12, fp);
        {
            char firstrecord[2+'a'];
            memset(firstrecord, 0, 2+'a');
            sprintf_s(firstrecord, 2+'a', "masscan/1.1");
            fwrite( firstrecord, 1, 2+'a', fp);
        }
        break;
    default:
        LOG(0, "output: ERROR: unknown format\n");
@@ -483,7 +493,7 @@ output_report(struct Output *out, int status, unsigned ip, unsigned port, unsign
        break;
    case Output_Binary:
        {
            unsigned foo[256];
            unsigned char foo[256];

            /* [TYPE] field */
            switch (status) {
@@ -546,7 +556,7 @@ proto_string(unsigned proto)
    }
}
const char *
banner_string(const unsigned char *px, size_t length, char *buf, size_t buf_len)
normalize_string(const unsigned char *px, size_t length, char *buf, size_t buf_len)
{
    size_t i=0;
    size_t offset = 0;
@@ -638,7 +648,7 @@ output_report_banner(struct Output *out, unsigned ip, unsigned port, unsigned pr
            (ip>> 0)&0xFF,
            port,
            proto_string(proto),
            banner_string(px, length, banner_buffer, sizeof(banner_buffer))
            normalize_string(px, length, banner_buffer, sizeof(banner_buffer))
            );
        }
        break;
@@ -669,8 +679,9 @@ output_report_banner(struct Output *out, unsigned ip, unsigned port, unsigned pr
            foo[10] = (unsigned char)(port>>8);
            foo[11] = (unsigned char)(port>>0);

            printf("banner: %.*s\n", length, px);
            /* Banner */
            memcpy(foo, px, length);
            memcpy(foo+12, px, length);



+265 −56
Original line number Diff line number Diff line
/*
    Converts Masscan "scan" binary format to text

    The "masscan/1.0" file format starts (and ends) with a 12 byte 
    string with the exact value of "masscan/1.0\0". If this string 
    The "masscan/1.1" file format starts (and ends) with a 12 byte 
    string with the exact value of "masscan/1.1\0". If this string 
    isn't found, then the data is in some other format. We will 
    likely change this format in a couple months as we add new scan
    types (UDP, ICMP, etc.).
 
    The file consists of 12 byte records in LITTLE-ENDIAN format:
    The file consists of a series of TLV (type-length-value) records.
    The first field is the "type" of the record. The next field indicates
    the number of remaining bytes.

    Everything is BIG-ENDIAN.
 
    Both the the "type" and "length" fields are variable length. If the
    high-order bit is set, then the byte is followed by another one. Here are
    some examples
    0x05 - encodes the value '5'
    0x7F - encodes the value '127'
    0x80 0x00 - encodes the value '128'
    0x80 0x01 - encodes the value '129'
    0xFF 0x7F - encodes the value '32767'
    0x81 0x00 0x00 - encodes the value '32768'
    0x80 0x05 - encodes the value '5', unecessarily


    Some record types currently produced by the program:
    1 - STATUS (minimal open/closed port record)
    2 - BANNER (banner for a port)
    109 - FILEHEADER (the file header record)
 
    The FILEHEADER record is the first header in the file. It's contents are 
    always exactly 97 bytes long. That's because the file starts with the
    string "masscan", and the letter 'm' maps to the value 109, and the 
    letter 'a' maps to the value 97.
 

    The STATUS record is formatted as the following:

    +--------+
    |  0x01  |
    +--------+
    |  0x0C  |
    +--------+--------+--------+--------+
    |            timestamp              |
    +--------+--------+--------+--------+
@@ -21,8 +54,28 @@
    |  TTL   |
    +--------+
 
 The BANNER record is formatted as the following. Like all records,
 it starts with the type/length fields. I the length of the banner
 is too long, the length field may be more than 1 byte long. Like many
 other records, it contains a timestamp, IP address, and port number.
 The length of the banner-text is the length
 +--------+
 |  0x01  |
 +--------+ . . . .
 |? length:        :
 +--------+--------+--------+--------+
 |            timestamp              |
 +--------+--------+--------+--------+
 |          IPv4 address             |
 +--------+--------+--------+--------+
 |     TCP port    |
 +--------+--------+ . . . . . .  .  .  .   .   .   .    .    .     .
 | the banner text
 +--------+--------+ . . . . . .  .  .  .   .   .   .    .    .     .

*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

@@ -34,6 +87,8 @@ struct MasscanRecord {
    unsigned char ttl;
};

static const size_t BUF_MAX = 1024*1024;

const char *
reason_string(unsigned x, char *buffer, size_t sizeof_buffer)
{
@@ -54,51 +109,20 @@ reason_string(unsigned x, char *buffer, size_t sizeof_buffer)
    return buffer;
}


void parse_file(const char *filename)
void parse_status(const unsigned char *buf, size_t buf_length)
{
    FILE *fp;
    unsigned char buf[12];
    int bytes_read;

    fp = fopen(filename, "rb");
    if (fp == NULL) {
        perror(filename);
        return;
    }

    /* first record is pseudo-record */
    bytes_read = fread(buf, 1, 12, fp);
    if (bytes_read < 12) {
        perror(filename);
        fclose(fp);
        return;
    }

    /* Make sure it's got the format string */
    if (memcmp(buf, "masscan/1.0\0", 12) != 0) {
        fprintf(stderr, "%s: unknown file format (expeced \"masscan/1.0\")\n", filename);
        fclose(fp);
        return;
    }

    /* Now read all records */
    for (;;) {
    struct MasscanRecord record;
    char timebuf[80];
    char addrbuf[20];
    char reasonbuf[80];

        bytes_read = fread(buf, 1, 12, fp);
        if (bytes_read < 12)
            break; /* eof */
        if (memcmp(buf, "masscan/1.0\0", 12) == 0)
            break; /* repeat = terminating record */
    if (buf_length < 12)
        return;
    
    /* parse record */        
        record.timestamp = buf[0] | buf[1]<<8 | buf[2]<<16 | buf[3]<<24;
        record.ip =  buf[4] | buf[5]<<8 | buf[6]<<16 | buf[7]<<24;
        record.port = buf[8] | buf[9]<<8;
    record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
    record.ip        = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
    record.port      = buf[8]<<8 | buf[9];
    record.reason    = buf[10];
    record.ttl       = buf[11];
    
@@ -127,9 +151,190 @@ void parse_file(const char *filename)
           record.port,
           reasonbuf,
           record.ttl);

}

/**
 * Normalize the string 'in-place' in the buffer. All non-printable characters,
 * include sensitive charactes like < and &, are converted to hex notation
 * like \x83.
 *
 * @param px
 *      the buffer containing the banner string we are normalizing
 * @param offset
 *      where within the buffer the banner starts
 * @param length
 *      where in the buffer the banner ends
 * @param max
 *      the maximum length of the buffer holding the banner, because as we
 *      increase the size of the banner string, we don't want to overwrite
 *      the end of the buffer.
 * @return
 *      a nul-terminated banner string with sensitive characters converted
 */
const char *
normalize_string(unsigned char *px, size_t offset, size_t length, size_t max)
{
    size_t i=0;
    
    for (i=offset; i<length; i++) {
        unsigned char c = px[i];
        
        if (isprint(c) && c != '<' && c != '>' && c != '&' && c != '\\') {
            /* do nothing */
        } else {
            if (i + 6 < max) {
                memmove(px+i+5, px+i, length-i+1);
                px[i++] = '\\';
                px[i++] = 'x';
                px[i++] = "0123456789abdef"[c >> 4];
                px[i  ] = "0123456789abdef"[c >> 0];
            }
        }
    }
    
    px[i] = '\0';
    
    return (char*)px;
}

/**
 * Parse the BANNER record, extracting the timestamp, IP addres, and port
 * number. We also convert the banner string into a safer form.
 */
void parse_banner(unsigned char *buf, size_t buf_length)
{
    struct MasscanRecord record;
    char timebuf[80];
    char addrbuf[20];
    char reasonbuf[80];
    
    /* parse record */        
    record.timestamp = buf[0]<<24 | buf[1]<<16 | buf[2]<<8 | buf[3];
    record.ip        = buf[4]<<24 | buf[5]<<16 | buf[6]<<8 | buf[7];
    record.port      = buf[8]<<8 | buf[9];
    
    /* format time */
    {
        time_t timestamp = (time_t)record.timestamp;
        struct tm *tm;
        tm = localtime(&timestamp);
        
        strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm);
    }
    
    /* format IP into fixed-length field */
    sprintf(addrbuf, "%u.%u.%u.%u", 
            (record.ip>>24)&0xFF, (record.ip>>16)&0xFF,
            (record.ip>>8)&0xFF, (record.ip>>0)&0xFF);
    
    
    
    /* output string */
    printf("%s %-15s :%5u %s\n",
           timebuf,
           addrbuf,
           record.port,
           normalize_string(buf, 10, buf_length, BUF_MAX)
           );
}

void parse_file(const char *filename)
{
    FILE *fp = 0;
    unsigned char *buf = 0;
    int bytes_read;
    
    buf = (unsigned char *)malloc(BUF_MAX);
    if (buf == 0) {
        fprintf(stderr, "memory allocation failure\n");
        goto end;
    }

    fp = fopen(filename, "rb");
    if (fp == NULL) {
        perror(filename);
        goto end;
    }

    /* first record is pseudo-record */
    bytes_read = fread(buf, 1, 'a'+2, fp);
    if (bytes_read < 'a'+2) {
        perror(filename);
        goto end;
    }

    /* Make sure it's got the format string */
    if (memcmp(buf, "masscan/1.1", 11) != 0) {
        fprintf(stderr, 
                "%s: unknown file format (expeced \"masscan/1.1\")\n", 
                filename);
        goto end;
    }

    /* Now read all records */
    for (;;) {
        unsigned type;
        unsigned length;

        /* get type field */
        bytes_read = fread(buf, 1, 1, fp);
        if (bytes_read != 1)
            break;
        type = buf[0] & 0x7F;
        while (buf[0] & 0x80) {
            bytes_read = fread(buf, 1, 1, fp);
            if (bytes_read != 1)
                break;
            type = (type << 7) | (buf[0] & 0x7F);
        }
        
        if (type == 'a')
            break; /* end record */
        
        /* get length field */
        bytes_read = fread(buf, 1, 1, fp);
        if (bytes_read != 1)
            break;
        length = buf[0] & 0x7F;
        while (buf[0] & 0x80) {
            bytes_read = fread(buf, 1, 1, fp);
            if (bytes_read != 1)
                break;
            length = (type << 7) | (buf[0] & 0x7F);
        }
        if (length > BUF_MAX) {
            fprintf(stderr, "file corrupt\n");
            goto end;
        }
        
        
        /* get the remainder fo the record */
        bytes_read = fread(buf, 1, length, fp);
        if (bytes_read < length)
            break; /* eof */
        
        /* Depending on record type, do something different */
        switch (type) {
            case 1: /* STATUS: open */
            case 2: /* STATUS: closed */
                parse_status(buf, bytes_read);
                break;
            case 3: /* BANNER */
                parse_banner(buf, bytes_read);
                break;
            case 'a': /* FILEHEADER */
                goto end;
            default:
                fprintf(stderr, "file corrupt: unknown type %u\n", type);
                goto end;
        }
    }

end:
    if (buf)
        free(buf);
    if (fp)
        fclose(fp);
}

@@ -137,6 +342,10 @@ int main(int argc, char *argv[])
{
    int i;

    if (argc <= 1) {
        printf("usage:\n masscan2text <scanfile>\ndecodes and prints text\n");
        return 1;
    }
    for (i=1; i<argc; i++) {
        parse_file(argv[i]);
    }