Skip to content

Commit

Permalink
Merge pull request #712 from appneta/Bug_#703_tcpreplay_heap-buffer-o…
Browse files Browse the repository at this point in the history
…verflow

Bug #703 safeguard against corrupt packet lengths in checksum functions
  • Loading branch information
fklassen authored Feb 12, 2022
2 parents c23738f + b63f169 commit b388cbc
Show file tree
Hide file tree
Showing 67 changed files with 124 additions and 65 deletions.
3 changes: 2 additions & 1 deletion docs/CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
02/10/2022 Version 4.4.1-beta1
02/11/2022 Version 4.4.1-beta1
- fix support for piping PCAP files from STDIN (#708)
- build failures Debian/kfreebsd (#706)
- bus error when building on armhf (#705)
- typo fixes (#704)
- heap buffer overflow in tcpreplay (#703)
- double free in Juniper DLT (#702)

01/31/2022 Version 4.4.0
Expand Down
20 changes: 20 additions & 0 deletions src/common/err.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ void notice(const char *fmt, ...);
exit(x); \
} while (0)

#define err_no_exit(y) do { \
fprintf(stderr, "\nFatal Error in %s:%s() line %d:\n%s\n", __FILE__, __FUNCTION__, __LINE__, y); \
fflush(NULL); \
} while (0)

#define err_no_exitx(y, ...) do {\
fprintf(stderr, "\nFatal Error in %s:%s() line %d:\n " y "\n", __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__); \
fflush(NULL); \
} while (0)

#else /* no detailed DEBUG info */

/* dbg() and dbgx() become no-ops for non-DEBUG builds */
Expand All @@ -124,6 +134,16 @@ void notice(const char *fmt, ...);
fflush(NULL); \
exit(x); \
} while (0)

#define err_no_exit(y) do {\
fprintf(stderr, "\nFatal Error:\n%s\n", y); \
fflush(NULL); \
} while(0)

#define err_no_exitx(y, ...) do {\
fprintf(stderr, "\nFatal Error: " y "\n", __VA_ARGS__); \
fflush(NULL); \
} while (0)
#endif /* DEBUG */


Expand Down
17 changes: 4 additions & 13 deletions src/tcpedit/checksum.c
Original file line number Diff line number Diff line change
Expand Up @@ -154,24 +154,15 @@ do_checksum(tcpedit_t *tcpedit, uint8_t *data, int proto, int len) {
icmp6->icmp_sum = CHECKSUM_CARRY(sum);
break;

case IPPROTO_IP:
default:
if (ipv4) {
ipv4->ip_sum = 0;
sum = do_checksum_math((uint16_t *)data, ip_hl);
ipv4->ip_sum = CHECKSUM_CARRY(sum);
} else {
tcpedit_setwarn(tcpedit, "Unsupported protocol for checksum: 0x%x", proto);
return TCPEDIT_WARN;
}
break;

case IPPROTO_IGMP:
case IPPROTO_GRE:
case IPPROTO_OSPF:
case IPPROTO_OSPF_LSA:
case IPPROTO_VRRP:
case TCPR_PROTO_CDP:
case TCPR_PROTO_ISL:
default:
tcpedit_setwarn(tcpedit, "Unsupported protocol for checksum: 0x%x", proto);
return TCPEDIT_WARN;
}

return TCPEDIT_OK;
Expand Down
104 changes: 67 additions & 37 deletions src/tcpedit/edit_packet.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ static int is_unicast_ipv4(tcpedit_t *tcpedit, uint32_t ip);
static void randomize_ipv6_addr(tcpedit_t *tcpedit, struct tcpr_in6_addr *addr);
static int remap_ipv6(tcpedit_t *tcpedit, tcpr_cidr_t *cidr, struct tcpr_in6_addr *addr);
static int is_multicast_ipv6(tcpedit_t *tcpedit, struct tcpr_in6_addr *addr);

static int ipv6_header_length(ipv6_hdr_t const * ip6_hdr, int pkt_len);
static int ipv6_header_length(ipv6_hdr_t const * ip6_hdr, const size_t pkt_len,
const size_t l2len);

/**
* this code re-calcs the IP and Layer 4 checksums
Expand All @@ -56,36 +56,44 @@ static int ipv6_header_length(ipv6_hdr_t const * ip6_hdr, int pkt_len);
* Returns 0 on success, -1 on error
*/
int
fix_ipv4_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr)
fix_ipv4_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
ipv4_hdr_t *ip_hdr, const size_t l2len)
{
int ret1 = 0, ret2 = 0, ip_len;
assert(tcpedit);
assert(pkthdr);
assert(ip_hdr);

if (pkthdr->caplen < sizeof(*ip_hdr)) {
tcpedit_setwarn(tcpedit, "caplen too small to read IPv4 header: %u",
pkthdr->caplen);
if (pkthdr->caplen < (sizeof(*ip_hdr) + l2len)) {
tcpedit_setwarn(tcpedit, "caplen too small to read IPv4 header: caplen=%u: pkt=" COUNTER_SPEC,
pkthdr->caplen, tcpedit->runtime.packetnum);
return TCPEDIT_WARN;
}

if (ip_hdr->ip_v != 4) {
tcpedit_seterr(tcpedit, "Invalid packet: Expected IPv4 packet: got %u", ip_hdr->ip_v);
tcpedit_seterr(tcpedit, "Invalid packet: Expected IPv4 packet: got %u: pkt=" COUNTER_SPEC,
ip_hdr->ip_v, tcpedit->runtime.packetnum);
return TCPEDIT_ERROR;
}

ip_len = (int)ntohs(ip_hdr->ip_len);
/* calc the L4 checksum if we have the whole packet && not a frag or first frag */
if (pkthdr->caplen == pkthdr->len &&
(htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0) {
ip_len = (int)ntohs(ip_hdr->ip_len);
ret1 = do_checksum(tcpedit, (u_char *) ip_hdr, ip_hdr->ip_p,
ip_len - (ip_hdr->ip_hl << 2));
if (ip_len != (int)(pkthdr->caplen - l2len)) {
tcpedit_seterr(tcpedit,
"caplen minus L2 length %u does IPv4 header length %u: pkt=" COUNTER_SPEC,
pkthdr->caplen - l2len, ip_len,
tcpedit->runtime.packetnum);
return TCPEDIT_ERROR;
}
ret1 = do_checksum(tcpedit, (u_char*)ip_hdr, ip_hdr->ip_p,
ip_len - (ip_hdr->ip_hl << 2));
if (ret1 < 0)
return TCPEDIT_ERROR;
}

/* calc IP checksum */
ip_len = (int)ntohs(ip_hdr->ip_len);
ret2 = do_checksum(tcpedit, (u_char *) ip_hdr, IPPROTO_IP, ip_len);
if (ret2 < 0)
return TCPEDIT_ERROR;
Expand All @@ -102,7 +110,8 @@ fix_ipv4_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv4_hdr_t *i
* -1 on error
*/
static int
ipv6_header_length(ipv6_hdr_t const * ip6_hdr, int pkt_len)
ipv6_header_length(ipv6_hdr_t const * ip6_hdr, const size_t pkt_len,
const size_t l2len)
{
struct tcpr_ipv6_ext_hdr_base const * nhdr;
uint8_t next_header;
Expand All @@ -111,8 +120,7 @@ ipv6_header_length(ipv6_hdr_t const * ip6_hdr, int pkt_len)
offset = sizeof(*ip6_hdr);
next_header = ip6_hdr->ip_nh;

while (sizeof(*nhdr) + offset < (size_t)pkt_len)
{
while (sizeof(*nhdr) + offset + l2len < (size_t)pkt_len) {
if (next_header != TCPR_IPV6_NH_HBH
&& next_header != TCPR_IPV6_NH_ROUTING
&& next_header != TCPR_IPV6_NH_FRAGMENT) {
Expand All @@ -128,16 +136,17 @@ ipv6_header_length(ipv6_hdr_t const * ip6_hdr, int pkt_len)
}

int
fix_ipv6_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr)
fix_ipv6_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
ipv6_hdr_t *ip6_hdr, const size_t l2len)
{
int ret = 0;
assert(tcpedit);
assert(pkthdr);
assert(ip6_hdr);

if (pkthdr->caplen < sizeof(*ip6_hdr)) {
tcpedit_setwarn(tcpedit, "caplen too small to read IPv6 header: %u",
pkthdr->caplen);
if (pkthdr->caplen < (sizeof(*ip6_hdr) + l2len)) {
tcpedit_setwarn(tcpedit, "caplen too small to read IPv6 header: caplen=%u pkt=" COUNTER_SPEC,
pkthdr->caplen, tcpedit->runtime.packetnum);
return TCPEDIT_WARN;
}

Expand All @@ -149,9 +158,10 @@ fix_ipv6_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv6_hdr_t *i

/* calc the L4 checksum if we have the whole packet && not a frag or first frag */
if (pkthdr->caplen == pkthdr->len) {
if (ip6_hdr->ip_len < ipv6_header_length(ip6_hdr, pkthdr->len)) {
tcpedit_setwarn(tcpedit, "Unable to checksum IPv6 packet with invalid length %u",
ip6_hdr->ip_len);
int ip6_len = ipv6_header_length(ip6_hdr, pkthdr->len, l2len);
if (ip6_hdr->ip_len < ip6_len) {
tcpedit_setwarn(tcpedit, "Unable to checksum IPv6 packet with invalid: pkt=" COUNTER_SPEC " IP length=%u caplen=" COUNTER_SPEC,
tcpedit->runtime.packetnum, ip6_hdr->ip_len);
return TCPEDIT_WARN;
}
ret = do_checksum(tcpedit, (u_char *)ip6_hdr, ip6_hdr->ip_nh,
Expand All @@ -167,20 +177,6 @@ fix_ipv6_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr, ipv6_hdr_t *i
return TCPEDIT_OK;
}

/*
* #406 fix IP headers which may be not be set properly due to TCP segmentation
*/
void fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr)
{
if (!ip_hdr->ip_len)
ip_hdr->ip_len = htons((uint16_t)pkthdr->len);
}

void fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr)
{
if (!ip6_hdr->ip_len)
ip6_hdr->ip_len = htons((uint16_t)pkthdr->len);
}

static void ipv4_l34_csum_replace(uint8_t *data, uint8_t protocol,
uint32_t old, uint32_t new)
Expand Down Expand Up @@ -369,6 +365,40 @@ randomize_ipv6_addr(tcpedit_t *tcpedit, struct tcpr_in6_addr *addr)
}
}

int fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr,
const size_t l2len)
{
int ip_len = (int)ntohs(ip_hdr->ip_len);
int ip_len_want = (int)(pkthdr->len - l2len);

if (pkthdr->caplen < l2len + sizeof(*ip_hdr))
return -1;

if ((htons(ip_hdr->ip_off) & (IP_MF | IP_OFFMASK)) == 0 &&
ip_len != ip_len_want) {
ip_hdr->ip_len = htons(ip_len_want);
return 1;
}

return 0;
}

int fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr,
const size_t l2len)
{
int ip_len = ntohs((uint16_t)ip6_hdr->ip_len);
int ip_len_want = (int)(pkthdr->len - l2len - sizeof(*ip6_hdr));

if (pkthdr->caplen < l2len + sizeof(*ip6_hdr))
return -1;

if (ip_len != ip_len_want) {
ip6_hdr->ip_len = htons((uint16_t)ip_len_want);
return 1;
}

return 0;
}

/**
* randomizes the source and destination IP addresses based on a
Expand Down Expand Up @@ -446,8 +476,8 @@ randomize_ipv6(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
/* randomize IP addresses based on the value of random */
dbgx(1, "Old Src IP: %s\tOld Dst IP: %s", srcip, dstip);
if (l3len < (int)sizeof(ipv6_hdr_t)) {
tcpedit_seterr(tcpedit, "Unable to randomize IPv6 header due to packet capture snap length %u",
pkthdr->caplen);
tcpedit_seterr(tcpedit, "Unable to randomize IPv6 header due to packet capture snap length %u: pkt=" COUNTER_SPEC,
pkthdr->caplen, tcpedit->runtime.packetnum);
return TCPEDIT_ERROR;
}

Expand Down
10 changes: 6 additions & 4 deletions src/tcpedit/edit_packet.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ int randomize_iparp(tcpedit_t *tcpedit, struct pcap_pkthdr *pkthdr,
u_char *pktdata, int datalink, const int l3len);

int fix_ipv4_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkdhdr,
ipv4_hdr_t *ip_hdr);
ipv4_hdr_t *ip_hdr, const size_t l2len);

int fix_ipv6_checksums(tcpedit_t *tcpedit, struct pcap_pkthdr *pkdhdr,
ipv6_hdr_t *ip_hdr);
ipv6_hdr_t *ip_hdr, const size_t l2len);

void fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr);
int fix_ipv4_length(struct pcap_pkthdr *pkthdr, ipv4_hdr_t *ip_hdr,
const size_t l2len);

void fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr);
int fix_ipv6_length(struct pcap_pkthdr *pkthdr, ipv6_hdr_t *ip6_hdr,
const size_t l2len);

int extract_data(tcpedit_t *tcpedit, const u_char *pktdata,
int caplen, char *l7data[]);
Expand Down
14 changes: 11 additions & 3 deletions src/tcpedit/tcpedit.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,14 +356,22 @@ tcpedit_packet(tcpedit_t *tcpedit, struct pcap_pkthdr **pkthdr,
}
}

/* ensure IP header length is correct */
if (ip_hdr != NULL) {
needtorecalc |= fix_ipv4_length(*pkthdr, ip_hdr, l2len);
needtorecalc = 1;
} else if (ip6_hdr != NULL) {
needtorecalc |= fix_ipv6_length(*pkthdr, ip6_hdr, l2len);
}

/* do we need to fix checksums? -- must always do this last! */
if ((tcpedit->fixcsum || needtorecalc)) {
if ((tcpedit->fixcsum || needtorecalc > 0)) {
if (ip_hdr != NULL) {
dbgx(3, "doing IPv4 checksum: needtorecalc=%d", needtorecalc);
retval = fix_ipv4_checksums(tcpedit, *pkthdr, ip_hdr);
retval = fix_ipv4_checksums(tcpedit, *pkthdr, ip_hdr, l2len);
} else if (ip6_hdr != NULL) {
dbgx(3, "doing IPv6 checksum: needtorecalc=%d", needtorecalc);
retval = fix_ipv6_checksums(tcpedit, *pkthdr, ip6_hdr);
retval = fix_ipv6_checksums(tcpedit, *pkthdr, ip6_hdr, l2len);
} else {
dbgx(3, "checksum not performed: needtorecalc=%d", needtorecalc);
retval = TCPEDIT_OK;
Expand Down
21 changes: 14 additions & 7 deletions src/tcprewrite.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,26 @@ main(int argc, char *argv[])

/* init tcpedit context */
if (tcpedit_init(&tcpedit, pcap_datalink(options.pin)) < 0) {
errx(-1, "Error initializing tcpedit: %s", tcpedit_geterr(tcpedit));
err_no_exitx("Error initializing tcpedit: %s", tcpedit_geterr(tcpedit));
tcpedit_close(&tcpedit);
exit(-1);
}

/* parse the tcpedit args */
rcode = tcpedit_post_args(tcpedit);
if (rcode < 0) {
err_no_exitx("Unable to parse args: %s", tcpedit_geterr(tcpedit));
tcpedit_close(&tcpedit);
errx(-1, "Unable to parse args: %s", tcpedit_geterr(tcpedit));
exit(-1);
} else if (rcode == 1) {
warnx("%s", tcpedit_geterr(tcpedit));
}

if (tcpedit_validate(tcpedit) < 0) {
tcpedit_close(&tcpedit);
errx(-1, "Unable to edit packets given options:\n%s",
err_no_exitx("Unable to edit packets given options:\n%s",
tcpedit_geterr(tcpedit));
tcpedit_close(&tcpedit);
exit(-1);
}

/* fuzzing init */
Expand All @@ -116,8 +120,9 @@ main(int argc, char *argv[])
#ifdef ENABLE_FRAGROUTE
if (options.fragroute_args) {
if ((options.frag_ctx = fragroute_init(65535, pcap_datalink(dlt_pcap), options.fragroute_args, ebuf)) == NULL) {
err_no_exitx("%s", ebuf);
tcpedit_close(&tcpedit);
errx(-1, "%s", ebuf);
exit(-1);
}
}
#endif
Expand All @@ -129,16 +134,18 @@ main(int argc, char *argv[])
#endif

if ((options.pout = pcap_dump_open(dlt_pcap, options.outfile)) == NULL) {
err_no_exitx("Unable to open output pcap file: %s", pcap_geterr(dlt_pcap));
tcpedit_close(&tcpedit);
errx(-1, "Unable to open output pcap file: %s", pcap_geterr(dlt_pcap));
exit(-1);
}

pcap_close(dlt_pcap);

/* rewrite packets */
if (rewrite_packets(tcpedit, options.pin, options.pout) == TCPEDIT_ERROR) {
err_no_exitx("Error rewriting packets: %s", tcpedit_geterr(tcpedit));
tcpedit_close(&tcpedit);
errx(-1, "Error rewriting packets: %s", tcpedit_geterr(tcpedit));
exit(-1);
}

/* clean up after ourselves */
Expand Down
Binary file modified test/test.rewrite_1ttl
Binary file not shown.
Binary file modified test/test.rewrite_2ttl
Binary file not shown.
Binary file modified test/test.rewrite_3ttl
Binary file not shown.
Binary file modified test/test.rewrite_config
Binary file not shown.
Binary file modified test/test.rewrite_dlthdlc
Binary file not shown.
Binary file modified test/test.rewrite_dltuser
Binary file not shown.
Binary file modified test/test.rewrite_efcs
Binary file not shown.
Binary file modified test/test.rewrite_endpoint
Binary file not shown.
Binary file modified test/test.rewrite_enet_subsmac
Binary file not shown.
Binary file modified test/test.rewrite_fixcsum
Binary file not shown.
Binary file modified test/test.rewrite_fixlen_del
Binary file not shown.
Binary file modified test/test.rewrite_fixlen_pad
Binary file not shown.
Binary file modified test/test.rewrite_fixlen_trunc
Binary file not shown.
Binary file modified test/test.rewrite_l7fuzzing
Binary file not shown.
Binary file modified test/test.rewrite_layer2
Binary file not shown.
Binary file modified test/test.rewrite_mac
Binary file not shown.
Binary file modified test/test.rewrite_mac_seed
Binary file not shown.
Binary file modified test/test.rewrite_mac_seed_keep
Binary file not shown.
Binary file modified test/test.rewrite_mtutrunc
Binary file not shown.
Binary file modified test/test.rewrite_pad
Binary file not shown.
Binary file modified test/test.rewrite_pnat
Binary file not shown.
Binary file modified test/test.rewrite_portmap
Binary file not shown.
Binary file modified test/test.rewrite_range_portmap
Binary file not shown.
Binary file modified test/test.rewrite_seed
Binary file not shown.
Binary file modified test/test.rewrite_sequence
Binary file not shown.
Binary file modified test/test.rewrite_skip
Binary file not shown.
Binary file modified test/test.rewrite_tos
Binary file not shown.
Binary file modified test/test.rewrite_trunc
Binary file not shown.
Binary file modified test/test.rewrite_vlan802.1ad
Binary file not shown.
Binary file modified test/test.rewrite_vlandel
Binary file not shown.
Binary file modified test/test2.rewrite_1ttl
Binary file not shown.
Binary file modified test/test2.rewrite_2ttl
Binary file not shown.
Binary file modified test/test2.rewrite_3ttl
Binary file not shown.
Binary file modified test/test2.rewrite_config
Binary file not shown.
Binary file modified test/test2.rewrite_dlthdlc
Binary file not shown.
Binary file modified test/test2.rewrite_dltuser
Binary file not shown.
Binary file modified test/test2.rewrite_efcs
Binary file not shown.
Binary file modified test/test2.rewrite_endpoint
Binary file not shown.
Binary file modified test/test2.rewrite_enet_subsmac
Binary file not shown.
Binary file modified test/test2.rewrite_fixcsum
Binary file not shown.
Binary file modified test/test2.rewrite_fixlen_del
Binary file not shown.
Binary file modified test/test2.rewrite_fixlen_pad
Binary file not shown.
Binary file modified test/test2.rewrite_fixlen_trunc
Binary file not shown.
Binary file modified test/test2.rewrite_l7fuzzing
Binary file not shown.
Binary file modified test/test2.rewrite_layer2
Binary file not shown.
Binary file modified test/test2.rewrite_mac
Binary file not shown.
Binary file modified test/test2.rewrite_mac_seed
Binary file not shown.
Binary file modified test/test2.rewrite_mac_seed_keep
Binary file not shown.
Binary file modified test/test2.rewrite_mtutrunc
Binary file not shown.
Binary file modified test/test2.rewrite_pad
Binary file not shown.
Binary file modified test/test2.rewrite_pnat
Binary file not shown.
Binary file modified test/test2.rewrite_portmap
Binary file not shown.
Binary file modified test/test2.rewrite_range_portmap
Binary file not shown.
Binary file modified test/test2.rewrite_seed
Binary file not shown.
Binary file modified test/test2.rewrite_sequence
Binary file not shown.
Binary file modified test/test2.rewrite_skip
Binary file not shown.
Binary file modified test/test2.rewrite_tos
Binary file not shown.
Binary file modified test/test2.rewrite_trunc
Binary file not shown.
Binary file modified test/test2.rewrite_vlan802.1ad
Binary file not shown.
Binary file modified test/test2.rewrite_vlandel
Binary file not shown.

0 comments on commit b388cbc

Please sign in to comment.