Network Programming 2
Network Programming 2
int sock;
sock = socket( PF_INET,
SOCK_DGRAM,
0);
if (sock<0) { /* ERROR */ }
mysock = socket(PF_INET,SOCK_DGRAM,0);
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( 1234 );
myaddr.sin_addr = htonl( INADDR_ANY );
Michael J. Donahoo
Kenneth L. Calvert
Computer Chat
How do we make computers talk?
192.18.22.13 209.134.16.123
Transport Protocols
Best-effort not sufficient!
Add services on top of IP
User Datagram Protocol (UDP)
Data checksum
Best-effort
Transmission Control Protocol (TCP)
Data checksum
Reliable byte-stream delivery
Flow and congestion control
Ports
Identifying the ultimate destination
IP addresses identify hosts
Host has many applications
Ports (16-bit identifier)
Application WWW E-mail Telnet
Port 80 25 23
192.18.22.13
Socket
Socket reference
File (socket) descriptor in UNIX
Socket handle in WinSock
struct sockaddr
{
Generic
unsigned short sa_family; /* Address family (e.g., AF_INET) */
char sa_data[14]; /* Protocol-specific address information */
};
struct sockaddr_in
{
unsigned short sin_family;/* Internet protocol (AF_INET) */
IP Specific
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Assign a port to socket
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
echoServAddr.sin_family = AF_INET; /* Internet address family */
echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);/* Any incoming interface */
echoServAddr.sin_port = htons(echoServPort); /* Local port */
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
/* Mark the socket so it will listen for incoming connections */
if (listen(servSock, MAXPENDING) < 0)
DieWithError("listen() failed");
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
for (;;) /* Run forever */
{
clntLen = sizeof(echoClntAddr);
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
echoServAddr.sin_family = AF_INET; /* Internet address family */
echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */
echoServAddr.sin_port = htons(echoServPort); /* Server port */
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
echoStringLen = strlen(echoString); /* Determine input length */
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Set socket to listen
3. Communicate
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
/* Receive message from client */
if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0)
DieWithError("recv() failed");
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Set socket to listen
3. Communicate
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Client/Server Interaction
close(sock); close(clntSocket)
Client Server
1. Create a TCP socket 1. Create a TCP socket
2. Establish connection 2. Bind socket to a port
3. Communicate 3. Set socket to listen
4. Close the connection 4. Repeatedly:
a. Accept new connection
b. Communicate
c. Close the connection
TCP Tidbits
Client must know the server’s address and port
Server only needs to know its own port
No correlation between send() and recv()
Client Server
send(“Hello Bob”)
recv() -> “Hello ”
recv() -> “Bob”
send(“Hi ”)
send(“Jane”)
recv() -> “Hi Jane”
Closing a Connection
close() used to delimit communication
Analogous to EOF
Echo Client Echo Server
send(string)
recv(buffer)
while (not received entire string) while(client has not closed connection)
recv(buffer) send(buffer)
print(buffer) recv(buffer)
close(socket)
close(client socket)
Constructing Messages
0 77 0 111 0 109 0 10
M o m \n
3 77 111 109
Primitive Types
Integer
Strings of character encoded decimal digits
49 55 57 57 56 55 48 10
‘1’ ‘7’ ‘9’ ‘9’ ‘8’ ‘7’ ‘0’ \n
Variable-length fields
M i k e 1 2 \n
“Beware the bytes of padding”
-- Julius Caesar, Shakespeare
Architecture alignment restrictions
Compiler pads structs to accommodate
struct tst {
short x;
int y; x [pad] y z [pad]
short z;
};
Problem: Alignment restrictions vary
Solution: 1) Rearrange struct members
2) Serialize struct by-member
Sockets Programming
Socket to me!
Application
Network API
by
e d
struct sockaddr { U s
uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
robster.sa_family = AF_DAVESKIDS;
robster.sa_data[0] = 3;
l y !
• For AF_INET we need: on
v 4
I P
– 16 bit port number
– 32 bit IP address
Common Mistake:
Ignoring Network
Byte Order Netprog: Sockets API
22
Network Byte Order Functions
‘h’ : host byte order ‘n’ : network byte order
‘s’ : short (16bit) ‘l’ : long (32bit)
uint16_t htons(uint16_t);
uint16_t ntohs(uint_16_t);
uint32_t htonl(uint32_t);
uint32_t ntohl(uint32_t);
sin_addr
sa_data
sin_zero
mysock = socket(PF_INET,SOCK_STREAM,0);
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons( portnum );
myaddr.sin_addr = htonl( ipaddress);
myaddr.port = htons(0);
• Connectionless (UDP)
– send()
– recv()
Netprog: Sockets API
33
Socket Hijacking
Author:
Neelay S. Shah
Senior Software Security Consultant
Foundstone Professional Services
Rudolph Araujo
Technical Director
Foundstone Professional Services
Socket Hijacking
Abstract
Sockets are one of the most widely used inter-process communication primitives for clientserver applications
due to a combination of the following factors. Sockets:
What application developers need to be aware of is that attackers can target these same client-server
applications by “hijacking” the server socket. Insecurely bound server sockets allow an attacker to bind his /
her own socket on the same port, gaining control of the client connections and ultimately allowing the
attacker to successfully steal sensitive application user information as well as launch denial of service attacks
against the application server.
In this white paper we discuss the socket hijacking vulnerability on Windows, the impact of the vulnerability
and what it takes to successfully exploit the vulnerability. We also review existing mitigating factors, the
cause of the vulnerability as well as its remediation.
This white paper is intended towards all software developers, architects, testers and system administrators.
Foundstone has released a free tool “Foundstone Socket Security Auditor” which identifies the insecurely
bound sockets on the local system. The free tool can be found at http://www.foundstone.com/us/resources-
free-tools.asp.
2 www.foundstone.com | 1.877.91.FOUND
Socket Hijacking
Discussion
Sockets are identified by an IP address and port number. Port number can be in the range of 0 to 65535
whereas the IP address can be any of the underlying IP addresses associated with the system including the
loopback address. The socket library also supports a wildcard IP address (INADDRY_ANY) that binds the
socket to the specified port on all underlying IP addresses associated with the system. This feature is
extremely attractive (and hence widely used) from an application development point of view for the following
reasons:
The application developer does not need to write code to programmatically enumerate the underlying
IP addresses (associated with the system) and then use one or more of them to bind the listening
server socket.
In scenarios where the server has multiple network routable IP addresses, there is no additional
overhead needed for exchanging the server’s listening IP address with the client. The client could use
any one of the server’s network routable address and connect successfully to the server.
However, it is possible to bind more than one socket to the same port. For instance, there could be an
application server with a listening socket bound to INADDR_ANY:9000 and another malicious application
server with its listening socket bound to 172.23.20.1101:9000. Note that both the applications are running on
1
Assuming 172.23.20.110 is the IP addresses associated with the system.
Socket Hijacking
the same system, the only difference (as far as their listener sockets are concerned) is the binding of the
listener socket. The legitimate application server has bound its listening socket to the wildcard IP address
(INADDR_ANY) whereas the malicious application server has bound its listening socket to a specific IP
address (172.23.20.110).
When the client initiates a connection to the server, the client needs to use the routable address
(172.23.20.110) and the port (9000) to connect to the server. When the connection request reaches the
server, it is the responsibility of the network stack on the server to forward the connection to the listener.
Now there are two sockets listening on the same port (9000), and the network stack can forward the
connection to only one of the listening sockets. Thus, the network stack needs to resolve this conflict and
choose one of the two sockets to forward the connection to.
For this, the network stack inspects the incoming client request which is targeted for 172.23.20.110:9000.
Based on this information, the network stack resolves in favor of the malicious application since it had bound
its listening socket specifically on 172.23.20.110. Thus the malicious application gets the client connection
and can communicate further with the client. This is referred to as “Socket Hijacking” i.e. the malicious
application has successfully hijacked the legitimate application’s listener socket.
The following figure illustrates the client-server communication setup in the event of socket hijacking:
4 www.foundstone.com | 1.877.91.FOUND
Socket Hijacking
Now that we understand and have discussed “socket hijacking” in detail, let’s turn our focus towards the
impact of the socket hijacking vulnerability; or in other words what damage an attacker can perform by
exploiting the socket hijacking vulnerability.
Hijacking the listener socket of the legitimate server essentially allows the attacker to setup a “spoof
server” and hijack client connections without having to poison the client application in any way i.e. the client
application still connects to the same IP address and the port as before however the attacker gets hold of the
client connection. Having received the client connection, the attacker will then be in a position to potentially
carry out much more damaging things such as:
Information Disclosure – Depending on the transport security primitives and the actions the client
and the server carry out based on the messages on the socket, the attacker could gain knowledge of
sensitive data such as user credentials and even launch man-in-the-middle attacks.
Denial of Service – The real server has no notification of the client connection and as such the
attacker would be successful in causing denial of service to legitimate client(s).
5 www.foundstone.com | 1.877.91.FOUND
Socket Hijacking
So the next question is: “What does the attacker need in order to successfully exploit this vulnerability?”
Following are the key considerations and the mitigating factors with respect to successful exploitation of this
vulnerability.
The attacker needs to have sufficient access to the system with the vulnerable application. The
attacker does not need to have privileged access but needs to be able to execute his malicious
application on the system.
On Windows Server 2003 and later a default ACL is applied to all sockets and as such a limited rights
user cannot hijack a socket opened by a different user unless the application explicitly used an
insecure ACL while creating the socket.
Ports 0-1023 are privileged ports on Windows XP SP2 & later. On these operating systems, the
attacker would need administrator/super-user privileges to hijack sockets which are bound to ports in
the range 0-1023.
The vulnerability is introduced due to binding the socket insecurely. Let us look at the signature of insecure
invocation of the “bind” API which is used to bind the socket to the underlying IP address and port. Since
the socket is bound to wildcard IP Address (INADDR_ANY), this code snippet is susceptible to “socket
hijacking” on Windows
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
service.sin_port = htons(9000);
6 www.foundstone.com | 1.877.91.FOUND
Socket Hijacking
Listener sockets must be bound securely by turning on the exclusive address use option
(SO_EXCLUSIVEADDRUSE) on the socket so that an attacker cannot hijack the server socket. The following
code snippet shows the secure binding of a listener socket:
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.S_un.S_addr = ::htonl(INADDR_ANY);
service.sin_port = htons(9000);
7 www.foundstone.com | 1.877.91.FOUND
Socket Hijacking
Acknowledgements
Rudolph Araujo provided significant support with reviewing the white paper.
Foundstone® Professional Services, a division of McAfee. Inc., offers expert services and education to help
organizations continuously and measurably protect their most important assets from the most critical threats.
Through a strategic approach to security, Foundstone identifies and implements the right balance of
technology, people, and process to manage digital risk and leverage security investments more effectively.
The company’s professional services team consists of recognized security experts and authors with broad
security experience with multinational corporations, the public sector, and the US military.
References
1. Socket Hijacking - Chapter 15, Writing Secure Code Vol. 2, Michael Howard et. al. ISBN: 0-7356-1722-8
sendto() recvfrom()
close() close()
socket structures Dealing with IP Addresses
struct sockaddr: Holds socket address information for int inet_aton(const char *cp, struct in_addr *inp);
many types of sockets Example usage:
struct sockaddr {
unsigned short sa_family; //address family AF_xxx struct sockaddr_in my_addr;
unsigned short sa_data[14]; //14 bytes of protocol addr my_addr.sin_family = AF_INET;
}
my_addr.sin_port = htons(MYPORT);
struct sockaddr_in: A parallel structure that makes it inet_aton(“10.0.0.5”,&(my_addr.sin_addr));
easy to reference elements of the socket address memset(&(my_addr.sin_zero),'\0',8);
struct sockaddr_in {
short int sin_family; // set to AF_INET inet_aton() gives non-zero on success and zero on failure
unsigned short int sin_port; // Port number
struct in_addr sin_addr; // Internet address To convert binary IP to string: inet_noa()
unsigned char sin_zero[8]; //set to all zeros
} printf(“%s”,inet_ntoa(my_addr.sin_addr));
sockfd is the socket descriptor returned by socket() sockfd is the socket descriptor returned by socket()
my_addr is pointer to struct sockaddr that contains information serv_addr is pointer to struct sockaddr that contains
about your IP address and port information on destination IP address and port
addrlen is set to sizeof(struct sockaddr) addrlen is set to sizeof(struct sockaddr)
returns -1 on error returns -1 on error
my_addr.sin_port = 0; //choose an unused port at random At times, you don't have to bind() when you are using
my_addr.sin_addr.s_addr = INADDR_ANY; //use my IP addr connect()
listen() - Call me please! accept() - Thank you for calling !
Waits for incoming connections accept() gets the pending connection on the port
int listen(int sockfd, int backlog); you are listen()ing on
sockfd is the socket file descriptor returned by socket() int accept(int sockfd, void *addr, int *addrlen);
backlog is the number of connections allowed on the sockfd is the listening socket descriptor
incoming queue information about incoming connection is stored in
listen() returns -1 on error addr which is a pointer to a local struct sockaddr_in
Need to call bind() before you can listen() addrlen is set to sizeof(struct sockaddr_in)
accept returns a new socket file descriptor to use for
this accepted connection and -1 on error
send() and recv() - Let's talk! send() and recv() - Let's talk!
The two functions are for communicating over stream int recv(int sockfd, void *buf, int len, int flags);
sockets or connected datagram sockets. sockfd is the socket descriptor to read from
int send(int sockfd, const void *msg, int len, int flags); buf is the buffer to read the information into
sockfd is the socket descriptor you want to send data to len is the maximum length of the buffer
(returned by socket() or got with accept()) set flags to 0 for now
msg is a pointer to the data you want to send recv() returns the number of bytes actually read into the
len is the length of that data in bytes buffer or -1 on error
set flags to 0 for now If recv() returns 0, the remote side has closed connection
on you
sent() returns the number of bytes actually sent (may be less
than the number you told it to send) or -1 on error
sendto() and recvfrom() - DGRAM style close() - Bye Bye!
int sendto(int sockfd, const void *msg, int len, int
flags, const struct sockaddr *to, int tolen);
int close(int sockfd);
to is a pointer to a struct sockaddr which contains the
Closes connection corresponding to the socket
destination IP and port
descriptor and frees the socket descriptor
tolen is sizeof(struct sockaddr)
Will prevent any more sends and recvs
int recvfrom(int sockfd, void *buf, int len, int flags,
struct sockaddr *from, int *fromlen);
from is a pointer to a local struct sockaddr that will be filled
with IP address and port of the originating machine
fromlen will contain length of address stored in from
#define SIN6_LEN
struct sockaddr_in6 {
u_int8_t sin6_len;
u_int8_t sin6_family;
BSD 4.4-based: u_int16_t sin6_port;
u_int32_t sin6_flowinfo;
struct sockaddr_in6 sin6_addr;
u_int32_t sin6_scope_id;
};
IPv6 Socket Addresses
• Most system calls that pass in or receive socket
addresses use a generic (struct sockaddr *)
– Cast your specific type of sockaddr_* to a sockaddr before
passing in/out
– Generic struct sockaddr is not large enough to hold IPv6
socket address
– Define new generic sockaddr_storage which has enough
space to hold largest sockaddr system supports
• Has ss_family member that overlaps sin_family & sin6_family
• Usually defined in /usr/include/sys/socket.h
IPv6 Socket Addresses
sockaddr_storage
struct sockaddr_storage ss;
int ss_len;
switch (ss.ss_family) {
case AF_INET:
sin = (struct sockaddr_in *)&ss;
ss_len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
sin6 = (struct sockaddr_in6 *)&ss;
ss_len = sizeof(struct sockaddr_in6);
break;
[...]
}
ip_output() ip6_output()
Auto-tunnel driver
To Link-Layer (prepend IPv4 header) To Link-Layer
Dual-Stacked Nodes
Receiving IPv4 and IPv6 Packets
AF_INET AF_INET AF_INET6 AF_INET6
SOCK_STREAM SOCK_DGRAM SOCK_STREAM SOCK_DGRAM
Auto-tunnel driver
(removes IPv4 header)
Version = 6
ip_input() ip6_input()
memset(&hints, 0, sizeof(hints));
hints.ai_family = PF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo("www.kame.net", "http", &hints, &res0);
[...]