1 /*
2           http://www.unhide-forensics.info
3 */
4 
5 /*
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <errno.h>
26 
27 #include <string.h>
28 #include <unistd.h>
29 // #include <time.h>
30 
31 #include "unhide-output.h"
32 #include "unhide-tcp.h"
33 
34 /*
35  * These are simplified hash set to store the ports that:
36  *
37  * - are visible to netstat
38  * - are found to be hidden by us
39  * - we want to check
40  *
41  * A value of 0 means the port is NOT in the set, and a value != 0 means
42  * otherwise.
43  */
44 
45 static char netstat_ports[65536];
46 static char hidden_ports[65536];
47 static char check_ports[65536];
48 
49 /* Fill netstat_ports with the ports netstat see as used for protocol proto. */
get_netstat_ports(enum Proto proto)50 static void get_netstat_ports(enum Proto proto)
51 {
52    FILE *fp;
53    int port;
54 
55    if (TCP == proto)
56    {
57       fp=popen (tcpcommand1, "r");
58    }
59    else
60    {
61       fp=popen (udpcommand1, "r");
62    }
63 
64    if (fp == NULL)
65    {
66       die(unlog, "popen failed to open netstat to get the ports list");
67    }
68 
69    memset(netstat_ports, 0, sizeof(netstat_ports));
70 
71    errno = 0;
72    while (!feof(fp))
73    {
74       if (fscanf(fp, "%i\n", &port) == EOF && errno != 0)
75       {
76          die(unlog, "fscanf failed to parse int");
77       }
78 
79       netstat_ports[port] = 1;
80    }
81 
82    pclose(fp);
83 }
84 
85 
86 /*
87  * Check a list of ports against what netstat report as used ports.
88  *
89  * All ports that are not reported as used by netstat are opened, binded and
90  * put in listen state (for the TCP proto). If any of that operations fail with
91  * an EADDRINUSE, it's reported as a port hidden to netstat.
92  */
check(enum Proto proto)93 static void check(enum Proto proto)
94 {
95    int i;
96    int protocol;
97 
98    if (proto == TCP)
99       protocol = SOCK_STREAM;
100    else if (proto == UDP)
101       protocol = SOCK_DGRAM;
102    else
103       abort();
104 
105    memset(hidden_ports, 0, sizeof(hidden_ports));
106    hidden_found = 0;
107 
108    get_netstat_ports(proto);
109    for (i = 0; i < 65536; i++)
110    {
111       int fd;
112       int reuseaddr;
113       struct sockaddr_in addr;
114 
115       /*
116       * skip if is not a port to check or is already visible to
117       * netstat
118       */
119       if (!check_ports[i] || netstat_ports[i])
120       {
121          continue;
122       }
123 
124       fd = socket(AF_INET, protocol, 0);
125       if (fd == -1)
126       {
127          die(unlog, "socket creation failed");
128       }
129 
130       reuseaddr = 1;
131       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr,
132                      sizeof(reuseaddr)) != 0)
133       {
134          die(unlog, "setsockopt can't set SO_REUSEADDR");
135       }
136 
137       addr.sin_family = AF_INET;
138       addr.sin_addr.s_addr = INADDR_ANY;
139       addr.sin_port = htons(i);
140 
141       /*
142        * if we can't bind or listen because the address is used, the
143        * port is asumed to be used and added to the hidden_ports list
144        * because we only check for ports not visible by netstat.
145        * If we can bind them, we remove them from the check_ports
146        * list so we don't try to check them again if a new pass is
147        * performed in the future.
148        */
149       errno = 0;
150       if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) != 0 ||
151                       (proto == TCP && listen(fd, 1) != 0))
152       {
153          if (errno == EADDRINUSE)
154          {
155             hidden_ports[i] = 1;
156             hidden_found++;
157          }
158          else
159          {
160             warnln(verbose, unlog, "bind failed, maybe you are not root?");
161             check_ports[i] = 0;
162          }
163       }
164       else
165       {
166          check_ports[i] = 0;
167       }
168 
169       close(fd);
170    }
171 }
172 
173 
174 /*
175  * Print ports not visible to netstat but that are being used.
176  *
177  * The check for hidden ports is retried to minimize false positives, see
178  * comments inside the function for details.
179  */
print_hidden_ports(enum Proto proto)180 void print_hidden_ports(enum Proto proto)
181 {
182    /* reset the list of ports to check (we start wanting to check all of
183     * them) and the list of hidden ports (none is hidden until we prove
184     * otherwise)
185     */
186    memset(check_ports, 1, sizeof(check_ports));
187    memset(hidden_ports, 0, sizeof(hidden_ports));
188 
189    /*
190     * Double-check to minimize false positives.
191     *
192     * For very short lived connections we have a race condition between
193     * getting the output from netstat and trying to open the port
194     * ourselves. To minize this problem we check again the ports reported
195     * as hidden. If in the next run of netstat those ports are not present
196     * anymore, is fairly safe to asume they were false positives.
197     */
198    check(proto);
199    if (hidden_found)
200    {
201       memcpy(check_ports, hidden_ports, sizeof(hidden_ports));
202       check(proto);
203    }
204 
205    if (hidden_found)
206    {
207       int i;
208       for (i = 0; i < 65536; i++)
209       {
210          if (hidden_ports[i])
211          {
212             print_port(proto, i);
213          }
214       }
215    }
216 }
217 
218 
219