Pitfalls Using strcat

strcat is a C standard library call that concatenates strings. strncat is a similar call with a notion of bounds checking. Correct use of strcat and strncat can be problematic and it's easy for developers to use these APIs incorrectly. I'll outline some of the problems and show real code in Kali/Debian Linux that uses them incorrectly. Introduction Code review is necessary to aid secure development. Code review is also a primary tool used in vulnerability research. Although application development is popular today, systems languages like C are dominant in Operating Systems and in embedded devices.

The C standard library includes a number of string related APIs. Strings have long been a source of vulnerabilities in C and in this blog post, I'll highlight issues related to the strcat/strncat API.

The strcat/strncat API is defined in the Linux man pages as: char *strcat(char *dest, constchar *src);char *strncat(char *dest, constchar *src, size_tn); The strcat() function appen…

Memory Bugs in Multiple Linux Kernel Drivers using DebugFS

Multiple drivers in the Linux Kernel using the DebugFS follow a bug pattern enabling memory disclosure and corruption of heap allocated memory. Generally, kernel memory is copied back into user space using an incorrect length field leading to memory disclosure.

There's no need for concern. These bugs are almost completely mitigated by the kernel configuration CONFIG_HARDENED_USERCOPY, which is set as a default on many Linux distributions. In the code review below, only 1 bug out of 4 is not mitigated by this configuration. Additionally, these bugs have low impact because DebugFS is normally only enabled by kernel developers. It is unlikely for production kernels to enable this feature.
Introduction The Linux kernel is the heart of the operating system and controls things from device interfacing to process scheduling. DebugFS is a filesystem used internally by kernel developers to provide additional debugging information undesirable in production.

From Wikipedia,

debugfs is a special f…

Linux Kernel Infoleaks

Here are 6 Linux kernel local infoleaks.

InfoSect is available for engagements in code review. Please look at or check out some of our public code review videos on Or, check out our code review training at

1 ==========================


struct ipddp_route
        struct net_device *dev;             /* Carrier device */
        __be32 ip;                       /* IP address */
        struct atalk_addr at;              /* Gateway appletalk address */
        int flags;
        struct ipddp_route *next;


static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt)
        struct ipddp_route *f;

        for(f = ipddp_route_list; f != NULL; f = f->next)
                if(f->ip == rt->ip &&
                   f->at.s_net == rt->a…

ASUS DSL-AC3100 Router Firmware BCM Kernel Driver Bug

Time for some kernel bugs.

// misc. ioctl calls come to here. (flash, led, reset, kernel memory access, etc.)
static int board_ioctl( struct inode *inode, struct file *flip,
                       unsigned int command, unsigned long arg )
    int ret = 0;
    BOARD_IOCTL_PARMS ctrlParms;
    unsigned char ucaMacAddr[NVRAM_MAC_ADDRESS_LEN];
    unsigned char ucaDectRfpi[NVRAM_ARCADYAN_DECT_RFPI_LEN];
    unsigned char ucaDectRxtun[NVRAM_ARCADYAN_DECT_RXTUN_LEN];

    switch (command) {
    case BOARD_IOCTL_FLASH_WRITE:         if (copy_from_user((void*)&ctrlParms, (void*)arg, sizeof(ctrlParms)) == 0) {
            switch (ctrlParms.action) {             case SCRATCH_P…

ASUS DSL-AC3100 Router Firmware radvd Bugs

Lets look at packet processing in the router code

                case ND_OPT_RDNSS_INFORMATION:
                        rdnssinfo = (struct nd_opt_rdnss_info_local *) opt_str;
                        count = rdnssinfo->nd_opt_rdnssi_len;

                        /* Check the RNDSS addresses received */
                        switch (count) {
                                case 7:

Now opt_str is the options part of the packet. Does the code check that the packet is big enough to account for these options? No. There are a bunch of cases like this. All lead to out of bounds memory access.
Lets look at the current radvd source from a recent Linux distro
                case ND_OPT_RDNSS_INFORMATION: {                         char rdnss_str[INET6_ADDRSTRLEN];                         struct AdvRDNSS *rdnss = 0;                         struct nd_opt_rdnss_info_local *rdnssinfo = (struct nd_opt_rdnss_info_local *)opt_str;                         if (len < sizeof(*rdnssinfo))                      …

ASUS DSL-AC3100 Router Firmware sendpackets Bug

This is a tiny bug, but it's still a bug nevertheless. strncpy is not guaranteed to NUL terminate if the max buf size is reached. The code below doesn't explicity NUL terminate the strncpy to iface. It's probably not been triggered because the stack is likely to be clean when the program reaches the strncpy. However, it's not guaranteed.

main (int argc, char **argv)
  pcap_t *fp;
  char errbuf[PCAP_ERRBUF_SIZE];
  int i;
  int j;
  int nstreams;

  int cnt;
  int tdelay;
  char iface[32];
  int patternlen;
  int opt;
  struct timeval tstart;
  struct timeval t;
  struct timeval tint;
  int pdone;
  int pbusy;

  iface[0] = 0;
  patternlen = 0;
  nstreams = 0;
  tdelay = 0;

  while ((opt = getopt (argc, argv, "i:t:c:p:")) != -1)
      switch (opt)
        case 'i':
          strncpy (iface, optarg, 32);

ASUS DSL-AC3100 Router Firmware PPP bug

Lets look at the following code in the current version of ppp on Linux.

static void
cbcp_recvreq(us, pckt, pcktlen)
    cbcp_state *us;
    u_char *pckt;
    int pcktlen;
    u_char type, opt_len, delay, addr_type;
    char address[256];
    int len = pcktlen;

    address[0] = 0;

    while (len >= 2) {
        dbglog("length: %d", len);

        GETCHAR(type, pckt);
        GETCHAR(opt_len, pckt);
        if (opt_len < 2 || opt_len > len)

        if (opt_len > 2)
            GETCHAR(delay, pckt);

        us->us_allowed |= (1 << type);

        switch(type) {
        case CB_CONF_NO:
            dbglog("no callback allowed");

        case CB_CONF_USER:
            dbglog("user callback allowed");             if (opt_len > 4) {                 GETCHAR(addr_type, pckt);                 memcpy(address, pckt, opt_len - 4);                 address[opt_len - 4] = 0;                 if (address[0])                     dbglog("a…