Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add insert_tcp_options() nasl function #618

Merged
merged 13 commits into from
Nov 16, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Extend nasl lint to detect if function parameter is used twice. [#585](https://github.com/greenbone/openvas/pull/585)
- Consider .csv files for checksum check and upload in redis cache. [#599](https://github.com/greenbone/openvas/pull/599)
- Add option to specify if a host can be scanned through its IPv4 and IPv6 in parallel. [#604](https://github.com/greenbone/openvas/pull/604)
- Add insert_tcp_options and insert_tcp_v6_options nasl functions. [#618](https://github.com/greenbone/openvas/pull/618)

### Changed
- Store results in main_kb instead of host_kb. [#550](https://github.com/greenbone/openvas/pull/550)
Expand Down
6 changes: 6 additions & 0 deletions nasl/nasl_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ static init_func libfuncs[] = {
{"get_tcp_v6_element", get_tcp_v6_element},
{"set_tcp_elements", set_tcp_elements},
{"set_tcp_v6_elements", set_tcp_v6_elements},
{"insert_tcp_options", insert_tcp_options},
{"insert_tcp_v6_options", insert_tcp_v6_options},
{"dump_tcp_packet", dump_tcp_packet},
{"dump_tcp_v6_packet", dump_tcp_v6_packet},
{"tcp_ping", nasl_tcp_ping},
Expand Down Expand Up @@ -412,6 +414,10 @@ static struct
{"IP_DF", IP_DF},
{"IP_MF", IP_MF},
{"IP_OFFMASK", IP_OFFMASK},
{"TCPOPT_MAXSEG", TCPOPT_MAXSEG},
{"TCPOPT_WINDOW", TCPOPT_WINDOW},
{"TCPOPT_SACK_PERMITTED", TCPOPT_SACK_PERMITTED},
{"TCPOPT_TIMESTAMP", TCPOPT_TIMESTAMP},
{"ACT_INIT", ACT_INIT},
{"ACT_GATHER_INFO", ACT_GATHER_INFO},
{"ACT_ATTACK", ACT_ATTACK},
Expand Down
304 changes: 303 additions & 1 deletion nasl/nasl_packet_forgery.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,36 @@ struct pseudohdr
u_char protocol;
u_short length;
struct tcphdr tcpheader;
};
} __attribute__ ((packed));

// TCP options
struct tcp_opt_mss
{
uint8_t kind; // 2
uint8_t len; // 4
uint16_t mss;
} __attribute__ ((packed));

struct tcp_opt_wscale
{
uint8_t kind; // 3
uint8_t len; // 3
uint8_t wscale;
} __attribute__ ((packed));

struct tcp_opt_sack_perm
{
uint8_t kind; // 4
uint8_t len; // 2
} __attribute__ ((packed));

struct tcp_opt_tstamp
{
uint8_t kind; // 8
uint8_t len; // 10
uint32_t tstamp;
uint32_t e_tstamp;
} __attribute__ ((packed));

/**
* @brief Fills an IP datagram with TCP data. Note that the ip_p field is not
Expand Down Expand Up @@ -818,6 +847,279 @@ set_tcp_elements (lex_ctxt *lexic)
return retc;
}

/**
* @brief Add options to a TCP segment header.
* Possible options are:
* TCPOPT_MAXSEG (2), values between 536 and 65535
* TCPOPT_WINDOW (3), with values between 0 and 14
* TCPOPT_SACK_PERMITTED (4), no value required.
* TCPOPT_TIMESTAMP (8), 8 bytes value for timestamp
* and echo timestamp, 4 bytes each one.
*
* @param[in] lexic Lexical context of NASL interpreter.
* @param[in] tcp IP datagram.
* @param[in] data (optional) TCP data payload.
* @param[in] unnamed option.
* @param[in] Value for unnamed option if required.
*
* @return The modified IP datagram.
*/
tree_cell *
insert_tcp_options (lex_ctxt *lexic)
{
char *pkt = get_str_var_by_name (lexic, "tcp");
struct ip *ip = (struct ip *) pkt;
int pktsz = get_var_size_by_name (lexic, "tcp");
struct tcphdr *tcp;
tree_cell *retc;
char *data = get_str_var_by_name (lexic, "data");
int data_len = get_var_size_by_name (lexic, "data");
char *npkt;
int tcp_opt, tcp_opt_val, tcp_opt_val2;
int current_opt_len, total_opt_len, opt_size_allocated;
char *opts, *ptr_opts_pos;
uint8_t eol, nop;
int i;

struct tcp_opt_mss *opt_mss;
struct tcp_opt_wscale *opt_wscale;
struct tcp_opt_sack_perm *opt_sack_perm;
struct tcp_opt_tstamp *opt_tstamp;

if (!ip)
{
nasl_perror (lexic, "%s: Invalid value for the argument 'tcp'\n",
__func__);
return NULL;
}

opts = g_malloc0 (sizeof (char) * 4);
ptr_opts_pos = opts;
opt_size_allocated = 4; // 4 bytes
total_opt_len = 0;
for (i = 0;; i++)
{
tcp_opt = get_int_var_by_num (lexic, i, -1);
current_opt_len = total_opt_len;

if (tcp_opt == -1)
break;

switch (tcp_opt)
{
case TCPOPT_MAXSEG:
tcp_opt_val = get_int_var_by_num (lexic, i + 1, -1);
i++;
if (tcp_opt_val < (int) TCP_MSS_DEFAULT || tcp_opt_val > 65535)
{
nasl_perror (lexic, "%s: Invalid value for TCP option MSS\n",
__func__);
break;
}
opt_mss = g_malloc0 (sizeof (struct tcp_opt_mss));
total_opt_len += TCPOLEN_MAXSEG;
opt_mss->kind = TCPOPT_MAXSEG;
opt_mss->len = TCPOLEN_MAXSEG;
opt_mss->mss = FIX (tcp_opt_val);

// Need reallocated memory because options requires it.
if (total_opt_len > opt_size_allocated)
{
opt_size_allocated = ((total_opt_len / 4) + 1) * 4;
opts = g_realloc (opts, sizeof (char) * opt_size_allocated);
ptr_opts_pos = opts + current_opt_len;
}

memcpy (ptr_opts_pos, (u_char *) opt_mss,
sizeof (struct tcp_opt_mss));
ptr_opts_pos = ptr_opts_pos + sizeof (struct tcp_opt_mss);
g_free (opt_mss);
break;
case TCPOPT_WINDOW:
tcp_opt_val = get_int_var_by_num (lexic, i + 1, -1);
i++;
if (tcp_opt_val < 0 || tcp_opt_val > 14)
{
nasl_perror (lexic, "%s: Invalid value for TCP option WScale\n",
__func__);
break;
}
opt_wscale = g_malloc0 (sizeof (struct tcp_opt_wscale));
total_opt_len += TCPOLEN_WINDOW;
opt_wscale->kind = TCPOPT_WINDOW;
opt_wscale->len = TCPOLEN_WINDOW;
opt_wscale->wscale = tcp_opt_val;

// Need reallocated memory because options requires it.
if (total_opt_len > opt_size_allocated)
{
opt_size_allocated = ((total_opt_len / 4) + 1) * 4;
opts = g_realloc (opts, sizeof (char) * opt_size_allocated);
ptr_opts_pos = opts + current_opt_len;
}

memcpy (ptr_opts_pos, (u_char *) opt_wscale,
sizeof (struct tcp_opt_wscale));
ptr_opts_pos = ptr_opts_pos + sizeof (struct tcp_opt_wscale);
g_free (opt_wscale);
break;
case TCPOPT_SACK_PERMITTED:
opt_sack_perm = g_malloc0 (sizeof (struct tcp_opt_sack_perm));
total_opt_len += TCPOLEN_SACK_PERMITTED;
opt_sack_perm->kind = TCPOPT_SACK_PERMITTED;
opt_sack_perm->len = TCPOLEN_SACK_PERMITTED;

// Need reallocated memory because options requires it.
if (total_opt_len > opt_size_allocated)
{
opt_size_allocated = ((total_opt_len / 4) + 1) * 4;
opts = g_realloc (opts, sizeof (char) * opt_size_allocated);
ptr_opts_pos = opts + current_opt_len;
}

memcpy (ptr_opts_pos, (u_char *) opt_sack_perm,
sizeof (struct tcp_opt_sack_perm));
ptr_opts_pos = ptr_opts_pos + sizeof (struct tcp_opt_sack_perm);
g_free (opt_sack_perm);
break;
case TCPOPT_TIMESTAMP:
tcp_opt_val = get_int_var_by_num (lexic, i + 1, -1);
tcp_opt_val2 = get_int_var_by_num (lexic, i + 2, -1);
i = i + 2;
if (tcp_opt_val < 0)
nasl_perror (lexic, "%s: Invalid value for TCP option Timestamp\n",
__func__);
opt_tstamp = g_malloc0 (sizeof (struct tcp_opt_tstamp));
total_opt_len += TCPOLEN_TIMESTAMP;
opt_tstamp->kind = TCPOPT_TIMESTAMP;
opt_tstamp->len = TCPOLEN_TIMESTAMP;
opt_tstamp->tstamp = htonl (tcp_opt_val);
opt_tstamp->e_tstamp = htonl (tcp_opt_val2);

// Need reallocated memory because options requires it.
if (total_opt_len > opt_size_allocated)
{
opt_size_allocated = ((total_opt_len / 4) + 1) * 4;
opts = g_realloc (opts, sizeof (char) * opt_size_allocated);
ptr_opts_pos = opts + current_opt_len;
}

memcpy (ptr_opts_pos, (u_char *) opt_tstamp,
sizeof (struct tcp_opt_tstamp));
ptr_opts_pos = ptr_opts_pos + sizeof (struct tcp_opt_tstamp);
g_free (opt_tstamp);
break;
case TCPOPT_NOP:
case TCPOPT_EOL:
case TCPOPT_SACK: /* Experimental, not supported */
default:
nasl_perror (lexic, "%s: TCP option %d not supported\n", __func__,
tcp_opt);
break;
}
}

// Add NOP padding and End Of Option list kinds.
current_opt_len = total_opt_len;
eol = TCPOPT_EOL;
nop = TCPOPT_NOP;
if (total_opt_len % 4 == 0)
{
opt_size_allocated = opt_size_allocated + 4;
opts = g_realloc (opts, sizeof (char) * opt_size_allocated);
ptr_opts_pos = opts + total_opt_len;
}
if (current_opt_len < opt_size_allocated - 1)
{
// Add NOPs
for (i = current_opt_len; i < opt_size_allocated - 1; i++)
{
memcpy (ptr_opts_pos, &nop, 1);
total_opt_len++;
ptr_opts_pos++;
}
}
// Add EOL
memcpy (ptr_opts_pos, &eol, 1);

if (ip->ip_hl * 4 > pktsz)
// ip->ip_hl is bogus, we work around that
tcp = (struct tcphdr *) (pkt + 20);
else
tcp = (struct tcphdr *) (pkt + ip->ip_hl * 4);

if (pktsz < UNFIX (ip->ip_len))
{
g_free (opts);
return NULL;
}

if (data_len == 0)
{
data_len = UNFIX (ip->ip_len) - (ip->ip_hl * 4) - (tcp->th_off * 4);
data = (char *) ((char *) tcp + tcp->th_off * 4);
}

// Alloc enough memory to hold the options
npkt =
g_malloc0 (ip->ip_hl * 4 + tcp->th_off * 4 + opt_size_allocated + data_len);
memcpy (npkt, pkt, UNFIX (ip->ip_len));
ip = (struct ip *) (npkt);
tcp = (struct tcphdr *) (npkt + ip->ip_hl * 4);

// copy options
memcpy ((char *) tcp + tcp->th_off * 4, opts, opt_size_allocated);

tcp->th_off = tcp->th_off + (opt_size_allocated / 4);
memcpy ((char *) tcp + tcp->th_off * 4, data, data_len);

// Update ip_len and calculate ip checksum
ip->ip_len = FIX (ip->ip_hl * 4 + tcp->th_off * 4 + data_len);
ip->ip_sum = 0;
ip->ip_sum = np_in_cksum ((u_short *) npkt, ip->ip_hl * 4);

// Calculate tcp header with options checksum
struct pseudohdr pseudoheader;
char *tcpsumdata =
g_malloc0 (sizeof (struct pseudohdr) + opt_size_allocated + data_len + 1);
struct in_addr source, dest;

source.s_addr = ip->ip_src.s_addr;
dest.s_addr = ip->ip_dst.s_addr;

memset (&pseudoheader, 0, sizeof (struct pseudohdr));
pseudoheader.saddr.s_addr = source.s_addr;
pseudoheader.daddr.s_addr = dest.s_addr;

pseudoheader.protocol = IPPROTO_TCP;
// TCP length is tcpheader + options + data
pseudoheader.length =
htons (sizeof (struct tcphdr) + opt_size_allocated + data_len);

// Set th_sum to Zero, necessary for the new checksum calculation
tcp->th_sum = 0;

memcpy ((char *) &pseudoheader.tcpheader, (char *) tcp,
sizeof (struct tcphdr));

/* fill tcpsumdata with data to checksum */
memcpy (tcpsumdata, (char *) &pseudoheader, sizeof (struct pseudohdr));
memcpy (tcpsumdata + sizeof (struct pseudohdr), (char *) opts,
opt_size_allocated);
memcpy (tcpsumdata + sizeof (struct pseudohdr) + opt_size_allocated,
(char *) data, data_len);
tcp->th_sum =
np_in_cksum ((unsigned short *) tcpsumdata,
sizeof (struct pseudohdr) + opt_size_allocated + data_len);
g_free (opts);
g_free (tcpsumdata);

retc = alloc_typed_cell (CONST_DATA);
retc->size = (ip->ip_hl * 4) + (tcp->th_off * 4) + data_len;
retc->x.str_val = npkt;
return retc;
}

/**
* @brief Dump the TCP part of a IP Datagram.
*
Expand Down
2 changes: 2 additions & 0 deletions nasl/nasl_packet_forgery.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ get_tcp_element (lex_ctxt *);
tree_cell *
set_tcp_elements (lex_ctxt *);
tree_cell *
insert_tcp_options (lex_ctxt *);
tree_cell *
dump_tcp_packet (lex_ctxt *);

tree_cell *
Expand Down
Loading