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…

ASUS DSL-AC3100 Router Firmware DHCP Bug

It's great that ASUS makes the GPL firmware source for their routers easy to download. I wish more vendors would do this.

Unfortunately, it didn't take more than a few minutes of auditing to come across the DHCPd code. Lets look at the original non ASUS code in wide-dhcp-server.

dhcp6_get_options(p, ep, optinfo)
        struct dhcp6opt *p, *ep;
        struct dhcp6_optinfo *optinfo;
        struct dhcp6opt *np, opth;
        int i, opt, optlen, reqopts, num;
        u_int16_t num16;
        char *bp, *cp, *val;
        u_int16_t val16;
        u_int32_t val32;
        struct dhcp6opt_ia optia;
        struct dhcp6_ia ia;
        struct dhcp6_list sublist;
        int authinfolen;

        bp = (char *)p;
        for (; p + 1 <= ep; p = np) {
                struct duid duid0;

                 * get the option header.  XXX: since there is no guarantee
                 * about the header alignment, we need to make a local copy.

Month of Kali Off-By-Ones #1 #2 # 3 #4

The following 4 code snippets have classic off-by-one bugs. They don't explicitly nul terminate strings after a strncpy. If strncpy reaches its buffer max, it won't nul terminate. In fact, strncpy's behaviour is quite problematic and prone to this type of bug so OpenBSD introduced strlcpy many years ago and other OSs have done the same.

source package: libaria #1

char myWaitingForDir[2048];

AREXPORT void ArClientFileLister::changeToAbsDir(const char *dir)
  strncpy(myWaitingForDir, dir, sizeof(myWaitingForDir));
  //printf("Getting %s\n", myWaitingForDir);
  std::string waitingFor = myWaitingForDir;
  //myClient->requestOnceWithString("getDirListing", waitingFor.c_str());

source package: xenomai #2

static inline void xntimer_set_name(xntimer_t *timer, const char *name)
        strncpy(timer->name, name, sizeof(timer-&g…

2 tiny bugs in sqlmap

I was at SecTalks-Canberra tonight and a workshop was being given on SQL injection. The speaker was talking about sqlmap, so I thought instead of spending the time doing the actual workshop content, which was quite good, I thought would have a quick look at the sqlmap code.

int main(int argc, char **argv) {
...         unsigned int max_data_size; ...         // parse command line options         for (opt = 1; opt < argc; opt++) {                 if (argv[opt][0] == '-') {                         switch(argv[opt][1]) { ...                                 case 's':                                         if (opt + 1 < argc) {                                                 max_data_size = atol(argv[opt + 1]);                                         }                                         break;
...         in_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE);         out_buf = (char *) malloc(max_data_size + ICMP_HEADERS_SIZE); +++ integer overf…