Command Injection
相关函数和预定义变量:
函数 | 作用 |
---|---|
$_POST | 接收来自 POST方式请求的数据。通常在提交表单时,使用 POST 请求来传递数据。 |
$_REQUEST | 接收来自 GET、POST 和 COOKIE 请求的数据,使用 $_REQUEST 可以同时接收三种类型请求的数据。 |
isset( ) | 检查变量是否已定义且不为 null。当变量存在且不为 null 时,isset() 函数返回 true,否则返回 false。 |
php_uname( ) | 获取服务器的操作系统信息。 |
stristr( ) | 用于在字符串中查找子字符串并返回匹配到的第一个子字符串及其后面的所有字符 。 |
shell_exec( ) | 执行系统命令的函数,它将命令字符串作为参数并返回命令输出的结果,需要用echo进行输出 。 |
array_keys( ) | 用于返回数组中所有的键名,组成一个新的数组。如果指定了第二个参数,则只返回与该参数相等的键名。 |
str_replace( ) | 用于在字符串中查找特定的字符或字符串,并将其替换为新的字符或字符串。 |
stripslashes( ) | 用于删除字符转义符反斜杠(\)。 |
explode( ) | 用于将一个字符串拆分成多个字符串数组,根据指定的分隔符进行拆分。 |
is_numeric | 用于判断一个变量是否为数字或数字字符串。 |
sizeof( ) | 用来计算数组中元素的数量。 |
Low Command Injection Source
<?php
//通过$_POST接受传递过来的数据,并通过isset函数判断提交过来的数据是否为空,若不为空进入二层判断。
if( isset( $_POST[ 'Submit' ] ) ) {
//通过$_REQUEST接受传递的字符串,将其赋予新的变量。
$target = $_REQUEST[ 'ip' ];
/*php_uname获取操作系统类型,返回的是一个字符串
利用这个stristr函数,查找在该字符串中查找是否存在“Windows NT”子串,决定Ping的次数
主要是因为如果为linux操作系统的话,不指定次数会一直ping。*/
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
//如果不是Windows操作系统,则指定ping 4次,利用shell_exec函数执行系统命令
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
/*整个代码没有对字符串的内容进行过滤,这就导致可以利用&,&&,|等字符拼接字符串,从而执行危险指令。
例如当他接受到一个字符串为:"127.0.0.1 && whoami"时,在Windows系统下他将执行:"ping 127.0.0.1 && whoami";
这就表明他会同时执行whoami指令。
?>
Medium Command Injection Source
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
/* 这里是一个数组的定义,定义了一个数组赋予了$substitutions变量,数组中包含两个元素
例如:“&&”=>"",&&叫做键名,“ ”叫做值,这个数组实现的是黑名单操作*/
$substitutions = array(
'&&' => '',
';' => '',
);
/*array_keys这个函数返回该数组中所有的键名,这里就是“&&”和“;”
而str_replace函数会将$target变量得到的字符串中,含有定义黑名单中键名的字符串替换为空,
所以这一步就过滤掉了“&&”和“;”比如$target="127.0.0.1 && whoami",那经过该函数以后中间的"&&"就为空了
变量就变成了$target="127.0.0.1 whoami"所以就不能执行whoami指令了,当然可以通过拼接例如“&”执行*/
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.下面的代码与low等级一致,不难理解。
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
/*整个代码仅对字符串"&&",";"进行过滤,但是也可以通过其他的进行绕过。
例如当他接受到一个字符串为:"127.0.0.1 & whoami"时,在Windows系统下他将执行:"ping 127.0.0.1 & whoami";
这就表明他会同时执行whoami指令。*/
?>
High Command Injection Source
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
/* 这里也是一个数组的定义,定义了一个数组赋予了$substitutions变量,只是数组中的元素更多,
换句话说就是扩大了黑名单而已。没有其他技术。*/
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
/*同样,str_replace函数会将$target变量得到的字符串中含有定义黑名单中键名的字符串替换为空,
所以这一步就过滤掉了更多的字符串,比如:'&',';','||'等,那经过该函数以后字符串中含有这些字符的地方均就为空了
这里需要注意的是 '| ' => ''它替换的是'|+空格',所以可以利用'|'进行绕过黑名单。
比如执行"127.0.0.1|whoami 或者127.0.0.1 |whoami"*/
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.下面的代码与low等级一致,不难理解
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
/*整个代码仅对字符串更多的字符串进行过滤,但是它没有对|进行过滤,它过滤的是|+空格,所以可以利用|或空格+|绕过。
例如当他接受到一个字符串为:"127.0.0.1|whoami"时,在Windows系统下他将执行:"ping 127.0.0.1|whoami";而|运算符表示无论前面执行是否为真后面命令都会执行,这就表明他会同时执行whoami指令。*/
?>
Impossible Command Injection Source
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
/* $_SESSION 是 PHP 中的一个超全局变量,用于存储会话信息,可以使用该变量中存储的信息来验证用户身份等操作
这个身份验证,主要是为了防止CSRF跨站请求伪造攻击的*/
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
/* stripslashes函数用于删除字符转义符反斜杠(\),他会以原字符串内容显示,而不会对里面的特殊字符进行转义。
比如,"hello \"world\""这个字符串,他会显示出"hello "world" "把内层的""保留。*/
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
/* explode函数是以指定字符串,分割原有的字符串的函数。这里它会以 . 来分割接受到的字符串,最终将这些分割后的字串存放到一个数组中。比如$target="127.0.0.1"这个字符串,那么就会被分割成127 0 0 1 这4个字符并存入数组中*/
$octet = explode( ".", $target );
/* is_numeric是检查每个被分割完的字符串是否为数字。
这个判断语句表明如果都为数字并且数组$octet的元素为4才会为真,否则为假。*/
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
/* 当判断为真以后再对这四个分开的字符串进行重组,组成一个新的只含有4个数字的字符串重新赋予$target,
所以$target最终接受的一定是一个含有4个纯数字拼接成的字符串,所以这个难度无法绕过。*/
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// Determine OS and execute the ping command.下面的代码与low等级一致,不难理解
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
函数:
- stristr() 函数:
以下是一个使用 stristr() 函数查找子字符串的示例:
$str = "Hello World!";
$sub_str = "World";
$matched_str = stristr($str, $sub_str);
echo $matched_str;
/*stristr() 函数在 $str 字符串中查找子字符串 “World”,并将匹配到的字符串 “World!” 返回保存在 $matched_str 变量中,最后将该字符串打印在页面上。*/
- array_keys()
以下是一个使用 array_keys() 函数获取数组键名的示例:
$arr = array("name" => "John", "age" => 30, "gender" => "male");
$keys = array_keys($arr);
print_r($keys);
/*上述代码中,array_keys() 函数接受一个数组参数 $arr,返回一个由该数组中所有键名所组成的新数组 $keys
最后使用 print_r() 函数将 $keys 数组打印在页面上。*/
- str_replace()
以下是一个使用 str_replace() 函数替换字符串中的字符或字符串的示例:
$str = "Hello World!";
$new_str = str_replace("World", "PHP", $str);
echo $new_str;
/*str_replace() 函数将字符串 $str 中的 “World” 替换为 “PHP”,
并将替换后的新字符串保存在变量 $new_str 中,最后将其打印在页面上。*/
- stripslashes()
以下是一个使用 stripslashes() 函数还原字符串的示例:
$str = "I\'m a \"good\" programmer.";
$new_str = stripslashes($str);
echo $new_str;
/*上述代码中,$str 变量是一个包含转义字符的字符串。
使用 stripslashes() 函数可以将字符串还原为 “I’m a “good” programmer.”
并将还原后的字符串保存在变量 $new_str 中,最后将其打印在页面上。*/