├── README.md ├── README.zh-cn.md ├── df-bypass.py ├── img ├── 1.png ├── 2.png └── 3.jpg └── putenv-ld_preload.py /README.md: -------------------------------------------------------------------------------- 1 | # ScanDF 2 | 3 | [中文版本(Chinese version)](README.zh-cn.md) 4 | 5 | >The purpose of this script is to bypass disablefund, provide some useful information, and dig the hook function of PHP extension. 6 | 7 | ### df-bypass.py 8 | 9 | Use: Python df-bypass.py - U URL (phpinfo information) 10 | 11 | EG1: Test [geek challenge 2019] rce me 12 | 13 | ![image-20210701161616342](img/1.png) 14 | 15 | You can directly use the hook function to bypass the DL - runtime load a PHP extension 16 | 17 | EG2: [Blue Hat Cup 2021] one pointer PHP 18 | 19 | ![image-20210701165831589](img/2.png) 20 | 21 | Direct hit FPM modify ant sword source code!! 22 | 23 | ### putenv-ld_preload.py 24 | 25 | By bypassing the putenv hook function, we can scan the available functions, load more plug-ins and use them better. It can be used with DF bypass.py 26 | 27 | Use: Python putenv LD_ Preload.py (scan the internal value function of the current PHP environment by default) 28 | 29 | ![QQ图片20210701172608](img/3.jpg) 30 | 31 | python putenv-ld_ Preload.py module 32 | 33 | Test the function provided by the module, which can better bypass. 34 | 35 | >reference resources: 36 | > 37 | >https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions 38 | > 39 | >https://blog.bi0s.in/2019/10/26/Web/bypass-disable-functions/ 40 | > 41 | >https://www.anquanke.com/post/id/197745 42 | 43 | -------------------------------------------------------------------------------- /README.zh-cn.md: -------------------------------------------------------------------------------- 1 | # ScanDF 2 | 3 | >该脚本是为了绕过disablefuntion 提供一些可用的信息,和挖php扩展的hook函数。 4 | 5 | ### df-bypass.py 6 | 7 | 使用:python df-bypass.py -u url(phpinfo信息) 8 | 9 | eg1:测试[极客大挑战 2019]RCE ME 10 | 11 | ![image-20210701161616342](img/1.png) 12 | 13 | 可以直接利用 hook 函数来绕过,dl — 运行时载入一个 PHP 扩展 14 | 15 | 16 | 17 | eg2: [蓝帽杯 2021]One Pointer PHP 18 | 19 | ![image-20210701165831589](img/2.png) 20 | 21 | 直接打fpm 修改蚁剑源代码!! 22 | 23 | ### putenv-ld_preload.py 24 | 25 | **通过putenv hook 函数的原理来绕过,来扫描可用的函数,加载更多的插件能更好的使用,可配合df-bypass.py使用** 26 | 27 | 使用:python putenv-ld_preload.py (默认扫描当前php环境的内值函数) 28 | 29 | ![QQ图片20210701172608](img/3.jpg) 30 | 31 | python putenv-ld_preload.py (模块) 32 | 33 | 测试该模块提供的函数,能更好的bypass。 34 | 35 | >参考: 36 | > 37 | >https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions 38 | > 39 | >https://blog.bi0s.in/2019/10/26/Web/bypass-disable-functions/ 40 | > 41 | >https://www.anquanke.com/post/id/197745 42 | 43 | -------------------------------------------------------------------------------- /df-bypass.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -* 2 | # /usr/bin/python3 3 | # @Author:Firebasky 4 | ''' 5 | 扫描思路 6 | 1.获得全部可以利用的函数 7 | 2.匹配漏网之鱼 8 | 3.根据感觉判断是不是uaf 9 | 4.判断fpm利用 ban了putenv 10 | 5.判断putenv利用hook so 11 | ''' 12 | 13 | import argparse 14 | import requests 15 | import re 16 | 17 | parser = argparse.ArgumentParser() 18 | parser.add_argument("-u", help="phpinfo URL: eg. https://example.com/phpinfo.php") 19 | parser.add_argument("-f", help="phpinfo localfile path: eg. dir/phpinfo") 20 | 21 | args = parser.parse_args() 22 | 23 | class colors: 24 | reset='\033[0m' 25 | red='\033[31m' 26 | green='\033[32m' 27 | orange='\033[33m' 28 | blue='\033[34m' 29 | 30 | print(colors.green + """ 31 | ____ ____ _____ 32 | / ___| ___ __ _ _ __ | _ \| ___| 33 | \___ \ / __/ _` | '_ \| | | | |_ 34 | ___) | (_| (_| | | | | |_| | _| 35 | |____/ \___\__,_|_| |_|____/|_| 36 | """ + "\n\t\t\t\t" + colors.blue + "authors: " + colors.orange + "Firebasky" + "\n" + colors.reset) 37 | 38 | def Get_phpinfo(args): 39 | if (args.u): 40 | url = args.u 41 | phpinfo = requests.get(url).text # 获得phpinfo信息 42 | return phpinfo 43 | elif (args.f): 44 | phpinfofile = args.f 45 | phpinfo = open(phpinfofile, 'r').read() # 获得phpinfo信息 46 | return phpinfo 47 | else: 48 | print(parser.print_help()) 49 | exit() 50 | 51 | dis_fun = [] 52 | phpinfo = Get_phpinfo(args) 53 | 54 | dis_fun = phpinfo.split('disable_functions')[1].split("PHP (.*) - phpinfo\(\)",phpinfo) 57 | 58 | serverapi = re.findall("Server API (.*) ",phpinfo) 59 | 60 | lwzy = ['system', 'shell_exec', 'exec', 'passthru', 'popen', 'proc_open', 'pcntl_exec', 'dl'] 61 | 62 | #我们能够的函数 63 | dangerous_functions = ['pfsockopen','fsockopen','stream_socket_client','stream_socket_client','pcntl_alarm','pcntl_fork','pcntl_waitpid','pcntl_wait','pcntl_wifexited','pcntl_wifstopped','pcntl_wifsignaled','pcntl_wifcontinued','pcntl_wexitstatus','pcntl_wtermsig','pcntl_wstopsig','pcntl_signal','pcntl_signal_get_handler','pcntl_signal_dispatch','pcntl_get_last_error','pcntl_strerror','pcntl_sigprocmask','pcntl_sigwaitinfo','pcntl_sigtimedwait','pcntl_exec','pcntl_getpriority','pcntl_setpriority','pcntl_async_signals','error_log','system','exec','shell_exec','popen','proc_open','passthru','link','symlink','syslog','mail'] 64 | 65 | def Get_Dafun(dangerous_functions):#通过匹配ini获得扩展的危险函数然后利用 66 | modules = [] 67 | if("mbstring.ini" in phpinfo):#安装的mbstring模块 68 | modules += ['mbstring'] 69 | dangerous_functions += ['mb_send_mail'] 70 | 71 | if("imap.ini" in phpinfo):#安装的imap扩展 72 | modules += ['imap'] 73 | dangerous_functions += ['imap_open','imap_mail'] 74 | 75 | if("libvirt-php.ini" in phpinfo):#安装的libvert扩展 76 | modules += ['libvert'] 77 | dangerous_functions += ['libvirt_connect'] 78 | 79 | if("gnupg.ini" in phpinfo):#安装的gnupg模块 80 | modules += ['gnupg'] 81 | dangerous_functions += ['gnupg_init'] 82 | 83 | return dangerous_functions 84 | 85 | def Scan_Fun(dangerous_functions): 86 | exploitable_functions = [] 87 | for i in dangerous_functions: 88 | if i not in dis_fun: 89 | exploitable_functions.append(i)#筛选过滤 90 | if len(exploitable_functions)==0: 91 | print('\nThe disable_functions I don\'t know') 92 | else: 93 | print('\nThis functions can used:') 94 | print(','.join(exploitable_functions)) 95 | 96 | #1.漏网之鱼,可以直接执行命令 97 | def Get_LWZY(lwzy): 98 | exploitable_functions = [] 99 | for i in lwzy: 100 | if i not in dis_fun: 101 | exploitable_functions.append(i)#筛选过滤 102 | if len(exploitable_functions)==0: 103 | print('\nNot easy!!') 104 | Get_uaf() 105 | else: 106 | print(colors.red+'\nnice,can exec!!! use below function'+colors.red) 107 | print(','.join(exploitable_functions)) 108 | 109 | #2.php>7 一般是uaf 110 | def Get_uaf(): 111 | if(int(phpversion[0].split(".")[0])>=7): 112 | print("mayby have uaf,yijian uaf used\n") 113 | Get_fpm() 114 | else: 115 | Get_fpm() 116 | 117 | #3.fpm利用 过滤了函数,使用其他函数实现功能 118 | def Get_fpm(): 119 | if ("FPM/FastCGI" in serverapi[0]): 120 | print(colors.orange+"Maybe fpm can exploit,This functions pfsockopen,stream_socket_sendto,stream_socket_client,fsockopen can also to exploit FPM,maybe modify yijian source code!!!"+colors.orange) 121 | else: 122 | Get_putenv(dis_fun) 123 | 124 | #4.putenv-LD_PRELOAD 125 | def Get_putenv(dis_fun): 126 | if("putenv" not in dis_fun): 127 | print(colors.blue+"Almost use putenv-LD_PRELOAD to hook\n"+colors.blue) 128 | if ("imagick.ini" in phpinfo): 129 | print(colors.blue+'\nPHP-imagick module is present. It can be exploited using LD_PRELOAD method\n'+colors.blue) 130 | else: 131 | print("mayby this is 0day!!\n maybe use extend's function to trigger putenv, can use putenv-ld_preload.py to fuzz") 132 | 133 | Scan_Fun(Get_Dafun(dangerous_functions)) 134 | Get_LWZY(lwzy) 135 | 136 | # print(serverapi[0]) -------------------------------------------------------------------------------- /img/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Firebasky/ScanDF/f20a210ce2c24f1440cbbdb303900d4f91d1f498/img/1.png -------------------------------------------------------------------------------- /img/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Firebasky/ScanDF/f20a210ce2c24f1440cbbdb303900d4f91d1f498/img/2.png -------------------------------------------------------------------------------- /img/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Firebasky/ScanDF/f20a210ce2c24f1440cbbdb303900d4f91d1f498/img/3.jpg -------------------------------------------------------------------------------- /putenv-ld_preload.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -* 2 | # @Author:Firebasky 3 | # strace -f php -r "xxx" 2>&1 | grep -E "execve|fork|vfork" 4 | ''' 5 | 1.获得该函数的参数 6 | 2.获得该函数的参数的数据类型 7 | 3.去fuzz执行函数看是否有调用外部函数从而hook 8 | ''' 9 | import os 10 | import sys 11 | import re 12 | 13 | def getAllDefinedFunc():#获取全部函数 14 | get_defined_function = os.popen("php -r 'print_r(get_defined_functions()['internal']);'").readlines() 15 | #print get_defined_function 16 | b = get_defined_function[2:-1] 17 | b = map(str.strip, b) 18 | for i in range(len(b)): 19 | b[i] = re.sub(r'.*> ', '', b[i]) 20 | get_defined_function = b # all defined PHP functions 21 | get_defined_function.remove(get_defined_function[0]) 22 | get_defined_function.remove(get_defined_function[0]) 23 | get_defined_function.remove('readline') 24 | return get_defined_function 25 | 26 | 27 | def getModuleFunc(phpModuleName,getDefinedFunction):#获取模块函数 28 | moduleFunc = [] 29 | for func in getDefinedFunction: 30 | getExtNameCmd = 'php -r "echo (new ReflectionFunction("{}"))->getExtensionName();"'.format(func) 31 | extName = os.popen(getExtNameCmd).readlines()[0] 32 | if extName == phpModuleName: 33 | moduleFunc.append(func) 34 | return moduleFunc 35 | 36 | def fuzzFunc(getDefinedFunction): 37 | for func in getDefinedFunction: 38 | maxParaNumCmd = 'php -r "echo (new ReflectionFunction("{}"))->getNumberOfParameters();"'.format(func) 39 | minParaNumCmd = 'php -r "echo (new ReflectionFunction("{}"))->getNumberOfRequiredParameters();"'.format(func) 40 | maxParaNum = int(os.popen(maxParaNumCmd).readlines()[0]) 41 | minParaNum = int(os.popen(minParaNumCmd).readlines()[0]) 42 | print(maxParaNum) 43 | print(minParaNum) 44 | for paraNum in range(minParaNum,maxParaNum + 1):#获得输入参数的范围 45 | paraMeters = ['\'1../../../../../../../../../etc/passwd\'' for i in range(paraNum)]#给参数需要字符串 46 | paraMeters = ','.join(paraMeters) 47 | newPhpCmd = "php -r \"{}({});\"".format(func,paraMeters)#去添加参数执行 48 | print(newPhpCmd) 49 | newFuzzCmd = "strace -f {} 2>&1 | grep -E 'execve|fork|vfork'".format(newPhpCmd)#去执行strace看是否满足条件 50 | print(newFuzzCmd) 51 | out = re.findall(r'execve', ''.join(os.popen(newFuzzCmd).readlines()[1:])) 52 | print(out) 53 | if len(out) >= 1: 54 | with open('fuzz-out.txt','a+') as file: 55 | file.write(newPhpCmd + "\n") 56 | break 57 | if __name__ == "__main__": 58 | if len(sys.argv) > 1: 59 | phpModuleName = sys.argv[1] 60 | getDefinedFunction = getAllDefinedFunc() 61 | moduleFunc = getModuleFunc(phpModuleName,getDefinedFunction) 62 | if len(moduleFunc) == 0: 63 | print ('没有找到与指定模块相关的函数,检查名称是否正确') 64 | else: 65 | #print moduleFunc 66 | fuzzFunc(moduleFunc) 67 | else: 68 | print ('fuzz all') 69 | getDefinedFunction = getAllDefinedFunc() 70 | fuzzFunc(getDefinedFunction) --------------------------------------------------------------------------------